@shgroup/dsh-serenity-hooks 1.47.0 → 1.47.2

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.47.0",
3
+ "version": "1.47.2",
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": {
@@ -94,7 +94,7 @@ export declare function declaresImage(input: unknown): boolean;
94
94
  */
95
95
  export declare function desiredInput(input: unknown): string[];
96
96
  interface DeepseekVisionPatchInput {
97
- /** `llm-pi-ai` 命名空间**解析后**的值(`settings.get` 结果;undefined = 命名空间未注册) */
97
+ /** `llm-pi-ai` 命名空间**解析后**的值(0.1.7 读面 `settings.describe()` 里按 `ns` 挑出的 `value`;undefined = 命名空间未注册) */
98
98
  resolved: unknown;
99
99
  /** 开关(CCC 配置 `visionPatch.enabled`);undefined ⇒ 按默认 true */
100
100
  enabled?: boolean;
package/lib/index.d.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * 配置:进程级 Config(cordis.yml 提供)+ 运行时读取 CCC 的 .opencode/serenity.json(规范位置,.dsh 回退)。
13
13
  */
14
- import type { Context } from 'cordis';
14
+ import type { Context, Volatile } from 'cordis';
15
15
  import z from '@deepseek-ai/schemastery';
16
16
  export declare const name = "dsh-serenity-hooks";
17
17
  /** 主动调用的服务;其余(agent 事件)随 harness 装配必然存在
@@ -83,25 +83,151 @@ export interface Config {
83
83
  enabled?: boolean;
84
84
  };
85
85
  /** F1 双端口网关总开关(面板层;部署层同义键 = `gateway.enabled`) */
86
- gatewayEnabled?: boolean;
86
+ gatewayEnabled?: Volatile<boolean | undefined>;
87
87
  /** F2 超限重建总开关(面板层;部署层同义键 = `rebuild.enabled`) */
88
- rebuildEnabled?: boolean;
88
+ rebuildEnabled?: Volatile<boolean | undefined>;
89
89
  /** F2 触发阈值(K token;面板层;部署层同义键 = `rebuild.thresholdK`) */
90
- rebuildThresholdK?: number;
90
+ rebuildThresholdK?: Volatile<number | undefined>;
91
91
  /** F4 Skiff 调试服务总开关(面板层;部署层同义键 = `skiff.enabled`) */
92
- skiffEnabled?: boolean;
92
+ skiffEnabled?: Volatile<boolean | undefined>;
93
93
  /** F4 Skiff 调试端口(面板层;部署层同义键 = `skiff.debugPort`) */
94
- skiffDebugPort?: number;
94
+ skiffDebugPort?: Volatile<number | undefined>;
95
95
  /** F4c ACP HTTP 端点总开关(面板层;部署层同义键 = `acp.enabled`) */
96
- acpEnabled?: boolean;
96
+ acpEnabled?: Volatile<boolean | undefined>;
97
97
  /** F4c ACP HTTP 端口(面板层;部署层同义键 = `acp.httpPort`) */
98
- acpHttpPort?: number;
98
+ acpHttpPort?: Volatile<number | undefined>;
99
99
  /** F4d 建议问答页总开关(面板层;无部署层同义键) */
100
- publicAskEnabled?: boolean;
100
+ publicAskEnabled?: Volatile<boolean | undefined>;
101
101
  /** CRO(轨迹自编程唤起)总闸(面板层;无部署层同义键;缺省开) */
102
- croEnabled?: boolean;
102
+ croEnabled?: Volatile<boolean | undefined>;
103
103
  /** 无人值守代理回复总闸(面板层;无部署层同义键;缺省关) */
104
- unattendedEnabled?: boolean;
104
+ unattendedEnabled?: Volatile<boolean | undefined>;
105
105
  }
106
- export declare const Config: z<Config>;
106
+ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
107
+ serenityConfigPaths: z<string[], string[], "defined">;
108
+ tools: z<boolean, boolean, "defined">;
109
+ guards: z<boolean, boolean, "defined">;
110
+ keeper: z<boolean, boolean, "defined">;
111
+ keeperThreshold: z<number, number, "defined">;
112
+ context: z<boolean, boolean, "defined">;
113
+ compactRetention: z<boolean, boolean, "defined">;
114
+ api: z<boolean, boolean, "defined">;
115
+ entrySkillMaxChars: z<number, number, "defined">;
116
+ env: z<boolean, boolean, "defined">;
117
+ opencodeSkills: z<boolean, boolean, "defined">;
118
+ gateway: z<Schemastery.ObjectS<NoInfer<{
119
+ enabled: z<boolean, boolean, "defined">;
120
+ }>>, Schemastery.ObjectT<NoInfer<{
121
+ enabled: z<boolean, boolean, "defined">;
122
+ }>>, "plain">;
123
+ rebuild: z<Schemastery.ObjectS<NoInfer<{
124
+ enabled: z<boolean, boolean, "defined">;
125
+ thresholdK: z<number, number, "defined">;
126
+ }>>, Schemastery.ObjectT<NoInfer<{
127
+ enabled: z<boolean, boolean, "defined">;
128
+ thresholdK: z<number, number, "defined">;
129
+ }>>, "plain">;
130
+ skiff: z<Schemastery.ObjectS<NoInfer<{
131
+ enabled: z<boolean, boolean, "defined">;
132
+ debugPort: z<number, number, "defined">;
133
+ }>>, Schemastery.ObjectT<NoInfer<{
134
+ enabled: z<boolean, boolean, "defined">;
135
+ debugPort: z<number, number, "defined">;
136
+ }>>, "plain">;
137
+ acp: z<Schemastery.ObjectS<NoInfer<{
138
+ enabled: z<boolean, boolean, "defined">;
139
+ httpPort: z<number, number, "defined">;
140
+ }>>, Schemastery.ObjectT<NoInfer<{
141
+ enabled: z<boolean, boolean, "defined">;
142
+ httpPort: z<number, number, "defined">;
143
+ }>>, "plain">;
144
+ webFetch: z<Schemastery.ObjectS<NoInfer<{
145
+ enabled: z<boolean, boolean, "defined">;
146
+ }>>, Schemastery.ObjectT<NoInfer<{
147
+ enabled: z<boolean, boolean, "defined">;
148
+ }>>, "plain">;
149
+ opencodeProvider: z<Schemastery.ObjectS<NoInfer<{
150
+ autoConfigure: z<boolean, boolean, "defined">;
151
+ }>>, Schemastery.ObjectT<NoInfer<{
152
+ autoConfigure: z<boolean, boolean, "defined">;
153
+ }>>, "plain">;
154
+ visionPatch: z<Schemastery.ObjectS<NoInfer<{
155
+ enabled: z<boolean, boolean, "defined">;
156
+ }>>, Schemastery.ObjectT<NoInfer<{
157
+ enabled: z<boolean, boolean, "defined">;
158
+ }>>, "plain">;
159
+ gatewayEnabled: z<boolean, boolean, "volatile">;
160
+ rebuildEnabled: z<boolean, boolean, "volatile">;
161
+ rebuildThresholdK: z<number, number, "volatile">;
162
+ skiffEnabled: z<boolean, boolean, "volatile">;
163
+ skiffDebugPort: z<number, number, "volatile">;
164
+ acpEnabled: z<boolean, boolean, "volatile">;
165
+ acpHttpPort: z<number, number, "volatile">;
166
+ publicAskEnabled: z<boolean, boolean, "volatile">;
167
+ croEnabled: z<boolean, boolean, "volatile">;
168
+ unattendedEnabled: z<boolean, boolean, "volatile">;
169
+ }>>, Schemastery.ObjectT<NoInfer<{
170
+ serenityConfigPaths: z<string[], string[], "defined">;
171
+ tools: z<boolean, boolean, "defined">;
172
+ guards: z<boolean, boolean, "defined">;
173
+ keeper: z<boolean, boolean, "defined">;
174
+ keeperThreshold: z<number, number, "defined">;
175
+ context: z<boolean, boolean, "defined">;
176
+ compactRetention: z<boolean, boolean, "defined">;
177
+ api: z<boolean, boolean, "defined">;
178
+ entrySkillMaxChars: z<number, number, "defined">;
179
+ env: z<boolean, boolean, "defined">;
180
+ opencodeSkills: z<boolean, boolean, "defined">;
181
+ gateway: z<Schemastery.ObjectS<NoInfer<{
182
+ enabled: z<boolean, boolean, "defined">;
183
+ }>>, Schemastery.ObjectT<NoInfer<{
184
+ enabled: z<boolean, boolean, "defined">;
185
+ }>>, "plain">;
186
+ rebuild: z<Schemastery.ObjectS<NoInfer<{
187
+ enabled: z<boolean, boolean, "defined">;
188
+ thresholdK: z<number, number, "defined">;
189
+ }>>, Schemastery.ObjectT<NoInfer<{
190
+ enabled: z<boolean, boolean, "defined">;
191
+ thresholdK: z<number, number, "defined">;
192
+ }>>, "plain">;
193
+ skiff: z<Schemastery.ObjectS<NoInfer<{
194
+ enabled: z<boolean, boolean, "defined">;
195
+ debugPort: z<number, number, "defined">;
196
+ }>>, Schemastery.ObjectT<NoInfer<{
197
+ enabled: z<boolean, boolean, "defined">;
198
+ debugPort: z<number, number, "defined">;
199
+ }>>, "plain">;
200
+ acp: z<Schemastery.ObjectS<NoInfer<{
201
+ enabled: z<boolean, boolean, "defined">;
202
+ httpPort: z<number, number, "defined">;
203
+ }>>, Schemastery.ObjectT<NoInfer<{
204
+ enabled: z<boolean, boolean, "defined">;
205
+ httpPort: z<number, number, "defined">;
206
+ }>>, "plain">;
207
+ webFetch: z<Schemastery.ObjectS<NoInfer<{
208
+ enabled: z<boolean, boolean, "defined">;
209
+ }>>, Schemastery.ObjectT<NoInfer<{
210
+ enabled: z<boolean, boolean, "defined">;
211
+ }>>, "plain">;
212
+ opencodeProvider: z<Schemastery.ObjectS<NoInfer<{
213
+ autoConfigure: z<boolean, boolean, "defined">;
214
+ }>>, Schemastery.ObjectT<NoInfer<{
215
+ autoConfigure: z<boolean, boolean, "defined">;
216
+ }>>, "plain">;
217
+ visionPatch: z<Schemastery.ObjectS<NoInfer<{
218
+ enabled: z<boolean, boolean, "defined">;
219
+ }>>, Schemastery.ObjectT<NoInfer<{
220
+ enabled: z<boolean, boolean, "defined">;
221
+ }>>, "plain">;
222
+ gatewayEnabled: z<boolean, boolean, "volatile">;
223
+ rebuildEnabled: z<boolean, boolean, "volatile">;
224
+ rebuildThresholdK: z<number, number, "volatile">;
225
+ skiffEnabled: z<boolean, boolean, "volatile">;
226
+ skiffDebugPort: z<number, number, "volatile">;
227
+ acpEnabled: z<boolean, boolean, "volatile">;
228
+ acpHttpPort: z<number, number, "volatile">;
229
+ publicAskEnabled: z<boolean, boolean, "volatile">;
230
+ croEnabled: z<boolean, boolean, "volatile">;
231
+ unattendedEnabled: z<boolean, boolean, "volatile">;
232
+ }>>, "plain">;
107
233
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -6,9 +6,9 @@ import { C as showSession, D as localFileStamp, E as isoLocal, S as setActiveSes
6
6
  import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, g as runLocalStore, h as readStore, i as matchWeixinRoute, l as weixinSessionIdFor, m as readGitTrack, n as extractWeixinText, o as readWeixinSettings, p as localstorePath, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-CWMRa36W.js";
7
7
  import { a as unregisterSkiffSession$1, i as skiffSessionSnapshot$1, n as skiffRoleFor$1, o as ACC_VERSION, r as skiffSessionInfo$1, t as registerSkiffSession$1 } from "./skiff-registry-BjIX_KD1.js";
8
8
  import { a as readSkiffRoles, c as roleMsmWhitelist, d as systemPromptSource, f as trajectorySubset, i as isSkiffSessionId, l as roleToolWhitelist, n as buildSkiffBasePrompt, o as resolveRoleSystemPrompt, r as createRolePromptReader, s as resolveSkiffKind } from "./skiff-role-KqQA6pkr.js";
9
- import { a as FACE_PORTS, c as MECHANISM_PORTS, i as ACP_HTTP_PORT, l as SKIFF_DEBUG_PORT, n as registerSettingsSection, o as GATEWAY_PORT, s as MAIN_WEB_PORT, t as readSimpleSettings, u as WEIXIN_SEND_PORT } from "./settings-section-DTJW4smh.js";
9
+ import { a as FACE_PORTS, c as MECHANISM_PORTS, i as ACP_HTTP_PORT, l as SKIFF_DEBUG_PORT, n as registerSettingsSection, o as GATEWAY_PORT, s as MAIN_WEB_PORT, t as readSimpleSettings, u as WEIXIN_SEND_PORT } from "./settings-section-Bq_d_3lb.js";
10
10
  import { r as sessionsRootDir, t as hasSessionLogById } from "./session-cleanup-BtkSmry8.js";
11
- import { a as registerDisposer, c as WAKE_CATCH_UP_MS, i as registerCroTurnTracking, l as addWake, n as sendToTrajectory, o as CRO_FILENAME, r as wakeSchedulerState, s as buildCroSnapshot, t as registerWakeScheduler, u as listWakes } from "./wake-scheduler-DdaRNofO.js";
11
+ import { a as registerDisposer, c as WAKE_CATCH_UP_MS, i as registerCroTurnTracking, l as addWake, n as sendToTrajectory, o as CRO_FILENAME, r as wakeSchedulerState, s as buildCroSnapshot, t as registerWakeScheduler, u as listWakes } from "./wake-scheduler-CPkYbPqu.js";
12
12
  import { a as markdownToPlainText, c as sendTyping, i as getUpdates, l as sniffImageExt, n as downloadMedia, o as sendFileMessage, r as getConfig, s as sendTextMessage, t as TypingStatus } from "./weixin-api-CtFYm53S.js";
13
13
  import z from "@deepseek-ai/schemastery";
14
14
  import { defineTool } from "@deepseek-ai/dsh-tools";
@@ -7779,7 +7779,7 @@ function registerStatusApi(ctx, opts = {}) {
7779
7779
  return;
7780
7780
  }
7781
7781
  const settings = readAdvancedSettings();
7782
- const { readSimpleSettings } = await import("./settings-section-DTJW4smh.js").then((n) => n.r);
7782
+ const { readSimpleSettings } = await import("./settings-section-Bq_d_3lb.js").then((n) => n.r);
7783
7783
  const simple = readSimpleSettings();
7784
7784
  const allowed = settings.publicAsk.allowed;
7785
7785
  const port = simple.acpHttpPort ?? ACP_HTTP_PORT;
@@ -8338,21 +8338,14 @@ const HOST_SERVICES = [
8338
8338
  id: "settings",
8339
8339
  name: "settings",
8340
8340
  access: "injected",
8341
- members: [
8342
- {
8343
- name: "installSection",
8344
- kind: "function"
8345
- },
8346
- {
8347
- name: "get",
8348
- kind: "function"
8349
- },
8350
- {
8351
- name: "update",
8352
- kind: "function"
8353
- }
8354
- ],
8355
- impact: "设置面板不安装 → 所有开关静默 no-op;opencode 路由自动配置失效",
8341
+ members: [{
8342
+ name: "describe",
8343
+ kind: "function"
8344
+ }, {
8345
+ name: "update",
8346
+ kind: "function"
8347
+ }],
8348
+ impact: "设置面板不装配 → 所有开关静默 no-op;opencode 路由自动配置与多模态补丁失效",
8356
8349
  required: true
8357
8350
  },
8358
8351
  {
@@ -14875,7 +14868,7 @@ function describeOpencodeAction(action) {
14875
14868
  */
14876
14869
  async function applyOpencodeAutoConfigOnce(ctx, env) {
14877
14870
  const settings = hostSettings(ctx);
14878
- if (settings?.get === void 0 || settings.update === void 0) {
14871
+ if (settings?.describe === void 0 || settings.update === void 0) {
14879
14872
  console.warn("[serenity-hooks] opencode 路由自动配置跳过:settings 服务不可用(未能读取/写入 llm-pi-ai 命名空间)");
14880
14873
  return {
14881
14874
  wrote: false,
@@ -14884,7 +14877,7 @@ async function applyOpencodeAutoConfigOnce(ctx, env) {
14884
14877
  }
14885
14878
  let resolved;
14886
14879
  try {
14887
- resolved = settings.get.call(settings, LLM_PI_AI_NAMESPACE);
14880
+ resolved = (settings.describe.call(settings) ?? []).find((f) => f.ns === LLM_PI_AI_NAMESPACE)?.value;
14888
14881
  } catch (error) {
14889
14882
  console.warn(`[serenity-hooks] opencode 路由自动配置跳过:读取 ${LLM_PI_AI_NAMESPACE} 失败: ${String(error?.message ?? error)}`);
14890
14883
  return {
@@ -15121,7 +15114,7 @@ function planDeepseekVisionPatch(input) {
15121
15114
  */
15122
15115
  async function applyDeepseekVisionPatchOnce(ctx, enabled) {
15123
15116
  const settings = hostSettings(ctx);
15124
- if (settings?.get === void 0 || settings.update === void 0) {
15117
+ if (settings?.describe === void 0 || settings.update === void 0) {
15125
15118
  console.warn("[serenity-hooks] DeepSeek 多模态补丁跳过:settings 服务不可用(未能读写 llm-pi-ai 命名空间)");
15126
15119
  return {
15127
15120
  wrote: false,
@@ -15130,7 +15123,7 @@ async function applyDeepseekVisionPatchOnce(ctx, enabled) {
15130
15123
  }
15131
15124
  let resolved;
15132
15125
  try {
15133
- resolved = settings.get.call(settings, LLM_PI_AI_NAMESPACE);
15126
+ resolved = (settings.describe.call(settings) ?? []).find((f) => f.ns === LLM_PI_AI_NAMESPACE)?.value;
15134
15127
  } catch (error) {
15135
15128
  console.warn(`[serenity-hooks] DeepSeek 多模态补丁跳过:读取 ${LLM_PI_AI_NAMESPACE} 失败: ${String(error?.message ?? error)}`);
15136
15129
  return {
@@ -16088,16 +16081,16 @@ const Config = z.object({
16088
16081
  webFetch: z.object({ enabled: z.boolean().default(true) }),
16089
16082
  opencodeProvider: z.object({ autoConfigure: z.boolean().default(true) }),
16090
16083
  visionPatch: z.object({ enabled: z.boolean().default(true) }),
16091
- gatewayEnabled: z.boolean(),
16092
- rebuildEnabled: z.boolean(),
16093
- rebuildThresholdK: z.number().min(50).max(4e3),
16094
- skiffEnabled: z.boolean(),
16095
- skiffDebugPort: z.number().min(1024).max(65535),
16096
- acpEnabled: z.boolean(),
16097
- acpHttpPort: z.number().min(1024).max(65535),
16098
- publicAskEnabled: z.boolean(),
16099
- croEnabled: z.boolean(),
16100
- unattendedEnabled: z.boolean()
16084
+ gatewayEnabled: z.boolean().volatile(),
16085
+ rebuildEnabled: z.boolean().volatile(),
16086
+ rebuildThresholdK: z.number().min(50).max(4e3).volatile(),
16087
+ skiffEnabled: z.boolean().volatile(),
16088
+ skiffDebugPort: z.number().min(1024).max(65535).volatile(),
16089
+ acpEnabled: z.boolean().volatile(),
16090
+ acpHttpPort: z.number().min(1024).max(65535).volatile(),
16091
+ publicAskEnabled: z.boolean().volatile(),
16092
+ croEnabled: z.boolean().volatile(),
16093
+ unattendedEnabled: z.boolean().volatile()
16101
16094
  });
16102
16095
  function apply(ctx, config) {
16103
16096
  try {
@@ -69,7 +69,7 @@ export declare const OPENCODE_API_KEY_ENV = "OPENCODE_API_KEY";
69
69
  */
70
70
  export declare function opencodeRouteHeaders(): Record<string, string>;
71
71
  interface OpencodeAutoConfigInput {
72
- /** `llm-pi-ai` 命名空间**解析后**的值(`settings.get` 结果;undefined = 命名空间未注册) */
72
+ /** `llm-pi-ai` 命名空间**解析后**的值(0.1.7 读面 `settings.describe()` 里按 `ns` 挑出的 `value`;undefined = 命名空间未注册) */
73
73
  resolved: unknown;
74
74
  /** 进程环境(注入以便测试) */
75
75
  env: Readonly<Record<string, string | undefined>>;
@@ -63,7 +63,8 @@ var settings_section_exports = /* @__PURE__ */ __exportAll({
63
63
  defaultSimpleSettings: () => defaultSimpleSettings,
64
64
  readSimpleSettings: () => readSimpleSettings,
65
65
  registerSettingsSection: () => registerSettingsSection,
66
- simpleSettingsFromConfig: () => simpleSettingsFromConfig
66
+ simpleSettingsFromConfig: () => simpleSettingsFromConfig,
67
+ unwrapVolatile: () => unwrapVolatile
67
68
  });
68
69
  /** 进程级默认(无 Config 可读时的兜底;与 Config 各字段的缺省一致) */
69
70
  function defaultSimpleSettings() {
@@ -81,6 +82,30 @@ function defaultSimpleSettings() {
81
82
  };
82
83
  }
83
84
  /**
85
+ * volatile 字段的**结构化解包**(v1.47.2;本文件读取语义的第一道)。
86
+ *
87
+ * ## 为什么必须有它(两个事实的合成,缺一即错)
88
+ * ① **0.1.7 的面板层字段必须标 `.volatile()`** —— 否则本插件命名空间整个不进宿主的
89
+ * `settings.describe()`(`volatileForm()` 只认 `meta.volatile`)⇒ **设置面板那一节消失**。
90
+ * ② **标了之后,运行期拿到的是引用包装,不是裸值** —— schemastery 的 `Schema.resolve()` 对
91
+ * `meta.volatile` **无条件**包 `createVolatile(value)`,且该分支排在"缺省值回退"那一段
92
+ * **之前** ⇒ **"用户没设过"的字段也是恒真对象**(`.get()` 返回 `undefined`),**不是 `undefined`**。
93
+ *
94
+ * ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 / Skiff / ACP 会被
95
+ * **无意打开**(比"面板不显示"更坏:静默改变对外暴露面)。
96
+ *
97
+ * ## 为什么用结构判定
98
+ * 可观察形状就是"有 `get()`"(cosmokit 的 `isVolatile` 也这么判);本模块不引它的运行时依赖。
99
+ *
100
+ * @param value - volatile 引用 | 裸值 | undefined
101
+ * @returns 解包后的裸值(引用 ⇒ `.get()`;裸值原样;缺省 ⇒ `undefined`)
102
+ */
103
+ function unwrapVolatile(value) {
104
+ if (value === void 0 || value === null) return void 0;
105
+ if (typeof value.get === "function") return value.get();
106
+ return value;
107
+ }
108
+ /**
84
109
  * Config(宿主 Loader 校验后的解析值)→ 扁平简单配置。**本文件唯一的读取语义**。
85
110
  *
86
111
  * 优先级:**面板层(扁平键)> 部署层(嵌套段)> 内建缺省**。
@@ -90,16 +115,16 @@ function defaultSimpleSettings() {
90
115
  function simpleSettingsFromConfig(config) {
91
116
  const d = defaultSimpleSettings();
92
117
  return {
93
- gatewayEnabled: config.gatewayEnabled ?? config.gateway?.enabled ?? d.gatewayEnabled,
94
- rebuildEnabled: config.rebuildEnabled ?? config.rebuild?.enabled ?? d.rebuildEnabled,
95
- rebuildThresholdK: config.rebuildThresholdK ?? config.rebuild?.thresholdK ?? d.rebuildThresholdK,
96
- skiffEnabled: config.skiffEnabled ?? config.skiff?.enabled ?? d.skiffEnabled,
97
- skiffDebugPort: config.skiffDebugPort ?? config.skiff?.debugPort ?? d.skiffDebugPort,
98
- acpEnabled: config.acpEnabled ?? config.acp?.enabled ?? d.acpEnabled,
99
- acpHttpPort: config.acpHttpPort ?? config.acp?.httpPort ?? d.acpHttpPort,
100
- publicAskEnabled: config.publicAskEnabled ?? d.publicAskEnabled,
101
- croEnabled: config.croEnabled ?? d.croEnabled,
102
- unattendedEnabled: config.unattendedEnabled ?? d.unattendedEnabled
118
+ gatewayEnabled: unwrapVolatile(config.gatewayEnabled) ?? config.gateway?.enabled ?? d.gatewayEnabled,
119
+ rebuildEnabled: unwrapVolatile(config.rebuildEnabled) ?? config.rebuild?.enabled ?? d.rebuildEnabled,
120
+ rebuildThresholdK: unwrapVolatile(config.rebuildThresholdK) ?? config.rebuild?.thresholdK ?? d.rebuildThresholdK,
121
+ skiffEnabled: unwrapVolatile(config.skiffEnabled) ?? config.skiff?.enabled ?? d.skiffEnabled,
122
+ skiffDebugPort: unwrapVolatile(config.skiffDebugPort) ?? config.skiff?.debugPort ?? d.skiffDebugPort,
123
+ acpEnabled: unwrapVolatile(config.acpEnabled) ?? config.acp?.enabled ?? d.acpEnabled,
124
+ acpHttpPort: unwrapVolatile(config.acpHttpPort) ?? config.acp?.httpPort ?? d.acpHttpPort,
125
+ publicAskEnabled: unwrapVolatile(config.publicAskEnabled) ?? d.publicAskEnabled,
126
+ croEnabled: unwrapVolatile(config.croEnabled) ?? d.croEnabled,
127
+ unattendedEnabled: unwrapVolatile(config.unattendedEnabled) ?? d.unattendedEnabled
103
128
  };
104
129
  }
105
130
  /** 运行时源(`registerSettingsSection` 装配:读本插件 fiber 的**当前** Config) */
@@ -33,7 +33,16 @@
33
33
  * 🔴 面板层的键**故意不带 schemastery 默认值** —— 有默认值就永远非 `undefined`,
34
34
  * 部署层会被无声压死("未设置"必须可观测)。
35
35
  */
36
- import type { Context } from 'cordis';
36
+ import type { Context, Volatile } from 'cordis';
37
+ /**
38
+ * 面板层布尔键的**两种来法**(v1.47.2):
39
+ * - 宿主 Loader 解析后的 Config ⇒ `Volatile<boolean | undefined>` **引用包装**(0.1.7 起面板字段必须 volatile)
40
+ * - 手写 Config / 部署 bundle / 单测 ⇒ 裸布尔
41
+ * ⇒ 本层两种都收,**读取一律先 `unwrapVolatile()`**(否则包装对象恒真 ⇒ 开关被无意打开)。
42
+ */
43
+ type PanelBoolean = boolean | Volatile<boolean | undefined>;
44
+ /** 面板层数值键:同 `PanelBoolean`(两种来法) */
45
+ type PanelNumber = number | Volatile<number | undefined>;
37
46
  /** 插件 Config 的简单配置片段(index.ts 的 Config 子集;面板层 + 部署层两种拼写) */
38
47
  interface SimpleConfigFragment {
39
48
  gateway?: {
@@ -53,16 +62,16 @@ interface SimpleConfigFragment {
53
62
  enabled?: boolean;
54
63
  httpPort?: number;
55
64
  };
56
- gatewayEnabled?: boolean;
57
- rebuildEnabled?: boolean;
58
- rebuildThresholdK?: number;
59
- skiffEnabled?: boolean;
60
- skiffDebugPort?: number;
61
- acpEnabled?: boolean;
62
- acpHttpPort?: number;
63
- publicAskEnabled?: boolean;
64
- croEnabled?: boolean;
65
- unattendedEnabled?: boolean;
65
+ gatewayEnabled?: PanelBoolean;
66
+ rebuildEnabled?: PanelBoolean;
67
+ rebuildThresholdK?: PanelNumber;
68
+ skiffEnabled?: PanelBoolean;
69
+ skiffDebugPort?: PanelNumber;
70
+ acpEnabled?: PanelBoolean;
71
+ acpHttpPort?: PanelNumber;
72
+ publicAskEnabled?: PanelBoolean;
73
+ croEnabled?: PanelBoolean;
74
+ unattendedEnabled?: PanelBoolean;
66
75
  }
67
76
  /**
68
77
  * 设置面板命名空间 = **profile 条目 id**(v0.1.7 起;与旧 `settings.yaml` 段名逐字相同)。
@@ -107,6 +116,30 @@ interface SerenitySimpleSettings {
107
116
  }
108
117
  /** 进程级默认(无 Config 可读时的兜底;与 Config 各字段的缺省一致) */
109
118
  export declare function defaultSimpleSettings(): SerenitySimpleSettings;
119
+ /** volatile 引用的**可观察形状**(结构判定用;刻意不引 cosmokit —— 它是宿主内部包,不是我们的 peerDep) */
120
+ interface VolatileRef<T> {
121
+ get(): T;
122
+ }
123
+ /**
124
+ * volatile 字段的**结构化解包**(v1.47.2;本文件读取语义的第一道)。
125
+ *
126
+ * ## 为什么必须有它(两个事实的合成,缺一即错)
127
+ * ① **0.1.7 的面板层字段必须标 `.volatile()`** —— 否则本插件命名空间整个不进宿主的
128
+ * `settings.describe()`(`volatileForm()` 只认 `meta.volatile`)⇒ **设置面板那一节消失**。
129
+ * ② **标了之后,运行期拿到的是引用包装,不是裸值** —— schemastery 的 `Schema.resolve()` 对
130
+ * `meta.volatile` **无条件**包 `createVolatile(value)`,且该分支排在"缺省值回退"那一段
131
+ * **之前** ⇒ **"用户没设过"的字段也是恒真对象**(`.get()` 返回 `undefined`),**不是 `undefined`**。
132
+ *
133
+ * ⇒ 若这里不解包,下面十行的 `??` 链会**短路在这层包装上** ⇒ 网关 / Skiff / ACP 会被
134
+ * **无意打开**(比"面板不显示"更坏:静默改变对外暴露面)。
135
+ *
136
+ * ## 为什么用结构判定
137
+ * 可观察形状就是"有 `get()`"(cosmokit 的 `isVolatile` 也这么判);本模块不引它的运行时依赖。
138
+ *
139
+ * @param value - volatile 引用 | 裸值 | undefined
140
+ * @returns 解包后的裸值(引用 ⇒ `.get()`;裸值原样;缺省 ⇒ `undefined`)
141
+ */
142
+ export declare function unwrapVolatile<T>(value: T | VolatileRef<T> | undefined): T | undefined;
110
143
  /**
111
144
  * Config(宿主 Loader 校验后的解析值)→ 扁平简单配置。**本文件唯一的读取语义**。
112
145
  *
@@ -2,7 +2,7 @@ import { r as hostService, t as hostAgents } from "./access-fiehjxV6.js";
2
2
  import { g as resolveInside } from "./ccc-DG4Oc7I1.js";
3
3
  import { o as listCccs } from "./ccc-roots-FxWXuZPw.js";
4
4
  import { E as isoLocal, O as localHuman, i as listBoundSessionIds, k as localIdStamp, m as findSession, n as PLUGIN_SOURCE, x as sessionsRoot } from "./message-source-Boc-AbHs.js";
5
- import { t as readSimpleSettings } from "./settings-section-DTJW4smh.js";
5
+ import { t as readSimpleSettings } from "./settings-section-Bq_d_3lb.js";
6
6
  import { basename, dirname, join } from "node:path";
7
7
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
8
8
  import { spawn } from "node:child_process";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.47.0",
3
+ "version": "1.47.2",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:11 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/im-bridge/acc-diag)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1。",
5
5
  "license": "MIT",
6
6
  "repository": {