larkin 0.4.26 → 0.4.28

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.
@@ -157,7 +157,7 @@ function agentConfigRequest(argv, env, config, agent, dependencies) {
157
157
  const target = config.agents[targetId];
158
158
  if (!target)
159
159
  throw new Error(`Agent 不存在:${targetId};运行 larkin config show --json 查看可用 Agent`);
160
- const currentDirectory = (runtime = target.runtime) => (dependencies.modelDirectory ?? ((input) => discoverRuntimeModelDirectorySync(input, env)))({
160
+ const currentDirectory = (runtime = larkinConfig.toUserRuntime(target.runtime, target.piDistribution)) => (dependencies.modelDirectory ?? ((input) => discoverRuntimeModelDirectorySync(input, env)))({
161
161
  agentId: target.agentId, cwd: target.workspaceDir, runtime,
162
162
  });
163
163
  if (operation === "show") {
@@ -593,7 +593,9 @@ export function runAgentCli(argv, env = process.env, dependencies = {}) {
593
593
  kind: "agent", id: agent.agentId, isSelf: true, name, displayName: name,
594
594
  openId: identity?.open_id ?? identity?.openId ?? null,
595
595
  avatarUrl: identity?.avatar_url ?? identity?.avatarUrl ?? null,
596
- runtime: agent.runtime, model: agent.model, reasoningEffort: agent.effort ?? null,
596
+ runtime: agent.runtime,
597
+ runtimeOption: larkinConfig.toUserRuntime(agent.runtime, agent.piDistribution),
598
+ model: agent.model, reasoningEffort: agent.effort ?? null,
597
599
  createdAt: agent.createdAt ?? "1970-01-01T00:00:00.000Z",
598
600
  });
599
601
  return 0;
@@ -11,7 +11,8 @@ import { isChannelReconnecting, projectAgentReadiness } from "./agent-readiness.
11
11
  import { requestAgentUpsert } from "./local-control.mjs";
12
12
  import * as larkinConfig from "../platform/config.mjs";
13
13
  import { managedOfficialLarkCli } from "./agent-lark-cli-workspace.mjs";
14
- import { assertBuiltinPiAgentDirectory, ownedPiCatalogAgentDir, piAgentDirectory, piCatalogCommandSpec, piDistributionLabel } from "../runtime/pi-provider-config.mjs";
14
+ import { assertBuiltinPiAgentDirectory, ownedPiCatalogAgentDir, piAgentDirectory, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
15
+ import { RUNTIME_OPTIONS, fromUserRuntime, isUserRuntime, piCatalogDistributionForUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
15
16
  import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
16
17
  function safeDocumentCommentCapabilities(configDir, profile) {
17
18
  try {
@@ -223,6 +224,7 @@ if (kind === "agents") {
223
224
  agent_id: agent.agentId,
224
225
  name: agent.name,
225
226
  runtime: agent.runtime,
227
+ runtimeOption: toUserRuntime(agent.runtime, agent.piDistribution),
226
228
  model: effectiveModel,
227
229
  document_comment: {
228
230
  event: "drive.notice.comment_add_v1",
@@ -293,7 +295,7 @@ if (kind === "agents") {
293
295
  say(` ${agent.name}${agent.name === config.activeAgent ? " [active]" : ""}`);
294
296
  const effectiveModel = status?.session?.runtime === agent.runtime && status.session.model ? status.session.model : agent.model;
295
297
  const effectiveEffort = status?.session?.runtime === agent.runtime && status.session.reasoningEffort ? status.session.reasoningEffort : agent.effort;
296
- say(` runtime=${agent.runtime} model=${effectiveModel}${effectiveModel !== agent.model ? ` stored=${agent.model}` : ""}${effectiveEffort ? ` effort=${effectiveEffort}` : ""}`);
298
+ say(` runtime=${toUserRuntime(agent.runtime, agent.piDistribution)} model=${effectiveModel}${effectiveModel !== agent.model ? ` stored=${agent.model}` : ""}${effectiveEffort ? ` effort=${effectiveEffort}` : ""}`);
297
299
  if (status?.runtimeReadiness) {
298
300
  const current = projectedReadiness.readiness.runtime_ready;
299
301
  const state = status.runtimeReadiness.state === "ready" && !current ? "unavailable" : status.runtimeReadiness.state || "incompatible";
@@ -427,7 +429,7 @@ if (!key) {
427
429
  say(`共 ${keys.length} 个 agent(用 --agent <appId> 查看/修改指定一个):`);
428
430
  for (const candidate of keys) {
429
431
  const item = config.agents[candidate];
430
- say(` ${candidate}${candidate === config.activeAgent ? " [active]" : ""} runtime=${item.runtime} model=${item.model}`);
432
+ say(` ${candidate}${candidate === config.activeAgent ? " [active]" : ""} runtime=${toUserRuntime(item.runtime, item.piDistribution)} model=${item.model}`);
431
433
  }
432
434
  process.exit(0);
433
435
  }
@@ -441,7 +443,12 @@ if (kind === "pi-distribution") {
441
443
  if (importExternalProfile && requested !== "builtin")
442
444
  die("--import-external-profile 只允许与 builtin 一起使用");
443
445
  if (requested === "show") {
444
- say(JSON.stringify({ agentId: selectedKey, runtime: agent.runtime, piDistribution: agent.piDistribution ?? "external" }));
446
+ say(JSON.stringify({
447
+ agentId: selectedKey,
448
+ runtime: agent.runtime,
449
+ runtimeOption: toUserRuntime(agent.runtime, agent.piDistribution),
450
+ piDistribution: agent.piDistribution ?? "external",
451
+ }));
445
452
  process.exit(0);
446
453
  }
447
454
  if (requested !== "builtin" && requested !== "external")
@@ -492,11 +499,18 @@ if ((["model", "effort"].includes(kind || "") && agent.runtime === "claude") ||
492
499
  }
493
500
  }
494
501
  let piCatalog = null;
495
- if (agent.runtime === "pi" || (kind === "runtime" && value === "pi")) {
496
- if (kind === "model" && !value)
497
- say(`发行版: ${piDistributionLabel(agent.piDistribution)}`);
502
+ const userRuntime = toUserRuntime(agent.runtime, agent.piDistribution);
503
+ const catalogUserRuntime = kind === "runtime" && (value === "pi" || value === "builtin-pi")
504
+ ? value
505
+ : userRuntime;
506
+ if ((kind === "model" || kind === "effort" || kind === "runtime") && !value) {
507
+ say(`agent=${selectedKey} runtime=${userRuntime} model=${agent.model}`);
508
+ }
509
+ const needsLivePiCatalog = (["model", "effort"].includes(kind) && agent.runtime === "pi")
510
+ || (kind === "runtime" && (value === "pi" || value === "builtin-pi") && Boolean(flagModel));
511
+ if (needsLivePiCatalog) {
498
512
  try {
499
- const catalogCommand = piCatalogCommandSpec(agent.piDistribution, process.env);
513
+ const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(catalogUserRuntime), process.env);
500
514
  piCatalog = await discoverPiModelCatalog({
501
515
  cwd: String(agent.workspaceDir),
502
516
  agentDir: ownedPiCatalogAgentDir(configDir, selectedKey),
@@ -513,10 +527,14 @@ if (agent.runtime === "pi" || (kind === "runtime" && value === "pi")) {
513
527
  const loadedPiCatalog = piCatalog;
514
528
  for (const diagnostic of loadedPiCatalog.diagnostics)
515
529
  console.error(`Pi diagnostic: ${diagnostic}`);
516
- catalog.pi = [{ id: "default", label: `default: ${loadedPiCatalog.effectiveModel}` }, ...loadedPiCatalog.models];
530
+ const piModels = [{ id: "default", label: `default: ${loadedPiCatalog.effectiveModel}` }, ...loadedPiCatalog.models];
531
+ catalog.pi = piModels;
532
+ }
533
+ function catalogRuntimeKey(runtime) {
534
+ return isUserRuntime(runtime) ? fromUserRuntime(runtime).runtime : runtime;
517
535
  }
518
536
  function listModels(runtime, current) {
519
- const items = catalog[runtime];
537
+ const items = catalog[catalogRuntimeKey(runtime)];
520
538
  if (!items) {
521
539
  say(` (无 ${runtime} 的目录数据)`);
522
540
  return;
@@ -536,7 +554,7 @@ function writeAndHint(mutation) {
536
554
  say(" 运行 larkin config apply --agent <App ID> 后应用到新 Runtime session");
537
555
  }
538
556
  function effortChoicesFor(runtime, model) {
539
- const entry = catalog[runtime]?.find((candidate) => candidate.id === model);
557
+ const entry = catalog[catalogRuntimeKey(runtime)]?.find((candidate) => candidate.id === model);
540
558
  if (!entry?.supportedReasoningEfforts?.length)
541
559
  return null;
542
560
  return { list: entry.supportedReasoningEfforts, note: "模型声明", def: entry.defaultReasoningEffort || null };
@@ -554,15 +572,14 @@ function dropEffortIfInvalid(next) {
554
572
  return withoutEffort;
555
573
  }
556
574
  if (kind === "model") {
557
- const runtime = agent.runtime;
575
+ const runtime = userRuntime;
558
576
  if (!value) {
559
- say(`agent=${selectedKey} runtime=${runtime} model=${agent.model}`);
560
577
  say(`\n${runtime} 可选模型:`);
561
578
  listModels(runtime, agent.model);
562
579
  say("\n切换: larkin model <id>");
563
580
  process.exit(0);
564
581
  }
565
- const items = catalog[runtime];
582
+ const items = catalog[catalogRuntimeKey(runtime)];
566
583
  if (items && !items.some((model) => model.id === value)) {
567
584
  console.error(`✗ "${value}" 不是 ${runtime} 的合法模型。合法值:`);
568
585
  for (const model of items)
@@ -588,13 +605,11 @@ else if (kind === "effort") {
588
605
  }
589
606
  const choices = effortChoicesFor(agent.runtime, agent.model);
590
607
  if (!choices) {
591
- say(`agent=${selectedKey} runtime=${agent.runtime} model=${agent.model}`);
592
- say(`模型 ${agent.runtime}/${agent.model} 未显式声明 supportedReasoningEfforts,不能设置 effort。`);
608
+ say(`模型 ${userRuntime}/${agent.model} 未显式声明 supportedReasoningEfforts,不能设置 effort。`);
593
609
  process.exit(value ? 1 : 0);
594
610
  }
595
611
  const { list, note, def } = choices;
596
612
  if (!value) {
597
- say(`agent=${selectedKey} runtime=${agent.runtime} model=${agent.model}`);
598
613
  say(`effort=${agent.effort || `(未设置,runtime 默认${def ? `,该模型默认 ${def}` : ""})`}`);
599
614
  say(`\n${agent.model} 可选档位(${note}):`);
600
615
  for (const level of list)
@@ -603,7 +618,7 @@ else if (kind === "effort") {
603
618
  process.exit(0);
604
619
  }
605
620
  if (!list.includes(value)) {
606
- console.error(`✗ "${value}" 不是 ${agent.runtime}/${agent.model} 的合法档位。合法值: ${list.join(", ")}`);
621
+ console.error(`✗ "${value}" 不是 ${userRuntime}/${agent.model} 的合法档位。合法值: ${list.join(", ")}`);
607
622
  process.exit(1);
608
623
  }
609
624
  if (value === agent.effort) {
@@ -671,34 +686,33 @@ else if (kind === "chats") {
671
686
  }
672
687
  else {
673
688
  if (!value) {
674
- say(`agent=${selectedKey} runtime=${agent.runtime} model=${agent.model}`);
675
689
  say("\n可选 runtime:");
676
- for (const runtime of Object.keys(catalog)) {
677
- say(` ${runtime.padEnd(12)}${runtime === agent.runtime ? " [← 当前]" : ""} 默认模型=${larkinConfig.defaultModelFor(runtime)}`);
690
+ for (const runtime of RUNTIME_OPTIONS) {
691
+ say(` ${runtime.padEnd(12)}${runtime === userRuntime ? " [← 当前]" : ""} 默认模型=${larkinConfig.defaultModelFor(runtime)}`);
678
692
  }
679
693
  say("\n切换: larkin runtime <id> [--model <id>]");
680
694
  process.exit(0);
681
695
  }
682
- if (!catalog[value]) {
683
- console.error(`✗ "${value}" 不是合法 runtime。合法值: ${Object.keys(catalog).join(", ")}`);
696
+ if (!isUserRuntime(value)) {
697
+ console.error(`✗ "${value}" 不是合法 runtime。合法值: ${RUNTIME_OPTIONS.join(", ")}`);
684
698
  process.exit(1);
685
699
  }
686
700
  let model = flagModel;
687
701
  if (model) {
688
- const items = catalog[value];
689
- if (!items.some((candidate) => candidate.id === model)) {
702
+ const items = catalog[catalogRuntimeKey(value)];
703
+ if (!items || !items.some((candidate) => candidate.id === model)) {
690
704
  console.error(`✗ "${model}" 不是 ${value} 的合法模型。合法值:`);
691
- for (const item of items)
705
+ for (const item of items || [])
692
706
  console.error(` ${item.id}`);
693
707
  process.exit(1);
694
708
  }
695
709
  }
696
710
  else {
697
711
  model = larkinConfig.defaultModelFor(value);
698
- if (value !== agent.runtime)
712
+ if (value !== userRuntime)
699
713
  say(`model 随 runtime 重置为默认: ${agent.model} → ${model}(可用 --model 显式指定)`);
700
714
  }
701
- if (value === agent.runtime && model === agent.model) {
715
+ if (value === userRuntime && model === agent.model) {
702
716
  say(`runtime 已经是 ${value},无需修改`);
703
717
  process.exit(0);
704
718
  }
package/dist/app/cli.mjs CHANGED
@@ -49,8 +49,8 @@ if (runtimeAgentAuthority && command === "comment")
49
49
  const commandHelp = {
50
50
  start: `Usage: larkin start [--agent <App ID> | --agents <App ID,...>]
51
51
  Start one foreground supervisor for the daemon and local dashboard, or reuse it.`,
52
- setup: `Usage: larkin setup [--tenant feishu|lark] [--runtime <builtin-pi|external-pi|codex|claude>] [--provider <id> --api-key <key>]
53
- Run setup to create or connect a bot, configure its Agent, and attach it. Choose Feishu or Lark before the authorization QR (--tenant; default feishu). Interactive terminals keep the guided flow; Agent-driven (non-TTY) runs default to builtin-pi and need --provider/--api-key (or --runtime external-pi).`,
52
+ setup: `Usage: larkin setup [--tenant feishu|lark] [--runtime <builtin-pi|pi|codex|claude>] [--provider <id> --api-key <key>]
53
+ Run setup to create or connect a bot, configure its Agent, and attach it. Choose Feishu or Lark before the authorization QR (--tenant; default feishu). Interactive terminals keep the guided flow; Agent-driven (non-TTY) runs default to builtin-pi and need --provider/--api-key (or --runtime pi).`,
54
54
  status: `Usage: larkin status [--json]
55
55
  Show Agent configuration, bot identity, credentials, and connection status. Use --json for readiness automation.`,
56
56
  agents: `Usage: larkin agents [--json]
@@ -58,7 +58,7 @@ List every configured Agent and its current local status. Use --json for daemon/
58
58
  model: `Usage: larkin model [<model>] [--agent <App ID>]
59
59
  Show or change an Agent model.`,
60
60
  runtime: `Usage: larkin runtime [<runtime>] [--agent <App ID>] [--model <model>]
61
- Show or change an Agent runtime.`,
61
+ Show or change an Agent runtime. User-facing ids: pi | builtin-pi | codex | claude.`,
62
62
  "pi-distribution": `Usage: larkin pi-distribution [show|builtin|external] [--agent <App ID>] [--snapshot <private-file>] [--import-external-profile]
63
63
  larkin pi-distribution rollback --snapshot <private-file>
64
64
  Show or change one Pi Agent distribution. builtin requires configured provider state, or explicitly imports the external Pi 0.84.2 profile with --import-external-profile; all changes support config-lock CAS rollback.`,
@@ -7,6 +7,7 @@ import { discoverClaudeModelCatalog } from "../runtime/claude-model-catalog.mjs"
7
7
  import { discoverPiModelCatalog } from "../runtime/pi-model-catalog.mjs";
8
8
  import { discoverCodexModelCatalog } from "../runtime/codex-model-catalog.mjs";
9
9
  import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
10
+ import { piCatalogDistributionForUserRuntime } from "../runtime/user-runtime.mjs";
10
11
  function readTestModelDirectory(env) {
11
12
  const fixture = env?.LARKIN_TEST_RUNTIME_MODEL_DIRECTORY_FILE;
12
13
  if (!fixture)
@@ -36,7 +37,7 @@ export async function discoverRuntimeModelDirectory(input) {
36
37
  ...catalog.models.map(({ id, label, supportedReasoningEfforts }) => ({ id, label, supportedReasoningEfforts })),
37
38
  ];
38
39
  }
39
- if (input.runtime === "pi") {
40
+ if (input.runtime === "pi" || input.runtime === "builtin-pi") {
40
41
  const env = input.env ?? process.env;
41
42
  const agentId = input.agentId || "";
42
43
  if (!agentId)
@@ -46,7 +47,7 @@ export async function discoverRuntimeModelDirectory(input) {
46
47
  const agent = config.agents[agentId];
47
48
  if (!agent)
48
49
  throw new Error(`unknown agent: ${agentId}`);
49
- const catalogCommand = piCatalogCommandSpec(agent.piDistribution, env);
50
+ const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(input.runtime), env);
50
51
  const catalog = await discoverPiModelCatalog({
51
52
  cwd: input.cwd,
52
53
  agentDir: ownedPiCatalogAgentDir(configDir, agentId),
@@ -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,7 @@ 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";
11
12
  function pruneCache(cache, expired, maxEntries) {
12
13
  for (const [key, value] of cache)
13
14
  if (expired(value))
@@ -383,12 +384,12 @@ function dashboardMutation(value) {
383
384
  return { kind: "set-agent-effort", agentId, effort: value.effort === null || value.effort === "default" ? null : String(value.effort || "") };
384
385
  }
385
386
  export function createDashboardConfigController({ csrfCapability, env = process.env, requestUpsert = requestAgentUpsert, chatDirectoryResolver = createChatDirectoryResolver(), claudeModelDirectoryResolver = createClaudeModelDirectoryResolver(), codexModelDirectoryResolver = createCodexModelDirectoryResolver(), piModelDirectoryResolver = createPiModelDirectoryResolver(), }) {
386
- const resolvePiModelDirectory = async (agentId) => {
387
+ const resolvePiModelDirectory = async (agentId, userRuntime) => {
387
388
  const { config, configDir } = loadConfig(env);
388
389
  const agent = config.agents[agentId];
389
390
  if (!agent)
390
391
  throw new Error("unknown agent");
391
- const catalogCommand = piCatalogCommandSpec(agent.piDistribution, env);
392
+ const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(userRuntime), env);
392
393
  return await piModelDirectoryResolver.resolve({
393
394
  agentId,
394
395
  cwd: agent.workspaceDir,
@@ -402,8 +403,8 @@ export function createDashboardConfigController({ csrfCapability, env = process.
402
403
  const agent = config.agents[agentId];
403
404
  if (!agent)
404
405
  throw new Error("unknown agent");
405
- if (runtime === "pi")
406
- return await resolvePiModelDirectory(agentId);
406
+ if (runtime === "pi" || runtime === "builtin-pi")
407
+ return await resolvePiModelDirectory(agentId, runtime);
407
408
  if (runtime === "codex")
408
409
  return await codexModelDirectoryResolver.resolve({ agentId, cwd: agent.workspaceDir, env });
409
410
  if (runtime === "claude")
@@ -420,7 +421,9 @@ export function createDashboardConfigController({ csrfCapability, env = process.
420
421
  const agent = config.agents[mutation.agentId];
421
422
  if (!agent)
422
423
  throw new Error("unknown agent");
423
- const runtime = mutation.kind === "set-agent-runtime" ? mutation.runtime : agent.runtime;
424
+ const runtime = mutation.kind === "set-agent-runtime"
425
+ ? mutation.runtime
426
+ : toUserRuntime(agent.runtime, agent.piDistribution);
424
427
  const model = mutation.kind === "set-agent-runtime" ? mutation.model || "default"
425
428
  : mutation.kind === "set-agent-model" ? mutation.model : agent.model;
426
429
  if (mutation.kind === "set-agent-effort" && mutation.effort === null)
@@ -440,18 +443,22 @@ export function createDashboardConfigController({ csrfCapability, env = process.
440
443
  if (requestUrl.pathname === "/api/config" && req.method === "GET") {
441
444
  try {
442
445
  assertPrivateReadRequest(req, csrfCapability);
443
- json(res, 200, { ...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined), runtimeModels: loadRuntimeModels() });
446
+ json(res, 200, {
447
+ ...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined),
448
+ runtimeModels: loadRuntimeModels(),
449
+ runtimeOptions: [...RUNTIME_OPTIONS],
450
+ });
444
451
  }
445
452
  catch (error) {
446
453
  json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "configuration unavailable" });
447
454
  }
448
455
  return true;
449
456
  }
450
- if (requestUrl.pathname === "/api/models/pi" && req.method === "GET") {
457
+ if ((requestUrl.pathname === "/api/models/pi" || requestUrl.pathname === "/api/models/builtin-pi") && req.method === "GET") {
451
458
  try {
452
459
  assertPrivateReadRequest(req, csrfCapability);
453
460
  const agentId = requestUrl.searchParams.get("agent") || "";
454
- const models = await resolvePiModelDirectory(agentId);
461
+ const models = await resolvePiModelDirectory(agentId, requestUrl.pathname === "/api/models/builtin-pi" ? "builtin-pi" : "pi");
455
462
  json(res, 200, { models });
456
463
  }
457
464
  catch (error) {
@@ -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,
@@ -46,4 +46,4 @@ Error generating stack: `+e.message+`
46
46
  `},or=function(){var e=parseInt(document.body.getAttribute(`data-scroll-locked`)||`0`,10);return isFinite(e)?e:0},sr=function(){C.useEffect(function(){return document.body.setAttribute(ir,(or()+1).toString()),function(){var e=or()-1;e<=0?document.body.removeAttribute(ir):document.body.setAttribute(ir,e.toString())}},[])},cr=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?`margin`:r;sr();var a=C.useMemo(function(){return nr(i)},[i]);return C.createElement(rr,{styles:ar(a,!t,i,n?``:`!important`)})},lr=!1;if(typeof window<`u`)try{var ur=Object.defineProperty({},"passive",{get:function(){return lr=!0,!0}});window.addEventListener(`test`,ur,ur),window.removeEventListener(`test`,ur,ur)}catch{lr=!1}var dr=lr?{passive:!1}:!1,fr=function(e){return e.tagName===`TEXTAREA`},pr=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!==`hidden`&&!(n.overflowY===n.overflowX&&!fr(e)&&n[t]===`visible`)},mr=function(e){return pr(e,`overflowY`)},hr=function(e){return pr(e,`overflowX`)},gr=function(e,t){var n=t.ownerDocument,r=t;do{if(typeof ShadowRoot<`u`&&r instanceof ShadowRoot&&(r=r.host),yr(e,r)){var i=br(e,r);if(i[1]>i[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},_r=function(e){return[e.scrollTop,e.scrollHeight,e.clientHeight]},vr=function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]},yr=function(e,t){return e===`v`?mr(t):hr(t)},br=function(e,t){return e===`v`?_r(t):vr(t)},xr=function(e,t){return e===`h`&&t===`rtl`?-1:1},Sr=function(e,t,n,r,i){var a=xr(e,window.getComputedStyle(t).direction),o=a*r,s=n.target,c=t.contains(s),l=!1,u=o>0,d=0,f=0;do{if(!s)break;var p=br(e,s),m=p[0],h=p[1]-p[2]-a*m;(m||h)&&yr(e,s)&&(d+=h,f+=m);var g=s.parentNode;s=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return(u&&(i&&Math.abs(d)<1||!i&&o>d)||!u&&(i&&Math.abs(f)<1||!i&&-o>f))&&(l=!0),l},Cr=function(e){return`changedTouches`in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},wr=function(e){return[e.deltaX,e.deltaY]},Tr=function(e){return e&&`current`in e?e.current:e},Er=function(e,t){return e[0]===t[0]&&e[1]===t[1]},Dr=function(e){return`
47
47
  .block-interactivity-${e} {pointer-events: none;}
48
48
  .allow-interactivity-${e} {pointer-events: all;}
49
- `},Or=0,kr=[];function Ar(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Or++)[0],a=C.useState(Qn)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=Dn([e.lockRef.current],(e.shards||[]).map(Tr),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Cr(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=gr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=gr(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Sr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!kr.length||kr[kr.length-1]!==a)){var r=`deltaY`in n?wr(n):Cr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Er(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(Tr).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:jr(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Cr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,wr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return kr.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,dr),document.addEventListener(`touchmove`,c,dr),document.addEventListener(`touchstart`,u,dr),function(){kr=kr.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,dr),document.removeEventListener(`touchmove`,c,dr),document.removeEventListener(`touchstart`,u,dr)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Dr(i)}):null,p?C.createElement(cr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function jr(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var Mr=Vn(Hn,Ar),Nr=C.forwardRef(function(e,t){return C.createElement(Wn,Tn({},e,{ref:t,sideCar:Mr}))});Nr.classNames=Wn.classNames;var Pr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Fr=new WeakMap,Ir=new WeakMap,Lr={},Rr=0,zr=function(e){return e&&(e.host||zr(e.parentNode))},Br=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=zr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Vr=function(e,t,n,r){var i=Br(t,Array.isArray(e)?e:[e]);Lr[n]||(Lr[n]=new WeakMap);var a=Lr[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(Fr.get(e)||0)+1,l=(a.get(e)||0)+1;Fr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Ir.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),Rr++,function(){o.forEach(function(e){var t=Fr.get(e)-1,i=a.get(e)-1;Fr.set(e,t),a.set(e,i),t||(Ir.has(e)||e.removeAttribute(r),Ir.delete(e)),i||e.removeAttribute(n)}),Rr--,Rr||(Fr=new WeakMap,Fr=new WeakMap,Ir=new WeakMap,Lr={})}},Hr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Pr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Vr(r,i,n,`aria-hidden`)):function(){return null}},Ur=Object.defineProperty,Wr=(e,t)=>Ur(e,`name`,{value:t,configurable:!0}),Gr=`Dialog`,[Kr,qr]=Be(Gr),[Jr,Yr]=Kr(Gr),Xr=Wr(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=nt({prop:r,defaultProp:i??!1,onChange:a,caller:Gr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,A.jsx)(Jr,{scope:t,triggerRef:s,contentRef:c,contentId:qe(),titleId:qe(),descriptionId:qe(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Zr=`DialogPortal`,[Qr,$r]=Kr(Zr,{forceMount:void 0}),ei=Wr(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Yr(Zr,t);return(0,A.jsx)(Qr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,A.jsx)(fn,{present:n||a.open,children:(0,A.jsx)(cn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ti=`DialogOverlay`,ni=C.forwardRef(Wr(function(e,t){let n=$r(ti,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ti,e.__scopeDialog);return a.modal?(0,A.jsx)(fn,{present:r||a.open,children:(0,A.jsx)(ii,{...i,ref:t})}):null},`DialogOverlay`)),ri=ut(`DialogOverlay.RemoveScroll`),ii=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ti,n),a=Fe(t,Lt());return(0,A.jsx)(Nr,{as:ri,allowPinchZoom:!0,shards:[i.contentRef],children:(0,A.jsx)(Tt.div,{"data-state":gi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ai=`DialogContent`,oi=C.forwardRef(Wr(function(e,t){let n=$r(ai,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ai,e.__scopeDialog);return(0,A.jsx)(fn,{present:r||a.open,children:a.modal?(0,A.jsx)(si,{...i,ref:t}):(0,A.jsx)(ci,{...i,ref:t})})},`DialogContent`)),si=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(null),i=Fe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Hr(e)},[]),(0,A.jsx)(li,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Ee(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Ee(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Ee(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),ci=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,A.jsx)(li,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),li=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Yr(ai,n);return Cn(),(0,A.jsx)(A.Fragment,{children:(0,A.jsx)(Jt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,A.jsx)(It,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":gi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),ui=`DialogTitle`,di=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ui,n),{setTitleCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),fi=`DialogDescription`,pi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(fi,n),{setDescriptionCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),mi=`DialogClose`,hi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(mi,n);return(0,A.jsx)(Tt.button,{type:`button`,...r,ref:t,onClick:Ee(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function gi(e){return e?`open`:`closed`}Wr(gi,`getState`);function _i(...e){return e.filter(Boolean).join(` `)}function vi({className:e,...t}){return(0,A.jsx)(`button`,{className:_i(`ui-button`,e),...t})}function yi({className:e,...t}){return(0,A.jsx)(`span`,{className:_i(`ui-badge`,e),...t})}function bi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,A.jsx)(Xr,{open:e,onOpenChange:t,children:(0,A.jsxs)(ei,{children:[(0,A.jsx)(ni,{className:`sheet-overlay`}),(0,A.jsxs)(oi,{className:`sheet-content`,children:[(0,A.jsxs)(`div`,{className:`sheet-head`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(di,{children:n}),r?(0,A.jsx)(pi,{children:r}):null]}),(0,A.jsx)(hi,{asChild:!0,children:(0,A.jsx)(vi,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,A.jsx)(me,{size:18})})})]}),i]})]})})}function xi({title:e,detail:t}){return(0,A.jsxs)(`div`,{className:`empty-state`,children:[(0,A.jsx)(`strong`,{children:e}),t?(0,A.jsx)(`p`,{children:t}):null]})}function Si(e){return e===`builtin`?`内置 Pi`:`用户安装的 Pi`}var Ci={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},wi=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},Ti=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ei(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new Ti(e.error||`HTTP ${n.status}`,e)}return n.json()}function Di(e,t){if(!(e instanceof Ti)||!e.body.readiness)return e instanceof Error?e.message:t;let n=e.body.readiness;return[n.reason||e.message,n.nextAction?`下一步:${n.nextAction}`:null].filter(Boolean).join(`;`)}async function Oi(e){return Ei(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify(e)})}function ki(e,t={}){return Ei(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":wi.csrfCapability}})}function Ai(e){return e==null||!Number.isFinite(e)?`暂无`:e<60?`${e}s 前`:e<3600?`${Math.floor(e/60)}m 前`:e<86400?`${Math.floor(e/3600)}h 前`:`${Math.floor(e/86400)}d 前`}function ji(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function Mi(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}function Ni(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Pi(){let[e,t]=(0,C.useState)(null),[n,r]=(0,C.useState)(null),[i,a]=(0,C.useState)(!0),[o,s]=(0,C.useState)(!1),c=(0,C.useRef)(Se()),l=(0,C.useRef)(null),u=(0,C.useRef)(null),d=(0,C.useCallback)(async()=>{u.current?.abort();let e=new AbortController;u.current=e;let n=c.current.issue();try{let i=await Ei(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(wi.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==wi.buildFingerprint){window.location.reload();return}t(i),r(null),s(!1)}catch(t){if(e.signal.aborted||!c.current.accepts(n))return;r(t instanceof Error?t.message:String(t)),s(!0)}finally{c.current.accepts(n)&&a(!1)}},[]);return(0,C.useEffect)(()=>{let e=!0,t=async()=>{await d(),e&&(l.current=window.setTimeout(t,3e3))};return t(),()=>{e=!1,l.current!==null&&window.clearTimeout(l.current),u.current?.abort()}},[d]),{data:e,error:n,loading:i,stale:o,retry:d}}function Fi({agent:e}){let t=(e.displayName||e.name||e.agentId).slice(0,2).toUpperCase(),[n,r]=(0,C.useState)(!1);(0,C.useEffect)(()=>r(!1),[e.agentId,e.bot?.hasAvatar]);let i=e.bot?.hasAvatar&&!n;return(0,A.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,A.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function Ii({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>be(e,i),[e,i]),s=(0,C.useRef)([]),c=(e,t)=>{if(!o.length)return;let n=t===0?e:(e+t+o.length)%o.length;s.current[n]?.focus()};return(0,A.jsxs)(`div`,{className:_i(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,A.jsxs)(`label`,{className:`agent-search`,children:[(0,A.jsx)(de,{size:15,"aria-hidden":`true`}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,A.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,A.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,A.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:_i(`agent-list-item`,e.agentId===t&&`selected`),onClick:()=>n(e.agentId),onKeyDown:e=>{e.key===`ArrowDown`&&(e.preventDefault(),c(r,1)),e.key===`ArrowUp`&&(e.preventDefault(),c(r,-1)),e.key===`Home`&&(e.preventDefault(),c(0,0)),e.key===`End`&&(e.preventDefault(),c(o.length-1,0))},children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,A.jsx)(`strong`,{children:e.displayName||e.name}),(0,A.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,A.jsxs)(`span`,{className:`status-line`,children:[(0,A.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ni(e)]})]}),e.issue?(0,A.jsx)(k,{size:15,className:`danger`,"aria-label":`异常`}):(0,A.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,A.jsx)(xi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function Li({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)(!1),[l,u]=(0,C.useState)(null),d=a!==r;return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return c(!0),ki(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),u(null)}).catch(e=>{t.signal.aborted||u(e.message)}).finally(()=>{t.signal.aborted||c(!1)}),()=>t.abort()},[e]),(0,A.jsx)(bi,{open:e,onOpenChange:e=>{!e&&d&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,A.jsxs)(`div`,{className:`settings-form`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`真人群消息默认策略`}),(0,A.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:s,children:[(0,A.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,A.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,A.jsxs)(`div`,{className:`form-actions`,children:[(0,A.jsx)(vi,{onClick:()=>o(r),disabled:!d||s,children:`放弃草稿`}),(0,A.jsx)(vi,{className:`primary`,onClick:async()=>{c(!0);try{let e=await Oi({operation:`set-global-mention`,value:a});i(a),u(`已保存 · ${e.applyState} · ${e.revision.slice(0,20)}…`),n()}catch(e){u(e instanceof Error?e.message:String(e))}finally{c(!1)}},disabled:!d||s,children:s?`保存中…`:`保存全局设置`})]}),l?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:l}):null]})})}function Ri({config:e,onSave:t,disabled:n}){let[r,i]=(0,C.useState)(``),[a,o]=(0,C.useState)(``),[s,c]=(0,C.useState)(`require`),l=e.knownChats.filter(e=>e.kind===`group`&&e.override!==`inherit`).filter(e=>{let t=r.trim().toLocaleLowerCase();return!t||e.chatId.toLocaleLowerCase().includes(t)||String(e.displayName||`未解析群名`).toLocaleLowerCase().includes(t)});return(0,A.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,A.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,A.jsxs)(`label`,{className:`table-search`,children:[(0,A.jsx)(de,{size:14}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,A.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,A.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,A.jsxs)(`table`,{className:`policy-table`,children:[(0,A.jsx)(`thead`,{children:(0,A.jsxs)(`tr`,{children:[(0,A.jsx)(`th`,{children:`群`}),(0,A.jsx)(`th`,{children:`消息策略`}),(0,A.jsx)(`th`,{children:`操作`})]})}),(0,A.jsx)(`tbody`,{children:l.map(e=>(0,A.jsxs)(`tr`,{children:[(0,A.jsxs)(`td`,{children:[(0,A.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,A.jsx)(`code`,{children:e.chatId}):(0,A.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,A.jsx)(`td`,{children:(0,A.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,A.jsx)(`td`,{children:(0,A.jsx)(vi,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,A.jsx)(xi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,A.jsxs)(`div`,{className:`chat-add`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`完整 chat_id`}),(0,A.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`消息策略`}),(0,A.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,A.jsx)(vi,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function zi({agentId:e,onDirtyChange:t,refreshKey:n}){let[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)({}),[s,c]=(0,C.useState)({}),[l,u]=(0,C.useState)(!0),[d,f]=(0,C.useState)(null),[p,m]=(0,C.useState)(null),h=(0,C.useRef)(Se()),g=(0,C.useRef)(Se()),_=!xe(a,s);(0,C.useEffect)(()=>t(_),[_,t]),(0,C.useEffect)(()=>{let e=e=>{_&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[_]);let v=(0,C.useCallback)(async(t=!1)=>{let n=h.current.issue();u(!0);try{let r=await ki(`/api/config?agent=${encodeURIComponent(e)}`);if(!h.current.accepts(n))return;let a=r.agents[0];if(!a)throw Error(`配置投影中没有当前 Agent`);let s={runtime:a.runtime,model:a.model,effort:a.effort||`default`,mention:a.mention.override};i(r),o(s),(!t||!_)&&c(s)}catch(e){h.current.accepts(n)&&f(e instanceof Error?e.message:String(e))}finally{h.current.accepts(n)&&u(!1)}},[e,_]);(0,C.useEffect)(()=>{v(!1)},[e]),(0,C.useEffect)(()=>{n>0&&v(!0)},[n]);let y=r?.agents[0],b=(e,t)=>c(n=>({...n,[e]:t})),x=String(s.runtime||y?.runtime||``),S=String(s.model||y?.model||`default`);(0,C.useEffect)(()=>{if(m({runtime:x,status:`loading`,models:[]}),!new Set([`pi`,`codex`,`claude`]).has(x)){m({runtime:x,status:`error`,models:[]});return}let t=g.current.issue(),n=new AbortController;return ki(`/api/models/${x}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{g.current.accepts(t)&&m({runtime:x,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&g.current.accepts(t)&&(m({runtime:x,status:`error`,models:[]}),f(`${x} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,x]);let w=p?.runtime===x&&p.status===`ready`,ee=p?.runtime===x?p.status:`loading`,te=w?p.models:[],T=`${S} · 当前配置(模型目录${ee===`error`?`不可用`:`加载中`})`,E=w?te.some(e=>e.id===S)?te:[{id:S,label:`${S} · 当前配置`},...te]:[{id:S,label:T}],ne=w&&S!=="default"&&te.find(e=>e.id===S)?.supportedReasoningEfforts||[],re=s.runtime!==a.runtime||s.model!==a.model||s.effort!==a.effort,ie=()=>Ei(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify({agentId:e})}),ae=async()=>{if(y){u(!0);try{let t=[];s.runtime===a.runtime?s.model!==a.model&&t.push({operation:`set-agent-model`,agentId:e,model:s.model}):t.push({operation:`set-agent-runtime`,agentId:e,runtime:s.runtime,model:s.model}),s.effort!==a.effort&&t.push({operation:`set-agent-effort`,agentId:e,effort:s.effort}),s.mention!==a.mention&&t.push({operation:`set-agent-mention`,agentId:e,value:s.mention});let n=null;for(let e of t)n=await Oi(e);if(re)try{await ie(),f(`配置已保存并应用`)}catch(e){f(`配置已保存但未应用:${Di(e,`稍后重试`)}`)}else f(n?`配置已保存`:`没有需要保存的修改`);await v(!1)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}}},oe=async()=>{u(!0);try{await ie(),f(`已应用保存的运行配置`),await v(!0)}catch(e){f(`配置保持已保存但未应用:${Di(e,`稍后重试`)}`)}finally{u(!1)}},se=async(t,n)=>{u(!0);try{let r=await Oi({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});f(`群策略已保存 · ${r.applyState}`),await v(!0)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}};if(!y&&l)return(0,A.jsx)(xi,{title:`加载 Agent 配置…`});if(!y)return(0,A.jsx)(xi,{title:`配置不可用`,detail:d||`请重试`});let ce=y.apply.applyState===`pending`?`待应用`:y.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,A.jsxs)(`div`,{className:`configuration-page`,children:[(0,A.jsxs)(`section`,{className:`config-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`Agent 配置`}),(0,A.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,A.jsx)(yi,{className:y.apply.applyState===`pending`?`warning`:`success`,children:ce})]}),(0,A.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,A.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),(0,A.jsxs)(`div`,{className:`config-grid`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Runtime`}),(0,A.jsx)(`select`,{value:x,disabled:l,onChange:e=>{b(`runtime`,e.target.value),b(`model`,`default`),b(`effort`,`default`)},children:Object.keys(r?.runtimeModels||{}).map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))})]}),x===`pi`?(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Pi 发行版`}),(0,A.jsx)(`input`,{readOnly:!0,disabled:!0,"aria-label":`Pi 发行版`,value:Si(y.piDistribution)})]}):null,(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Model`}),(0,A.jsx)(`select`,{value:S,disabled:l||!w,onChange:e=>{b(`model`,e.target.value),e.target.value==="default"&&b(`effort`,`default`)},children:E.map(e=>(0,A.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Effort`}),(0,A.jsxs)(`select`,{value:String(s.effort||`default`),disabled:l||!w||S==="default"||!ne.length,onChange:e=>b(`effort`,e.target.value),children:[(0,A.jsx)(`option`,{value:`default`,children:`default · 不指定`}),ne.map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))]})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`群消息策略`}),(0,A.jsxs)(`select`,{value:String(s.mention||`inherit`),disabled:l,onChange:e=>b(`mention`,e.target.value),children:[(0,A.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),_||y.apply.applyState===`pending`?(0,A.jsxs)(`div`,{className:`form-actions`,children:[_?(0,A.jsx)(vi,{disabled:l,onClick:()=>c(a),children:`放弃草稿`}):null,_?(0,A.jsx)(vi,{className:`primary`,disabled:l,onClick:ae,children:l?`处理中…`:re?`保存并应用`:`保存 Agent 配置`}):(0,A.jsxs)(vi,{className:`primary`,disabled:l,onClick:oe,children:[(0,A.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,d?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:d}):null]}),(0,A.jsx)(Ri,{config:y,onSave:se,disabled:l})]})}function Bi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,A.jsxs)(`div`,{className:`overview-page`,children:[(0,A.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ie,{})}),(0,A.jsx)(`span`,{children:`连接`}),(0,A.jsx)(`strong`,{children:e.connection.state}),(0,A.jsx)(`small`,{children:e.connection.reason})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`执行状态`}),(0,A.jsx)(`strong`,{children:Ni(e)}),(0,A.jsx)(`small`,{children:e.lastActivity?Ai(e.lastActivity.ageSec):`尚无活动`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(le,{})}),(0,A.jsx)(`span`,{children:`最近消息`}),(0,A.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,A.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${Ai(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`上下文`}),(0,A.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,A.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`累计 Token`}),(0,A.jsx)(`strong`,{children:Mi(t.cumulativeTokens)}),(0,A.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`最近一轮用量`}),(0,A.jsx)(`strong`,{children:Mi(t.latestTokens)}),(0,A.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ue,{})}),(0,A.jsx)(`span`,{children:`Compaction`}),(0,A.jsx)(`strong`,{children:Mi(n.count)}),(0,A.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${ji(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,A.jsxs)(`div`,{className:`overview-sections`,children:[(0,A.jsxs)(`section`,{className:`content-section wide`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`最近动态`}),(0,A.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed.slice(0,10)})]}),(0,A.jsxs)(`section`,{className:`content-section`,children:[(0,A.jsx)(`h3`,{children:`运行摘要`}),(0,A.jsxs)(`dl`,{className:`facts`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Runtime`}),(0,A.jsx)(`dd`,{children:e.runtime})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Readiness`}),(0,A.jsx)(`dd`,{children:e.runtimeReadiness?`${e.runtimeReadiness.state}${e.runtimeReadiness.version?` · ${e.runtimeReadiness.version}`:``}${e.runtimeReadiness.executable?` · ${e.runtimeReadiness.executable}`:``}${e.runtimeReadiness.reason?` · ${e.runtimeReadiness.reason}`:``}${e.runtimeReadiness.nextAction?` · ${e.runtimeReadiness.nextAction}`:``}`:`unknown`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Model`}),(0,A.jsx)(`dd`,{children:e.model})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Effort`}),(0,A.jsx)(`dd`,{children:e.effort||`default`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`入站`}),(0,A.jsx)(`dd`,{children:e.inbound.state})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`提醒`}),(0,A.jsx)(`dd`,{children:e.activeReminders})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`指示灯`}),(0,A.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function M({items:e}){if(!e.length)return(0,A.jsx)(xi,{title:`还没有活动记录`});let t=e=>e.kind===`error`||e.state===`error`?`state-error`:e.kind===`deliver`?`state-deliver`:[`working`,`thinking`,`tool`].includes(String(e.state))?`state-active`:[`idle`,`online`].includes(String(e.state))?`state-idle`:`state-unknown`;return(0,A.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,A.jsxs)(`li`,{children:[(0,A.jsx)(`span`,{className:_i(`timeline-dot`,t(e))}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,A.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,A.jsx)(`time`,{children:ji(e.at)})]})]},`${e.at}-${n}`))})}function N({agent:e}){return e.conversation.length?(0,A.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,A.jsxs)(`article`,{className:_i(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,A.jsxs)(`header`,{children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(yi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,A.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,A.jsx)(`time`,{children:ji(t.at)})]}),(0,A.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,A.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,A.jsx)(xi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Vi({agent:e}){if(!e.remindersList.length)return(0,A.jsx)(xi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,A.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,A.jsxs)(`article`,{className:_i(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,A.jsx)(ne,{size:16}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,A.jsxs)(`p`,{children:[ji(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,A.jsx)(yi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Hi({agentId:e}){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)(!0),[s,c]=(0,C.useState)(null),l=(0,C.useRef)(Se()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ei(`/api/workspace?agent=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`);if(!l.current.accepts(r))return;a.kind===`directory`?(n(a),i(null)):i(a),c(null)}catch(e){l.current.accepts(r)&&c(e instanceof Error?e.message:String(e))}finally{l.current.accepts(r)&&o(!1)}},[e]);(0,C.useEffect)(()=>{u(``)},[u]);let d=t?.path.split(`/`).filter(Boolean)||[];return(0,A.jsxs)(`div`,{className:`workspace-browser`,children:[(0,A.jsxs)(`div`,{className:`workspace-side`,children:[(0,A.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,A.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,A.jsxs)(`span`,{children:[(0,A.jsx)(re,{size:12}),(0,A.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,A.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,A.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,A.jsx)(se,{size:15}):(0,A.jsx)(oe,{size:15}),(0,A.jsx)(`span`,{children:e.name}),(0,A.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,A.jsx)(xi,{title:`空目录`}):null]})]}),(0,A.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,A.jsx)(xi,{title:`读取工作区…`}):s?(0,A.jsx)(xi,{title:`工作区读取失败`,detail:s}):r?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`header`,{children:[(0,A.jsx)(`strong`,{children:r.name}),(0,A.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,A.jsx)(xi,{title:`二进制文件不可预览`}):(0,A.jsx)(`pre`,{children:r.content})]}):(0,A.jsx)(xi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function Ui({agent:e}){return(0,A.jsxs)(`section`,{className:`logs-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`结构化日志`}),(0,A.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed}),e.recentErrors.length?(0,A.jsxs)(`section`,{className:`error-stack`,children:[(0,A.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,A.jsxs)(`div`,{children:[(0,A.jsx)(ae,{size:14}),(0,A.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,A.jsx)(`time`,{children:ji(e.at)})]},`${e.at}-${t}`))]}):null]})}function Wi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{},r=t.contextPercent==null?`上下文 ${e.session?.turns||0} turns`:`上下文 ${Math.round(Number(t.contextPercent))}% · ${Mi(t.latestTokens)} / ${Mi(t.contextWindow)}`;return(0,A.jsxs)(`header`,{className:`agent-header`,children:[(0,A.jsxs)(`div`,{className:`agent-title`,children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`p`,{children:e.agentId}),(0,A.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,A.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,A.jsx)(yi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,A.jsx)(`span`,{children:e.runtime}),(0,A.jsx)(`span`,{children:e.model}),(0,A.jsx)(`span`,{children:e.effort||`default effort`}),(0,A.jsx)(`span`,{children:r}),(0,A.jsxs)(`span`,{children:[`累计 `,Mi(t.cumulativeTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`最近一轮 `,Mi(t.latestTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`Compact `,Mi(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,A.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?Ai(e.lastDeliver.ageSec):`暂无`]}),(0,A.jsx)(`span`,{children:e.lastActivity?.detail||Ni(e)})]})]})}function Gi(){let e=Pi(),t=(0,C.useMemo)(()=>_e(window.location.search),[]),[n,r]=(0,C.useState)(t.agentId),[i,a]=(0,C.useState)(t.tab),[o,s]=(0,C.useState)(!1),[c,l]=(0,C.useState)(!1),[u,d]=(0,C.useState)(0),[f,p]=(0,C.useState)(!1),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(new Map),_=e.data?.agents||[],v=e.data?.packageVersion||wi.packageVersion,y=_.find(e=>e.agentId===n)||null,b=(0,C.useCallback)((e,t)=>{let n=`${e}:${t}`,r=window.scrollY;g.current.set(n,r);try{sessionStorage.setItem(`larkin-dashboard-scroll:${n}`,String(r))}catch{}},[]),x=(0,C.useCallback)(e=>{if(g.current.has(e))return g.current.get(e)||0;try{let t=Number(sessionStorage.getItem(`larkin-dashboard-scroll:${e}`));return Number.isFinite(t)&&t>=0?t:0}catch{return 0}},[]),S=(0,C.useCallback)((e,t,o=!1)=>{n&&b(n,i);let s=o?`replaceState`:`pushState`;window.history[s](null,``,ve(e,t)),r(e),a(t)},[b,n,i]),w=()=>!f||window.confirm(`Agent 配置尚未保存,确定放弃草稿吗?`),ee=e=>{w()&&(p(!1),S(e,i),l(!1))},te=e=>{e!==`configuration`&&!w()||(e!==`configuration`&&p(!1),S(n,e))};return(0,C.useEffect)(()=>{if(!e.data)return;let t=ye(n,e.data.agents);t!==n&&(h(n?`原 Agent 已不存在,已切换到第一个可用 Agent。`:null),S(t,i,!0))},[e.data,n,i,S]),(0,C.useEffect)(()=>{let e=()=>{let e=_e(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,ve(n,i));return}n&&b(n,i),p(!1),r(e.agentId),a(e.tab)};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[f,b,n,i]),(0,C.useLayoutEffect)(()=>{if(!n)return;let e=`${n}:${i}`,t=x(e);window.scrollY!==t&&window.scrollTo({top:t,behavior:`auto`})},[x,n,i]),(0,A.jsxs)(`div`,{className:_i(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,A.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,A.jsxs)(`div`,{className:`brand`,children:[(0,A.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,A.jsx)(Ii,{agents:_,selectedId:n,onSelect:ee}),(0,A.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,A.jsxs)(`main`,{className:_i(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,A.jsxs)(`div`,{className:`app-topbar`,children:[(0,A.jsx)(vi,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,A.jsx)(O,{size:18})}),(0,A.jsxs)(`div`,{children:[(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,A.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,A.jsx)(yi,{className:`warning`,children:`状态陈旧`}):null]}),(0,A.jsxs)(vi,{onClick:()=>s(!0),children:[(0,A.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,A.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,A.jsx)(k,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,A.jsx)(vi,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,A.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,A.jsx)(vi,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,A.jsxs)(`div`,{className:`page-loading`,children:[(0,A.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(Wi,{agent:y}),(0,A.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:ge.map(e=>(0,A.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,A.jsx)(ce,{}):e===`conversation`?(0,A.jsx)(le,{}):e===`configuration`?(0,A.jsx)(pe,{}):e===`reminders`?(0,A.jsx)(ne,{}):e===`workspace`?(0,A.jsx)(se,{}):(0,A.jsx)(D,{}),Ci[e]]},e))}),(0,A.jsxs)(`div`,{className:_i(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,A.jsx)(Bi,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,A.jsx)(N,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,A.jsx)(zi,{agentId:y.agentId,onDirtyChange:p,refreshKey:u})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,A.jsx)(Vi,{agent:y})}),(0,A.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,A.jsx)(Hi,{agentId:y.agentId})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,A.jsx)(Ui,{agent:y})})]},y.agentId)]}):(0,A.jsx)(xi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,A.jsx)(Li,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,A.jsx)(bi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,A.jsx)(Ii,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Ki=document.getElementById(`root`);if(!Ki)throw Error(`dashboard root is missing`);(0,he.createRoot)(Ki).render((0,A.jsx)(C.StrictMode,{children:(0,A.jsx)(Gi,{})}));
49
+ `},Or=0,kr=[];function Ar(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Or++)[0],a=C.useState(Qn)[0],o=C.useRef(e);C.useEffect(function(){o.current=e},[e]),C.useEffect(function(){if(e.inert){document.body.classList.add(`block-interactivity-${i}`);var t=Dn([e.lockRef.current],(e.shards||[]).map(Tr),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(`allow-interactivity-${i}`)}),function(){document.body.classList.remove(`block-interactivity-${i}`),t.forEach(function(e){return e.classList.remove(`allow-interactivity-${i}`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=C.useCallback(function(e,t){if(`touches`in e&&e.touches.length===2||e.type===`wheel`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Cr(e),a=n.current,s=`deltaX`in e?e.deltaX:a[0]-i[0],c=`deltaY`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?`h`:`v`;if(`touches`in e&&d===`h`&&u.type===`range`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=gr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=gr(d,u)),!m)return!1;if(!r.current&&`changedTouches`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Sr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!kr.length||kr[kr.length-1]!==a)){var r=`deltaY`in n?wr(n):Cr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Er(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(Tr).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=C.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:jr(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=C.useCallback(function(e){n.current=Cr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,wr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return kr.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,dr),document.addEventListener(`touchmove`,c,dr),document.addEventListener(`touchstart`,u,dr),function(){kr=kr.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,dr),document.removeEventListener(`touchmove`,c,dr),document.removeEventListener(`touchstart`,u,dr)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Dr(i)}):null,p?C.createElement(cr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function jr(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var Mr=Vn(Hn,Ar),Nr=C.forwardRef(function(e,t){return C.createElement(Wn,Tn({},e,{ref:t,sideCar:Mr}))});Nr.classNames=Wn.classNames;var Pr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Fr=new WeakMap,Ir=new WeakMap,Lr={},Rr=0,zr=function(e){return e&&(e.host||zr(e.parentNode))},Br=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=zr(t);return n&&e.contains(n)?n:(console.error(`aria-hidden`,t,`in not contained inside`,e,`. Doing nothing`),null)}).filter(function(e){return!!e})},Vr=function(e,t,n,r){var i=Br(t,Array.isArray(e)?e:[e]);Lr[n]||(Lr[n]=new WeakMap);var a=Lr[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==`false`,c=(Fr.get(e)||0)+1,l=(a.get(e)||0)+1;Fr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Ir.set(e,!0),l===1&&e.setAttribute(n,`true`),i||e.setAttribute(r,`true`)}catch(t){console.error(`aria-hidden: cannot operate on `,e,t)}})};return u(t),s.clear(),Rr++,function(){o.forEach(function(e){var t=Fr.get(e)-1,i=a.get(e)-1;Fr.set(e,t),a.set(e,i),t||(Ir.has(e)||e.removeAttribute(r),Ir.delete(e)),i||e.removeAttribute(n)}),Rr--,Rr||(Fr=new WeakMap,Fr=new WeakMap,Ir=new WeakMap,Lr={})}},Hr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Pr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Vr(r,i,n,`aria-hidden`)):function(){return null}},Ur=Object.defineProperty,Wr=(e,t)=>Ur(e,`name`,{value:t,configurable:!0}),Gr=`Dialog`,[Kr,qr]=Be(Gr),[Jr,Yr]=Kr(Gr),Xr=Wr(e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=C.useRef(null),c=C.useRef(null),[l,u]=nt({prop:r,defaultProp:i??!1,onChange:a,caller:Gr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,A.jsx)(Jr,{scope:t,triggerRef:s,contentRef:c,contentId:qe(),titleId:qe(),descriptionId:qe(),titlePresent:d>0,descriptionPresent:p>0,setTitleCount:f,setDescriptionCount:m,open:l,onOpenChange:u,onOpenToggle:C.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})},`Dialog`),Zr=`DialogPortal`,[Qr,$r]=Kr(Zr,{forceMount:void 0}),ei=Wr(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Yr(Zr,t);return(0,A.jsx)(Qr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,A.jsx)(fn,{present:n||a.open,children:(0,A.jsx)(cn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ti=`DialogOverlay`,ni=C.forwardRef(Wr(function(e,t){let n=$r(ti,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ti,e.__scopeDialog);return a.modal?(0,A.jsx)(fn,{present:r||a.open,children:(0,A.jsx)(ii,{...i,ref:t})}):null},`DialogOverlay`)),ri=ut(`DialogOverlay.RemoveScroll`),ii=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ti,n),a=Fe(t,Lt());return(0,A.jsx)(Nr,{as:ri,allowPinchZoom:!0,shards:[i.contentRef],children:(0,A.jsx)(Tt.div,{"data-state":gi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ai=`DialogContent`,oi=C.forwardRef(Wr(function(e,t){let n=$r(ai,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Yr(ai,e.__scopeDialog);return(0,A.jsx)(fn,{present:r||a.open,children:a.modal?(0,A.jsx)(si,{...i,ref:t}):(0,A.jsx)(ci,{...i,ref:t})})},`DialogContent`)),si=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(null),i=Fe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Hr(e)},[]),(0,A.jsx)(li,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Ee(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Ee(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Ee(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),ci=C.forwardRef(Wr(function(e,t){let n=Yr(ai,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,A.jsx)(li,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})},`DialogContentNonModal`)),li=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Yr(ai,n);return Cn(),(0,A.jsx)(A.Fragment,{children:(0,A.jsx)(Jt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,A.jsx)(It,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":gi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),ui=`DialogTitle`,di=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(ui,n),{setTitleCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),fi=`DialogDescription`,pi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(fi,n),{setDescriptionCount:a}=i;return He(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,A.jsx)(Tt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),mi=`DialogClose`,hi=C.forwardRef(Wr(function(e,t){let{__scopeDialog:n,...r}=e,i=Yr(mi,n);return(0,A.jsx)(Tt.button,{type:`button`,...r,ref:t,onClick:Ee(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function gi(e){return e?`open`:`closed`}Wr(gi,`getState`);function _i(...e){return e.filter(Boolean).join(` `)}function vi({className:e,...t}){return(0,A.jsx)(`button`,{className:_i(`ui-button`,e),...t})}function yi({className:e,...t}){return(0,A.jsx)(`span`,{className:_i(`ui-badge`,e),...t})}function bi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,A.jsx)(Xr,{open:e,onOpenChange:t,children:(0,A.jsxs)(ei,{children:[(0,A.jsx)(ni,{className:`sheet-overlay`}),(0,A.jsxs)(oi,{className:`sheet-content`,children:[(0,A.jsxs)(`div`,{className:`sheet-head`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(di,{children:n}),r?(0,A.jsx)(pi,{children:r}):null]}),(0,A.jsx)(hi,{asChild:!0,children:(0,A.jsx)(vi,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,A.jsx)(me,{size:18})})})]}),i]})]})})}function xi({title:e,detail:t}){return(0,A.jsxs)(`div`,{className:`empty-state`,children:[(0,A.jsx)(`strong`,{children:e}),t?(0,A.jsx)(`p`,{children:t}):null]})}var Si=[`codex`,`claude`,`pi`,`builtin-pi`],Ci={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},wi=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},Ti=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ei(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new Ti(e.error||`HTTP ${n.status}`,e)}return n.json()}function Di(e,t){if(!(e instanceof Ti)||!e.body.readiness)return e instanceof Error?e.message:t;let n=e.body.readiness;return[n.reason||e.message,n.nextAction?`下一步:${n.nextAction}`:null].filter(Boolean).join(`;`)}async function Oi(e){return Ei(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify(e)})}function ki(e,t={}){return Ei(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":wi.csrfCapability}})}function Ai(e){return e==null||!Number.isFinite(e)?`暂无`:e<60?`${e}s 前`:e<3600?`${Math.floor(e/60)}m 前`:e<86400?`${Math.floor(e/3600)}h 前`:`${Math.floor(e/86400)}d 前`}function ji(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function Mi(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}function Ni(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Pi(){let[e,t]=(0,C.useState)(null),[n,r]=(0,C.useState)(null),[i,a]=(0,C.useState)(!0),[o,s]=(0,C.useState)(!1),c=(0,C.useRef)(Se()),l=(0,C.useRef)(null),u=(0,C.useRef)(null),d=(0,C.useCallback)(async()=>{u.current?.abort();let e=new AbortController;u.current=e;let n=c.current.issue();try{let i=await Ei(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(wi.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==wi.buildFingerprint){window.location.reload();return}t(i),r(null),s(!1)}catch(t){if(e.signal.aborted||!c.current.accepts(n))return;r(t instanceof Error?t.message:String(t)),s(!0)}finally{c.current.accepts(n)&&a(!1)}},[]);return(0,C.useEffect)(()=>{let e=!0,t=async()=>{await d(),e&&(l.current=window.setTimeout(t,3e3))};return t(),()=>{e=!1,l.current!==null&&window.clearTimeout(l.current),u.current?.abort()}},[d]),{data:e,error:n,loading:i,stale:o,retry:d}}function Fi({agent:e}){let t=(e.displayName||e.name||e.agentId).slice(0,2).toUpperCase(),[n,r]=(0,C.useState)(!1);(0,C.useEffect)(()=>r(!1),[e.agentId,e.bot?.hasAvatar]);let i=e.bot?.hasAvatar&&!n;return(0,A.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,A.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function Ii({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>be(e,i),[e,i]),s=(0,C.useRef)([]),c=(e,t)=>{if(!o.length)return;let n=t===0?e:(e+t+o.length)%o.length;s.current[n]?.focus()};return(0,A.jsxs)(`div`,{className:_i(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,A.jsxs)(`label`,{className:`agent-search`,children:[(0,A.jsx)(de,{size:15,"aria-hidden":`true`}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,A.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,A.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,A.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:_i(`agent-list-item`,e.agentId===t&&`selected`),onClick:()=>n(e.agentId),onKeyDown:e=>{e.key===`ArrowDown`&&(e.preventDefault(),c(r,1)),e.key===`ArrowUp`&&(e.preventDefault(),c(r,-1)),e.key===`Home`&&(e.preventDefault(),c(0,0)),e.key===`End`&&(e.preventDefault(),c(o.length-1,0))},children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,A.jsx)(`strong`,{children:e.displayName||e.name}),(0,A.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,A.jsxs)(`span`,{className:`status-line`,children:[(0,A.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ni(e)]})]}),e.issue?(0,A.jsx)(k,{size:15,className:`danger`,"aria-label":`异常`}):(0,A.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,A.jsx)(xi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function Li({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)(!1),[l,u]=(0,C.useState)(null),d=a!==r;return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return c(!0),ki(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),u(null)}).catch(e=>{t.signal.aborted||u(e.message)}).finally(()=>{t.signal.aborted||c(!1)}),()=>t.abort()},[e]),(0,A.jsx)(bi,{open:e,onOpenChange:e=>{!e&&d&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,A.jsxs)(`div`,{className:`settings-form`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`真人群消息默认策略`}),(0,A.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:s,children:[(0,A.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,A.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,A.jsxs)(`div`,{className:`form-actions`,children:[(0,A.jsx)(vi,{onClick:()=>o(r),disabled:!d||s,children:`放弃草稿`}),(0,A.jsx)(vi,{className:`primary`,onClick:async()=>{c(!0);try{let e=await Oi({operation:`set-global-mention`,value:a});i(a),u(`已保存 · ${e.applyState} · ${e.revision.slice(0,20)}…`),n()}catch(e){u(e instanceof Error?e.message:String(e))}finally{c(!1)}},disabled:!d||s,children:s?`保存中…`:`保存全局设置`})]}),l?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:l}):null]})})}function Ri({config:e,onSave:t,disabled:n}){let[r,i]=(0,C.useState)(``),[a,o]=(0,C.useState)(``),[s,c]=(0,C.useState)(`require`),l=e.knownChats.filter(e=>e.kind===`group`&&e.override!==`inherit`).filter(e=>{let t=r.trim().toLocaleLowerCase();return!t||e.chatId.toLocaleLowerCase().includes(t)||String(e.displayName||`未解析群名`).toLocaleLowerCase().includes(t)});return(0,A.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,A.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,A.jsxs)(`label`,{className:`table-search`,children:[(0,A.jsx)(de,{size:14}),(0,A.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,A.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,A.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,A.jsxs)(`table`,{className:`policy-table`,children:[(0,A.jsx)(`thead`,{children:(0,A.jsxs)(`tr`,{children:[(0,A.jsx)(`th`,{children:`群`}),(0,A.jsx)(`th`,{children:`消息策略`}),(0,A.jsx)(`th`,{children:`操作`})]})}),(0,A.jsx)(`tbody`,{children:l.map(e=>(0,A.jsxs)(`tr`,{children:[(0,A.jsxs)(`td`,{children:[(0,A.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,A.jsx)(`code`,{children:e.chatId}):(0,A.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,A.jsx)(`td`,{children:(0,A.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,A.jsx)(`td`,{children:(0,A.jsx)(vi,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,A.jsx)(xi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,A.jsxs)(`div`,{className:`chat-add`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`完整 chat_id`}),(0,A.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`消息策略`}),(0,A.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,A.jsx)(vi,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function zi({agentId:e,onDirtyChange:t,refreshKey:n}){let[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)({}),[s,c]=(0,C.useState)({}),[l,u]=(0,C.useState)(!0),[d,f]=(0,C.useState)(null),[p,m]=(0,C.useState)(null),h=(0,C.useRef)(Se()),g=(0,C.useRef)(Se()),_=!xe(a,s);(0,C.useEffect)(()=>t(_),[_,t]),(0,C.useEffect)(()=>{let e=e=>{_&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[_]);let v=(0,C.useCallback)(async(t=!1)=>{let n=h.current.issue();u(!0);try{let r=await ki(`/api/config?agent=${encodeURIComponent(e)}`);if(!h.current.accepts(n))return;let a=r.agents[0];if(!a)throw Error(`配置投影中没有当前 Agent`);let s={runtime:a.runtimeOption||a.runtime,model:a.model,effort:a.effort||`default`,mention:a.mention.override};i(r),o(s),(!t||!_)&&c(s)}catch(e){h.current.accepts(n)&&f(e instanceof Error?e.message:String(e))}finally{h.current.accepts(n)&&u(!1)}},[e,_]);(0,C.useEffect)(()=>{v(!1)},[e]),(0,C.useEffect)(()=>{n>0&&v(!0)},[n]);let y=r?.agents[0],b=(e,t)=>c(n=>({...n,[e]:t})),x=String(s.runtime||y?.runtime||``),S=String(s.model||y?.model||`default`);(0,C.useEffect)(()=>{if(m({runtime:x,status:`loading`,models:[]}),!new Set([`pi`,`builtin-pi`,`codex`,`claude`]).has(x)){m({runtime:x,status:`error`,models:[]});return}let t=g.current.issue(),n=new AbortController;return ki(`/api/models/${x}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{g.current.accepts(t)&&m({runtime:x,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&g.current.accepts(t)&&(m({runtime:x,status:`error`,models:[]}),f(`${x} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,x]);let w=p?.runtime===x&&p.status===`ready`,ee=p?.runtime===x?p.status:`loading`,te=w?p.models:[],T=`${S} · 当前配置(模型目录${ee===`error`?`不可用`:`加载中`})`,E=w?te.some(e=>e.id===S)?te:[{id:S,label:`${S} · 当前配置`},...te]:[{id:S,label:T}],ne=w&&S!=="default"&&te.find(e=>e.id===S)?.supportedReasoningEfforts||[],re=s.runtime!==a.runtime||s.model!==a.model||s.effort!==a.effort,ie=()=>Ei(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":wi.csrfCapability},body:JSON.stringify({agentId:e})}),ae=async()=>{if(y){u(!0);try{let t=[];s.runtime===a.runtime?s.model!==a.model&&t.push({operation:`set-agent-model`,agentId:e,model:s.model}):t.push({operation:`set-agent-runtime`,agentId:e,runtime:s.runtime,model:s.model}),s.effort!==a.effort&&t.push({operation:`set-agent-effort`,agentId:e,effort:s.effort}),s.mention!==a.mention&&t.push({operation:`set-agent-mention`,agentId:e,value:s.mention});let n=null;for(let e of t)n=await Oi(e);if(re)try{await ie(),f(`配置已保存并应用`)}catch(e){f(`配置已保存但未应用:${Di(e,`稍后重试`)}`)}else f(n?`配置已保存`:`没有需要保存的修改`);await v(!1)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}}},oe=async()=>{u(!0);try{await ie(),f(`已应用保存的运行配置`),await v(!0)}catch(e){f(`配置保持已保存但未应用:${Di(e,`稍后重试`)}`)}finally{u(!1)}},se=async(t,n)=>{u(!0);try{let r=await Oi({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});f(`群策略已保存 · ${r.applyState}`),await v(!0)}catch(e){f(e instanceof Error?e.message:String(e))}finally{u(!1)}};if(!y&&l)return(0,A.jsx)(xi,{title:`加载 Agent 配置…`});if(!y)return(0,A.jsx)(xi,{title:`配置不可用`,detail:d||`请重试`});let ce=y.apply.applyState===`pending`?`待应用`:y.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,A.jsxs)(`div`,{className:`configuration-page`,children:[(0,A.jsxs)(`section`,{className:`config-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`Agent 配置`}),(0,A.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,A.jsx)(yi,{className:y.apply.applyState===`pending`?`warning`:`success`,children:ce})]}),(0,A.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,A.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),(0,A.jsxs)(`div`,{className:`config-grid`,children:[(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Runtime`}),(0,A.jsx)(`select`,{value:x,disabled:l,onChange:e=>{b(`runtime`,e.target.value),b(`model`,`default`),b(`effort`,`default`)},children:(r?.runtimeOptions||Si).map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Model`}),(0,A.jsx)(`select`,{value:S,disabled:l||!w,onChange:e=>{b(`model`,e.target.value),e.target.value==="default"&&b(`effort`,`default`)},children:E.map(e=>(0,A.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`Effort`}),(0,A.jsxs)(`select`,{value:String(s.effort||`default`),disabled:l||!w||S==="default"||!ne.length,onChange:e=>b(`effort`,e.target.value),children:[(0,A.jsx)(`option`,{value:`default`,children:`default · 不指定`}),ne.map(e=>(0,A.jsx)(`option`,{value:e,children:e},e))]})]}),(0,A.jsxs)(`label`,{children:[(0,A.jsx)(`span`,{children:`群消息策略`}),(0,A.jsxs)(`select`,{value:String(s.mention||`inherit`),disabled:l,onChange:e=>b(`mention`,e.target.value),children:[(0,A.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,A.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,A.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),_||y.apply.applyState===`pending`?(0,A.jsxs)(`div`,{className:`form-actions`,children:[_?(0,A.jsx)(vi,{disabled:l,onClick:()=>c(a),children:`放弃草稿`}):null,_?(0,A.jsx)(vi,{className:`primary`,disabled:l,onClick:ae,children:l?`处理中…`:re?`保存并应用`:`保存 Agent 配置`}):(0,A.jsxs)(vi,{className:`primary`,disabled:l,onClick:oe,children:[(0,A.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,d?(0,A.jsx)(`p`,{role:`status`,className:`feedback`,children:d}):null]}),(0,A.jsx)(Ri,{config:y,onSave:se,disabled:l})]})}function Bi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,A.jsxs)(`div`,{className:`overview-page`,children:[(0,A.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ie,{})}),(0,A.jsx)(`span`,{children:`连接`}),(0,A.jsx)(`strong`,{children:e.connection.state}),(0,A.jsx)(`small`,{children:e.connection.reason})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`执行状态`}),(0,A.jsx)(`strong`,{children:Ni(e)}),(0,A.jsx)(`small`,{children:e.lastActivity?Ai(e.lastActivity.ageSec):`尚无活动`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(le,{})}),(0,A.jsx)(`span`,{children:`最近消息`}),(0,A.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,A.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${Ai(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`上下文`}),(0,A.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,A.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ce,{})}),(0,A.jsx)(`span`,{children:`累计 Token`}),(0,A.jsx)(`strong`,{children:Mi(t.cumulativeTokens)}),(0,A.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(E,{})}),(0,A.jsx)(`span`,{children:`最近一轮用量`}),(0,A.jsx)(`strong`,{children:Mi(t.latestTokens)}),(0,A.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,A.jsxs)(`section`,{className:`metric-stat`,children:[(0,A.jsx)(`div`,{className:`metric-icon`,children:(0,A.jsx)(ue,{})}),(0,A.jsx)(`span`,{children:`Compaction`}),(0,A.jsx)(`strong`,{children:Mi(n.count)}),(0,A.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${ji(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,A.jsxs)(`div`,{className:`overview-sections`,children:[(0,A.jsxs)(`section`,{className:`content-section wide`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`最近动态`}),(0,A.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed.slice(0,10)})]}),(0,A.jsxs)(`section`,{className:`content-section`,children:[(0,A.jsx)(`h3`,{children:`运行摘要`}),(0,A.jsxs)(`dl`,{className:`facts`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Runtime`}),(0,A.jsx)(`dd`,{children:e.runtime})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Readiness`}),(0,A.jsx)(`dd`,{children:e.runtimeReadiness?`${e.runtimeReadiness.state}${e.runtimeReadiness.version?` · ${e.runtimeReadiness.version}`:``}${e.runtimeReadiness.executable?` · ${e.runtimeReadiness.executable}`:``}${e.runtimeReadiness.reason?` · ${e.runtimeReadiness.reason}`:``}${e.runtimeReadiness.nextAction?` · ${e.runtimeReadiness.nextAction}`:``}`:`unknown`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Model`}),(0,A.jsx)(`dd`,{children:e.model})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`Effort`}),(0,A.jsx)(`dd`,{children:e.effort||`default`})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`入站`}),(0,A.jsx)(`dd`,{children:e.inbound.state})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`提醒`}),(0,A.jsx)(`dd`,{children:e.activeReminders})]}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`dt`,{children:`指示灯`}),(0,A.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function M({items:e}){if(!e.length)return(0,A.jsx)(xi,{title:`还没有活动记录`});let t=e=>e.kind===`error`||e.state===`error`?`state-error`:e.kind===`deliver`?`state-deliver`:[`working`,`thinking`,`tool`].includes(String(e.state))?`state-active`:[`idle`,`online`].includes(String(e.state))?`state-idle`:`state-unknown`;return(0,A.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,A.jsxs)(`li`,{children:[(0,A.jsx)(`span`,{className:_i(`timeline-dot`,t(e))}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,A.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,A.jsx)(`time`,{children:ji(e.at)})]})]},`${e.at}-${n}`))})}function N({agent:e}){return e.conversation.length?(0,A.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,A.jsxs)(`article`,{className:_i(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,A.jsxs)(`header`,{children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(yi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,A.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,A.jsx)(`time`,{children:ji(t.at)})]}),(0,A.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,A.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,A.jsx)(xi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Vi({agent:e}){if(!e.remindersList.length)return(0,A.jsx)(xi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,A.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,A.jsxs)(`article`,{className:_i(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,A.jsx)(ne,{size:16}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,A.jsxs)(`p`,{children:[ji(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,A.jsx)(yi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Hi({agentId:e}){let[t,n]=(0,C.useState)(null),[r,i]=(0,C.useState)(null),[a,o]=(0,C.useState)(!0),[s,c]=(0,C.useState)(null),l=(0,C.useRef)(Se()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ei(`/api/workspace?agent=${encodeURIComponent(e)}&path=${encodeURIComponent(t)}`);if(!l.current.accepts(r))return;a.kind===`directory`?(n(a),i(null)):i(a),c(null)}catch(e){l.current.accepts(r)&&c(e instanceof Error?e.message:String(e))}finally{l.current.accepts(r)&&o(!1)}},[e]);(0,C.useEffect)(()=>{u(``)},[u]);let d=t?.path.split(`/`).filter(Boolean)||[];return(0,A.jsxs)(`div`,{className:`workspace-browser`,children:[(0,A.jsxs)(`div`,{className:`workspace-side`,children:[(0,A.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,A.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,A.jsxs)(`span`,{children:[(0,A.jsx)(re,{size:12}),(0,A.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,A.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,A.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,A.jsx)(se,{size:15}):(0,A.jsx)(oe,{size:15}),(0,A.jsx)(`span`,{children:e.name}),(0,A.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,A.jsx)(xi,{title:`空目录`}):null]})]}),(0,A.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,A.jsx)(xi,{title:`读取工作区…`}):s?(0,A.jsx)(xi,{title:`工作区读取失败`,detail:s}):r?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsxs)(`header`,{children:[(0,A.jsx)(`strong`,{children:r.name}),(0,A.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,A.jsx)(xi,{title:`二进制文件不可预览`}):(0,A.jsx)(`pre`,{children:r.content})]}):(0,A.jsx)(xi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function Ui({agent:e}){return(0,A.jsxs)(`section`,{className:`logs-section`,children:[(0,A.jsxs)(`div`,{className:`section-heading`,children:[(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`h3`,{children:`结构化日志`}),(0,A.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,A.jsx)(yi,{children:e.feed.length})]}),(0,A.jsx)(M,{items:e.feed}),e.recentErrors.length?(0,A.jsxs)(`section`,{className:`error-stack`,children:[(0,A.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,A.jsxs)(`div`,{children:[(0,A.jsx)(ae,{size:14}),(0,A.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,A.jsx)(`time`,{children:ji(e.at)})]},`${e.at}-${t}`))]}):null]})}function Wi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{},r=t.contextPercent==null?`上下文 ${e.session?.turns||0} turns`:`上下文 ${Math.round(Number(t.contextPercent))}% · ${Mi(t.latestTokens)} / ${Mi(t.contextWindow)}`;return(0,A.jsxs)(`header`,{className:`agent-header`,children:[(0,A.jsxs)(`div`,{className:`agent-title`,children:[(0,A.jsx)(Fi,{agent:e}),(0,A.jsxs)(`div`,{children:[(0,A.jsx)(`p`,{children:e.agentId}),(0,A.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,A.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,A.jsx)(yi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,A.jsx)(`span`,{children:e.runtime}),(0,A.jsx)(`span`,{children:e.model}),(0,A.jsx)(`span`,{children:e.effort||`default effort`}),(0,A.jsx)(`span`,{children:r}),(0,A.jsxs)(`span`,{children:[`累计 `,Mi(t.cumulativeTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`最近一轮 `,Mi(t.latestTokens),` tokens`]}),(0,A.jsxs)(`span`,{children:[`Compact `,Mi(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,A.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?Ai(e.lastDeliver.ageSec):`暂无`]}),(0,A.jsx)(`span`,{children:e.lastActivity?.detail||Ni(e)})]})]})}function Gi(){let e=Pi(),t=(0,C.useMemo)(()=>_e(window.location.search),[]),[n,r]=(0,C.useState)(t.agentId),[i,a]=(0,C.useState)(t.tab),[o,s]=(0,C.useState)(!1),[c,l]=(0,C.useState)(!1),[u,d]=(0,C.useState)(0),[f,p]=(0,C.useState)(!1),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(new Map),_=e.data?.agents||[],v=e.data?.packageVersion||wi.packageVersion,y=_.find(e=>e.agentId===n)||null,b=(0,C.useCallback)((e,t)=>{let n=`${e}:${t}`,r=window.scrollY;g.current.set(n,r);try{sessionStorage.setItem(`larkin-dashboard-scroll:${n}`,String(r))}catch{}},[]),x=(0,C.useCallback)(e=>{if(g.current.has(e))return g.current.get(e)||0;try{let t=Number(sessionStorage.getItem(`larkin-dashboard-scroll:${e}`));return Number.isFinite(t)&&t>=0?t:0}catch{return 0}},[]),S=(0,C.useCallback)((e,t,o=!1)=>{n&&b(n,i);let s=o?`replaceState`:`pushState`;window.history[s](null,``,ve(e,t)),r(e),a(t)},[b,n,i]),w=()=>!f||window.confirm(`Agent 配置尚未保存,确定放弃草稿吗?`),ee=e=>{w()&&(p(!1),S(e,i),l(!1))},te=e=>{e!==`configuration`&&!w()||(e!==`configuration`&&p(!1),S(n,e))};return(0,C.useEffect)(()=>{if(!e.data)return;let t=ye(n,e.data.agents);t!==n&&(h(n?`原 Agent 已不存在,已切换到第一个可用 Agent。`:null),S(t,i,!0))},[e.data,n,i,S]),(0,C.useEffect)(()=>{let e=()=>{let e=_e(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,ve(n,i));return}n&&b(n,i),p(!1),r(e.agentId),a(e.tab)};return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},[f,b,n,i]),(0,C.useLayoutEffect)(()=>{if(!n)return;let e=`${n}:${i}`,t=x(e);window.scrollY!==t&&window.scrollTo({top:t,behavior:`auto`})},[x,n,i]),(0,A.jsxs)(`div`,{className:_i(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,A.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,A.jsxs)(`div`,{className:`brand`,children:[(0,A.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,A.jsx)(Ii,{agents:_,selectedId:n,onSelect:ee}),(0,A.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,A.jsxs)(`main`,{className:_i(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,A.jsxs)(`div`,{className:`app-topbar`,children:[(0,A.jsx)(vi,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,A.jsx)(O,{size:18})}),(0,A.jsxs)(`div`,{children:[(0,A.jsxs)(`strong`,{children:[`Larkin `,(0,A.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,A.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,A.jsx)(yi,{className:`warning`,children:`状态陈旧`}):null]}),(0,A.jsxs)(vi,{onClick:()=>s(!0),children:[(0,A.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,A.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,A.jsx)(k,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,A.jsx)(vi,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,A.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,A.jsx)(vi,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,A.jsxs)(`div`,{className:`page-loading`,children:[(0,A.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,A.jsxs)(A.Fragment,{children:[(0,A.jsx)(Wi,{agent:y}),(0,A.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:ge.map(e=>(0,A.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,A.jsx)(ce,{}):e===`conversation`?(0,A.jsx)(le,{}):e===`configuration`?(0,A.jsx)(pe,{}):e===`reminders`?(0,A.jsx)(ne,{}):e===`workspace`?(0,A.jsx)(se,{}):(0,A.jsx)(D,{}),Ci[e]]},e))}),(0,A.jsxs)(`div`,{className:_i(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,A.jsx)(Bi,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,A.jsx)(N,{agent:y})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,A.jsx)(zi,{agentId:y.agentId,onDirtyChange:p,refreshKey:u})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,A.jsx)(Vi,{agent:y})}),(0,A.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,A.jsx)(Hi,{agentId:y.agentId})}),(0,A.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,A.jsx)(Ui,{agent:y})})]},y.agentId)]}):(0,A.jsx)(xi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,A.jsx)(Li,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,A.jsx)(bi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,A.jsx)(Ii,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Ki=document.getElementById(`root`);if(!Ki)throw Error(`dashboard root is missing`);(0,he.createRoot)(Ki).render((0,A.jsx)(C.StrictMode,{children:(0,A.jsx)(Gi,{})}));
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.toUserRuntime = exports.runtimeOptionTarget = exports.runtimeOptionOf = exports.isUserRuntime = exports.isAdapterRuntime = exports.fromUserRuntime = exports.RUNTIME_OPTIONS = void 0;
39
40
  exports.loadRuntimeModels = loadRuntimeModels;
40
41
  exports.defaultModelFor = defaultModelFor;
41
42
  exports.resolveConfigDir = resolveConfigDir;
@@ -63,8 +64,18 @@ const path = __importStar(require("node:path"));
63
64
  const root_layout_js_1 = require("./root-layout.cjs");
64
65
  const secure_metadata_js_1 = require("./secure-metadata.cjs");
65
66
  const runtime_model_catalog_js_1 = require("../runtime/runtime-model-catalog.cjs");
67
+ const pi_provider_config_js_1 = require("../runtime/pi-provider-config.cjs");
68
+ const user_runtime_js_1 = require("../runtime/user-runtime.cjs");
66
69
  const process_inspect_cjs_1 = __importDefault(require("./process-inspect.cjs"));
67
70
  const pi_profile_migration_js_1 = require("../runtime/pi-profile-migration.cjs");
71
+ var user_runtime_js_2 = require("../runtime/user-runtime.cjs");
72
+ Object.defineProperty(exports, "RUNTIME_OPTIONS", { enumerable: true, get: function () { return user_runtime_js_2.RUNTIME_OPTIONS; } });
73
+ Object.defineProperty(exports, "fromUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.fromUserRuntime; } });
74
+ Object.defineProperty(exports, "isAdapterRuntime", { enumerable: true, get: function () { return user_runtime_js_2.isAdapterRuntime; } });
75
+ Object.defineProperty(exports, "isUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.isUserRuntime; } });
76
+ Object.defineProperty(exports, "runtimeOptionOf", { enumerable: true, get: function () { return user_runtime_js_2.runtimeOptionOf; } });
77
+ Object.defineProperty(exports, "runtimeOptionTarget", { enumerable: true, get: function () { return user_runtime_js_2.runtimeOptionTarget; } });
78
+ Object.defineProperty(exports, "toUserRuntime", { enumerable: true, get: function () { return user_runtime_js_2.toUserRuntime; } });
68
79
  const TOP_FIELDS_V3 = new Set(["version", "serverId", "activeAgent", "agents"]);
69
80
  const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAgent", "agents"]);
70
81
  const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
@@ -105,7 +116,8 @@ function loadRuntimeModels() {
105
116
  return runtimeModels;
106
117
  }
107
118
  function defaultModelFor(runtime) {
108
- const models = loadRuntimeModels()[runtime];
119
+ const adapter = (0, user_runtime_js_1.isUserRuntime)(runtime) ? (0, user_runtime_js_1.fromUserRuntime)(runtime).runtime : runtime;
120
+ const models = loadRuntimeModels()[adapter];
109
121
  if (!models)
110
122
  throw new Error(`runtime 模型目录不存在 runtime: ${runtime}`);
111
123
  return models[0].id;
@@ -153,7 +165,7 @@ function validateStoredAgent(key, agent, version) {
153
165
  assertAllowedFields(agent, version === 3 ? AGENT_FIELDS_V3 : AGENT_FIELDS_V4, `Agent ${key}`);
154
166
  if (typeof agent.runtime !== "string" || !agent.runtime)
155
167
  throw new Error(`Agent ${key}.runtime 必须是非空字符串`);
156
- if (!loadRuntimeModels()[agent.runtime])
168
+ if (!(0, user_runtime_js_1.isAdapterRuntime)(agent.runtime))
157
169
  throw new Error(`Agent ${key}.runtime 未知:${agent.runtime}`);
158
170
  if (typeof agent.model !== "string" || !agent.model)
159
171
  throw new Error(`Agent ${key}.model 必须是非空字符串`);
@@ -632,12 +644,23 @@ function applyMutation(config, mutation) {
632
644
  agent.piDistribution = mutation.distribution;
633
645
  }
634
646
  else if (mutation.kind === "set-agent-runtime") {
635
- if (!loadRuntimeModels()[mutation.runtime])
647
+ if (!(0, user_runtime_js_1.isUserRuntime)(mutation.runtime))
636
648
  throw new Error(`未知 runtime:${mutation.runtime}`);
637
- assertModel(mutation.runtime, mutation.model || "default");
638
- agent.runtime = mutation.runtime;
649
+ const stored = (0, user_runtime_js_1.fromUserRuntime)(mutation.runtime);
650
+ if (stored.piDistribution === "builtin") {
651
+ try {
652
+ (0, pi_provider_config_js_1.assertBuiltinPiAgentDirectory)((0, pi_provider_config_js_1.piAgentDirectory)(config.configDir, mutation.agentId));
653
+ }
654
+ catch (error) {
655
+ throw new Error(`无法切换到 builtin-pi:${error instanceof Error ? error.message : String(error)}。请先运行 larkin setup 并为该 Agent 选择 builtin-pi 完成 provider 配置`);
656
+ }
657
+ }
658
+ assertModel(stored.runtime, mutation.model || "default");
659
+ agent.runtime = stored.runtime;
639
660
  agent.model = mutation.model || "default";
640
- if (mutation.runtime !== "pi")
661
+ if (stored.piDistribution)
662
+ agent.piDistribution = stored.piDistribution;
663
+ else
641
664
  delete agent.piDistribution;
642
665
  delete agent.effort;
643
666
  }
@@ -1105,7 +1128,7 @@ function commitSetupConfig(env, expectedRevision, nextStored) {
1105
1128
  function safeConfigView(config, onlyAgentId, chatId, applyState) {
1106
1129
  const entries = onlyAgentId ? [[onlyAgentId, config.agents[onlyAgentId]]] : Object.entries(config.agents);
1107
1130
  const agents = entries.filter((entry) => Boolean(entry[1])).map(([agentId, agent]) => ({
1108
- agentId, runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
1131
+ agentId, runtime: agent.runtime, runtimeOption: (0, user_runtime_js_1.toUserRuntime)(agent.runtime, agent.piDistribution), model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
1109
1132
  mention: {
1110
1133
  override: agent.mentionPolicy ?? "inherit",
1111
1134
  effective: agent.mentionPolicy ?? config.mentionPolicy,
@@ -5,8 +5,11 @@ import * as path from "node:path";
5
5
  import { TargetRootLayout, resolveConfigDir as resolveRootConfigDir } from "./root-layout.mjs";
6
6
  import { exactMode, fsyncDirectoryOf } from "./secure-metadata.mjs";
7
7
  import { CURRENT_RUNTIME_MODELS } from "../runtime/runtime-model-catalog.mjs";
8
+ import { assertBuiltinPiAgentDirectory, piAgentDirectory } from "../runtime/pi-provider-config.mjs";
9
+ import { fromUserRuntime, isAdapterRuntime, isUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
8
10
  import processInspect from "./process-inspect.cjs";
9
11
  import { applyPiProfileMigration, assertPiProfileMigrationAfterState, preparePiProfileMigration, releasePiProfileMigrationLock, rollbackPiProfileMigration, validatePiProfileMigrationState, } from "../runtime/pi-profile-migration.mjs";
12
+ export { RUNTIME_OPTIONS, fromUserRuntime, isAdapterRuntime, isUserRuntime, runtimeOptionOf, runtimeOptionTarget, toUserRuntime, } from "../runtime/user-runtime.mjs";
10
13
  const TOP_FIELDS_V3 = new Set(["version", "serverId", "activeAgent", "agents"]);
11
14
  const TOP_FIELDS_V4 = new Set(["version", "serverId", "mentionPolicy", "activeAgent", "agents"]);
12
15
  const AGENT_FIELDS_V3 = new Set(["runtime", "model", "effort", "noMentionChats", "createdAt"]);
@@ -47,7 +50,8 @@ export function loadRuntimeModels() {
47
50
  return runtimeModels;
48
51
  }
49
52
  export function defaultModelFor(runtime) {
50
- const models = loadRuntimeModels()[runtime];
53
+ const adapter = isUserRuntime(runtime) ? fromUserRuntime(runtime).runtime : runtime;
54
+ const models = loadRuntimeModels()[adapter];
51
55
  if (!models)
52
56
  throw new Error(`runtime 模型目录不存在 runtime: ${runtime}`);
53
57
  return models[0].id;
@@ -95,7 +99,7 @@ function validateStoredAgent(key, agent, version) {
95
99
  assertAllowedFields(agent, version === 3 ? AGENT_FIELDS_V3 : AGENT_FIELDS_V4, `Agent ${key}`);
96
100
  if (typeof agent.runtime !== "string" || !agent.runtime)
97
101
  throw new Error(`Agent ${key}.runtime 必须是非空字符串`);
98
- if (!loadRuntimeModels()[agent.runtime])
102
+ if (!isAdapterRuntime(agent.runtime))
99
103
  throw new Error(`Agent ${key}.runtime 未知:${agent.runtime}`);
100
104
  if (typeof agent.model !== "string" || !agent.model)
101
105
  throw new Error(`Agent ${key}.model 必须是非空字符串`);
@@ -574,12 +578,23 @@ function applyMutation(config, mutation) {
574
578
  agent.piDistribution = mutation.distribution;
575
579
  }
576
580
  else if (mutation.kind === "set-agent-runtime") {
577
- if (!loadRuntimeModels()[mutation.runtime])
581
+ if (!isUserRuntime(mutation.runtime))
578
582
  throw new Error(`未知 runtime:${mutation.runtime}`);
579
- assertModel(mutation.runtime, mutation.model || "default");
580
- agent.runtime = mutation.runtime;
583
+ const stored = fromUserRuntime(mutation.runtime);
584
+ if (stored.piDistribution === "builtin") {
585
+ try {
586
+ assertBuiltinPiAgentDirectory(piAgentDirectory(config.configDir, mutation.agentId));
587
+ }
588
+ catch (error) {
589
+ throw new Error(`无法切换到 builtin-pi:${error instanceof Error ? error.message : String(error)}。请先运行 larkin setup 并为该 Agent 选择 builtin-pi 完成 provider 配置`);
590
+ }
591
+ }
592
+ assertModel(stored.runtime, mutation.model || "default");
593
+ agent.runtime = stored.runtime;
581
594
  agent.model = mutation.model || "default";
582
- if (mutation.runtime !== "pi")
595
+ if (stored.piDistribution)
596
+ agent.piDistribution = stored.piDistribution;
597
+ else
583
598
  delete agent.piDistribution;
584
599
  delete agent.effort;
585
600
  }
@@ -1047,7 +1062,7 @@ export function commitSetupConfig(env, expectedRevision, nextStored) {
1047
1062
  export function safeConfigView(config, onlyAgentId, chatId, applyState) {
1048
1063
  const entries = onlyAgentId ? [[onlyAgentId, config.agents[onlyAgentId]]] : Object.entries(config.agents);
1049
1064
  const agents = entries.filter((entry) => Boolean(entry[1])).map(([agentId, agent]) => ({
1050
- agentId, runtime: agent.runtime, model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
1065
+ agentId, runtime: agent.runtime, runtimeOption: toUserRuntime(agent.runtime, agent.piDistribution), model: agent.model, piDistribution: agent.piDistribution ?? (agent.runtime === "pi" ? "external" : null), effort: agent.effort ?? null,
1051
1066
  mention: {
1052
1067
  override: agent.mentionPolicy ?? "inherit",
1053
1068
  effective: agent.mentionPolicy ?? config.mentionPolicy,
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CURRENT_RUNTIME_MODELS = void 0;
4
- // Larkin supports exactly these three native Runtime adapters. Pi, Codex and Claude replace
5
- // these placeholders/fallbacks at runtime with their machine-readable catalogs.
4
+ // Authored catalog keys are adapter ids only. builtin-pi is a user-facing sibling, not a stored runtime.
5
+ // Pi, Codex and Claude replace these placeholders/fallbacks at runtime with their machine-readable catalogs.
6
6
  // Claude's supported stdin control-channel list is the live authority; its entries below
7
7
  // remain explicit compatibility candidates for config loading and discovery failures.
8
+ const PI_AUTHORED_MODELS = [{ id: "default", label: "default", verified: "dynamic" }];
8
9
  exports.CURRENT_RUNTIME_MODELS = {
9
10
  codex: [
10
11
  { id: "default", label: "default", verified: "dynamic" },
@@ -39,5 +40,5 @@ exports.CURRENT_RUNTIME_MODELS = {
39
40
  { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 · 静态候选", verified: "authored-candidate" },
40
41
  { id: "claude-haiku-4-5", label: "Claude Haiku 4.5 · 静态候选", verified: "authored-candidate" },
41
42
  ],
42
- pi: [{ id: "default", label: "default", verified: "dynamic" }],
43
+ pi: [...PI_AUTHORED_MODELS],
43
44
  };
@@ -1,7 +1,8 @@
1
- // Larkin supports exactly these three native Runtime adapters. Pi, Codex and Claude replace
2
- // these placeholders/fallbacks at runtime with their machine-readable catalogs.
1
+ // Authored catalog keys are adapter ids only. builtin-pi is a user-facing sibling, not a stored runtime.
2
+ // Pi, Codex and Claude replace these placeholders/fallbacks at runtime with their machine-readable catalogs.
3
3
  // Claude's supported stdin control-channel list is the live authority; its entries below
4
4
  // remain explicit compatibility candidates for config loading and discovery failures.
5
+ const PI_AUTHORED_MODELS = [{ id: "default", label: "default", verified: "dynamic" }];
5
6
  export const CURRENT_RUNTIME_MODELS = {
6
7
  codex: [
7
8
  { id: "default", label: "default", verified: "dynamic" },
@@ -36,5 +37,5 @@ export const CURRENT_RUNTIME_MODELS = {
36
37
  { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6 · 静态候选", verified: "authored-candidate" },
37
38
  { id: "claude-haiku-4-5", label: "Claude Haiku 4.5 · 静态候选", verified: "authored-candidate" },
38
39
  ],
39
- pi: [{ id: "default", label: "default", verified: "dynamic" }],
40
+ pi: [...PI_AUTHORED_MODELS],
40
41
  };
@@ -42,8 +42,10 @@ exports.resolveRuntimeExecutable = resolveRuntimeExecutable;
42
42
  exports.classifyRuntimePrerequisite = classifyRuntimePrerequisite;
43
43
  exports.probeNativeRuntimeReadiness = probeNativeRuntimeReadiness;
44
44
  const node_fs_1 = __importDefault(require("node:fs"));
45
+ const node_os_1 = __importDefault(require("node:os"));
45
46
  const node_path_1 = __importDefault(require("node:path"));
46
47
  const node_child_process_1 = require("node:child_process");
48
+ const root_layout_js_1 = require("../platform/root-layout.cjs");
47
49
  const builtin_pi_assets_js_1 = require("./builtin-pi-assets.cjs");
48
50
  const pi_provider_config_js_1 = require("./pi-provider-config.cjs");
49
51
  const process_boundary_trace_js_1 = require("../platform/process-boundary-trace.cjs");
@@ -162,11 +164,27 @@ async function probeNativeRuntimeReadiness(options) {
162
164
  ? "Install Pi and ensure `pi` is on PATH, or set LARKIN_PI_COMMAND."
163
165
  : `Install ${options.runtime} and ensure it is on PATH.`,
164
166
  };
167
+ const piAgentId = options.agentId;
168
+ if (options.runtime === "pi" && !piAgentId) {
169
+ return {
170
+ runtime: "pi",
171
+ state: "unavailable",
172
+ executable,
173
+ reason: "Pi catalog readiness requires Agent ID",
174
+ nextAction: "Retry setup or start so Larkin can probe the Agent-owned Pi provider directory.",
175
+ };
176
+ }
165
177
  const version = executableVersion(executable, env, options.commandArgs);
166
178
  try {
167
179
  if (options.runtime === "pi") {
168
180
  const { discoverPiModelCatalog } = await Promise.resolve().then(() => __importStar(require("./pi-model-catalog.cjs")));
169
- await discoverPiModelCatalog({ cwd: options.cwd, command: executable, commandArgs: options.commandArgs, env });
181
+ await discoverPiModelCatalog({
182
+ cwd: options.cwd,
183
+ command: executable,
184
+ commandArgs: options.commandArgs,
185
+ env,
186
+ agentDir: (0, pi_provider_config_js_1.ownedPiCatalogAgentDir)((0, root_layout_js_1.resolveConfigDir)(env, node_os_1.default.homedir()), piAgentId),
187
+ });
170
188
  }
171
189
  else if (options.runtime === "codex") {
172
190
  const { discoverCodexModelCatalog } = await Promise.resolve().then(() => __importStar(require("./codex-model-catalog.cjs")));
@@ -1,8 +1,10 @@
1
1
  import fs from "node:fs";
2
+ import os from "node:os";
2
3
  import path from "node:path";
3
4
  import { spawnSync } from "node:child_process";
5
+ import { resolveConfigDir as resolveRootConfigDir } from "../platform/root-layout.mjs";
4
6
  import { applyPiPackageDirForChild, piChildDistributionFromOverrides } from "./builtin-pi-assets.mjs";
5
- import { assertBuiltinPiAgentDirectory, BUNDLED_PI_VERSION, piAgentDirectory } from "./pi-provider-config.mjs";
7
+ import { assertBuiltinPiAgentDirectory, BUNDLED_PI_VERSION, ownedPiCatalogAgentDir, piAgentDirectory } from "./pi-provider-config.mjs";
6
8
  import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
7
9
  function safeProviderLabel(value) {
8
10
  const provider = typeof value === "string" ? value.trim() : "";
@@ -118,11 +120,27 @@ export async function probeNativeRuntimeReadiness(options) {
118
120
  ? "Install Pi and ensure `pi` is on PATH, or set LARKIN_PI_COMMAND."
119
121
  : `Install ${options.runtime} and ensure it is on PATH.`,
120
122
  };
123
+ const piAgentId = options.agentId;
124
+ if (options.runtime === "pi" && !piAgentId) {
125
+ return {
126
+ runtime: "pi",
127
+ state: "unavailable",
128
+ executable,
129
+ reason: "Pi catalog readiness requires Agent ID",
130
+ nextAction: "Retry setup or start so Larkin can probe the Agent-owned Pi provider directory.",
131
+ };
132
+ }
121
133
  const version = executableVersion(executable, env, options.commandArgs);
122
134
  try {
123
135
  if (options.runtime === "pi") {
124
136
  const { discoverPiModelCatalog } = await import("./pi-model-catalog.mjs");
125
- await discoverPiModelCatalog({ cwd: options.cwd, command: executable, commandArgs: options.commandArgs, env });
137
+ await discoverPiModelCatalog({
138
+ cwd: options.cwd,
139
+ command: executable,
140
+ commandArgs: options.commandArgs,
141
+ env,
142
+ agentDir: ownedPiCatalogAgentDir(resolveRootConfigDir(env, os.homedir()), piAgentId),
143
+ });
126
144
  }
127
145
  else if (options.runtime === "codex") {
128
146
  const { discoverCodexModelCatalog } = await import("./codex-model-catalog.mjs");
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ADAPTER_RUNTIMES = exports.USER_RUNTIMES = exports.RUNTIME_OPTIONS = void 0;
4
+ exports.isUserRuntime = isUserRuntime;
5
+ exports.isAdapterRuntime = isAdapterRuntime;
6
+ exports.runtimeOptionOf = runtimeOptionOf;
7
+ exports.toUserRuntime = toUserRuntime;
8
+ exports.runtimeOptionTarget = runtimeOptionTarget;
9
+ exports.fromUserRuntime = fromUserRuntime;
10
+ exports.piCatalogDistributionForUserRuntime = piCatalogDistributionForUserRuntime;
11
+ exports.RUNTIME_OPTIONS = ["codex", "claude", "pi", "builtin-pi"];
12
+ exports.USER_RUNTIMES = exports.RUNTIME_OPTIONS;
13
+ exports.ADAPTER_RUNTIMES = ["pi", "codex", "claude"];
14
+ function isUserRuntime(value) {
15
+ return value === "pi" || value === "builtin-pi" || value === "codex" || value === "claude";
16
+ }
17
+ function isAdapterRuntime(value) {
18
+ return value === "pi" || value === "codex" || value === "claude";
19
+ }
20
+ /** Project stored adapter + distribution to the user-facing sibling id. Legacy `runtime=pi` without distribution is `pi`, never builtin. */
21
+ function runtimeOptionOf(input) {
22
+ const { runtime, piDistribution } = input;
23
+ if (runtime === "codex" || runtime === "claude" || runtime === "builtin-pi")
24
+ return runtime;
25
+ if (runtime === "pi")
26
+ return piDistribution === "builtin" ? "builtin-pi" : "pi";
27
+ return runtime;
28
+ }
29
+ function toUserRuntime(runtime, piDistribution) {
30
+ return runtimeOptionOf({ runtime, piDistribution });
31
+ }
32
+ /** Persist a user-facing sibling as adapter id + distribution. Adapter id stays `pi` for both Pi siblings. */
33
+ function runtimeOptionTarget(option) {
34
+ if (!isUserRuntime(option))
35
+ throw new Error(`未知 runtime:${option}`);
36
+ if (option === "builtin-pi")
37
+ return { runtime: "pi", piDistribution: "builtin" };
38
+ if (option === "pi")
39
+ return { runtime: "pi", piDistribution: "external" };
40
+ return { runtime: option };
41
+ }
42
+ function fromUserRuntime(userRuntime) {
43
+ return runtimeOptionTarget(userRuntime);
44
+ }
45
+ function piCatalogDistributionForUserRuntime(userRuntime) {
46
+ if (userRuntime === "builtin-pi")
47
+ return "builtin";
48
+ if (userRuntime === "pi")
49
+ return "external";
50
+ throw new Error(`非 Pi sibling runtime:${userRuntime}`);
51
+ }
@@ -0,0 +1,41 @@
1
+ export const RUNTIME_OPTIONS = ["codex", "claude", "pi", "builtin-pi"];
2
+ export const USER_RUNTIMES = RUNTIME_OPTIONS;
3
+ export const ADAPTER_RUNTIMES = ["pi", "codex", "claude"];
4
+ export function isUserRuntime(value) {
5
+ return value === "pi" || value === "builtin-pi" || value === "codex" || value === "claude";
6
+ }
7
+ export function isAdapterRuntime(value) {
8
+ return value === "pi" || value === "codex" || value === "claude";
9
+ }
10
+ /** Project stored adapter + distribution to the user-facing sibling id. Legacy `runtime=pi` without distribution is `pi`, never builtin. */
11
+ export function runtimeOptionOf(input) {
12
+ const { runtime, piDistribution } = input;
13
+ if (runtime === "codex" || runtime === "claude" || runtime === "builtin-pi")
14
+ return runtime;
15
+ if (runtime === "pi")
16
+ return piDistribution === "builtin" ? "builtin-pi" : "pi";
17
+ return runtime;
18
+ }
19
+ export function toUserRuntime(runtime, piDistribution) {
20
+ return runtimeOptionOf({ runtime, piDistribution });
21
+ }
22
+ /** Persist a user-facing sibling as adapter id + distribution. Adapter id stays `pi` for both Pi siblings. */
23
+ export function runtimeOptionTarget(option) {
24
+ if (!isUserRuntime(option))
25
+ throw new Error(`未知 runtime:${option}`);
26
+ if (option === "builtin-pi")
27
+ return { runtime: "pi", piDistribution: "builtin" };
28
+ if (option === "pi")
29
+ return { runtime: "pi", piDistribution: "external" };
30
+ return { runtime: option };
31
+ }
32
+ export function fromUserRuntime(userRuntime) {
33
+ return runtimeOptionTarget(userRuntime);
34
+ }
35
+ export function piCatalogDistributionForUserRuntime(userRuntime) {
36
+ if (userRuntime === "builtin-pi")
37
+ return "builtin";
38
+ if (userRuntime === "pi")
39
+ return "external";
40
+ throw new Error(`非 Pi sibling runtime:${userRuntime}`);
41
+ }
@@ -666,7 +666,8 @@ Lark 必须用 --tenant lark:授权二维码是官方 /page/cli(begin 仍走
666
666
  const choice = injectedAgentChoice
667
667
  ? requested
668
668
  : await recoverUnavailableExternalPi(requested, questioner, () => probeNativeRuntimeReadiness({
669
- runtime: "pi", agentId: id, cwd: path.join(CFG_DIR, "agents", id), env: process.env,
669
+ runtime: "pi", agentId: id, cwd: path.join(CFG_DIR, "agents", id),
670
+ env: { ...process.env, LARKIN_CONFIG_DIR: CFG_DIR, LARKIN_PI_DISTRIBUTION: "external" },
670
671
  }), (message) => say(`! ${message}`), authServices);
671
672
  if (choice) {
672
673
  let serializedChoice = choice;
@@ -718,10 +719,10 @@ Lark 必须用 --tenant lark:授权二维码是官方 /page/cli(begin 仍走
718
719
  const setupModel = flag("--model");
719
720
  if (piDistributionFlag === "builtin") {
720
721
  if (!setupProvider && !setupBaseUrl) {
721
- throw new Error(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_PRESETS.map((p) => p.id).join("|")}|custom)或 --base-url;或改用 --runtime external-pi 使用已有 pi 环境`);
722
+ throw new Error(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_PRESETS.map((p) => p.id).join("|")}|custom)或 --base-url;或改用 --runtime pi 使用已有 pi 环境`);
722
723
  }
723
724
  if (!setupApiKey)
724
- throw new Error("builtin-pi 需要 --api-key;或改用 --runtime external-pi 使用已有 pi 登录");
725
+ throw new Error("builtin-pi 需要 --api-key;或改用 --runtime pi 使用已有 pi 登录");
725
726
  // 自定义网关(--base-url)走 custom 预设:pi 会把 baseUrl 作为 provider 端点写入配置,
726
727
  // 而不是用预设厂商目录(否则会打到预设默认地址,如 api.openai.com)。
727
728
  const presetId = setupBaseUrl ? "custom" : (setupProvider ?? "custom");
@@ -762,7 +763,7 @@ Lark 必须用 --tenant lark:授权二维码是官方 /page/cli(begin 仍走
762
763
  say(`[setup 2/5] ✓ 内置 Pi provider 已配置(${presetId}${setupBaseUrl ? " / custom" : ""})`);
763
764
  }
764
765
  else if (piDistributionFlag === "external" && (setupProvider || setupApiKey || setupBaseUrl)) {
765
- throw new Error("external-pi 使用已有 pi 环境与登录,不接受 --provider/--api-key/--base-url");
766
+ throw new Error("pi 使用已有 pi 环境与登录,不接受 --provider/--api-key/--base-url");
766
767
  }
767
768
  const stagedBotFile = path.join(botsDir, `.${id}.${process.pid}.tmp`);
768
769
  try {
@@ -1,5 +1,6 @@
1
1
  import { createInterface } from "node:readline/promises";
2
2
  import { stdin, stdout } from "node:process";
3
+ import { toUserRuntime } from "../runtime/user-runtime.mjs";
3
4
  import { PI_PROVIDER_PRESETS, resolveBuiltinPiProviderSetupSelection, } from "../runtime/pi-provider-config.mjs";
4
5
  function number(value, fallback, max) {
5
6
  const parsed = value.trim() ? Number(value.trim()) : fallback;
@@ -54,20 +55,20 @@ export async function collectBuiltinPiChoice(questioner, services) {
54
55
  }
55
56
  export async function collectSetupAgentChoice(questioner, prior, services) {
56
57
  if (prior) {
57
- const keep = (await questioner.ask(`已有 Agent:${prior.runtime}/${prior.model}。直接回车保留;输入 c 才修改:`)).trim().toLowerCase();
58
+ const keep = (await questioner.ask(`已有 Agent:${toUserRuntime(prior.runtime, prior.piDistribution)}/${prior.model}。直接回车保留;输入 c 才修改:`)).trim().toLowerCase();
58
59
  if (!keep)
59
60
  return null;
60
61
  if (keep !== "c")
61
62
  throw new Error("请输入 c 修改,或直接回车保留现有配置");
62
63
  }
63
- const runtimeChoice = number(await questioner.ask("选择 Agent:\n 1. Pi(推荐)\n 2. Codex\n 3. Claude Code\n> "), 1, 3);
64
- if (runtimeChoice === 2)
65
- return { runtime: "codex" };
64
+ const runtimeChoice = number(await questioner.ask("选择 Agent:\n 1. builtin-pi(Larkin 捆绑,推荐)\n 2. pi(本机官方 pi CLI)\n 3. Codex\n 4. Claude Code\n> "), 1, 4);
66
65
  if (runtimeChoice === 3)
66
+ return { runtime: "codex" };
67
+ if (runtimeChoice === 4)
67
68
  return { runtime: "claude" };
68
- const sourceChoice = number(await questioner.ask("选择 Pi:\n 1. 外置 Pi(使用本机官方 pi)\n 2. 内置 Pi(无需安装 Agent CLI,推荐)\n> "), 2, 2);
69
- const distribution = sourceChoice === 1 ? "external" : "builtin";
70
- return distribution === "external" ? { runtime: "pi", distribution } : collectBuiltinPiChoice(questioner, services);
69
+ if (runtimeChoice === 2)
70
+ return { runtime: "pi", distribution: "external" };
71
+ return collectBuiltinPiChoice(questioner, services);
71
72
  }
72
73
  export async function recoverUnavailableExternalPi(initial, questioner, probe, report = () => { }, services) {
73
74
  let choice = initial;
@@ -77,8 +78,8 @@ export async function recoverUnavailableExternalPi(initial, questioner, probe, r
77
78
  const readiness = await probe();
78
79
  if (readiness.state === "ready")
79
80
  return choice;
80
- report(`外置 Pi ${readiness.state}:${readiness.reason || "prerequisite unavailable"};${readiness.nextAction || "可切换到内置 Pi"}`);
81
- const action = number(await questioner.ask("外置 Pi 当前不可用:\n 1. 切换到内置 Pi(推荐)\n 2. 重新选择 Agent\n 3. 取消 setup(保留原配置)\n> "), 1, 3);
81
+ report(`外置 Pi ${readiness.state}:${readiness.reason || "prerequisite unavailable"};${readiness.nextAction || "可切换到 builtin-pi"}`);
82
+ const action = number(await questioner.ask("外置 Pi 当前不可用:\n 1. 切换到 builtin-pi(推荐)\n 2. 重新选择 Agent\n 3. 取消 setup(保留原配置)\n> "), 1, 3);
82
83
  if (action === 1)
83
84
  return collectBuiltinPiChoice(questioner, services);
84
85
  if (action === 3)
@@ -209,11 +209,11 @@ function formatExternalPiSetupFailure(error, options) {
209
209
  const alternatives = catalogModelsFromError(error);
210
210
  const parts = [];
211
211
  if (/external Pi (?:auth\.json|models\.json|settings\.json) is required|external Pi profile|external Pi auth\.json has an unsafe mode|external Pi models\.json has an unsafe mode|external Pi settings\.json has an unsafe mode/.test(original)) {
212
- parts.push("external-pi requires the official file-backed Pi profile containing auth.json (0600), models.json, and settings.json, normally after `pi` login and default-model selection. Environment-variable-only credentials are unsupported.");
212
+ parts.push("pi requires the official file-backed Pi profile containing auth.json (0600), models.json, and settings.json, normally after `pi` login and default-model selection. Environment-variable-only credentials are unsupported.");
213
213
  }
214
214
  if (original)
215
215
  parts.push(original);
216
- parts.push("Fix the official `pi` login and default model, then rerun `larkin setup --runtime external-pi`.");
216
+ parts.push("Fix the official `pi` login and default model, then rerun `larkin setup --runtime pi`.");
217
217
  if (options.agentExisted) {
218
218
  parts.push("This Agent already exists; after a successful setup you can switch models with `larkin model <provider>/<model>`.");
219
219
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkin",
3
- "version": "0.4.26",
3
+ "version": "0.4.28",
4
4
  "private": false,
5
5
  "larkinPackageRole": "npm-published",
6
6
  "files": [