@shgroup/dsh-serenity-hooks 1.69.3 → 1.69.4
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 +1 -1
- package/lib/index.js +87 -33
- package/lib/seams/bootstrap.d.ts +48 -16
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.69.
|
|
3
|
+
"version": "1.69.4",
|
|
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 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/官方 ACP 面/认知轨迹唤醒注册表。适配 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
|
@@ -13909,6 +13909,13 @@ const DEFAULT_COMPACTION_TOOLS = [
|
|
|
13909
13909
|
*/
|
|
13910
13910
|
const DEFAULT_ANCHOR_MESSAGES = ["You are the operator agent of Serenity — a cognitive container governed by the Abstract Cognitive Container (ACC) protocol.\nWe operate under the Explicit Abstraction Principle (EAP): the functional value of a thought equals its external reconstructability — every output we produce must be explicit (E↑), reconstructable (R↓), and stable (S↑).\nThe personal pronoun is us/we.\nWe anchor first, then act: the abstract layer precedes the concrete.\nPlease simply reply \"acknowledge\" — no action needed.", "Before we proceed, align on how we work:\n1. We read before we write — every decision grounds in what already exists in the container.\n2. Every output records its reasoning (R↓): decisions carry reasons and alternatives.\n3. We never jump levels — abstract layer first, then specifics.\n4. Every artifact we create is a durable cognitive anchor for the work that follows.\n5. We keep the container's state coherent (SESSION.md) as we advance.\nPlease simply reply \"acknowledge\" — no action needed."];
|
|
13911
13911
|
/**
|
|
13912
|
+
* 锚定交换里「模型那半」的回复文本。
|
|
13913
|
+
*
|
|
13914
|
+
* 只写一个词:锚定话语明确要求 `reply "acknowledge" — no action needed`,
|
|
13915
|
+
* 伪造一条更长的回复反而与提示词不符(模型会据历史学样)。
|
|
13916
|
+
*/
|
|
13917
|
+
const ANCHOR_ACK_TEXT = "acknowledge";
|
|
13918
|
+
/**
|
|
13912
13919
|
* 会话里是否已有模型回复(= 已经不是首轮)。
|
|
13913
13920
|
*
|
|
13914
13921
|
* 🔴 这是锚定的**形状判据**(v1.62):锚定只能在**首轮**造。
|
|
@@ -13924,14 +13931,24 @@ function hasAssistantHistory(session) {
|
|
|
13924
13931
|
*
|
|
13925
13932
|
* ## 🔴 两条硬约束(宿主 v4 格式校验器,实测逐条踩过)
|
|
13926
13933
|
*
|
|
13927
|
-
* **约束 A —
|
|
13928
|
-
*
|
|
13929
|
-
*
|
|
13930
|
-
*
|
|
13931
|
-
*
|
|
13932
|
-
*
|
|
13933
|
-
*
|
|
13934
|
-
*
|
|
13934
|
+
* **约束 A — `system/message` 绝不能代写;`assistant/message` 可以,但必须落在已打开的 turn/step 内**。
|
|
13935
|
+
*
|
|
13936
|
+
* 🔴 **D140 的理由已由本文件 `tests/anchor-history-shape.test.ts` 当场更正**(探针直调宿主
|
|
13937
|
+
* `assertReleasedV4Relationships()`,不靠读源码推断):
|
|
13938
|
+
* 原记「`system/message` 与 `assistant/message` 都在 `STEP_EVENT_TYPES` 里」—— **只对前半**。
|
|
13939
|
+
* 实测 `STEP_EVENT_TYPES` = `{ system/message, developer/message, assistant/attempt }`,
|
|
13940
|
+
* **`assistant/message` 不在其中**。它仍要过 `requireStep`,但走的是另一条路
|
|
13941
|
+
* (`Relationships.accept()` → `case "assistant/message"` → `tool()` → `requireStep`)。
|
|
13942
|
+
*
|
|
13943
|
+
* ⇒ **结论保留、理由作废**:`assistant/message` 在**该 system/message 之前**写是非法的
|
|
13944
|
+
* (那时 turn/step 尚未打开);而在**之后**写是**合法的**(那时两者都已打开)。
|
|
13945
|
+
* 探针三态实测:A(仅 user)✅ / **B(user + assistant 交替,turn/step 匹配)✅** /
|
|
13946
|
+
* C(assistant 落在未打开的 turn/step)❌ `does not match an open turn and step`。
|
|
13947
|
+
*
|
|
13948
|
+
* ⚠️ 症状(不许省这条):写通道不校验 ⇒ **首轮看着正常**;**重新进入**时重放才抛
|
|
13949
|
+
* `does not match an open turn and step` ⇒ 会话 corrupt、界面空白。
|
|
13950
|
+
* ⇒ **v1.60 那版「先写占位 system 等宿主 replace」的整套方案仍然作废**
|
|
13951
|
+
* (它写的是 `system/message`,那**确实**在 `STEP_EVENT_TYPES` 里)。
|
|
13935
13952
|
*
|
|
13936
13953
|
* **约束 B — 必须写在宿主提交 `system/message` 之后**:格式层有 `protectedHead`
|
|
13937
13954
|
* 机制 —— `foldSurface()` 里 `system/message` 若在 **surface 非空且尚无 protectedHead**
|
|
@@ -13944,38 +13961,69 @@ function hasAssistantHistory(session) {
|
|
|
13944
13961
|
* ⇒ 因此调用时机 = **`session/event` 里看到该会话第一条 `system/message` 之后**(见下方注册处),
|
|
13945
13962
|
* 而不是 `agent/inbox/inserted`(那太早)。
|
|
13946
13963
|
*
|
|
13947
|
-
* ##
|
|
13964
|
+
* ## 为什么必须成对(user + assistant ack)—— v1.69.4 修正
|
|
13965
|
+
*
|
|
13966
|
+
* 只造 user 的那一版在**界面上读不成对话**:两条"用户"消息连排在模型回复之前,
|
|
13967
|
+
* 看起来像用户自己连说了两句协议、而模型没理。owner 2026-10-19 报的两点
|
|
13968
|
+
* (「助手的回应也需要造出来」+「用户的怎么跑到第一个了」)其实是**同一个缺陷的两面**。
|
|
13969
|
+
* ⇒ 造**成对交换**:每条锚定话语后面跟一条 `acknowledge` 回复。
|
|
13948
13970
|
*
|
|
13949
|
-
*
|
|
13950
|
-
*
|
|
13951
|
-
*
|
|
13971
|
+
* 🔴 **前提条件**:`assistant/message` 必须落在**已打开**的 turn/step 内
|
|
13972
|
+
* (探针 S3/S4 实测:未打开/已关闭都抛 `does not match an open turn and step`)。
|
|
13973
|
+
* 本函数由 `session/event` 的 `system/message` 分支触发,而**那条事件自己就带着
|
|
13974
|
+
* `data.turn` / `data.step`** —— 且实测此刻 turn/step 确实处于打开态
|
|
13975
|
+
* (真机日志:`step/start` → `system/message` → 锚定写入 → `step/end`)。
|
|
13976
|
+
* ⇒ turn/step **从触发事件透传进来**,不自己猜。
|
|
13977
|
+
*
|
|
13978
|
+
* ⚠️ **拿不到 turn/step 时退回只造 user**(不抛、不阻断):宁可少伪造一条 ack,
|
|
13979
|
+
* 也绝不在非法坐标上写 `assistant/message` —— 那会让会话在重放时 corrupt。
|
|
13952
13980
|
*
|
|
13953
13981
|
* ## 判据(改这个函数前后都必须跑)
|
|
13954
13982
|
*
|
|
13955
|
-
* ①
|
|
13956
|
-
* ②
|
|
13983
|
+
* ① 形状判据 = `tests/anchor-history-shape.test.ts`(直调宿主校验器,正控 + 两种负控);
|
|
13984
|
+
* ② 单测钉事件序列(user/assistant 交替、turn/step 逐条匹配);
|
|
13985
|
+
* ③ **容器内真跑一轮 → 重启宿主 → 重新进入该会话** —— 只有这条路能证明日志合法
|
|
13957
13986
|
* (前两次修复都是"单测全绿 + 首轮正常"却仍然 corrupt)。
|
|
13958
|
-
* ③ 直接调宿主自己的 `assertReleasedV4Relationships()` 判形状,**不要自写复刻**。
|
|
13959
13987
|
*
|
|
13960
13988
|
* @param session 会话(需有 append)
|
|
13961
13989
|
* @param anchorMessages 锚定话语序列(缺省协议固有两条)
|
|
13990
|
+
* @param slot 触发本轮 `system/message` 所在的 turn/step(缺省或非法 ⇒ 只造 user)
|
|
13962
13991
|
* @returns 是否成功写入
|
|
13963
13992
|
*/
|
|
13964
|
-
function appendAnchorHistory(session, anchorMessages = DEFAULT_ANCHOR_MESSAGES) {
|
|
13993
|
+
function appendAnchorHistory(session, anchorMessages = DEFAULT_ANCHOR_MESSAGES, slot) {
|
|
13965
13994
|
const target = session;
|
|
13966
13995
|
if (typeof target?.append !== "function") return false;
|
|
13967
13996
|
const append = target.append.bind(target);
|
|
13968
13997
|
const rid = (prefix) => `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
13998
|
+
const turn = typeof slot?.turn === "number" ? slot.turn : 0;
|
|
13999
|
+
const step = typeof slot?.step === "number" ? slot.step : 0;
|
|
14000
|
+
const canPlaceAck = turn > 0 && step > 0;
|
|
13969
14001
|
try {
|
|
13970
|
-
for (let i = 0; i < anchorMessages.length; i++)
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
14002
|
+
for (let i = 0; i < anchorMessages.length; i++) {
|
|
14003
|
+
append("user/message", {
|
|
14004
|
+
id: rid(`anchor-user-${i + 1}`),
|
|
14005
|
+
role: "user",
|
|
14006
|
+
content: [{
|
|
14007
|
+
type: "text",
|
|
14008
|
+
text: anchorMessages[i]
|
|
14009
|
+
}],
|
|
14010
|
+
source: { kind: "user" }
|
|
14011
|
+
}, { surfaceOp: "append" });
|
|
14012
|
+
if (!canPlaceAck) continue;
|
|
14013
|
+
append("assistant/message", {
|
|
14014
|
+
turn,
|
|
14015
|
+
step,
|
|
14016
|
+
message: {
|
|
14017
|
+
id: rid(`anchor-ack-${i + 1}`),
|
|
14018
|
+
role: "assistant",
|
|
14019
|
+
content: [{
|
|
14020
|
+
type: "text",
|
|
14021
|
+
text: ANCHOR_ACK_TEXT
|
|
14022
|
+
}],
|
|
14023
|
+
source: { kind: "model" }
|
|
14024
|
+
}
|
|
14025
|
+
}, { surfaceOp: "append" });
|
|
14026
|
+
}
|
|
13979
14027
|
return anchorMessages.length > 0;
|
|
13980
14028
|
} catch {
|
|
13981
14029
|
return false;
|
|
@@ -14152,24 +14200,30 @@ function registerBootstrap(ctx) {
|
|
|
14152
14200
|
const root = cccRootForCwd(cwd);
|
|
14153
14201
|
if (!root) return;
|
|
14154
14202
|
trackerFor(root).observe(session, event);
|
|
14155
|
-
if (event.type === "system/message")
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14203
|
+
if (event.type === "system/message") {
|
|
14204
|
+
const slot = event.data;
|
|
14205
|
+
setTimeout(() => {
|
|
14206
|
+
try {
|
|
14207
|
+
maybeAnchor(session, root, slot);
|
|
14208
|
+
} catch {}
|
|
14209
|
+
}, 0);
|
|
14210
|
+
}
|
|
14160
14211
|
} catch {}
|
|
14161
14212
|
});
|
|
14162
14213
|
/** 造锚定历史(幂等;防重靠 `anchoredSessions`)。 */
|
|
14163
|
-
const maybeAnchor = (session, _root) => {
|
|
14214
|
+
const maybeAnchor = (session, _root, slot) => {
|
|
14164
14215
|
const s = session;
|
|
14165
14216
|
s?.header?.delegationDepth;
|
|
14166
14217
|
const sid = typeof s?.id === "string" ? s.id : void 0;
|
|
14167
14218
|
if (sid !== void 0 && anchoredSessions.has(sid)) return;
|
|
14168
14219
|
if (sid !== void 0 && (sid.startsWith("cheap-worker-") || isSkiffSessionId(sid))) return;
|
|
14169
14220
|
if (hasAssistantHistory(session)) return;
|
|
14170
|
-
const wrote = appendAnchorHistory(session, SETTINGS.anchorMessages);
|
|
14221
|
+
const wrote = appendAnchorHistory(session, SETTINGS.anchorMessages, slot);
|
|
14171
14222
|
if (wrote && sid !== void 0) anchoredSessions.add(sid);
|
|
14172
|
-
if (wrote)
|
|
14223
|
+
if (wrote) {
|
|
14224
|
+
const paired = typeof slot?.turn === "number" && typeof slot?.step === "number" && slot.turn > 0 && slot.step > 0;
|
|
14225
|
+
warnOnce(`anchor history fabricated: ${SETTINGS.anchorMessages.length} 条 user 消息` + (paired ? ` + ${SETTINGS.anchorMessages.length} 条 ack(turn ${String(slot.turn)} / step ${String(slot.step)})` : "(无打开坐标 ⇒ 未造 ack)") + "(不再真跑)");
|
|
14226
|
+
}
|
|
14173
14227
|
};
|
|
14174
14228
|
ctx.on("agent/inbox/inserted", ({ agent, message }) => {
|
|
14175
14229
|
try {
|
package/lib/seams/bootstrap.d.ts
CHANGED
|
@@ -50,6 +50,13 @@ export declare const DEFAULT_COMPACTION_TOOLS: string[];
|
|
|
50
50
|
* 文本 = 原 home-serenity CCC 配置(anchorMessages),随零配置化固化进代码。
|
|
51
51
|
*/
|
|
52
52
|
export declare const DEFAULT_ANCHOR_MESSAGES: string[];
|
|
53
|
+
/**
|
|
54
|
+
* 锚定交换里「模型那半」的回复文本。
|
|
55
|
+
*
|
|
56
|
+
* 只写一个词:锚定话语明确要求 `reply "acknowledge" — no action needed`,
|
|
57
|
+
* 伪造一条更长的回复反而与提示词不符(模型会据历史学样)。
|
|
58
|
+
*/
|
|
59
|
+
export declare const ANCHOR_ACK_TEXT = "acknowledge";
|
|
53
60
|
/**
|
|
54
61
|
* 根会话锚定重入判定(v1.28.1 提取导出以便单测):会话是否已有真实用户消息。
|
|
55
62
|
* resume/续跑的会话已有对话历史 → **不重锚**(first-anchor 只注入一次)。
|
|
@@ -73,14 +80,24 @@ export declare function hasAssistantHistory(session: unknown): boolean;
|
|
|
73
80
|
*
|
|
74
81
|
* ## 🔴 两条硬约束(宿主 v4 格式校验器,实测逐条踩过)
|
|
75
82
|
*
|
|
76
|
-
* **约束 A —
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
83
|
+
* **约束 A — `system/message` 绝不能代写;`assistant/message` 可以,但必须落在已打开的 turn/step 内**。
|
|
84
|
+
*
|
|
85
|
+
* 🔴 **D140 的理由已由本文件 `tests/anchor-history-shape.test.ts` 当场更正**(探针直调宿主
|
|
86
|
+
* `assertReleasedV4Relationships()`,不靠读源码推断):
|
|
87
|
+
* 原记「`system/message` 与 `assistant/message` 都在 `STEP_EVENT_TYPES` 里」—— **只对前半**。
|
|
88
|
+
* 实测 `STEP_EVENT_TYPES` = `{ system/message, developer/message, assistant/attempt }`,
|
|
89
|
+
* **`assistant/message` 不在其中**。它仍要过 `requireStep`,但走的是另一条路
|
|
90
|
+
* (`Relationships.accept()` → `case "assistant/message"` → `tool()` → `requireStep`)。
|
|
91
|
+
*
|
|
92
|
+
* ⇒ **结论保留、理由作废**:`assistant/message` 在**该 system/message 之前**写是非法的
|
|
93
|
+
* (那时 turn/step 尚未打开);而在**之后**写是**合法的**(那时两者都已打开)。
|
|
94
|
+
* 探针三态实测:A(仅 user)✅ / **B(user + assistant 交替,turn/step 匹配)✅** /
|
|
95
|
+
* C(assistant 落在未打开的 turn/step)❌ `does not match an open turn and step`。
|
|
96
|
+
*
|
|
97
|
+
* ⚠️ 症状(不许省这条):写通道不校验 ⇒ **首轮看着正常**;**重新进入**时重放才抛
|
|
98
|
+
* `does not match an open turn and step` ⇒ 会话 corrupt、界面空白。
|
|
99
|
+
* ⇒ **v1.60 那版「先写占位 system 等宿主 replace」的整套方案仍然作废**
|
|
100
|
+
* (它写的是 `system/message`,那**确实**在 `STEP_EVENT_TYPES` 里)。
|
|
84
101
|
*
|
|
85
102
|
* **约束 B — 必须写在宿主提交 `system/message` 之后**:格式层有 `protectedHead`
|
|
86
103
|
* 机制 —— `foldSurface()` 里 `system/message` 若在 **surface 非空且尚无 protectedHead**
|
|
@@ -93,24 +110,39 @@ export declare function hasAssistantHistory(session: unknown): boolean;
|
|
|
93
110
|
* ⇒ 因此调用时机 = **`session/event` 里看到该会话第一条 `system/message` 之后**(见下方注册处),
|
|
94
111
|
* 而不是 `agent/inbox/inserted`(那太早)。
|
|
95
112
|
*
|
|
96
|
-
* ##
|
|
113
|
+
* ## 为什么必须成对(user + assistant ack)—— v1.69.4 修正
|
|
114
|
+
*
|
|
115
|
+
* 只造 user 的那一版在**界面上读不成对话**:两条"用户"消息连排在模型回复之前,
|
|
116
|
+
* 看起来像用户自己连说了两句协议、而模型没理。owner 2026-10-19 报的两点
|
|
117
|
+
* (「助手的回应也需要造出来」+「用户的怎么跑到第一个了」)其实是**同一个缺陷的两面**。
|
|
118
|
+
* ⇒ 造**成对交换**:每条锚定话语后面跟一条 `acknowledge` 回复。
|
|
119
|
+
*
|
|
120
|
+
* 🔴 **前提条件**:`assistant/message` 必须落在**已打开**的 turn/step 内
|
|
121
|
+
* (探针 S3/S4 实测:未打开/已关闭都抛 `does not match an open turn and step`)。
|
|
122
|
+
* 本函数由 `session/event` 的 `system/message` 分支触发,而**那条事件自己就带着
|
|
123
|
+
* `data.turn` / `data.step`** —— 且实测此刻 turn/step 确实处于打开态
|
|
124
|
+
* (真机日志:`step/start` → `system/message` → 锚定写入 → `step/end`)。
|
|
125
|
+
* ⇒ turn/step **从触发事件透传进来**,不自己猜。
|
|
97
126
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* 而那原本也是**我们伪造的**,不是模型真说过的。
|
|
127
|
+
* ⚠️ **拿不到 turn/step 时退回只造 user**(不抛、不阻断):宁可少伪造一条 ack,
|
|
128
|
+
* 也绝不在非法坐标上写 `assistant/message` —— 那会让会话在重放时 corrupt。
|
|
101
129
|
*
|
|
102
130
|
* ## 判据(改这个函数前后都必须跑)
|
|
103
131
|
*
|
|
104
|
-
* ①
|
|
105
|
-
* ②
|
|
132
|
+
* ① 形状判据 = `tests/anchor-history-shape.test.ts`(直调宿主校验器,正控 + 两种负控);
|
|
133
|
+
* ② 单测钉事件序列(user/assistant 交替、turn/step 逐条匹配);
|
|
134
|
+
* ③ **容器内真跑一轮 → 重启宿主 → 重新进入该会话** —— 只有这条路能证明日志合法
|
|
106
135
|
* (前两次修复都是"单测全绿 + 首轮正常"却仍然 corrupt)。
|
|
107
|
-
* ③ 直接调宿主自己的 `assertReleasedV4Relationships()` 判形状,**不要自写复刻**。
|
|
108
136
|
*
|
|
109
137
|
* @param session 会话(需有 append)
|
|
110
138
|
* @param anchorMessages 锚定话语序列(缺省协议固有两条)
|
|
139
|
+
* @param slot 触发本轮 `system/message` 所在的 turn/step(缺省或非法 ⇒ 只造 user)
|
|
111
140
|
* @returns 是否成功写入
|
|
112
141
|
*/
|
|
113
|
-
export declare function appendAnchorHistory(session: unknown, anchorMessages?: string[]
|
|
142
|
+
export declare function appendAnchorHistory(session: unknown, anchorMessages?: string[], slot?: {
|
|
143
|
+
turn?: unknown;
|
|
144
|
+
step?: unknown;
|
|
145
|
+
}): boolean;
|
|
114
146
|
interface PromotionStatus {
|
|
115
147
|
/** 最后一次 compaction/end 的 seq(-1 = 未压缩过) */
|
|
116
148
|
boundary: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.69.
|
|
3
|
+
"version": "1.69.4",
|
|
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 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/官方 ACP 面/认知轨迹唤醒注册表。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|