@shgroup/dsh-serenity-hooks 1.21.0 → 1.21.1

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/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.21.0",
3
+ "version": "1.21.1",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/loop/session_rebuild + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -262,7 +262,7 @@ window.__ModuleLoader__.load({
262
262
  }
263
263
  //#endregion
264
264
  //#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SafeModePanel
265
- const css$1 = "/* SafeModePanel — 会话头部 Serenity 状态徽章(点击展开详情卡)。\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 尺寸与官方 header 标签一致;前缀 sp- 防冲突。 */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 4px 0 4px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 24px;\n white-space: nowrap;\n}\n\n.sp-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 22px;\n padding: 0 6px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-badge:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-badge:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: 18px;\n padding: 0 8px;\n border: none;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggle:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-toggle:disabled {\n opacity: 0.55;\n cursor: default;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.sp-toggleOn:hover:not(:disabled) {\n background: var(--dsw-alias-state-error-secondary);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleOff:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleLg {\n height: 26px;\n padding: 0 14px;\n font-size: 13px;\n line-height: 26px;\n border-radius: 6px;\n}\n\n/* 详情卡:相对徽章绝对定位(右对齐),卡片 token */\n.sp-pop {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n z-index: 20;\n width: 300px;\n box-sizing: border-box;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n background: var(--dsw-specific-tip);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* v1.21 大面板:双 tab(状态/账号),无滚动条约束(内容在视口内完整容纳) */\n.sp-popLg {\n width: 460px;\n padding: 10px 14px 14px;\n}\n\n.sp-tabs {\n display: flex;\n gap: 4px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.sp-tab {\n flex: 1;\n padding: 6px 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.sp-tab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-tabOn {\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.2));\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n\n.sp-popRow {\n display: flex;\n align-items: baseline;\n gap: 10px;\n min-width: 0;\n}\n\n.sp-popLabel {\n flex: none;\n width: 34px;\n font-size: 12px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-popValue {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-popActions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n/* loop 运行状态区块(WebUI 等待界面;并行任务各自一行) */\n.sp-popSection {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 2px 0 2px 44px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-loopLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-state-info-primary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-loopResp {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sp-toggle,\n .sp-badge {\n transition: none;\n }\n}\n";
265
+ const css$1 = "/* SafeModePanel — 会话头部 Serenity 状态卡片(v1.21 UX 重构)\n * 卡片 → 点击 → 官方 Modal(body portal 遮罩 + 居中)\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应);\n * 前缀 sp- 防冲突。 */\n\n/* ── 头部状态卡片(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.sp-dotOn {\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n}\n\n/* safe-mode 徽标(卡片本体直接可见;ON 红 / OFF 灰) */\n.sp-sm {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n height: 18px;\n padding: 0 7px;\n border-radius: 9px;\n font-size: 11px;\n font-weight: 600;\n line-height: 1;\n}\n\n.sp-smOn {\n background: var(--dsw-alias-state-warning-bg, rgba(245, 158, 11, 0.18));\n color: var(--dsw-alias-state-warning-fg, #f59e0b);\n border: 1px solid var(--dsw-alias-state-warning-border, rgba(245, 158, 11, 0.4));\n}\n\n.sp-smOff {\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.16));\n color: var(--dsw-alias-label-dimmed);\n border: 1px solid var(--dsw-alias-border-l1);\n}\n\n.sp-smIcon {\n flex: none;\n}\n\n/* ── 官方 Modal 定制(宽度 + 无滚动条) ── */\n\n.sp-modal {\n width: 480px;\n max-width: calc(100vw - 48px);\n /* 无滚动条约束:内容在视口内完整容纳(紧凑布局,溢出不滚动) */\n}\n\n/* ── tab ── */\n\n.sp-tabs {\n display: flex;\n gap: 4px;\n padding: 0 2px 10px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n\n.sp-tab {\n flex: 1;\n padding: 7px 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.sp-tab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-tabOn {\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.2));\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n\n/* ── 模态内容(紧凑布局,无滚动条) ── */\n\n.sp-modalBody {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding-top: 12px;\n /* 内容超出视口时 body 区滚动(兜底——不再截断);整卡不滚动 */\n overflow-y: auto;\n max-height: calc(100vh - 200px);\n}\n\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 10px;\n min-width: 0;\n}\n\n.sp-label {\n flex: none;\n width: 40px;\n font-size: 12px;\n color: var(--dsw-alias-label-caption);\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n}\n\n.sp-actions {\n display: flex;\n justify-content: flex-end;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n/* safe-mode 开关(大) */\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: var(--dsw-alias-state-warning-bg, rgba(245, 158, 11, 0.15));\n color: var(--dsw-alias-state-warning-fg, #f59e0b);\n border-color: var(--dsw-alias-state-warning-border, rgba(245, 158, 11, 0.4));\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n/* loop 运行状态区块(并行任务各自一行) */\n\n.sp-section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n margin-top: 2px;\n}\n\n.sp-loopItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 2px 0 2px 50px;\n min-width: 0;\n}\n\n.sp-loopHead {\n display: flex;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n}\n\n.sp-loopLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-loopRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-loopTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-loopResp {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* loop 详情折叠控件 */\n.sp-loopsToggle {\n flex: none;\n padding: 2px 8px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 5px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n cursor: pointer;\n}\n\n.sp-loopsToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-loopsList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 2px;\n}\n\n.sp-loopsMore {\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n padding: 2px 0 2px 50px;\n}\n\n";
266
266
  if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
267
267
  const tag = document.createElement("style");
268
268
  tag.setAttribute("data-sp-css", "1");
@@ -277,14 +277,14 @@ window.__ModuleLoader__.load({
277
277
  function cx(...parts) {
278
278
  return parts.filter(Boolean).join(" ");
279
279
  }
280
- /** 会话头部:可点击徽章 + safe-mode 开关;点击徽章展开双 tab 大面板 */
280
+ /** 会话头部:Serenity 状态卡片(点击 官方 Modal 居中,双 tab */
281
281
  function SafeModePanel(props) {
282
282
  const [status, setStatus] = (0, react.useState)(null);
283
283
  const [busy, setBusy] = (0, react.useState)(false);
284
284
  const [open, setOpen] = (0, react.useState)(false);
285
285
  const [tab, setTab] = (0, react.useState)("status");
286
286
  const [loops, setLoops] = (0, react.useState)([]);
287
- const rootRef = (0, react.useRef)(null);
287
+ const [loopsOpen, setLoopsOpen] = (0, react.useState)(false);
288
288
  const sessionId = props.sessionId;
289
289
  const refresh = (0, react.useCallback)(async () => {
290
290
  const qs = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
@@ -317,14 +317,6 @@ window.__ModuleLoader__.load({
317
317
  clearInterval(timer);
318
318
  };
319
319
  }, [open, sessionId]);
320
- (0, react.useEffect)(() => {
321
- if (!open) return;
322
- const onDown = (e) => {
323
- if (rootRef.current !== null && !rootRef.current.contains(e.target)) setOpen(false);
324
- };
325
- document.addEventListener("pointerdown", onDown);
326
- return () => document.removeEventListener("pointerdown", onDown);
327
- }, [open]);
328
320
  const toggle = async (on) => {
329
321
  setBusy(true);
330
322
  try {
@@ -344,130 +336,141 @@ window.__ModuleLoader__.load({
344
336
  setBusy(false);
345
337
  }
346
338
  };
347
- if (!status) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
348
- className: cx("sp-root"),
349
- children: "⏳ Serenity…"
339
+ if (!status) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
340
+ type: "button",
341
+ className: cx("sp-card"),
342
+ disabled: true,
343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cx("sp-dot", "sp-dotOff") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
344
+ className: cx("sp-brand"),
345
+ children: "Serenity…"
346
+ })]
350
347
  });
351
348
  const inCcc = status.root !== null;
352
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
353
- ref: rootRef,
354
- className: cx("sp-root"),
355
- title: status.root ?? void 0,
349
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
350
+ type: "button",
351
+ className: cx("sp-card"),
352
+ "aria-haspopup": "dialog",
353
+ "aria-expanded": open,
354
+ onClick: () => setOpen(true),
355
+ title: inCcc ? `Serenity v${status.accVersion} — ${status.root}` : "Serenity(未激活)",
356
356
  children: [
357
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
358
- type: "button",
359
- className: cx("sp-badge"),
360
- "aria-expanded": open,
361
- onClick: () => setOpen((v) => !v),
362
- title: open ? "收起详情" : "查看 Serenity 状态",
363
- children: [
364
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cx("sp-dot", inCcc ? "sp-dotOn" : "sp-dotOff") }),
365
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
366
- className: cx("sp-brand"),
367
- children: ["Serenity v", status.accVersion]
368
- }),
369
- open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 12 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })
370
- ]
357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cx("sp-dot", inCcc ? "sp-dotOn" : "sp-dotOff") }),
358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
359
+ className: cx("sp-brand"),
360
+ children: ["Serenity v", status.accVersion]
371
361
  }),
372
- inCcc && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
373
- type: "button",
374
- className: cx("sp-toggle", status.safeModeOn ? "sp-toggleOn" : "sp-toggleOff"),
375
- disabled: busy,
376
- onClick: () => void toggle(!status.safeModeOn),
377
- title: status.safeModeOn ? "关闭 safe-mode(解除 bash 限制)" : "开启 safe-mode(隐藏写工具)",
378
- children: [
379
- status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
380
- size: 12,
381
- className: cx("sp-toggleIcon")
382
- }),
383
- "safe-mode ",
384
- status.safeModeOn ? "ON" : "OFF"
385
- ]
362
+ inCcc && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
363
+ className: cx("sp-sm", status.safeModeOn ? "sp-smOn" : "sp-smOff"),
364
+ title: status.safeModeOn ? "safe-mode 已开启(隐藏写工具)" : "safe-mode 关闭",
365
+ children: [status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
366
+ size: 11,
367
+ className: cx("sp-smIcon")
368
+ }), status.safeModeOn ? "safe" : "safe-off"]
386
369
  }),
387
- open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
388
- className: cx("sp-pop", "sp-popLg"),
389
- role: "dialog",
390
- "aria-label": "Serenity 高级设定",
391
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
392
- className: cx("sp-tabs"),
393
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
394
- type: "button",
395
- className: cx("sp-tab", tab === "status" && "sp-tabOn"),
396
- onClick: () => setTab("status"),
397
- children: "状态"
398
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
399
- type: "button",
400
- className: cx("sp-tab", tab === "accounts" && "sp-tabOn"),
401
- onClick: () => setTab("accounts"),
402
- children: "账号"
370
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
371
+ size: 12,
372
+ className: cx("sp-chev")
373
+ })
374
+ ]
375
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
376
+ open,
377
+ onClose: () => setOpen(false),
378
+ title: "Serenity 高级设定",
379
+ closeLabel: "关闭",
380
+ className: cx("sp-modal"),
381
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
382
+ className: cx("sp-tabs"),
383
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
384
+ type: "button",
385
+ className: cx("sp-tab", tab === "status" && "sp-tabOn"),
386
+ onClick: () => setTab("status"),
387
+ children: "状态"
388
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
389
+ type: "button",
390
+ className: cx("sp-tab", tab === "accounts" && "sp-tabOn"),
391
+ onClick: () => setTab("accounts"),
392
+ children: "账号"
393
+ })]
394
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
395
+ className: cx("sp-modalBody"),
396
+ children: tab === "accounts" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsTab, { sessionId: String(sessionId ?? "") }) : inCcc ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
397
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
398
+ className: cx("sp-row"),
399
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
400
+ className: cx("sp-label"),
401
+ children: "CCC"
402
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
403
+ className: cx("sp-value"),
404
+ title: status.root ?? void 0,
405
+ children: status.root
403
406
  })]
404
- }), tab === "accounts" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsTab, { sessionId: String(sessionId ?? "") }) : inCcc ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
405
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
406
- className: cx("sp-popRow"),
407
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
408
- className: cx("sp-popLabel"),
409
- children: "CCC"
410
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
411
- className: cx("sp-popValue"),
412
- title: status.root ?? void 0,
413
- children: status.root
414
- })]
415
- }),
416
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
417
- className: cx("sp-popRow"),
418
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
419
- className: cx("sp-popLabel"),
420
- children: "loop"
421
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
422
- className: cx("sp-popValue"),
423
- title: status.loopModel ? `loop.defaultModel: ${status.loopModel}` : void 0,
424
- children: status.loopModel ?? "未配置"
425
- })]
426
- }),
427
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
428
- className: cx("sp-popRow"),
429
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
430
- className: cx("sp-popLabel"),
431
- children: "守卫"
432
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
433
- className: cx("sp-popValue"),
434
- children: [
435
- "blacklist ",
436
- status.blacklist.length,
437
- " 条",
438
- status.threshold !== null ? ` · keeper 阈值 ${status.threshold}` : ""
439
- ]
440
- })]
441
- }),
442
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
443
- className: cx("sp-popActions"),
444
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
445
- type: "button",
446
- className: cx("sp-toggle", status.safeModeOn ? "sp-toggleOn" : "sp-toggleOff", "sp-toggleLg"),
447
- disabled: busy,
448
- onClick: () => void toggle(!status.safeModeOn),
449
- children: [
450
- status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
451
- size: 14,
452
- className: cx("sp-toggleIcon")
453
- }),
454
- "safe-mode ",
455
- status.safeModeOn ? "ON" : "OFF"
456
- ]
457
- })
458
- }),
459
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
460
- className: cx("sp-popSection"),
461
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
462
- className: cx("sp-popRow"),
463
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
464
- className: cx("sp-popLabel"),
407
+ }),
408
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
409
+ className: cx("sp-row"),
410
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
411
+ className: cx("sp-label"),
412
+ children: "loop"
413
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
414
+ className: cx("sp-value"),
415
+ title: status.loopModel ? `loop.defaultModel: ${status.loopModel}` : void 0,
416
+ children: status.loopModel ?? "未配置"
417
+ })]
418
+ }),
419
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
420
+ className: cx("sp-row"),
421
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
422
+ className: cx("sp-label"),
423
+ children: "守卫"
424
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
425
+ className: cx("sp-value"),
426
+ children: [
427
+ "blacklist ",
428
+ status.blacklist.length,
429
+ " 条",
430
+ status.threshold !== null ? ` · keeper 阈值 ${status.threshold}` : ""
431
+ ]
432
+ })]
433
+ }),
434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
435
+ className: cx("sp-actions"),
436
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
437
+ type: "button",
438
+ className: cx("sp-toggle", status.safeModeOn ? "sp-toggleOn" : "sp-toggleOff", "sp-toggleLg"),
439
+ disabled: busy,
440
+ onClick: () => void toggle(!status.safeModeOn),
441
+ children: [
442
+ status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
443
+ size: 14,
444
+ className: cx("sp-toggleIcon")
445
+ }),
446
+ "safe-mode ",
447
+ status.safeModeOn ? "ON" : "OFF"
448
+ ]
449
+ })
450
+ }),
451
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
452
+ className: cx("sp-section"),
453
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
454
+ className: cx("sp-row"),
455
+ children: [
456
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
457
+ className: cx("sp-label"),
465
458
  children: "loops"
466
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
467
- className: cx("sp-popValue"),
459
+ }),
460
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
461
+ className: cx("sp-value"),
468
462
  children: loops.filter((l) => !l.done).length > 0 ? `运行中 ${loops.filter((l) => !l.done).length}${loops.length > 0 ? ` / 共 ${loops.length}` : ""}` : "无运行中 loop"
469
- })]
470
- }), loops.slice(0, 4).map((l) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
463
+ }),
464
+ loops.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
465
+ type: "button",
466
+ className: cx("sp-loopsToggle"),
467
+ onClick: () => setLoopsOpen((v) => !v),
468
+ children: loopsOpen ? "收起" : "详情"
469
+ })
470
+ ]
471
+ }), loopsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
472
+ className: cx("sp-loopsList"),
473
+ children: [loops.slice(0, 3).map((l) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
471
474
  className: cx("sp-loopItem"),
472
475
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
473
476
  className: cx("sp-loopHead"),
@@ -489,23 +492,30 @@ window.__ModuleLoader__.load({
489
492
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
490
493
  className: cx("sp-loopResp"),
491
494
  title: l.lastResponse,
492
- children: l.lastResponse.slice(0, 60) || (l.done ? "已完成" : "(等待首轮响应)")
495
+ children: l.lastResponse.slice(0, 80) || (l.done ? "已完成" : "(等待首轮响应)")
493
496
  })]
494
- }, l.label))]
495
- })
496
- ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
497
- className: cx("sp-popRow"),
498
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
499
- className: cx("sp-popLabel"),
500
- children: "状态"
501
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
502
- className: cx("sp-popValue"),
503
- children: "ACC 未激活(工作区不在 CCC 内)"
497
+ }, l.label)), loops.length > 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
498
+ className: cx("sp-loopsMore"),
499
+ children: [
500
+ "… ",
501
+ loops.length,
502
+ " 个 loop(其余见等待界面)"
503
+ ]
504
+ })]
504
505
  })]
506
+ })
507
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
508
+ className: cx("sp-row"),
509
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
510
+ className: cx("sp-label"),
511
+ children: "状态"
512
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
513
+ className: cx("sp-value"),
514
+ children: "ACC 未激活(工作区不在 CCC 内)"
505
515
  })]
506
516
  })
507
- ]
508
- });
517
+ })]
518
+ })] });
509
519
  }
510
520
  //#endregion
511
521
  //#region src/client/image-fallback-api.ts
@@ -632,7 +642,7 @@ window.__ModuleLoader__.load({
632
642
  }
633
643
  //#endregion
634
644
  //#region \0sp-css:/home/yh/home/home-serenity/AI_LAB/dsh-serenity-plugin/hooks/dsh-serenity-hooks/src/client/SettingsSection
635
- const css = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n 沿用 --dsw-alias-* 语义 token(明暗自适应)+ 紧凑布局 */\n\n.ss-root {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 8px 0;\n}\n\n.ss-note {\n margin: 0 0 4px;\n font-size: 12px;\n opacity: 0.7;\n}\n\n.ss-row {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n}\n\n.ss-label {\n min-width: 88px;\n font-weight: 600;\n}\n\n.ss-desc {\n flex: 1;\n font-size: 12px;\n opacity: 0.65;\n}\n\n.ss-row input[type='checkbox'] {\n flex: none;\n}\n\n.ss-row input[type='range'] {\n flex: none;\n width: 140px;\n}\n\n.ss-value {\n min-width: 36px;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n opacity: 0.8;\n}\n\n.ss-degrade {\n padding: 12px 8px;\n font-size: 13px;\n}\n\n.ss-degrade p {\n margin: 0;\n}\n";
645
+ const css = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.3));\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary, #eee);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n";
636
646
  if (typeof document !== "undefined" && document.querySelector("style[data-sp-css]") === null) {
637
647
  const tag = document.createElement("style");
638
648
  tag.setAttribute("data-sp-css", "1");
@@ -643,7 +653,54 @@ window.__ModuleLoader__.load({
643
653
  //#region src/client/SettingsSection.tsx
644
654
  /** 降级提示文案(旧 RC 白名单时引导去宁静号面板) */
645
655
  const DEGRADE_NOTE = "当前运行版本未暴露 serenity-hooks 配置(旧 RC 白名单)。请在会话头部 Serenity 徽章的「高级设定」面板中管理,或升级 DSH。";
646
- /** dsh 原生设置面板:serenity-hooks 简单配置页 */
656
+ /** 开关(官方 settings 语言:label 右置 toggle,--dsw-alias-* token) */
657
+ function Toggle(props) {
658
+ const { checked, disabled, onChange } = props;
659
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
660
+ className: "ss-switch",
661
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
662
+ type: "checkbox",
663
+ checked,
664
+ disabled,
665
+ onChange: (e) => onChange(e.target.checked)
666
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "ss-switchTrack" })]
667
+ });
668
+ }
669
+ /** 一个配置行卡(官方 rowCard:标题+说明 左列 / 控件右置) */
670
+ function RowCard(props) {
671
+ const { title, desc, control } = props;
672
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
673
+ className: "ss-rowCard",
674
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
675
+ className: "ss-rowText",
676
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
677
+ className: "ss-rowName",
678
+ children: title
679
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
680
+ className: "ss-rowDesc",
681
+ children: desc
682
+ })]
683
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
684
+ className: "ss-rowControl",
685
+ children: control
686
+ })]
687
+ });
688
+ }
689
+ /** 分组(多级标题:分组标题 + 一组行卡) */
690
+ function Group(props) {
691
+ const { title, children } = props;
692
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
693
+ className: "ss-group",
694
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
695
+ className: "ss-groupTitle",
696
+ children: title
697
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
698
+ className: "ss-rows",
699
+ children
700
+ })]
701
+ });
702
+ }
703
+ /** dsh 原生设置面板:serenity-hooks 简单配置页(官方 settings 设计语言,多级标题) */
647
704
  function SettingsSection(props) {
648
705
  const { scope } = props;
649
706
  const [snapshot, setSnapshot] = (0, react.useState)(() => scope.getSnapshot());
@@ -659,99 +716,81 @@ window.__ModuleLoader__.load({
659
716
  scope.set("rebuildThreshold", Math.min(1, Math.max(.01, v)));
660
717
  };
661
718
  if (!ready) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
662
- className: "ss-degrade",
663
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: DEGRADE_NOTE })
719
+ className: "ss-section",
720
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
721
+ className: "ss-degrade",
722
+ children: DEGRADE_NOTE
723
+ })
664
724
  });
665
725
  const gatewayOn = value?.gatewayEnabled ?? false;
666
726
  const rebuildOn = value?.rebuildEnabled ?? true;
667
727
  const threshold = value?.rebuildThreshold ?? .9;
668
728
  const namingOn = value?.namingEnabled ?? true;
669
729
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
670
- className: "ss-root",
730
+ className: "ss-section",
671
731
  children: [
732
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
733
+ className: "ss-title",
734
+ children: "Serenity"
735
+ }),
672
736
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
673
- className: "ss-note",
674
- children: "简单配置(开关/阈值)。账号列表等复杂配置请到会话头部 Serenity 徽章「高级设定」面板。"
737
+ className: "ss-intro",
738
+ children: "宁静号的开关与阈值,改动即时保存(DSH settings.yaml)。账号密码等复杂配置请到会话头部「Serenity」卡片 → 高级设定 → 账号。"
675
739
  }),
676
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
677
- className: "ss-row",
678
- children: [
679
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
680
- className: "ss-label",
681
- children: "F1 双端口网关"
682
- }),
683
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
684
- className: "ss-desc",
685
- children: "额外监听端口 + 网页登录(外部访问)"
686
- }),
687
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
688
- type: "checkbox",
740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Group, {
741
+ title: "访问",
742
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
743
+ title: "双端口网关",
744
+ desc: "额外监听一个端口,登录后可从外部访问 Web UI",
745
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
689
746
  checked: gatewayOn,
690
- onChange: (e) => toggle("gatewayEnabled", e.target.checked)
747
+ onChange: (on) => toggle("gatewayEnabled", on)
691
748
  })
692
- ]
749
+ }) })
693
750
  }),
694
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
695
- className: "ss-row",
696
- children: [
697
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
698
- className: "ss-label",
699
- children: "F2 超限重建"
700
- }),
701
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
702
- className: "ss-desc",
703
- children: "上下文接近上限时提示 session_rebuild 清空重建"
704
- }),
705
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
706
- type: "checkbox",
751
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Group, {
752
+ title: "上下文",
753
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
754
+ title: "超限重建",
755
+ desc: "上下文接近上限时提示调用 session_rebuild 清空重建",
756
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
707
757
  checked: rebuildOn,
708
- onChange: (e) => toggle("rebuildEnabled", e.target.checked)
758
+ onChange: (on) => toggle("rebuildEnabled", on)
709
759
  })
710
- ]
711
- }),
712
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
713
- className: "ss-row",
714
- children: [
715
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
716
- className: "ss-label",
717
- children: "重建阈值"
718
- }),
719
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
720
- className: "ss-desc",
721
- children: "contextPressure 触发比例(0.01~1)"
722
- }),
723
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
724
- type: "range",
725
- min: .1,
726
- max: 1,
727
- step: .05,
728
- value: threshold,
729
- disabled: !rebuildOn,
730
- onChange: (e) => setThreshold(Number(e.target.value))
731
- }),
732
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
733
- className: "ss-value",
734
- children: threshold.toFixed(2)
760
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
761
+ title: "重建阈值",
762
+ desc: "上下文占用达到该比例时提示(0.10 ~ 1.00)",
763
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
764
+ className: "ss-threshold",
765
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
766
+ type: "range",
767
+ min: .1,
768
+ max: 1,
769
+ step: .05,
770
+ value: threshold,
771
+ disabled: !rebuildOn,
772
+ onChange: (e) => setThreshold(Number(e.target.value))
773
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
774
+ className: "ss-value",
775
+ children: threshold.toFixed(2)
776
+ })]
735
777
  })
736
- ]
778
+ }) })]
737
779
  }),
738
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
739
- className: "ss-row",
740
- children: [
741
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
742
- className: "ss-label",
743
- children: "F3 会话命名"
744
- }),
745
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
746
- className: "ss-desc",
747
- children: "dsh 会话创建时自动命名 S###-日期"
748
- }),
749
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
750
- type: "checkbox",
780
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Group, {
781
+ title: "会话",
782
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
783
+ title: "会话命名",
784
+ desc: "使用宁静号会话时,把当前会话命名为 SESSION 目录名",
785
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
751
786
  checked: namingOn,
752
- onChange: (e) => toggle("namingEnabled", e.target.checked)
787
+ onChange: (on) => toggle("namingEnabled", on)
753
788
  })
754
- ]
789
+ }) })
790
+ }),
791
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
792
+ className: "ss-hint",
793
+ children: "💡 双端口网关需先在「Serenity 卡片 → 账号」里添加账号并设置监听端口。"
755
794
  })
756
795
  ]
757
796
  });
package/lib/index.js CHANGED
@@ -7330,7 +7330,11 @@ function registerStatusApi(ctx, opts = {}) {
7330
7330
  sendJson(res, 404, { error: `no CCC found from workspace: ${workspace}` });
7331
7331
  return;
7332
7332
  }
7333
- sendJson(res, 200, { config: toWire(applyWirePatch(root, body.config ?? {})) });
7333
+ const saved = applyWirePatch(root, body.config ?? {});
7334
+ try {
7335
+ ctx.emit?.("serenity/config-updated");
7336
+ } catch {}
7337
+ sendJson(res, 200, { config: toWire(saved) });
7334
7338
  return;
7335
7339
  }
7336
7340
  sendJson(res, 405, { error: "method not allowed" });
@@ -7618,22 +7622,35 @@ function startGateway(config, getAccounts) {
7618
7622
  }
7619
7623
  };
7620
7624
  }
7621
- /** 注册 gateway(index.ts apply 调用):webServer 就绪后按配置启动第二监听器。 */
7625
+ /** 注册 gateway(index.ts apply 调用)。
7626
+ * root 发现:dsh web 进程 cwd 是 $HOME(非 CCC)——不能靠 process.cwd()。
7627
+ * 改为:agent/session-start 的 header.cwd 解析 CCC 根(首个会话出现即发现)。
7628
+ * 幂等 sync:仅当 配置/端口/root 变化时重建监听器。
7629
+ * 事件驱动:config-updated(/serenity/config PUT 后 emit)触发重新 sync。
7630
+ */
7622
7631
  function registerGateway(ctx, opts) {
7623
7632
  let current = null;
7633
+ let discoveredRoot = null;
7634
+ let lastSig = null;
7635
+ const resolveRoot = opts.resolveRoot ?? ((cwd) => findSerenityRoot(cwd));
7624
7636
  const sync = () => {
7625
7637
  try {
7638
+ const root = discoveredRoot;
7639
+ const webServer = ctx.get?.("webServer");
7640
+ if (!root || !webServer?.port) return;
7641
+ const settings = readAdvancedSettings(root);
7642
+ const enabled = settings.gateway.enabled;
7643
+ const sig = `${enabled}|${settings.gateway.host}|${settings.gateway.port}|${settings.gateway.accounts.length}|${webServer.port}`;
7644
+ if (sig === lastSig) return;
7645
+ lastSig = sig;
7626
7646
  if (current) {
7627
7647
  current.dispose();
7628
7648
  current = null;
7629
7649
  }
7630
- const root = opts.getRoot();
7631
- if (!root) return;
7632
- const webServer = ctx.get?.("webServer");
7633
- if (!webServer?.port) return;
7634
- const settings = readAdvancedSettings(root);
7635
- if (!settings.gateway.enabled) return;
7636
- if (settings.gateway.accounts.length === 0) return;
7650
+ if (!enabled) {
7651
+ console.log("[serenity-hooks] gateway 已停止(enabled=false)");
7652
+ return;
7653
+ }
7637
7654
  current = startGateway({
7638
7655
  host: settings.gateway.host,
7639
7656
  port: settings.gateway.port,
@@ -7641,13 +7658,29 @@ function registerGateway(ctx, opts) {
7641
7658
  mainPort: webServer.port,
7642
7659
  loginDelayMs: 0
7643
7660
  }, () => readAdvancedSettings(root).gateway.accounts);
7644
- console.log(`[serenity-hooks] gateway 已启动: http://${settings.gateway.host}:${settings.gateway.port} → 127.0.0.1:${webServer.port}`);
7661
+ const accounts = settings.gateway.accounts.length;
7662
+ console.log(`[serenity-hooks] gateway 已启动: http://${settings.gateway.host}:${settings.gateway.port} → 127.0.0.1:${webServer.port}` + (accounts === 0 ? "(⚠️ 未配置账号,登录页将提示)" : `(${accounts} 个账号)`));
7645
7663
  } catch (err) {
7646
- console.warn(`[serenity-hooks] gateway 启动失败: ${String(err?.message ?? err)}`);
7664
+ console.warn(`[serenity-hooks] gateway 同步失败: ${String(err?.message ?? err)}`);
7647
7665
  }
7648
7666
  };
7649
- sync();
7650
- ctx.on("session/event", () => {});
7667
+ ctx.on("agent/session-start", (payload) => {
7668
+ try {
7669
+ const cwd = payload.agent?.session?.header?.cwd;
7670
+ if (!cwd) return;
7671
+ const root = resolveRoot(cwd);
7672
+ if (!root) return;
7673
+ if (discoveredRoot !== root) {
7674
+ discoveredRoot = root;
7675
+ lastSig = null;
7676
+ }
7677
+ sync();
7678
+ } catch {}
7679
+ });
7680
+ ctx.on("serenity/config-updated", () => {
7681
+ lastSig = null;
7682
+ sync();
7683
+ });
7651
7684
  }
7652
7685
  //#endregion
7653
7686
  //#region src/index.ts
@@ -7712,7 +7745,7 @@ function apply(ctx, config) {
7712
7745
  });
7713
7746
  if (config.api) registerStatusApi(ctx, { configPaths: config.serenityConfigPaths });
7714
7747
  registerSettingsSection(ctx, config);
7715
- registerGateway(ctx, { getRoot: () => findSerenityRoot(process.cwd()) });
7748
+ registerGateway(ctx, {});
7716
7749
  if (config.env) registerEnv(ctx);
7717
7750
  if (config.opencodeSkills) registerOpencodeSkills(ctx);
7718
7751
  registerBootstrap(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.21.0",
3
+ "version": "1.21.1",
4
4
  "description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
5
5
  "license": "MIT",
6
6
  "repository": {