@shgroup/dsh-serenity-hooks 1.45.1 → 1.47.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > **一句话**:装上这个插件,你在电脑上给 AI 划一块自己的工作区(就是一个普通目录),
4
4
  > AI 在里面干活时就有了**记忆、纪律、工具和边界**——中途换模型、重启电脑、第二天再来,都能接着干。
5
5
  >
6
- > 适用 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(下称 DSH)0.1.5-rc.2 及以上。
6
+ > 适用 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(下称 DSH)0.1.7-rc.1 及以上。
7
7
  > 想了解背后的想法(为什么叫"认知容器"),看 [docs/cognitive-container-theory.md](https://github.com/tellmewhattodo/dsh-serenity-plugin/blob/master/docs/cognitive-container-theory.md);本文只讲**能干什么、怎么用**。
8
8
 
9
9
  **几个词先说明白**(后面都用这几个词,不再解释):
@@ -32,7 +32,7 @@
32
32
 
33
33
  ## 2. 快速开始(2 分钟)
34
34
 
35
- 前置:Node ≥ 20(或 bun)、DSH 0.1.5-rc.2 及以上。
35
+ 前置:Node ≥ 20(或 bun)、DSH 0.1.7-rc.1 及以上。
36
36
 
37
37
  ```bash
38
38
  # 1. 装插件(自动加入 DSH 的 web profile)
@@ -423,4 +423,4 @@ pnpm build # 打包(lib/index.js + client.js)
423
423
 
424
424
  MIT(见 [LICENSE](https://github.com/tellmewhattodo/dsh-serenity-plugin/blob/master/LICENSE))
425
425
 
426
- > **版本**:v1.31.13 | **前置**:DSH 0.1.5-rc.2+ / Node ≥ 20 或 bun | **测试**:80 个文件 / 1186 个用例
426
+ > **版本**:v1.47.0 | **前置**:DSH 0.1.7-rc.1+ / Node ≥ 20 或 bun | **测试**:107 个文件 / 1679 个用例
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.45.1",
3
+ "version": "1.47.0",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——11 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/im-bridge/acc-diag;后两个按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.5-rc.2(deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -208,7 +208,7 @@ window.__ModuleLoader__.load({
208
208
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cx("sp-switchThumb") })
209
209
  })
210
210
  ] }),
211
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {
211
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineMedium, {
212
212
  size: 12,
213
213
  className: cx("sp-chev")
214
214
  })
@@ -289,7 +289,7 @@ window.__ModuleLoader__.load({
289
289
  disabled: busy,
290
290
  onClick: () => void toggle(!status.safeModeOn),
291
291
  children: [
292
- status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
292
+ status.safeModeOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineMedium, {
293
293
  size: 14,
294
294
  className: cx("sp-toggleIcon")
295
295
  }),
@@ -4672,8 +4672,6 @@ Instruction-following style:
4672
4672
  }
4673
4673
  //#endregion
4674
4674
  //#region src/client/SettingsSection.tsx
4675
- /** 降级提示文案(旧 RC 白名单时引导去宁静号面板) */
4676
- const DEGRADE_NOTE = "当前运行版本未暴露 serenity-hooks 配置(旧 RC 白名单)。请升级 DSH 或使用会话头部 Serenity 状态栏。";
4677
4675
  /** 开关(官方 settings 语言:label 右置 toggle,--dsw-alias-* token) */
4678
4676
  function Toggle(props) {
4679
4677
  const { checked, disabled, onChange } = props;
@@ -4791,32 +4789,20 @@ Instruction-following style:
4791
4789
  }
4792
4790
  /** dsh 原生设置面板:serenity-hooks 配置页(官方 settings 设计语言,多级标题) */
4793
4791
  function SettingsSection(props) {
4794
- const { scope } = props;
4795
- const [snapshot, setSnapshot] = (0, react.useState)(() => scope.getSnapshot());
4796
- (0, react.useEffect)(() => {
4797
- return scope.subscribe(() => setSnapshot(scope.getSnapshot()));
4798
- }, [scope]);
4799
- const value = snapshot.value;
4800
- const ready = snapshot.status === "ready";
4792
+ const { set, useSerenitySettings } = props;
4793
+ const value = useSerenitySettings((s) => s).value;
4801
4794
  const toggle = (field, on) => {
4802
- scope.set(field, on);
4795
+ set(field, on);
4803
4796
  };
4804
4797
  const setThreshold = (v) => {
4805
- scope.set("rebuildThresholdK", Math.min(4e3, Math.max(50, Math.round(v))));
4798
+ set("rebuildThresholdK", Math.min(4e3, Math.max(50, Math.round(v))));
4806
4799
  };
4807
4800
  const setSkiffPort = (v) => {
4808
- scope.set("skiffDebugPort", Math.min(65535, Math.max(1024, Math.round(v))));
4801
+ set("skiffDebugPort", Math.min(65535, Math.max(1024, Math.round(v))));
4809
4802
  };
4810
4803
  const setAcpPort = (v) => {
4811
- scope.set("acpHttpPort", Math.min(65535, Math.max(1024, Math.round(v))));
4804
+ set("acpHttpPort", Math.min(65535, Math.max(1024, Math.round(v))));
4812
4805
  };
4813
- if (!ready) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4814
- className: "ss-section",
4815
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4816
- className: "ss-degrade",
4817
- children: DEGRADE_NOTE
4818
- })
4819
- });
4820
4806
  const gatewayOn = value?.gatewayEnabled ?? false;
4821
4807
  const rebuildOn = value?.rebuildEnabled ?? true;
4822
4808
  const thresholdK = value?.rebuildThresholdK ?? 400;
@@ -4826,12 +4812,14 @@ Instruction-following style:
4826
4812
  const acpPort = value?.acpHttpPort ?? 3100;
4827
4813
  const publicAskOn = value?.publicAskEnabled ?? false;
4828
4814
  const croOn = value?.croEnabled ?? true;
4815
+ const unattendedOn = value?.unattendedEnabled ?? false;
4829
4816
  const [openGateway, setOpenGateway] = (0, react.useState)(false);
4830
4817
  const [openRebuild, setOpenRebuild] = (0, react.useState)(false);
4831
4818
  const [openSkiff, setOpenSkiff] = (0, react.useState)(false);
4832
4819
  const [openAcp, setOpenAcp] = (0, react.useState)(false);
4833
4820
  const [openPublicAsk, setOpenPublicAsk] = (0, react.useState)(false);
4834
4821
  const [openCro, setOpenCro] = (0, react.useState)(false);
4822
+ const [openUnattended, setOpenUnattended] = (0, react.useState)(false);
4835
4823
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4836
4824
  className: "ss-section",
4837
4825
  children: [
@@ -4902,6 +4890,27 @@ Instruction-following style:
4902
4890
  })
4903
4891
  }) })
4904
4892
  }),
4893
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Group, {
4894
+ title: "无人值守",
4895
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4896
+ title: "无人值守代理回复",
4897
+ desc: "你走开后,会话被模型停下且无人应答时,由 ACC 代你回一句让它自己收束(默认关)",
4898
+ expandable: true,
4899
+ open: openUnattended,
4900
+ onToggle: setOpenUnattended,
4901
+ control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4902
+ checked: unattendedOn,
4903
+ onChange: (on) => toggle("unattendedEnabled", on)
4904
+ }),
4905
+ detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4906
+ className: "ss-detailStack",
4907
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4908
+ className: "ss-detailIntro",
4909
+ children: "无人值守代理回复(unattended proxy)\n· 作用:你发完任务就走开,模型干一段停下来又没人接话 ⇒ 会话就挂住了;\n 本机制由 ACC 注入一条“代理用户”的结构化回复,让它自己接着干\n· 收束方式:每轮给一个验证码;只有四种明确收束才放过 —— 干完了 / 已通知你并停下等你 /\n 需确认但可自决(按最保守可逆的做并记理由)/ 撞上“不可代批”的事(立刻停并写待办)\n· 身份:代理消息会自报“无人值守代理 · 非本人”,不会被当成你说的话\n· 不适用:有 CRO 自编程唤起程序的轨迹(它自己会叫自己)、对外面与微信桥会话、子代理会话\n· 默认关:它把“人”从回路里拿掉,失败是静默的 ⇒ 只在你要离开时才开\n· 配套:你自己用 send-later 给自己排的“定时叫醒”(现在约每 2 小时一次)会改成“很久才叫一次”(6–12 小时);\n 平时“接着干”的活交给本机制 —— ⚠️ 只在本机制上线之后才这么改,否则中间会出现没人管的空档\n· 本版现状:机制已实现(开关 + 注入行为 + 流水);⚠️ 尚未部署 ⇒ 现在拨开关不会生效,等发布后生效"
4910
+ })
4911
+ })
4912
+ }) })
4913
+ }),
4905
4914
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Group, {
4906
4915
  title: "外部能力",
4907
4916
  children: [
@@ -5147,19 +5156,16 @@ Instruction-following style:
5147
5156
  "slots",
5148
5157
  "conversation",
5149
5158
  "sessions",
5150
- "settingsScope"
5159
+ "configForms"
5151
5160
  ];
5152
- /** serenity-hooks 简单配置 scope spec(与 host 侧 registerSettingsSection 对齐) */
5153
- const SERENITY_SCOPE_SPEC = {
5154
- namespace: "serenity-hooks",
5155
- decode: (section) => section !== null && typeof section === "object" ? section : void 0
5156
- };
5161
+ /** 设置页命名空间 = profile 条目 id(与 host 侧 `SERENITY_SETTINGS_NS` 同值;单一真相源在 host 侧常量,此处只作字面量引用) */
5162
+ const SERENITY_SETTINGS_NS = "serenity-hooks";
5157
5163
  function apply(ctx) {
5158
5164
  ctx.inject([
5159
5165
  "slots",
5160
5166
  "conversation",
5161
5167
  "sessions",
5162
- "settingsScope"
5168
+ "configForms"
5163
5169
  ], (scope) => {
5164
5170
  scope.effect(() => scope.slots.register({
5165
5171
  name: "conversation.session.header.actions",
@@ -5182,14 +5188,19 @@ Instruction-following style:
5182
5188
  order: 110,
5183
5189
  inject: () => ({ uploadFile: (file, sessionId) => uploadFile(file, sessionId) })
5184
5190
  }, FileFallbackDock), "serenity: file fallback dock");
5185
- const serenityScope = scope.get("settingsScope").bind(SERENITY_SCOPE_SPEC);
5186
- scope.effect(() => scope.slots.register({
5191
+ const serenityForm = scope.configForms.get(SERENITY_SETTINGS_NS);
5192
+ scope.effect(() => scope.configForms.whileServed([SERENITY_SETTINGS_NS], () => scope.slots.inject("settings.section", () => scope.slots.register({
5187
5193
  name: "settings.section",
5188
- id: "serenity-hooks",
5194
+ id: SERENITY_SETTINGS_NS,
5189
5195
  order: 90,
5190
5196
  label: () => "Serenity",
5191
- inject: () => ({ scope: serenityScope })
5192
- }, SettingsSection), "serenity: simple settings section");
5197
+ inject: () => ({
5198
+ hooks: { serenitySettings: serenityForm },
5199
+ set: (field, value) => {
5200
+ serenityForm.set(field, value).catch((_error) => {});
5201
+ }
5202
+ })
5203
+ }, SettingsSection))), "serenity: simple settings section");
5193
5204
  });
5194
5205
  }
5195
5206
  //#endregion
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * | 事件 | 动作 | 出处 |
14
14
  * |---|---|---|
15
- * | `agent/session-start` | 标记「在跑」 | `host/contract.ts:275`(dsp 已在 `seams/context.ts:235` 订阅) |
15
+ * | `agent/created` | 标记「在跑」 | `host/contract.ts`(dsp 已在 `seams/context.ts` 订阅)。⚠️ v0.1.7 前名为 `agent/session-start`;新事件 `@mode = serial` ⇒ 处理函数须快速返回 |
16
16
  * | `agent/turn-stopping` | 清除标记 | `host/contract.ts:277`(dsp 已在 `rebuild.ts:442` 等 4 处订阅) |
17
17
  * | `agent/disposed` | **清该载体的项** | `host/contract.ts:280` |
18
18
  * | `session/disposed` | **清该载体的项** | `host/contract.ts:283` |
@@ -52,7 +52,7 @@ import type { Context } from 'cordis';
52
52
  import type { Agent } from '@deepseek-ai/dsh-agent';
53
53
  /** 一次「在跑」记录的 TTL(见文件头的方向性判据) */
54
54
  export declare const TURN_TTL_MS: number;
55
- /** 标记「在跑」(`agent/session-start` + `agent/status=running` 调用) */
55
+ /** 标记「在跑」(`agent/created` + `agent/status=running` 调用) */
56
56
  export declare function markTurnRunning(sessionId: string, nowMs: number, turn?: number | null): void;
57
57
  /** 清除「在跑」(`turn-stopping` / disposed 调用) */
58
58
  export declare function clearTurnRunning(sessionId: string): void;
@@ -82,7 +82,7 @@ export declare function __resetCroTurnsForTest(): void;
82
82
  * 装配追踪(`index.ts` apply 调用):订阅四个既有事件 + 拆卸。
83
83
  *
84
84
  * **为什么订阅四个而不是两个**(R↓):
85
- * · 前两个(`session-start` / `turn-stopping`)负责**语义**——"在不在跑";
85
+ * · 前两个(`agent/created` / `turn-stopping`)负责**语义**——"在不在跑";
86
86
  * · 后两个(`agent/disposed` / `session/disposed`)负责**生命周期**——
87
87
  * 载体没了必须清项,否则表按载体只增(本模块的头号风险)。
88
88
  *
@@ -55,8 +55,18 @@ interface HostWebServer {
55
55
  register?: (registration: unknown) => unknown;
56
56
  port?: number;
57
57
  }
58
+ /** `ctx.settings`(v0.1.7 起为 `SettingsForms`:按条目 Config 投影表单 + 页策略) */
58
59
  interface HostSettings {
59
- installSection?: (...args: unknown[]) => unknown;
60
+ /** 页策略(本插件用它关掉自动生成页;v0.1.7 前无此成员) */
61
+ configure?: (presentation: {
62
+ auto?: boolean;
63
+ }, owner?: unknown) => unknown;
64
+ /** 读各条目表单(含 live 值 / 用户层);保留可选读取,供诊断面取证 */
65
+ describe?: (options?: {
66
+ redactSecrets?: boolean;
67
+ }) => unknown;
68
+ /** 写一个条目的 config(宿主负责落 profile 文档 + 校验) */
69
+ update?: (ns: string, patch: object, expectedRevision?: number) => Promise<void>;
60
70
  }
61
71
  /** `ctx.web`(injected;v1.30.12:fetch provider 注册通道) */
62
72
  interface HostWeb {
@@ -55,14 +55,14 @@ interface HostEventContract {
55
55
  * 在名字写错/宿主改名时**编译失败**(v1.30.7,review F-04/F-05)。
56
56
  * 该 import 为 type-only,编译期擦除,不产生运行时依赖。
57
57
  */
58
- export declare const HOST_EVENT_NAMES: readonly ["agent/session-start", "agent/pre-step", "agent/turn-stopping", "agent/status", "agent/inbox/inserted", "agent/disposed", "session/event", "session/created", "session/disposed", "settings/updated", "tools/pre-execute", "tools/post-execute", "system-prompt/assemble"];
58
+ export declare const HOST_EVENT_NAMES: readonly ["agent/created", "agent/pre-step", "agent/turn-stopping", "agent/status", "agent/inbox/inserted", "agent/inbox/claimed", "agent/disposed", "session/event", "session/created", "session/disposed", "settings/document-updated", "tools/pre-execute", "tools/post-execute", "system-prompt/assemble"];
59
59
  type HostEventName = (typeof HOST_EVENT_NAMES)[number];
60
60
  /**
61
61
  * dsp 订阅的宿主事件清单(与 HOST_EVENT_NAMES 同源;impact/site 供 health 报告与文档)。
62
62
  */
63
63
  export declare const HOST_EVENTS: readonly HostEventContract[];
64
64
  /** dsp 被验证过的宿主版本范围(与 package.json peerDependencies 同源,单一真相源) */
65
- export declare const REQUIRED_HOST_RANGE = "^0.1.5-rc.2";
65
+ export declare const REQUIRED_HOST_RANGE = "^0.1.7-rc.1";
66
66
  interface HostContractIssue {
67
67
  id: string;
68
68
  kind: 'service' | 'member' | 'version';
package/lib/index.d.ts CHANGED
@@ -82,6 +82,26 @@ export interface Config {
82
82
  visionPatch?: {
83
83
  enabled?: boolean;
84
84
  };
85
+ /** F1 双端口网关总开关(面板层;部署层同义键 = `gateway.enabled`) */
86
+ gatewayEnabled?: boolean;
87
+ /** F2 超限重建总开关(面板层;部署层同义键 = `rebuild.enabled`) */
88
+ rebuildEnabled?: boolean;
89
+ /** F2 触发阈值(K token;面板层;部署层同义键 = `rebuild.thresholdK`) */
90
+ rebuildThresholdK?: number;
91
+ /** F4 Skiff 调试服务总开关(面板层;部署层同义键 = `skiff.enabled`) */
92
+ skiffEnabled?: boolean;
93
+ /** F4 Skiff 调试端口(面板层;部署层同义键 = `skiff.debugPort`) */
94
+ skiffDebugPort?: number;
95
+ /** F4c ACP HTTP 端点总开关(面板层;部署层同义键 = `acp.enabled`) */
96
+ acpEnabled?: boolean;
97
+ /** F4c ACP HTTP 端口(面板层;部署层同义键 = `acp.httpPort`) */
98
+ acpHttpPort?: number;
99
+ /** F4d 建议问答页总开关(面板层;无部署层同义键) */
100
+ publicAskEnabled?: boolean;
101
+ /** CRO(轨迹自编程唤起)总闸(面板层;无部署层同义键;缺省开) */
102
+ croEnabled?: boolean;
103
+ /** 无人值守代理回复总闸(面板层;无部署层同义键;缺省关) */
104
+ unattendedEnabled?: boolean;
85
105
  }
86
106
  export declare const Config: z<Config>;
87
107
  export declare function apply(ctx: Context, config: Config): void;