@shgroup/dsh-serenity-hooks 1.48.2 → 1.49.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.48.2",
3
+ "version": "1.49.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
@@ -4741,7 +4741,7 @@ Instruction-following style:
4741
4741
  })
4742
4742
  ]
4743
4743
  }),
4744
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4744
+ control !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4745
4745
  className: "ss-rowControl",
4746
4746
  children: control
4747
4747
  }),
@@ -4804,7 +4804,6 @@ Instruction-following style:
4804
4804
  set("acpHttpPort", Math.min(65535, Math.max(1024, Math.round(v))));
4805
4805
  };
4806
4806
  const gatewayOn = value?.gatewayEnabled ?? false;
4807
- const rebuildOn = value?.rebuildEnabled ?? true;
4808
4807
  const thresholdK = value?.rebuildThresholdK ?? 400;
4809
4808
  const skiffOn = value?.skiffEnabled ?? false;
4810
4809
  const skiffPort = value?.skiffDebugPort ?? 3099;
@@ -4862,19 +4861,15 @@ Instruction-following style:
4862
4861
  open: openRebuild,
4863
4862
  onToggle: setOpenRebuild,
4864
4863
  expandHint: "配置",
4865
- control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
4866
- checked: rebuildOn,
4867
- onChange: (on) => toggle("rebuildEnabled", on)
4868
- }),
4869
4864
  detail: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4870
4865
  className: "ss-detailStack",
4871
4866
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4872
4867
  className: "ss-detailIntro",
4873
- children: "上下文超限自动重建(container_trajectory rebuild,v1.33 起 logbook 并入 trajectory):\n· 机制:agent 上下文占用达到阈值时,由 LLM 主动调用重建\n· 语义:完全丢弃当前 dsh 会话 + 自动新建 + 注入「继续原 SESSION 的工作」\n· 效果:SESSION.md 原位不动,认知轨迹延续,上下文归零\n· 关闭后:上下文超限时不再提示,可能导致会话卡顿或丢失"
4868
+ children: "上下文超限自动重建(container_trajectory rebuild,v1.33 起 logbook 并入 trajectory):\n· 机制:agent 上下文占用达到阈值时,由 LLM 主动调用重建\n· 语义:完全丢弃当前 dsh 会话 + 自动新建 + 注入「继续原 SESSION 的工作」\n· 效果:SESSION.md 原位不动,认知轨迹延续,上下文归零\n· 无总开关(v1.49.0 砍掉):长期验证下它已能很好地代替上下文压缩;\n 要调敏感度就改下面的阈值"
4874
4869
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4875
4870
  title: "重建阈值",
4876
4871
  desc: "上下文占用达到该 K 数值时提示(单位 K token,默认 400)",
4877
- help: "重建触发阈值(需求①:K 数值,默认 400K,范围 50~4000K):\n· 含义:上下文 projected tokens 达到 thresholdK × 1000 时触发提醒\n· 纯绝对数值(不再依赖窗口比例)\n· 迁移:旧版 0~1 比例(settings.yaml rebuildThreshold)已废弃被忽略\n· 调低更早触发 / 调高更晚触发;仅「超限重建」开启时有意义",
4872
+ help: "重建触发阈值(需求①:K 数值,默认 400K,范围 50~4000K):\n· 含义:上下文 projected tokens 达到 thresholdK × 1000 时触发提醒\n· 纯绝对数值(不再依赖窗口比例)\n· 迁移:旧版 0~1 比例(settings.yaml rebuildThreshold)已废弃被忽略\n· 调低更早触发 / 调高更晚触发(v1.49.0 起无总开关,它恒生效)",
4878
4873
  control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4879
4874
  className: "ss-portInput",
4880
4875
  type: "number",
@@ -4882,7 +4877,6 @@ Instruction-following style:
4882
4877
  max: 4e3,
4883
4878
  step: 50,
4884
4879
  value: thresholdK,
4885
- disabled: !rebuildOn,
4886
4880
  title: "重建阈值(K token,默认 400)",
4887
4881
  onChange: (e) => setThreshold(Number(e.target.value))
4888
4882
  })
package/lib/index.d.ts CHANGED
@@ -121,10 +121,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
121
121
  enabled: z<boolean, boolean, "defined">;
122
122
  }>>, "plain">;
123
123
  rebuild: z<Schemastery.ObjectS<NoInfer<{
124
- enabled: z<boolean, boolean, "defined">;
125
124
  thresholdK: z<number, number, "defined">;
126
125
  }>>, Schemastery.ObjectT<NoInfer<{
127
- enabled: z<boolean, boolean, "defined">;
128
126
  thresholdK: z<number, number, "defined">;
129
127
  }>>, "plain">;
130
128
  skiff: z<Schemastery.ObjectS<NoInfer<{
@@ -157,7 +155,6 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
157
155
  enabled: z<boolean, boolean, "defined">;
158
156
  }>>, "plain">;
159
157
  gatewayEnabled: z<boolean, boolean, "volatile">;
160
- rebuildEnabled: z<boolean, boolean, "volatile">;
161
158
  rebuildThresholdK: z<number, number, "volatile">;
162
159
  skiffEnabled: z<boolean, boolean, "volatile">;
163
160
  skiffDebugPort: z<number, number, "volatile">;
@@ -184,10 +181,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
184
181
  enabled: z<boolean, boolean, "defined">;
185
182
  }>>, "plain">;
186
183
  rebuild: z<Schemastery.ObjectS<NoInfer<{
187
- enabled: z<boolean, boolean, "defined">;
188
184
  thresholdK: z<number, number, "defined">;
189
185
  }>>, Schemastery.ObjectT<NoInfer<{
190
- enabled: z<boolean, boolean, "defined">;
191
186
  thresholdK: z<number, number, "defined">;
192
187
  }>>, "plain">;
193
188
  skiff: z<Schemastery.ObjectS<NoInfer<{
@@ -220,7 +215,6 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
220
215
  enabled: z<boolean, boolean, "defined">;
221
216
  }>>, "plain">;
222
217
  gatewayEnabled: z<boolean, boolean, "volatile">;
223
- rebuildEnabled: z<boolean, boolean, "volatile">;
224
218
  rebuildThresholdK: z<number, number, "volatile">;
225
219
  skiffEnabled: z<boolean, boolean, "volatile">;
226
220
  skiffDebugPort: z<number, number, "volatile">;
package/lib/index.js CHANGED
@@ -6,7 +6,7 @@ import { A as localIdStamp, C as setActiveSessionInfo, D as isoLocal, E as useSe
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-DxMwlCv9.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-B-KOyOYn.js";
10
10
  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";
11
11
  import z from "@deepseek-ai/schemastery";
12
12
  import { defineTool } from "@deepseek-ai/dsh-tools";
@@ -9235,7 +9235,12 @@ const EVENT_LABEL = {
9235
9235
  /** 敏感输出边界守卫(原 SERENITY OUTPUT GUARD) */
9236
9236
  guard: "BOUNDARY GUARD",
9237
9237
  /** SESSION.md 体积超限重写提醒(v1.31.1,S142 用户需求) */
9238
- compaction: "LOGBOOK COMPACTION"
9238
+ compaction: "LOGBOOK COMPACTION",
9239
+ /** **交界便签(REBUILD-TODO.md)体积超限清理提醒**(v1.49.0,owner 2026-09-26 需求)。
9240
+ *
9241
+ * 🔴 **刻意与 `compaction` 分开**:那条要求"**重写 SESSION.md 并保留 EAP 骨架**",
9242
+ * 本条只要求"**清理便签**"——共用一个 token 会让模型误以为要动 SESSION.md。 */
9243
+ scratchPrune: "SCRATCH PRUNE"
9239
9244
  };
9240
9245
  /** 家族标识(所有动态注入统一前缀) */
9241
9246
  const ASSISTANT_PREFIX = "TRAJECTORY-ASSISTANT";
@@ -9246,16 +9251,24 @@ function eventToken(event) {
9246
9251
  /** ACK 确认码前缀(recorded/skipped 语义不变,仅家族名更新) */
9247
9252
  const ACK_PREFIX = `${ASSISTANT_PREFIX}-recorded`;
9248
9253
  /**
9249
- * rebuild 交接区块标题(v1.31.1,S142 用户需求)。
9254
+ * rebuild 交界便签的**文件名**(v1.49.0;owner 2026-09-26 需求)。
9255
+ *
9256
+ * ## 它取代了什么(owner 原话:*"砍掉 in-flight,这正是目的"*)
9257
+ * v1.31.1 的交界协议要求把手头事项写进 **SESSION.md 末尾**的 `## In-flight (rebuild handover)` 段。
9258
+ * 实测的代价(S142 自身取证):In-flight 段曾积 **12 块**(含多份**已被取代**的交接),
9259
+ * 而 SESSION.md 是**有格式义务**的持久文件(7 段固定 + 粒度受 EAP 约束)⇒
9260
+ * ① 持久文件被临时物污染 ② 每次整理都要付"压缩+归档+保真"三件套(该文件曾达 292 KB)。
9261
+ *
9262
+ * ⇒ 分家:**只用一次的交界材料**写进这份**临时便签**(无格式义务、无留档义务、可随时丢弃)。
9250
9263
  *
9251
- * 用途:rebuild 提醒要求模型把**当前手头事项**写到 SESSION.md **末尾**的这个小标题之下;
9252
- * rebuild 锚点随后要求重建后的自己去读这一段并处理。两侧引用**同一个常量**——
9253
- * 标题一旦漂移,读侧就找不到写侧写的东西(单一真相源)。
9264
+ * ## 🔴 三处引用同一常量(单一真相源)
9265
+ * **写侧** = `seams/keeper.ts` 的 `rebuildReminderText()`|**读侧** = `rebuild.ts` 的重建锚点|
9266
+ * **体积提醒** = `seams/keeper.ts` 的 `rebuildTodoReminderText()`。
9267
+ * 文件名一旦漂移,写侧写的东西读侧就找不到 —— **且没有任何机械信号会报警**。
9254
9268
  *
9255
- * 形态(用户拍板 Q2):**固定英文标题行**(不是 HTML 注释锚)——人读友好;ACC 写死英文,
9256
- * 因为 SESSION.md 的正文语言归 CCC,但两侧的**机械锚点**必须逐字一致。
9269
+ * 🔴 **不得改名**(owner 已具名)。形态 = **固定文件名**(人读友好;比照旧 IN_FLIGHT_HEADING 的取舍)。
9257
9270
  */
9258
- const IN_FLIGHT_HEADING = "## In-flight (rebuild handover)";
9271
+ const REBUILD_TODO_FILENAME = "REBUILD-TODO.md";
9259
9272
  //#endregion
9260
9273
  //#region src/seams/keeper.ts
9261
9274
  const SCORES = {
@@ -9328,12 +9341,15 @@ function reminderText(code) {
9328
9341
  * 使用者若要查看当前占用/阈值:看面板(`rebuildThresholdK` 滑块 + 状态显示)——那是给人看的。
9329
9342
  *
9330
9343
  * v1.31.1 交接协议(S142 用户需求"要求 LLM 将当前手头事项写在 SESSION.md 尾部,并要求
9331
- * rebuild 后去读并处理"):**写侧** = 这里(两条文案都要求把 in-flight 事项写在
9332
- * SESSION.md 末尾的 `IN_FLIGHT_HEADING` 之下);**读侧** = rebuild 锚点(rebuild.ts)。
9333
- * 两侧共用 trajectory-assistant 的标题常量(单一真相源)。
9344
+ * rebuild 后去读并处理"):**写侧** = 这里;**读侧** = rebuild 锚点(rebuild.ts)。
9345
+ * 两侧共用 trajectory-assistant 的常量(单一真相源)。
9346
+ *
9347
+ * 🔴 **v1.49.0(owner 2026-09-26 裁决:"砍掉 in-flight,这正是目的")**:写侧**不再**指向
9348
+ * SESSION.md 末尾的段,改指**独立便签文件** `REBUILD-TODO_FILENAME`(轨迹目录下)——
9349
+ * 理由见该常量的头注(持久文件 vs 只用一次的交界材料必须分家)。
9334
9350
  */
9335
9351
  function rebuildReminderText(escalated = false) {
9336
- const handover = ` Before rebuilding, write your current in-flight items (the exact step you are in the middle of, what is not finished yet, and what the next action is) at the very end of SESSION.md under the heading "${IN_FLIGHT_HEADING}" — the rebuilt conversation reads that section first and continues from it.`;
9352
+ const handover = ` Before rebuilding, write your current in-flight items (the exact step you are in the middle of, what is not finished yet, and what the next action is) into "${REBUILD_TODO_FILENAME}" in this trajectory's directory — create it if it does not exist. That file is a scratch note for the handover only: no format obligation, no archival duty, safe to discard at any time. The rebuilt conversation reads it first and continues from it.`;
9337
9353
  if (escalated) return `${eventToken("limitMandatory")} Context usage has reached the configured rebuild threshold — you have been reminded repeatedly and have NOT called the container_trajectory rebuild action. This is now mandatory: STOP at the current task step, preserve valuable cognition into the CCC skills (or write a new-skill proposal into SESSION.md), then call container_trajectory rebuild immediately, passing --summary "<content summary ≤20 chars>" (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild).${handover} The conversation will be cleared and rebuilt in place; SESSION.md is the persistent trajectory and stays in place — identity continues from it. Do not continue working without rebuilding; this reminder persists until you call container_trajectory rebuild.`;
9338
9354
  return `${eventToken("limit")} Context usage has reached the configured rebuild threshold. This session is the rebuildable carrier of the trajectory: SESSION.md is the persistent body, this conversation is only a temporary work copy. Before rebuilding: if this conversation produced valuable cognition, revise the relevant existing skill of this CCC (structure it with eap); if a new skill is warranted, write a short proposal into SESSION.md for the user to review — do not create it yourself.${handover} ACT NOW: at the next natural pause (end of the current task step), call the container_trajectory rebuild action — passing --summary "<content summary ≤20 chars>" describing the next work phase (required; the dsh session title is renamed to S###-YYYY-MM-DD-<summary> after rebuild) — to clear and rebuild this conversation: the current copy is discarded, identity continues from SESSION.md. If you are in the middle of an unbreakable step, continue it, then rebuild at its end. Do not ignore this; rebuild is the expected action, not an option.`;
9339
9355
  }
@@ -9443,6 +9459,37 @@ function trajectoryCompactionReminderText(input) {
9443
9459
  if (escalated) return `${eventToken("compaction")} ${facts} — you have been reminded repeatedly and have NOT compacted SESSION.md. This is now mandatory: STOP the current task step, load eap (praxis eap), rewrite SESSION.md, then resume. ${principles} ${frontmatterGuard} This reminder persists until the file is under the limit.`;
9444
9460
  return `${eventToken("compaction")} ${facts}. Pause the current work and compact this trajectory's log (SESSION.md) before continuing — that log is the persistent body of this trajectory, and its size is operational entropy (H_op) the container pays on every read. ${principles} ${frontmatterGuard} After rewriting, resume the paused work; this reminder stops once the file is under the limit.`;
9445
9461
  }
9462
+ /** 连续超限轮数达此值 → 语气升级(对齐 COMPACTION_ESCALATE_AFTER / REBUILD_ESCALATE_AFTER) */
9463
+ const REBUILD_TODO_ESCALATE_AFTER = 3;
9464
+ const rebuildTodoStates = /* @__PURE__ */ new Map();
9465
+ /**
9466
+ * 交界便签超限提醒文案(v1.49.0)。
9467
+ *
9468
+ * ## 与 SESSION.md 档的**语义差别**(刻意,不是疏漏)
9469
+ *
9470
+ * | | SESSION.md 超限(`trajectoryCompactionReminderText`) | 便签超限(本条) |
9471
+ * |---|---|---|
9472
+ * | 动作 | **重写**(compaction) | **清理/丢弃**(prune) |
9473
+ * | 结构义务 | 保 EAP 骨架 + 4 条重写原则 + frontmatter 护栏 | **无** |
9474
+ * | 强度 | 升级后**强制**("STOP…必须先重写") | 升级后**语气加重,但动作仍只是"清理"** |
9475
+ * | 授权 | — | 🔴 **明确授权整份删除** |
9476
+ *
9477
+ * ## 归属(D23)
9478
+ * 机制(文件名 / 上限 / 注入时机)归 **ACC**;**写什么、怎么组织、何时丢**归 **CCC**。
9479
+ * 🔴 owner 裁决(逐字):*"rebuild 提示 Agent 去写,清空我们就不干涉了,只管最大量"* ⇒
9480
+ * **ACC 只提醒,不做任何写/删/归档操作**(自动删 = 数据丢失风险:rebuild 时序上新我还没读就被删)。
9481
+ *
9482
+ * ## 为什么要求"参照 eap"
9483
+ * owner 裁决(逐字):*"不规定,但要求参照 eap 来编写(这样质量会好点)"* ⇒
9484
+ * **不规定内部结构**(不给骨架),但**要求按 EAP 写**(显式 / 可重建 / 稳定)。
9485
+ */
9486
+ function rebuildTodoReminderText(input) {
9487
+ const { sizeKB, limitKB, mdPath, escalated = false } = input;
9488
+ const facts = `${REBUILD_TODO_FILENAME} is ${sizeKB} KB (limit ${limitKB} KB): ${mdPath}`;
9489
+ const action = "This file is a scratch note for the rebuild handover — it carries no archival duty and you may discard it wholesale at any time. Prune it now, writing in the spirit of eap (praxis eap): (1) delete what is already done; (2) delete what has been superseded; (3) delete what is already recorded in SESSION.md — that file is the trajectory's persistent body, this one only carries what the next self needs once. Do not restructure it into a formal document: the only mechanical constraint on this file is its size.";
9490
+ if (escalated) return `${eventToken("scratchPrune")} ${facts} — you have been reminded repeatedly and have NOT pruned it. Prune it now. ${action} This reminder persists until the file is under the limit.`;
9491
+ return `${eventToken("scratchPrune")} ${facts}. ${action} This reminder stops once the file is under the limit.`;
9492
+ }
9446
9493
  /** 每 agent 一个跟踪器(进程内存态,agent token 维度) */
9447
9494
  const trackers = /* @__PURE__ */ new Map();
9448
9495
  /** 连续超阈值轮数达此值 → 升级为 [TRAJECTORY-ESCALATED] 强制语气(此后持续升级催,直到 rebuild) */
@@ -9482,7 +9529,7 @@ function registerKeeper(ctx, opts = {}) {
9482
9529
  text: reminderText(code)
9483
9530
  });
9484
9531
  }
9485
- if (skiffRebuild && readSimpleSettings().rebuildEnabled) {
9532
+ if (skiffRebuild) {
9486
9533
  const session = exec.agent?.session;
9487
9534
  if (session) {
9488
9535
  const pressure = readContextPressure(ctx, session);
@@ -9525,6 +9572,27 @@ function registerKeeper(ctx, opts = {}) {
9525
9572
  } else compactionStates.delete(scope);
9526
9573
  }
9527
9574
  }
9575
+ if (skiffTrajectoryEnabled(root, sessionId, "session")) {
9576
+ const scope = sessionId ?? "global";
9577
+ const session = exec.agent?.session;
9578
+ const mdPath = resolveActiveSessionMdPath(root, scope, session);
9579
+ const todoPath = mdPath ? join(dirname(mdPath), REBUILD_TODO_FILENAME) : null;
9580
+ const size = todoPath ? readFileSize(todoPath) : null;
9581
+ if (todoPath && size !== null && size > 10240) {
9582
+ const st = rebuildTodoStates.get(scope) ?? { consecutive: 0 };
9583
+ st.consecutive += 1;
9584
+ blocks.push({
9585
+ type: "text",
9586
+ text: rebuildTodoReminderText({
9587
+ sizeKB: Math.round(size / 1024),
9588
+ limitKB: 10,
9589
+ mdPath: todoPath,
9590
+ escalated: st.consecutive >= REBUILD_TODO_ESCALATE_AFTER
9591
+ })
9592
+ });
9593
+ rebuildTodoStates.set(scope, st);
9594
+ } else rebuildTodoStates.delete(scope);
9595
+ }
9528
9596
  if (blocks.length === 0) return downstream;
9529
9597
  const reminderAll = createUserMessage({
9530
9598
  content: blocks,
@@ -10181,7 +10249,7 @@ function registerStatusApi(ctx, opts = {}) {
10181
10249
  return;
10182
10250
  }
10183
10251
  const settings = readAdvancedSettings();
10184
- const { readSimpleSettings } = await import("./settings-section-DxMwlCv9.js").then((n) => n.r);
10252
+ const { readSimpleSettings } = await import("./settings-section-B-KOyOYn.js").then((n) => n.r);
10185
10253
  const simple = readSimpleSettings();
10186
10254
  const allowed = settings.publicAsk.allowed;
10187
10255
  const port = simple.acpHttpPort ?? ACP_HTTP_PORT;
@@ -12172,6 +12240,8 @@ function stripAckSuffix(text) {
12172
12240
  */
12173
12241
  function buildRebuildAnchor(root, sessionName, activeMdPath, anchorMessages = DEFAULT_ANCHOR_MESSAGES, focus) {
12174
12242
  const rel = activeMdPath.startsWith(root) ? activeMdPath.slice(root.length + 1) : activeMdPath;
12243
+ const todoAbs = join(dirname(activeMdPath), REBUILD_TODO_FILENAME);
12244
+ const todoRel = todoAbs.startsWith(root) ? todoAbs.slice(root.length + 1) : todoAbs;
12175
12245
  const sessionDir = basename(dirname(activeMdPath));
12176
12246
  const sessionLine = sessionDir !== "AGENT_SESSIONS" ? `- Serenity session: ${sessionName !== "" ? `${sessionName} (${sessionDir})` : sessionDir}` : null;
12177
12247
  const focusLine = sanitizeFocusLine(focus);
@@ -12184,7 +12254,7 @@ function buildRebuildAnchor(root, sessionName, activeMdPath, anchorMessages = DE
12184
12254
  `- Persistent trajectory — SESSION.md path: ${rel}`,
12185
12255
  ` (the trajectory's persistent body — stays in place through rebuilds)`,
12186
12256
  `- Read that SESSION.md first (goal/decisions/progress/unresolved), then continue from the last checkpoint.`,
12187
- `- Then read the "${IN_FLIGHT_HEADING}" section at the very end of SESSION.md and work through those in-flight items (the previous self paused mid-task and handed them over). If that section is missing, infer the in-flight items from the latest progress entries.`,
12257
+ `- Then read the handover scratch note: ${todoRel} (path: ${todoAbs}) — the previous self paused mid-task and left its in-flight items there. That file carries no format obligation and may be discarded at any time; it is NOT the authoritative state. If it is missing, infer the in-flight items from SESSION.md's latest progress entries.`,
12188
12258
  ...focusLine ? [`- Task focus: ${focusLine}`] : []
12189
12259
  ].join("\n");
12190
12260
  }
@@ -12290,7 +12360,6 @@ function writeRebuildDiag(root, entry) {
12290
12360
  * @returns 排队结果;rebuild.enabled=false 或非 CCC 抛错
12291
12361
  */
12292
12362
  async function queueRebuild(ctx, opts) {
12293
- if (!readSimpleSettings().rebuildEnabled) throw new Error("container_trajectory rebuild is disabled (rebuild.enabled=false — enable it in the dsh settings panel)");
12294
12363
  const { root, note, summary, dshSessionId } = opts;
12295
12364
  const session = ctx.sessions?.get?.(dshSessionId);
12296
12365
  if (!session) throw new Error(`Unable to locate dsh session ${dshSessionId} (session may be closed)`);
@@ -18699,10 +18768,7 @@ const Config = z.object({
18699
18768
  env: z.boolean().default(true),
18700
18769
  opencodeSkills: z.boolean().default(true),
18701
18770
  gateway: z.object({ enabled: z.boolean().default(false) }),
18702
- rebuild: z.object({
18703
- enabled: z.boolean().default(true),
18704
- thresholdK: z.number().min(50).max(4e3).default(400)
18705
- }),
18771
+ rebuild: z.object({ thresholdK: z.number().min(50).max(4e3).default(400) }),
18706
18772
  skiff: z.object({
18707
18773
  enabled: z.boolean().default(false),
18708
18774
  debugPort: z.number().min(1024).max(65535).default(SKIFF_DEBUG_PORT)
@@ -18715,7 +18781,6 @@ const Config = z.object({
18715
18781
  opencodeProvider: z.object({ autoConfigure: z.boolean().default(true) }),
18716
18782
  visionPatch: z.object({ enabled: z.boolean().default(true) }),
18717
18783
  gatewayEnabled: z.boolean().volatile(),
18718
- rebuildEnabled: z.boolean().volatile(),
18719
18784
  rebuildThresholdK: z.number().min(50).max(4e3).volatile(),
18720
18785
  skiffEnabled: z.boolean().volatile(),
18721
18786
  skiffDebugPort: z.number().min(1024).max(65535).volatile(),
@@ -48,9 +48,12 @@ export declare function reminderText(code: string): string;
48
48
  * 使用者若要查看当前占用/阈值:看面板(`rebuildThresholdK` 滑块 + 状态显示)——那是给人看的。
49
49
  *
50
50
  * v1.31.1 交接协议(S142 用户需求"要求 LLM 将当前手头事项写在 SESSION.md 尾部,并要求
51
- * rebuild 后去读并处理"):**写侧** = 这里(两条文案都要求把 in-flight 事项写在
52
- * SESSION.md 末尾的 `IN_FLIGHT_HEADING` 之下);**读侧** = rebuild 锚点(rebuild.ts)。
53
- * 两侧共用 trajectory-assistant 的标题常量(单一真相源)。
51
+ * rebuild 后去读并处理"):**写侧** = 这里;**读侧** = rebuild 锚点(rebuild.ts)。
52
+ * 两侧共用 trajectory-assistant 的常量(单一真相源)。
53
+ *
54
+ * 🔴 **v1.49.0(owner 2026-09-26 裁决:"砍掉 in-flight,这正是目的")**:写侧**不再**指向
55
+ * SESSION.md 末尾的段,改指**独立便签文件** `REBUILD-TODO_FILENAME`(轨迹目录下)——
56
+ * 理由见该常量的头注(持久文件 vs 只用一次的交界材料必须分家)。
54
57
  */
55
58
  export declare function rebuildReminderText(escalated?: boolean): string;
56
59
  /** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
@@ -110,6 +113,51 @@ export declare function trajectoryCompactionReminderText(input: {
110
113
  mdPath: string;
111
114
  escalated?: boolean;
112
115
  }): string;
116
+ /**
117
+ * 交界便签体积上限(KB)—— **硬编码,刻意不设配置键**。
118
+ *
119
+ * 🔴 owner 2026-09-26 裁决(逐字):*"不需要设定,很多设定项我想砍掉了,好的功能用久了就发现
120
+ * 离不开,就不需要了"* ⇒ **不提供 `sessionKeeper.rebuildTodoMaxKB`**(设计件 Q4 的原倾向被否)。
121
+ * 对照:SESSION.md 的上限**保留可配置**(既有面,本版不动)。
122
+ */
123
+ export declare const DEFAULT_REBUILD_TODO_MAX_KB = 10;
124
+ interface RebuildTodoState {
125
+ /** 连续超限轮数(文件降回限内即清零 → 清理完成后提醒自动停止) */
126
+ consecutive: number;
127
+ }
128
+ /** 测试/调试:查看交界便签超限提醒状态 */
129
+ export declare function rebuildTodoStateSnapshot(): ReadonlyMap<string, RebuildTodoState>;
130
+ /** 会话销毁/测试重置:清理超限计数(防 per-会话 Map 无界增长) */
131
+ export declare function forgetRebuildTodoState(scope: string): void;
132
+ /** 测试辅助:清空交界便签提醒状态 */
133
+ export declare function __resetRebuildTodoForTest(): void;
134
+ /**
135
+ * 交界便签超限提醒文案(v1.49.0)。
136
+ *
137
+ * ## 与 SESSION.md 档的**语义差别**(刻意,不是疏漏)
138
+ *
139
+ * | | SESSION.md 超限(`trajectoryCompactionReminderText`) | 便签超限(本条) |
140
+ * |---|---|---|
141
+ * | 动作 | **重写**(compaction) | **清理/丢弃**(prune) |
142
+ * | 结构义务 | 保 EAP 骨架 + 4 条重写原则 + frontmatter 护栏 | **无** |
143
+ * | 强度 | 升级后**强制**("STOP…必须先重写") | 升级后**语气加重,但动作仍只是"清理"** |
144
+ * | 授权 | — | 🔴 **明确授权整份删除** |
145
+ *
146
+ * ## 归属(D23)
147
+ * 机制(文件名 / 上限 / 注入时机)归 **ACC**;**写什么、怎么组织、何时丢**归 **CCC**。
148
+ * 🔴 owner 裁决(逐字):*"rebuild 提示 Agent 去写,清空我们就不干涉了,只管最大量"* ⇒
149
+ * **ACC 只提醒,不做任何写/删/归档操作**(自动删 = 数据丢失风险:rebuild 时序上新我还没读就被删)。
150
+ *
151
+ * ## 为什么要求"参照 eap"
152
+ * owner 裁决(逐字):*"不规定,但要求参照 eap 来编写(这样质量会好点)"* ⇒
153
+ * **不规定内部结构**(不给骨架),但**要求按 EAP 写**(显式 / 可重建 / 稳定)。
154
+ */
155
+ export declare function rebuildTodoReminderText(input: {
156
+ sizeKB: number;
157
+ limitKB: number;
158
+ mdPath: string;
159
+ escalated?: boolean;
160
+ }): string;
113
161
  interface KeeperRegistration {
114
162
  /** 缺省阈值(serenity.json sessionKeeper.threshold 优先,其次此值) */
115
163
  defaultThreshold?: number;
@@ -70,7 +70,6 @@ var settings_section_exports = /* @__PURE__ */ __exportAll({
70
70
  function defaultSimpleSettings() {
71
71
  return {
72
72
  gatewayEnabled: false,
73
- rebuildEnabled: true,
74
73
  rebuildThresholdK: 400,
75
74
  skiffEnabled: false,
76
75
  skiffDebugPort: SKIFF_DEBUG_PORT,
@@ -116,7 +115,6 @@ function simpleSettingsFromConfig(config) {
116
115
  const d = defaultSimpleSettings();
117
116
  return {
118
117
  gatewayEnabled: unwrapVolatile(config.gatewayEnabled) ?? config.gateway?.enabled ?? d.gatewayEnabled,
119
- rebuildEnabled: unwrapVolatile(config.rebuildEnabled) ?? config.rebuild?.enabled ?? d.rebuildEnabled,
120
118
  rebuildThresholdK: unwrapVolatile(config.rebuildThresholdK) ?? config.rebuild?.thresholdK ?? d.rebuildThresholdK,
121
119
  skiffEnabled: unwrapVolatile(config.skiffEnabled) ?? config.skiff?.enabled ?? d.skiffEnabled,
122
120
  skiffDebugPort: unwrapVolatile(config.skiffDebugPort) ?? config.skiff?.debugPort ?? d.skiffDebugPort,
@@ -49,7 +49,6 @@ interface SimpleConfigFragment {
49
49
  enabled?: boolean;
50
50
  };
51
51
  rebuild?: {
52
- enabled?: boolean;
53
52
  thresholdK?: number;
54
53
  };
55
54
  /** F4 Skiff(实验性):调试服务启停(人工) */
@@ -63,7 +62,6 @@ interface SimpleConfigFragment {
63
62
  httpPort?: number;
64
63
  };
65
64
  gatewayEnabled?: PanelBoolean;
66
- rebuildEnabled?: PanelBoolean;
67
65
  rebuildThresholdK?: PanelNumber;
68
66
  skiffEnabled?: PanelBoolean;
69
67
  skiffDebugPort?: PanelNumber;
@@ -81,8 +79,6 @@ export declare const SERENITY_SETTINGS_NS = "serenity-hooks";
81
79
  interface SerenitySimpleSettings {
82
80
  /** F1 双端口网关总开关 */
83
81
  gatewayEnabled: boolean;
84
- /** F2 超限重建总开关 */
85
- rebuildEnabled: boolean;
86
82
  /** F2 触发阈值(需求① S142 用户拍板:百分比比例 → K 数值;projectedTokens ≥ thresholdK*1000 触发) */
87
83
  rebuildThresholdK: number;
88
84
  /** F4 Skiff 调试服务总开关(实验性;默认关——不随插件加载自动启动,人工开启) */
@@ -33,6 +33,11 @@ export declare const EVENT_LABEL: {
33
33
  readonly guard: "BOUNDARY GUARD";
34
34
  /** SESSION.md 体积超限重写提醒(v1.31.1,S142 用户需求) */
35
35
  readonly compaction: "LOGBOOK COMPACTION";
36
+ /** **交界便签(REBUILD-TODO.md)体积超限清理提醒**(v1.49.0,owner 2026-09-26 需求)。
37
+ *
38
+ * 🔴 **刻意与 `compaction` 分开**:那条要求"**重写 SESSION.md 并保留 EAP 骨架**",
39
+ * 本条只要求"**清理便签**"——共用一个 token 会让模型误以为要动 SESSION.md。 */
40
+ readonly scratchPrune: "SCRATCH PRUNE";
36
41
  };
37
42
  /** 家族标识(所有动态注入统一前缀) */
38
43
  export declare const ASSISTANT_PREFIX = "TRAJECTORY-ASSISTANT";
@@ -42,16 +47,24 @@ export declare function eventToken(event: keyof typeof EVENT_LABEL): string;
42
47
  export declare const ACK_PREFIX = "TRAJECTORY-ASSISTANT-recorded";
43
48
  export declare const ACK_SKIP_PREFIX = "TRAJECTORY-ASSISTANT-skipped";
44
49
  /**
45
- * rebuild 交接区块标题(v1.31.1,S142 用户需求)。
50
+ * rebuild 交界便签的**文件名**(v1.49.0;owner 2026-09-26 需求)。
46
51
  *
47
- * 用途:rebuild 提醒要求模型把**当前手头事项**写到 SESSION.md **末尾**的这个小标题之下;
48
- * rebuild 锚点随后要求重建后的自己去读这一段并处理。两侧引用**同一个常量**——
49
- * 标题一旦漂移,读侧就找不到写侧写的东西(单一真相源)。
52
+ * ## 它取代了什么(owner 原话:*"砍掉 in-flight,这正是目的"*)
53
+ * v1.31.1 的交界协议要求把手头事项写进 **SESSION.md 末尾**的 `## In-flight (rebuild handover)` 段。
54
+ * 实测的代价(S142 自身取证):In-flight 段曾积 **12 块**(含多份**已被取代**的交接),
55
+ * 而 SESSION.md 是**有格式义务**的持久文件(7 段固定 + 粒度受 EAP 约束)⇒
56
+ * ① 持久文件被临时物污染 ② 每次整理都要付"压缩+归档+保真"三件套(该文件曾达 292 KB)。
50
57
  *
51
- * 形态(用户拍板 Q2):**固定英文标题行**(不是 HTML 注释锚)——人读友好;ACC 写死英文,
52
- * 因为 SESSION.md 的正文语言归 CCC,但两侧的**机械锚点**必须逐字一致。
58
+ * ⇒ 分家:**只用一次的交界材料**写进这份**临时便签**(无格式义务、无留档义务、可随时丢弃)。
59
+ *
60
+ * ## 🔴 三处引用同一常量(单一真相源)
61
+ * **写侧** = `seams/keeper.ts` 的 `rebuildReminderText()`|**读侧** = `rebuild.ts` 的重建锚点|
62
+ * **体积提醒** = `seams/keeper.ts` 的 `rebuildTodoReminderText()`。
63
+ * 文件名一旦漂移,写侧写的东西读侧就找不到 —— **且没有任何机械信号会报警**。
64
+ *
65
+ * 🔴 **不得改名**(owner 已具名)。形态 = **固定文件名**(人读友好;比照旧 IN_FLIGHT_HEADING 的取舍)。
53
66
  */
54
- export declare const IN_FLIGHT_HEADING = "## In-flight (rebuild handover)";
67
+ export declare const REBUILD_TODO_FILENAME = "REBUILD-TODO.md";
55
68
  /** 风格档位:plain(默认,精确文本)/ metaphor(借星舰词——产品隐喻非游戏词) */
56
69
  type TrajectoryStyle = 'plain' | 'metaphor';
57
70
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.48.2",
3
+ "version": "1.49.0",
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": {