@shgroup/dsh-serenity-hooks 1.91.0 → 1.92.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.91.0",
3
+ "version": "1.92.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
@@ -8957,10 +8957,14 @@ function appendCroWakeLog(root, dirName, input, nowMs = Date.now()) {
8957
8957
  *
8958
8958
  * ## 判据(三条同时成立,缺一不可)
8959
8959
  *
8960
- * 1. 日志里**最后一条 `turn/end`** 的 `reason.kind === 'interrupted'`;
8960
+ * 1. 日志里**最后一条 `turn/end`** 的 `reason.kind` ∈ {@link UNFINISHED_KINDS}
8961
+ * (= `interrupted` 崩溃补收尾 / `aborted` 正常终止,两者都是"这一轮没收完");
8961
8962
  * 2. 该 `turn/end` **之后没有新的 `turn/start`**(否则已被后续轮次接续);
8962
8963
  * 3. ⛔ **不要求**末条是 `session/end-seed`(2026-10-23 放宽,原因见 {@link detectInterruptedEnd})。
8963
8964
  *
8965
+ * 🔴 **2026-10-24 补入 `aborted`**:修前只认 `interrupted` ⇒「被正常终止」整类漏扫
8966
+ * (发布重启/换宿主最常见的形态)。判据问的是「这一轮收完了吗」,**不是"进程死得惨不惨"**。
8967
+ *
8964
8968
  * ⚠️ **为什么不能只看"存在过 interrupted"**:一个长会话历史上可能中断几十次
8965
8969
  * (本容器实测单会话 33 次)。只看"曾经中断"会把早已恢复的会话全部当成待续 ⇒ 灾难。
8966
8970
  *
@@ -9053,34 +9057,54 @@ function readTailEvents(logPath, want) {
9053
9057
  return events.slice(-want);
9054
9058
  }
9055
9059
  /**
9056
- * 判定一组尾部事件是否「以 interrupted 结尾」。
9060
+ * 判定一组尾部事件是否「以未完成的轮次结尾」。
9057
9061
  * 不命中 → null。
9058
9062
  *
9059
- * ## 🔴 2026-10-23 判据放宽(owner 实测:「不是挂在工具调用上的,也要能被 interrupted 筛选到并唤醒」)
9063
+ * ## 🔴🔴 2026-10-24 判据放宽(owner 报障:「有的会话在重启时不是崩溃的就扫不到,还是没修好」)
9064
+ *
9065
+ * ### 漏的是什么(宿主源码实读,非推测)
9060
9066
  *
9061
- * ### 原判据(v1.87.4 起)为什么漏
9062
- * 原来只认 **末条事件 = `session/end-seed`**。实测(本机 3337 条会话全扫):
9063
- * · 末轮 `turn/end[interrupted]` 的会话 **26** 条;
9064
- * · 其中满足原判据的只有 **24** 条 ⇒ **漏 2 条**,形态逐字为:
9065
- * `… turn/end[interrupted] -> session/end-seed -> agent/inbox/spliced -> agent/inbox/spliced`
9066
- * ⇒ 重启后宿主把这**两条**追加在 end-seed 之后(它们是"恢复时把待办消息重新塞进收件箱"),
9067
- * 于是"末条必须 end-seed"这条**在恢复动作发生之后就恒不成立**。
9068
- * ⚠️ 讽刺的是:**正是那些被恢复过、又再次被打断的会话**最容易长成这样
9069
- * ⇒ 原判据恰好对"反复中断"的会话失明,而它们才是最需要守护的。
9067
+ * 宿主只有**两条**会写出「轮次没收完」的 `turn/end`,而它们的 `kind` **不是同一个词**:
9068
+ *
9069
+ * | 形态 | 写谁 | `reason.kind` | 修前是否命中 |
9070
+ * |---|---|---|---|
9071
+ * | 进程**崩溃**/被强杀,下一轮 resume 时补收尾 | `interruptedTurnClosers()` → `openTurnClosers(events,{kind:'interrupted'})` | **`interrupted`** | ✅ |
9072
+ * | 进程**正常退出**(`restart-web` 的 SIGTERM、`agent/disposed`、用户中断) | 驱动 catch 里 `abortedCancelCause(signal)` | **`aborted`** + 内层 `reason:{kind:'user'\|'parent'\|'disposed'\|'hook'}` | ❌ **漏** |
9070
9073
  *
9071
- * ### 新判据(三条,缺一不可)
9072
- * 1. 日志里**最后一条 `turn/end`** 的 `reason.kind === 'interrupted'`;
9073
- * 2. 该 `turn/end` **之后没有新的 `turn/start`**(否则说明它已被后续轮次接续);
9074
- * 3. ⛔ **不再要求末条是 `end-seed`**。
9074
+ * ⇒ 修前只认 `interrupted` ⇒ **「被正常终止」这一大类全部漏掉**,而它恰恰是
9075
+ * **发布重启/换宿主** 最常见的形态(比崩溃常见得多)。
9075
9076
  *
9076
- * ### 为什么不改成"末条必须是某个白名单"
9077
- * 那就是原缺陷换个白名单再犯一次(`agent/inbox/spliced` 之外还会有别的)。
9078
- * **判据要问的是"最后一轮的结束原因是什么、之后有没有新轮开始"**,
9079
- * 而不是"日志最后一个 token 长什么样"——后者是**载体形态**,前者是**状态**。
9077
+ * 🔴 **这两条是同一个状态的两种成因,不是"一个对一个不对"**:
9078
+ * 判据问的是「**这一轮有没有收完**」,不是「进程死得惨不惨」。
9079
+ * 拿死法当判据 ⇒ 换一种死法就漏一批(这正是 owner 反复看到的"还是没修好")。
9080
+ *
9081
+ * ### 为什么不能只加 `aborted` 就完事
9082
+ * `aborted.reason.kind` 里还可能有 `hook`(宿主主动 hook 取消)——它同属"没跑完"。
9083
+ * ⇒ 本判据**只看外层 `kind`**,理由:外层已经回答了"这一轮收完了吗",
9084
+ * 内层回答的是"谁让它停的",**那是另一个问题**,混进来会让判据随"谁取消的"漂移。
9085
+ *
9086
+ * ### ⛔ 刻意**不**纳入的形态(保守边界,写明理由)
9087
+ * - `error`:本轮**收完了**(宿主记了错误收尾),它不是"停在这里等人续";
9088
+ * - 完全没有 `turn/end` 的开轮(例如日志末尾是 `turn/start` 后再无事件):
9089
+ * 那**也可能是"进程正活着、这一轮正在跑"**,与真中断在读数上不可分
9090
+ * ⇒ 纳入会误唤醒在跑的会话。⚠️ 这是**已知边界**,不是遗漏。
9080
9091
  *
9081
9092
  * ### 为什么不会误报"曾经中断但已恢复"
9082
9093
  * 恢复一定伴随 `turn/start`(新一轮)或新的 `turn/end`(`completed` 等)⇒ 判据 1/2 各自挡住。
9083
- * 实测该放宽**不新增误报**:原 24 条全含于新判据(`curOnly = 0`),新增 2 条全是真漏。
9094
+ */
9095
+ /**
9096
+ * 「这一轮没跑完」的 `turn/end.reason.kind` 取值域。
9097
+ *
9098
+ * 🔴🔴 **2026-10-24(owner 报障)**:原判据只认 `'interrupted'` 一个词 ⇒
9099
+ * **「被正常终止」整类漏扫**。两个值对应的形态见 {@link detectInterruptedEnd} 的头注。
9100
+ *
9101
+ * ⚠️ **这是判据的唯一取值点** —— 别在别处再写一份字面量(两份会漂)。
9102
+ */
9103
+ const UNFINISHED_KINDS = /* @__PURE__ */ new Set(["interrupted", "aborted"]);
9104
+ /**
9105
+ * 命中判定。
9106
+ *
9107
+ * @returns 命中时给出被中断的轮次与"可能有副作用"的工具名;不命中 null
9084
9108
  */
9085
9109
  function detectInterruptedEnd(events) {
9086
9110
  if (events.length === 0) return null;
@@ -9091,9 +9115,10 @@ function detectInterruptedEnd(events) {
9091
9115
  }
9092
9116
  if (idx < 0) return null;
9093
9117
  const turnEnd = events[idx];
9094
- if (turnEnd?.data?.reason?.kind !== "interrupted") return null;
9118
+ const kind = turnEnd?.data?.reason?.kind;
9119
+ if (typeof kind !== "string" || !UNFINISHED_KINDS.has(kind)) return null;
9095
9120
  for (let i = idx + 1; i < events.length; i++) if (events[i]?.type === "turn/start") return null;
9096
- const turn = typeof turnEnd.data?.turn === "number" ? turnEnd.data.turn : null;
9121
+ const turn = typeof turnEnd?.data?.turn === "number" ? turnEnd.data.turn : null;
9097
9122
  return {
9098
9123
  turn,
9099
9124
  pendingTools: findPendingTools(events, turn)
@@ -10935,12 +10960,12 @@ function collectRunningSessionIds(nowMs) {
10935
10960
  function renderInterruptedScan(input) {
10936
10961
  const { projectDir, lookbackHours, scan } = input;
10937
10962
  const lines = [];
10938
- lines.push(`═══ 以 interrupted 结尾的 dsh 会话(回看 ${lookbackHours}h)═══`);
10963
+ lines.push(`═══ 末轮未跑完的 dsh 会话(回看 ${lookbackHours}h)═══`);
10939
10964
  lines.push(` 扫描域: ${projectDir}`);
10940
10965
  lines.push(` 检查 ${scan.scanned} 个 · 命中 ${scan.sessions.length} · 跳过 ${scan.skipped} · 耗时 ${scan.elapsedMs}ms${scan.errors.length > 0 ? ` · 读取失败 ${scan.errors.length}` : ""}`);
10941
10966
  if (scan.sessions.length === 0) {
10942
10967
  lines.push("");
10943
- lines.push("✓ 没有会话以 interrupted 结尾 —— 无需处理。");
10968
+ lines.push("✓ 没有末轮未跑完的会话 —— 无需处理。");
10944
10969
  return lines.join("\n");
10945
10970
  }
10946
10971
  lines.push("");
@@ -10951,7 +10976,9 @@ function renderInterruptedScan(input) {
10951
10976
  if (s.pendingTools.length > 0) lines.push(` ⚠️ 有调用无结果的工具(副作用未知): ${s.pendingTools.join(", ")}`);
10952
10977
  }
10953
10978
  lines.push("");
10954
- lines.push("判据: 日志末条 = session/end-seed,且其前最近的 turn/end 的 reason.kind = interrupted。");
10979
+ lines.push("判据: 日志末条 `turn/end` 的 `reason.kind` ∈ {interrupted, aborted},且其后没有新的 turn/start。");
10980
+ lines.push(" interrupted = 进程崩溃后被 resume 补收尾 / aborted = 被正常终止(重启、销毁、用户中断)。");
10981
+ lines.push(" 两者都表示「这一轮没跑完」,都会被收进来。");
10955
10982
  lines.push("🔴 本动作**只读** —— 是否续跑、续哪条、几点续,由调用方(你的认知轨迹)决定。");
10956
10983
  lines.push("⚠️ 未完成的工具调用「副作用未知」≠「没执行」:它们可能已写文件 / 已发消息。");
10957
10984
  lines.push(" 续跑前应先核对副作用,而不是把那一轮整段重放。");
@@ -10977,7 +11004,7 @@ function renderContinueResult(input) {
10977
11004
  lines.push(` 检查 ${scan.scanned} 个 · 命中 ${scan.sessions.length} · 跳过 ${scan.skipped} · 耗时 ${scan.elapsedMs}ms`);
10978
11005
  if (scan.sessions.length === 0) {
10979
11006
  lines.push("");
10980
- lines.push("✓ 没有会话以 interrupted 结尾 —— 无需续跑。");
11007
+ lines.push("✓ 没有末轮未跑完的会话 —— 无需续跑。");
10981
11008
  return lines.join("\n");
10982
11009
  }
10983
11010
  lines.push("");
@@ -11126,7 +11153,7 @@ function createCognitiveTrajectoryTool(ctx) {
11126
11153
  "list (inventory + stats) / show (read one SESSION.md) / create (make a NEW trajectory) / use (bind THIS conversation) / rebuild (clear-and-rebuild THIS conversation in place) / archive (move a trajectory out of the active list; one-way, never overwrites).",
11127
11154
  "(B) DELIVERY — sends ONE message to ANOTHER trajectory; your own binding is unchanged: send-now (right now) / send-later (one future instant).",
11128
11155
  "(C) REFERENCE — cro-guide: how to give a trajectory its own program that decides when it should be woken.",
11129
- "(D) INSPECTION — interrupted: list dsh sessions whose LAST turn ended with reason \"interrupted\" (the process died before that turn completed). PURE READ: no periodic scheduling, no auto-continue, no log mutation — deciding IF/WHEN/WHICH to resume belongs to the calling trajectory (this is the \"entry point only\" contract). The criterion is EXACTLY \"ends with interrupted\", NOT \"was ever interrupted\": one long session may have been interrupted dozens of times historically, and treating those as pending would resurrect sessions that already recovered. Each hit reports the turn number and the tool names that had a call but no result (best-effort — those side effects are UNKNOWN, so re-running them blindly is dangerous).",
11156
+ "(D) INSPECTION — interrupted: list dsh sessions whose LAST turn did not finish — reason.kind is \"interrupted\" (process died; the host synthesised the closer on resume) OR \"aborted\" (terminated normally: restart, dispose, user cancel). Both mean \"this turn did not complete\"; picking only one misses an entire class. PURE READ: no periodic scheduling, no auto-continue, no log mutation — deciding IF/WHEN/WHICH to resume belongs to the calling trajectory (this is the \"entry point only\" contract). The criterion is EXACTLY \"the last turn did not finish\", NOT \"was ever interrupted\": one long session may have been interrupted dozens of times historically, and treating those as pending would resurrect sessions that already recovered. Each hit reports the turn number and the tool names that had a call but no result (best-effort — those side effects are UNKNOWN, so re-running them blindly is dangerous).",
11130
11157
  "HOW TO CHOOSE (a rule, not a guess):",
11131
11158
  "not yet bound + starting new work ⇒ create (with no prior binding it activates immediately, so the new trajectory has a carrier at once).",
11132
11159
  "already bound + recording a SEPARATE future line ⇒ create (it does NOT steal your binding; activate the new one later by send-now).",
@@ -11422,16 +11449,16 @@ function createCognitiveTrajectoryTool(ctx) {
11422
11449
  return listResident(root);
11423
11450
  case "interrupted": {
11424
11451
  /**
11425
- * 以 `interrupted` 结尾的 dsh 会话(owner 2026-10-22 令:**只出入口**)。
11452
+ * 以未完成轮次结尾的 dsh 会话(owner 2026-10-22 令:**只出入口**)。
11426
11453
  *
11427
11454
  * 🔴 **边界(owner 原话:「我们 ACC 不关心周期」)**:本动作**只回答**"此刻哪些会话
11428
- * 的最后一轮以 interrupted 结束",**不判周期、不自动续跑、不改日志**。
11455
+ * 的最后一轮没跑完",**不判周期、不自动续跑、不改日志**。
11429
11456
  * 何时扫、续谁、怎么续 = 调用方(CCC 的认知轨迹)的事。
11430
11457
  *
11431
11458
  * 🔴 **为什么不是"曾经中断过"**:长会话历史上可中断几十次(实测单会话 33 次);
11432
- * 按"曾经"判会把早已恢复的会话全部当成待续 ⇒ 灾难。判据在
11433
- * `trajectory/interrupted.ts` 的 `detectInterruptedEnd`:**末条 = end-seed
11434
- * 且其前最近的 turn/end 是 interrupted**。
11459
+ * 按"曾经"判会把早已恢复的会话全部当成待续 ⇒ 灾难。
11460
+ * 🔴 **判据只在一处**:`trajectory/interrupted.ts` 的 `detectInterruptedEnd`
11461
+ * (⛔ 别在此处复述取值域 —— 2026-10-24 就是这份复述漏掉了 `aborted`)。
11435
11462
  *
11436
11463
  * 🔴 **正在跑的会话要排除**:live 会话不该被判成"待续"(它正在跑,不是中断)。
11437
11464
  */
@@ -63,12 +63,15 @@
63
63
  * 判据:它管的是**被找到**,不是**不被销毁** ⇒ 纯读、不碰生命周期、不进系统提示词。
64
64
  * ⚠️ 这也修正了 v1.77.1 的一处说明面失真(手册曾描述一个**不存在**的动作,D179(a))。
65
65
  * 2026-10-22(owner 令「在 trajectory 加上触发入口即可,剩下的 CCC 搞定」):新增
66
- * **`interrupted`** —— 列出**以 `interrupted` 结尾**的 dsh 会话(进程在轮次完成前退出)。
66
+ * **`interrupted`** —— 列出**末轮未跑完**的 dsh 会话(进程在轮次完成前退出)。
67
67
  * 判据(R↓,为何 ACC 只出**入口**不管周期):**周期性归属调用方的认知轨迹**,
68
- * ACC 只回答"此刻哪些会话以 interrupted 结尾"这一个问题。⇒ 本动作**纯读**、
68
+ * ACC 只回答"此刻哪些会话的最后一轮没跑完"这一个问题。⇒ 本动作**纯读**、
69
69
  * **不判周期、不自动续跑、不改任何会话日志**(是否续、续谁、几点续 = CCC 的事)。
70
- * ⚠️ 判据的锋利处:**只认"以 interrupted 结尾"**,不认"曾经中断过"—— 长会话历史上
70
+ * ⚠️ 判据的锋利处:**只认"末轮未跑完"**,不认"曾经中断过"—— 长会话历史上
71
71
  * 可中断几十次(实测单会话 33 次),按"曾经"判会把早已恢复的会话全部当成待续。
72
+ * 🔴 **2026-10-24(owner 报障「不是崩溃的就扫不到」)**:"未跑完"有两种宿主写法 ——
73
+ * `interrupted`(崩溃后 resume 补收尾)与 `aborted`(被正常终止:重启/销毁/用户中断)。
74
+ * 修前只认前者 ⇒ **发布重启这一类整批漏扫**。取值域只在 `trajectory/interrupted.ts` 一处定义。
72
75
  * 2026-10-22(owner 追加令「我希望实现的是 continue-all-interrupted 方法,调用后自动找到
73
76
  * 8h 内所有 dsh 会话注入简单的 continue 进行唤醒,这样 CCC 就不用动脑子了」):
74
77
  * 新增 **`continue-all-interrupted`** = `interrupted` 的**动作版**(扫 → 逐条注入续跑消息)。
@@ -17,10 +17,14 @@
17
17
  *
18
18
  * ## 判据(三条同时成立,缺一不可)
19
19
  *
20
- * 1. 日志里**最后一条 `turn/end`** 的 `reason.kind === 'interrupted'`;
20
+ * 1. 日志里**最后一条 `turn/end`** 的 `reason.kind` ∈ {@link UNFINISHED_KINDS}
21
+ * (= `interrupted` 崩溃补收尾 / `aborted` 正常终止,两者都是"这一轮没收完");
21
22
  * 2. 该 `turn/end` **之后没有新的 `turn/start`**(否则已被后续轮次接续);
22
23
  * 3. ⛔ **不要求**末条是 `session/end-seed`(2026-10-23 放宽,原因见 {@link detectInterruptedEnd})。
23
24
  *
25
+ * 🔴 **2026-10-24 补入 `aborted`**:修前只认 `interrupted` ⇒「被正常终止」整类漏扫
26
+ * (发布重启/换宿主最常见的形态)。判据问的是「这一轮收完了吗」,**不是"进程死得惨不惨"**。
27
+ *
24
28
  * ⚠️ **为什么不能只看"存在过 interrupted"**:一个长会话历史上可能中断几十次
25
29
  * (本容器实测单会话 33 次)。只看"曾经中断"会把早已恢复的会话全部当成待续 ⇒ 灾难。
26
30
  *
@@ -96,34 +100,9 @@ export interface ScanResult {
96
100
  elapsedMs: number;
97
101
  }
98
102
  /**
99
- * 判定一组尾部事件是否「以 interrupted 结尾」。
100
- * 不命中 → null。
101
- *
102
- * ## 🔴 2026-10-23 判据放宽(owner 实测:「不是挂在工具调用上的,也要能被 interrupted 筛选到并唤醒」)
103
- *
104
- * ### 原判据(v1.87.4 起)为什么漏
105
- * 原来只认 **末条事件 = `session/end-seed`**。实测(本机 3337 条会话全扫):
106
- * · 末轮 `turn/end[interrupted]` 的会话 **26** 条;
107
- * · 其中满足原判据的只有 **24** 条 ⇒ **漏 2 条**,形态逐字为:
108
- * `… turn/end[interrupted] -> session/end-seed -> agent/inbox/spliced -> agent/inbox/spliced`
109
- * ⇒ 重启后宿主把这**两条**追加在 end-seed 之后(它们是"恢复时把待办消息重新塞进收件箱"),
110
- * 于是"末条必须 end-seed"这条**在恢复动作发生之后就恒不成立**。
111
- * ⚠️ 讽刺的是:**正是那些被恢复过、又再次被打断的会话**最容易长成这样
112
- * ⇒ 原判据恰好对"反复中断"的会话失明,而它们才是最需要守护的。
113
- *
114
- * ### 新判据(三条,缺一不可)
115
- * 1. 日志里**最后一条 `turn/end`** 的 `reason.kind === 'interrupted'`;
116
- * 2. 该 `turn/end` **之后没有新的 `turn/start`**(否则说明它已被后续轮次接续);
117
- * 3. ⛔ **不再要求末条是 `end-seed`**。
118
- *
119
- * ### 为什么不改成"末条必须是某个白名单"
120
- * 那就是原缺陷换个白名单再犯一次(`agent/inbox/spliced` 之外还会有别的)。
121
- * **判据要问的是"最后一轮的结束原因是什么、之后有没有新轮开始"**,
122
- * 而不是"日志最后一个 token 长什么样"——后者是**载体形态**,前者是**状态**。
123
- *
124
- * ### 为什么不会误报"曾经中断但已恢复"
125
- * 恢复一定伴随 `turn/start`(新一轮)或新的 `turn/end`(`completed` 等)⇒ 判据 1/2 各自挡住。
126
- * 实测该放宽**不新增误报**:原 24 条全含于新判据(`curOnly = 0`),新增 2 条全是真漏。
103
+ * 命中判定。
104
+ *
105
+ * @returns 命中时给出被中断的轮次与"可能有副作用"的工具名;不命中 null
127
106
  */
128
107
  export declare function detectInterruptedEnd(events: SessionEvent[]): {
129
108
  turn: number | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.91.0",
3
+ "version": "1.92.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": {