@shgroup/dsh-serenity-hooks 1.34.2 → 1.36.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.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * live-sessions.ts — 进程内 live 会话取数 + 精简诊断
3
+ *
4
+ * ── 为什么单独成模块(R↓,2026-09-15 S142「ACC 侧 autopilot 退场」S1 搬迁)──
5
+ * 这两个函数原先住在 `autopilot-trajectory.ts`,但它们的**主语不是 autopilot**:
6
+ * `listLiveSessions` 服务 `acc-diag` ① 段的"**哪个会话属于哪个 CCC**"(会话维度,
7
+ * 不是 CCC 维度),而"**独立进程看不到运行时**"正是 `acc-diag` 存在的理由。
8
+ * autopilot 退场后它们必须活下去 ⇒ 独立成模块,不再挂着已死机制的名字。
9
+ *
10
+ * ── 迁移边界(不搬什么,以及为什么)──────────────────────────────────────
11
+ * 原 `diagLive` 还含两段,**均不搬**(其主语是 autopilot,随机制退场):
12
+ * · `autopilotCccs`(逐 CCC 目标命中 / agent 定位诊断)—— 依赖 `readAutopilotSettings` /
13
+ * `getAutopilotStatus` / `resolveTargetAgent`
14
+ * · `panelResolved`(无参面板请求落到哪个 CCC)—— 面板区块随机制退场
15
+ * 留下的 = **进程 cwd/CCC + live 会话清单**(`acc-diag` ① 段仍需要)。
16
+ *
17
+ * 判据纪律:本模块**不得**反向依赖 autopilot 判据层(那会让退场半途而废)。
18
+ */
19
+ import type { Context } from 'cordis';
20
+ /** live 会话条目(诊断用;标题从 events 读) */
21
+ export interface LiveSessionEntry {
22
+ id: string;
23
+ cwd: string | null;
24
+ cccRoot: string | null;
25
+ title: string | null;
26
+ }
27
+ /**
28
+ * 从 dsh 会话 log 读取标题(latest-wins `session/title` 事件)——
29
+ * **标题不在 `sessions.list()` 条目上**(wire/对象均无 title 字段;F3 命名经
30
+ * `sessionTitle.rename` 写进 session log),必须从 events 提取(rebuild 同款读取模式)。
31
+ */
32
+ export declare function readSessionTitle(session: unknown): string | null;
33
+ /**
34
+ * 逐条 live 会话(`cccRoot` = 该会话 cwd 的 CCC 归属,无 → null)。
35
+ *
36
+ * CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集语义);
37
+ * 本函数保留的是它**独有**的那部分:会话维度 + cwd 归属 + 标题 + id。
38
+ *
39
+ * @param ctx 插件上下文
40
+ * @returns 逐条 live 会话
41
+ */
42
+ export declare function listLiveSessions(ctx: Context): LiveSessionEntry[];
43
+ /**
44
+ * 进程内诊断(`acc-diag` ① 段数据源)——输出当前实例的进程归属 + live 会话清单。
45
+ *
46
+ * 为什么必须在插件进程内算:脚本看不到 live 会话 / cwd 归属(这正是本数据的**唯一来源**,
47
+ * 没有第二个取数出口)。`autopilotCccs` / `panelResolved` 两段随 autopilot 机制退场,见文件头。
48
+ */
49
+ export interface DiagLiveReport {
50
+ processCwd: string;
51
+ processCcc: string | null;
52
+ liveSessions: LiveSessionEntry[];
53
+ }
54
+ export declare function diagLive(ctx: Context): DiagLiveReport;
@@ -24,7 +24,7 @@ export declare class KeeperTracker {
24
24
  ack(): string;
25
25
  get currentScore(): number;
26
26
  }
27
- export declare function reminderText(code: string, score: number): string;
27
+ export declare function reminderText(code: string): string;
28
28
  /**
29
29
  * F2 rebuild 提示(v1.21;v1.22.1 对齐"轨迹跟踪器"概念;v1.23.0 英化 + 载体关系;
30
30
  * v1.23.3 行动指令化——用户反馈"一直在触发为啥没执行":旧文案是状态播报式
@@ -38,15 +38,21 @@ export declare function reminderText(code: string, score: number): string;
38
38
  * escalated=true(v1.23.3):连续多轮超阈值仍未 rebuild → 升级强制语气
39
39
  * (STOP and rebuild now,持续注入直到调用 container_trajectory rebuild)。
40
40
  *
41
- * 需求①(S142 用户拍板):百分比比例 → K 数值——tokensK = 实际占用(千 token),
42
- * thresholdK = 配置阈值(千 token);文案 `Context usage at NNNK (threshold NNNK)`。
41
+ * 需求①(S142 用户拍板,v1.28.0):百分比比例 → K 数值——**判定**改为绝对
42
+ * `projectedTokens thresholdK*1000`(触发语义,未变)。
43
+ * 🔴 2026-09-16(S142 所有者指令,D64):**判定用 K,但注入文本里不再给任何数值**——
44
+ * 原文案 `Context usage at NNNK (threshold NNNK)` 属于"向 LLM 透露上下文消耗与限制",
45
+ * 是非必要信息(LLM 不据此决策,只占注意力)。⇒ 参数退化为仅 `escalated`。
46
+ * ⚠️ 触发判定与提醒行为**一字未改**(仍读 projectedTokens / thresholdK 比对;仍升级催);
47
+ * 变的只是**注入文本不再携带 tokensK / thresholdK**。
48
+ * 使用者若要查看当前占用/阈值:看面板(`rebuildThresholdK` 滑块 + 状态显示)——那是给人看的。
43
49
  *
44
50
  * v1.31.1 交接协议(S142 用户需求"要求 LLM 将当前手头事项写在 SESSION.md 尾部,并要求
45
51
  * rebuild 后去读并处理"):**写侧** = 这里(两条文案都要求把 in-flight 事项写在
46
52
  * SESSION.md 末尾的 `IN_FLIGHT_HEADING` 之下);**读侧** = rebuild 锚点(rebuild.ts)。
47
53
  * 两侧共用 trajectory-assistant 的标题常量(单一真相源)。
48
54
  */
49
- export declare function rebuildReminderText(tokensK: number, thresholdK: number, escalated?: boolean): string;
55
+ export declare function rebuildReminderText(escalated?: boolean): string;
50
56
  /** 读取会话 contextPressure 投影(sessionProjections 可选服务;未装配返回 null) */
51
57
  export declare function readContextPressure(ctx: Context, session: unknown): {
52
58
  projectedTokens: number;
@@ -78,8 +78,6 @@ const simpleSettingsSchema = z.object({
78
78
  acpEnabled: z.boolean().default(false),
79
79
  acpHttpPort: z.number().min(1024).max(65535).default(ACP_HTTP_PORT),
80
80
  publicAskEnabled: z.boolean().default(false),
81
- autopilotWakeEnabled: z.boolean().required(false),
82
- autopilotEnabled: z.boolean().required(false),
83
81
  wakeSchedulerEnabled: z.boolean().default(true)
84
82
  });
85
83
  /** 从插件 Config 提取 entry 默认(settings base 层) */
@@ -63,16 +63,11 @@ interface SerenitySimpleSettings {
63
63
  acpHttpPort: number;
64
64
  /** F4d 建议问答页总开关(实验性;默认关——按认知容器暴露问答页,key 认证) */
65
65
  publicAskEnabled: boolean;
66
- /** **周期自唤醒(autopilot)全局闸** —— v1.34 更名收窄(原 `autopilotEnabled`)。
67
- * **只管周期自唤醒**:关掉它**不**影响一次性唤醒 `container_trajectory wake-later`(后者归 {@link wakeSchedulerEnabled})。
68
- * 缺省关(多台电脑装 dsp 时只在指定电脑跑周期自唤醒)。
69
- * 可选类型是为了**迁移语义精确**:老装机里存的旧键 `autopilotEnabled` 仍应生效,而"新键显式 false"必须能覆盖它。 */
70
- autopilotWakeEnabled?: boolean;
71
- /** @deprecated v1.34 前的旧键(**仅迁移期回退读**,不回写)。新装/已迁移请用 `autopilotWakeEnabled` */
72
- autopilotEnabled?: boolean;
73
66
  /** **唤醒调度器全局闸** —— v1.34 更名(原 `trajectoryEnabled`):投递已登记的"未来时刻 + 一条 message"。
74
67
  * **缺省开**:它是 trajectory 的基础能力(D58),且**不得**被"关掉周期自唤醒"连带关掉
75
- * ——用户 2026-09-15 裁决:「auto-trajectory 的开关只关闭 auto-trajectory 唤醒」。 */
68
+ * ——用户 2026-09-15 裁决:「auto-trajectory 的开关只关闭 auto-trajectory 唤醒」。
69
+ * (原并列的周期自唤醒闸 `autopilotWakeEnabled` / 旧键 `autopilotEnabled` 已随 ACC 侧
70
+ * autopilot 于 2026-09-15 整段退场删除——现只剩这一座闸。) */
76
71
  wakeSchedulerEnabled: boolean;
77
72
  }
78
73
  /** 从插件 Config 提取 entry 默认(settings base 层) */
@@ -17,6 +17,9 @@
17
17
  import type { Context } from 'cordis';
18
18
  /**
19
19
  * 运行态诊断工具(`acc-diag`)。**无动作参数**:一次调用即全报告
20
- * live 运行态 / 面板解析 / 唤醒注册表 / 唤起条件链)。
20
+ * (① live 运行态 / ①b 唤醒时钟 / 唤醒注册表)。
21
+ *
22
+ * ⚠️ 2026-09-15(ACC 侧 autopilot 退场,S142):原四段降为三段——原 ②「面板解析」与
23
+ * ④「唤起条件链」的主语随机制消失,已删(见 `../diag-ops.ts` 文件头;④ 的缺口已登记)。
21
24
  */
22
25
  export declare function createAccDiagTool(ctx: Context): import("@deepseek-ai/dsh-tools").ToolDefinition;
@@ -13,9 +13,9 @@
13
13
  * 参数极简(LLM 直觉):domain(管理域)+ action(该域子命令)+ 少量业务参数。
14
14
  *
15
15
  * v1.34.1(⑥ C6a):由 `export const containerAdminTool` 改为**工厂**
16
- * `createContainerAdminTool(ctx)`——autopilot 域搬进进程内后,`status` 必须读只有进程内
17
- * 能看到的事实(全局闸 / live 会话 / agent 可解析性,见 `../autopilot-ops.js`)。
18
- * 这是本轮唯一为"取 ctx"而做的形态改动(其余域不依赖 ctx,行为不变)。
16
+ * `createContainerAdminTool(ctx)`——原为 autopilot 域传进程内运行态事实(全局闸 / live 会话 /
17
+ * agent 可解析性)。该域已于 2026-09-15 ACC autopilot 整段退场删除(所有者裁决 (a)):
18
+ * `ctx` 现**无消费者**,工厂形态保留只是为了让装配点签名不再变动(见 index.ts 注册处)。
19
19
  */
20
20
  import type { Context } from 'cordis';
21
21
  export declare function createContainerAdminTool(ctx: Context): import("@deepseek-ai/dsh-tools").ToolDefinition;
@@ -1,5 +1,6 @@
1
+ import { r as cccRootForCwd } from "./ccc-roots-Bd_SjEs7.js";
1
2
  import { basename, dirname, join } from "node:path";
2
- import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
3
4
  //#region src/trajectory-ops.ts
4
5
  /**
5
6
  * trajectory-ops.ts — 轨迹工具纯操作层(零 DSH 依赖,可独立单测)
@@ -407,4 +408,189 @@ function summarize(root) {
407
408
  return lines.join("\n");
408
409
  }
409
410
  //#endregion
410
- export { summarize as _, extractSessionMdPathFromText as a, getLastActiveSessionInfo as c, readActiveSessionMd as d, resolveSessionByTitle as f, showSession as g, setActiveSessionInfo as h, createSession as i, listSessions as l, sessionsRoot as m, TRAJECTORY_ACTIONS as n, findSession as o, sessionEvents as p, clearActiveSessionInfo as r, getActiveSessionInfo as s, DEFAULT_SESSION_SCOPE as t, parseSessionContextFromEvents as u, useSession as v };
411
+ //#region src/trajectory-bound.ts
412
+ /**
413
+ * trajectory-bound.ts — SESSION 绑定持久化(CCC 内 `AGENT_SESSIONS/.bindings.json`)
414
+ *
415
+ * 目标(S142 用户拍板,方案 v1.0):让「dsh 会话(载体)↔ SESSION(宁静号轨迹)」
416
+ * 的绑定坚固——LLM 不能因智力因素(幻觉/理解偏差)在过程中或 container_trajectory rebuild 后
417
+ * 自行更换 SESSION。
418
+ *
419
+ * 存储形态(v1.30.6 起,S142 review F-01):绑定**不再写入 dsh 会话日志**。
420
+ * 原因(宿主契约实证):dsp 的自定义事件 `serenity/bound` 既不在宿主的
421
+ * `KNOWN_SESSION_EVENT_TYPES`(生成静态集,明确说明 out-of-repo 插件事件不在其中),
422
+ * 而 `Session.append` 构造事件时只写 `{type,seq,time,data,surfaceOp?,sourceEventSeqs?}`
423
+ * ——**没有写入 envelope `ignorable` 标记的通道**;宿主的 `assertEventsSupported`
424
+ * (session-persistence/coordinator.ts:1248-1252,调用点含恢复/HMR 路径)会拒绝任何
425
+ * "未知且非 ignorable" 的事件 → 含该事件的会话在冷加载时可能抛
426
+ * `SessionFormatUnsupportedError`。v1.29.1~v1.30.5 的设计文档虽写明要带
427
+ * `ignorable: true`(docs/session-binding-hardening-research.md:53),但类型层声明
428
+ * 不等于持久层标记,实际从未落盘。
429
+ *
430
+ * 现形态:每 CCC 一个 JSON 文件 `AGENT_SESSIONS/.bindings.json`(原子写:tmp + rename),
431
+ * 以 dsh 会话 id 为键,latest-wins 覆盖。**向后兼容**:读取时若文件中无该会话记录,
432
+ * 仍回落到扫描会话日志中的旧 `serenity/bound` 事件(老会话的绑定不丢)。
433
+ *
434
+ * 编码无关(U4):绑定锚 = **完整 AGENT_SESSIONS 目录名**(磁盘唯一存在),
435
+ * sessionId(S###/apaas-xxx/自定义)仅是派生展示字段——绝不假设 S 前缀。
436
+ */
437
+ /** 旧形态事件类型(v1.29.1~v1.30.5 写入会话日志;v1.30.6 起只读不写——兼容存量绑定) */
438
+ const SESSION_BOUND_EVENT = "serenity/bound";
439
+ /** 绑定文件相对 CCC 根的路径 */
440
+ const BINDINGS_REL_PATH = "AGENT_SESSIONS/.bindings.json";
441
+ const BINDINGS_VERSION = 1;
442
+ function sessionHeader(session) {
443
+ const header = session?.header;
444
+ if (!header || typeof header !== "object") return null;
445
+ return header;
446
+ }
447
+ /** 绑定文件绝对路径(由会话 cwd 上溯 .serenity 定位 CCC 根);无法定位返回 null */
448
+ function bindingsPathFor(session) {
449
+ const header = sessionHeader(session);
450
+ if (typeof header?.cwd !== "string") return null;
451
+ const root = cccRootForCwd(header.cwd);
452
+ if (!root) return null;
453
+ return join(root, BINDINGS_REL_PATH);
454
+ }
455
+ function readBindingsFile(path) {
456
+ if (!existsSync(path)) return {
457
+ version: BINDINGS_VERSION,
458
+ sessions: {}
459
+ };
460
+ try {
461
+ const sessions = JSON.parse(readFileSync(path, "utf-8")).sessions;
462
+ if (!sessions || typeof sessions !== "object") return {
463
+ version: BINDINGS_VERSION,
464
+ sessions: {}
465
+ };
466
+ return {
467
+ version: BINDINGS_VERSION,
468
+ sessions
469
+ };
470
+ } catch {
471
+ return {
472
+ version: BINDINGS_VERSION,
473
+ sessions: {}
474
+ };
475
+ }
476
+ }
477
+ /** 原子写:同目录 tmp + rename(避免半写文件被后续读取) */
478
+ function writeBindingsFile(path, data) {
479
+ mkdirSync(dirname(path), { recursive: true });
480
+ const tmp = `${path}.tmp`;
481
+ writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
482
+ renameSync(tmp, path);
483
+ }
484
+ /** 记录形状校验(容忍手改/旧数据) */
485
+ function asBoundRecord(value) {
486
+ const r = value;
487
+ if (!r || typeof r.dirName !== "string" || typeof r.mdPath !== "string") return null;
488
+ return r;
489
+ }
490
+ /** 旧形态:从会话日志扫最后一条 `serenity/bound`(兼容 v1.30.5 及更早写入的绑定) */
491
+ function readLegacyBoundFromEvents(session) {
492
+ const events = sessionEvents(session);
493
+ for (let i = events.length - 1; i >= 0; i--) {
494
+ const ev = events[i];
495
+ if (!ev || ev.type !== SESSION_BOUND_EVENT) continue;
496
+ const rec = asBoundRecord(ev.data);
497
+ if (rec) return rec;
498
+ }
499
+ return null;
500
+ }
501
+ /**
502
+ * 读取会话当前绑定(权威,latest-wins):文件记录优先,无则回落旧事件形态。
503
+ * 无绑定返回 null。
504
+ */
505
+ function readLastBound(session) {
506
+ const path = bindingsPathFor(session);
507
+ const id = sessionHeader(session)?.id;
508
+ if (path && typeof id === "string") {
509
+ const rec = asBoundRecord(readBindingsFile(path).sessions[id]);
510
+ if (rec) return rec;
511
+ }
512
+ return readLegacyBoundFromEvents(session);
513
+ }
514
+ /** 目录名 → 轨迹标签(`--S###--` 命中取 `S###`;未命中原样返回——编码无关 U4) */
515
+ function trajectoryLabel(dirName) {
516
+ const m = dirName.match(/--S(\d{3,})--/);
517
+ return m ? `S${m[1]}` : dirName;
518
+ }
519
+ /**
520
+ * **调用方自身**所属 trajectory 的标签(审计字段 `createdBy` 的唯一正解)。
521
+ *
522
+ * 三级来源,逐级回退(R↓:为什么不用单一来源):
523
+ * 1. `readLastBound(session)` —— **持久 bound**,权威(重启存活、不受别的会话激活污染)
524
+ * 2. `getActiveSessionInfo(scope)` —— 本 dsh 会话的**内存**激活(按 scope 隔离)
525
+ * 3. `getLastActiveSessionInfo()` —— **全局**最近激活指针(无绑定的会话兼容回退)
526
+ *
527
+ * ⚠️ 为何单列成本函数(S142 §30.12.1 实测缺陷):唤醒注册表的 `createdBy` 原先直接取第 3 级
528
+ * 全局指针,而它记录的是"**进程内最近一次被激活的 trajectory**"——多会话并发时必然张冠李戴
529
+ * (实测:S142 登记唤醒被写成 `by=S060`)。`createdBy` 是 D60「以可审计替代入口栅」的**唯一凭证**,
530
+ * 失真即替代控制失效 ⇒ 必须以"调用方自己是谁"为准,全局指针只作最后兜底。
531
+ *
532
+ * @param session 调用方 dsh 会话对象(需 `header.id` + `header.cwd`)
533
+ * @param scope 调用方 dsh 会话 id(取不到传空串)
534
+ * @returns 轨迹标签(`S###` 或完整目录名);三级全落空 → 空串(渲染为 `?`)
535
+ */
536
+ function resolveSessionTrajectoryLabel(session, scope) {
537
+ const bound = session ? readLastBound(session) : null;
538
+ if (bound?.dirName) return trajectoryLabel(bound.dirName);
539
+ const scoped = scope !== "" ? getActiveSessionInfo(scope) : null;
540
+ if (scoped) return scoped.sessionId;
541
+ return getLastActiveSessionInfo()?.sessionId ?? "";
542
+ }
543
+ /**
544
+ * 反向查:绑定到某 trajectory 目录名的 dsh 会话 id(**按绑定时间倒序,最新在前**)。
545
+ *
546
+ * 用途(wake-scheduler 冷唤醒):.bindings.json 以 **sessionId 为键**,而唤醒条目的
547
+ * target 是**目录名**——需要值侧扫描做反向索引。冷会话被唤醒时必须先知道"哪个 dsh
548
+ * 会话承载这条轨迹",这是唯一的权威来源(标题只作回退,见 v1.29.2 R2)。
549
+ *
550
+ * @param root CCC 根
551
+ * @param dirName AGENT_SESSIONS 目录名(完整名,如 `2026-09-01--S151--x--auto`)
552
+ * @returns 会话 id 列表(最新绑定在前);无绑定 → `[]`
553
+ */
554
+ function listBoundSessionIds(root, dirName) {
555
+ const file = readBindingsFile(join(root, BINDINGS_REL_PATH));
556
+ const hits = [];
557
+ for (const [id, raw] of Object.entries(file.sessions)) {
558
+ const rec = asBoundRecord(raw);
559
+ if (!rec || rec.dirName !== dirName) continue;
560
+ hits.push({
561
+ id,
562
+ at: typeof rec.at === "number" ? rec.at : 0
563
+ });
564
+ }
565
+ return hits.sort((a, b) => b.at - a.at).map((h) => h.id);
566
+ }
567
+ /**
568
+ * 写入绑定(latest-wins 覆盖该会话记录)。
569
+ *
570
+ * 绑定持久化尽力而为:无法定位 CCC 根 / 无会话 id / 写盘失败 → 返回 false,不阻断主流程。
571
+ * @param session 目标 dsh 会话(需 `header.id` + `header.cwd` 可定位 CCC 根)
572
+ * @param action 绑定动作
573
+ * @param rec 绑定记录(dirName + mdPath 必填;sessionId 可选展示码)
574
+ */
575
+ function appendBound(session, action, rec) {
576
+ const path = bindingsPathFor(session);
577
+ const id = sessionHeader(session)?.id;
578
+ if (!path || typeof id !== "string") return false;
579
+ try {
580
+ const file = readBindingsFile(path);
581
+ file.sessions[id] = {
582
+ dirName: rec.dirName,
583
+ mdPath: rec.mdPath,
584
+ ...rec.sessionId ? { sessionId: rec.sessionId } : {},
585
+ action,
586
+ at: Date.now(),
587
+ ...rec.note ? { note: rec.note } : {}
588
+ };
589
+ writeBindingsFile(path, file);
590
+ return true;
591
+ } catch {
592
+ return false;
593
+ }
594
+ }
595
+ //#endregion
596
+ export { sessionsRoot as _, DEFAULT_SESSION_SCOPE as a, summarize as b, createSession as c, getActiveSessionInfo as d, listSessions as f, sessionEvents as g, resolveSessionByTitle as h, resolveSessionTrajectoryLabel as i, extractSessionMdPathFromText as l, readActiveSessionMd as m, listBoundSessionIds as n, TRAJECTORY_ACTIONS as o, parseSessionContextFromEvents as p, readLastBound as r, clearActiveSessionInfo as s, appendBound as t, findSession as u, setActiveSessionInfo as v, useSession as x, showSession as y };
@@ -18,7 +18,8 @@
18
18
  * 「都合并成 trajectory,废除 logbook 这个词」并逐条删并 ——
19
19
  * summary 并进 list|health/qa 淘汰(判据=旧模板,EAP 已取代;仅"空壳/长期无活动"并入 use)|
20
20
  * close/archive 删(completed 由 SESSION.md 的 `[x]` 推导;归档走 container_fs mv)|
21
- * hook-develop-guide 并进 container_admin msm guide|autopilot 面归 container_admin 的 autopilot 域|
21
+ * hook-develop-guide 并进 container_admin msm guide|autopilot 面归 container_admin 的 autopilot
22
+ * (该域 2026-09-15 随 ACC 侧 autopilot 退场删除)|
22
23
  * wake-add/list/rm 收敛为 wake-later。
23
24
  */
24
25
  type TrajectoryAction = 'list' | 'show' | 'create' | 'use' | 'rebuild' | 'wake-later';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * voyage-page.ts — `/serenity/voyage`:宁静号航行动画(**ACC 层静态资产**)
3
+ *
4
+ * 作用:把「宁静号航行动画」作为 **ACC 的一部分**随包分发,并用一条 HTTP 路由把它
5
+ * 交给 WebUI。当前唯一消费者 = 会话头部状态胶囊展开卡片(`SafeModePanel` 的 `sp-pop`)
6
+ * 的**背景层**——即所有者定的定位:「宁静号的心智模型」。
7
+ *
8
+ * 资产本体:`assets/serenity-voyage.html`(单文件自包含,three.js r180 内联)。
9
+ * 同一份文件两种用法,靠 query 切换:
10
+ * - 无参数 → **全屏构图版**(标题/品牌语/航速/容器事实面板齐备),可独立打开
11
+ * - `?card=1` → **卡片模式**:隐藏自带 UI 层、镜头拉远、星野调暗、铭牌层关闭
12
+ *
13
+ * 为什么用「独立路由 + iframe」而不是内联进 client bundle:
14
+ * ① 作品自包含 830 KB(其中 704 KB 是 three.js)——内联会把整包灌进 client chunk;
15
+ * ② 作品自带全局 CSS/画布,内联会与卡片样式互相污染;
16
+ * ③ 独立路由 ⇒ **惰性加载**(只在卡片展开时请求一次)+ 可长缓存,关闭 iframe 即卸载,
17
+ * WebGL 上下文随之释放。
18
+ *
19
+ * 来历(R↓):作品由 **tiangong-serenity**(另一个 Serenity CCC)赠予;**本仓库只携带
20
+ * 构建产物**,源与构建脚本住在 CCC 侧 `AGENT_SESSIONS/…S142…/serenity-voyage-animation/`
21
+ * (详见 `assets/README.md`)。内容已于 S142 重播为宁静号自己的技能/公理/MSM/本体词元
22
+ * 与实测容器规模数字。
23
+ */
24
+ import type { Context } from 'cordis';
25
+ export declare const VOYAGE_PATH = "/serenity/voyage";
26
+ /**
27
+ * 资产相对包根的路径。
28
+ * 🔒 它**必须**同时出现在 `package.json` 的 `files` 白名单里——否则发布包缺文件,
29
+ * 路由会在运行时报「资产不可用」(同 v1.26.15 那次 `files` 漏 chunk 的事故形态)。
30
+ * `dsh-develop pack-check` 的 `required` 已把它列为发布前硬断言。
31
+ */
32
+ export declare const VOYAGE_ASSET = "assets/serenity-voyage.html";
33
+ /** 资产绝对路径(导出以便测试与 pack-check 共用同一判据来源) */
34
+ export declare function voyageAssetPath(): string;
35
+ /** 读取资产全文。读不到即抛错(由 handler 转 500,绝不静默发空页)。 */
36
+ export declare function loadVoyageHtml(assetPath?: string): string;
37
+ /** 清缓存(测试用) */
38
+ export declare function resetVoyageCache(): void;
39
+ /**
40
+ * 注册 `/serenity/voyage`(GET)。
41
+ * @param opts.assetPath 覆盖资产路径(测试用;生产不传)
42
+ */
43
+ export declare function registerVoyageApi(ctx: Context, opts?: {
44
+ assetPath?: string;
45
+ }): void;
@@ -5,8 +5,9 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "
5
5
  * wake-registry.ts — trajectory 唤醒注册表(D58,S142 2026-09-13)
6
6
  *
7
7
  * 需求(用户原话):一个 CCC 内允许多个 trajectory 任意形态并行;某个 trajectory 可以
8
- * **预定未来唤醒自己或别人**,本质上是一次「未来时刻 + 一条 message」的投递;autopilot
9
- * 是「周期自唤醒」的特例(独立循环,D59)。协作边界:互相可见、**无阻塞无等待**。
8
+ * **预定未来唤醒自己或别人**,本质上是一次「未来时刻 + 一条 message」的投递;「周期自唤醒」
9
+ * 曾是它的特例(原 ACC autopilot / D59,该机制已于 2026-09-15 整段退场)。协作边界:
10
+ * 互相可见、**无阻塞无等待**。
10
11
  *
11
12
  * 本模块只负责**注册表本身**(纯数据层,无宿主依赖,可单测):
12
13
  * - 落点:CCC 内 `AGENT_SESSIONS/wake-registry.json`(随 CCC git,人可读可审计)
@@ -2,8 +2,9 @@
2
2
  * wake-registry.ts — trajectory 唤醒注册表(D58,S142 2026-09-13)
3
3
  *
4
4
  * 需求(用户原话):一个 CCC 内允许多个 trajectory 任意形态并行;某个 trajectory 可以
5
- * **预定未来唤醒自己或别人**,本质上是一次「未来时刻 + 一条 message」的投递;autopilot
6
- * 是「周期自唤醒」的特例(独立循环,D59)。协作边界:互相可见、**无阻塞无等待**。
5
+ * **预定未来唤醒自己或别人**,本质上是一次「未来时刻 + 一条 message」的投递;「周期自唤醒」
6
+ * 曾是它的特例(原 ACC autopilot / D59,该机制已于 2026-09-15 整段退场)。协作边界:
7
+ * 互相可见、**无阻塞无等待**。
7
8
  *
8
9
  * 本模块只负责**注册表本身**(纯数据层,无宿主依赖,可单测):
9
10
  * - 落点:CCC 内 `AGENT_SESSIONS/wake-registry.json`(随 CCC git,人可读可审计)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.34.2",
3
+ "version": "1.36.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.5-rc.2。",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,6 +28,9 @@
28
28
  "lib/*.js",
29
29
  "lib/*.d.ts",
30
30
  "lib/**/*.d.ts",
31
+ "assets/serenity-voyage.html",
32
+ "assets/serenity-voyage-preview.png",
33
+ "assets/README.md",
31
34
  "cordis.patch.yml",
32
35
  "dsh.plugin.json",
33
36
  "README.md"
@@ -1,76 +0,0 @@
1
- /**
2
- * autopilot-chain.ts — **唤起条件链的唯一实现**("为什么这轮没被唤起")
3
- *
4
- * 为什么单独成模块(R↓,⑥ C6a 的硬约束):
5
- * C6 裁决把 autopilot 拆两半——**入口**分两处(CCC 工具面 / 开发面),但**判据只能有一份**。
6
- * 若开发面另写"文件读取版",刚消掉的分歧会在开发面复活。故:
7
- * · `acc-diag` ④ 段(进程内,带运行态事实)—— `./diag-ops.ts` 调 {@link buildWakeChain}
8
- * · 开发面 `dsh-develop diag [--ccc <path>]`(离线 bun 进程,无运行态事实)—— **import 同一实现**
9
- * 两者的差别**只是传入的 facts**(进程内三项已知 / 离线三项不可知),判据本身不复制。
10
- *
11
- * 本模块**零 DSH 依赖**(只 node:fs/path + `ccc.js` + `autopilot-core.js`)——离线 bun 进程
12
- * 才 import 得动。判据一律取自 {@link judgeWake}(判据唯一真相源),本模块只做**分解与分类**。
13
- *
14
- * 判决语义(§12.7 修复,**不得回退**):
15
- * `✗` = 配置/环境问题(需人改,如 enabled=false / session 未命中 / agent 未加载)
16
- * `⏸` = 等待中(到点/出窗口/上一轮跑完自会满足——**非**配置问题)
17
- * `?` = 不可知(离线通道看不到的运行态;**不**当成满足)
18
- * **只有 `✗` 与 `⏸` 都为空、且无 `?` 时才印 ✅**——旧实现只统计 `✗` ⇒ 每天北京 8~18 点
19
- * (10 小时)都印「✅ 唤起条件全部满足」而插件实际不唤起(假阳性,已实测复现)。
20
- */
21
- /**
22
- * 条件链的**运行态事实**(只有插件进程知道的那部分;离线通道传 null = 不可知)。
23
- * 由 `autopilot-trajectory.ts` 的 `autopilotRuntimeFacts(ctx, root)` 产出。
24
- */
25
- export interface WakeChainFacts {
26
- /** 周期自唤醒全局闸(`autopilotGloballyEnabled()`);null = 不可知(离线通道) */
27
- globalGate: boolean | null;
28
- /** 该 CCC 是否已有唤起轮在进行中(重入守卫);null = 不可知 */
29
- running: boolean | null;
30
- /** live 运行态;null = 不可知(离线通道) */
31
- live: {
32
- /** 该 CCC 的 live 会话数 */
33
- sessionCount: number;
34
- /** 目标 agent 能否解析;null = 不适用(目标会话未命中,已由 session 条件判 ✗) */
35
- agentResolved: boolean | null;
36
- /** agent 不可解析时的诊断文本(`diagnoseTargetUnavailable`) */
37
- diagnosis: string | null;
38
- } | null;
39
- }
40
- /** 离线通道事实(三项运行态全不可知) */
41
- export declare const NO_RUNTIME_FACTS: WakeChainFacts;
42
- /** 判决档(`ready` 只有在**没有** unknowns 时才可能给出——离线通道永远不会印 ✅) */
43
- export type WakeChainVerdict = 'ready' | 'waiting' | 'blocked' | 'unknown';
44
- export interface WakeChainReport {
45
- root: string;
46
- /** 逐条件行(人读;行首 = ✓/✗/⏸/?/·) */
47
- lines: string[];
48
- /** `✗` 行(配置/环境问题,需人改) */
49
- blockers: string[];
50
- /** `⏸` 行(等待中) */
51
- waiting: string[];
52
- /** `?` 行(不可知——不当作满足) */
53
- unknowns: string[];
54
- /** 修复建议(仅在存在阻断点时输出) */
55
- suggestions: string[];
56
- verdict: WakeChainVerdict;
57
- /** 判决行原文 */
58
- judgement: string;
59
- }
60
- /** 运行偏见脚本的注入点(测试可替换;生产 = `fetchBiasContent`——插件 tick 的同一执行路径) */
61
- export type BiasProbe = (root: string, providerRel: string) => Promise<{
62
- text: string | null;
63
- error: string | null;
64
- }>;
65
- /**
66
- * 组装条件链(**唯一实现**)。
67
- *
68
- * @param root CCC 根
69
- * @param facts 运行态事实(离线通道传 {@link NO_RUNTIME_FACTS})
70
- * @param now 当前时刻(测试注入)
71
- * @param biasProbe 偏见执行(默认 {@link fetchBiasContent}——**不另写 spawn**)
72
- * @returns 结构化报告(渲染见 {@link renderWakeChain})
73
- */
74
- export declare function buildWakeChain(root: string, facts?: WakeChainFacts, now?: number, biasProbe?: BiasProbe): Promise<WakeChainReport>;
75
- /** 条件链渲染(④ 段文本;`renderAccDiag` 与开发面 `diag` 共用) */
76
- export declare function renderWakeChain(r: WakeChainReport): string;