@shgroup/dsh-serenity-hooks 1.99.0 → 1.100.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/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.99.0",
3
+ "version": "1.100.0",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/index.js CHANGED
@@ -12635,8 +12635,6 @@ function createContainerAdminTool(ctx) {
12635
12635
  }
12636
12636
  //#endregion
12637
12637
  //#region src/seams/tool-visibility.ts
12638
- /** ACC 自己注册的工具名集合(退化档只发这些 —— 它们一定在册) */
12639
- const REGISTERED_TOOLS_SET = new Set(REGISTERED_TOOLS);
12640
12638
  /** 收集器:把各来源的 deny 合并成一个判决(**纯函数、无副作用**) */
12641
12639
  function collectVisibilityDecision(sources) {
12642
12640
  const deny = /* @__PURE__ */ new Set();
@@ -12668,12 +12666,13 @@ const applied = /* @__PURE__ */ new Map();
12668
12666
  * (旧集合是新集合的子集时叠不叠都看不出来,但**不是子集时**会残留 —— 必须撤。)
12669
12667
  * ③ 🔴 **未注册的名字不许炸掉整次发射**:宿主契约"unknown names … fail"。
12670
12668
  * 合并后集合里只要有一个不在册的名字,整次 restrict 就失败 ⇒ 六个机制**全都不生效**。
12671
- * ⇒ 策略 = **直接发 → 全量被拒才退到「只发 ACC 自己注册的那些」一档**,退档时
12672
- * 把被放弃的名字如实记账(进 `dropped`,由调用方决定要不要告警)。
12669
+ * ⇒ 策略 = **直接发 → 被拒时从宿主的错误文案里解析出它点名的未知名字 → 只剔那几个、其余重发一次**。
12670
+ * 被剔掉的名字进 `dropped`(调用方决定要不要告警)。
12673
12671
  * ⚠️ **不做 `tools.get` 预探活** —— 那个视图查不出 agent-plane 贡献的工具
12674
12672
  * (`bash` / `schedule_*`),会把**在册的名字误判成未注册**(见下方发射段的实测记录)。
12675
- * ⚠️ 这比旧行为**更保守**:旧行为是"某条 restrict 失败 ⇒ 那一格静默不生效",
12676
- * 新行为是"全量失败 ⇒ 至少 ACC 那一格照常生效" —— **等价或更好**,且总有日志。
12673
+ * 🔴 **也不按"谁注册的"降级** —— `REGISTERED_TOOLS` 只含 ACC 自己的 14 个,
12674
+ * 用它当"在册面"会**整类丢掉宿主工具**(2026-10-24 真机缺陷,见发射段)。
12675
+ * ⇒ 唯一合法的在册判据是**宿主自己的回答**:要么发一次成功,要么它说出哪些名字不认识。
12677
12676
  *
12678
12677
  * @returns `'unchanged'` / `'applied'` / `'failed'`(`failed` 时 `deny` 保持原样)
12679
12678
  */
@@ -12716,33 +12715,72 @@ function applyVisibilityDecision(agent, sessionId, decision) {
12716
12715
  dropped
12717
12716
  };
12718
12717
  } catch (firstError) {
12719
- const accOnly = names.filter((n) => REGISTERED_TOOLS_SET.has(n));
12720
- if (accOnly.length === 0 || accOnly.length === names.length) return {
12718
+ const message = errorText(firstError);
12719
+ const unknown = parseUnknownToolNames(message);
12720
+ if (unknown.length === 0) return {
12721
12721
  status: "failed",
12722
12722
  dropped,
12723
- error: firstError.message
12723
+ error: message
12724
+ };
12725
+ const unknownSet = new Set(unknown);
12726
+ const remaining = names.filter((n) => !unknownSet.has(n));
12727
+ dropped.push(...names.filter((n) => unknownSet.has(n)));
12728
+ if (remaining.length === 0) return {
12729
+ status: "failed",
12730
+ dropped,
12731
+ error: message
12724
12732
  };
12725
- const abandoned = names.filter((n) => !REGISTERED_TOOLS_SET.has(n));
12726
12733
  try {
12727
- const dispose = tools.restrict({ deny: accOnly });
12734
+ const dispose = tools.restrict({ deny: remaining });
12728
12735
  applied.set(sessionId, {
12729
- signature,
12730
- deny: accOnly,
12736
+ signature: decisionSignature({
12737
+ ...decision,
12738
+ deny: remaining
12739
+ }),
12740
+ deny: remaining,
12731
12741
  dispose
12732
12742
  });
12733
12743
  return {
12734
12744
  status: "applied",
12735
- dropped: abandoned
12745
+ dropped
12736
12746
  };
12737
12747
  } catch (secondError) {
12738
12748
  return {
12739
12749
  status: "failed",
12740
12750
  dropped,
12741
- error: secondError.message
12751
+ error: errorText(secondError)
12742
12752
  };
12743
12753
  }
12744
12754
  }
12745
12755
  }
12756
+ /** 取异常的可读文案(非 Error 也能取到 message;都没有则 String()) */
12757
+ function errorText(e) {
12758
+ if (e instanceof Error) return e.message;
12759
+ if (typeof e === "object" && e !== null && "message" in e && typeof e.message === "string") return e.message;
12760
+ return String(e);
12761
+ }
12762
+ /**
12763
+ * 从宿主的 `tools.restrict()` 错误文案里解析出**被点名的未知工具名**。
12764
+ *
12765
+ * 宿主逐字形态(`dsh-tools/lib/types/index.js` 的 `restrict()`):
12766
+ * ```
12767
+ * tools.restrict() names unknown global tool(s) "a", "b"; known global tools: x, y, z
12768
+ * ```
12769
+ * 单数时是 `tool`,复数时是 `tool(s)`——两种都匹配。
12770
+ *
12771
+ * 🔴 **只取分号之前那一段**:分号后是 `known global tools:` 清单,**不能**误当成未知名字。
12772
+ * 🔴 解析不出来就返回 `[]`(调用方据此走"如实失败、不记账"),**绝不猜**。
12773
+ */
12774
+ function parseUnknownToolNames(message) {
12775
+ const head = message.split(";")[0] ?? "";
12776
+ if (!/names unknown global tool/.test(head)) return [];
12777
+ const out = [];
12778
+ for (const m of head.matchAll(/"([^"]+)"/g)) {
12779
+ const name = m[1];
12780
+ if (name !== void 0 && name !== "") out.push(name);
12781
+ }
12782
+ return [...new Set(out)];
12783
+ }
12746
12784
  /** 会话销毁清理(防 per-会话 Map 无界增长;lifecycle 调用) */
12747
12785
  function forgetToolVisibility(sessionId) {
12748
12786
  const existing = applied.get(sessionId);
@@ -51,12 +51,13 @@ export declare function getToolVisibilityDiagnostics(): {
51
51
  * (旧集合是新集合的子集时叠不叠都看不出来,但**不是子集时**会残留 —— 必须撤。)
52
52
  * ③ 🔴 **未注册的名字不许炸掉整次发射**:宿主契约"unknown names … fail"。
53
53
  * 合并后集合里只要有一个不在册的名字,整次 restrict 就失败 ⇒ 六个机制**全都不生效**。
54
- * ⇒ 策略 = **直接发 → 全量被拒才退到「只发 ACC 自己注册的那些」一档**,退档时
55
- * 把被放弃的名字如实记账(进 `dropped`,由调用方决定要不要告警)。
54
+ * ⇒ 策略 = **直接发 → 被拒时从宿主的错误文案里解析出它点名的未知名字 → 只剔那几个、其余重发一次**。
55
+ * 被剔掉的名字进 `dropped`(调用方决定要不要告警)。
56
56
  * ⚠️ **不做 `tools.get` 预探活** —— 那个视图查不出 agent-plane 贡献的工具
57
57
  * (`bash` / `schedule_*`),会把**在册的名字误判成未注册**(见下方发射段的实测记录)。
58
- * ⚠️ 这比旧行为**更保守**:旧行为是"某条 restrict 失败 ⇒ 那一格静默不生效",
59
- * 新行为是"全量失败 ⇒ 至少 ACC 那一格照常生效" —— **等价或更好**,且总有日志。
58
+ * 🔴 **也不按"谁注册的"降级** —— `REGISTERED_TOOLS` 只含 ACC 自己的 14 个,
59
+ * 用它当"在册面"会**整类丢掉宿主工具**(2026-10-24 真机缺陷,见发射段)。
60
+ * ⇒ 唯一合法的在册判据是**宿主自己的回答**:要么发一次成功,要么它说出哪些名字不认识。
60
61
  *
61
62
  * @returns `'unchanged'` / `'applied'` / `'failed'`(`failed` 时 `deny` 保持原样)
62
63
  */
@@ -73,5 +74,18 @@ export declare function applyVisibilityDecision(agent: {
73
74
  dropped: string[];
74
75
  error?: string;
75
76
  };
77
+ /**
78
+ * 从宿主的 `tools.restrict()` 错误文案里解析出**被点名的未知工具名**。
79
+ *
80
+ * 宿主逐字形态(`dsh-tools/lib/types/index.js` 的 `restrict()`):
81
+ * ```
82
+ * tools.restrict() names unknown global tool(s) "a", "b"; known global tools: x, y, z
83
+ * ```
84
+ * 单数时是 `tool`,复数时是 `tool(s)`——两种都匹配。
85
+ *
86
+ * 🔴 **只取分号之前那一段**:分号后是 `known global tools:` 清单,**不能**误当成未知名字。
87
+ * 🔴 解析不出来就返回 `[]`(调用方据此走"如实失败、不记账"),**绝不猜**。
88
+ */
89
+ export declare function parseUnknownToolNames(message: string): string[];
76
90
  /** 会话销毁清理(防 per-会话 Map 无界增长;lifecycle 调用) */
77
91
  export declare function forgetToolVisibility(sessionId: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.99.0",
3
+ "version": "1.100.0",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档)。",
5
5
  "license": "MIT",
6
6
  "repository": {