larkin 0.5.19 → 0.5.20

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.
@@ -232,17 +232,15 @@ function normalizeInboxState(value) {
232
232
  }
233
233
  function newInboxLockOwner(inspect) {
234
234
  const inspected = inspect(process.pid);
235
- if (!inspected.ok || !inspected.startToken) {
236
- throw new Error(`无法读取 Inbox lock owner 身份:${inspected.reason || "metadata incomplete"}`);
237
- }
238
- return { version: 1, pid: process.pid, processStartToken: inspected.startToken, nonce: crypto.randomUUID() };
235
+ const identity = inspected.ok && inspected.startToken ? { processStartToken: inspected.startToken } : {};
236
+ return { version: 1, pid: process.pid, ...identity, nonce: crypto.randomUUID() };
239
237
  }
240
238
  function validInboxLockOwner(value) {
241
239
  if (!value || typeof value !== "object" || Array.isArray(value))
242
240
  return false;
243
241
  const owner = value;
244
242
  return owner.version === 1 && Number.isSafeInteger(owner.pid) && Number(owner.pid) > 0
245
- && typeof owner.processStartToken === "string" && !!owner.processStartToken
243
+ && (owner.processStartToken === undefined || (typeof owner.processStartToken === "string" && !!owner.processStartToken))
246
244
  && typeof owner.nonce === "string" && /^[0-9a-f-]{36}$/i.test(owner.nonce);
247
245
  }
248
246
  class ContextOverflowRecoveryError extends Error {
@@ -308,6 +306,12 @@ class AgentStateStore {
308
306
  const age = Date.now() - stat.mtimeMs;
309
307
  return age >= INBOX_MALFORMED_LOCK_GRACE_MS ? "reclaimable" : "unknown";
310
308
  }
309
+ if (!(0, process_state_js_1.pidAlive)(owner.pid))
310
+ return "reclaimable";
311
+ // A live owner is reclaimed only when its start token proves the pid was reused. Without a
312
+ // token to compare, the lock may still be held, and its owner may still write back.
313
+ if (!owner.processStartToken)
314
+ return "unknown";
311
315
  const inspected = this.inspect(owner.pid);
312
316
  if (!inspected.ok)
313
317
  return inspected.dead ? "reclaimable" : "unknown";
@@ -333,7 +337,8 @@ class AgentStateStore {
333
337
  });
334
338
  }
335
339
  catch (error) {
336
- if (/lock 已被|无法取得 lock|正在创建|暂不能接管/.test(error instanceof Error ? error.message : String(error)))
340
+ // A sandboxed process cannot record its own identity for the reclaim lock; it waits for another process to reclaim.
341
+ if (/lock 已被|无法取得 lock|正在创建|暂不能接管|无法读取当前进程身份/.test(error instanceof Error ? error.message : String(error)))
337
342
  return false;
338
343
  throw error;
339
344
  }
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as crypto from "node:crypto";
3
3
  import * as path from "node:path";
4
4
  import { TargetRootLayout } from "../platform/root-layout.mjs";
5
- import { acquireProcessLock, inspectProcess } from "../platform/process-state.mjs";
5
+ import { acquireProcessLock, inspectProcess, pidAlive } from "../platform/process-state.mjs";
6
6
  import { isWindows } from "../platform/secure-metadata.mjs";
7
7
  import { isCanonicalInboxTarget, isUserDeliveryTarget, RUNTIME_REMINDER_TARGET, targetKeyOfInboxEnvelope } from "./inbox-projection.mjs";
8
8
  import { buildStrictProviderErrorInput, classifyStrictProviderError } from "../runtime/provider-error-classifier.mjs";
@@ -195,17 +195,15 @@ function normalizeInboxState(value) {
195
195
  }
196
196
  function newInboxLockOwner(inspect) {
197
197
  const inspected = inspect(process.pid);
198
- if (!inspected.ok || !inspected.startToken) {
199
- throw new Error(`无法读取 Inbox lock owner 身份:${inspected.reason || "metadata incomplete"}`);
200
- }
201
- return { version: 1, pid: process.pid, processStartToken: inspected.startToken, nonce: crypto.randomUUID() };
198
+ const identity = inspected.ok && inspected.startToken ? { processStartToken: inspected.startToken } : {};
199
+ return { version: 1, pid: process.pid, ...identity, nonce: crypto.randomUUID() };
202
200
  }
203
201
  function validInboxLockOwner(value) {
204
202
  if (!value || typeof value !== "object" || Array.isArray(value))
205
203
  return false;
206
204
  const owner = value;
207
205
  return owner.version === 1 && Number.isSafeInteger(owner.pid) && Number(owner.pid) > 0
208
- && typeof owner.processStartToken === "string" && !!owner.processStartToken
206
+ && (owner.processStartToken === undefined || (typeof owner.processStartToken === "string" && !!owner.processStartToken))
209
207
  && typeof owner.nonce === "string" && /^[0-9a-f-]{36}$/i.test(owner.nonce);
210
208
  }
211
209
  export class ContextOverflowRecoveryError extends Error {
@@ -270,6 +268,12 @@ export class AgentStateStore {
270
268
  const age = Date.now() - stat.mtimeMs;
271
269
  return age >= INBOX_MALFORMED_LOCK_GRACE_MS ? "reclaimable" : "unknown";
272
270
  }
271
+ if (!pidAlive(owner.pid))
272
+ return "reclaimable";
273
+ // A live owner is reclaimed only when its start token proves the pid was reused. Without a
274
+ // token to compare, the lock may still be held, and its owner may still write back.
275
+ if (!owner.processStartToken)
276
+ return "unknown";
273
277
  const inspected = this.inspect(owner.pid);
274
278
  if (!inspected.ok)
275
279
  return inspected.dead ? "reclaimable" : "unknown";
@@ -295,7 +299,8 @@ export class AgentStateStore {
295
299
  });
296
300
  }
297
301
  catch (error) {
298
- if (/lock 已被|无法取得 lock|正在创建|暂不能接管/.test(error instanceof Error ? error.message : String(error)))
302
+ // A sandboxed process cannot record its own identity for the reclaim lock; it waits for another process to reclaim.
303
+ if (/lock 已被|无法取得 lock|正在创建|暂不能接管|无法读取当前进程身份/.test(error instanceof Error ? error.message : String(error)))
299
304
  return false;
300
305
  throw error;
301
306
  }
@@ -13,11 +13,10 @@ import "../platform/check-bun-version.cjs";
13
13
  import fs from "node:fs";
14
14
  import http from "node:http";
15
15
  import path from "node:path";
16
- import crypto from "node:crypto";
17
16
  import { spawnSync } from "node:child_process";
18
17
  import { fileURLToPath } from "node:url";
19
18
  import { currentProcessMetadata, readJson } from "../platform/process-state.mjs";
20
- import { prepareDashboardLaunch } from "../dashboard/dashboard-lifecycle.mjs";
19
+ import { loadDashboardCapability, prepareDashboardLaunch } from "../dashboard/dashboard-lifecycle.mjs";
21
20
  import { DASHBOARD_BUILD_FINGERPRINT, DASHBOARD_BUILD_VERSION, PACKAGE_VERSION, collectStatus, collectWorkspaceEntry, loadDashboardConfig, resolveDashboardAvatarSource } from "../dashboard/dashboard-view-model.mjs";
22
21
  import { renderDashboardHtml } from "../dashboard/dashboard-template.mjs";
23
22
  import { createDashboardConfigController, createPiModelDirectoryResolver } from "../dashboard/dashboard-config-controller.mjs";
@@ -64,7 +63,7 @@ if (preparedDashboard.action === "reuse") {
64
63
  process.exit(0);
65
64
  }
66
65
  const dashboardLaunchLock = { release: preparedDashboard.release };
67
- const csrfCapability = crypto.randomBytes(32).toString("base64url");
66
+ const csrfCapability = loadDashboardCapability(path.join(startupConfig.larkinHome, "dashboard-capability"));
68
67
  const HTML = renderDashboardHtml(PACKAGE_VERSION, DASHBOARD_BUILD_VERSION, BUILD_FINGERPRINT, csrfCapability);
69
68
  const piModelResolver = createPiModelDirectoryResolver();
70
69
  const configController = createDashboardConfigController({ csrfCapability, piModelDirectoryResolver: piModelResolver });
package/dist/app/run.mjs CHANGED
@@ -385,10 +385,12 @@ export async function main() {
385
385
  launchLock.release();
386
386
  console.error(`[start] unified supervisor pid=${process.pid} daemon=${daemon.pid} agents=${names.join(",")}`);
387
387
  console.error("[start] dashboard 已随服务启动;不会自动打开浏览器");
388
- const requestStop = (signal) => {
388
+ const requestStop = (signal, received = signal) => {
389
389
  if (stopping)
390
390
  return;
391
391
  stopping = true;
392
+ // Name the stop signal so a service that vanished can be told apart from a crash (a crash logs its own line).
393
+ console.error(`[start] 收到 ${received},停止服务 (pid=${process.pid} ppid=${process.ppid})`);
392
394
  if (daemonRestartTimer) {
393
395
  clearTimeout(daemonRestartTimer);
394
396
  daemonRestartTimer = null;
@@ -405,6 +407,8 @@ export async function main() {
405
407
  };
406
408
  process.once("SIGINT", () => requestStop("SIGINT"));
407
409
  process.once("SIGTERM", () => requestStop("SIGTERM"));
410
+ // A closed terminal would otherwise end the supervisor without a trace; stop the same way as SIGTERM.
411
+ process.once("SIGHUP", () => requestStop("SIGTERM", "SIGHUP"));
408
412
  const daemonResult = await daemonFinal;
409
413
  try {
410
414
  cleanupStaleAgentControlSocket(configDir, controlToken);
@@ -310,9 +310,12 @@ export function createClaudeModelDirectoryResolver(options = {}) {
310
310
  },
311
311
  };
312
312
  }
313
- async function sanitizedView(env, chatDirectoryResolver, onlyAgentId, chatId) {
313
+ async function sanitizedView(env, chatDirectoryResolver, onlyAgentId, chatId, withChats = true) {
314
314
  const { config } = loadConfig(env);
315
315
  const view = safeConfigView(config, onlyAgentId, chatId, configApplyState(env, config));
316
+ // Group names come from Feishu, one lookup per Agent; callers that only need the global values skip them.
317
+ if (!withChats)
318
+ return view;
316
319
  for (const item of view.agents) {
317
320
  const agentId = String(item.agentId);
318
321
  const agent = config.agents[agentId];
@@ -367,6 +370,37 @@ function assertWriteRequest(req, csrfCapability) {
367
370
  if (!String(req.headers["content-type"] || "").toLowerCase().startsWith("application/json"))
368
371
  throw new Error("application/json required");
369
372
  }
373
+ /**
374
+ * Map a failed dashboard request to a status and a message the page shows as is.
375
+ * The raw reason goes to the local log. A wrong capability means the page was opened before this dashboard
376
+ * (re)started; an unknown operation or field means the page is from another build; both are fixed by a reload.
377
+ */
378
+ export function dashboardError(error, fallback) {
379
+ const message = error instanceof Error ? error.message : String(error);
380
+ console.warn(`[dashboard] ${fallback}: ${message}`);
381
+ const reply = (status, text) => ({ status, body: { error: text } });
382
+ if (/^(?:csrf|capability) rejected$/.test(message))
383
+ return reply(403, "这个页面已过期,刷新页面后再操作。");
384
+ if (/^(?:invalid host|same-origin required)$/.test(message))
385
+ return reply(403, "请在本机用 http://localhost 打开 dashboard。");
386
+ if (message === "request too large")
387
+ return reply(413, "内容太大,无法保存。");
388
+ if (message === "unknown agent")
389
+ return reply(404, "这个 Agent 已不存在,刷新页面看看。");
390
+ if (/not in current (?:runtime|model) directory$/.test(message))
391
+ return reply(400, "所选的模型或推理强度已不在可用列表里,重新选择后再保存。");
392
+ if (/^(?:unsupported operation|invalid |JSON object required|application\/json required)/.test(message) || error instanceof SyntaxError) {
393
+ return reply(400, "页面和正在运行的 larkin 版本不一致。刷新页面后再操作。");
394
+ }
395
+ // Messages written for people (Chinese) are shown as is; anything else keeps the fallback plus its reason.
396
+ if (/[\u4e00-\u9fff]/.test(message))
397
+ return reply(400, message);
398
+ return reply(500, `${fallback}:${message.slice(0, 200)}`);
399
+ }
400
+ function sendError(res, error, fallback, extra = {}) {
401
+ const { status, body } = dashboardError(error, fallback);
402
+ json(res, status, { ...body, ...extra });
403
+ }
370
404
  function assertPrivateReadRequest(req, csrfCapability) {
371
405
  const host = String(req.headers.host || "");
372
406
  if (!/^(?:localhost|127\.0\.0\.1):\d+$/.test(host))
@@ -508,13 +542,13 @@ export function createDashboardConfigController({ csrfCapability, env = process.
508
542
  try {
509
543
  assertPrivateReadRequest(req, csrfCapability);
510
544
  json(res, 200, {
511
- ...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined),
545
+ ...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined, requestUrl.searchParams.get("chats") !== "0"),
512
546
  runtimeModels: loadRuntimeModels(),
513
547
  runtimeOptions: [...RUNTIME_OPTIONS],
514
548
  });
515
549
  }
516
550
  catch (error) {
517
- json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "configuration unavailable" });
551
+ sendError(res, error, "读取配置失败");
518
552
  }
519
553
  return true;
520
554
  }
@@ -526,7 +560,7 @@ export function createDashboardConfigController({ csrfCapability, env = process.
526
560
  json(res, 200, { models });
527
561
  }
528
562
  catch (error) {
529
- json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "Pi model directory unavailable" });
563
+ sendError(res, error, "读取 Pi 模型列表失败");
530
564
  }
531
565
  return true;
532
566
  }
@@ -542,7 +576,7 @@ export function createDashboardConfigController({ csrfCapability, env = process.
542
576
  json(res, 200, { models });
543
577
  }
544
578
  catch (error) {
545
- json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "Codex model directory unavailable" });
579
+ sendError(res, error, "读取 Codex 模型列表失败");
546
580
  }
547
581
  return true;
548
582
  }
@@ -558,7 +592,7 @@ export function createDashboardConfigController({ csrfCapability, env = process.
558
592
  json(res, 200, { models });
559
593
  }
560
594
  catch (error) {
561
- json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "Claude model directory unavailable" });
595
+ sendError(res, error, "读取 Claude 模型列表失败");
562
596
  }
563
597
  return true;
564
598
  }
@@ -572,15 +606,10 @@ export function createDashboardConfigController({ csrfCapability, env = process.
572
606
  json(res, 200, { ok: true, revision: result.revision, persisted: true, applyState: result.applyState, changedScope: result.changedScope });
573
607
  }
574
608
  catch (error) {
575
- if (error instanceof RuntimePrerequisiteError) {
609
+ if (error instanceof RuntimePrerequisiteError)
576
610
  json(res, 400, { error: error.message, readiness: error.readiness });
577
- }
578
- else if (error instanceof Error && error.message.startsWith("未知 runtime")) {
579
- json(res, 400, { error: error.message });
580
- }
581
- else {
582
- json(res, 400, { error: "configuration update rejected" });
583
- }
611
+ else
612
+ sendError(res, error, "保存失败");
584
613
  }
585
614
  return true;
586
615
  }
@@ -597,15 +626,17 @@ export function createDashboardConfigController({ csrfCapability, env = process.
597
626
  const expectedSignature = runtimeConfigSignature(config, agentId);
598
627
  const result = await requestUpsert({ larkinHome: config.larkinHome, agentId });
599
628
  if (!result.ok) {
600
- json(res, 409, { error: result.error || "configuration saved but apply failed",
629
+ json(res, 409, { error: result.error || "配置已保存,但应用失败",
601
630
  ...(result.readiness ? { readiness: result.readiness } : {}) });
602
631
  return true;
603
632
  }
604
633
  markConfigApplied(env, agentId, expectedSignature);
605
634
  json(res, 200, { ok: true, agentId, applyState: "applied" });
606
635
  }
607
- catch {
608
- json(res, 409, { error: "configuration saved but apply failed" });
636
+ catch (error) {
637
+ const { status, body } = dashboardError(error, "配置已保存,但应用失败");
638
+ // An unreachable runtime is a failed apply of a saved config, not a server error.
639
+ json(res, status === 500 ? 409 : status, body);
609
640
  }
610
641
  return true;
611
642
  }
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadDashboardCapability = loadDashboardCapability;
6
7
  exports.dashboardBuildFingerprint = dashboardBuildFingerprint;
7
8
  exports.dashboardReuseDecision = dashboardReuseDecision;
8
9
  exports.reconcileDashboardRecord = reconcileDashboardRecord;
@@ -12,6 +13,7 @@ const node_path_1 = __importDefault(require("node:path"));
12
13
  const node_fs_1 = __importDefault(require("node:fs"));
13
14
  const process_state_js_1 = require("../platform/process-state.cjs");
14
15
  const internal_command_js_1 = require("../app/internal-command.cjs");
16
+ const secure_metadata_js_1 = require("../platform/secure-metadata.cjs");
15
17
  const PUBLISHED_RUNTIME_DIRS = ["dist", "assets"];
16
18
  const RUNTIME_EXTENSIONS = new Set([".cjs", ".mjs", ".js", ".css", ".json", ".node", ".svg", ".wasm"]);
17
19
  function publishedRuntimeFiles(root) {
@@ -39,6 +41,33 @@ function publishedRuntimeFiles(root) {
39
41
  return files.sort((a, b) => a.localeCompare(b, "en"));
40
42
  }
41
43
  /** Same semver can contain a different local build, so hash every published shell runtime byte. */
44
+ /**
45
+ * The dashboard's CSRF capability, kept in an owned 0600 file so a page opened before a restart keeps working.
46
+ * A missing or unsafe file gets a new value; if it cannot be written, the value lasts for this process only.
47
+ */
48
+ function loadDashboardCapability(file) {
49
+ try {
50
+ const stat = node_fs_1.default.lstatSync(file);
51
+ const value = node_fs_1.default.readFileSync(file, "utf8").trim();
52
+ if (stat.isFile() && (0, secure_metadata_js_1.exactMode)(stat, 0o600) && (typeof process.getuid !== "function" || stat.uid === process.getuid())
53
+ && /^[A-Za-z0-9_-]{43}$/.test(value))
54
+ return value;
55
+ }
56
+ catch { /* missing or unreadable: issue a new one */ }
57
+ const value = node_crypto_1.default.randomBytes(32).toString("base64url");
58
+ const temporary = `${file}.${process.pid}.tmp`;
59
+ try {
60
+ node_fs_1.default.writeFileSync(temporary, `${value}\n`, { mode: 0o600, flag: "wx" });
61
+ node_fs_1.default.renameSync(temporary, file);
62
+ }
63
+ catch {
64
+ try {
65
+ node_fs_1.default.unlinkSync(temporary);
66
+ }
67
+ catch { /* nothing was written */ }
68
+ }
69
+ return value;
70
+ }
42
71
  function dashboardBuildFingerprint(root) {
43
72
  const hash = node_crypto_1.default.createHash("sha256");
44
73
  for (const relative of publishedRuntimeFiles(root)) {
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import fs from "node:fs";
4
4
  import { acquireProcessLock, readProcessState, terminateOwnedProcess, waitForProcessExit, } from "../platform/process-state.mjs";
5
5
  import { processCommandToken } from "../app/internal-command.mjs";
6
+ import { exactMode } from "../platform/secure-metadata.mjs";
6
7
  const PUBLISHED_RUNTIME_DIRS = ["dist", "assets"];
7
8
  const RUNTIME_EXTENSIONS = new Set([".cjs", ".mjs", ".js", ".css", ".json", ".node", ".svg", ".wasm"]);
8
9
  function publishedRuntimeFiles(root) {
@@ -30,6 +31,33 @@ function publishedRuntimeFiles(root) {
30
31
  return files.sort((a, b) => a.localeCompare(b, "en"));
31
32
  }
32
33
  /** Same semver can contain a different local build, so hash every published shell runtime byte. */
34
+ /**
35
+ * The dashboard's CSRF capability, kept in an owned 0600 file so a page opened before a restart keeps working.
36
+ * A missing or unsafe file gets a new value; if it cannot be written, the value lasts for this process only.
37
+ */
38
+ export function loadDashboardCapability(file) {
39
+ try {
40
+ const stat = fs.lstatSync(file);
41
+ const value = fs.readFileSync(file, "utf8").trim();
42
+ if (stat.isFile() && exactMode(stat, 0o600) && (typeof process.getuid !== "function" || stat.uid === process.getuid())
43
+ && /^[A-Za-z0-9_-]{43}$/.test(value))
44
+ return value;
45
+ }
46
+ catch { /* missing or unreadable: issue a new one */ }
47
+ const value = crypto.randomBytes(32).toString("base64url");
48
+ const temporary = `${file}.${process.pid}.tmp`;
49
+ try {
50
+ fs.writeFileSync(temporary, `${value}\n`, { mode: 0o600, flag: "wx" });
51
+ fs.renameSync(temporary, file);
52
+ }
53
+ catch {
54
+ try {
55
+ fs.unlinkSync(temporary);
56
+ }
57
+ catch { /* nothing was written */ }
58
+ }
59
+ return value;
60
+ }
33
61
  export function dashboardBuildFingerprint(root) {
34
62
  const hash = crypto.createHash("sha256");
35
63
  for (const relative of publishedRuntimeFiles(root)) {
@@ -46,4 +46,4 @@ Error generating stack: `+e.message+`
46
46
  `},ar=function(){var e=parseInt(document.body.getAttribute(`data-scroll-locked`)||`0`,10);return isFinite(e)?e:0},or=function(){C.useEffect(function(){return document.body.setAttribute(rr,(ar()+1).toString()),function(){var e=ar()-1;e<=0?document.body.removeAttribute(rr):document.body.setAttribute(rr,e.toString())}},[])},sr=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?`margin`:r;or();var a=C.useMemo(function(){return tr(i)},[i]);return C.createElement(nr,{styles:ir(a,!t,i,n?``:`!important`)})},cr=!1;if(typeof window<`u`)try{var lr=Object.defineProperty({},"passive",{get:function(){return cr=!0,!0}});window.addEventListener(`test`,lr,lr),window.removeEventListener(`test`,lr,lr)}catch{cr=!1}var ur=cr?{passive:!1}:!1,dr=function(e){return e.tagName===`TEXTAREA`},fr=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!==`hidden`&&!(n.overflowY===n.overflowX&&!dr(e)&&n[t]===`visible`)},pr=function(e){return fr(e,`overflowY`)},mr=function(e){return fr(e,`overflowX`)},hr=function(e,t){var n=t.ownerDocument,r=t;do{if(typeof ShadowRoot<`u`&&r instanceof ShadowRoot&&(r=r.host),vr(e,r)){var i=yr(e,r);if(i[1]>i[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},gr=function(e){return[e.scrollTop,e.scrollHeight,e.clientHeight]},_r=function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]},vr=function(e,t){return e===`v`?pr(t):mr(t)},yr=function(e,t){return e===`v`?gr(t):_r(t)},br=function(e,t){return e===`h`&&t===`rtl`?-1:1},xr=function(e,t,n,r,i){var a=br(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=yr(e,s),m=p[0],h=p[1]-p[2]-a*m;(m||h)&&vr(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},Sr=function(e){return`changedTouches`in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},Cr=function(e){return[e.deltaX,e.deltaY]},wr=function(e){return e&&`current`in e?e.current:e},Tr=function(e,t){return e[0]===t[0]&&e[1]===t[1]},Er=function(e){return`
47
47
  .block-interactivity-${e} {pointer-events: none;}
48
48
  .allow-interactivity-${e} {pointer-events: all;}
49
- `},Dr=0,Or=[];function kr(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Dr++)[0],a=C.useState(Zn)[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=En([e.lockRef.current],(e.shards||[]).map(wr),!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=Sr(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=hr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=hr(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 xr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!Or.length||Or[Or.length-1]!==a)){var r=`deltaY`in n?Cr(n):Sr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Tr(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(wr).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:Ar(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=Sr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Sr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return Or.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,ur),document.addEventListener(`touchmove`,c,ur),document.addEventListener(`touchstart`,u,ur),function(){Or=Or.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,ur),document.removeEventListener(`touchmove`,c,ur),document.removeEventListener(`touchstart`,u,ur)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Er(i)}):null,p?C.createElement(sr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function Ar(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var jr=Bn(Vn,kr),Mr=C.forwardRef(function(e,t){return C.createElement(Un,wn({},e,{ref:t,sideCar:jr}))});Mr.classNames=Un.classNames;var Nr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Pr=new WeakMap,Fr=new WeakMap,Ir={},Lr=0,Rr=function(e){return e&&(e.host||Rr(e.parentNode))},zr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=Rr(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})},Br=function(e,t,n,r){var i=zr(t,Array.isArray(e)?e:[e]);Ir[n]||(Ir[n]=new WeakMap);var a=Ir[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=(Pr.get(e)||0)+1,l=(a.get(e)||0)+1;Pr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Fr.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(),Lr++,function(){o.forEach(function(e){var t=Pr.get(e)-1,i=a.get(e)-1;Pr.set(e,t),a.set(e,i),t||(Fr.has(e)||e.removeAttribute(r),Fr.delete(e)),i||e.removeAttribute(n)}),Lr--,Lr||(Pr=new WeakMap,Pr=new WeakMap,Fr=new WeakMap,Ir={})}},Vr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Nr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Br(r,i,n,`aria-hidden`)):function(){return null}},Hr=Object.defineProperty,Ur=(e,t)=>Hr(e,`name`,{value:t,configurable:!0}),Wr=`Dialog`,[Gr,Kr]=ze(Wr),[qr,Jr]=Gr(Wr),Yr=Ur(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]=tt({prop:r,defaultProp:i??!1,onChange:a,caller:Wr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,j.jsx)(qr,{scope:t,triggerRef:s,contentRef:c,contentId:Ke(),titleId:Ke(),descriptionId:Ke(),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`),Xr=`DialogPortal`,[Zr,Qr]=Gr(Xr,{forceMount:void 0}),$r=Ur(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Jr(Xr,t);return(0,j.jsx)(Zr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,j.jsx)(dn,{present:n||a.open,children:(0,j.jsx)(sn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ei=`DialogOverlay`,ti=C.forwardRef(Ur(function(e,t){let n=Qr(ei,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ei,e.__scopeDialog);return a.modal?(0,j.jsx)(dn,{present:r||a.open,children:(0,j.jsx)(ri,{...i,ref:t})}):null},`DialogOverlay`)),ni=lt(`DialogOverlay.RemoveScroll`),ri=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(ei,n),a=Pe(t,It());return(0,j.jsx)(Mr,{as:ni,allowPinchZoom:!0,shards:[i.contentRef],children:(0,j.jsx)(wt.div,{"data-state":hi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ii=`DialogContent`,ai=C.forwardRef(Ur(function(e,t){let n=Qr(ii,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ii,e.__scopeDialog);return(0,j.jsx)(dn,{present:r||a.open,children:a.modal?(0,j.jsx)(oi,{...i,ref:t}):(0,j.jsx)(si,{...i,ref:t})})},`DialogContent`)),oi=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(null),i=Pe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Vr(e)},[]),(0,j.jsx)(ci,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Te(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Te(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Te(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),si=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,j.jsx)(ci,{...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`)),ci=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Jr(ii,n);return Sn(),(0,j.jsx)(j.Fragment,{children:(0,j.jsx)(qt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,j.jsx)(Ft,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":hi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),li=`DialogTitle`,ui=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(li,n),{setTitleCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),di=`DialogDescription`,fi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(di,n),{setDescriptionCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),pi=`DialogClose`,mi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(pi,n);return(0,j.jsx)(wt.button,{type:`button`,...r,ref:t,onClick:Te(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function hi(e){return e?`open`:`closed`}Ur(hi,`getState`);function gi(...e){return e.filter(Boolean).join(` `)}function _i({className:e,...t}){return(0,j.jsx)(`button`,{className:gi(`ui-button`,e),...t})}function vi({className:e,...t}){return(0,j.jsx)(`span`,{className:gi(`ui-badge`,e),...t})}function yi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,j.jsx)(Yr,{open:e,onOpenChange:t,children:(0,j.jsxs)($r,{children:[(0,j.jsx)(ti,{className:`sheet-overlay`}),(0,j.jsxs)(ai,{className:`sheet-content`,children:[(0,j.jsxs)(`div`,{className:`sheet-head`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ui,{children:n}),r?(0,j.jsx)(fi,{children:r}):null]}),(0,j.jsx)(mi,{asChild:!0,children:(0,j.jsx)(_i,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,j.jsx)(pe,{size:18})})})]}),i]})]})})}function bi({title:e,detail:t}){return(0,j.jsxs)(`div`,{className:`empty-state`,children:[(0,j.jsx)(`strong`,{children:e}),t?(0,j.jsx)(`p`,{children:t}):null]})}var xi=[`codex`,`claude`,`pi`],Si={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},Ci=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},wi=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ti(e,t={}){let n=await fetch(e,{cache:`no-store`,...t});if(!n.ok){let e=await n.json().catch(()=>({}));throw new wi(e.error||`HTTP ${n.status}`,e)}return n.json()}function Ei(e,t){if(!(e instanceof wi)||!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 Di(e){return Ti(`/api/config`,{method:`PATCH`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify(e)})}function Oi(e,t={}){return Ti(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":Ci.csrfCapability}})}function ki(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 Ai(e){let t=new Date(String(e||``));return Number.isFinite(t.getTime())?t.toLocaleString(`zh-CN`,{hour12:!1}):`未知时间`}function ji(e){return Number.isFinite(Number(e))?Number(e).toLocaleString(`en-US`):`—`}var Mi=1,Ni=1440;function Pi(e){return String(e/6e4)}function Fi(e){let t=Number(String(e).trim()),n=t*6e4,r=Math.round(n);return Number.isFinite(t)&&t>=Mi&&t<=Ni&&Number.isSafeInteger(r)&&Math.abs(n-r)<=1e-6?r:null}function Ii(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Li(){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)(xe()),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 Ti(`/api/status`,{signal:e.signal});if(!c.current.accepts(n))return;if(Ci.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==Ci.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 Ri({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,j.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,j.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function zi({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>ye(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,j.jsxs)(`div`,{className:gi(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,j.jsxs)(`label`,{className:`agent-search`,children:[(0,j.jsx)(de,{size:15,"aria-hidden":`true`}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,j.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,j.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,j.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:gi(`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,j.jsx)(Ri,{agent:e}),(0,j.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,j.jsx)(`strong`,{children:e.displayName||e.name}),(0,j.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,j.jsxs)(`span`,{className:`status-line`,children:[(0,j.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Ii(e)]})]}),e.issue?(0,j.jsx)(A,{size:15,className:`danger`,"aria-label":`异常`}):(0,j.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,j.jsx)(bi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function N({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[l,u]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[d,f]=(0,C.useState)(`15`),[p,m]=(0,C.useState)(!1),[h,g]=(0,C.useState)(null),_=a!==r||l.enabled!==s.enabled||d!==Pi(s.intervalMs);return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return m(!0),Oi(`/api/config`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),c(e.inboxAudit),u(e.inboxAudit),f(Pi(e.inboxAudit.intervalMs)),g(null)}).catch(e=>{t.signal.aborted||g(e.message)}).finally(()=>{t.signal.aborted||m(!1)}),()=>t.abort()},[e]),(0,j.jsx)(yi,{open:e,onOpenChange:e=>{!e&&_&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,j.jsxs)(`div`,{className:`settings-form`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`真人群消息默认策略`}),(0,j.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:p,children:[(0,j.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`global-inbox-audit-title`,children:[(0,j.jsxs)(`div`,{className:`audit-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`global-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsx)(`p`,{children:`定时检查仍待处理的入站工作;关闭时不会安排巡检。`})]}),(0,j.jsxs)(`label`,{className:`audit-switch`,children:[(0,j.jsx)(`input`,{"aria-label":`Inbox 巡检开关`,type:`checkbox`,checked:l.enabled,disabled:p,onChange:e=>u(t=>({...t,enabled:e.target.checked}))}),(0,j.jsx)(`span`,{"aria-hidden":`true`}),(0,j.jsx)(`b`,{children:l.enabled?`已开启`:`已关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`全局巡检间隔(分钟)`,type:`number`,min:Mi,max:Ni,step:`any`,inputMode:`decimal`,value:d,disabled:p,onChange:e=>f(e.target.value)})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`可在关闭时预先调整;保存间隔本身不会开启巡检。`})]}),(0,j.jsxs)(`div`,{className:`form-actions`,children:[(0,j.jsx)(_i,{onClick:()=>{o(r),u(s),f(Pi(s.intervalMs))},disabled:!_||p,children:`放弃草稿`}),(0,j.jsx)(_i,{className:`primary`,onClick:async()=>{let e=l.enabled!==s.enabled||d!==Pi(s.intervalMs),t=e?Fi(d):null;if(e&&t===null){g(`巡检间隔必须在 1 到 1440 分钟之间。`);return}let o=t??s.intervalMs;m(!0);try{let t=null;a!==r&&(t=await Di({operation:`set-global-mention`,value:a})),e&&(t=await Di({operation:`set-global-inbox-audit`,...l.enabled===s.enabled?{}:{enabled:l.enabled},...d===Pi(s.intervalMs)?{}:{intervalMs:o}})),i(a),c({...l,intervalMs:o}),u(e=>({...e,intervalMs:o})),f(Pi(o)),g(t?`已保存 · ${t.applyState} · ${t.revision.slice(0,20)}…`:`没有需要保存的修改`),n()}catch(e){g(e instanceof Error?e.message:String(e))}finally{m(!1)}},disabled:!_||p,children:p?`保存中…`:`保存全局设置`})]}),h?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:h}):null]})})}function P({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,j.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,j.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,j.jsxs)(`label`,{className:`table-search`,children:[(0,j.jsx)(de,{size:14}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,j.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,j.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,j.jsxs)(`table`,{className:`policy-table`,children:[(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{children:`群`}),(0,j.jsx)(`th`,{children:`消息策略`}),(0,j.jsx)(`th`,{children:`操作`})]})}),(0,j.jsx)(`tbody`,{children:l.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsxs)(`td`,{children:[(0,j.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,j.jsx)(`code`,{children:e.chatId}):(0,j.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,j.jsx)(`td`,{children:(0,j.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,j.jsx)(`td`,{children:(0,j.jsx)(_i,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,j.jsx)(bi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,j.jsxs)(`div`,{className:`chat-add`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`完整 chat_id`}),(0,j.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`消息策略`}),(0,j.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,j.jsx)(_i,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function Bi(e){return[`Readiness ${e.state}`,e.reason,e.nextAction].filter(Boolean).join(` · `)}function Vi({agentId:e,readiness:t,onDirtyChange:n,refreshKey:r}){let[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)({}),[c,l]=(0,C.useState)({}),[u,d]=(0,C.useState)(!0),[f,p]=(0,C.useState)(null),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(xe()),_=(0,C.useRef)(xe()),v=!be(o,c);(0,C.useEffect)(()=>n(v),[v,n]),(0,C.useEffect)(()=>{let e=e=>{v&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[v]);let y=(0,C.useCallback)(async(t=!1)=>{let n=g.current.issue();d(!0);try{let r=await Oi(`/api/config?agent=${encodeURIComponent(e)}`);if(!g.current.accepts(n))return;let i=r.agents[0];if(!i)throw Error(`配置投影中没有当前 Agent`);let o=i.inboxAudit.override.intervalMs===`inherit`?i.inboxAudit.effective.intervalMs:i.inboxAudit.override.intervalMs,c={runtime:i.runtimeOption||i.runtime,model:i.model,effort:i.effort||`default`,mention:i.mention.override,auditEnabled:i.inboxAudit.override.enabled,auditIntervalInherited:i.inboxAudit.override.intervalMs===`inherit`,auditGapMinutes:Pi(o)};a(r),s(c),(!t||!v)&&l(c)}catch(e){g.current.accepts(n)&&p(e instanceof Error?e.message:String(e))}finally{g.current.accepts(n)&&d(!1)}},[e,v]);(0,C.useEffect)(()=>{y(!1)},[e]),(0,C.useEffect)(()=>{r>0&&y(!0)},[r]);let b=i?.agents[0],x=(e,t)=>l(n=>({...n,[e]:t})),S=String(c.runtime||b?.runtime||``),w=String(c.model||b?.model||`default`);(0,C.useEffect)(()=>{if(h({runtime:S,status:`loading`,models:[]}),!xi.includes(S)){h({runtime:S,status:`error`,models:[]});return}let t=_.current.issue(),n=new AbortController;return Oi(`/api/models/${S}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{_.current.accepts(t)&&h({runtime:S,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&_.current.accepts(t)&&(h({runtime:S,status:`error`,models:[]}),p(`${S} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,S]);let ee=m?.runtime===S&&m.status===`ready`,te=m?.runtime===S?m.status:`loading`,T=ee?m.models:[],E=`${w} · 当前配置(模型目录${te===`error`?`不可用`:`加载中`})`,ne=ee?T.some(e=>e.id===w)?T:[{id:w,label:`${w} · 当前配置`},...T]:[{id:w,label:E}],re=ee&&w!=="default"&&T.find(e=>e.id===w)?.supportedReasoningEfforts||[],ie=c.runtime!==o.runtime||c.model!==o.model||c.effort!==o.effort,ae=c.auditEnabled===`on`||c.auditEnabled===`off`?c.auditEnabled:`inherit`,oe=c.auditIntervalInherited===!0,se=String(c.auditGapMinutes||``),ce=c.auditEnabled!==o.auditEnabled||c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes,D=()=>Ti(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify({agentId:e})}),O=async()=>{if(!b)return;let t=Fi(c.auditGapMinutes);if(ce&&!oe&&t===null){p(`巡检间隔必须在 1 到 1440 分钟之间;草稿已保留。`);return}d(!0);try{let n=[];c.runtime===o.runtime?c.model!==o.model&&n.push({operation:`set-agent-model`,agentId:e,model:c.model}):n.push({operation:`set-agent-runtime`,agentId:e,runtime:c.runtime,model:c.model}),c.effort!==o.effort&&n.push({operation:`set-agent-effort`,agentId:e,effort:c.effort}),c.mention!==o.mention&&n.push({operation:`set-agent-mention`,agentId:e,value:c.mention}),ce&&n.push({operation:`set-agent-inbox-audit`,agentId:e,...c.auditEnabled===o.auditEnabled?{}:{enabled:ae===`inherit`?`inherit`:ae===`on`},...c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes?{intervalMs:oe?`inherit`:t}:{}});let r=null;for(let e of n)r=await Di(e);if(ie)try{await D(),p(`配置已保存并应用`)}catch(e){p(`配置已保存但未应用:${Ei(e,`稍后重试`)}`)}else p(r?`配置已保存`:`没有需要保存的修改`);await y(!1)}catch(e){p(Ei(e,e instanceof Error?e.message:String(e)))}finally{d(!1)}},le=async()=>{d(!0);try{await D(),p(`已应用保存的运行配置`),await y(!0)}catch(e){p(`配置保持已保存但未应用:${Ei(e,`稍后重试`)}`)}finally{d(!1)}},de=async(t,n)=>{d(!0);try{let r=await Di({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});p(`群策略已保存 · ${r.applyState}`),await y(!0)}catch(e){p(e instanceof Error?e.message:String(e))}finally{d(!1)}};if(!b&&u)return(0,j.jsx)(bi,{title:`加载 Agent 配置…`});if(!b)return(0,j.jsx)(bi,{title:`配置不可用`,detail:f||`请重试`});let fe=b.inboxAudit.effective,k=b.inboxAudit.source,A=`${k.enabled===`agent`?`当前 Agent 已单独设置`:k.enabled===`global`?`当前使用全局设置`:`当前使用默认设置`}:${fe.enabled?`已开启`:`已关闭`},每 ${Pi(fe.intervalMs)} 分钟`,pe=b.apply.applyState===`pending`?`待应用`:b.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,j.jsxs)(`div`,{className:`configuration-page`,children:[(0,j.jsxs)(`section`,{className:`config-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`Agent 配置`}),(0,j.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,j.jsx)(vi,{className:b.apply.applyState===`pending`?`warning`:`success`,children:pe})]}),(0,j.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,j.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),t?(0,j.jsx)(`p`,{className:`field-help`,children:Bi(t)}):null,(0,j.jsxs)(`div`,{className:`config-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Runtime`}),(0,j.jsx)(`select`,{value:S,disabled:u,onChange:e=>{x(`runtime`,e.target.value),x(`model`,`default`),x(`effort`,`default`)},children:(i?.runtimeOptions||xi).map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Model`}),(0,j.jsx)(`select`,{value:w,disabled:u||!ee,onChange:e=>{x(`model`,e.target.value),e.target.value==="default"&&x(`effort`,`default`)},children:ne.map(e=>(0,j.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Effort`}),(0,j.jsxs)(`select`,{value:String(c.effort||`default`),disabled:u||!ee||w==="default"||!re.length,onChange:e=>x(`effort`,e.target.value),children:[(0,j.jsx)(`option`,{value:`default`,children:`default · 不指定`}),re.map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`群消息策略`}),(0,j.jsxs)(`select`,{value:String(c.mention||`inherit`),disabled:u,onChange:e=>x(`mention`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`agent-inbox-audit-title`,children:[(0,j.jsx)(`div`,{className:`audit-heading`,children:(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`agent-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsxs)(`p`,{children:[A,`。`]})]})}),(0,j.jsxs)(`div`,{className:`config-grid audit-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检设置`}),(0,j.jsxs)(`select`,{"aria-label":`Inbox 巡检设置`,value:ae,disabled:u,onChange:e=>x(`auditEnabled`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`on`,children:`为此 Agent 开启`}),(0,j.jsx)(`option`,{value:`off`,children:`为此 Agent 关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`Agent 巡检间隔(分钟)`,type:`number`,min:Mi,max:Ni,step:`any`,inputMode:`decimal`,value:se,disabled:u,onChange:e=>l(t=>({...t,auditGapMinutes:e.target.value,auditIntervalInherited:!1}))})]})]}),(0,j.jsxs)(`div`,{className:`audit-inline-actions`,children:[(0,j.jsx)(`span`,{children:oe?`间隔跟随全局设置;编辑数字后会仅作用于此 Agent。`:`此 Agent 使用单独的巡检间隔。`}),oe?null:(0,j.jsx)(_i,{disabled:u,onClick:()=>l(e=>({...e,auditIntervalInherited:!0,auditGapMinutes:Pi(fe.intervalMs)})),children:`间隔跟随全局设置`})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`即使关闭巡检,也可保存间隔;间隔修改不会开启巡检。`})]}),v||b.apply.applyState===`pending`?(0,j.jsxs)(`div`,{className:`form-actions`,children:[v?(0,j.jsx)(_i,{disabled:u,onClick:()=>l(o),children:`放弃草稿`}):null,v?(0,j.jsx)(_i,{className:`primary`,disabled:u,onClick:O,children:u?`处理中…`:ie?`保存并应用`:`保存 Agent 配置`}):(0,j.jsxs)(_i,{className:`primary`,disabled:u,onClick:le,children:[(0,j.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,f?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:f}):null]}),(0,j.jsx)(P,{config:b,onSave:de,disabled:u})]})}function Hi({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,j.jsxs)(`div`,{className:`overview-page`,children:[(0,j.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ie,{})}),(0,j.jsx)(`span`,{children:`连接`}),(0,j.jsx)(`strong`,{children:e.connection.state}),(0,j.jsx)(`small`,{children:e.connection.reason})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`执行状态`}),(0,j.jsx)(`strong`,{children:Ii(e)}),(0,j.jsx)(`small`,{children:e.lastActivity?ki(e.lastActivity.ageSec):`尚无活动`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(le,{})}),(0,j.jsx)(`span`,{children:`最近消息`}),(0,j.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,j.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${ki(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`上下文`}),(0,j.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,j.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`累计 Token`}),(0,j.jsx)(`strong`,{children:ji(t.cumulativeTokens)}),(0,j.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`最近一轮用量`}),(0,j.jsx)(`strong`,{children:ji(t.latestTokens)}),(0,j.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ue,{})}),(0,j.jsx)(`span`,{children:`Compaction`}),(0,j.jsx)(`strong`,{children:ji(n.count)}),(0,j.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${Ai(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,j.jsxs)(`div`,{className:`overview-sections`,children:[(0,j.jsxs)(`section`,{className:`content-section wide`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`最近动态`}),(0,j.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Ui,{items:e.feed.slice(0,10)})]}),(0,j.jsxs)(`section`,{className:`content-section`,children:[(0,j.jsx)(`h3`,{children:`运行摘要`}),(0,j.jsxs)(`dl`,{className:`facts`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Runtime`}),(0,j.jsx)(`dd`,{children:e.runtime})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Readiness`}),(0,j.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,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Model`}),(0,j.jsx)(`dd`,{children:e.model})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Effort`}),(0,j.jsx)(`dd`,{children:e.effort||`default`})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`入站`}),(0,j.jsx)(`dd`,{children:e.inbound.state})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`提醒`}),(0,j.jsx)(`dd`,{children:e.activeReminders})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`指示灯`}),(0,j.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function Ui({items:e}){if(!e.length)return(0,j.jsx)(bi,{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,j.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,j.jsxs)(`li`,{children:[(0,j.jsx)(`span`,{className:gi(`timeline-dot`,t(e))}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,j.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,j.jsx)(`time`,{children:Ai(e.at)})]})]},`${e.at}-${n}`))})}function Wi({agent:e}){return e.conversation.length?(0,j.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,j.jsxs)(`article`,{className:gi(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,j.jsxs)(`header`,{children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(vi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,j.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,j.jsx)(`time`,{children:Ai(t.at)})]}),(0,j.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,j.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,j.jsx)(bi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Gi({agent:e}){if(!e.remindersList.length)return(0,j.jsx)(bi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,j.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,j.jsxs)(`article`,{className:gi(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,j.jsx)(ne,{size:16}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,j.jsxs)(`p`,{children:[Ai(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,j.jsx)(vi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function Ki({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)(xe()),u=(0,C.useCallback)(async t=>{let r=l.current.issue();o(!0);try{let a=await Ti(`/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,j.jsxs)(`div`,{className:`workspace-browser`,children:[(0,j.jsxs)(`div`,{className:`workspace-side`,children:[(0,j.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,j.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,j.jsxs)(`span`,{children:[(0,j.jsx)(re,{size:12}),(0,j.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,j.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,j.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,j.jsx)(se,{size:15}):(0,j.jsx)(oe,{size:15}),(0,j.jsx)(`span`,{children:e.name}),(0,j.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,j.jsx)(bi,{title:`空目录`}):null]})]}),(0,j.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,j.jsx)(bi,{title:`读取工作区…`}):s?(0,j.jsx)(bi,{title:`工作区读取失败`,detail:s}):r?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`header`,{children:[(0,j.jsx)(`strong`,{children:r.name}),(0,j.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,j.jsx)(bi,{title:`二进制文件不可预览`}):(0,j.jsx)(`pre`,{children:r.content})]}):(0,j.jsx)(bi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function qi({agent:e}){return(0,j.jsxs)(`section`,{className:`logs-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`结构化日志`}),(0,j.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Ui,{items:e.feed}),e.recentErrors.length?(0,j.jsxs)(`section`,{className:`error-stack`,children:[(0,j.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ae,{size:14}),(0,j.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,j.jsx)(`time`,{children:Ai(e.at)})]},`${e.at}-${t}`))]}):null]})}function Ji({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))}% · ${ji(t.latestTokens)} / ${ji(t.contextWindow)}`;return(0,j.jsxs)(`header`,{className:`agent-header`,children:[(0,j.jsxs)(`div`,{className:`agent-title`,children:[(0,j.jsx)(Ri,{agent:e}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`p`,{children:e.agentId}),(0,j.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,j.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,j.jsx)(vi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,j.jsx)(`span`,{children:e.runtime}),(0,j.jsx)(`span`,{children:e.model}),(0,j.jsx)(`span`,{children:e.effort||`default effort`}),(0,j.jsx)(`span`,{children:r}),(0,j.jsxs)(`span`,{children:[`累计 `,ji(t.cumulativeTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`最近一轮 `,ji(t.latestTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`Compact `,ji(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,j.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?ki(e.lastDeliver.ageSec):`暂无`]}),(0,j.jsx)(`span`,{children:e.lastActivity?.detail||Ii(e)})]})]})}function Yi(){let e=Li(),t=(0,C.useMemo)(()=>ge(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||Ci.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,``,_e(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=ve(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=ge(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,_e(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,j.jsxs)(`div`,{className:gi(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,j.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,j.jsxs)(`div`,{className:`brand`,children:[(0,j.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,j.jsx)(zi,{agents:_,selectedId:n,onSelect:ee}),(0,j.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,j.jsxs)(`main`,{className:gi(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,j.jsxs)(`div`,{className:`app-topbar`,children:[(0,j.jsx)(_i,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,j.jsx)(O,{size:18})}),(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,j.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,j.jsx)(vi,{className:`warning`,children:`状态陈旧`}):null]}),(0,j.jsxs)(_i,{onClick:()=>s(!0),children:[(0,j.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,j.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,j.jsx)(A,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,j.jsx)(_i,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,j.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,j.jsx)(_i,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,j.jsxs)(`div`,{className:`page-loading`,children:[(0,j.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(Ji,{agent:y}),(0,j.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:he.map(e=>(0,j.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,j.jsx)(ce,{}):e===`conversation`?(0,j.jsx)(le,{}):e===`configuration`?(0,j.jsx)(k,{}):e===`reminders`?(0,j.jsx)(ne,{}):e===`workspace`?(0,j.jsx)(se,{}):(0,j.jsx)(D,{}),Si[e]]},e))}),(0,j.jsxs)(`div`,{className:gi(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,j.jsx)(Hi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,j.jsx)(Wi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,j.jsx)(Vi,{agentId:y.agentId,readiness:y.runtimeReadiness??null,onDirtyChange:p,refreshKey:u})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,j.jsx)(Gi,{agent:y})}),(0,j.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,j.jsx)(Ki,{agentId:y.agentId})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,j.jsx)(qi,{agent:y})})]},y.agentId)]}):(0,j.jsx)(bi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,j.jsx)(N,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,j.jsx)(yi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,j.jsx)(zi,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Xi=document.getElementById(`root`);if(!Xi)throw Error(`dashboard root is missing`);(0,me.createRoot)(Xi).render((0,j.jsx)(C.StrictMode,{children:(0,j.jsx)(Yi,{})}));
49
+ `},Dr=0,Or=[];function kr(e){var t=C.useRef([]),n=C.useRef([0,0]),r=C.useRef(),i=C.useState(Dr++)[0],a=C.useState(Zn)[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=En([e.lockRef.current],(e.shards||[]).map(wr),!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=Sr(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=hr(d,u);if(!m)return!0;if(m?l=d:(l=d===`v`?`h`:`v`,m=hr(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 xr(h,t,e,h===`h`?s:c,!0)},[]),c=C.useCallback(function(e){var n=e;if(!(!Or.length||Or[Or.length-1]!==a)){var r=`deltaY`in n?Cr(n):Sr(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Tr(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(wr).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:Ar(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=Sr(e),r.current=void 0},[]),d=C.useCallback(function(t){l(t.type,Cr(t),t.target,s(t,e.lockRef.current))},[]),f=C.useCallback(function(t){l(t.type,Sr(t),t.target,s(t,e.lockRef.current))},[]);C.useEffect(function(){return Or.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(`wheel`,c,ur),document.addEventListener(`touchmove`,c,ur),document.addEventListener(`touchstart`,u,ur),function(){Or=Or.filter(function(e){return e!==a}),document.removeEventListener(`wheel`,c,ur),document.removeEventListener(`touchmove`,c,ur),document.removeEventListener(`touchstart`,u,ur)}},[]);var p=e.removeScrollBar,m=e.inert;return C.createElement(C.Fragment,null,m?C.createElement(a,{styles:Er(i)}):null,p?C.createElement(sr,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function Ar(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var jr=Bn(Vn,kr),Mr=C.forwardRef(function(e,t){return C.createElement(Un,wn({},e,{ref:t,sideCar:jr}))});Mr.classNames=Un.classNames;var Nr=function(e){return typeof document>`u`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},Pr=new WeakMap,Fr=new WeakMap,Ir={},Lr=0,Rr=function(e){return e&&(e.host||Rr(e.parentNode))},zr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=Rr(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})},Br=function(e,t,n,r){var i=zr(t,Array.isArray(e)?e:[e]);Ir[n]||(Ir[n]=new WeakMap);var a=Ir[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=(Pr.get(e)||0)+1,l=(a.get(e)||0)+1;Pr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&Fr.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(),Lr++,function(){o.forEach(function(e){var t=Pr.get(e)-1,i=a.get(e)-1;Pr.set(e,t),a.set(e,i),t||(Fr.has(e)||e.removeAttribute(r),Fr.delete(e)),i||e.removeAttribute(n)}),Lr--,Lr||(Pr=new WeakMap,Pr=new WeakMap,Fr=new WeakMap,Ir={})}},Vr=function(e,t,n){n===void 0&&(n=`data-aria-hidden`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||Nr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(`[aria-live], script`))),Br(r,i,n,`aria-hidden`)):function(){return null}},Hr=Object.defineProperty,Ur=(e,t)=>Hr(e,`name`,{value:t,configurable:!0}),Wr=`Dialog`,[Gr,Kr]=ze(Wr),[qr,Jr]=Gr(Wr),Yr=Ur(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]=tt({prop:r,defaultProp:i??!1,onChange:a,caller:Wr}),[d,f]=C.useState(0),[p,m]=C.useState(0);return(0,j.jsx)(qr,{scope:t,triggerRef:s,contentRef:c,contentId:Ke(),titleId:Ke(),descriptionId:Ke(),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`),Xr=`DialogPortal`,[Zr,Qr]=Gr(Xr,{forceMount:void 0}),$r=Ur(e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=Jr(Xr,t);return(0,j.jsx)(Zr,{scope:t,forceMount:n,children:C.Children.map(r,e=>(0,j.jsx)(dn,{present:n||a.open,children:(0,j.jsx)(sn,{asChild:!0,container:i,children:e})}))})},`DialogPortal`),ei=`DialogOverlay`,ti=C.forwardRef(Ur(function(e,t){let n=Qr(ei,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ei,e.__scopeDialog);return a.modal?(0,j.jsx)(dn,{present:r||a.open,children:(0,j.jsx)(ri,{...i,ref:t})}):null},`DialogOverlay`)),ni=lt(`DialogOverlay.RemoveScroll`),ri=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(ei,n),a=Pe(t,It());return(0,j.jsx)(Mr,{as:ni,allowPinchZoom:!0,shards:[i.contentRef],children:(0,j.jsx)(wt.div,{"data-state":hi(i.open),...r,ref:a,style:{pointerEvents:`auto`,...r.style}})})},`DialogOverlayImpl`)),ii=`DialogContent`,ai=C.forwardRef(Ur(function(e,t){let n=Qr(ii,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=Jr(ii,e.__scopeDialog);return(0,j.jsx)(dn,{present:r||a.open,children:a.modal?(0,j.jsx)(oi,{...i,ref:t}):(0,j.jsx)(si,{...i,ref:t})})},`DialogContent`)),oi=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(null),i=Pe(t,n.contentRef,r);return C.useEffect(()=>{let e=r.current;if(e)return Vr(e)},[]),(0,j.jsx)(ci,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:Te(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:Te(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:Te(e.onFocusOutside,e=>e.preventDefault())})},`DialogContentModal`)),si=C.forwardRef(Ur(function(e,t){let n=Jr(ii,e.__scopeDialog),r=C.useRef(!1),i=C.useRef(!1);return(0,j.jsx)(ci,{...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`)),ci=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=Jr(ii,n);return Sn(),(0,j.jsx)(j.Fragment,{children:(0,j.jsx)(qt,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,j.jsx)(Ft,{role:`dialog`,id:s.contentId,"aria-describedby":s.descriptionPresent?s.descriptionId:void 0,"aria-labelledby":s.titlePresent?s.titleId:void 0,"data-state":hi(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})},`DialogContentImpl`)),li=`DialogTitle`,ui=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(li,n),{setTitleCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.h2,{id:i.titleId,...r,ref:t})},`DialogTitle`)),di=`DialogDescription`,fi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(di,n),{setDescriptionCount:a}=i;return Ve(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,j.jsx)(wt.p,{id:i.descriptionId,...r,ref:t})},`DialogDescription`)),pi=`DialogClose`,mi=C.forwardRef(Ur(function(e,t){let{__scopeDialog:n,...r}=e,i=Jr(pi,n);return(0,j.jsx)(wt.button,{type:`button`,...r,ref:t,onClick:Te(e.onClick,()=>i.onOpenChange(!1))})},`DialogClose`));function hi(e){return e?`open`:`closed`}Ur(hi,`getState`);function gi(...e){return e.filter(Boolean).join(` `)}function _i({className:e,...t}){return(0,j.jsx)(`button`,{className:gi(`ui-button`,e),...t})}function vi({className:e,...t}){return(0,j.jsx)(`span`,{className:gi(`ui-badge`,e),...t})}function yi({open:e,onOpenChange:t,title:n,description:r,children:i}){return(0,j.jsx)(Yr,{open:e,onOpenChange:t,children:(0,j.jsxs)($r,{children:[(0,j.jsx)(ti,{className:`sheet-overlay`}),(0,j.jsxs)(ai,{className:`sheet-content`,children:[(0,j.jsxs)(`div`,{className:`sheet-head`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ui,{children:n}),r?(0,j.jsx)(fi,{children:r}):null]}),(0,j.jsx)(mi,{asChild:!0,children:(0,j.jsx)(_i,{"aria-label":`关闭面板`,className:`icon-button`,children:(0,j.jsx)(pe,{size:18})})})]}),i]})]})})}function bi({title:e,detail:t}){return(0,j.jsxs)(`div`,{className:`empty-state`,children:[(0,j.jsx)(`strong`,{children:e}),t?(0,j.jsx)(`p`,{children:t}):null]})}var xi=[`codex`,`claude`,`pi`],Si={overview:`概览`,conversation:`对话`,configuration:`配置`,reminders:`提醒`,workspace:`工作区`,logs:`日志`},Ci=window.__LARKIN_DASHBOARD__||{packageVersion:`dev`,dashboardVersion:`dev`,buildFingerprint:``,csrfCapability:``},wi=`连不上 larkin:服务可能已停止或正在重启。确认 larkin 在运行后刷新页面。`,Ti=class extends Error{body;constructor(e,t){super(e),this.body=t}};async function Ei(e,t={}){let n;try{n=await fetch(e,{cache:`no-store`,...t})}catch(e){throw t.signal?.aborted?e:Error(wi)}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":Ci.csrfCapability},body:JSON.stringify(e)})}function ki(e,t={}){return Ei(e,{...t,headers:{...t.headers,"X-Larkin-CSRF":Ci.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`):`—`}var Ni=1,Pi=1440;function Fi(e){return String(e/6e4)}function Ii(e){let t=Number(String(e).trim()),n=t*6e4,r=Math.round(n);return Number.isFinite(t)&&t>=Ni&&t<=Pi&&Number.isSafeInteger(r)&&Math.abs(n-r)<=1e-6?r:null}function Li(e){return e.issue?`异常`:e.lastActivity?.state?String(e.lastActivity.state):e.running?`空闲`:`离线`}function Ri(){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)(xe()),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(Ci.buildFingerprint&&i.buildFingerprint&&i.buildFingerprint!==Ci.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 zi({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,j.jsx)(`span`,{className:`agent-avatar`,"aria-hidden":`true`,children:i?(0,j.jsx)(`img`,{src:`/api/avatar/${encodeURIComponent(e.agentId)}`,alt:``,onError:()=>r(!0)}):t})}function N({agents:e,selectedId:t,onSelect:n,compact:r=!1}){let[i,a]=(0,C.useState)(``),o=(0,C.useMemo)(()=>ye(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,j.jsxs)(`div`,{className:gi(`sidebar-inner`,r&&`compact-sidebar`),children:[(0,j.jsxs)(`label`,{className:`agent-search`,children:[(0,j.jsx)(de,{size:15,"aria-hidden":`true`}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索 Agent`}),(0,j.jsx)(`input`,{value:i,onChange:e=>a(e.target.value),placeholder:`搜索 Agent…`})]}),(0,j.jsxs)(`div`,{className:`agent-list`,role:`listbox`,"aria-label":`Agent 列表`,children:[o.map((e,r)=>(0,j.jsxs)(`button`,{ref:e=>{s.current[r]=e},type:`button`,role:`option`,"aria-selected":e.agentId===t,className:gi(`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,j.jsx)(zi,{agent:e}),(0,j.jsxs)(`span`,{className:`agent-list-copy`,children:[(0,j.jsx)(`strong`,{children:e.displayName||e.name}),(0,j.jsxs)(`small`,{children:[e.runtime,` · `,e.model,e.runtimeReadiness?` · ${e.runtimeReadiness.state}`:``]}),(0,j.jsxs)(`span`,{className:`status-line`,children:[(0,j.jsx)(ie,{size:10}),` `,e.connection.state,` · `,Li(e)]})]}),e.issue?(0,j.jsx)(A,{size:15,className:`danger`,"aria-label":`异常`}):(0,j.jsx)(re,{size:15,"aria-hidden":`true`})]},e.agentId)),o.length?null:(0,j.jsx)(bi,{title:`没有匹配的 Agent`,detail:`可按名称或 App ID 搜索`})]})]})}function P({open:e,onOpenChange:t,onSaved:n}){let[r,i]=(0,C.useState)(`require`),[a,o]=(0,C.useState)(`require`),[s,c]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[l,u]=(0,C.useState)({enabled:!1,intervalMs:9e5}),[d,f]=(0,C.useState)(`15`),[p,m]=(0,C.useState)(!1),[h,g]=(0,C.useState)(!1),[_,v]=(0,C.useState)(null),y=a!==r||l.enabled!==s.enabled||d!==Fi(s.intervalMs);return(0,C.useEffect)(()=>{if(!e)return;let t=new AbortController;return m(!0),ki(`/api/config?chats=0`,{signal:t.signal}).then(e=>{i(e.mentionPolicy),o(e.mentionPolicy),c(e.inboxAudit),u(e.inboxAudit),f(Fi(e.inboxAudit.intervalMs)),v(null)}).catch(e=>{t.signal.aborted||v(e.message)}).finally(()=>{t.signal.aborted||m(!1)}),()=>t.abort()},[e]),(0,j.jsx)(yi,{open:e,onOpenChange:e=>{!e&&y&&!window.confirm(`全局设置尚未保存,确定放弃草稿吗?`)||t(e)},title:`全局设置`,description:`只管理所有 Agent 共同继承的默认值;高级操作仍使用 CLI。`,children:(0,j.jsxs)(`div`,{className:`settings-form`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`真人群消息默认策略`}),(0,j.jsxs)(`select`,{value:a,onChange:e=>o(e.target.value),disabled:p,children:[(0,j.jsx)(`option`,{value:`require`,children:`require · 群内需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`free · 群内无需 @`})]})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`未单独设置的 Agent 和群会使用这个值。机器人消息仍必须精确 @ 当前 Agent。`}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`global-inbox-audit-title`,children:[(0,j.jsxs)(`div`,{className:`audit-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`global-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsx)(`p`,{children:`定时检查仍待处理的入站工作;关闭时不会安排巡检。`})]}),(0,j.jsxs)(`label`,{className:`audit-switch`,children:[(0,j.jsx)(`input`,{"aria-label":`Inbox 巡检开关`,type:`checkbox`,checked:l.enabled,disabled:p,onChange:e=>u(t=>({...t,enabled:e.target.checked}))}),(0,j.jsx)(`span`,{"aria-hidden":`true`}),(0,j.jsx)(`b`,{children:l.enabled?`已开启`:`已关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`全局巡检间隔(分钟)`,type:`number`,min:Ni,max:Pi,step:`any`,inputMode:`decimal`,value:d,disabled:p,onChange:e=>f(e.target.value)})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`可在关闭时预先调整;保存间隔本身不会开启巡检。`})]}),(0,j.jsxs)(`div`,{className:`form-actions`,children:[(0,j.jsx)(_i,{onClick:()=>{o(r),u(s),f(Fi(s.intervalMs))},disabled:!y||p,children:`放弃草稿`}),(0,j.jsx)(_i,{className:`primary`,onClick:async()=>{let e=l.enabled!==s.enabled||d!==Fi(s.intervalMs),t=e?Ii(d):null;if(e&&t===null){v(`巡检间隔必须在 1 到 1440 分钟之间。`);return}let o=t??s.intervalMs;m(!0),g(!0);try{let t=null;a!==r&&(t=await Oi({operation:`set-global-mention`,value:a})),e&&(t=await Oi({operation:`set-global-inbox-audit`,...l.enabled===s.enabled?{}:{enabled:l.enabled},...d===Fi(s.intervalMs)?{}:{intervalMs:o}})),i(a),c({...l,intervalMs:o}),u(e=>({...e,intervalMs:o})),f(Fi(o)),v(t?`已保存 · ${t.applyState} · ${t.revision.slice(0,20)}…`:`没有需要保存的修改`),n()}catch(e){v(e instanceof Error?e.message:String(e))}finally{m(!1),g(!1)}},disabled:!y||p,children:h?`保存中…`:p?`读取中…`:`保存全局设置`})]}),_?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:_}):null]})})}function Bi({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,j.jsxs)(`section`,{className:`group-policy-section`,"aria-labelledby":`group-policy-title`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`group-policy-title`,children:`群策略`}),(0,j.jsx)(`p`,{children:`这里只显示为当前 Agent 单独设置过消息策略的群。`})]}),(0,j.jsxs)(`label`,{className:`table-search`,children:[(0,j.jsx)(de,{size:14}),(0,j.jsx)(`span`,{className:`sr-only`,children:`搜索群`}),(0,j.jsx)(`input`,{value:r,onChange:e=>i(e.target.value),placeholder:`搜索群名或 chat_id`})]})]}),(0,j.jsxs)(`div`,{className:`policy-table-wrap`,children:[(0,j.jsxs)(`table`,{className:`policy-table`,children:[(0,j.jsx)(`thead`,{children:(0,j.jsxs)(`tr`,{children:[(0,j.jsx)(`th`,{children:`群`}),(0,j.jsx)(`th`,{children:`消息策略`}),(0,j.jsx)(`th`,{children:`操作`})]})}),(0,j.jsx)(`tbody`,{children:l.map(e=>(0,j.jsxs)(`tr`,{children:[(0,j.jsxs)(`td`,{children:[(0,j.jsx)(`strong`,{children:e.displayName||e.chatId}),e.displayName?(0,j.jsx)(`code`,{children:e.chatId}):(0,j.jsx)(`small`,{children:`群名暂时不可用`})]}),(0,j.jsx)(`td`,{children:(0,j.jsxs)(`select`,{"aria-label":`${e.displayName||e.chatId} 消息策略`,value:e.override,disabled:n,onChange:n=>void t(e.chatId,n.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})}),(0,j.jsx)(`td`,{children:(0,j.jsx)(_i,{disabled:n,onClick:()=>void t(e.chatId,`inherit`),children:`移除特别配置`})})]},e.chatId))})]}),l.length?null:(0,j.jsx)(bi,{title:r?`没有匹配的群`:`还没有单独配置的群`,detail:`新增后,Larkin 会自动解析并缓存群名。`})]}),(0,j.jsxs)(`div`,{className:`chat-add`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`完整 chat_id`}),(0,j.jsx)(`input`,{value:a,onChange:e=>o(e.target.value),placeholder:`oc_QAConfigChat1`})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`消息策略`}),(0,j.jsxs)(`select`,{value:s,onChange:e=>c(e.target.value),children:[(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]}),(0,j.jsx)(_i,{disabled:n||!/^oc_[A-Za-z0-9_-]+$/.test(a),onClick:()=>void t(a,s).then(()=>o(``)),children:`保存群策略`})]})]})}function Vi(e){return[`Readiness ${e.state}`,e.reason,e.nextAction].filter(Boolean).join(` · `)}function Hi({agentId:e,readiness:t,onDirtyChange:n,refreshKey:r}){let[i,a]=(0,C.useState)(null),[o,s]=(0,C.useState)({}),[c,l]=(0,C.useState)({}),[u,d]=(0,C.useState)(!0),[f,p]=(0,C.useState)(null),[m,h]=(0,C.useState)(null),g=(0,C.useRef)(xe()),_=(0,C.useRef)(xe()),v=!be(o,c);(0,C.useEffect)(()=>n(v),[v,n]),(0,C.useEffect)(()=>{let e=e=>{v&&e.preventDefault()};return window.addEventListener(`beforeunload`,e),()=>window.removeEventListener(`beforeunload`,e)},[v]);let y=(0,C.useCallback)(async(t=!1)=>{let n=g.current.issue();d(!0);try{let r=await ki(`/api/config?agent=${encodeURIComponent(e)}`);if(!g.current.accepts(n))return;let i=r.agents[0];if(!i)throw Error(`配置投影中没有当前 Agent`);let o=i.inboxAudit.override.intervalMs===`inherit`?i.inboxAudit.effective.intervalMs:i.inboxAudit.override.intervalMs,c={runtime:i.runtimeOption||i.runtime,model:i.model,effort:i.effort||`default`,mention:i.mention.override,auditEnabled:i.inboxAudit.override.enabled,auditIntervalInherited:i.inboxAudit.override.intervalMs===`inherit`,auditGapMinutes:Fi(o)};a(r),s(c),(!t||!v)&&l(c)}catch(e){g.current.accepts(n)&&p(e instanceof Error?e.message:String(e))}finally{g.current.accepts(n)&&d(!1)}},[e,v]);(0,C.useEffect)(()=>{y(!1)},[e]),(0,C.useEffect)(()=>{r>0&&y(!0)},[r]);let b=i?.agents[0],x=(e,t)=>l(n=>({...n,[e]:t})),S=String(c.runtime||b?.runtime||``),w=String(c.model||b?.model||`default`);(0,C.useEffect)(()=>{if(h({runtime:S,status:`loading`,models:[]}),!xi.includes(S)){h({runtime:S,status:`error`,models:[]});return}let t=_.current.issue(),n=new AbortController;return ki(`/api/models/${S}?agent=${encodeURIComponent(e)}`,{signal:n.signal}).then(e=>{_.current.accepts(t)&&h({runtime:S,status:`ready`,models:e.models})}).catch(e=>{!n.signal.aborted&&_.current.accepts(t)&&(h({runtime:S,status:`error`,models:[]}),p(`${S} 模型列表暂时不可用:${e instanceof Error?e.message:String(e)}`))}),()=>n.abort()},[e,S]);let ee=m?.runtime===S&&m.status===`ready`,te=m?.runtime===S?m.status:`loading`,T=ee?m.models:[],E=`${w} · 当前配置(模型目录${te===`error`?`不可用`:`加载中`})`,ne=ee?T.some(e=>e.id===w)?T:[{id:w,label:`${w} · 当前配置`},...T]:[{id:w,label:E}],re=ee&&w!=="default"&&T.find(e=>e.id===w)?.supportedReasoningEfforts||[],ie=c.runtime!==o.runtime||c.model!==o.model||c.effort!==o.effort,ae=c.auditEnabled===`on`||c.auditEnabled===`off`?c.auditEnabled:`inherit`,oe=c.auditIntervalInherited===!0,se=String(c.auditGapMinutes||``),ce=c.auditEnabled!==o.auditEnabled||c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes,D=()=>Ei(`/api/config/apply`,{method:`POST`,headers:{"Content-Type":`application/json`,"X-Larkin-CSRF":Ci.csrfCapability},body:JSON.stringify({agentId:e})}),O=async()=>{if(!b)return;let t=Ii(c.auditGapMinutes);if(ce&&!oe&&t===null){p(`巡检间隔必须在 1 到 1440 分钟之间;草稿已保留。`);return}d(!0);try{let n=[];c.runtime===o.runtime?c.model!==o.model&&n.push({operation:`set-agent-model`,agentId:e,model:c.model}):n.push({operation:`set-agent-runtime`,agentId:e,runtime:c.runtime,model:c.model}),c.effort!==o.effort&&n.push({operation:`set-agent-effort`,agentId:e,effort:c.effort}),c.mention!==o.mention&&n.push({operation:`set-agent-mention`,agentId:e,value:c.mention}),ce&&n.push({operation:`set-agent-inbox-audit`,agentId:e,...c.auditEnabled===o.auditEnabled?{}:{enabled:ae===`inherit`?`inherit`:ae===`on`},...c.auditIntervalInherited!==o.auditIntervalInherited||c.auditGapMinutes!==o.auditGapMinutes?{intervalMs:oe?`inherit`:t}:{}});let r=null;for(let e of n)r=await Oi(e);if(ie)try{await D(),p(`配置已保存并应用`)}catch(e){p(`配置已保存但未应用:${Di(e,`稍后重试`)}`)}else p(r?`配置已保存`:`没有需要保存的修改`);await y(!1)}catch(e){p(Di(e,e instanceof Error?e.message:String(e)))}finally{d(!1)}},le=async()=>{d(!0);try{await D(),p(`已应用保存的运行配置`),await y(!0)}catch(e){p(`配置保持已保存但未应用:${Di(e,`稍后重试`)}`)}finally{d(!1)}},de=async(t,n)=>{d(!0);try{let r=await Oi({operation:`set-chat-mention`,agentId:e,chatId:t,value:n});p(`群策略已保存 · ${r.applyState}`),await y(!0)}catch(e){p(e instanceof Error?e.message:String(e))}finally{d(!1)}};if(!b&&u)return(0,j.jsx)(bi,{title:`加载 Agent 配置…`});if(!b)return(0,j.jsx)(bi,{title:`配置不可用`,detail:f||`请重试`});let fe=b.inboxAudit.effective,k=b.inboxAudit.source,A=`${k.enabled===`agent`?`当前 Agent 已单独设置`:k.enabled===`global`?`当前使用全局设置`:`当前使用默认设置`}:${fe.enabled?`已开启`:`已关闭`},每 ${Fi(fe.intervalMs)} 分钟`,pe=b.apply.applyState===`pending`?`待应用`:b.apply.applyState===`applied`?`已应用`:`状态未知`;return(0,j.jsxs)(`div`,{className:`configuration-page`,children:[(0,j.jsxs)(`section`,{className:`config-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`Agent 配置`}),(0,j.jsxs)(`p`,{children:[`只作用于 `,e,`;运行配置会在安全时机自动应用,Agent 正忙时会保留待处理。`]})]}),(0,j.jsx)(vi,{className:b.apply.applyState===`pending`?`warning`:`success`,children:pe})]}),(0,j.jsxs)(`p`,{className:`cli-guidance`,children:[`这里只提供常用微调。更多配置可以直接告诉当前 Agent,让它运行 `,(0,j.jsx)(`code`,{children:`larkin config --help`}),` 后完成。`]}),t?(0,j.jsx)(`p`,{className:`field-help`,children:Vi(t)}):null,(0,j.jsxs)(`div`,{className:`config-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Runtime`}),(0,j.jsx)(`select`,{value:S,disabled:u,onChange:e=>{x(`runtime`,e.target.value),x(`model`,`default`),x(`effort`,`default`)},children:(i?.runtimeOptions||xi).map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Model`}),(0,j.jsx)(`select`,{value:w,disabled:u||!ee,onChange:e=>{x(`model`,e.target.value),e.target.value==="default"&&x(`effort`,`default`)},children:ne.map(e=>(0,j.jsx)(`option`,{value:e.id,children:e.label||e.id},e.id))})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`Effort`}),(0,j.jsxs)(`select`,{value:String(c.effort||`default`),disabled:u||!ee||w==="default"||!re.length,onChange:e=>x(`effort`,e.target.value),children:[(0,j.jsx)(`option`,{value:`default`,children:`default · 不指定`}),re.map(e=>(0,j.jsx)(`option`,{value:e,children:e},e))]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`群消息策略`}),(0,j.jsxs)(`select`,{value:String(c.mention||`inherit`),disabled:u,onChange:e=>x(`mention`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`require`,children:`需要 @`}),(0,j.jsx)(`option`,{value:`free`,children:`无需 @`})]})]})]}),(0,j.jsxs)(`div`,{className:`audit-controls`,"aria-labelledby":`agent-inbox-audit-title`,children:[(0,j.jsx)(`div`,{className:`audit-heading`,children:(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{id:`agent-inbox-audit-title`,children:`Inbox 巡检`}),(0,j.jsxs)(`p`,{children:[A,`。`]})]})}),(0,j.jsxs)(`div`,{className:`config-grid audit-grid`,children:[(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检设置`}),(0,j.jsxs)(`select`,{"aria-label":`Inbox 巡检设置`,value:ae,disabled:u,onChange:e=>x(`auditEnabled`,e.target.value),children:[(0,j.jsx)(`option`,{value:`inherit`,children:`跟随全局设置`}),(0,j.jsx)(`option`,{value:`on`,children:`为此 Agent 开启`}),(0,j.jsx)(`option`,{value:`off`,children:`为此 Agent 关闭`})]})]}),(0,j.jsxs)(`label`,{children:[(0,j.jsx)(`span`,{children:`巡检间隔(分钟)`}),(0,j.jsx)(`input`,{"aria-label":`Agent 巡检间隔(分钟)`,type:`number`,min:Ni,max:Pi,step:`any`,inputMode:`decimal`,value:se,disabled:u,onChange:e=>l(t=>({...t,auditGapMinutes:e.target.value,auditIntervalInherited:!1}))})]})]}),(0,j.jsxs)(`div`,{className:`audit-inline-actions`,children:[(0,j.jsx)(`span`,{children:oe?`间隔跟随全局设置;编辑数字后会仅作用于此 Agent。`:`此 Agent 使用单独的巡检间隔。`}),oe?null:(0,j.jsx)(_i,{disabled:u,onClick:()=>l(e=>({...e,auditIntervalInherited:!0,auditGapMinutes:Fi(fe.intervalMs)})),children:`间隔跟随全局设置`})]}),(0,j.jsx)(`p`,{className:`field-help`,children:`即使关闭巡检,也可保存间隔;间隔修改不会开启巡检。`})]}),v||b.apply.applyState===`pending`?(0,j.jsxs)(`div`,{className:`form-actions`,children:[v?(0,j.jsx)(_i,{disabled:u,onClick:()=>l(o),children:`放弃草稿`}):null,v?(0,j.jsx)(_i,{className:`primary`,disabled:u,onClick:O,children:u?`处理中…`:ie?`保存并应用`:`保存 Agent 配置`}):(0,j.jsxs)(_i,{className:`primary`,disabled:u,onClick:le,children:[(0,j.jsx)(ue,{size:14}),` 应用已保存配置`]})]}):null,f?(0,j.jsx)(`p`,{role:`status`,className:`feedback`,children:f}):null]}),(0,j.jsx)(Bi,{config:b,onSave:de,disabled:u})]})}function Ui({agent:e}){let t=e.session?.usage||{},n=e.session?.compaction||{};return(0,j.jsxs)(`div`,{className:`overview-page`,children:[(0,j.jsxs)(`div`,{className:`metrics-band`,"aria-label":`Agent 运行指标`,children:[(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ie,{})}),(0,j.jsx)(`span`,{children:`连接`}),(0,j.jsx)(`strong`,{children:e.connection.state}),(0,j.jsx)(`small`,{children:e.connection.reason})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`执行状态`}),(0,j.jsx)(`strong`,{children:Li(e)}),(0,j.jsx)(`small`,{children:e.lastActivity?Ai(e.lastActivity.ageSec):`尚无活动`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(le,{})}),(0,j.jsx)(`span`,{children:`最近消息`}),(0,j.jsx)(`strong`,{children:e.lastDeliver?.from||`暂无`}),(0,j.jsx)(`small`,{children:e.lastDeliver?`${e.lastDeliver.target||``} · ${Ai(e.lastDeliver.ageSec)}`:`尚无消息记录`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`上下文`}),(0,j.jsx)(`strong`,{children:t.contextPercent==null?`${e.session?.turns||0} turns`:`${Math.round(Number(t.contextPercent))}%`}),(0,j.jsx)(`small`,{children:e.session?`session ${e.session.id.slice(0,12)}…`:`尚未建立 session`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ce,{})}),(0,j.jsx)(`span`,{children:`累计 Token`}),(0,j.jsx)(`strong`,{children:Mi(t.cumulativeTokens)}),(0,j.jsx)(`small`,{children:t.available?`完整 session 累计`:t.reason||`暂无用量`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(E,{})}),(0,j.jsx)(`span`,{children:`最近一轮用量`}),(0,j.jsx)(`strong`,{children:Mi(t.latestTokens)}),(0,j.jsx)(`small`,{children:`输入、缓存与输出合计`})]}),(0,j.jsxs)(`section`,{className:`metric-stat`,children:[(0,j.jsx)(`div`,{className:`metric-icon`,children:(0,j.jsx)(ue,{})}),(0,j.jsx)(`span`,{children:`Compaction`}),(0,j.jsx)(`strong`,{children:Mi(n.count)}),(0,j.jsx)(`small`,{children:n.active?`正在压缩上下文`:n.lastFinishedAt?`最近 ${ji(n.lastFinishedAt)}`:`尚无压缩记录`})]})]}),(0,j.jsxs)(`div`,{className:`overview-sections`,children:[(0,j.jsxs)(`section`,{className:`content-section wide`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`最近动态`}),(0,j.jsx)(`p`,{children:`当前 Agent 的消息、工具和状态转换。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Wi,{items:e.feed.slice(0,10)})]}),(0,j.jsxs)(`section`,{className:`content-section`,children:[(0,j.jsx)(`h3`,{children:`运行摘要`}),(0,j.jsxs)(`dl`,{className:`facts`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Runtime`}),(0,j.jsx)(`dd`,{children:e.runtime})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Readiness`}),(0,j.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,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Model`}),(0,j.jsx)(`dd`,{children:e.model})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`Effort`}),(0,j.jsx)(`dd`,{children:e.effort||`default`})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`入站`}),(0,j.jsx)(`dd`,{children:e.inbound.state})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`提醒`}),(0,j.jsx)(`dd`,{children:e.activeReminders})]}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`dt`,{children:`指示灯`}),(0,j.jsx)(`dd`,{children:e.eyeIndicator.stuck?`疑似卡住`:`${e.eyeIndicator.pendingCount} pending`})]})]})]})]})]})}function Wi({items:e}){if(!e.length)return(0,j.jsx)(bi,{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,j.jsx)(`ol`,{className:`timeline`,children:e.map((e,n)=>(0,j.jsxs)(`li`,{children:[(0,j.jsx)(`span`,{className:gi(`timeline-dot`,t(e))}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.kind===`deliver`?`收到消息`:e.kind===`error`?`错误`:e.state||`活动`}),(0,j.jsx)(`p`,{children:e.text||e.detail||[e.from,e.target].filter(Boolean).join(` → `)||e.tool||`状态已更新`}),(0,j.jsx)(`time`,{children:ji(e.at)})]})]},`${e.at}-${n}`))})}function Gi({agent:e}){return e.conversation.length?(0,j.jsx)(`div`,{className:`conversation-list`,children:e.conversation.map((t,n)=>{let r=t.direction===`out`?`已发送`:t.wake?`入站 · 已唤醒`:`旁听 · 未唤醒`;return(0,j.jsxs)(`article`,{className:gi(`conversation-item`,t.direction===`out`?`outbound`:`inbound`,t.direction===`in`&&!t.wake&&`listening`),children:[(0,j.jsxs)(`header`,{children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(vi,{className:t.direction===`in`&&!t.wake?`warning`:t.direction===`out`?`accent`:`success`,children:r}),(0,j.jsx)(`strong`,{children:t.from||(t.direction===`out`?e.displayName:`未知发送者`)})]}),(0,j.jsx)(`time`,{children:ji(t.at)})]}),(0,j.jsx)(`p`,{children:t.text||`[非文本消息]`}),t.target?(0,j.jsx)(`small`,{children:t.target}):null]},`${t.at}-${n}`)})}):(0,j.jsx)(bi,{title:`还没有对话摘录`,detail:`Host 更新本地 conversation projection 后会显示在这里。`})}function Ki({agent:e}){if(!e.remindersList.length)return(0,j.jsx)(bi,{title:`还没有提醒`});let t={scheduled:`待执行`,pending:`待执行`,fired:`已完成`,canceled:`已取消`,cancelled:`已取消`,failed:`失败`};return(0,j.jsx)(`div`,{className:`reminder-list`,children:e.remindersList.map((e,n)=>{let r=e.status||`scheduled`,i=[`fired`,`canceled`,`cancelled`].includes(r);return(0,j.jsxs)(`article`,{className:gi(`status-${r===`cancelled`?`canceled`:r}`,i&&`done`),children:[(0,j.jsx)(ne,{size:16}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`strong`,{children:e.title||`未命名提醒`}),(0,j.jsxs)(`p`,{children:[ji(e.fireAt),e.repeat?` · 重复`:``]})]}),(0,j.jsx)(vi,{className:[`scheduled`,`pending`].includes(r)?`warning`:r===`fired`?`success`:r===`failed`?`danger-badge`:``,children:t[r]||r})]},`${e.fireAt}-${n}`)})})}function qi({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)(xe()),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,j.jsxs)(`div`,{className:`workspace-browser`,children:[(0,j.jsxs)(`div`,{className:`workspace-side`,children:[(0,j.jsxs)(`nav`,{className:`breadcrumbs`,"aria-label":`工作区路径`,children:[(0,j.jsx)(`button`,{onClick:()=>void u(``),children:`workspace`}),d.map((e,t)=>(0,j.jsxs)(`span`,{children:[(0,j.jsx)(re,{size:12}),(0,j.jsx)(`button`,{onClick:()=>void u(d.slice(0,t+1).join(`/`)),children:e})]},`${e}-${t}`))]}),(0,j.jsxs)(`div`,{className:`workspace-list`,role:`region`,"aria-label":`工作区目录`,children:[t?.entries.map(e=>(0,j.jsxs)(`button`,{onClick:()=>void u(e.path),children:[e.kind===`directory`?(0,j.jsx)(se,{size:15}):(0,j.jsx)(oe,{size:15}),(0,j.jsx)(`span`,{children:e.name}),(0,j.jsx)(`small`,{children:e.kind===`file`?`${e.size} B`:`目录`})]},e.path)),!a&&t&&!t.entries.length?(0,j.jsx)(bi,{title:`空目录`}):null]})]}),(0,j.jsx)(`div`,{className:`workspace-preview`,role:`region`,"aria-label":`文件预览`,children:a?(0,j.jsx)(bi,{title:`读取工作区…`}):s?(0,j.jsx)(bi,{title:`工作区读取失败`,detail:s}):r?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`header`,{children:[(0,j.jsx)(`strong`,{children:r.name}),(0,j.jsx)(`small`,{children:r.truncated?`仅预览前 1 MB`:`${r.size} B`})]}),r.binary?(0,j.jsx)(bi,{title:`二进制文件不可预览`}):(0,j.jsx)(`pre`,{children:r.content})]}):(0,j.jsx)(bi,{title:`只读工作区`,detail:`选择文件进行预览`})})]})}function Ji({agent:e}){return(0,j.jsxs)(`section`,{className:`logs-section`,children:[(0,j.jsxs)(`div`,{className:`section-heading`,children:[(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`h3`,{children:`结构化日志`}),(0,j.jsx)(`p`,{children:`最近 30 条本地状态投影;不暴露原始日志文件或路径。`})]}),(0,j.jsx)(vi,{children:e.feed.length})]}),(0,j.jsx)(Wi,{items:e.feed}),e.recentErrors.length?(0,j.jsxs)(`section`,{className:`error-stack`,children:[(0,j.jsx)(`h4`,{children:`最近错误`}),e.recentErrors.map((e,t)=>(0,j.jsxs)(`div`,{children:[(0,j.jsx)(ae,{size:14}),(0,j.jsx)(`span`,{children:e.text||e.message||`未知错误`}),(0,j.jsx)(`time`,{children:ji(e.at)})]},`${e.at}-${t}`))]}):null]})}function Yi({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,j.jsxs)(`header`,{className:`agent-header`,children:[(0,j.jsxs)(`div`,{className:`agent-title`,children:[(0,j.jsx)(zi,{agent:e}),(0,j.jsxs)(`div`,{children:[(0,j.jsx)(`p`,{children:e.agentId}),(0,j.jsx)(`h1`,{children:e.displayName||e.name})]})]}),(0,j.jsxs)(`div`,{className:`agent-header-summary`,children:[(0,j.jsx)(vi,{className:e.connection.state===`connected`?`success`:e.issue?`danger-badge`:``,children:e.connection.state}),(0,j.jsx)(`span`,{children:e.runtime}),(0,j.jsx)(`span`,{children:e.model}),(0,j.jsx)(`span`,{children:e.effort||`default effort`}),(0,j.jsx)(`span`,{children:r}),(0,j.jsxs)(`span`,{children:[`累计 `,Mi(t.cumulativeTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`最近一轮 `,Mi(t.latestTokens),` tokens`]}),(0,j.jsxs)(`span`,{children:[`Compact `,Mi(n.count),` 次 · `,n.active?`active`:`idle`]}),(0,j.jsxs)(`span`,{children:[`最近消息 `,e.lastDeliver?Ai(e.lastDeliver.ageSec):`暂无`]}),(0,j.jsx)(`span`,{children:e.lastActivity?.detail||Li(e)})]})]})}function Xi(){let e=Ri(),t=(0,C.useMemo)(()=>ge(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||Ci.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,``,_e(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=ve(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=ge(window.location.search);if(f&&!window.confirm(`Agent 配置尚未保存,确定离开吗?`)){window.history.pushState(null,``,_e(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,j.jsxs)(`div`,{className:gi(`app-shell`,i===`workspace`&&`workspace-app-active`),children:[(0,j.jsxs)(`aside`,{className:`desktop-sidebar`,children:[(0,j.jsxs)(`div`,{className:`brand`,children:[(0,j.jsx)(`img`,{src:`/assets/larkin-mark.svg`,alt:``}),(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]})]}),(0,j.jsx)(N,{agents:_,selectedId:n,onSelect:ee}),(0,j.jsx)(`footer`,{children:e.data?.daemon.running?`${_.length} Agents · daemon online`:`daemon offline`})]}),(0,j.jsxs)(`main`,{className:gi(`workspace-shell`,i===`workspace`&&`workspace-route-active`),children:[(0,j.jsxs)(`div`,{className:`app-topbar`,children:[(0,j.jsx)(_i,{className:`mobile-menu`,"aria-label":`打开 Agent 导航`,onClick:()=>l(!0),children:(0,j.jsx)(O,{size:18})}),(0,j.jsxs)(`div`,{children:[(0,j.jsxs)(`strong`,{children:[`Larkin `,(0,j.jsxs)(`span`,{className:`brand-version`,children:[`v`,v]})]}),(0,j.jsx)(`span`,{children:e.data?.daemon.running?`运行中`:`未运行`}),e.stale?(0,j.jsx)(vi,{className:`warning`,children:`状态陈旧`}):null]}),(0,j.jsxs)(_i,{onClick:()=>s(!0),children:[(0,j.jsx)(fe,{size:15}),` 全局设置`]})]}),e.error?(0,j.jsxs)(`div`,{className:`stale-banner`,role:`alert`,children:[(0,j.jsx)(A,{size:15}),`状态刷新失败,保留最后一次数据:`,e.error,(0,j.jsx)(_i,{onClick:()=>void e.retry(),children:`重试`})]}):null,m?(0,j.jsxs)(`div`,{className:`notice`,role:`status`,children:[m,(0,j.jsx)(_i,{"aria-label":`关闭提示`,onClick:()=>h(null),children:`×`})]}):null,e.loading&&!e.data?(0,j.jsxs)(`div`,{className:`page-loading`,children:[(0,j.jsx)(ue,{className:`spin`}),`加载 Agent 工作台…`]}):y?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(Yi,{agent:y}),(0,j.jsx)(`nav`,{className:`agent-tabs`,"aria-label":`${y.displayName} 内容页签`,role:`tablist`,children:he.map(e=>(0,j.jsxs)(`button`,{type:`button`,role:`tab`,"aria-selected":i===e,className:i===e?`active`:``,onClick:()=>te(e),children:[e===`overview`?(0,j.jsx)(ce,{}):e===`conversation`?(0,j.jsx)(le,{}):e===`configuration`?(0,j.jsx)(k,{}):e===`reminders`?(0,j.jsx)(ne,{}):e===`workspace`?(0,j.jsx)(se,{}):(0,j.jsx)(D,{}),Si[e]]},e))}),(0,j.jsxs)(`div`,{className:gi(`agent-content`,i===`workspace`&&`workspace-active`),children:[(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`overview`,children:(0,j.jsx)(Ui,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`conversation`,children:(0,j.jsx)(Gi,{agent:y})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`configuration`,children:(0,j.jsx)(Hi,{agentId:y.agentId,readiness:y.runtimeReadiness??null,onDirtyChange:p,refreshKey:u})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`reminders`,children:(0,j.jsx)(Ki,{agent:y})}),(0,j.jsx)(`section`,{className:`workspace-tab-panel`,role:`tabpanel`,hidden:i!==`workspace`,children:(0,j.jsx)(qi,{agentId:y.agentId})}),(0,j.jsx)(`section`,{role:`tabpanel`,hidden:i!==`logs`,children:(0,j.jsx)(Ji,{agent:y})})]},y.agentId)]}):(0,j.jsx)(bi,{title:`还没有配置任何 Agent`,detail:`先运行 larkin setup。`})]}),(0,j.jsx)(P,{open:o,onOpenChange:s,onSaved:()=>d(e=>e+1)}),(0,j.jsx)(yi,{open:c,onOpenChange:l,title:`选择 Agent`,description:`搜索、查看状态并切换当前工作区。`,children:(0,j.jsx)(N,{compact:!0,agents:_,selectedId:n,onSelect:ee})})]})}var Zi=document.getElementById(`root`);if(!Zi)throw Error(`dashboard root is missing`);(0,me.createRoot)(Zi).render((0,j.jsx)(C.StrictMode,{children:(0,j.jsx)(Xi,{})}));
@@ -1221,6 +1221,9 @@ function createRuntimeHost(options) {
1221
1221
  agent.config.sessionId = event.sessionId;
1222
1222
  emit({ type: "session", agentId: agent.config.agentId, runtime: agent.adapter.id, sessionId: event.sessionId, launchId: agent.launchId,
1223
1223
  ...(event.model ? { model: event.model } : {}), ...(event.reasoningEffort ? { reasoningEffort: event.reasoningEffort } : {}) });
1224
+ // A runtime can defer input until its session is ready (Codex before thread/start or thread/resume returns);
1225
+ // nothing else would resubmit that delivery, so retry pending work once the session reports in.
1226
+ void retryPending(agent);
1224
1227
  }
1225
1228
  else if (event.type === "turn-start") {
1226
1229
  // Clear a source left by a crashed/aborted prior turn before a new turn
@@ -1212,6 +1212,9 @@ export function createRuntimeHost(options) {
1212
1212
  agent.config.sessionId = event.sessionId;
1213
1213
  emit({ type: "session", agentId: agent.config.agentId, runtime: agent.adapter.id, sessionId: event.sessionId, launchId: agent.launchId,
1214
1214
  ...(event.model ? { model: event.model } : {}), ...(event.reasoningEffort ? { reasoningEffort: event.reasoningEffort } : {}) });
1215
+ // A runtime can defer input until its session is ready (Codex before thread/start or thread/resume returns);
1216
+ // nothing else would resubmit that delivery, so retry pending work once the session reports in.
1217
+ void retryPending(agent);
1215
1218
  }
1216
1219
  else if (event.type === "turn-start") {
1217
1220
  // Clear a source left by a crashed/aborted prior turn before a new turn
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkin",
3
- "version": "0.5.19",
3
+ "version": "0.5.20",
4
4
  "private": false,
5
5
  "larkinPackageRole": "npm-published",
6
6
  "files": [