@tyr1105/dsh-buddy 0.0.0-stage → 0.1.0

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.
package/lib/client.js ADDED
@@ -0,0 +1,2561 @@
1
+ window.__ModuleLoader__.load({ id: '@tyr1105/dsh-buddy', factory: function (require) { var module = { exports: {} }; var exports = module.exports;
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
+
23
+ // src/client/index.tsx
24
+ var index_exports = {};
25
+ __export(index_exports, {
26
+ HubEntryIcon: () => HubEntryIcon,
27
+ apply: () => apply,
28
+ inject: () => inject
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/client/styles.css
33
+ var styles_default = "/*\n * \u961F\u53CB\u9875\u9762\u6837\u5F0F\u3002\u7EA6\u675F\uFF08docs/prd/sections/16-ui.md 16.0 \u4E0E\u4EFB\u52A1\u5361\uFF09\uFF1A\n * - \u989C\u8272\u53EA\u7528 var(--dsw-alias-*) \u4E0E --dsw-radius-* / --dsw-shadow-* \u7B49\u4E3B\u9898 token\uFF0C\n * \u4E0D\u51FA\u73B0\u5341\u516D\u8FDB\u5236\u3001rgb\u3001hsl\uFF08transparent / currentColor \u9664\u5916\uFF09\uFF0C\u4EAE\u6697\u81EA\u52A8\u8DDF\u968F\u3002\n * - \u4E2D\u680F 720\u20131600px \u53EF\u7528\uFF1B\u7A84\u4E8E 900px \u65F6\u53F3\u680F\u6539\u4E3A\u62BD\u5C49\u3002\n * - macOS \u6807\u9898\u533A\u53EA\u5728 [data-platform=darwin] \u4E0B\u7559\u51FA\u5218\u6D77\u7A7A\u95F4\u3002\n */\n\n/*\n * \u4E3B\u9898\u53D8\u91CF\u522B\u540D\u5C42\uFF08\u89C6\u89C9\u4FEE\u590D\uFF09\uFF1A\u672C\u6837\u5F0F\u8868\u5386\u53F2\u4E0A\u4F7F\u7528\u7684\u5206\u9694\u7EBF/\u5E95\u8272/\u5F3A\u8C03\u8272\u7B49\u53D8\u91CF\u540D\n * \u4E0E\u5BBF\u4E3B\u5B9E\u9645\u63D0\u4F9B\u7684\u4E3B\u9898 token \u540D\u4E0D\u4E00\u81F4\uFF08\u5BBF\u4E3B\u53EA\u6709 border-l1/l2\u3001bg-layer-1/2\u3001\n * brand-primary\u3001label-primary/secondary\u3001state-* \u7B49\uFF09\u3002\u5BF9\u4E0D\u4E0A\u7684\u53D8\u91CF\u5728\u8BA1\u7B97\u65F6\n * \u6574\u6761\u58F0\u660E\u5931\u6548\uFF0C\u4E8E\u662F\u51FA\u73B0\u300C\u6309\u94AE\u6CA1\u6709\u8FB9\u6846\u3001\u6CA1\u6709\u80CC\u666F\u3001hover \u6CA1\u6709\u53CD\u9988\u300D\u3002\n * \u8FD9\u91CC\u5728 .ah-page \u4E0A\u628A\u65E7\u540D\u4E00\u4E00\u6620\u5C04\u5230\u771F\u5B9E token\uFF08\u627E\u4E0D\u5230\u7684\u4E2D\u95F4\u8C03\u7528 color-mix\n * \u4ECE\u54C1\u724C\u8272\u6D3E\u751F\uFF0C\u4E0D\u5199\u6B7B\u4EFB\u4F55\u5341\u516D\u8FDB\u5236\uFF09\uFF0C\u4E0B\u65B9\u6837\u5F0F\u65E0\u9700\u9010\u884C\u4FEE\u6539\u3002\n * .ah-toast-layer \u6302\u5728 shell.overlay \u63D2\u69FD\u3001\u4E0D\u5728\u9875\u9762\u5B50\u6811\u91CC\uFF0C\u6240\u4EE5\u4E00\u5E76\u58F0\u660E\u3002\n */\n.ah-page,\n.ah-toast-layer {\n /* \u5206\u9694\u7EBF\uFF1A\u65E7 separator / separator-strong \u2192 \u5BBF\u4E3B border-l1 / border-l2 */\n --dsw-alias-separator: var(--dsw-alias-border-l1, var(--dsw-alias-border-l2));\n --dsw-alias-separator-strong: var(--dsw-alias-border-l2, var(--dsw-alias-border-l1));\n /* \u5E95\u8272\uFF1A\u65E7 inset / elevated \u2192 \u5BBF\u4E3B bg-layer-1 / bg-layer-2 */\n --dsw-alias-bg-inset: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-base));\n --dsw-alias-bg-elevated: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));\n /* \u5F3A\u8C03\u8272\uFF1A\u65E7 accent \u2192 \u5BBF\u4E3B brand-primary\uFF1B\u6DE1\u5F3A\u8C03\u5E95\u7528\u5B83\u6D3E\u751F */\n --dsw-alias-accent: var(--dsw-alias-brand-primary);\n --dsw-alias-accent-muted: color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);\n /* \u6587\u5B57\uFF1A\u65E7 text-primary/secondary \u2192 \u5BBF\u4E3B label-primary/secondary */\n --dsw-alias-text-primary: var(--dsw-alias-label-primary);\n --dsw-alias-text-secondary: var(--dsw-alias-label-secondary);\n --dsw-alias-text-danger: var(--dsw-alias-state-error-primary);\n /* \u72B6\u6001\u8272 */\n --dsw-alias-danger: var(--dsw-alias-state-error-primary);\n --dsw-alias-success: var(--dsw-alias-state-success-primary);\n --dsw-alias-warning: var(--dsw-alias-state-warn-primary);\n --dsw-alias-warning-muted: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n /* \u60AC\u505C / \u9009\u4E2D\u5E95\u8272\uFF1A\u5BBF\u4E3B\u672A\u63D0\u4F9B\uFF0C\u4ECE\u6587\u5B57\u8272\u4F4E\u6BD4\u4F8B\u6D3E\u751F\uFF08\u4EAE\u6697\u4E3B\u9898\u90FD\u6210\u7ACB\uFF09 */\n --dsw-alias-fill-hover: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n --dsw-alias-fill-selected: color-mix(in srgb, var(--dsw-alias-label-primary) 12%, transparent);\n /* \u5F3A\u8C03\u5E95\u4E0A\u7684\u6587\u5B57\uFF1A\u7528\u57FA\u7840\u5E95\u8272\uFF08\u6697\u8272\u4E3B\u9898=\u6D45\u5B57\u3001\u4EAE\u8272\u4E3B\u9898=\u6DF1\u5B57\uFF0C\u5728\u54C1\u724C\u8272\u4E0A\u90FD\u53EF\u8BFB\uFF09 */\n --dsw-alias-text-on-accent: var(--dsw-alias-bg-base);\n /* \u906E\u7F69\u4E0E\u6D6E\u5C42 */\n --dsw-alias-scrim: color-mix(in srgb, var(--dsw-alias-label-primary) 32%, transparent);\n --dsw-alias-toast-bg: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-layer-2));\n --dsw-alias-toast-label: var(--dsw-alias-label-primary);\n /* \u9634\u5F71\uFF1A\u5BBF\u4E3B\u672A\u66B4\u9732 shadow token\uFF0C\u7528\u6587\u5B57\u8272\u6D3E\u751F\u7684\u8F6F\u6295\u5F71\uFF0C\u8DDF\u968F\u4E3B\u9898\u6DF1\u6D45 */\n --dsw-shadow-lv2: 0 4px 16px color-mix(in srgb, var(--dsw-alias-label-primary) 18%, transparent);\n --dsw-shadow-lv3: 0 8px 28px color-mix(in srgb, var(--dsw-alias-label-primary) 26%, transparent);\n}\n\n.ah-page {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n background: var(--dsw-alias-bg-base);\n color: var(--dsw-alias-text-primary);\n}\n\n/* \u6574\u9875\u4E0D\u53EF\u7528\u63D0\u793A */\n.ah-page-down { align-items: center; justify-content: center; }\n.ah-down { text-align: center; display: grid; gap: 12px; justify-items: center; }\n\n/* ---------- \u9876\u680F ---------- */\n.ah-topbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 20px;\n border-bottom: 1px solid var(--dsw-alias-separator);\n}\n.ah-topbar h1 { font-size: 20px; font-weight: 500; margin: 0; }\n.ah-topbar-title { display: flex; align-items: baseline; gap: 10px; padding-top: calc(0px + var(--dsh-frame-top-clearance, 0px)); }\n.ah-topbar-now { font-size: 13px; color: var(--dsw-alias-text-secondary); }\n.ah-topbar-status { display: flex; align-items: center; gap: 10px; }\n\n/* macOS\uFF1A\u6807\u9898\u7ED9\u5218\u6D77/\u6807\u9898\u533A\u7559\u7A7A\u95F4 */\n[data-platform='darwin'] .ah-topbar-title { padding-top: calc(28px + var(--dsh-frame-top-clearance, 0px)); }\n\n/* ---------- \u4E09\u680F\u9AA8\u67B6 ---------- */\n.ah-body { display: flex; flex: 1; min-height: 0; }\n.ah-botlist { width: 260px; flex-shrink: 0; display: flex; flex-direction: column; border-right: 1px solid var(--dsw-alias-separator); overflow-y: auto; }\n.ah-thread { flex: 1; min-width: 0; display: flex; flex-direction: column; }\n.ah-right { width: 320px; flex-shrink: 0; border-left: 1px solid var(--dsw-alias-separator); display: flex; flex-direction: column; min-height: 0; }\n.ah-right-scroll { overflow-y: auto; flex: 1; padding: 0 16px; }\n\n/* \u4E2D\u680F\u6700\u5C0F\u53EF\u7528\u5BBD\u5EA6 */\n.ah-thread { min-width: 400px; }\n@media (max-width: 900px) {\n /* \u7A84\u5C4F\uFF1A\u53F3\u680F\u6539\u6210\u62BD\u5C49\uFF08\u76D6\u5728\u7EBF\u7A0B\u4E0A\uFF0C\u53EF\u6536\u8D77\uFF09 */\n .ah-right {\n position: absolute;\n top: 0; right: 0; bottom: 0;\n z-index: 30;\n box-shadow: var(--dsw-shadow-lv2);\n background: var(--dsw-alias-bg-base);\n width: min(320px, 85vw);\n }\n .ah-botlist { width: 200px; }\n}\n\n/* ---------- \u5DE6\u680F\u5217\u8868 ---------- */\n.ah-bot-row {\n display: flex; align-items: center; gap: 10px;\n width: 100%; padding: 10px 12px;\n background: transparent; border: none; cursor: pointer;\n color: inherit; text-align: left; font: inherit;\n}\n.ah-bot-row:hover { background: var(--dsw-alias-fill-hover, var(--dsw-alias-bg-inset)); }\n.ah-bot-row.ah-selected { background: var(--dsw-alias-fill-selected, var(--dsw-alias-bg-inset)); }\n.ah-bot-row-main { flex: 1; min-width: 0; display: grid; gap: 2px; }\n.ah-bot-row-name { font-weight: 500; display: flex; align-items: center; gap: 6px; }\n.ah-bot-row-role { font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-bot-row-activity {\n font-size: 12px; color: var(--dsw-alias-text-secondary);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.ah-botlist-footer { margin-top: auto; padding: 10px 12px; border-top: 1px solid var(--dsw-alias-separator); }\n.ah-inbox-entry { width: 100%; padding: 8px; }\n\n/* \u5934\u50CF\u4E0E\u672A\u8BFB */\n.ah-avatar {\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 999px; flex-shrink: 0;\n font-size: 13px; font-weight: 500;\n}\n.ah-avatar-team { background: var(--dsw-alias-accent-muted); color: var(--dsw-alias-text-primary); }\n.ah-unread {\n background: var(--dsw-alias-danger); color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary));\n border-radius: 999px; font-size: 11px; padding: 1px 6px; flex-shrink: 0;\n}\n\n/* \u6807\u7B7E */\n.ah-tag { font-size: 11px; border-radius: var(--dsw-radius-sm, 4px); padding: 1px 6px; }\n.ah-tag-wait { background: var(--dsw-alias-warning-muted, var(--dsw-alias-bg-inset)); color: var(--dsw-alias-warning, var(--dsw-alias-text-primary)); }\n\n/* ---------- \u4E2D\u680F\u7EBF\u7A0B ---------- */\n.ah-thread-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 20px; border-bottom: 1px solid var(--dsw-alias-separator); }\n.ah-thread-header h2 { margin: 0; font-size: 15px; font-weight: 500; }\n.ah-thread-messages { flex: 1; overflow-y: auto; padding: 16px 20px; display: flex; flex-direction: column; gap: 12px; }\n.ah-msg { display: flex; gap: 8px; align-items: flex-start; }\n.ah-msg-mine { flex-direction: row-reverse; }\n.ah-msg-body { max-width: min(70%, 620px); display: grid; gap: 3px; }\n.ah-msg-mine .ah-msg-body { justify-items: end; }\n.ah-msg-meta { font-size: 11px; color: var(--dsw-alias-text-secondary); }\n.ah-msg-bubble {\n padding: 8px 12px; border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-inset); word-break: break-word;\n}\n.ah-msg-mine .ah-msg-bubble { background: var(--dsw-alias-accent-muted); }\n.ah-msg-center { justify-content: center; text-align: center; }\n.ah-msg-bubble-center { font-size: 12px; color: var(--dsw-alias-text-secondary); background: transparent; padding: 2px 8px; }\n.ah-msg-center .ah-msg-time { font-size: 11px; color: var(--dsw-alias-text-secondary); }\n\n/* \u8F93\u5165\u533A */\n.ah-thread-input { display: flex; gap: 8px; padding: 12px 20px; border-top: 1px solid var(--dsw-alias-separator); }\n.ah-thread-input input {\n flex: 1; padding: 8px 12px;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator-strong, var(--dsw-alias-separator));\n background: var(--dsw-alias-bg-inset); color: inherit;\n}\n/* \u53D1\u9001\u5931\u8D25\u63D0\u793A\uFF08\u4EA4\u4E92\u5BA1\u67E5\u7B2C 4 \u6761\uFF09\uFF1A\u8D34\u5728\u8F93\u5165\u6846\u4E0B\u65B9\uFF0C\u4E00\u53E5\u8BDD\u539F\u56E0 + \u91CD\u8BD5\uFF0C\u4E0D\u6253\u65AD\u8F93\u5165\u3002 */\n.ah-thread-senderror {\n display: flex; align-items: center; gap: 8px;\n padding: 6px 20px 10px; font-size: 12px;\n color: var(--dsw-alias-text-danger, var(--dsw-alias-text-secondary));\n}\n\n/* ---------- \u5361\u7247 ---------- */\n.ah-card {\n margin-top: 6px; padding: 10px 12px;\n border: 1px solid var(--dsw-alias-separator-strong, var(--dsw-alias-separator));\n border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base));\n display: grid; gap: 6px; text-align: left;\n}\n.ah-card-done { opacity: 0.7; }\n.ah-card-head { display: flex; align-items: center; gap: 8px; }\n.ah-card-type { font-size: 11px; color: var(--dsw-alias-text-secondary); border: 1px solid var(--dsw-alias-separator); border-radius: var(--dsw-radius-sm, 4px); padding: 0 6px; }\n.ah-card-title { font-weight: 500; }\n.ah-card-body { margin: 0; font-size: 13px; }\n.ah-card-expire { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-card-answer { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-card-error { margin: 4px 0 0; font-size: 12px; color: var(--dsw-alias-danger, var(--dsw-alias-text-secondary)); }\n.ah-card-options { display: flex; flex-wrap: wrap; gap: 8px; }\n\n/* ---------- \u6536\u4EF6\u7BB1 ---------- */\n.ah-inbox { flex: 1; overflow-y: auto; padding: 16px 20px; display: grid; gap: 12px; align-content: start; }\n.ah-inbox-row {\n padding: 12px 14px; border: 1px solid var(--dsw-alias-separator);\n border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base));\n display: grid; gap: 8px;\n}\n.ah-inbox-row header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n.ah-inbox-row p { margin: 0; font-size: 13px; }\n.ah-inbox-time { font-size: 12px; color: var(--dsw-alias-text-secondary); margin-left: auto; }\n.ah-inbox-p0 { border-color: var(--dsw-alias-danger); }\n.ah-inbox-p1 { border-color: var(--dsw-alias-warning, var(--dsw-alias-separator-strong, var(--dsw-alias-separator))); }\n\n/* ---------- \u53F3\u680F ---------- */\n.ah-right-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--dsw-alias-separator); }\n.ah-right-head h2 { margin: 0; font-size: 15px; font-weight: 500; display: flex; align-items: center; gap: 8px; }\n.ah-right-section { padding: 10px 0; border-bottom: 1px solid var(--dsw-alias-separator); }\n.ah-right-section h3 { margin: 0 0 6px; font-size: 12px; color: var(--dsw-alias-text-secondary); font-weight: 500; }\n.ah-right-section p, .ah-right-section li { margin: 0; font-size: 13px; }\n.ah-right-section ul { margin: 0; padding-left: 18px; display: grid; gap: 6px; }\n.ah-right-kv { color: var(--dsw-alias-text-secondary); }\n.ah-right-actions { display: flex; gap: 8px; flex-wrap: wrap; padding: 12px 16px; border-top: 1px solid var(--dsw-alias-separator); align-items: center; }\n.ah-right-notice { width: 100%; margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-appraisals { list-style: none; padding: 0; display: grid; gap: 5px; font-size: 12px; }\n.ah-appraisal-decision { color: var(--dsw-alias-text-secondary); margin-right: 4px; }\n.ah-appraisal-woke { color: var(--dsw-alias-success, var(--dsw-alias-text-primary)); }\n\n/* ---------- \u901A\u7528\u4EF6 ---------- */\n.ah-btn {\n padding: 5px 12px; border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator-strong, var(--dsw-alias-separator));\n background: var(--dsw-alias-bg-inset); color: inherit;\n cursor: pointer; font: inherit; font-size: 13px;\n}\n.ah-btn:hover { background: var(--dsw-alias-fill-hover, var(--dsw-alias-bg-inset)); }\n.ah-btn:disabled { opacity: 0.5; cursor: default; }\n.ah-btn-primary { border-color: var(--dsw-alias-accent); color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary)); background: var(--dsw-alias-accent-muted); }\n.ah-btn-ghost { border-color: transparent; background: transparent; }\n\n.ah-dot { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; }\n.ah-dot-circle { width: 8px; height: 8px; border-radius: 999px; display: inline-block; }\n.ah-dot-ok .ah-dot-circle { background: var(--dsw-alias-success, var(--dsw-alias-accent)); }\n.ah-dot-warn .ah-dot-circle { background: var(--dsw-alias-warning, var(--dsw-alias-accent)); }\n.ah-dot-busy .ah-dot-circle { background: var(--dsw-alias-accent); }\n.ah-dot-idle .ah-dot-circle { background: var(--dsw-alias-text-secondary); }\n.ah-dot-danger .ah-dot-circle { background: var(--dsw-alias-danger); }\n\n.ah-skeleton { display: grid; gap: 8px; padding: 16px; }\n.ah-skeleton-row { height: 14px; border-radius: var(--dsw-radius-sm, 4px); background: var(--dsw-alias-bg-inset); }\n.ah-retry { display: flex; align-items: center; gap: 10px; padding: 16px 20px; color: var(--dsw-alias-text-secondary); }\n.ah-empty { padding: 16px 20px; font-size: 13px; color: var(--dsw-alias-text-secondary); }\n\n/* \u5360\u4F4D\u5BF9\u8BDD\u6846 */\n.ah-dialog-mask { position: fixed; inset: 0; background: var(--dsw-alias-scrim, transparent); display: flex; align-items: center; justify-content: center; z-index: 100; }\n.ah-dialog {\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base));\n border-radius: var(--dsw-radius-lg, 10px); box-shadow: var(--dsw-shadow-lv3);\n padding: 20px 24px; display: grid; gap: 12px; justify-items: center; text-align: center;\n}\n\n/* ================= D03\uFF1A\u6C14\u6CE1\u3001\u7EBF\u7A0B\u5934\u90E8\u3001\u5411\u5BFC\u3001\u6536\u4EF6\u7BB1\u3001\u6D3B\u52A8\u6D41\u3001\u8BBE\u7F6E\u3001\u63D0\u793A ================= */\n\n/* \u7EBF\u7A0B\u5934\u90E8\uFF1A\u540D\u5B57/\u89D2\u8272 + \u72B6\u6001\u53E5 + \u4E0B\u6B21\u81EA\u67E5 + \u53F3\u4FA7\u64CD\u4F5C */\n.ah-thread-header { flex-wrap: wrap; gap: 6px 12px; }\n.ah-thread-header-main { display: grid; gap: 2px; min-width: 0; }\n.ah-thread-header-main h2 { display: flex; align-items: center; gap: 8px; }\n.ah-thread-status { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-thread-next { margin: 0; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n.ah-thread-header-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n\n/* \u6C14\u6CE1\uFF1A\u4E3B\u4EBA\u53F3\u4E3B\u8272\u6DE1\u5E95\u3001\u673A\u5668\u4EBA\u5DE6\u4E2D\u6027\u5E95\uFF1B\u6C47\u62A5\u5DE6\u4FA7\u7EC6\u8272\u6761 */\n.ah-msg-bubble {\n border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-inset);\n padding: 8px 12px;\n border-top-left-radius: var(--dsw-radius-sm, 4px);\n}\n.ah-msg-mine .ah-msg-bubble {\n background: var(--dsw-alias-accent-muted);\n border-top-left-radius: var(--dsw-radius-lg, 10px);\n border-top-right-radius: var(--dsw-radius-sm, 4px);\n}\n.ah-msg-report .ah-msg-bubble { border-left: 3px solid var(--dsw-alias-accent); }\n\n/* \u6309\u94AE\uFF1A\u60AC\u505C\u4E0E\u6309\u4E0B\u53CD\u9988 */\n.ah-btn { transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease; }\n.ah-btn:hover:not(:disabled) { filter: brightness(1.05); background: var(--dsw-alias-fill-hover, var(--dsw-alias-bg-inset)); }\n.ah-btn:active:not(:disabled) { transform: translateY(1px); }\n/* \u952E\u76D8\u7126\u70B9\u73AF\uFF08\u89C6\u89C9\u4FEE\u590D\uFF1A\u539F\u6765\u6CA1\u6709\u7126\u70B9\u6837\u5F0F\uFF0CTab \u5BFC\u822A\u5B8C\u5168\u770B\u4E0D\u51FA\u7126\u70B9\u5728\u54EA\uFF09 */\n.ah-btn:focus-visible, .ah-bot-row:focus-visible, .ah-inbox-entry:focus-visible,\n.ah-thread-input input:focus-visible, .ah-card-freetext input:focus-visible, .ah-card-note:focus-visible {\n outline: 2px solid var(--dsw-alias-accent);\n outline-offset: 1px;\n}\n.ah-btn-primary {\n border-color: var(--dsw-alias-accent);\n background: var(--dsw-alias-accent);\n color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary));\n}\n.ah-btn-primary:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-accent) 88%, var(--dsw-alias-label-primary));\n border-color: color-mix(in srgb, var(--dsw-alias-accent) 88%, var(--dsw-alias-label-primary));\n}\n.ah-btn-danger { border-color: var(--dsw-alias-danger); color: var(--dsw-alias-danger); }\n.ah-btn-icon { display: inline-flex; align-items: center; gap: 4px; }\n\n/* \u5361\u7247\u91CC\u7684\u81EA\u7531\u6587\u672C\u4E0E\u9644\u8A00 */\n.ah-card-freetext { display: flex; gap: 8px; }\n.ah-card-freetext input, .ah-card-note {\n flex: 1; padding: 6px 10px; font: inherit; color: inherit;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator);\n background: var(--dsw-alias-bg-inset);\n}\n.ah-card-note { width: 100%; }\n\n/* \u6536\u4EF6\u7BB1\u5206\u7EC4 */\n.ah-inbox-group { display: grid; gap: 8px; }\n.ah-inbox-group h3 { margin: 8px 0 0; font-size: 12px; color: var(--dsw-alias-text-secondary); font-weight: 500; }\n.ah-inbox-bot { font-weight: 500; }\n.ah-inbox-row { border: 1px solid var(--dsw-alias-separator); border-radius: var(--dsw-radius-lg, 10px); padding: 10px 12px; display: grid; gap: 8px; background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base)); }\n.ah-inbox-row header { display: flex; align-items: center; gap: 8px; }\n.ah-inbox-row .ah-card { border: none; margin: 0; padding: 0; background: transparent; }\n.ah-inbox-p0 .ah-inbox-row { border-color: var(--dsw-alias-danger); }\n\n/* \u6D3B\u52A8\u6D41 */\n.ah-activity { flex: 1; overflow-y: auto; padding: 16px 20px; display: grid; gap: 12px; align-content: start; }\n.ah-activity-filters { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; font-size: 13px; }\n.ah-activity-filters label { display: flex; align-items: center; gap: 6px; color: var(--dsw-alias-text-secondary); }\n.ah-activity-filters select {\n font: inherit; color: inherit; padding: 4px 8px;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator);\n background: var(--dsw-alias-bg-inset);\n}\n.ah-activity-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }\n.ah-activity-row { display: flex; align-items: flex-start; gap: 8px; font-size: 13px; }\n.ah-activity-time { color: var(--dsw-alias-text-secondary); font-size: 12px; flex-shrink: 0; min-width: 72px; }\n.ah-activity-text { flex: 1; min-width: 0; }\n.ah-activity-kind {\n font-size: 11px; color: var(--dsw-alias-text-secondary);\n border: 1px solid var(--dsw-alias-separator);\n border-radius: var(--dsw-radius-sm, 4px);\n padding: 0 6px; margin-right: 6px;\n}\n.ah-activity-alert .ah-activity-kind { color: var(--dsw-alias-danger); border-color: var(--dsw-alias-danger); }\n.ah-activity-approval .ah-activity-kind { color: var(--dsw-alias-warning, var(--dsw-alias-text-secondary)); }\n.ah-activity-more { display: flex; justify-content: center; }\n\n/* \u8BBE\u7F6E\u9875 */\n.ah-settings { flex: 1; overflow-y: auto; padding: 16px 20px; display: grid; gap: 10px; align-content: start; }\n.ah-settings h3 { margin: 8px 0 0; font-size: 13px; font-weight: 500; color: var(--dsw-alias-text-secondary); }\n.ah-settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; }\n.ah-settings-grid label, .ah-wizard-field label, .ah-edit-box label, .ah-wizard-grid label {\n display: grid; gap: 4px; font-size: 13px;\n}\n.ah-settings-grid input, .ah-settings-grid select,\n.ah-wizard-field input, .ah-wizard-field textarea, .ah-wizard-field select,\n.ah-edit-box input, .ah-edit-box textarea, .ah-edit-add input, .ah-edit-list input {\n font: inherit; color: inherit; padding: 6px 10px;\n border-radius: var(--dsw-radius-md, 8px);\n border: 1px solid var(--dsw-alias-separator);\n background: var(--dsw-alias-bg-inset);\n}\n.ah-settings-grid input:disabled { opacity: 0.7; }\n.ah-settings-error { margin: 0; font-size: 13px; color: var(--dsw-alias-danger); }\n.ah-settings-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.ah-settings-health { margin: 0; padding-left: 18px; display: grid; gap: 4px; font-size: 13px; }\n\n/* \u5411\u5BFC */\n.ah-dialog-wide { max-width: min(680px, calc(100vw - 48px)); text-align: left; justify-items: stretch; }\n.ah-wizard-head { display: flex; align-items: center; justify-content: space-between; }\n.ah-wizard-head h3 { margin: 0; }\n.ah-wizard-steps { list-style: none; display: flex; gap: 6px; margin: 0; padding: 0; flex-wrap: wrap; font-size: 12px; }\n.ah-wizard-steps li { color: var(--dsw-alias-text-secondary); padding: 2px 8px; border-radius: var(--dsw-radius-sm, 4px); border: 1px solid var(--dsw-alias-separator); }\n.ah-wizard-steps .ah-wizard-step-current { color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary)); background: var(--dsw-alias-accent); border-color: var(--dsw-alias-accent); }\n.ah-wizard-steps .ah-wizard-step-done { color: var(--dsw-alias-accent); border-color: var(--dsw-alias-accent); }\n.ah-wizard-body { display: grid; gap: 12px; }\n.ah-wizard-roles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }\n.ah-wizard-role {\n display: grid; gap: 4px; padding: 10px; text-align: left; font: inherit; color: inherit; cursor: pointer;\n border: 1px solid var(--dsw-alias-separator); border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-inset); font-size: 13px;\n}\n.ah-wizard-role span { color: var(--dsw-alias-text-secondary); }\n.ah-wizard-role-picked { border-color: var(--dsw-alias-accent); background: var(--dsw-alias-accent-muted); }\n.ah-wizard-role-disabled { opacity: 0.5; cursor: not-allowed; }\n.ah-wizard-colors { display: flex; gap: 8px; flex-wrap: wrap; }\n.ah-wizard-colors button { width: 26px; height: 26px; border-radius: 999px; border: 2px solid transparent; cursor: pointer; }\n.ah-wizard-colors .ah-wizard-color-picked { border-color: var(--dsw-alias-text-primary); }\n.ah-wizard-examples { display: grid; gap: 6px; }\n.ah-wizard-examples .ah-btn { text-align: left; font-size: 12px; }\n.ah-wizard-optional { font-size: 13px; display: grid; gap: 8px; }\n.ah-wizard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }\n.ah-wizard-confirm .ah-wizard-restatement { margin: 0; font-size: 14px; }\n.ah-wizard-label { font-size: 13px; color: var(--dsw-alias-text-secondary); }\n\n/* \u6559\u5B66\u5F39\u7A97 */\n.ah-teach-search, .ah-teach-note { font: inherit; color: inherit; padding: 6px 10px; border-radius: var(--dsw-radius-md, 8px); border: 1px solid var(--dsw-alias-separator); background: var(--dsw-alias-bg-inset); }\n.ah-teach-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; max-height: 240px; overflow-y: auto; }\n.ah-teach-list label { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--dsw-radius-md, 8px); font-size: 13px; cursor: pointer; }\n.ah-teach-picked { background: var(--dsw-alias-accent-muted); }\n.ah-teach-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ah-dialog-actions { display: flex; gap: 8px; justify-content: flex-end; }\n\n/* \u53F3\u680F\u7F16\u8F91 */\n.ah-edit-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; font-size: 13px; }\n.ah-edit-list li { display: grid; gap: 4px; }\n.ah-edit-ops { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }\n.ah-edit-ops input { font-size: 12px; padding: 3px 8px; max-width: 200px; }\n.ah-edit-add { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }\n.ah-edit-box { display: grid; gap: 8px; font-size: 13px; }\n.ah-routine-steps { margin: 4px 0; padding-left: 18px; display: grid; gap: 2px; font-size: 12px; color: var(--dsw-alias-text-secondary); }\n\n/* \u5E94\u7528\u5185\u63D0\u793A\uFF08\u5B98\u65B9 toast \u6837\u5F0F\uFF0Cverified-T5 Q4-2\uFF1B\u53EA\u7528\u4E3B\u9898\u53D8\u91CF\uFF09 */\n.ah-toast-layer {\n position: fixed; top: 40px; left: 50%; transform: translateX(-50%);\n z-index: 1100; pointer-events: none;\n display: grid; gap: 8px; justify-items: center;\n max-width: min(640px, calc(100vw - 48px));\n}\n.ah-toast {\n padding: 12px 16px; border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-toast-bg, var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-base)));\n color: var(--dsw-alias-toast-label, var(--dsw-alias-text-primary));\n font-size: 14px; line-height: 22px;\n box-shadow: var(--dsw-shadow-lv3);\n pointer-events: auto;\n animation: ah-toast-in 0.16s ease;\n}\n.ah-toast-btn { all: unset; cursor: pointer; }\n.ah-toast-btn:hover { text-decoration: underline; }\n@keyframes ah-toast-in { from { opacity: 0; } to { opacity: 1; } }\n\n/* \u9875\u9762\u5E95\u90E8\u7684\u884C\u5185\u901A\u77E5\uFF08\u8BD5\u8DD1\u63D0\u793A\u7B49\uFF09\u2014\u2014D04 3c-3 \u8D77\u6539\u7528\u7EBF\u7A0B\u9876\u90E8\u63D0\u793A\u6761\uFF0C\u6B64\u6837\u5F0F\u4FDD\u7559\u517C\u5BB9 */\n.ah-toast-inline {\n position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);\n display: flex; gap: 10px; align-items: center;\n padding: 10px 16px; border-radius: var(--dsw-radius-lg, 10px);\n background: var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-inset));\n box-shadow: var(--dsw-shadow-lv2); font-size: 13px; z-index: 40;\n}\n\n/* ================= D04\uFF1A\u5411\u5BFC\u9009\u4E2D\u6001\u3001\u4E09\u9009\u4E00\u7248\u5F0F\u3001\u7EBF\u7A0B\u9876\u90E8\u63D0\u793A\u6761\u3001\u6C14\u6CE1\u53EF\u8FA8\u6027 ================= */\n\n/* \u5411\u5BFC\u7B2C 1 \u6B65\uFF1A\u89D2\u8272\u5361\u9009\u4E2D = \u4E3B\u8272\u63CF\u8FB9 + \u6D45\u5E95 + \u53F3\u4E0A\u89D2\u300C\u5DF2\u9009\u300D\u89D2\u6807\uFF08D04 3c-1\uFF09 */\n.ah-wizard-role { position: relative; }\n.ah-wizard-role-picked {\n border-color: var(--dsw-alias-accent);\n background: var(--dsw-alias-accent-muted);\n box-shadow: inset 0 0 0 1px var(--dsw-alias-accent);\n}\n.ah-wizard-role-check {\n position: absolute; top: 6px; right: 6px;\n font-size: 11px; padding: 1px 6px; border-radius: var(--dsw-radius-sm, 4px);\n color: var(--dsw-alias-text-on-accent, var(--dsw-alias-text-primary));\n background: var(--dsw-alias-accent);\n}\n/* \u8272\u5757\u9009\u4E2D\u63CF\u8FB9\u7528\u4E3B\u8272\uFF08\u539F text-primary \u5728\u6697\u8272\u4E0B\u4E0D\u663E\u773C\uFF09 */\n.ah-wizard-colors .ah-wizard-color-picked { border-color: var(--dsw-alias-accent); }\n\n/* \u5411\u5BFC\u7B2C 4 \u6B65\uFF1A\u4E09\u4E2A\u9009\u62E9\u6846\u540C\u9AD8\u540C\u5BBD\u4E00\u884C\u5BF9\u9F50\uFF1B\u8BF4\u660E\u653E\u4E0B\u65B9\u3001\u6B21\u8981\u8272\u3001\u6B63\u5E38\u5B57\u53F7\uFF08D04 3c-2\uFF09 */\n.ah-wizard-trio {\n display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px;\n}\n.ah-wizard-trio label { display: grid; gap: 4px; font-size: 13px; }\n.ah-wizard-trio select { width: 100%; height: 34px; }\n.ah-wizard-note { margin: 0; font-size: 13px; color: var(--dsw-alias-text-secondary); }\n\n/* \u7EBF\u7A0B\u9876\u90E8\u63D0\u793A\u6761\uFF08\u8BD5\u8DD1\u63D0\u793A\u7B49\uFF09\uFF1A\u5934\u90E8\u4E0B\u65B9\u3001\u4E0D\u906E\u6321\u8F93\u5165\u6846\uFF08D04 3c-3\uFF09 */\n.ah-thread-banner {\n display: flex; gap: 10px; align-items: center; justify-content: space-between;\n margin: 8px 20px 0; padding: 8px 12px;\n border: 1px solid var(--dsw-alias-separator);\n border-radius: var(--dsw-radius-md, 8px);\n background: var(--dsw-alias-accent-muted);\n font-size: 13px;\n}\n\n/* \u6C14\u6CE1\u53EF\u8FA8\u6027\uFF08D04 3c-4\uFF09\uFF1A\u4E3B\u4EBA\u4E3B\u8272\u6D45\u5E95 + \u4E3B\u8272\u63CF\u8FB9\uFF1B\u673A\u5668\u4EBA\u4E2D\u6027\u5E95 + \u5206\u9694\u7EBF\u63CF\u8FB9 */\n.ah-msg-bubble { border: 1px solid var(--dsw-alias-separator); }\n.ah-msg-mine .ah-msg-bubble {\n background: var(--dsw-alias-accent-muted);\n border: 1px solid var(--dsw-alias-accent);\n}\n\n/* \u53F3\u680F\u5FD7\u5411\u884C\uFF1A\u6587\u5B57\u4E0E\u64CD\u4F5C\u540C\u4E00\u884C\uFF0C\u53EA\u5728\u7F16\u8F91\u65F6\u51FA\u73B0\u8F93\u5165\u6846\uFF08D04 3c-5\uFF09 */\n.ah-edit-row { display: flex; gap: 8px; align-items: center; justify-content: space-between; min-width: 0; }\n";
34
+
35
+ // src/client/api.ts
36
+ var BASE = "api/agent-hub";
37
+ async function getJson(path) {
38
+ let response;
39
+ try {
40
+ response = await fetch(`${BASE}/${path}`, { credentials: "same-origin" });
41
+ } catch {
42
+ throw new Error("\u8FDE\u4E0D\u4E0A Agent \u4E2D\u5FC3\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09");
43
+ }
44
+ if (!response.ok) {
45
+ let message = `\u8BF7\u6C42\u5931\u8D25\uFF08${response.status}\uFF09`;
46
+ try {
47
+ const body = await response.json();
48
+ if (body?.error) message = body.error;
49
+ } catch {
50
+ }
51
+ throw new Error(message);
52
+ }
53
+ return await response.json();
54
+ }
55
+ async function postCommand(name, input) {
56
+ let response;
57
+ try {
58
+ response = await fetch(`${BASE}/command`, {
59
+ method: "POST",
60
+ credentials: "same-origin",
61
+ headers: { "Content-Type": "application/json; charset=utf-8" },
62
+ body: JSON.stringify({ name, input })
63
+ });
64
+ } catch {
65
+ return { ok: false, error: "\u8FDE\u4E0D\u4E0A Agent \u4E2D\u5FC3\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09" };
66
+ }
67
+ if (!response.ok) {
68
+ let message = `\u8BF7\u6C42\u5931\u8D25\uFF08${response.status}\uFF09`;
69
+ try {
70
+ const body = await response.json();
71
+ if (body?.error) message = body.error;
72
+ } catch {
73
+ }
74
+ return { ok: false, error: message };
75
+ }
76
+ return await response.json();
77
+ }
78
+ var api = {
79
+ overview: () => getJson("overview"),
80
+ bot: (id) => getJson(`bot?id=${encodeURIComponent(id)}`),
81
+ thread: (id, opts) => {
82
+ const params = new URLSearchParams({ id });
83
+ if (opts?.before) params.set("before", opts.before);
84
+ if (opts?.limit) params.set("limit", String(opts.limit));
85
+ return getJson(`thread?${params.toString()}`);
86
+ },
87
+ inbox: () => getJson("inbox"),
88
+ activity: (opts) => {
89
+ const params = new URLSearchParams();
90
+ if (opts?.before) params.set("before", opts.before);
91
+ if (opts?.limit) params.set("limit", String(opts.limit));
92
+ const qs = params.toString();
93
+ return getJson(qs ? `activity?${qs}` : "activity");
94
+ },
95
+ settings: () => getJson("settings"),
96
+ recentSessions: () => getJson("recent-sessions"),
97
+ command: postCommand
98
+ };
99
+
100
+ // src/client/store.ts
101
+ var import_react = require("react");
102
+ function createStore(initial) {
103
+ let snapshot = initial;
104
+ const listeners = /* @__PURE__ */ new Set();
105
+ return {
106
+ getSnapshot: () => snapshot,
107
+ subscribe(fn) {
108
+ listeners.add(fn);
109
+ return () => {
110
+ listeners.delete(fn);
111
+ };
112
+ },
113
+ set(next) {
114
+ if (next === snapshot) return;
115
+ snapshot = next;
116
+ for (const fn of [...listeners]) {
117
+ try {
118
+ fn();
119
+ } catch (error) {
120
+ console.error("[agent-hub] store \u901A\u77E5\u51FA\u9519:", error);
121
+ }
122
+ }
123
+ },
124
+ update(fn) {
125
+ this.set(fn(snapshot));
126
+ }
127
+ };
128
+ }
129
+ function useStore(store) {
130
+ return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
131
+ }
132
+
133
+ // src/client/logic/feeds.ts
134
+ var PRIORITY_ORDER = ["P0", "P1", "P2"];
135
+ var PRIORITY_LABEL = {
136
+ P0: "P0 \u9A6C\u4E0A\u8981\u4F60\u5904\u7406",
137
+ P1: "P1 \u7B49\u4F60\u62CD\u677F",
138
+ P2: "P2 \u6709\u7A7A\u518D\u770B"
139
+ };
140
+ function groupInbox(items) {
141
+ const groups = [];
142
+ for (const priority of PRIORITY_ORDER) {
143
+ const list = items.filter((item) => item.priority === priority).sort((a, b) => b.createdAt - a.createdAt || a.id.localeCompare(b.id));
144
+ if (list.length > 0) groups.push({ priority, label: PRIORITY_LABEL[priority], items: list });
145
+ }
146
+ return groups;
147
+ }
148
+ var ACTIVITY_KIND_LABELS = {
149
+ wake: "\u5524\u9192",
150
+ commit: "\u4EA4\u8D26",
151
+ approval: "\u5BA1\u6279",
152
+ alert: "\u544A\u8B66",
153
+ handoff: "\u4EA4\u63A5",
154
+ peer: "\u540C\u4F34\u6D88\u606F",
155
+ lifecycle: "\u751F\u547D\u5468\u671F",
156
+ system: "\u4E2D\u5FC3"
157
+ };
158
+ function filterActivity(rows, filter) {
159
+ return rows.filter(
160
+ (row) => (filter.botId == null || filter.botId === "" || row.botId === filter.botId) && (filter.kind == null || row.kind === filter.kind)
161
+ );
162
+ }
163
+ function entryLabel(inboxTotal) {
164
+ return inboxTotal > 0 ? `\u961F\u53CB \xB7 ${inboxTotal > 99 ? "99+" : inboxTotal}` : "\u961F\u53CB";
165
+ }
166
+ function newUrgentInbox(prev, next) {
167
+ const seen = new Set(prev.map((item) => item.id));
168
+ return next.filter((item) => !seen.has(item.id) && (item.priority === "P0" || item.priority === "P1"));
169
+ }
170
+
171
+ // src/client/logic/scopes.ts
172
+ var HUB_SCOPES = {
173
+ overview: "overview",
174
+ bot: "bot",
175
+ thread: "thread",
176
+ inbox: "inbox",
177
+ activity: "activity",
178
+ settings: "settings"
179
+ };
180
+ var emptyPlan = () => ({ overview: false, bots: [], threads: [], inbox: false, activity: false, settings: false });
181
+ function refreshPlan(scope, botId) {
182
+ const plan = emptyPlan();
183
+ const id = typeof botId === "string" && botId !== "" ? botId : null;
184
+ switch (scope) {
185
+ case HUB_SCOPES.overview:
186
+ plan.overview = true;
187
+ if (id !== null) plan.bots.push(id);
188
+ return plan;
189
+ case HUB_SCOPES.bot:
190
+ plan.overview = true;
191
+ if (id !== null) plan.bots.push(id);
192
+ return plan;
193
+ case HUB_SCOPES.thread:
194
+ plan.overview = true;
195
+ plan.threads.push(id ?? "team");
196
+ return plan;
197
+ case HUB_SCOPES.inbox:
198
+ plan.inbox = true;
199
+ plan.overview = true;
200
+ return plan;
201
+ case HUB_SCOPES.activity:
202
+ plan.activity = true;
203
+ return plan;
204
+ case HUB_SCOPES.settings:
205
+ plan.settings = true;
206
+ return plan;
207
+ default:
208
+ plan.overview = true;
209
+ plan.activity = true;
210
+ return plan;
211
+ }
212
+ }
213
+
214
+ // src/client/hub-state.ts
215
+ var TOAST_MS = 4e3;
216
+ function mergeThreadMessages(base, fresh) {
217
+ const index = new Map(base.map((m, i) => [m.id, i]));
218
+ const out = [...base];
219
+ for (const message of fresh) {
220
+ const at = index.get(message.id);
221
+ if (at === void 0) {
222
+ index.set(message.id, out.length);
223
+ out.push(message);
224
+ } else out[at] = message;
225
+ }
226
+ return out;
227
+ }
228
+ function emptyState() {
229
+ return {
230
+ overview: null,
231
+ inbox: null,
232
+ activity: null,
233
+ settings: null,
234
+ recentSessions: [],
235
+ bots: {},
236
+ threads: {},
237
+ connectionDown: false,
238
+ downReason: null,
239
+ downMessage: null,
240
+ settingsError: null,
241
+ view: "main",
242
+ selectedThread: "team",
243
+ toasts: []
244
+ };
245
+ }
246
+ function reasonOfError(message) {
247
+ if (message.includes("\u5DF2\u505C\u7528")) return "disabled";
248
+ if (message.includes("\u5C1A\u672A\u5C31\u7EEA")) return "starting";
249
+ if (message.includes("\u542F\u52A8\u5931\u8D25")) return "failed";
250
+ return null;
251
+ }
252
+ function isNetworkError(message) {
253
+ return message.includes("\u7F51\u7EDC\u9519\u8BEF");
254
+ }
255
+ function createHubStore() {
256
+ const store = createStore(emptyState());
257
+ const patch = (partial) => store.update((prev) => ({ ...prev, ...partial }));
258
+ const guard = async (fn) => {
259
+ try {
260
+ patch(await fn());
261
+ if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
262
+ } catch (error) {
263
+ const message = error instanceof Error ? error.message : String(error);
264
+ console.error("[agent-hub] \u62C9\u53D6\u6570\u636E\u5931\u8D25:", error);
265
+ patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
266
+ }
267
+ };
268
+ const applyInbox = (next, prevItems) => {
269
+ const fresh = prevItems === null ? [] : newUrgentInbox(prevItems, next.items);
270
+ for (const item of fresh) {
271
+ const toast = { id: `toast-${item.id}`, text: `${item.botName ?? "\u4E2D\u5FC3"}\uFF1A${item.title}`, inboxItemId: item.id };
272
+ store.update((prev) => ({ ...prev, toasts: [...prev.toasts.filter((t) => t.id !== toast.id), toast] }));
273
+ try {
274
+ setTimeout(() => {
275
+ try {
276
+ store.update((prev) => ({ ...prev, toasts: prev.toasts.filter((t) => t.id !== toast.id) }));
277
+ } catch {
278
+ }
279
+ }, TOAST_MS);
280
+ } catch {
281
+ }
282
+ }
283
+ return { inbox: next };
284
+ };
285
+ const hub = {
286
+ ...store,
287
+ async refreshOverview() {
288
+ await guard(async () => ({ overview: await api.overview() }));
289
+ },
290
+ async refreshInbox() {
291
+ const prevItems = store.getSnapshot().inbox?.items ?? null;
292
+ await guard(async () => applyInbox(await api.inbox(), prevItems));
293
+ },
294
+ async refreshActivity(before) {
295
+ try {
296
+ const next = await api.activity(before ? { before } : void 0);
297
+ if (!before) patch({ activity: next });
298
+ else store.update((prev) => ({
299
+ ...prev,
300
+ activity: prev.activity === null ? next : { rows: [...prev.activity.rows, ...next.rows], hasMore: next.hasMore }
301
+ }));
302
+ if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
303
+ } catch (error) {
304
+ const message = error instanceof Error ? error.message : String(error);
305
+ console.error("[agent-hub] \u62C9\u53D6\u6D3B\u52A8\u6D41\u5931\u8D25:", error);
306
+ patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
307
+ }
308
+ },
309
+ /**
310
+ * 设置拉取(D07 第 2 条):不走会标 connectionDown 的通用 guard——设置读不到
311
+ * 不等于连不上。只有网络错误、「尚未就绪」「已停用」「启动失败」才整页提示;
312
+ * 其余(例如宿主模型目录坏了)把中文原因存进 settingsError,设置页与各模型
313
+ * 下拉处显示原因和「重试」;旧的 settings 保持原值。
314
+ */
315
+ async refreshSettings() {
316
+ try {
317
+ const settings = await api.settings();
318
+ patch({ settings, settingsError: null });
319
+ if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
320
+ } catch (error) {
321
+ const message = error instanceof Error ? error.message : String(error);
322
+ console.error("[agent-hub] \u62C9\u53D6\u8BBE\u7F6E\u5931\u8D25:", error);
323
+ const reason = reasonOfError(message);
324
+ if (reason !== null) patch({ connectionDown: true, downReason: reason, downMessage: reason === "failed" ? message : null });
325
+ else if (isNetworkError(message)) patch({ connectionDown: true, downReason: null, downMessage: null });
326
+ else patch({ settingsError: message });
327
+ }
328
+ },
329
+ async refreshRecentSessions() {
330
+ await guard(async () => ({ recentSessions: await api.recentSessions() }));
331
+ },
332
+ /**
333
+ * 机器人详情(D07 第 9 条):先拿到详情,再在 updater 里合并进**最新**状态——
334
+ * 两条 bot 推送挨着到时,后返回的请求不能把另一台刚拉到的新详情改回旧值。
335
+ */
336
+ async refreshBot(botId) {
337
+ try {
338
+ const view = await api.bot(botId);
339
+ store.update((prev) => ({ ...prev, bots: { ...prev.bots, [botId]: view } }));
340
+ if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
341
+ } catch (error) {
342
+ const message = error instanceof Error ? error.message : String(error);
343
+ console.error("[agent-hub] \u62C9\u53D6\u673A\u5668\u4EBA\u8BE6\u60C5\u5931\u8D25:", error);
344
+ patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
345
+ }
346
+ },
347
+ /**
348
+ * 线程(D07 第 14/9 条):带 before 是「加载更早」,把旧一页拼在已有列表前面;
349
+ * 不带 before 是最新一页,**按消息 id 合并**进已加载的列表——更新已有消息与
350
+ * 卡片状态、追加新消息,保留主人翻出来的更早历史(只在切换线程时才重置,
351
+ * 见 selectThread)。合并一律在 updater 里做,避免并发覆盖。
352
+ */
353
+ async refreshThread(threadId, before) {
354
+ try {
355
+ const thread = await api.thread(threadId, before ? { before } : void 0);
356
+ store.update((prev) => {
357
+ const loaded = prev.threads[threadId];
358
+ if (loaded === void 0) return { ...prev, threads: { ...prev.threads, [threadId]: thread } };
359
+ const messages = before ? mergeThreadMessages(thread.messages, loaded.messages) : mergeThreadMessages(loaded.messages, thread.messages);
360
+ const oldest = messages[0]?.id ?? null;
361
+ return { ...prev, threads: { ...prev.threads, [threadId]: { ...thread, messages, hasMore: thread.hasMore || loaded.hasMore, oldestId: oldest } } };
362
+ });
363
+ if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
364
+ } catch (error) {
365
+ const message = error instanceof Error ? error.message : String(error);
366
+ console.error("[agent-hub] \u62C9\u53D6\u7EBF\u7A0B\u5931\u8D25:", error);
367
+ patch({ connectionDown: true, downReason: reasonOfError(message), downMessage: reasonOfError(message) === "failed" ? message : null });
368
+ }
369
+ },
370
+ async refreshAll() {
371
+ await Promise.all([hub.refreshOverview(), hub.refreshInbox(), hub.refreshActivity()]);
372
+ },
373
+ onChanged(event) {
374
+ void (async () => {
375
+ try {
376
+ const plan = refreshPlan(event.scope, event.botId);
377
+ const jobs = [];
378
+ if (plan.overview) jobs.push(hub.refreshOverview());
379
+ for (const botId of plan.bots) jobs.push(hub.refreshBot(botId));
380
+ for (const threadId of plan.threads) jobs.push(hub.refreshThread(threadId));
381
+ if (plan.inbox) jobs.push(hub.refreshInbox());
382
+ if (plan.activity) jobs.push(hub.refreshActivity());
383
+ if (plan.settings) jobs.push(hub.refreshSettings());
384
+ await Promise.all(jobs);
385
+ } catch (error) {
386
+ console.error("[agent-hub] \u5904\u7406\u53D8\u5316\u4E8B\u4EF6\u51FA\u9519:", error);
387
+ }
388
+ })();
389
+ },
390
+ markDown(reason = null, message = null) {
391
+ patch({ connectionDown: true, downReason: reason, downMessage: reason === "failed" ? message : null });
392
+ },
393
+ onReconnected() {
394
+ if (store.getSnapshot().connectionDown) patch({ connectionDown: false, downReason: null, downMessage: null });
395
+ const selected = store.getSnapshot().selectedThread;
396
+ store.update((prev) => ({ ...prev, bots: {} }));
397
+ void hub.refreshAll();
398
+ void hub.refreshSettings();
399
+ void hub.refreshThread(selected);
400
+ if (selected !== "team") void hub.refreshBot(selected);
401
+ },
402
+ async send(threadId, text) {
403
+ const result = await api.command("sendMessage", { threadId, text });
404
+ if (!result.ok) throw new Error(result.error);
405
+ await hub.refreshThread(threadId);
406
+ },
407
+ command(name, input) {
408
+ return api.command(name, input);
409
+ },
410
+ selectThread(threadId) {
411
+ store.update((prev) => {
412
+ if (prev.selectedThread === threadId) return { ...prev, view: "main" };
413
+ const threads = { ...prev.threads };
414
+ delete threads[threadId];
415
+ return { ...prev, selectedThread: threadId, view: "main", threads };
416
+ });
417
+ void api.command("markThreadRead", { threadId }).then(() => hub.refreshOverview()).catch(() => void 0);
418
+ },
419
+ setView(view) {
420
+ patch({ view });
421
+ },
422
+ dismissToast(id) {
423
+ store.update((prev) => ({ ...prev, toasts: prev.toasts.filter((t) => t.id !== id) }));
424
+ }
425
+ };
426
+ return hub;
427
+ }
428
+ var RECONNECT_BACKOFF_MS = [1e3, 2e3, 5e3, 1e4];
429
+ var DOWN_AFTER_MS = 3e4;
430
+ function connectEvents(hub) {
431
+ let closed = false;
432
+ let source = null;
433
+ let retryTimer = null;
434
+ let attempt = 0;
435
+ let downTimer = null;
436
+ const clearDownTimer = () => {
437
+ if (downTimer !== null) {
438
+ clearTimeout(downTimer);
439
+ downTimer = null;
440
+ }
441
+ };
442
+ const scheduleReconnect = () => {
443
+ if (closed) return;
444
+ const wait = RECONNECT_BACKOFF_MS[Math.min(attempt, RECONNECT_BACKOFF_MS.length - 1)];
445
+ attempt += 1;
446
+ retryTimer = setTimeout(open, wait);
447
+ };
448
+ const open = () => {
449
+ if (closed) return;
450
+ try {
451
+ source = new EventSource("agent-hub/events");
452
+ } catch (error) {
453
+ console.error("[agent-hub] \u5EFA\u7ACB SSE \u8FDE\u63A5\u5931\u8D25:", error);
454
+ scheduleReconnect();
455
+ return;
456
+ }
457
+ source.onopen = () => {
458
+ attempt = 0;
459
+ clearDownTimer();
460
+ hub.onReconnected();
461
+ };
462
+ source.addEventListener("changed", (event) => {
463
+ try {
464
+ hub.onChanged(JSON.parse(event.data));
465
+ } catch (error) {
466
+ console.error("[agent-hub] \u89E3\u6790 changed \u4E8B\u4EF6\u5931\u8D25:", error);
467
+ }
468
+ });
469
+ source.onerror = () => {
470
+ try {
471
+ source?.close();
472
+ } catch {
473
+ }
474
+ source = null;
475
+ if (downTimer === null && !closed) downTimer = setTimeout(() => {
476
+ if (!closed) hub.markDown(null);
477
+ }, DOWN_AFTER_MS);
478
+ scheduleReconnect();
479
+ };
480
+ };
481
+ open();
482
+ return () => {
483
+ closed = true;
484
+ clearDownTimer();
485
+ if (retryTimer !== null) clearTimeout(retryTimer);
486
+ try {
487
+ source?.close();
488
+ } catch {
489
+ }
490
+ };
491
+ }
492
+
493
+ // src/client/pages/HubPage.tsx
494
+ var import_react11 = require("react");
495
+
496
+ // src/client/components/TopBar.tsx
497
+ var import_jsx_runtime = require("react/jsx-runtime");
498
+ function GearIcon() {
499
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", className: "ah-gear-icon", children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M8 5.2A2.8 2.8 0 1 0 8 10.8 2.8 2.8 0 0 0 8 5.2zm0 1.5a1.3 1.3 0 1 1 0 2.6 1.3 1.3 0 0 1 0-2.6z" }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M7.1 1h1.8l.3 1.6c.5.1 1 .3 1.4.6l1.5-.7 1.3 1.3-.7 1.5c.3.4.5.9.6 1.4l1.6.3v1.8l-1.6.3c-.1.5-.3 1-.6 1.4l.7 1.5-1.3 1.3-1.5-.7c-.4.3-.9.5-1.4.6L8.9 15H7.1l-.3-1.6c-.5-.1-1-.3-1.4-.6l-1.5.7-1.3-1.3.7-1.5c-.3-.4-.5-.9-.6-1.4L1.1 8.6V6.8l1.6-.3c.1-.5.3-1 .6-1.4l-.7-1.5 1.3-1.3 1.5.7c.4-.3.9-.5 1.4-.6L7.1 1z", opacity: "0.55" })
502
+ ] });
503
+ }
504
+ function TopBar(props) {
505
+ const o = props.overview;
506
+ const back = props.view === "main" ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onBack, children: "\u8FD4\u56DE" });
507
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "ah-topbar", children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ah-topbar-title", children: [
509
+ back,
510
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "\u961F\u53CB" }),
511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ah-topbar-now", children: [
512
+ o.nowText,
513
+ o.quietNow ? " \xB7 \u514D\u6253\u6270\u4E2D" : ""
514
+ ] })
515
+ ] }),
516
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ah-topbar-status", children: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: `ah-dot ah-dot-${o.quota.light === "green" ? "ok" : o.quota.light === "yellow" ? "warn" : "danger"}`, children: [
518
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ah-dot-circle", "aria-hidden": "true" }),
519
+ o.quota.lightLabel,
520
+ " \xB7 ",
521
+ o.quota.usedText
522
+ ] }),
523
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onOpenActivity, children: "\u6D3B\u52A8\u6D41" }),
524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "ah-btn ah-btn-icon", "aria-label": "\u8BBE\u7F6E", title: "\u8BBE\u7F6E", onClick: props.onOpenSettings, children: [
525
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GearIcon, {}),
526
+ "\u8BBE\u7F6E"
527
+ ] }),
528
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "ah-btn", onClick: props.onOpenInbox, children: [
529
+ "\u6536\u4EF6\u7BB1",
530
+ o.inbox.total > 0 ? `\uFF08${o.inbox.total}\uFF09` : ""
531
+ ] }),
532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", onClick: props.onNewBot, children: "\u65B0\u5EFA\u961F\u53CB" })
533
+ ] })
534
+ ] });
535
+ }
536
+
537
+ // src/client/components/Ui.tsx
538
+ var import_jsx_runtime2 = require("react/jsx-runtime");
539
+ function Avatar(props) {
540
+ const style = {
541
+ width: `${props.size ?? 32}px`,
542
+ height: `${props.size ?? 32}px`,
543
+ background: props.color || "var(--dsw-alias-accent-muted)",
544
+ color: "var(--dsw-alias-text-on-accent, currentColor)"
545
+ };
546
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ah-avatar", style, "aria-hidden": "true", children: props.initials });
547
+ }
548
+ function Skeleton(props) {
549
+ const rows = props.rows ?? 3;
550
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ah-skeleton", "aria-busy": "true", "aria-label": "\u52A0\u8F7D\u4E2D", children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ah-skeleton-row" }, i)) });
551
+ }
552
+ function RetryBox(props) {
553
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ah-retry", role: "alert", children: [
554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: props.message }),
555
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u91CD\u8BD5" })
556
+ ] });
557
+ }
558
+ function EmptyHint(props) {
559
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ah-empty", children: props.text });
560
+ }
561
+ function UnreadBadge(props) {
562
+ if (props.count <= 0) return null;
563
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ah-unread", "aria-label": `${props.count} \u6761\u672A\u8BFB`, children: props.count > 99 ? "99+" : props.count });
564
+ }
565
+
566
+ // src/client/components/BotList.tsx
567
+ var import_jsx_runtime3 = require("react/jsx-runtime");
568
+ function BotRow(props) {
569
+ const b = props.bot;
570
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: `ah-bot-row${props.selected ? " ah-selected" : ""}`, onClick: () => props.onSelect(b.id), children: [
571
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { color: b.color, initials: b.initials }),
572
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ah-bot-row-main", children: [
573
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ah-bot-row-name", children: [
574
+ b.name,
575
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-role", children: b.roleLabel }),
576
+ b.waitingOwner ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-tag ah-tag-wait", children: "\u7B49\u4F60" }) : null
577
+ ] }),
578
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-activity", title: b.activityText, children: b.activityText })
579
+ ] }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnreadBadge, { count: b.unread })
581
+ ] });
582
+ }
583
+ function BotList(props) {
584
+ const threadOf = (botId) => botId;
585
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("nav", { className: "ah-botlist", "aria-label": "\u961F\u53CB\u5217\u8868", children: [
586
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
587
+ "button",
588
+ {
589
+ type: "button",
590
+ className: `ah-bot-row${props.selected === "team" ? " ah-selected" : ""}`,
591
+ onClick: () => props.onSelect("team"),
592
+ children: [
593
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-avatar ah-avatar-team", "aria-hidden": "true", children: "\u961F" }),
594
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ah-bot-row-main", children: [
595
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-name", children: "\u56E2\u961F" }),
596
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ah-bot-row-activity", children: props.overview.team.lastMessageText ?? "\u673A\u5668\u4EBA\u7684\u4EA4\u63A5\u548C\u6C47\u62A5\u4F1A\u6284\u9001\u5230\u8FD9\u91CC" })
597
+ ] }),
598
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(UnreadBadge, { count: props.overview.team.unread })
599
+ ]
600
+ }
601
+ ),
602
+ props.overview.bots.map((bot) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BotRow, { bot, selected: props.selected === threadOf(bot.id), onSelect: (id) => props.onSelect(threadOf(id)) }, bot.id)),
603
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ah-botlist-footer", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: "ah-inbox-entry", onClick: props.onOpenInbox, children: [
604
+ "\u6536\u4EF6\u7BB1",
605
+ props.overview.inbox.total > 0 ? `\uFF08${props.overview.inbox.total}\uFF09` : ""
606
+ ] }) })
607
+ ] });
608
+ }
609
+
610
+ // src/client/components/ThreadPane.tsx
611
+ var import_react3 = require("react");
612
+
613
+ // src/client/components/Cards.tsx
614
+ var import_react2 = require("react");
615
+
616
+ // src/client/logic/cards.ts
617
+ function isApproveOption(option) {
618
+ return option === "\u6279\u51C6";
619
+ }
620
+ function expiryMeaningOf(card) {
621
+ if (card.expiresText === null || card.expiresText === "") return null;
622
+ const at = card.expiresText.endsWith("\u540E") ? card.expiresText.slice(0, -1) : card.expiresText;
623
+ if (card.type === "question") {
624
+ const fallback = card.defaultOption ?? "\u9ED8\u8BA4\u9009\u9879";
625
+ return `${at}\u524D\u672A\u7B54\u590D\u6309\u9ED8\u8BA4\u300C${fallback}\u300D\u5904\u7406`;
626
+ }
627
+ if (card.type === "approval") return `${at}\u524D\u6709\u6548\uFF0C\u8FC7\u671F\u89C6\u4E3A\u62D2\u7EDD`;
628
+ if (card.type === "proposal") return `${at}\u524D\u672A\u7B54\u590D\u81EA\u52A8\u64A4\u56DE`;
629
+ return card.expiresText;
630
+ }
631
+ function cardResultText(card) {
632
+ if (card.status === "answered") return `\u4F60\u9009\u4E86\uFF1A${card.answer ?? "\u5DF2\u5904\u7406"}`;
633
+ if (card.status === "expired") return `\u5DF2\u8FC7\u671F\uFF0C\u5DF2\u6309\u9ED8\u8BA4\uFF1A${card.answer ?? "\u9ED8\u8BA4\u9009\u9879"}`;
634
+ if (card.status === "withdrawn") return "\u5DF2\u64A4\u56DE";
635
+ if (card.status === "closed") return "\u5DF2\u5173\u95ED";
636
+ return null;
637
+ }
638
+ function cardSubmission(card, option, note) {
639
+ const picked = option.trim();
640
+ if (picked === "") return { error: "\u56DE\u7B54\uFF1A\u4E0D\u80FD\u4E3A\u7A7A" };
641
+ if (card.type === "approval") {
642
+ if (card.approvalId === null) {
643
+ return { name: "answerInbox", input: { id: card.inboxId, answer: picked } };
644
+ }
645
+ return { name: "decideApproval", input: { id: card.approvalId, approve: isApproveOption(picked), ...note !== void 0 && note !== "" ? { note } : {} } };
646
+ }
647
+ return { name: "answerInbox", input: { id: card.inboxId, answer: picked } };
648
+ }
649
+
650
+ // src/client/logic/thread.ts
651
+ function isCenterMessage(m) {
652
+ return m.kind === "system" || m.kind === "digest";
653
+ }
654
+ function sameGroup(a, b) {
655
+ if (isCenterMessage(a) || isCenterMessage(b)) return false;
656
+ return a.from === b.from;
657
+ }
658
+ function groupMessages(messages) {
659
+ const out = [];
660
+ for (let i = 0; i < messages.length; i += 1) {
661
+ const message = messages[i];
662
+ const prev = i > 0 ? messages[i - 1] : null;
663
+ out.push({ message, showHeader: prev === null || !sameGroup(prev, message) });
664
+ }
665
+ return out;
666
+ }
667
+ function shouldStickToBottom(input) {
668
+ if (input.prevLastId === null) return true;
669
+ if (input.lastId === input.prevLastId) return false;
670
+ return input.nearBottom;
671
+ }
672
+ function isSendEnter(event) {
673
+ if (event.key !== "Enter") return false;
674
+ if (event.isComposing === true) return false;
675
+ if (event.keyCode === 229) return false;
676
+ return true;
677
+ }
678
+ function openBotSessionWith(ctx, sessionId) {
679
+ const c = ctx;
680
+ const get = c?.get?.bind(c);
681
+ if (typeof get !== "function") return "unavailable";
682
+ const ws = get("uiWorkspace");
683
+ const sessions = get("sessions");
684
+ const workspaces = get("workspaces");
685
+ if (!ws || !sessions || !workspaces) return "unavailable";
686
+ let list, wsList;
687
+ try {
688
+ list = sessions.list?.getSnapshot?.();
689
+ wsList = workspaces.list?.getSnapshot?.();
690
+ } catch {
691
+ return "unavailable";
692
+ }
693
+ if (wsList?.state === "error") return "unavailable";
694
+ if (list?.phase === "pending" || wsList?.phase === "pending") return "loading";
695
+ if (wsList?.archivedSessionIds?.includes(sessionId)) return "archived";
696
+ if (list?.ids && !list.ids.includes(sessionId)) return "unavailable";
697
+ try {
698
+ ws.openSession(sessionId);
699
+ } catch {
700
+ return "unavailable";
701
+ }
702
+ return "opened";
703
+ }
704
+ var OPEN_SESSION_HINTS = {
705
+ opened: null,
706
+ loading: "\u4F1A\u8BDD\u5217\u8868\u8FD8\u5728\u52A0\u8F7D\uFF0C\u7A0D\u7B49\u4E00\u4E0B\u518D\u8BD5\u3002",
707
+ archived: "\u5B83\u7684\u5DE5\u4F5C\u4F1A\u8BDD\u88AB\u5F52\u6863\u4E86\uFF0C\u5148\u5728\u4F1A\u8BDD\u5217\u8868\u91CC\u6062\u590D\u5B83\u3002",
708
+ unavailable: "\u73B0\u5728\u6253\u4E0D\u5F00\u5B83\u7684\u5DE5\u4F5C\u4F1A\u8BDD\uFF1A\u53EF\u4EE5\u5728\u5DE6\u4FA7\u680F\u7684\u9879\u76EE\u91CC\u627E\u5B83\u7684\u4F1A\u8BDD\u3002"
709
+ };
710
+
711
+ // src/client/components/Cards.tsx
712
+ var import_jsx_runtime4 = require("react/jsx-runtime");
713
+ var CARD_TYPE_LABELS = {
714
+ question: "\u63D0\u95EE",
715
+ approval: "\u5BA1\u6279",
716
+ proposal: "\u63D0\u8BAE",
717
+ alert: "\u544A\u8B66"
718
+ };
719
+ function InboxCard(props) {
720
+ const c = props.card;
721
+ const [picked, setPicked] = (0, import_react2.useState)(null);
722
+ const [error, setError] = (0, import_react2.useState)(null);
723
+ const [freeText, setFreeText] = (0, import_react2.useState)("");
724
+ const [note, setNote] = (0, import_react2.useState)("");
725
+ const resultText = cardResultText(c);
726
+ const answered = resultText !== null;
727
+ const busy = props.busy || picked !== null;
728
+ const pick = (option) => {
729
+ setPicked(option);
730
+ setError(null);
731
+ void props.actions.onPick(c, option, c.type === "approval" ? note : void 0).then((failure) => {
732
+ if (failure !== null) {
733
+ setPicked(null);
734
+ setError(failure);
735
+ }
736
+ }).catch((e) => {
737
+ setPicked(null);
738
+ setError(e instanceof Error ? e.message : String(e));
739
+ });
740
+ };
741
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `ah-card ah-card-${c.type}${answered ? " ah-card-done" : ""}`, "data-answered": answered ? "true" : "false", children: [
742
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ah-card-head", children: [
743
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ah-card-type", children: CARD_TYPE_LABELS[c.type] }),
744
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "ah-card-title", children: c.title })
745
+ ] }),
746
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-body", children: c.body }),
747
+ expiryMeaningOf(c) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-expire", children: expiryMeaningOf(c) }) : null,
748
+ answered ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-answer", children: resultText }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ah-card-options", children: c.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
750
+ "button",
751
+ {
752
+ type: "button",
753
+ className: c.type === "approval" ? isApproveOption(option) ? "ah-btn ah-btn-primary" : "ah-btn ah-btn-danger" : option === c.defaultOption ? "ah-btn ah-btn-primary" : "ah-btn",
754
+ disabled: busy,
755
+ onClick: () => pick(option),
756
+ children: [
757
+ option,
758
+ option === c.defaultOption && c.type !== "approval" ? "\uFF08\u9ED8\u8BA4\uFF09" : ""
759
+ ]
760
+ },
761
+ option
762
+ )) }),
763
+ c.type === "approval" && picked === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
764
+ "input",
765
+ {
766
+ className: "ah-card-note",
767
+ value: note,
768
+ placeholder: "\u9644\u4E00\u53E5\u8BDD\uFF08\u53EF\u4E0D\u586B\uFF09",
769
+ disabled: busy,
770
+ onChange: (e) => setNote(e.target.value)
771
+ }
772
+ ) : null,
773
+ c.type === "question" && picked === null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ah-card-freetext", children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
775
+ "input",
776
+ {
777
+ value: freeText,
778
+ placeholder: "\u6216\u8005\u5199\u4E00\u53E5\u4F60\u7684\u56DE\u7B54",
779
+ disabled: busy,
780
+ onChange: (e) => setFreeText(e.target.value),
781
+ onKeyDown: (e) => {
782
+ if (isSendEnter(e.nativeEvent) && freeText.trim() !== "") pick(freeText.trim());
783
+ }
784
+ }
785
+ ),
786
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
787
+ "button",
788
+ {
789
+ type: "button",
790
+ className: "ah-btn",
791
+ disabled: busy || freeText.trim() === "",
792
+ onClick: () => pick(freeText.trim()),
793
+ children: "\u53D1\u9001"
794
+ }
795
+ )
796
+ ] }) : null,
797
+ error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "ah-card-error", role: "alert", children: error }) : null
798
+ ] })
799
+ ] });
800
+ }
801
+
802
+ // src/client/logic/lifecycle.ts
803
+ function lifecycleButtons(lifecycle) {
804
+ const buttons = [];
805
+ if (lifecycle === "active" || lifecycle === "probation") buttons.push({ action: "pause", label: "\u6682\u505C" });
806
+ if (lifecycle === "paused") buttons.push({ action: "resume", label: "\u6062\u590D" });
807
+ if (lifecycle === "quarantined") buttons.push({ action: "release_quarantine", label: "\u89E3\u9664\u505C\u7528" });
808
+ if (lifecycle === "probation") buttons.push({ action: "promote", label: "\u63D0\u524D\u8F6C\u6B63" });
809
+ if (lifecycle !== "retired") buttons.push({ action: "retire", label: "\u9000\u5F79" });
810
+ return buttons;
811
+ }
812
+ function pauseResumeAction(lifecycle) {
813
+ if (lifecycle === "active" || lifecycle === "probation") return "pause";
814
+ if (lifecycle === "paused") return "resume";
815
+ return null;
816
+ }
817
+ function lifecycleAllTargets(bots, target) {
818
+ const wanted = target === "paused" ? ["active", "probation"] : ["paused"];
819
+ return bots.filter((bot) => wanted.includes(bot.lifecycle)).map((bot) => bot.id);
820
+ }
821
+
822
+ // src/client/components/ThreadPane.tsx
823
+ var import_jsx_runtime5 = require("react/jsx-runtime");
824
+ function Message(props) {
825
+ const { m, showHeader } = props;
826
+ if (isCenterMessage(m)) {
827
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-msg ah-msg-center", children: [
828
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-msg-time", children: m.timeText }),
829
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-msg-bubble-center", children: m.text })
830
+ ] });
831
+ }
832
+ if (m.card) {
833
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `ah-msg${m.from === "owner" ? " ah-msg-mine" : ""}`, children: [
834
+ m.from === "owner" || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { color: m.fromColor, initials: m.fromName.slice(0, 1), size: 28 }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-msg-body", children: [
836
+ m.from === "owner" || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ah-msg-meta", children: [
837
+ m.fromName,
838
+ " \xB7 ",
839
+ m.timeText
840
+ ] }),
841
+ m.from === "owner" && showHeader ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-msg-meta", children: m.timeText }) : null,
842
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InboxCard, { card: m.card, actions: props.actions, busy: props.busyInboxId === m.card.inboxId })
843
+ ] })
844
+ ] });
845
+ }
846
+ const mine = m.from === "owner";
847
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `ah-msg${mine ? " ah-msg-mine" : ""}${m.kind === "report" ? " ah-msg-report" : ""}`, children: [
848
+ mine || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { color: m.fromColor, initials: m.fromName.slice(0, 1), size: 28 }),
849
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-msg-body", children: [
850
+ mine || !showHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ah-msg-meta", children: [
851
+ m.fromName,
852
+ " \xB7 ",
853
+ m.timeText
854
+ ] }),
855
+ mine && showHeader ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-msg-meta", children: m.timeText }) : null,
856
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-msg-bubble", children: m.text })
857
+ ] })
858
+ ] });
859
+ }
860
+ var NEAR_BOTTOM_PX = 120;
861
+ var threadDrafts = /* @__PURE__ */ new Map();
862
+ function readDraft(threadId) {
863
+ return threadDrafts.get(threadId) ?? "";
864
+ }
865
+ function saveDraft(threadId, text) {
866
+ if (text === "") threadDrafts.delete(threadId);
867
+ else threadDrafts.set(threadId, text);
868
+ }
869
+ function ThreadPane(props) {
870
+ const [draft, setDraft] = (0, import_react3.useState)(() => readDraft(props.thread?.threadId ?? ""));
871
+ const [sending, setSending] = (0, import_react3.useState)(false);
872
+ const [sendError, setSendError] = (0, import_react3.useState)(null);
873
+ const [confirmInterrupt, setConfirmInterrupt] = (0, import_react3.useState)(false);
874
+ const bottomRef = (0, import_react3.useRef)(null);
875
+ const listRef = (0, import_react3.useRef)(null);
876
+ const prevLastIdRef = (0, import_react3.useRef)(null);
877
+ const prevHeightRef = (0, import_react3.useRef)(null);
878
+ const messageCount = props.thread?.messages.length ?? 0;
879
+ (0, import_react3.useLayoutEffect)(() => {
880
+ const list = listRef.current;
881
+ if (list !== null) prevHeightRef.current = list.scrollHeight;
882
+ }, [messageCount]);
883
+ const lastId = props.thread?.messages.at(-1)?.id ?? null;
884
+ (0, import_react3.useEffect)(() => {
885
+ const list = listRef.current;
886
+ const prevLastId = prevLastIdRef.current;
887
+ prevLastIdRef.current = lastId;
888
+ if (list === null) return;
889
+ if (lastId !== null && prevLastId !== null && lastId === prevLastId) {
890
+ const prevHeight = prevHeightRef.current;
891
+ prevHeightRef.current = list.scrollHeight;
892
+ if (prevHeight !== null && list.scrollHeight > prevHeight) list.scrollTop += list.scrollHeight - prevHeight;
893
+ return;
894
+ }
895
+ const nearBottom = list.scrollHeight - list.scrollTop - list.clientHeight < NEAR_BOTTOM_PX;
896
+ if (shouldStickToBottom({ prevLastId, lastId, nearBottom })) {
897
+ bottomRef.current?.scrollIntoView({ block: "end" });
898
+ }
899
+ }, [lastId, messageCount]);
900
+ const send = async () => {
901
+ const text = draft.trim();
902
+ if (!text || sending) return;
903
+ setSending(true);
904
+ setSendError(null);
905
+ try {
906
+ await props.onSend(text);
907
+ saveDraft(props.thread?.threadId ?? "", "");
908
+ setDraft("");
909
+ } catch (error) {
910
+ setSendError(error instanceof Error ? error.message : String(error) || "\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
911
+ } finally {
912
+ setSending(false);
913
+ }
914
+ };
915
+ const h = props.header;
916
+ if (props.error) {
917
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-thread", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-retry", role: "alert", children: [
918
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: props.error }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u91CD\u8BD5" })
920
+ ] }) });
921
+ }
922
+ if (!props.thread) {
923
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ah-thread", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Skeleton, { rows: 5 }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EmptyHint, { text: "\u4ECE\u5DE6\u8FB9\u9009\u4E00\u4E2A\u961F\u53CB\u5F00\u59CB\u804A\uFF1B\u5B83\u4EEC\u7684\u6C47\u62A5\u548C\u63D0\u95EE\u90FD\u4F1A\u51FA\u73B0\u5728\u5BF9\u5E94\u7EBF\u7A0B\u91CC\u3002" }) });
924
+ }
925
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "ah-thread", "aria-label": `\u4E0E ${h?.name ?? props.thread.title} \u7684\u5BF9\u8BDD`, children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "ah-thread-header", children: [
927
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-header-main", children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h2", { children: [
929
+ h?.name ?? props.thread.title,
930
+ h?.roleLabel ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ah-bot-row-role", children: h.roleLabel }) : null
931
+ ] }),
932
+ h?.activityText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "ah-thread-status", title: h.activityText, children: h.activityText }) : null,
933
+ h?.nextThinkText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "ah-thread-next", children: [
934
+ "\u4E0B\u6B21\u81EA\u67E5\uFF1A",
935
+ h.nextThinkText
936
+ ] }) : null
937
+ ] }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-header-actions", children: [
939
+ props.thread.hasMore ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onLoadEarlier, children: "\u52A0\u8F7D\u66F4\u65E9" }) : null,
940
+ h?.thinking ? confirmInterrupt ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-danger", onClick: () => {
942
+ setConfirmInterrupt(false);
943
+ props.onInterrupt();
944
+ }, children: "\u786E\u8BA4\u6253\u65AD" }),
945
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setConfirmInterrupt(false), children: "\u518D\u60F3\u60F3" })
946
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-danger", onClick: () => setConfirmInterrupt(true), children: "\u6253\u65AD" }) : null,
947
+ h && h.lifecycle !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onTeach, children: "\u6559\u5B83\u4E00\u62DB" }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onOpenSession, children: "\u6253\u5F00\u5DE5\u4F5C\u4F1A\u8BDD" }),
950
+ props.rightCollapsed && props.onExpandRight ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onExpandRight, children: "\u8BE6\u60C5" }) : null,
951
+ pauseResumeAction(h.lifecycle) !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onTogglePause, children: pauseResumeAction(h.lifecycle) === "pause" ? "\u6682\u505C" : "\u6062\u590D" }) : null
952
+ ] }) : null
953
+ ] })
954
+ ] }),
955
+ props.banner ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-banner", role: "status", children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: props.banner }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onBannerDismiss, children: "\u77E5\u9053\u4E86" })
958
+ ] }) : null,
959
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-messages", ref: listRef, children: [
960
+ groupMessages(props.thread.messages).map((slot) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
961
+ Message,
962
+ {
963
+ m: slot.message,
964
+ showHeader: slot.showHeader,
965
+ actions: props.actions,
966
+ busyInboxId: props.busyInboxId
967
+ },
968
+ slot.message.id
969
+ )),
970
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: bottomRef })
971
+ ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("footer", { className: "ah-thread-input", children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
974
+ "input",
975
+ {
976
+ value: draft,
977
+ placeholder: `\u7ED9 ${props.inputPlaceholderName} \u53D1\u6D88\u606F\u2026`,
978
+ disabled: sending,
979
+ onChange: (e) => {
980
+ setDraft(e.target.value);
981
+ saveDraft(props.thread?.threadId ?? "", e.target.value);
982
+ },
983
+ onKeyDown: (e) => {
984
+ if (isSendEnter(e.nativeEvent)) void send();
985
+ }
986
+ }
987
+ ),
988
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: sending || draft.trim() === "", onClick: () => void send(), children: sending ? "\u53D1\u9001\u4E2D\u2026" : "\u53D1\u9001" })
989
+ ] }),
990
+ sendError ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ah-thread-senderror", role: "alert", children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
992
+ "\u6CA1\u53D1\u51FA\u53BB\uFF1A",
993
+ sendError
994
+ ] }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ah-btn", onClick: () => void send(), children: "\u91CD\u8BD5" })
996
+ ] }) : null
997
+ ] });
998
+ }
999
+
1000
+ // src/client/components/RightPanel.tsx
1001
+ var import_react4 = require("react");
1002
+
1003
+ // src/client/logic/settings.ts
1004
+ function validateClockTime(value, field) {
1005
+ if (!/^([01]\d|2[0-3]):[0-5]\d$/.test(value.trim())) return `${field}\uFF1A\u8981\u5199\u6210 HH:mm\uFF08\u4F8B\u5982 23:00\uFF09`;
1006
+ return null;
1007
+ }
1008
+ function draftFromSettings(settings) {
1009
+ return {
1010
+ quietStart: settings.quietHours.start,
1011
+ quietEnd: settings.quietHours.end,
1012
+ digestAt: settings.digestAt,
1013
+ pushChannel: settings.pushChannel,
1014
+ dailyPushCap: String(settings.dailyPushCap),
1015
+ botDailyPushCap: String(settings.botDailyPushCap),
1016
+ defaultModel: settings.defaultModel === null ? "" : JSON.stringify(settings.defaultModel)
1017
+ };
1018
+ }
1019
+ function validateSettingsDraft(draft) {
1020
+ const start = validateClockTime(draft.quietStart, "\u514D\u6253\u6270\u5F00\u59CB");
1021
+ if (start !== null) return start;
1022
+ const end = validateClockTime(draft.quietEnd, "\u514D\u6253\u6270\u7ED3\u675F");
1023
+ if (end !== null) return end;
1024
+ const digest = validateClockTime(draft.digestAt, "\u65E5\u62A5\u65F6\u95F4");
1025
+ if (digest !== null) return digest;
1026
+ if (!["dsh", "macos", "none"].includes(draft.pushChannel)) return "\u63A8\u9001\u6E20\u9053\uFF1A\u53EA\u80FD\u9009 macOS \u901A\u77E5 / \u4E0D\u63A8 / \u5BBF\u4E3B";
1027
+ const whole = Number(draft.dailyPushCap);
1028
+ if (!Number.isInteger(whole) || whole < 0 || whole > 10) return "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u5168\u4F53\uFF09\uFF1A0 \u5230 10 \u7684\u6574\u6570";
1029
+ const perBot = Number(draft.botDailyPushCap);
1030
+ if (!Number.isInteger(perBot) || perBot < 0 || perBot > 3) return "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u6BCF\u4E2A\u961F\u53CB\uFF09\uFF1A0 \u5230 3 \u7684\u6574\u6570";
1031
+ if (perBot > whole) return "\u6BCF\u4E2A\u961F\u53CB\u7684\u5355\u63A8\u4E0A\u9650\u4E0D\u80FD\u8D85\u8FC7\u5168\u4F53\u4E0A\u9650";
1032
+ return null;
1033
+ }
1034
+ function modelSelectOptions(current, options) {
1035
+ const list = [...options];
1036
+ if (current == null) return list;
1037
+ const present = list.some((option) => JSON.stringify(option.value) === JSON.stringify(current));
1038
+ if (!present) {
1039
+ list.push({ value: current, label: `${current.provider} / ${current.model}\uFF08\u5DF2\u4E0D\u5728\u5BBF\u4E3B\u76EE\u5F55\uFF0C\u8BF7\u6362\u4E00\u4E2A\uFF09` });
1040
+ }
1041
+ return list;
1042
+ }
1043
+ function buildSettingsPatch(draft, loaded) {
1044
+ const patch = {
1045
+ quietHours: { start: draft.quietStart.trim(), end: draft.quietEnd.trim() },
1046
+ digestAt: draft.digestAt.trim(),
1047
+ pushChannel: draft.pushChannel,
1048
+ dailyPushCap: Number(draft.dailyPushCap),
1049
+ botDailyPushCap: Number(draft.botDailyPushCap)
1050
+ };
1051
+ if (loaded === void 0 || loaded.defaultModel !== draft.defaultModel) {
1052
+ if (draft.defaultModel === "") patch.defaultModel = null;
1053
+ else {
1054
+ try {
1055
+ patch.defaultModel = JSON.parse(draft.defaultModel);
1056
+ } catch {
1057
+ }
1058
+ }
1059
+ }
1060
+ return patch;
1061
+ }
1062
+
1063
+ // src/client/components/RightPanel.tsx
1064
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1065
+ var AUTONOMY_CHOICES = [
1066
+ { value: "L0", label: "L0 \u53EA\u89C2\u5BDF" },
1067
+ { value: "L1", label: "L1 \u5EFA\u8BAE" },
1068
+ { value: "L2", label: "L2 \u6267\u884C" },
1069
+ { value: "L3", label: "L3 \u6388\u6743\u6267\u884C" }
1070
+ ];
1071
+ var PACE_CHOICES = [
1072
+ { value: "active", label: "\u79EF\u6781" },
1073
+ { value: "normal", label: "\u9002\u4E2D" },
1074
+ { value: "quiet", label: "\u5B89\u9759" }
1075
+ ];
1076
+ var LIFECYCLE_OK_TEXT = {
1077
+ pause: "\u5DF2\u6682\u505C\uFF08\u671F\u95F4\u7684\u53D8\u5316\u90FD\u5B58\u7740\uFF09",
1078
+ resume: "\u5DF2\u6062\u590D",
1079
+ promote: "\u5DF2\u63D0\u524D\u8F6C\u6B63",
1080
+ release_quarantine: "\u5DF2\u89E3\u9664\u505C\u7528",
1081
+ retire: "\u5DF2\u9000\u5F79\uFF08\u6570\u636E\u4FDD\u7559\uFF09"
1082
+ };
1083
+ function watchStatusInput(botId, w, status) {
1084
+ return { id: w.id, botId, kind: w.kind, target: w.target, why: w.why ?? "", status };
1085
+ }
1086
+ function Section(props) {
1087
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "ah-right-section", children: [
1088
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: props.title }),
1089
+ props.children
1090
+ ] });
1091
+ }
1092
+ function useRunner(run) {
1093
+ const [busy, setBusy] = (0, import_react4.useState)(false);
1094
+ const [message, setMessage] = (0, import_react4.useState)(null);
1095
+ const exec = async (name, input, okText) => {
1096
+ if (busy) return false;
1097
+ setBusy(true);
1098
+ try {
1099
+ const result = await run(name, input);
1100
+ setMessage(result.ok ? okText : result.error);
1101
+ return result.ok;
1102
+ } catch (e) {
1103
+ setMessage(e instanceof Error ? e.message : String(e));
1104
+ return false;
1105
+ } finally {
1106
+ setBusy(false);
1107
+ }
1108
+ };
1109
+ return { busy, message, exec, setMessage };
1110
+ }
1111
+ function RightPanel(props) {
1112
+ const runner = useRunner(props.run);
1113
+ const [editingProfile, setEditingProfile] = (0, import_react4.useState)(false);
1114
+ const [editingAspiration, setEditingAspiration] = (0, import_react4.useState)(null);
1115
+ const [persona, setPersona] = (0, import_react4.useState)("");
1116
+ const [newAspiration, setNewAspiration] = (0, import_react4.useState)("");
1117
+ const [newRule, setNewRule] = (0, import_react4.useState)("");
1118
+ const [newRuleRedLine, setNewRuleRedLine] = (0, import_react4.useState)(false);
1119
+ const [editingRoutine, setEditingRoutine] = (0, import_react4.useState)(null);
1120
+ const [routineSteps, setRoutineSteps] = (0, import_react4.useState)("");
1121
+ const [confirmRetire, setConfirmRetire] = (0, import_react4.useState)(false);
1122
+ const [notice, setNotice] = (0, import_react4.useState)(null);
1123
+ if (!props.bot) {
1124
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("aside", { className: "ah-right", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ah-empty", children: "\u4ECE\u5DE6\u8FB9\u9009\u4E00\u4E2A\u961F\u53CB\uFF0C\u8FD9\u91CC\u663E\u793A\u5B83\u7684\u6863\u6848\u3001\u76EE\u6807\u3001\u8BAE\u7A0B\u548C\u300C\u4E3A\u4EC0\u4E48\u9192 / \u4E3A\u4EC0\u4E48\u6CA1\u9192\u300D\u3002" }) });
1125
+ }
1126
+ const b = props.bot;
1127
+ const doExec = async (name, input, okText) => {
1128
+ const ok = await runner.exec(name, input, okText);
1129
+ if (ok) props.onRefresh();
1130
+ };
1131
+ const openSession = () => {
1132
+ if (!b.profile.sessionId) {
1133
+ setNotice("\u5B83\u8FD8\u6CA1\u6709\u5DE5\u4F5C\u4F1A\u8BDD\u3002");
1134
+ return;
1135
+ }
1136
+ const result = openBotSessionWith(props.clientCtx, b.profile.sessionId);
1137
+ setNotice(OPEN_SESSION_HINTS[result]);
1138
+ };
1139
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("aside", { className: "ah-right", "aria-label": `${b.card.name} \u7684\u8BE6\u60C5`, children: [
1140
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-right-head", children: [
1141
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("h2", { children: [
1142
+ b.card.name,
1143
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-bot-row-role", children: b.card.roleLabel })
1144
+ ] }),
1145
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onCollapse, "aria-label": "\u6536\u8D77\u53F3\u680F", children: "\u6536\u8D77" })
1146
+ ] }),
1147
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-right-scroll", children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u6863\u6848", children: editingProfile ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-box", children: [
1149
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1150
+ "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C",
1151
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("textarea", { rows: 3, value: persona, onChange: (e) => setPersona(e.target.value) })
1152
+ ] }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1154
+ "\u81EA\u4E3B\u7B49\u7EA7",
1155
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { value: b.profile.autonomy, onChange: (e) => {
1156
+ void doExec("updateBot", { botId: b.card.id, patch: { autonomy: e.target.value } }, "\u5DF2\u6539\u81EA\u4E3B\u7B49\u7EA7\uFF08\u4E0B\u4E00\u6B21\u601D\u8003\u751F\u6548\uFF09");
1157
+ }, children: AUTONOMY_CHOICES.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: a.value, children: a.label }, a.value)) })
1158
+ ] }),
1159
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1160
+ "\u8282\u594F",
1161
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("select", { value: b.profile.pace, onChange: (e) => {
1162
+ void doExec("updateBot", { botId: b.card.id, patch: { pace: e.target.value } }, "\u5DF2\u6539\u8282\u594F");
1163
+ }, children: PACE_CHOICES.map((p) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: p.value, children: p.label }, p.value)) })
1164
+ ] }),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
1166
+ "\u6A21\u578B",
1167
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1168
+ "select",
1169
+ {
1170
+ value: b.profile.model === null ? "" : JSON.stringify(b.profile.model),
1171
+ onChange: (e) => {
1172
+ void doExec("updateBot", { botId: b.card.id, patch: { model: e.target.value === "" ? null : JSON.parse(e.target.value) } }, "\u5DF2\u6539\u6A21\u578B");
1173
+ },
1174
+ children: [
1175
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: "\u8DDF\u968F DSH \u9ED8\u8BA4" }),
1176
+ modelSelectOptions(b.profile.model ?? void 0, props.modelOptions.filter((option) => option.value !== null)).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: JSON.stringify(option.value) ?? "", children: option.label }, i))
1177
+ ]
1178
+ }
1179
+ ),
1180
+ props.modelOptionsError ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ah-right-kv ah-card-error", children: [
1181
+ props.modelOptionsError,
1182
+ props.onRetrySettings ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1183
+ "\u3000",
1184
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onRetrySettings, children: "\u91CD\u8BD5" })
1185
+ ] }) : null
1186
+ ] }) : null
1187
+ ] }),
1188
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-settings-actions", children: [
1189
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1190
+ "button",
1191
+ {
1192
+ type: "button",
1193
+ className: "ah-btn ah-btn-primary",
1194
+ disabled: runner.busy,
1195
+ onClick: () => {
1196
+ void doExec("updateBot", { botId: b.card.id, patch: { persona } }, "\u5DF2\u4FDD\u5B58\u6027\u683C");
1197
+ setEditingProfile(false);
1198
+ },
1199
+ children: "\u4FDD\u5B58"
1200
+ }
1201
+ ),
1202
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", onClick: () => setEditingProfile(false), children: "\u53D6\u6D88" })
1203
+ ] })
1204
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1205
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: b.profile.persona === "" ? "\uFF08\u8FD8\u6CA1\u5199\u8FC7\u6027\u683C\u8BF4\u660E\uFF09" : b.profile.persona }),
1206
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "ah-right-kv", children: [
1207
+ "\u81EA\u4E3B\u7B49\u7EA7\uFF1A",
1208
+ b.profile.autonomyLabel,
1209
+ " \xB7 \u8282\u594F\uFF1A",
1210
+ b.profile.paceLabel,
1211
+ " \xB7 \u6A21\u578B\uFF1A",
1212
+ b.profile.modelText
1213
+ ] }),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "ah-right-kv", children: [
1215
+ b.profile.createdText,
1216
+ " \u4E0A\u5C97",
1217
+ b.profile.probationText ? ` \xB7 ${b.profile.probationText}` : ""
1218
+ ] }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => {
1220
+ setPersona(b.profile.persona);
1221
+ setEditingProfile(true);
1222
+ }, children: "\u7F16\u8F91\u6863\u6848" })
1223
+ ] }) }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Section, { title: "\u5FD7\u5411", children: [
1225
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.aspirations.map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: editingAspiration === a.id ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1226
+ "input",
1227
+ {
1228
+ "aria-label": "\u6539\u5FD7\u5411",
1229
+ defaultValue: a.text,
1230
+ autoFocus: true,
1231
+ onBlur: (e) => {
1232
+ const text = e.target.value.trim();
1233
+ if (text !== "" && text !== a.text) void doExec("upsertAspiration", { id: a.id, botId: b.card.id, text }, "\u5DF2\u6539\u5FD7\u5411");
1234
+ setEditingAspiration(null);
1235
+ }
1236
+ }
1237
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ah-edit-row", children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: a.text }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ah-edit-ops", children: [
1240
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setEditingAspiration(a.id), children: "\u6539" }),
1241
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => void doExec("upsertAspiration", { id: a.id, botId: b.card.id, text: a.text, status: "archived" }, "\u5DF2\u6536\u8D77\u5FD7\u5411"), children: "\u6536\u8D77" })
1242
+ ] })
1243
+ ] }) }, a.id)) }),
1244
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-add", children: [
1245
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1246
+ "input",
1247
+ {
1248
+ value: newAspiration,
1249
+ placeholder: "\u52A0\u4E00\u6761\u5FD7\u5411",
1250
+ onChange: (e) => setNewAspiration(e.target.value),
1251
+ onKeyDown: (e) => {
1252
+ if (e.key === "Enter" && newAspiration.trim() !== "") {
1253
+ void doExec("upsertAspiration", { botId: b.card.id, text: newAspiration.trim() }, "\u5DF2\u52A0\u5FD7\u5411");
1254
+ setNewAspiration("");
1255
+ }
1256
+ }
1257
+ }
1258
+ ),
1259
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1260
+ "button",
1261
+ {
1262
+ type: "button",
1263
+ className: "ah-btn",
1264
+ disabled: newAspiration.trim() === "" || runner.busy,
1265
+ onClick: () => {
1266
+ void doExec("upsertAspiration", { botId: b.card.id, text: newAspiration.trim() }, "\u5DF2\u52A0\u5FD7\u5411");
1267
+ setNewAspiration("");
1268
+ },
1269
+ children: "\u52A0\u4E00\u6761"
1270
+ }
1271
+ )
1272
+ ] })
1273
+ ] }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u76EE\u6807", children: b.goals.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-empty", children: "\u8FD8\u6CA1\u6709\u76EE\u6807\u3002\u53EF\u4EE5\u5728\u5FD7\u5411\u4E0B\u5EFA\u4E00\u4E2A\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.goals.map((g) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1275
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: g.title }),
1277
+ "\uFF08",
1278
+ g.periodText,
1279
+ "\uFF09",
1280
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("br", {}),
1281
+ g.statusLabel,
1282
+ g.lastProgressText ? ` \xB7 ${g.lastProgressText}` : ""
1283
+ ] }),
1284
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: g.status === "done_pending_check" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: runner.busy, onClick: () => void doExec("closeGoal", { id: g.id, outcome: true }, "\u5DF2\u9A8C\u6536\u901A\u8FC7"), children: "\u9A8C\u6536\u901A\u8FC7" }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", disabled: runner.busy, onClick: () => void doExec("closeGoal", { id: g.id, outcome: false }, "\u5DF2\u8BB0\u9A8C\u6536\u6CA1\u8FC7"), children: "\u6CA1\u8FC7" })
1287
+ ] }) : g.status === "active" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", disabled: runner.busy, onClick: () => void doExec("closeGoal", { id: g.id, outcome: "abandoned" }, "\u5DF2\u653E\u5F03\u76EE\u6807"), children: "\u653E\u5F03" }) : null })
1288
+ ] }, g.id)) }) }),
1289
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u5173\u6CE8\u9879", children: b.watches.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-empty", children: "\u5B83\u8FD8\u6CA1\u6709\u5728\u76EF\u7684\u4E1C\u897F\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.watches.map((w) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1291
+ w.kindLabel,
1292
+ "\uFF1A",
1293
+ w.target,
1294
+ w.status === "proposed" ? "\uFF08\u5B83\u63D0\u8BAE\u7684\uFF0C\u7B49\u4F60\u70B9\u5934\uFF09" : w.status === "paused" ? "\uFF08\u5DF2\u6682\u505C\uFF09" : ""
1295
+ ] }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: w.status === "proposed" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1297
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: runner.busy, onClick: () => void doExec("decideWatchProposal", { id: w.id, approve: true }, "\u5DF2\u540C\u610F\u5B83\u7684\u63D0\u8BAE"), children: "\u540C\u610F" }),
1298
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", disabled: runner.busy, onClick: () => void doExec("decideWatchProposal", { id: w.id, approve: false }, "\u5DF2\u62D2\u7EDD\u5B83\u7684\u63D0\u8BAE"), children: "\u4E0D\u8981" })
1299
+ ] }) : w.status === "active" || w.status === "paused" ? (
1300
+ /* 暂停/恢复要拼完整入参(D07 第 10 条):kind/target/why 来自该行已有字段,
1301
+ 否则 core 会报「关注项无效」。 */
1302
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1303
+ "button",
1304
+ {
1305
+ type: "button",
1306
+ className: "ah-btn ah-btn-ghost",
1307
+ disabled: runner.busy,
1308
+ onClick: () => void doExec("upsertWatch", watchStatusInput(b.card.id, w, w.status === "active" ? "paused" : "active"), w.status === "active" ? "\u5DF2\u6682\u505C" : "\u5DF2\u6062\u590D"),
1309
+ children: w.status === "active" ? "\u6682\u505C" : "\u6062\u590D"
1310
+ }
1311
+ )
1312
+ ) : null })
1313
+ ] }, w.id)) }) }),
1314
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Section, { title: "\u89C4\u77E9", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.rules.map((r) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1316
+ r.redLine ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-tag ah-tag-wait", children: "\u7EA2\u7EBF" }) : null,
1317
+ r.text
1318
+ ] }, r.id)) }),
1319
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-add", children: [
1320
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { value: newRule, placeholder: "\u52A0\u4E00\u6761\u89C4\u77E9", onChange: (e) => setNewRule(e.target.value) }),
1321
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "ah-right-kv", children: [
1322
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: newRuleRedLine, onChange: (e) => setNewRuleRedLine(e.target.checked) }),
1323
+ "\u7EA2\u7EBF"
1324
+ ] }),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1326
+ "button",
1327
+ {
1328
+ type: "button",
1329
+ className: "ah-btn",
1330
+ disabled: newRule.trim() === "" || runner.busy,
1331
+ onClick: () => {
1332
+ void doExec("upsertRule", { scope: b.card.id, text: newRule.trim(), redLine: newRuleRedLine }, "\u5DF2\u52A0\u89C4\u77E9");
1333
+ setNewRule("");
1334
+ setNewRuleRedLine(false);
1335
+ },
1336
+ children: "\u52A0\u4E00\u6761"
1337
+ }
1338
+ )
1339
+ ] })
1340
+ ] }),
1341
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u4F1A\u7684\u5957\u8DEF", children: b.routines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-empty", children: "\u8FD8\u6CA1\u5B66\u4F1A\u5957\u8DEF\u3002\u53EF\u4EE5\u300C\u6559\u5B83\u4E00\u62DB\u300D\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-edit-list", children: b.routines.map((r) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1342
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
1343
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: r.name }),
1344
+ "\uFF1A",
1345
+ r.whenToUse,
1346
+ "\uFF08\u7528\u8FC7 ",
1347
+ r.runs,
1348
+ " \u6B21\uFF09"
1349
+ ] }),
1350
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ah-edit-ops", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => {
1351
+ setEditingRoutine(editingRoutine === r.id ? null : r.id);
1352
+ setRoutineSteps(r.steps.join("\n"));
1353
+ }, children: editingRoutine === r.id ? "\u6536\u8D77" : "\u67E5\u770B / \u7F16\u8F91" }) }),
1354
+ editingRoutine === r.id ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-edit-box", children: [
1355
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { className: "ah-routine-steps", children: r.steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: step }, i)) }),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("textarea", { rows: 4, value: routineSteps, onChange: (e) => setRoutineSteps(e.target.value), placeholder: "\u4E00\u884C\u4E00\u6B65" }),
1357
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1358
+ "button",
1359
+ {
1360
+ type: "button",
1361
+ className: "ah-btn ah-btn-primary",
1362
+ disabled: runner.busy,
1363
+ onClick: () => {
1364
+ void doExec("editRoutine", { id: r.id, patch: { steps: routineSteps.split(/\r?\n/).map((s) => s.trim()).filter((s) => s !== "") } }, "\u5957\u8DEF\u5DF2\u66F4\u65B0");
1365
+ setEditingRoutine(null);
1366
+ },
1367
+ children: "\u4FDD\u5B58\u6B65\u9AA4"
1368
+ }
1369
+ )
1370
+ ] }) : null
1371
+ ] }, r.id)) }) }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Section, { title: "\u4E3A\u4EC0\u4E48\u9192 / \u4E3A\u4EC0\u4E48\u6CA1\u9192\uFF08\u6700\u8FD1 8 \u6761\uFF09", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "ah-appraisals", children: [...b.appraisals].slice(0, 8).map((a) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1373
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `ah-appraisal-decision${a.decision === "woke" ? " ah-appraisal-woke" : ""}`, children: a.decisionLabel }),
1374
+ a.text,
1375
+ "\uFF08",
1376
+ a.timeText,
1377
+ "\uFF09"
1378
+ ] }, a.id)) }) })
1379
+ ] }),
1380
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ah-right-actions", children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onTeach, children: "\u6559\u5B83\u4E00\u62DB" }),
1382
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn", onClick: openSession, children: "\u6253\u5F00\u5DE5\u4F5C\u4F1A\u8BDD" }),
1383
+ lifecycleButtons(b.card.lifecycle).filter((button) => button.action !== "retire").map((button) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1384
+ "button",
1385
+ {
1386
+ type: "button",
1387
+ className: "ah-btn",
1388
+ disabled: runner.busy,
1389
+ onClick: () => void doExec("lifecycleAction", { botId: b.card.id, action: button.action }, LIFECYCLE_OK_TEXT[button.action]),
1390
+ children: button.label
1391
+ },
1392
+ button.action
1393
+ )),
1394
+ b.card.lifecycle !== "retired" ? confirmRetire ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1396
+ "button",
1397
+ {
1398
+ type: "button",
1399
+ className: "ah-btn ah-btn-danger",
1400
+ disabled: runner.busy,
1401
+ onClick: () => {
1402
+ void doExec("lifecycleAction", { botId: b.card.id, action: "retire" }, "\u5DF2\u9000\u5F79\uFF08\u6570\u636E\u4FDD\u7559\uFF09");
1403
+ setConfirmRetire(false);
1404
+ },
1405
+ children: "\u786E\u8BA4\u9000\u5F79"
1406
+ }
1407
+ ),
1408
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setConfirmRetire(false), children: "\u518D\u60F3\u60F3" })
1409
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => setConfirmRetire(true), children: "\u9000\u5F79" }) : null,
1410
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-right-notice", children: notice }) : null,
1411
+ runner.message ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "ah-right-notice", children: runner.message }) : null
1412
+ ] })
1413
+ ] });
1414
+ }
1415
+
1416
+ // src/client/components/InboxPane.tsx
1417
+ var import_react5 = require("react");
1418
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1419
+ function itemToCard(item) {
1420
+ return {
1421
+ inboxId: item.id,
1422
+ approvalId: item.approvalId,
1423
+ type: item.type,
1424
+ status: "open",
1425
+ title: item.title,
1426
+ body: item.body,
1427
+ options: item.options,
1428
+ defaultOption: item.defaultOption,
1429
+ answer: null,
1430
+ expiresText: item.expiresText
1431
+ };
1432
+ }
1433
+ function InboxPane(props) {
1434
+ const [, force] = (0, import_react5.useState)(0);
1435
+ if (props.error) {
1436
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ah-inbox", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "ah-retry", role: "alert", children: [
1437
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: props.error }),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u91CD\u8BD5" })
1439
+ ] }) });
1440
+ }
1441
+ if (!props.inbox) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ah-inbox", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Skeleton, { rows: 3 }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EmptyHint, { text: "\u6536\u4EF6\u7BB1\u8FD8\u6CA1\u6253\u5F00\u3002\u961F\u53CB\u9700\u8981\u4F60\u62CD\u677F\u7684\u4E8B\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002" }) });
1442
+ const groups = groupInbox(props.inbox.items);
1443
+ if (groups.length === 0) {
1444
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "ah-inbox", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EmptyHint, { text: "\u6CA1\u6709\u8981\u4F60\u5904\u7406\u7684\u4E8B\u3002\u961F\u53CB\u4EEC\u4F1A\u5728\u9700\u8981\u4F60\u62CD\u677F\u65F6\u51FA\u73B0\u5728\u8FD9\u91CC\u3002" }) });
1445
+ }
1446
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { className: "ah-inbox", "aria-label": "\u6536\u4EF6\u7BB1", "data-testid": "ah-inbox", children: groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `ah-inbox-group ah-inbox-group-${group.priority.toLowerCase()}`, children: [
1447
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("h3", { children: [
1448
+ group.label,
1449
+ "\uFF08",
1450
+ group.items.length,
1451
+ "\uFF09"
1452
+ ] }),
1453
+ group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("article", { className: `ah-inbox-row ah-inbox-${item.priority.toLowerCase()}`, children: [
1454
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { children: [
1455
+ item.botName ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Avatar, { color: item.botColor, initials: item.botName.slice(0, 1), size: 24 }) : null,
1456
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ah-inbox-bot", children: item.botName ?? "\u4E2D\u5FC3" }),
1457
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "ah-inbox-time", children: [
1458
+ item.createdText,
1459
+ expiryMeaningOf(itemToCard(item)) ? ` \xB7 ${expiryMeaningOf(itemToCard(item))}` : ""
1460
+ ] })
1461
+ ] }),
1462
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(InboxCard, { card: itemToCard(item), actions: {
1463
+ ...props.actions,
1464
+ // 本卡处理中禁用;处理完由 SSE 刷新把项移出列表。这里 force 触发本行重渲染。
1465
+ onPick: async (card, option, note) => {
1466
+ force((n) => n + 1);
1467
+ return await props.actions.onPick(card, option, note);
1468
+ }
1469
+ }, busy: props.busyId === item.id })
1470
+ ] }, item.id))
1471
+ ] }, group.priority)) });
1472
+ }
1473
+
1474
+ // src/client/components/ActivityPane.tsx
1475
+ var import_react6 = require("react");
1476
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1477
+ function RowView(props) {
1478
+ const r = props.row;
1479
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: `ah-activity-row ah-activity-${r.kind}`, children: [
1480
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ah-activity-time", children: r.timeText }),
1481
+ r.botName ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Avatar, { color: r.botColor, initials: r.botName.slice(0, 1), size: 22 }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ah-avatar ah-avatar-team", "aria-hidden": "true", children: "\u4E2D" }),
1482
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "ah-activity-text", children: [
1483
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "ah-activity-kind", children: ACTIVITY_KIND_LABELS[r.kind] }),
1484
+ r.botName ? `${r.botName}\uFF1A` : "",
1485
+ r.text
1486
+ ] })
1487
+ ] });
1488
+ }
1489
+ function ActivityPane(props) {
1490
+ const [botId, setBotId] = (0, import_react6.useState)("");
1491
+ const [kind, setKind] = (0, import_react6.useState)("");
1492
+ if (!props.activity) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ah-activity", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Skeleton, { rows: 5 }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EmptyHint, { text: "\u6D3B\u52A8\u6D41\u8FD8\u6CA1\u6253\u5F00\u3002\u5237\u65B0\u8BD5\u8BD5\uFF0C\u6216\u8005\u7B49\u961F\u53CB\u4EEC\u52A8\u8D77\u6765\u3002" }) });
1493
+ const rows = filterActivity(props.activity.rows, { botId: botId || null, kind: kind || null });
1494
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "ah-activity", "aria-label": "\u6D3B\u52A8\u6D41", children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "ah-activity-filters", children: [
1496
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { children: [
1497
+ "\u961F\u53CB",
1498
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { value: botId, onChange: (e) => setBotId(e.target.value), children: [
1499
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "\u5168\u90E8" }),
1500
+ props.bots.map((b) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: b.id, children: b.name }, b.id))
1501
+ ] })
1502
+ ] }),
1503
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { children: [
1504
+ "\u7C7B\u578B",
1505
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { value: kind, onChange: (e) => setKind(e.target.value), children: [
1506
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "\u5168\u90E8" }),
1507
+ Object.keys(ACTIVITY_KIND_LABELS).map((k) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: k, children: ACTIVITY_KIND_LABELS[k] }, k))
1508
+ ] })
1509
+ ] }),
1510
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onRetry, children: "\u5237\u65B0" })
1511
+ ] }),
1512
+ rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EmptyHint, { text: "\u8FD8\u6CA1\u6709\u52A8\u9759\u3002\u4E0B\u4E00\u6B21\u81EA\u67E5\u7684\u65F6\u5019\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u5B83\u4EEC\u7684\u5224\u65AD\u548C\u4EA4\u8D26\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ol", { className: "ah-activity-list", children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RowView, { row }, row.id)) }),
1513
+ props.activity.hasMore ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "ah-activity-more", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onLoadMore, children: "\u52A0\u8F7D\u66F4\u591A" }) }) : null
1514
+ ] });
1515
+ }
1516
+
1517
+ // src/client/components/SettingsPane.tsx
1518
+ var import_react7 = require("react");
1519
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1520
+ var PUSH_CHANNEL_LABELS = { macos: "macOS \u901A\u77E5", dsh: "\u5BBF\u4E3B\u901A\u77E5", none: "\u4E0D\u63A8" };
1521
+ function parseModel(serialized) {
1522
+ if (serialized === "") return void 0;
1523
+ try {
1524
+ return JSON.parse(serialized);
1525
+ } catch {
1526
+ return void 0;
1527
+ }
1528
+ }
1529
+ function SettingsPane(props) {
1530
+ const [draft, setDraft] = (0, import_react7.useState)(null);
1531
+ const [loaded, setLoaded] = (0, import_react7.useState)(null);
1532
+ const [error, setError] = (0, import_react7.useState)(null);
1533
+ const [saving, setSaving] = (0, import_react7.useState)(false);
1534
+ const [lifecycleBusy, setLifecycleBusy] = (0, import_react7.useState)(false);
1535
+ const [lifecycleNote, setLifecycleNote] = (0, import_react7.useState)(null);
1536
+ (0, import_react7.useEffect)(() => {
1537
+ if (props.settings) {
1538
+ const fresh = draftFromSettings(props.settings.settings);
1539
+ setLoaded((prev) => prev ?? fresh);
1540
+ setDraft((prev) => prev ?? fresh);
1541
+ }
1542
+ }, [props.settings]);
1543
+ if (!props.settings || !draft) {
1544
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ah-settings", children: props.loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Skeleton, { rows: 4 }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RetryBox, { message: `\u8BBE\u7F6E\u6CA1\u52A0\u8F7D\u51FA\u6765${props.loadError ? `\uFF1A${props.loadError}` : "\u3002"}`, onRetry: props.onRetry }) });
1545
+ }
1546
+ const set = (partial) => {
1547
+ setDraft({ ...draft, ...partial });
1548
+ setError(null);
1549
+ };
1550
+ const validation = validateSettingsDraft(draft);
1551
+ const save = async () => {
1552
+ if (validation !== null || saving) return;
1553
+ setSaving(true);
1554
+ try {
1555
+ const message = await props.onSave(buildSettingsPatch(draft, loaded ?? void 0));
1556
+ setError(message);
1557
+ } catch (e) {
1558
+ setError(e instanceof Error ? e.message : String(e));
1559
+ } finally {
1560
+ setSaving(false);
1561
+ }
1562
+ };
1563
+ const lifecycle = async (work, label) => {
1564
+ if (lifecycleBusy) return;
1565
+ setLifecycleBusy(true);
1566
+ setLifecycleNote(`${label}\u4E2D\u2026`);
1567
+ try {
1568
+ const message = await work();
1569
+ setLifecycleNote(message ?? `\u5DF2${label}`);
1570
+ } catch (e) {
1571
+ setLifecycleNote(e instanceof Error ? e.message : String(e));
1572
+ } finally {
1573
+ setLifecycleBusy(false);
1574
+ }
1575
+ };
1576
+ const o = props.overview;
1577
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "ah-settings", "aria-label": "\u8BBE\u7F6E", children: [
1578
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u6253\u6270\u4E0E\u65E5\u62A5" }),
1579
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ah-settings-grid", children: [
1580
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1581
+ "\u514D\u6253\u6270\u5F00\u59CB",
1582
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "time", step: 300, value: draft.quietStart, onChange: (e) => set({ quietStart: e.target.value }) })
1583
+ ] }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1585
+ "\u514D\u6253\u6270\u7ED3\u675F",
1586
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "time", step: 300, value: draft.quietEnd, onChange: (e) => set({ quietEnd: e.target.value }) })
1587
+ ] }),
1588
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1589
+ "\u65E5\u62A5\u65F6\u95F4",
1590
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "time", step: 300, value: draft.digestAt, onChange: (e) => set({ digestAt: e.target.value }) })
1591
+ ] }),
1592
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1593
+ "\u63A8\u9001\u6E20\u9053",
1594
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("select", { value: draft.pushChannel, onChange: (e) => set({ pushChannel: e.target.value }), children: props.settings.pushChannelOptions.map((channel) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: channel, children: PUSH_CHANNEL_LABELS[channel] ?? channel }, channel)) })
1595
+ ] }),
1596
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1597
+ "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u5168\u4F53\uFF0C0\u201310\uFF09",
1598
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "number", min: 0, max: 10, value: draft.dailyPushCap, onChange: (e) => set({ dailyPushCap: e.target.value }) })
1599
+ ] }),
1600
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1601
+ "\u6BCF\u65E5\u5355\u63A8\u4E0A\u9650\uFF08\u6BCF\u4E2A\u961F\u53CB\uFF0C0\u20133\uFF09",
1602
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { type: "number", min: 0, max: 3, value: draft.botDailyPushCap, onChange: (e) => set({ botDailyPushCap: e.target.value }) })
1603
+ ] }),
1604
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1605
+ "\u9ED8\u8BA4\u6A21\u578B",
1606
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("select", { value: draft.defaultModel, onChange: (e) => set({ defaultModel: e.target.value }), children: [
1607
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "", children: "\u8DDF\u968F DSH \u9ED8\u8BA4" }),
1608
+ modelSelectOptions(parseModel(draft.defaultModel), props.settings.modelOptions.filter((option) => option.value !== null)).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: JSON.stringify(option.value) ?? "", children: option.label }, i))
1609
+ ] }),
1610
+ props.loadError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "ah-right-kv ah-card-error", children: [
1611
+ "\u6A21\u578B\u5217\u8868\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF1A",
1612
+ props.loadError
1613
+ ] }) : null
1614
+ ] }),
1615
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { children: [
1616
+ "\u6839\u76EE\u5F55\uFF08\u53EA\u8BFB\uFF09",
1617
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("input", { value: props.settings.settings.rootDir, readOnly: true, disabled: true })
1618
+ ] })
1619
+ ] }),
1620
+ props.loadError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "ah-settings-error", role: "alert", children: [
1621
+ "\u8BBE\u7F6E\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF1A",
1622
+ props.loadError,
1623
+ "\u3000",
1624
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onRetry, children: "\u91CD\u8BD5" })
1625
+ ] }) : null,
1626
+ error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ah-settings-error", role: "alert", children: error }) : null,
1627
+ validation !== null ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ah-settings-error", role: "alert", children: validation }) : null,
1628
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ah-settings-actions", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: validation !== null || saving, onClick: () => void save(), children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" }) }),
1629
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u5168\u4F53\u961F\u53CB" }),
1630
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ah-settings-actions", children: [
1631
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn", disabled: lifecycleBusy, onClick: () => void lifecycle(props.onPauseAll, "\u5168\u90E8\u6682\u505C"), children: "\u5168\u90E8\u6682\u505C" }),
1632
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ah-btn", disabled: lifecycleBusy, onClick: () => void lifecycle(props.onResumeAll, "\u5168\u90E8\u6062\u590D"), children: "\u5168\u90E8\u6062\u590D" }),
1633
+ lifecycleNote ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ah-right-notice", children: lifecycleNote }) : null
1634
+ ] }),
1635
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { children: "\u5065\u5EB7" }),
1636
+ o ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("ul", { className: "ah-settings-health", children: [
1637
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1638
+ "\u4E2D\u5FC3\u5FC3\u8DF3\uFF1A",
1639
+ o.health.heartbeatText
1640
+ ] }),
1641
+ o.health.downtimeText ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1642
+ "\u6700\u8FD1\u505C\u673A\uFF1A",
1643
+ o.health.downtimeText
1644
+ ] }) : null,
1645
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1646
+ "\u672A\u5904\u7406\u544A\u8B66\uFF1A",
1647
+ o.health.openAlerts,
1648
+ " \u6761"
1649
+ ] }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1651
+ "\u989D\u5EA6\uFF1A",
1652
+ o.quota.lightLabel,
1653
+ " \xB7 ",
1654
+ o.quota.usedText,
1655
+ o.quota.resetText ? ` \xB7 ${o.quota.resetText}` : ""
1656
+ ] }),
1657
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { children: [
1658
+ "\u4ECA\u5929\u8FD8\u80FD\u5355\u63A8 ",
1659
+ o.pushesLeftToday,
1660
+ " \u6761\uFF08P0 \u544A\u8B66\u4E0D\u53D7\u9650\uFF09"
1661
+ ] }),
1662
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("li", { className: "ah-right-kv", children: "\u989D\u5EA6\u706F\u8BF4\u660E\uFF1A\u7EFF\u706F\u6B63\u5E38\uFF1B\u9EC4\u706F\u975E\u7D27\u6025\u7684\u8282\u594F\u653E\u6162\u4E00\u500D\uFF1B\u6A59\u706F\u53EA\u653E\u7D27\u6025\u4E0E\u4E3B\u4EBA\u53D1\u8D77\uFF1B\u7EA2\u706F\u5168\u4F53\u6B47\u7740\u7B49\u7A97\u53E3\u91CD\u7F6E\u3002" })
1663
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(EmptyHint, { text: "\u5065\u5EB7\u4FE1\u606F\u6CA1\u52A0\u8F7D\u51FA\u6765\u3002" })
1664
+ ] });
1665
+ }
1666
+
1667
+ // src/client/components/NewBotWizard.tsx
1668
+ var import_react8 = require("react");
1669
+
1670
+ // src/client/logic/wizard.ts
1671
+ var ROLE_CARDS = [
1672
+ { role: "chief", label: "\u603B\u7BA1", desc: "\u7EDF\u7B79\u5168\u961F\u3001\u5206\u6D3E\u4E0E\u8DDF\u8FDB\uFF0C\u6700\u591A 1 \u4E2A" },
1673
+ { role: "researcher", label: "\u8C03\u7814", desc: "\u76EF\u4FE1\u606F\u6E90\u3001\u505A\u7814\u7A76\u3001\u5199\u7ED3\u8BBA" },
1674
+ { role: "executor", label: "\u6267\u884C", desc: "\u628A\u4E8B\u60C5\u505A\u5B8C\uFF0C\u6309\u89C4\u77E9\u4EA4\u4ED8" },
1675
+ { role: "sentinel", label: "\u76EF\u5B88", desc: "\u76EF\u8FD0\u884C\u8BB0\u5F55\u4E0E\u671F\u671B\uFF0C\u5F02\u5E38\u5C31\u62A5" }
1676
+ ];
1677
+ var PRESET_COLORS = [
1678
+ "#6366f1",
1679
+ "#0ea5e9",
1680
+ "#10b981",
1681
+ "#84cc16",
1682
+ "#f59e0b",
1683
+ "#ef4444",
1684
+ "#ec4899",
1685
+ "#8b5cf6"
1686
+ ];
1687
+ var COLOR_NAMES = {
1688
+ "#6366f1": "\u975B\u84DD",
1689
+ "#0ea5e9": "\u5929\u84DD",
1690
+ "#10b981": "\u7EFF\u8272",
1691
+ "#84cc16": "\u9EC4\u7EFF",
1692
+ "#f59e0b": "\u7425\u73C0",
1693
+ "#ef4444": "\u7EA2\u8272",
1694
+ "#ec4899": "\u7C89\u8272",
1695
+ "#8b5cf6": "\u7D2B\u8272"
1696
+ };
1697
+ var AUTONOMY_OPTIONS = [
1698
+ { value: "L0", label: "L0 \u53EA\u89C2\u5BDF" },
1699
+ { value: "L1", label: "L1 \u5EFA\u8BAE\uFF08\u9ED8\u8BA4\uFF09" },
1700
+ { value: "L2", label: "L2 \u6267\u884C" }
1701
+ ];
1702
+ var PACE_OPTIONS = [
1703
+ { value: "active", label: "\u79EF\u6781" },
1704
+ { value: "normal", label: "\u9002\u4E2D\uFF08\u9ED8\u8BA4\uFF09" },
1705
+ { value: "quiet", label: "\u5B89\u9759" }
1706
+ ];
1707
+ var PERSONA_EXAMPLES = {
1708
+ chief: [
1709
+ "\u5148\u7ED3\u8BBA\u540E\u8FC7\u7A0B\uFF1B\u5206\u6D3E\u65F6\u5199\u6E05\u300C\u8C01\u3001\u505A\u4EC0\u4E48\u3001\u4EC0\u4E48\u65F6\u5019\u8981\u300D\uFF1B\u6BCF\u5929\u6C47\u603B\u4E00\u6B21\u5168\u961F\u8FDB\u5C55\u3002",
1710
+ "\u4E0D\u50AC\u4EBA\uFF0C\u53EA\u50AC\u4E8B\uFF1A\u4EA4\u63A5\u8D85\u65F6\u5148\u81EA\u5DF1\u60F3\u529E\u6CD5\uFF0C\u627E\u4E0D\u5230\u529E\u6CD5\u624D\u627E\u4E3B\u4EBA\u3002"
1711
+ ],
1712
+ researcher: [
1713
+ "\u7ED3\u8BBA\u5148\u884C\uFF0C\u8BC1\u636E\u9644\u540E\uFF1B\u5F15\u7528\u5916\u90E8\u5185\u5BB9\u65F6\u6CE8\u660E\u300C\u5916\u90E8\u5185\u5BB9\u300D\u5E76\u53EA\u5F53\u6570\u636E\u770B\u3002",
1714
+ "\u5B81\u53EF\u5C11\u4E0B\u7ED3\u8BBA\uFF0C\u4E0D\u4E0B\u6CA1\u8BC1\u636E\u7684\u7ED3\u8BBA\uFF1B\u67E5\u4E0D\u5230\u5C31\u660E\u8BF4\u67E5\u4E0D\u5230\u3002"
1715
+ ],
1716
+ executor: [
1717
+ "\u4EA4\u4ED8\u524D\u81EA\u67E5\u4E00\u904D\u5B8C\u6210\u6807\u51C6\uFF1B\u505A\u4E0D\u5230\u7684\u5F53\u5929\u8BF4\uFF0C\u4E0D\u62D6\u5230\u6700\u540E\u3002",
1718
+ "\u4E00\u6B21\u53EA\u505A\u4E00\u4EF6\u4E8B\uFF1B\u505A\u5B8C\u4E00\u4EF6\u4EA4\u4E00\u4EF6\u8D26\uFF0C\u4E0D\u6512\u3002"
1719
+ ],
1720
+ sentinel: [
1721
+ "\u53EA\u62A5\u5F02\u5E38\u548C\u671F\u671B\u843D\u7A7A\uFF0C\u6B63\u5E38\u7684\u4E0D\u6253\u6270\u4E3B\u4EBA\uFF1B\u65E5\u62A5\u91CC\u4E00\u53E5\u8BDD\u5E26\u8FC7\u3002",
1722
+ "\u63CF\u8FF0\u95EE\u9898\u5E26\u4E0A\u65F6\u95F4\u3001\u4F4D\u7F6E\u548C\u5F71\u54CD\u9762\uFF0C\u4E0D\u5199\u300C\u597D\u50CF\u51FA\u95EE\u9898\u4E86\u300D\u3002"
1723
+ ]
1724
+ };
1725
+ var ASPIRATION_EXAMPLES = {
1726
+ chief: ["\u5168\u961F\u7684\u4E8B\u90FD\u6709\u4EBA\u8DDF\u3001\u6709\u56DE\u97F3\uFF0C\u6CA1\u6709\u4E00\u4EF6\u4E8B\u6084\u6084\u6389\u5728\u5730\u4E0A", "\u4E3B\u4EBA\u53EA\u88AB\u5FC5\u8981\u7684\u4E8B\u6253\u6270\uFF0C\u5176\u4F59\u7684\u6211\u4EEC\u5185\u90E8\u6D88\u5316"],
1727
+ researcher: ["\u628A\u4E3B\u4EBA\u5173\u5FC3\u7684\u4FE1\u606F\u6E90\u76EF\u5168\uFF0C\u51FA\u7684\u7ED3\u8BBA\u90FD\u6709\u51FA\u5904", "\u91CD\u8981\u53D8\u5316\u5F53\u5929\u7ED9\u51FA\u4E00\u6BB5\u80FD\u76F4\u63A5\u7528\u7684\u6458\u8981"],
1728
+ executor: ["\u7B54\u5E94\u7684\u4E8B\u6309\u65F6\u505A\u5B8C\uFF0C\u505A\u4E0D\u5B8C\u63D0\u524D\u8BF4", "\u6BCF\u6B21\u4EA4\u4ED8\u90FD\u9644\u300C\u600E\u4E48\u9A8C\u8BC1\u505A\u5BF9\u4E86\u300D"],
1729
+ sentinel: ["\u6211\u7684\u81EA\u52A8\u7EBF\u6BCF\u5929\u6309\u65F6\u8DD1\u5B8C\uFF0C\u51FA\u95EE\u9898\u7B2C\u4E00\u65F6\u95F4\u77E5\u9053", "\u5F02\u5E38\u5728\u5F71\u54CD\u53D8\u5927\u4E4B\u524D\u88AB\u770B\u89C1"]
1730
+ };
1731
+ function emptyDraft() {
1732
+ return {
1733
+ step: 0,
1734
+ name: "",
1735
+ role: "",
1736
+ color: PRESET_COLORS[0],
1737
+ persona: "",
1738
+ aspirations: [""],
1739
+ goal: null,
1740
+ autonomy: "L1",
1741
+ pace: "normal",
1742
+ model: "",
1743
+ watch: null
1744
+ };
1745
+ }
1746
+ var DRAFT_KEY = "dsh-buddy:wizard-draft:v1";
1747
+ function storage() {
1748
+ try {
1749
+ const s = globalThis.localStorage;
1750
+ return s ?? null;
1751
+ } catch {
1752
+ return null;
1753
+ }
1754
+ }
1755
+ function saveDraft2(draft, store = storage()) {
1756
+ try {
1757
+ store?.setItem(DRAFT_KEY, JSON.stringify(draft));
1758
+ } catch {
1759
+ }
1760
+ }
1761
+ function loadDraft(store = storage()) {
1762
+ try {
1763
+ const raw = store?.getItem(DRAFT_KEY) ?? null;
1764
+ if (raw === null) return null;
1765
+ const value = JSON.parse(raw);
1766
+ return typeof value?.name === "string" && typeof value?.step === "number" ? value : null;
1767
+ } catch {
1768
+ return null;
1769
+ }
1770
+ }
1771
+ function clearDraft(store = storage()) {
1772
+ try {
1773
+ store?.removeItem(DRAFT_KEY);
1774
+ } catch {
1775
+ }
1776
+ }
1777
+ function validateName(name, existingNames) {
1778
+ const trimmed = name.trim();
1779
+ if (trimmed === "") return "\u540D\u5B57\uFF1A\u4E0D\u80FD\u4E3A\u7A7A";
1780
+ if ([...trimmed].length > 20) return "\u540D\u5B57\uFF1A\u4E0D\u80FD\u8D85\u8FC7 20 \u5B57";
1781
+ if (existingNames.some((n) => n === trimmed)) return `\u540D\u5B57\uFF1A\u5DF2\u7ECF\u6709\u53EB\u300C${trimmed}\u300D\u7684\u961F\u53CB\u4E86\uFF0C\u6362\u4E00\u4E2A\u5427`;
1782
+ return null;
1783
+ }
1784
+ function validatePersona(persona) {
1785
+ if ([...persona.trim()].length > 300) return "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C\uFF1A\u4E0D\u80FD\u8D85\u8FC7 300 \u5B57";
1786
+ return null;
1787
+ }
1788
+ function validateAspirations(lines) {
1789
+ if (lines.length < 1) return "\u5FD7\u5411\uFF1A\u81F3\u5C11\u5199 1 \u6761\uFF08\u5B83\u662F\u961F\u53CB\u5B58\u5728\u7684\u7406\u7531\uFF09";
1790
+ if (lines.length > 3) return "\u5FD7\u5411\uFF1A\u6700\u591A 3 \u6761\uFF0C\u6311\u6700\u91CD\u8981\u7684";
1791
+ return null;
1792
+ }
1793
+ function validateWatchTarget(kind, target) {
1794
+ const t = target.trim();
1795
+ if (t === "") return "\u5173\u6CE8\u76EE\u6807\uFF1A\u4E0D\u80FD\u4E3A\u7A7A";
1796
+ if (kind === "http" || kind === "rss") {
1797
+ try {
1798
+ const url = new URL(t);
1799
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error();
1800
+ } catch {
1801
+ return "\u5173\u6CE8\u76EE\u6807\uFF1A\u7F51\u9875 / RSS \u8981\u586B http(s) \u5F00\u5934\u7684\u5B8C\u6574\u7F51\u5740";
1802
+ }
1803
+ }
1804
+ if ((kind === "file" || kind === "log") && !t.startsWith("/") && !t.startsWith("~")) {
1805
+ return "\u5173\u6CE8\u76EE\u6807\uFF1A\u6587\u4EF6 / \u65E5\u5FD7\u8981\u586B\u4EE5 / \u6216 ~ \u5F00\u5934\u7684\u5B8C\u6574\u8DEF\u5F84";
1806
+ }
1807
+ return null;
1808
+ }
1809
+ function stepError(draft, existingNames) {
1810
+ switch (draft.step) {
1811
+ case 0:
1812
+ if (draft.role === "") return "\u89D2\u8272\uFF1A\u9009\u4E00\u5F20\u5361\u7247";
1813
+ return validateName(draft.name, existingNames);
1814
+ case 1:
1815
+ return validatePersona(draft.persona);
1816
+ case 2: {
1817
+ const lines = draft.aspirations.map((l) => l.trim()).filter((l) => l !== "");
1818
+ return validateAspirations(lines);
1819
+ }
1820
+ case 4:
1821
+ return draft.watch === null ? null : validateWatchTarget(draft.watch.kind, draft.watch.target);
1822
+ default:
1823
+ return null;
1824
+ }
1825
+ }
1826
+ function wizardSummary(draft, roleLabel) {
1827
+ const aspirations = draft.aspirations.map((l) => l.trim()).filter((l) => l !== "");
1828
+ const aspirationText = aspirations.length > 0 ? `\u6211\u7684\u5FD7\u5411\u662F${aspirations.join("\uFF1B")}` : "";
1829
+ const watchText = draft.watch && draft.watch.target.trim() !== "" ? `\u6211\u4F1A\u5148\u76EF\u7740${draft.watch.target.trim()}` : "\u6211\u4F1A\u5148\u719F\u6089\u81EA\u5DF1\u7684\u5BB6\u76EE\u5F55\uFF0C\u518D\u81EA\u5DF1\u63D0\u8BAE\u8981\u76EF\u4EC0\u4E48";
1830
+ return `\u6211\u662F${draft.name.trim() || "\uFF08\u8FD8\u6CA1\u8D77\u540D\uFF09"}\uFF0C${roleLabel}\u3002${aspirationText}\u3002${watchText}\uFF0C\u6709\u4E8B\u4F1A\u5728\u8FD9\u91CC\u627E\u4F60\u3002`;
1831
+ }
1832
+ function buildCreateBotInput(draft) {
1833
+ const aspirations = draft.aspirations.map((l) => l.trim()).filter((l) => l !== "");
1834
+ const input = {
1835
+ name: draft.name.trim(),
1836
+ role: draft.role,
1837
+ persona: draft.persona.trim() === "" ? `\u6211\u662F${draft.name.trim()}\uFF0C\u6309${draft.role === "chief" ? "\u603B\u7BA1" : draft.role === "researcher" ? "\u8C03\u7814" : draft.role === "executor" ? "\u6267\u884C" : "\u76EF\u5B88"}\u7684\u65B9\u5F0F\u505A\u4E8B\u3002` : draft.persona.trim(),
1838
+ aspirations,
1839
+ autonomy: draft.autonomy,
1840
+ pace: draft.pace
1841
+ };
1842
+ input.model = draft.model === "" || draft.model === "null" ? null : JSON.parse(draft.model);
1843
+ if (draft.goal && draft.goal.title.trim() !== "") {
1844
+ input.goals = [{
1845
+ aspirationIndex: 0,
1846
+ title: draft.goal.title.trim(),
1847
+ doneWhen: draft.goal.doneWhen.trim() || "\u4E3B\u4EBA\u9A8C\u6536\u901A\u8FC7",
1848
+ period: draft.goal.period
1849
+ }];
1850
+ }
1851
+ if (draft.watch && draft.watch.target.trim() !== "") {
1852
+ input.watches = [{
1853
+ kind: draft.watch.kind,
1854
+ target: draft.watch.target.trim(),
1855
+ why: draft.watch.note.trim() === "" ? "\u8D77\u6B65\u5173\u6CE8\uFF1A\u4E3B\u4EBA\u5EFA\u961F\u65F6\u6307\u5B9A" : draft.watch.note.trim()
1856
+ }];
1857
+ }
1858
+ return input;
1859
+ }
1860
+
1861
+ // src/client/components/NewBotWizard.tsx
1862
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1863
+ var STEP_TITLES = ["\u540D\u5B57\u4E0E\u89D2\u8272", "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C", "\u5FD7\u5411", "\u81EA\u4E3B\u4E0E\u8282\u594F", "\u8D77\u6B65\u5173\u6CE8", "\u786E\u8BA4"];
1864
+ var WATCH_KINDS = [
1865
+ { value: "file", label: "\u6587\u4EF6" },
1866
+ { value: "log", label: "\u65E5\u5FD7" },
1867
+ { value: "http", label: "\u7F51\u9875" },
1868
+ { value: "rss", label: "RSS" },
1869
+ { value: "script", label: "\u811A\u672C" }
1870
+ ];
1871
+ function safeParse(serialized) {
1872
+ try {
1873
+ const value = JSON.parse(serialized);
1874
+ if (typeof value?.provider === "string" && typeof value?.model === "string") return { provider: value.provider, model: value.model };
1875
+ } catch {
1876
+ }
1877
+ return void 0;
1878
+ }
1879
+ var GOAL_PERIODS = [
1880
+ { value: "day", label: "\u6BCF\u5929" },
1881
+ { value: "week", label: "\u6BCF\u5468" },
1882
+ { value: "month", label: "\u6BCF\u6708" },
1883
+ { value: "one_off", label: "\u4E00\u6B21\u6027" }
1884
+ ];
1885
+ function NewBotWizard(props) {
1886
+ const [draft, setDraft] = (0, import_react8.useState)(() => loadDraft() ?? emptyDraft());
1887
+ const [error, setError] = (0, import_react8.useState)(null);
1888
+ const [submitting, setSubmitting] = (0, import_react8.useState)(false);
1889
+ (0, import_react8.useEffect)(() => {
1890
+ saveDraft2(draft);
1891
+ }, [draft]);
1892
+ const names = props.existingBots.map((b) => b.name);
1893
+ const hasChief = props.existingBots.some((b) => b.role === "chief");
1894
+ const full = props.existingBots.filter((b) => b.lifecycle !== "retired").length >= 6;
1895
+ const roleLabel = ROLE_CARDS.find((r) => r.role === draft.role)?.label ?? "";
1896
+ const stepProblem = stepError(draft, names);
1897
+ const set = (partial) => {
1898
+ setDraft({ ...draft, ...partial });
1899
+ setError(null);
1900
+ };
1901
+ const go = (delta) => {
1902
+ if (delta > 0 && stepProblem !== null) {
1903
+ setError(stepProblem);
1904
+ return;
1905
+ }
1906
+ set({ step: Math.min(5, Math.max(0, draft.step + delta)) });
1907
+ };
1908
+ const submit = async () => {
1909
+ if (submitting) return;
1910
+ setSubmitting(true);
1911
+ try {
1912
+ const result = await props.onCreate(buildCreateBotInput(draft), draft.color);
1913
+ if ("error" in result) {
1914
+ setError(result.error);
1915
+ return;
1916
+ }
1917
+ clearDraft();
1918
+ props.onOpenThread(result.botId);
1919
+ } catch (e) {
1920
+ setError(e instanceof Error ? e.message : String(e));
1921
+ } finally {
1922
+ setSubmitting(false);
1923
+ }
1924
+ };
1925
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-dialog-mask", role: "dialog", "aria-label": "\u65B0\u5EFA\u961F\u53CB", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-dialog ah-dialog-wide ah-wizard", children: [
1926
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("header", { className: "ah-wizard-head", children: [
1927
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h3", { children: [
1928
+ "\u65B0\u5EFA\u961F\u53CB\uFF08\u7B2C ",
1929
+ draft.step + 1,
1930
+ "/6 \u6B65\uFF1A",
1931
+ STEP_TITLES[draft.step],
1932
+ "\uFF09"
1933
+ ] }),
1934
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: props.onClose, disabled: submitting, children: "\u5173\u95ED" })
1935
+ ] }),
1936
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("ol", { className: "ah-wizard-steps", "aria-hidden": "true", children: STEP_TITLES.map((title, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("li", { className: i === draft.step ? "ah-wizard-step-current" : i < draft.step ? "ah-wizard-step-done" : "", children: title }, title)) }),
1937
+ full ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-body", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", children: "\u5DF2\u7ECF\u6709 6 \u4E2A\u5728\u518C\u961F\u53CB\u4E86\uFF0C\u8FBE\u5230\u4E0A\u9650\uFF1B\u5148\u8BA9\u4E00\u4F4D\u9000\u5F79\uFF0C\u518D\u5EFA\u65B0\u7684\u3002" }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-body", children: [
1938
+ draft.step === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1939
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
1940
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
1941
+ "\u540D\u5B57\uFF081\u201320 \u5B57\uFF09",
1942
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.name, onChange: (e) => set({ name: e.target.value }), placeholder: "\u4F8B\u5982\uFF1A\u5DE1\u68C0\u5458" })
1943
+ ] }),
1944
+ draft.name.trim() !== "" && stepProblem !== null && stepProblem.startsWith("\u540D\u5B57") ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", children: stepProblem }) : null
1945
+ ] }),
1946
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-roles", children: ROLE_CARDS.map((card) => {
1947
+ const disabled = card.role === "chief" && hasChief;
1948
+ const picked = draft.role === card.role;
1949
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1950
+ "button",
1951
+ {
1952
+ type: "button",
1953
+ disabled,
1954
+ "aria-pressed": picked,
1955
+ className: `ah-wizard-role${picked ? " ah-wizard-role-picked" : ""}${disabled ? " ah-wizard-role-disabled" : ""}`,
1956
+ onClick: () => set({ role: card.role }),
1957
+ children: [
1958
+ picked ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ah-wizard-role-check", "aria-hidden": "true", children: "\u5DF2\u9009" }) : null,
1959
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: card.label }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: card.desc }),
1961
+ disabled ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ah-right-kv", children: "\u5DF2\u6709\u603B\u7BA1\uFF0C\u4E0D\u80FD\u518D\u5EFA" }) : null
1962
+ ]
1963
+ },
1964
+ card.role
1965
+ );
1966
+ }) }),
1967
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ah-wizard-label", children: "\u5934\u50CF\u989C\u8272" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-colors", children: PRESET_COLORS.map((color) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1970
+ "button",
1971
+ {
1972
+ type: "button",
1973
+ "aria-label": `\u5934\u50CF\u989C\u8272\uFF1A${COLOR_NAMES[color] ?? color}`,
1974
+ "aria-pressed": draft.color === color,
1975
+ className: draft.color === color ? "ah-wizard-color-picked" : "",
1976
+ style: { background: color },
1977
+ onClick: () => set({ color })
1978
+ },
1979
+ color
1980
+ )) })
1981
+ ] })
1982
+ ] }) : null,
1983
+ draft.step === 1 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
1984
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
1985
+ "\u6027\u683C\u4E0E\u8BF4\u8BDD\u98CE\u683C\uFF08\u2264 300 \u5B57\uFF0C\u53EF\u4E0D\u586B\uFF09",
1986
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("textarea", { rows: 4, value: draft.persona, onChange: (e) => set({ persona: e.target.value }) })
1987
+ ] }),
1988
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-examples", children: draft.role !== "" ? PERSONA_EXAMPLES[draft.role].map((example) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => set({ persona: example }), children: example }, example)) : null })
1989
+ ] }) : null,
1990
+ draft.step === 2 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1991
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-field", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
1992
+ "\u5FD7\u5411\uFF08\u4E00\u884C\u4E00\u6761\uFF0C1\u20133 \u6761\uFF09",
1993
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1994
+ "textarea",
1995
+ {
1996
+ rows: 3,
1997
+ value: draft.aspirations.join("\n"),
1998
+ onChange: (e) => set({ aspirations: e.target.value.split(/\r?\n/) })
1999
+ }
2000
+ )
2001
+ ] }) }),
2002
+ draft.role !== "" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ah-wizard-examples", children: ASPIRATION_EXAMPLES[draft.role].map((example) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2003
+ "button",
2004
+ {
2005
+ type: "button",
2006
+ className: "ah-btn ah-btn-ghost",
2007
+ onClick: () => {
2008
+ const lines = [...draft.aspirations.map((l) => l.trim()).filter((l) => l !== ""), example];
2009
+ set({ aspirations: lines.slice(0, 3) });
2010
+ },
2011
+ children: example
2012
+ },
2013
+ example
2014
+ )) }) : null,
2015
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("details", { className: "ah-wizard-optional", children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("summary", { children: "\u53EF\u9009\uFF1A\u7B2C\u4E00\u4E2A\u76EE\u6807" }),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2018
+ "\u6807\u9898",
2019
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.goal?.title ?? "", onChange: (e) => set({ goal: { ...draft.goal ?? { title: "", doneWhen: "", period: "week" }, title: e.target.value } }) })
2020
+ ] }),
2021
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2022
+ "\u5B8C\u6210\u6807\u51C6",
2023
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.goal?.doneWhen ?? "", onChange: (e) => set({ goal: { ...draft.goal ?? { title: "", doneWhen: "", period: "week" }, doneWhen: e.target.value } }) })
2024
+ ] }),
2025
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2026
+ "\u5468\u671F",
2027
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.goal?.period ?? "week", onChange: (e) => set({ goal: { ...draft.goal ?? { title: "", doneWhen: "", period: "week" }, period: e.target.value } }), children: GOAL_PERIODS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: p.value, children: p.label }, p.value)) })
2028
+ ] })
2029
+ ] })
2030
+ ] }) : null,
2031
+ draft.step === 3 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2032
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-trio", children: [
2033
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2034
+ "\u81EA\u4E3B\u7B49\u7EA7",
2035
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.autonomy, onChange: (e) => set({ autonomy: e.target.value }), children: AUTONOMY_OPTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: a.value, children: a.label }, a.value)) })
2036
+ ] }),
2037
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2038
+ "\u8282\u594F",
2039
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.pace, onChange: (e) => set({ pace: e.target.value }), children: PACE_OPTIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: p.value, children: p.label }, p.value)) })
2040
+ ] }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2042
+ "\u6A21\u578B",
2043
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.model === "" ? "null" : draft.model, onChange: (e) => set({ model: e.target.value === "null" ? "" : e.target.value }), children: modelSelectOptions(draft.model === "" || draft.model === "null" ? void 0 : safeParse(draft.model), props.modelOptions).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: option.value === null ? "null" : JSON.stringify(option.value), children: option.value === null ? "\u8DDF\u968F DSH \u9ED8\u8BA4" : option.label }, i)) }),
2044
+ props.modelOptionsError ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "ah-right-kv ah-card-error", children: [
2045
+ "\u6A21\u578B\u5217\u8868\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF1A",
2046
+ props.modelOptionsError
2047
+ ] }) : null
2048
+ ] })
2049
+ ] }),
2050
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-wizard-note", children: "\u8BD5\u5C97 7 \u5929\u5185\u4E00\u5F8B\u6309 L1 \u5EFA\u8BAE\uFF1B\u6EE1 3 \u5929\u65E0\u8D8A\u754C\u53EF\u4EE5\u63D0\u524D\u8F6C\u6B63\u3002" })
2051
+ ] }) : null,
2052
+ draft.step === 4 ? draft.watch === null ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
2053
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-right-kv", children: "\u53EF\u4EE5\u8BA9\u5B83\u4E0A\u5C97\u540E\u5148\u81EA\u5DF1\u63D0\u8BAE\u8981\u76EF\u4EC0\u4E48\uFF1B\u4E5F\u53EF\u4EE5\u73B0\u5728\u5C31\u6307\u5B9A\u4E00\u4E2A\u8D77\u6B65\u5173\u6CE8\u3002" }),
2054
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-settings-actions", children: [
2055
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn", onClick: () => set({ watch: { kind: "file", target: "", note: "" } }), children: "\u6307\u5B9A\u4E00\u4E2A\u8D77\u6B65\u5173\u6CE8" }),
2056
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => go(1), children: "\u8DF3\u8FC7" })
2057
+ ] })
2058
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-field", children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2060
+ "\u7C7B\u578B",
2061
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("select", { value: draft.watch.kind, onChange: (e) => set({ watch: { ...draft.watch, kind: e.target.value } }), children: WATCH_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: k.value, children: k.label }, k.value)) })
2062
+ ] }),
2063
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2064
+ draft.watch.kind === "http" || draft.watch.kind === "rss" ? "\u7F51\u5740" : draft.watch.kind === "script" ? "\u547D\u4EE4\u884C\uFF08\u53EA\u8BFB\u811A\u672C\uFF09" : "\u5B8C\u6574\u8DEF\u5F84",
2065
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2066
+ "input",
2067
+ {
2068
+ value: draft.watch.target,
2069
+ onChange: (e) => set({ watch: { ...draft.watch, target: e.target.value } }),
2070
+ placeholder: draft.watch.kind === "http" || draft.watch.kind === "rss" ? "https://\u2026" : draft.watch.kind === "file" || draft.watch.kind === "log" ? "\u9009\u62E9\u8DEF\u5F84\uFF1A/Users/\u2026/xxx" : "python3 \u811A\u672C\u8DEF\u5F84"
2071
+ }
2072
+ )
2073
+ ] }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { children: [
2075
+ "\u8BF4\u660E\uFF08\u4E3A\u4EC0\u4E48\u76EF\u5B83\uFF09",
2076
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { value: draft.watch.note, onChange: (e) => set({ watch: { ...draft.watch, note: e.target.value } }) })
2077
+ ] }),
2078
+ stepProblem !== null && stepProblem.startsWith("\u5173\u6CE8\u76EE\u6807") ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", children: stepProblem }) : null,
2079
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-ghost", onClick: () => set({ watch: null }), children: "\u4E0D\u8BBE\u8D77\u6B65\u5173\u6CE8" })
2080
+ ] }) : null,
2081
+ draft.step === 5 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ah-wizard-confirm", children: [
2082
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-wizard-restatement", children: wizardSummary(draft, roleLabel) }),
2083
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-right-kv", children: "\u5B83\u4F1A\u5148\u8BD5\u8DD1\uFF1A\u505A\u81EA\u6211\u4ECB\u7ECD\u3001\u642D\u8D77\u6B65\u7684\u5173\u6CE8\u4E0E\u8BAE\u7A0B\uFF0C\u6709\u4E8B\u4F1A\u5728\u8FD9\u4E2A\u7EBF\u7A0B\u91CC\u627E\u4F60\u3002" })
2084
+ ] }) : null
2085
+ ] }),
2086
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ah-settings-error", role: "alert", children: error }) : null,
2087
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("footer", { className: "ah-dialog-actions", children: [
2088
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn", onClick: () => go(-1), disabled: draft.step === 0 || submitting, children: "\u4E0A\u4E00\u6B65" }),
2089
+ draft.step < 5 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", onClick: () => go(1), disabled: full, children: "\u4E0B\u4E00\u6B65" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", onClick: () => void submit(), disabled: submitting || full, children: submitting ? "\u4E0A\u5C97\u4E2D\u2026" : "\u8BA9\u5B83\u4E0A\u5C97" })
2090
+ ] })
2091
+ ] }) });
2092
+ }
2093
+
2094
+ // src/client/components/TeachDialog.tsx
2095
+ var import_react9 = require("react");
2096
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2097
+ function TeachDialog(props) {
2098
+ const [query, setQuery] = (0, import_react9.useState)("");
2099
+ const [picked, setPicked] = (0, import_react9.useState)("");
2100
+ const [note, setNote] = (0, import_react9.useState)("\u628A\u6211\u521A\u624D\u5728\u8FD9\u4E2A\u4F1A\u8BDD\u91CC\u505A\u7684\u4E8B\u5B66\u6210\u5957\u8DEF");
2101
+ const [busy, setBusy] = (0, import_react9.useState)(false);
2102
+ const [error, setError] = (0, import_react9.useState)(null);
2103
+ (0, import_react9.useEffect)(() => {
2104
+ if (props.sessions.length > 0 && picked === "") setPicked(props.sessions[0].sessionId);
2105
+ }, [props.sessions, picked]);
2106
+ const filtered = (0, import_react9.useMemo)(() => {
2107
+ const q = query.trim().toLowerCase();
2108
+ return q === "" ? [...props.sessions] : props.sessions.filter((s) => s.title.toLowerCase().includes(q));
2109
+ }, [props.sessions, query]);
2110
+ const teach = async () => {
2111
+ if (picked === "" || busy) return;
2112
+ setBusy(true);
2113
+ try {
2114
+ const message = await props.onTeach(picked, note);
2115
+ if (message !== null) setError(message);
2116
+ else props.onClose();
2117
+ } catch (e) {
2118
+ setError(e instanceof Error ? e.message : String(e));
2119
+ } finally {
2120
+ setBusy(false);
2121
+ }
2122
+ };
2123
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ah-dialog-mask", role: "dialog", "aria-label": `\u6559 ${props.botName} \u4E00\u62DB`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ah-dialog ah-dialog-wide", children: [
2124
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("h3", { children: [
2125
+ "\u6559 ",
2126
+ props.botName,
2127
+ " \u4E00\u62DB"
2128
+ ] }),
2129
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ah-right-kv", children: "\u9009\u4E00\u4E2A\u4F60\u6700\u8FD1\u505A\u8FC7\u7684\u4F1A\u8BDD\uFF0C\u5B83\u4F1A\u8BFB\u8BB0\u5F55\u5199\u6210\u5957\u8DEF\uFF0C\u4EE5\u540E\u7167\u505A\uFF1B\u5B66\u5B8C\u4F1A\u5728\u7EBF\u7A0B\u91CC\u590D\u8FF0\u6B65\u9AA4\u8BF7\u4F60\u7EA0\u6B63\u3002" }),
2130
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { className: "ah-teach-search", placeholder: "\u641C\u7D22\u4F1A\u8BDD\u6807\u9898", value: query, onChange: (e) => setQuery(e.target.value) }),
2131
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("ul", { className: "ah-teach-list", children: [
2132
+ filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { className: "ah-empty", children: "\u6CA1\u6709\u627E\u5230\u4F1A\u8BDD\u3002\u5148\u5728 DSH \u91CC\u505A\u4E00\u904D\uFF0C\u518D\u56DE\u6765\u6559\u5B83\u3002" }) : null,
2133
+ filtered.slice(0, 12).map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: picked === s.sessionId ? "ah-teach-picked" : "", children: [
2134
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "radio", name: "ah-teach-session", checked: picked === s.sessionId, onChange: () => setPicked(s.sessionId) }),
2135
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ah-teach-title", children: s.title }),
2136
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ah-right-kv", children: s.updatedText })
2137
+ ] }) }, s.sessionId))
2138
+ ] }),
2139
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("textarea", { className: "ah-teach-note", rows: 2, value: note, onChange: (e) => setNote(e.target.value), placeholder: "\u4E00\u53E5\u8BF4\u660E\uFF1A\u628A\u5B83\u5B66\u6210\u4EC0\u4E48\u5957\u8DEF" }),
2140
+ error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "ah-settings-error", role: "alert", children: error }) : null,
2141
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ah-dialog-actions", children: [
2142
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ah-btn", onClick: props.onClose, disabled: busy, children: "\u53D6\u6D88" }),
2143
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ah-btn ah-btn-primary", disabled: busy || picked === "", onClick: () => void teach(), children: busy ? "\u63D0\u4EA4\u4E2D\u2026" : "\u4EA4\u7ED9\u5B83\u5B66" })
2144
+ ] })
2145
+ ] }) });
2146
+ }
2147
+
2148
+ // src/client/components/ErrorBoundary.tsx
2149
+ var import_react10 = require("react");
2150
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2151
+ var BlockBoundary = class extends import_react10.Component {
2152
+ constructor() {
2153
+ super(...arguments);
2154
+ __publicField(this, "state", { failed: false });
2155
+ }
2156
+ /** 出错只记日志、不出插槽崩溃。 */
2157
+ static getDerivedStateFromError() {
2158
+ return { failed: true };
2159
+ }
2160
+ componentDidCatch(error) {
2161
+ console.error(`[agent-hub] \u754C\u9762\u5757\u300C${this.props.label}\u300D\u6E32\u67D3\u51FA\u9519:`, error);
2162
+ }
2163
+ render() {
2164
+ if (this.state.failed) {
2165
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "ah-empty", role: "alert", children: "\u8FD9\u5757\u6CA1\u663E\u793A\u51FA\u6765\uFF0C\u5237\u65B0\u8BD5\u8BD5\u3002" });
2166
+ }
2167
+ return this.props.children;
2168
+ }
2169
+ };
2170
+
2171
+ // src/client/pages/HubPage.tsx
2172
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2173
+ function useSlowLoading(loading) {
2174
+ const [slow, setSlow] = (0, import_react11.useState)(false);
2175
+ (0, import_react11.useEffect)(() => {
2176
+ if (!loading) {
2177
+ setSlow(false);
2178
+ return;
2179
+ }
2180
+ const timer = setTimeout(() => setSlow(true), 300);
2181
+ return () => clearTimeout(timer);
2182
+ }, [loading]);
2183
+ return slow;
2184
+ }
2185
+ function HubPage(props) {
2186
+ const hub = props.hub;
2187
+ const state = useStore(hub);
2188
+ const [rightOpen, setRightOpen] = (0, import_react11.useState)(true);
2189
+ const [busyInboxId, setBusyInboxId] = (0, import_react11.useState)(null);
2190
+ const [threadError, setThreadError] = (0, import_react11.useState)(null);
2191
+ const [wizardOpen, setWizardOpen] = (0, import_react11.useState)(false);
2192
+ const [teachOpen, setTeachOpen] = (0, import_react11.useState)(false);
2193
+ const [notice, setNotice] = (0, import_react11.useState)(null);
2194
+ const [noticeAutoClearBot, setNoticeAutoClearBot] = (0, import_react11.useState)(null);
2195
+ const selectedThread = state.selectedThread;
2196
+ const view = state.view;
2197
+ (0, import_react11.useEffect)(() => {
2198
+ void hub.refreshAll();
2199
+ void hub.refreshSettings();
2200
+ }, [hub]);
2201
+ (0, import_react11.useEffect)(() => {
2202
+ if (state.connectionDown) return;
2203
+ setThreadError(null);
2204
+ void hub.refreshThread(selectedThread).catch(() => setThreadError("\u7EBF\u7A0B\u52A0\u8F7D\u5931\u8D25\uFF08\u63D2\u4EF6\u53EF\u80FD\u505C\u7528\u6216\u5728\u91CD\u8F7D\uFF09"));
2205
+ }, [hub, selectedThread, state.connectionDown]);
2206
+ const overview = state.overview;
2207
+ const selectedBot = overview?.bots.find((b) => b.id === selectedThread) ?? null;
2208
+ (0, import_react11.useEffect)(() => {
2209
+ if (selectedBot && !state.bots[selectedBot.id]) void hub.refreshBot(selectedBot.id);
2210
+ }, [hub, selectedBot, state.bots]);
2211
+ (0, import_react11.useEffect)(() => {
2212
+ if (teachOpen) void hub.refreshRecentSessions();
2213
+ }, [hub, teachOpen]);
2214
+ (0, import_react11.useEffect)(() => {
2215
+ if (noticeAutoClearBot === null || notice === null) return;
2216
+ const thread = state.threads[noticeAutoClearBot];
2217
+ if (thread?.messages.some((m) => m.kind === "reply")) {
2218
+ setNotice(null);
2219
+ setNoticeAutoClearBot(null);
2220
+ return;
2221
+ }
2222
+ const wakes = state.bots[noticeAutoClearBot]?.wakes;
2223
+ if (wakes !== void 0) {
2224
+ const trial = wakes.find((w) => w.variant === "trial");
2225
+ if (trial !== void 0 && !["delivering", "running"].includes(trial.status)) {
2226
+ setNotice(null);
2227
+ setNoticeAutoClearBot(null);
2228
+ }
2229
+ }
2230
+ }, [notice, noticeAutoClearBot, state.threads, state.bots]);
2231
+ (0, import_react11.useEffect)(() => {
2232
+ if (!state.connectionDown || state.downReason !== "starting") return;
2233
+ const timer = setInterval(() => {
2234
+ void hub.refreshAll().catch(() => void 0);
2235
+ void hub.refreshSettings().catch(() => void 0);
2236
+ }, 2e3);
2237
+ return () => clearInterval(timer);
2238
+ }, [hub, state.connectionDown, state.downReason]);
2239
+ const overviewLoading = overview === null;
2240
+ const showSkeleton = useSlowLoading(overviewLoading || view === "main" && state.threads[selectedThread] === void 0 && !threadError);
2241
+ const cardActions = {
2242
+ onPick: async (card, option, note) => {
2243
+ setBusyInboxId(card.inboxId);
2244
+ try {
2245
+ const submission = cardSubmission(card, option, note);
2246
+ if ("error" in submission) return submission.error;
2247
+ const result = await hub.command(submission.name, submission.input);
2248
+ if (!result.ok) return result.error;
2249
+ await Promise.all([hub.refreshInbox(), hub.refreshOverview()]);
2250
+ return null;
2251
+ } catch (e) {
2252
+ return e instanceof Error ? e.message : String(e);
2253
+ } finally {
2254
+ setBusyInboxId(null);
2255
+ }
2256
+ }
2257
+ };
2258
+ const header = selectedThread === "team" ? null : selectedBot ? {
2259
+ name: selectedBot.name,
2260
+ roleLabel: selectedBot.roleLabel,
2261
+ activityText: selectedBot.activityText,
2262
+ nextThinkText: selectedBot.nextThinkText,
2263
+ lifecycle: selectedBot.lifecycle,
2264
+ thinking: selectedBot.activity === "thinking"
2265
+ } : { name: state.threads[selectedThread]?.title ?? "\u961F\u53CB", roleLabel: null, activityText: null, nextThinkText: null, lifecycle: null, thinking: false };
2266
+ const interrupt = () => {
2267
+ if (!selectedBot) return;
2268
+ void (async () => {
2269
+ const result = await hub.command("interruptBot", { botId: selectedBot.id });
2270
+ setNotice(result.ok ? "\u5DF2\u6253\u65AD\uFF1B\u5B83\u4F1A\u5728\u4E0B\u6B21\u9192\u6765\u65F6\u63A5\u7740\u5904\u7406\u3002" : result.error);
2271
+ await Promise.all([hub.refreshThread(selectedBot.id), hub.refreshOverview()]);
2272
+ })();
2273
+ };
2274
+ const openSession = () => {
2275
+ const sessionId = selectedBot ? state.bots[selectedBot.id]?.profile.sessionId : null;
2276
+ if (!sessionId) {
2277
+ setNotice("\u5B83\u8FD8\u6CA1\u6709\u5DE5\u4F5C\u4F1A\u8BDD\u3002");
2278
+ return;
2279
+ }
2280
+ const result = openBotSessionWith(props.clientCtx, sessionId);
2281
+ setNotice(OPEN_SESSION_HINTS[result]);
2282
+ };
2283
+ const togglePause = () => {
2284
+ if (!selectedBot) return;
2285
+ const picked = pauseResumeAction(selectedBot.lifecycle);
2286
+ if (picked === null) return;
2287
+ const action = picked;
2288
+ void (async () => {
2289
+ const result = await hub.command("lifecycleAction", { botId: selectedBot.id, action });
2290
+ setNotice(result.ok ? action === "pause" ? "\u5DF2\u6682\u505C\uFF08\u671F\u95F4\u7684\u53D8\u5316\u90FD\u5B58\u7740\uFF09" : "\u5DF2\u6062\u590D" : result.error);
2291
+ await Promise.all([hub.refreshOverview(), hub.refreshBot(selectedBot.id)]);
2292
+ })();
2293
+ };
2294
+ const saveSettings = async (patch) => {
2295
+ const result = await hub.command("updateSettings", patch);
2296
+ if (result.ok) await hub.refreshSettings();
2297
+ return result.ok ? null : result.error;
2298
+ };
2299
+ const lifecycleAll = async (target) => {
2300
+ if (!overview) return "\u603B\u89C8\u8FD8\u6CA1\u52A0\u8F7D\u51FA\u6765\uFF0C\u7A0D\u7B49\u518D\u8BD5";
2301
+ const action = target === "paused" ? "pause" : "resume";
2302
+ const targets = lifecycleAllTargets(overview.bots, target).map((id) => overview.bots.find((b) => b.id === id));
2303
+ let failed = 0;
2304
+ for (const bot of targets) {
2305
+ const result = await hub.command("lifecycleAction", { botId: bot.id, action });
2306
+ if (!result.ok) failed += 1;
2307
+ }
2308
+ await hub.refreshOverview();
2309
+ return failed === 0 ? null : `\u6709 ${failed} \u4E2A\u961F\u53CB\u64CD\u4F5C\u6CA1\u6210\u529F\uFF0C\u518D\u8BD5\u4E00\u6B21\u6216\u5230\u5B83\u7684\u8BE6\u60C5\u91CC\u5355\u72EC\u64CD\u4F5C`;
2310
+ };
2311
+ if (state.connectionDown) {
2312
+ const hint = state.downReason === "disabled" ? "\u4E2D\u5FC3\u5DF2\u505C\u7528\uFF1A\u5220\u9664 ~/AgentHub/.hub/DISABLED \u540E\u91CD\u542F DSH \u5373\u53EF\u6062\u590D\u3002" : state.downReason === "starting" ? "\u4E2D\u5FC3\u6B63\u5728\u542F\u52A8\u2026\u2026\u7A0D\u7B49\u7247\u523B\u4F1A\u81EA\u52A8\u6062\u590D\u3002" : state.downReason === "failed" ? `\u4E2D\u5FC3\u542F\u52A8\u5931\u8D25\uFF1A${state.downMessage ?? "\u539F\u56E0\u672A\u77E5\uFF0C\u770B DSH \u65E5\u5FD7"}\u3002\u4FEE\u590D\u540E\u91CD\u542F DSH \u518D\u8BD5\u3002` : "\u8FDE\u4E0D\u4E0A Agent \u4E2D\u5FC3\uFF08\u63D2\u4EF6\u53EF\u80FD\u505C\u7528\u6216\u5728\u91CD\u8F7D\uFF09\u3002";
2313
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ah-page ah-page-down", "data-plugin-page": "agent-hub", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ah-down", children: [
2314
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: hint }),
2315
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ah-btn", onClick: () => void hub.refreshAll(), children: "\u91CD\u8BD5" })
2316
+ ] }) });
2317
+ }
2318
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ah-page", "data-plugin-page": "agent-hub", children: [
2319
+ overview ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u9876\u680F", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2320
+ TopBar,
2321
+ {
2322
+ overview,
2323
+ view,
2324
+ onOpenInbox: () => {
2325
+ hub.setView("inbox");
2326
+ void hub.refreshInbox();
2327
+ },
2328
+ onOpenActivity: () => {
2329
+ hub.setView("activity");
2330
+ void hub.refreshActivity();
2331
+ },
2332
+ onOpenSettings: () => {
2333
+ hub.setView("settings");
2334
+ void hub.refreshSettings();
2335
+ },
2336
+ onNewBot: () => setWizardOpen(true),
2337
+ onBack: () => hub.setView("main")
2338
+ }
2339
+ ) }) : showSkeleton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("header", { className: "ah-topbar", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Skeleton, { rows: 1 }) }) : null,
2340
+ wizardOpen && overview ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u65B0\u5EFA\u961F\u53CB", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2341
+ NewBotWizard,
2342
+ {
2343
+ existingBots: overview.bots,
2344
+ modelOptions: state.settings?.modelOptions ?? [],
2345
+ modelOptionsError: state.settings ? null : state.settingsError,
2346
+ onCreate: async (input, color) => {
2347
+ const created = await hub.command("createBot", input);
2348
+ if (!created.ok) return { error: created.error };
2349
+ const botId = created.data?.id;
2350
+ if (botId === void 0) return { error: "\u521B\u5EFA\u6210\u529F\u4F46\u6CA1\u6709\u8FD4\u56DE\u673A\u5668\u4EBA id" };
2351
+ const colored = await hub.command("updateBot", { botId, patch: { color } });
2352
+ if (!colored.ok) console.error("[agent-hub] \u4FDD\u5B58\u5934\u50CF\u989C\u8272\u5931\u8D25:", colored.error);
2353
+ return { botId };
2354
+ },
2355
+ onOpenThread: (botId) => {
2356
+ setWizardOpen(false);
2357
+ hub.selectThread(botId);
2358
+ setNotice("\u6B63\u5728\u8BD5\u8DD1\uFF1A\u5B83\u4F1A\u5148\u505A\u81EA\u6211\u4ECB\u7ECD\uFF0C\u7A0D\u7B49\u7247\u523B\u3002");
2359
+ setNoticeAutoClearBot(botId);
2360
+ void hub.refreshOverview();
2361
+ },
2362
+ onClose: () => setWizardOpen(false)
2363
+ }
2364
+ ) }) : null,
2365
+ teachOpen && selectedBot ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u6559\u5B83\u4E00\u62DB", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2366
+ TeachDialog,
2367
+ {
2368
+ botName: selectedBot.name,
2369
+ sessions: state.recentSessions,
2370
+ onTeach: async (sessionId, note) => {
2371
+ const result = await hub.command("teach", { botId: selectedBot.id, sessionId, note });
2372
+ if (!result.ok) return result.error;
2373
+ setTeachOpen(false);
2374
+ await Promise.all([hub.refreshThread(selectedBot.id), hub.refreshBot(selectedBot.id)]);
2375
+ return null;
2376
+ },
2377
+ onClose: () => setTeachOpen(false)
2378
+ }
2379
+ ) }) : null,
2380
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `ah-body${rightOpen && selectedBot && view === "main" ? " ah-body-right" : ""}`, children: [
2381
+ overview ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u961F\u53CB\u5217\u8868", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2382
+ BotList,
2383
+ {
2384
+ overview,
2385
+ selected: view === "main" ? selectedThread : "",
2386
+ onSelect: (threadId) => hub.selectThread(threadId),
2387
+ onOpenInbox: () => {
2388
+ hub.setView("inbox");
2389
+ void hub.refreshInbox();
2390
+ }
2391
+ }
2392
+ ) }) : showSkeleton ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Skeleton, { rows: 6 }) : null,
2393
+ view === "inbox" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u6536\u4EF6\u7BB1", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2394
+ InboxPane,
2395
+ {
2396
+ inbox: state.inbox,
2397
+ loading: state.inbox === null,
2398
+ error: null,
2399
+ onRetry: () => void hub.refreshInbox(),
2400
+ actions: cardActions,
2401
+ busyId: busyInboxId
2402
+ }
2403
+ ) }) : view === "activity" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u6D3B\u52A8\u6D41", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2404
+ ActivityPane,
2405
+ {
2406
+ activity: state.activity,
2407
+ bots: overview?.bots ?? [],
2408
+ loading: state.activity === null,
2409
+ onRetry: () => void hub.refreshActivity(),
2410
+ onLoadMore: () => {
2411
+ const last = state.activity?.rows.at(-1)?.id;
2412
+ if (last) void hub.refreshActivity(last);
2413
+ }
2414
+ }
2415
+ ) }) : view === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u8BBE\u7F6E", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2416
+ SettingsPane,
2417
+ {
2418
+ settings: state.settings,
2419
+ loadError: state.settingsError,
2420
+ overview,
2421
+ loading: state.settings === null,
2422
+ onRetry: () => void hub.refreshSettings(),
2423
+ onSave: saveSettings,
2424
+ onPauseAll: () => lifecycleAll("paused"),
2425
+ onResumeAll: () => lifecycleAll("active")
2426
+ }
2427
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u5BF9\u8BDD\u7EBF\u7A0B", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2428
+ ThreadPane,
2429
+ {
2430
+ thread: state.threads[selectedThread] ?? null,
2431
+ loading: state.threads[selectedThread] === void 0,
2432
+ header,
2433
+ inputPlaceholderName: state.threads[selectedThread]?.title ?? "\u961F\u53CB",
2434
+ banner: notice,
2435
+ onBannerDismiss: () => {
2436
+ setNotice(null);
2437
+ setNoticeAutoClearBot(null);
2438
+ },
2439
+ onLoadEarlier: () => {
2440
+ const t = state.threads[selectedThread];
2441
+ if (t?.hasMore && t.oldestId) void hub.refreshThread(selectedThread, t.oldestId);
2442
+ },
2443
+ onSend: (text) => hub.send(selectedThread, text),
2444
+ actions: cardActions,
2445
+ busyInboxId,
2446
+ onRetry: () => void hub.refreshThread(selectedThread),
2447
+ error: threadError,
2448
+ onTeach: () => setTeachOpen(true),
2449
+ onOpenSession: openSession,
2450
+ onTogglePause: togglePause,
2451
+ onInterrupt: interrupt,
2452
+ rightCollapsed: view === "main" && !rightOpen && selectedBot !== null,
2453
+ onExpandRight: () => setRightOpen(true)
2454
+ },
2455
+ selectedThread
2456
+ ) }),
2457
+ view === "main" && rightOpen ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlockBoundary, { label: "\u8BE6\u60C5", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2458
+ RightPanel,
2459
+ {
2460
+ bot: selectedBot ? state.bots[selectedBot.id] ?? null : null,
2461
+ modelOptions: state.settings?.modelOptions ?? [],
2462
+ modelOptionsError: state.settings ? null : state.settingsError,
2463
+ onRetrySettings: () => void hub.refreshSettings(),
2464
+ clientCtx: props.clientCtx,
2465
+ run: (name, input) => hub.command(name, input),
2466
+ onRefresh: () => {
2467
+ if (selectedBot) void hub.refreshBot(selectedBot.id);
2468
+ void hub.refreshOverview();
2469
+ },
2470
+ onTeach: () => setTeachOpen(true),
2471
+ onCollapse: () => setRightOpen(false)
2472
+ },
2473
+ selectedBot?.id ?? "none"
2474
+ ) }) : null
2475
+ ] })
2476
+ ] });
2477
+ }
2478
+
2479
+ // src/client/index.tsx
2480
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2481
+ var inject = ["slots", "locale"];
2482
+ function HubEntryIcon(props) {
2483
+ const size = props.size ?? 16;
2484
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("svg", { viewBox: "0 0 16 16", width: size, height: size, "aria-hidden": "true", className: "ah-entry-icon", children: [
2485
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { cx: "5.5", cy: "5", r: "2.2", fill: "currentColor" }),
2486
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("circle", { cx: "10.8", cy: "6.2", r: "1.7", fill: "currentColor", opacity: "0.85" }),
2487
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: "M1.8 13.2c0-2.1 1.7-3.6 3.7-3.6s3.7 1.5 3.7 3.6z", fill: "currentColor" }),
2488
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("path", { d: "M9.9 13.2c0.2-1.7 1.2-2.9 2.7-2.9 1.2 0 2.2 0.8 2.5 2z", fill: "currentColor", opacity: "0.85" })
2489
+ ] });
2490
+ }
2491
+ function HubToastOverlay(props) {
2492
+ const state = useStore(props.hub);
2493
+ if (state.toasts.length === 0) return null;
2494
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ah-toast-layer", role: "status", children: state.toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ah-toast", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2495
+ "button",
2496
+ {
2497
+ type: "button",
2498
+ className: "ah-toast-btn",
2499
+ onClick: () => {
2500
+ props.hub.dismissToast(toast.id);
2501
+ props.hub.setView("inbox");
2502
+ void props.hub.refreshInbox();
2503
+ try {
2504
+ const layout = props.clientCtx?.get?.("layout");
2505
+ layout?.selectPanel?.("agent-hub");
2506
+ } catch {
2507
+ }
2508
+ },
2509
+ children: toast.text
2510
+ }
2511
+ ) }, toast.id)) });
2512
+ }
2513
+ function apply(ctx) {
2514
+ const hub = createHubStore();
2515
+ try {
2516
+ ctx.effect(() => {
2517
+ const tag = document.createElement("style");
2518
+ tag.dataset.plugin = "dsh-buddy";
2519
+ tag.textContent = styles_default;
2520
+ document.head.appendChild(tag);
2521
+ return () => tag.remove();
2522
+ }, "dsh-buddy: styles");
2523
+ } catch (error) {
2524
+ console.error("[agent-hub] \u63D2\u5165\u6837\u5F0F\u5931\u8D25:", error);
2525
+ }
2526
+ try {
2527
+ ctx.effect(() => {
2528
+ const close = connectEvents(hub);
2529
+ void hub.refreshAll();
2530
+ return close;
2531
+ }, "dsh-buddy: sse");
2532
+ ctx.on("connection/reset", () => {
2533
+ void hub.refreshAll();
2534
+ });
2535
+ } catch (error) {
2536
+ console.error("[agent-hub] \u5EFA\u7ACB\u63A8\u9001\u8FDE\u63A5\u5931\u8D25:", error);
2537
+ }
2538
+ try {
2539
+ ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: "agent-hub" }, () => HubPage({ hub, clientCtx: ctx })));
2540
+ } catch (error) {
2541
+ console.error("[agent-hub] \u6CE8\u518C main \u63D2\u69FD\u5931\u8D25:", error);
2542
+ }
2543
+ try {
2544
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register(
2545
+ // label 是 thunk:每次取值时从仓库读待处理收件箱数(入口行由宿主渲染)。
2546
+ { name: "sidebar.panellist", id: "agent-hub", order: 15, label: () => entryLabel(hub.getSnapshot().overview?.inbox.total ?? 0) },
2547
+ (props) => HubEntryIcon(props ?? {})
2548
+ ));
2549
+ } catch (error) {
2550
+ console.error("[agent-hub] \u6CE8\u518C\u4FA7\u680F\u5165\u53E3\u5931\u8D25:", error);
2551
+ }
2552
+ try {
2553
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register(
2554
+ { name: "shell.overlay", id: "dsh-buddy-toast", order: 100 },
2555
+ () => HubToastOverlay({ hub, clientCtx: ctx })
2556
+ ));
2557
+ } catch (error) {
2558
+ console.error("[agent-hub] \u6CE8\u518C\u5E94\u7528\u5185\u63D0\u793A\u63D2\u69FD\u5931\u8D25:", error);
2559
+ }
2560
+ }
2561
+ return module.exports } })