@shgroup/dsh-serenity-hooks 1.32.0 → 1.34.1

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.
Files changed (100) hide show
  1. package/README.md +12 -11
  2. package/dsh.plugin.json +5 -5
  3. package/lib/acp-core.d.ts +2 -22
  4. package/lib/acp-http.d.ts +8 -0
  5. package/lib/api.d.ts +12 -4
  6. package/lib/autopilot-chain.d.ts +76 -0
  7. package/lib/autopilot-core-DCRkiWEe.js +229 -0
  8. package/lib/autopilot-core.d.ts +132 -0
  9. package/lib/autopilot-ops.d.ts +48 -0
  10. package/lib/autopilot-trajectory-D9Znxo75.js +782 -0
  11. package/lib/autopilot-trajectory.d.ts +79 -83
  12. package/lib/{ccc-DAsSHsub.js → ccc-NlLr_sxy.js} +18 -2
  13. package/lib/ccc-roots-Bd_SjEs7.js +152 -0
  14. package/lib/ccc-roots.d.ts +105 -0
  15. package/lib/ccc.d.ts +23 -6
  16. package/lib/client.js +30 -10
  17. package/lib/clock-runtime.d.ts +197 -0
  18. package/lib/config-ops.d.ts +9 -14
  19. package/lib/constants.d.ts +0 -2
  20. package/lib/container-status.d.ts +183 -0
  21. package/lib/diag-ops.d.ts +76 -0
  22. package/lib/face-host.d.ts +110 -0
  23. package/lib/fs-ops.d.ts +3 -2
  24. package/lib/gateway-auth.d.ts +2 -5
  25. package/lib/gateway.d.ts +0 -34
  26. package/lib/git-ops.d.ts +3 -2
  27. package/lib/handyman-ops.d.ts +3 -2
  28. package/lib/handyman-preset-inherit.d.ts +2 -1
  29. package/lib/host/access.d.ts +8 -7
  30. package/lib/host/contract.d.ts +17 -6
  31. package/lib/host/type-contract.d.ts +1 -2
  32. package/lib/im-bridge.d.ts +6 -5
  33. package/lib/index.d.ts +1 -1
  34. package/lib/index.js +11053 -8305
  35. package/lib/invariant.d.ts +5 -16
  36. package/lib/invariant.js +8 -4
  37. package/lib/json.d.ts +1 -1
  38. package/lib/kit-ops.d.ts +11 -31
  39. package/lib/localstore-ops.d.ts +3 -17
  40. package/lib/msm-ops.d.ts +34 -52
  41. package/lib/opencode-provider.d.ts +4 -5
  42. package/lib/output-guard-seam.d.ts +1 -4
  43. package/lib/output-guard.d.ts +12 -3
  44. package/lib/ports.d.ts +59 -0
  45. package/lib/rebuild.d.ts +5 -4
  46. package/lib/seams/bootstrap.d.ts +4 -3
  47. package/lib/seams/compact.d.ts +2 -1
  48. package/lib/seams/context.d.ts +2 -4
  49. package/lib/seams/env.d.ts +2 -1
  50. package/lib/seams/guards.d.ts +22 -34
  51. package/lib/seams/keeper.d.ts +4 -4
  52. package/lib/seams/system-prompt.d.ts +1 -5
  53. package/lib/session-cleanup.d.ts +3 -2
  54. package/lib/{settings-section-Bm-xG-NR.js → settings-section-DTYZjwUs.js} +70 -15
  55. package/lib/settings-section.d.ts +14 -35
  56. package/lib/skiff-core.d.ts +5 -10
  57. package/lib/skiff-debug.d.ts +15 -21
  58. package/lib/skiff-registry-FTjoWQTQ.js +25 -0
  59. package/lib/skiff-registry.d.ts +2 -1
  60. package/lib/{skiff-role-BYvNnwv1.js → skiff-role-CEHL5cek.js} +1 -1
  61. package/lib/skiff-role.d.ts +2 -2
  62. package/lib/skills/opencode-scan.d.ts +3 -2
  63. package/lib/skills-discovery.d.ts +15 -2
  64. package/lib/status.d.ts +2 -1
  65. package/lib/tools/acc-diag.d.ts +22 -0
  66. package/lib/tools/cce.d.ts +1 -2
  67. package/lib/tools/container-admin.d.ts +7 -3
  68. package/lib/tools/eap.d.ts +2 -3
  69. package/lib/tools/handyman.d.ts +0 -29
  70. package/lib/tools/msm.d.ts +0 -4
  71. package/lib/tools/neat.d.ts +1 -2
  72. package/lib/tools/praxis.d.ts +0 -1
  73. package/lib/tools/skiff-admin.d.ts +2 -2
  74. package/lib/tools/{session.d.ts → trajectory.d.ts} +25 -17
  75. package/lib/totp.d.ts +0 -4
  76. package/lib/trajectory-assistant.d.ts +2 -1
  77. package/lib/trajectory-bound-Cax1p9ut.js +190 -0
  78. package/lib/{session-bound.d.ts → trajectory-bound.d.ts} +5 -6
  79. package/lib/trajectory-ops-mrXK0H0I.js +410 -0
  80. package/lib/{session-ops.d.ts → trajectory-ops.d.ts} +27 -36
  81. package/lib/trajectory-skills.d.ts +71 -0
  82. package/lib/wake-registry-CYxIb484.js +257 -0
  83. package/lib/wake-registry.d.ts +3 -11
  84. package/lib/wake-scheduler.d.ts +23 -20
  85. package/lib/web-fetch-provider.d.ts +0 -21
  86. package/lib/{weixin-api-PjEhmjlZ.js → weixin-api-CtFYm53S.js} +3 -9
  87. package/lib/weixin-api.d.ts +6 -37
  88. package/lib/weixin-bridge.d.ts +5 -24
  89. package/lib/weixin-hook.d.ts +6 -12
  90. package/lib/weixin-output-guard.d.ts +2 -1
  91. package/lib/{weixin-route-wWk4AIwV.js → weixin-route-B2ajFypI.js} +4 -5
  92. package/lib/weixin-route.d.ts +0 -2
  93. package/lib/weixin-send-api.d.ts +15 -6
  94. package/package.json +3 -5
  95. package/experiments/autopilot-trajectory/SKILL.md +0 -115
  96. package/experiments/autopilot-trajectory/scripts/autopilot-trajectory.ts +0 -522
  97. package/lib/session-bound-NHozeLFc.js +0 -822
  98. package/lib/skiff-debug-K2k1JnGj.js +0 -2867
  99. package/lib/tools/autopilot-trajectory.d.ts +0 -25
  100. package/lib/{session-cleanup-CMT4mHWt.js → session-cleanup-B6IHZgww.js} +1 -1
@@ -26,69 +26,20 @@
26
26
  * · **默认关**:serenity.json autopilotTrajectory.enabled=false → 定时器不启动,零资源占用。
27
27
  */
28
28
  import type { Context } from 'cordis';
29
- import { type AutopilotTrajectorySettings } from './ccc.js';
30
- /** 偏见内容提供者脚本缺省名(CCC 根目录下;正式版 autopilot-bias.ts) */
31
- export declare const DEFAULT_BIAS_PROVIDER = "autopilot-bias.ts";
32
- /** 旧默认偏见脚本名(autotrajectory 时代;未显式配置时回退——pangu 等已配置 CCC 零迁移) */
33
- export declare const LEGACY_BIAS_PROVIDER = "autotrajectory-bias.ts";
34
- /** 自主轨迹会话目录后缀标志(--auto 保留——简短且历史会话 S060--auto 已存在) */
35
- export declare const AUTO_DIR_SUFFIX = "--auto";
36
- /** 调度 tick 周期(5min——用户"tick改为5分钟吧";0.01h 间隔下每 5min 评估一次) */
37
- export declare const TICK_MS: number;
38
- /** 间隔下限(支持小数——用户"让它支持小数行吗,这样可以配0.01";0.01h ≈ 36s) */
39
- export declare const MIN_INTERVAL_HOURS = 0.01;
40
- /** SESSION.md 内「下一轮动机」段标记(自生偏见载体——轨迹预设自己的未来) */
41
- export declare const MOTIVATION_MARKER = "\u4E0B\u4E00\u8F6E\u52A8\u673A";
42
- /** 缺省避开的高峰时段(北京时间 [start, end) 不唤起——用量峰谷省钱) */
43
- export declare const DEFAULT_AVOID_HOURS: {
44
- start: number;
45
- end: number;
46
- };
47
- /** 偏见脚本输出上限(沙箱:截断防失控——正式版 v1.27.4) */
48
- export declare const BIAS_OUTPUT_MAX: number;
49
- /** 偏见脚本执行超时(正式版沙箱;原 600s 过长阻塞 tick) */
50
- export declare const BIAS_RUN_TIMEOUT_MS = 60000;
51
- /** 审计历史 ring 上限(每 CCC 保留最近 N 条唤起记录) */
52
- export declare const AUDIT_HISTORY_MAX = 50;
53
- /** 读配置:`trajectory.autopilot`(D58 新键)优先 → 旧键 autopilotTrajectory → 旧键 autotrajectory */
54
- export declare function readAutopilotSettings(root: string): AutopilotTrajectorySettings | null;
55
- /**
56
- * 北京时间(UTC+8)当前小时——不依赖服务器时区(服务器可能 UTC/本地任意)。
57
- */
58
- export declare function beijingHour(nowMs: number): number;
59
- /**
60
- * 唤起窗口判定:北京时间 [avoidStart, avoidEnd) 内不唤起(缺省 8~18)。
61
- * 单段避开(start<=end):窗口 = [0,start) ∪ [end,24);跨零点避开(start>end):窗口 = [end,start)。
62
- */
63
- export declare function inAllowedWakeWindow(nowMs: number, avoid?: {
64
- start?: number;
65
- end?: number;
66
- }): boolean;
67
- /** 标志位判定:SESSION.md 所在目录名以 --auto 结尾 → 自主轨迹形态 */
68
- export declare function isAutopilotSession(mdPath: string): boolean;
69
- /**
70
- * 目标会话定位:**必须配置 cfg.session**(S###/目录名)——CCC 日常有多条 trajectory 在跑,
71
- * 绝不默认唤起(缺省最近活跃会误伤其他正在运行的轨迹;用户拍板:必须配置才生效)。
72
- * 未配置 session 或未命中 → null(不唤起)。
73
- */
74
- export declare function resolveTargetMd(root: string, cfg: AutopilotTrajectorySettings): string | null;
75
- /**
76
- * 唤起条件(纯逻辑,可测):enabled + 未在运行 + 目录标志 + mtime 超间隔 + 窗口允许。
77
- * v1.27.12:移除每日唤起预算上限(用户"把这个上限删了吧,没意义"——高频实验不受限)。
78
- */
79
- export declare function shouldWake(settings: AutopilotTrajectorySettings, mdPath: string | null, nowMs: number, running: boolean, _history?: readonly WakeRecord[]): boolean;
80
- /** 自生动机读取:SESSION.md「下一轮动机」段内容(到下一个二级标题或文件尾;无 → null) */
81
- export declare function readSelfGeneratedMotivation(mdPath: string): string | null;
29
+ import type { AutopilotTrajectorySettings } from './ccc.js';
30
+ import { type WakeRecord } from './autopilot-core.js';
31
+ import { type ClockRuntime } from './clock-runtime.js';
32
+ import type { WakeChainFacts } from './autopilot-chain.js';
82
33
  /**
83
- * 偏见内容:直接运行 CCC 根目录下偏见提供者脚本(biasProvider,缺省 autopilot-bias.ts;
84
- * 未显式配置且新默认缺失 回退旧默认 autotrajectory-bias.ts——pangu 兼容)→ stdout。
85
- * 脚本缺失 返回 { text: null, error: 提示实现 }(唤起侧报错要求实现,不静默跳过)。
86
- * 路径逃逸校验(resolveInside);bun 优先,node 兜底;**60s 超时 + 8KB 输出截断(沙箱)**。
34
+ * 判据原语(`readAutopilotSettings` / `beijingHour` / `inAllowedWakeWindow` /
35
+ * `isAutopilotSession` / `resolveTargetMd` / `judgeWake` / `shouldWake` /
36
+ * `readSelfGeneratedMotivation` / `fetchBiasContent` / 常量)**已提取至
37
+ * `autopilot-core.ts`**(S142 §12.13 C6a)——本文件是它们的**组合方之一**
38
+ * (tick + 面板 + agent 定位),不再自持判据副本。
39
+ * 提取理由与"三处消费方共用一份判据"的约束见该文件头部注释。
87
40
  */
88
- export declare function fetchBiasContent(root: string, providerRel: string): Promise<{
89
- text: string | null;
90
- error: string | null;
91
- }>;
41
+ /** 审计历史 ring 上限(每 CCC 保留最近 N 条唤起记录) */
42
+ export declare const AUDIT_HISTORY_MAX = 50;
92
43
  /** 唤起消息(四段式:轨迹焦点[CCC 定义,稳定] / 身份锚定 / 先验偏见[自生动机+偏见内容] / 任务)——注入前台会话,用户可见 */
93
44
  export declare function buildWakeMessage(opts: {
94
45
  sessionName: string;
@@ -99,13 +50,7 @@ export declare function buildWakeMessage(opts: {
99
50
  biasContent: string | null;
100
51
  }): string;
101
52
  /** 唤起结果(tick 日志 / 面板显示 / 审计记录共用) */
102
- export interface WakeResult {
103
- ok: boolean;
104
- detail: string;
105
- }
106
- /** 审计记录(每 CCC ring buffer——正式版 v1.27.4:可回看、可分析) */
107
- export interface WakeRecord {
108
- time: number;
53
+ interface WakeResult {
109
54
  ok: boolean;
110
55
  detail: string;
111
56
  }
@@ -125,6 +70,39 @@ export declare function resetWakeHistory(): void;
125
70
  export declare function performAutopilotWake(ctx: Context, root: string, settings: AutopilotTrajectorySettings, opts?: {
126
71
  force?: boolean;
127
72
  }): Promise<WakeResult>;
73
+ /**
74
+ * **周期自唤醒全局闸**(v1.34 更名收窄,原 `autopilotEnabled`):关 = 定时器不武装 + tick 不唤起。
75
+ *
76
+ * 语义边界(用户 2026-09-15 裁决 S-1):**只管周期自唤醒** —— 一次性唤醒(`container_trajectory wake-later`)
77
+ * 归 `wakeSchedulerEnabled`,两者**互不连带**(旧实现共用一闸是缺陷,见 wake-scheduler 注释)。
78
+ *
79
+ * 读取顺序(迁移期,不回写):`autopilotWakeEnabled`(新键)→ `autopilotEnabled`(旧键)→ false。
80
+ * 用 `??` 而非 `||` 的理由:**新键显式 false 必须能覆盖旧键 true**(否则"关不掉")。
81
+ * 抽成模块级函数是为了让 {@link autopilotClockState}(诊断)与 tick 读**同一判据**。
82
+ *
83
+ * v1.34.1(⑥ C6a):**导出**——CCC 工具面(`container_admin autopilot status`)与条件链
84
+ * ({@link autopilotRuntimeFacts})必须读**这一份**闸值。旧独立脚本读不到它 ⇒ 闸关着也印
85
+ * 「✅ 唤起条件全部满足」(8 项分歧之首,见 SESSION §12.13 与现状稿 §4.4)。
86
+ */
87
+ export declare function autopilotGloballyEnabled(): boolean;
88
+ /**
89
+ * autopilot **进程态**类型(诊断用,模块级 = 进程级)。
90
+ *
91
+ * C6b(P2):字段由 {@link ClockRuntime}(**工厂那份**)提供,**字段名与语义一个字未改**。
92
+ * 与 {@link wakeSchedulerState} 的差别只有一项:本钟**没有** `lastTickLog`(结果进下方
93
+ * `wakeHistory` 审计 ring,不走 tick 日志)——故 `ClockRuntime.lastTickLog` 在工厂里是可选的。
94
+ */
95
+ export type AutopilotClockRuntime = ClockRuntime;
96
+ declare const clock: import("./clock-runtime.js").Clock;
97
+ /**
98
+ * autopilot **进程态**快照(只读;`enabled` = 全局闸**当前**值)。
99
+ * ⚠️ 与 {@link wakeSchedulerState} 同规格,但**本钟无 `lastTickLog`**(见 {@link ClockOptions.logFrom})。
100
+ */
101
+ export declare function autopilotClockState(): ReturnType<typeof clock.snapshot>;
102
+ /** 测试用:复位进程态(含 per-CCC 重入守卫,避免用例间串味) */
103
+ export declare function __resetAutopilotClockStateForTest(): void;
104
+ /** 该 CCC 是否有唤起轮正在进行中(tick 的 per-CCC 防重入判据;诊断读同一份) */
105
+ export declare function autopilotWakeInFlight(root: string): boolean;
128
106
  /**
129
107
  * 装配(index.ts apply 调用)。时钟唤起(v1.26.14 修复 + v1.27.4 多 CCC 独立):
130
108
  *
@@ -132,12 +110,12 @@ export declare function performAutopilotWake(ctx: Context, root: string, setting
132
110
  * 修复:① 每次 tick 动态解析 root+settings ② 监听 session/created 启动定时器 ③ 优先实验 CCC。
133
111
  *
134
112
  * **v1.27.4 多 CCC 独立(用户"4个CCC能各自有autotrajectory吗")**:
135
- * - 单定时器保留(TICK_MS 10min),每次 tick **遍历所有 live+enabled CCC**(collectAutopilotCccs)
113
+ * - 单定时器保留(TICK_MS 5min),每次 tick **遍历所有 live+enabled CCC**(collectAutopilotCccs)
136
114
  * 各自评估 shouldWake + 各自唤起——每 CCC 的 interval/session/bias/topPrompt/窗口独立
137
115
  * - `running` 守卫 **per-CCC**(不同 CCC 唤起互不阻塞)
138
116
  * - **全局串行化**(wakeChain:同 tick 多 CCC 到点 → 依次唤起,防模型并发挤兑)
139
117
  *
140
- * 零资源占用语义保留:无 enabled CCC → tick 内直接 return(定时器存在但每 10min 一次空检查,
118
+ * 零资源占用语义保留:无 enabled CCC → tick 内直接 return(定时器存在但每 5min 一次空检查,
141
119
  * unref 不阻塞进程退出)。
142
120
  */
143
121
  export declare function registerAutopilot(ctx: Context): void;
@@ -147,7 +125,7 @@ export declare function registerAutopilot(ctx: Context): void;
147
125
  * 命中/标志/空闲时长 + 当前窗口/可唤起判定 + 审计(最近唤起)。
148
126
  * 不运行偏见脚本(只报脚本是否就绪——运行验证走 trajectory random)。
149
127
  */
150
- export interface AutopilotTrajectoryStatus {
128
+ interface AutopilotTrajectoryStatus {
151
129
  /** 是否配置了 trajectory.autopilot 段(.opencode/serenity.json;旧键 autopilotTrajectory / autotrajectory 回退) */
152
130
  configured: boolean;
153
131
  /** 总开关(缺省 false——未开零资源占用) */
@@ -185,13 +163,21 @@ export interface AutopilotTrajectoryStatus {
185
163
  }
186
164
  export declare function getAutopilotStatus(root: string): AutopilotTrajectoryStatus;
187
165
  /** live 会话条目(诊断/面板解析用;标题从 events 读) */
188
- export interface LiveSessionEntry {
166
+ interface LiveSessionEntry {
189
167
  id: string;
190
168
  cwd: string | null;
191
169
  cccRoot: string | null;
192
170
  title: string | null;
193
171
  }
194
- /** 遍历 live 会话(sessions.list())+ 补标题(events session/title latest-wins)+ ccc 归属 */
172
+ /**
173
+ * live 会话清单(诊断/面板解析用;标题从 events 读)——**只枚举 live 会话本身**。
174
+ *
175
+ * C2:CCC 枚举("本机有哪些 CCC")**不在这里**,归 `ccc-roots.listCccs`(并集)。
176
+ * 本函数保留的是它**独有**的那部分:逐条 live 会话 + cwd 归属 + 标题 + id
177
+ * (诊断页需要"哪个会话属于哪个 CCC",那是会话维度、不是 CCC 维度)。
178
+ * @param ctx 插件上下文
179
+ * @returns 逐条 live 会话(`cccRoot` = 该会话 cwd 的 CCC 归属,无 → null)
180
+ */
195
181
  export declare function listLiveSessions(ctx: Context): LiveSessionEntry[];
196
182
  /**
197
183
  * 收集所有「配置了 Autopilot Trajectory 的 live CCC」——**v1.27.4 多 CCC 独立**:
@@ -201,16 +187,6 @@ export declare function listLiveSessions(ctx: Context): LiveSessionEntry[];
201
187
  export declare function collectAutopilotCccs(ctx: Context, opts?: {
202
188
  includeDisabled?: boolean;
203
189
  }): string[];
204
- /**
205
- * 所有 live 会话所属的 CCC 根(去重,保持出现顺序)。
206
- *
207
- * 与 {@link collectAutopilotCccs} 的区别:**不要求该 CCC 配置 trajectory.autopilot**——
208
- * 唤醒注册表(wake-scheduler)的条目可以指向任何 CCC 的任何 trajectory。
209
- *
210
- * @param ctx 插件上下文
211
- * @returns CCC 根路径列表(无 live 会话 → `[]`)
212
- */
213
- export declare function collectLiveCccs(ctx: Context): string[];
214
190
  /**
215
191
  * 进程内诊断(trajectory diag-live 数据源;用户"排查访问不到 pangu 写个 msm")——
216
192
  * 输出当前实例 live 会话清单(id/cwd/ccc/标题)+ 每个配置了 Autopilot 的 CCC
@@ -232,3 +208,23 @@ export interface DiagLiveReport {
232
208
  panelResolved: string | null;
233
209
  }
234
210
  export declare function diagLive(ctx: Context): DiagLiveReport;
211
+ /**
212
+ * **条件链的运行态事实**(⑥ C6a 的核心出口)——把只有插件进程能看到的判据交给
213
+ * 条件链({@link buildWakeChain} 的输入):全局闸 / 重入守卫 / live 运行态。
214
+ *
215
+ * 为什么必须有(这是 C6 的动机本身):旧独立脚本只能读文件 ⇒ 结构性地看不到这三样,
216
+ * 于是逐条件报告**必然**与实际唤起行为分歧(8 项,见 `acc-component-relations-current-state.md` §4.4)。
217
+ * 搬进进程内之后,`container_admin autopilot status` 与 `acc-diag` ④ 段读的就是**同一份事实**。
218
+ *
219
+ * `ctx` 可传 `null`(**不是**"三项全不可知"):全局闸(`readSimpleSettings`)与重入守卫
220
+ * (模块级 `runningByRoot`)**都不需要 ctx**,无 ctx 时也应读出真值——只有 live 运行态
221
+ * (live 会话 / agent 可解析)依赖 ctx,那才是真正的"不可知"。
222
+ * 这条区分由 `autopilot-ops.test.ts` 的全局闸可见性用例逼出来(首版把三者一起降级为不可知,
223
+ * 使"闸关"在无 ctx 路径下隐形——正是本轮要消灭的那类假报告)。
224
+ *
225
+ * @param ctx 插件上下文(读 live 会话 + agent);null = 无进程上下文(live 标不可知)
226
+ * @param root 目标 CCC 根
227
+ * @returns 条件链事实(`live` 为 null 表示"live 运行态不可知")
228
+ */
229
+ export declare function autopilotRuntimeFacts(ctx: Context | null, root: string): WakeChainFacts;
230
+ export {};
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync, statSync } from "node:fs";
2
1
  import { dirname, resolve } from "node:path";
2
+ import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  //#region src/ccc.ts
4
4
  /**
5
5
  * ccc.ts — CCC 纯逻辑层(零 DSH 依赖,可独立单测)
@@ -139,6 +139,22 @@ function loadSerenityConfig(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
139
139
  }
140
140
  return {};
141
141
  }
142
+ /**
143
+ * 读取 CCC 声明的「专属工具」清单(`exclusiveTools`,v1.33)。
144
+ *
145
+ * 失败语义:无配置/字段缺失/配置损坏 → **空数组**(= 不声明任何专属工具 ⇒ 专属工具保持隐藏)。
146
+ * 这里选 **fail-closed**(与 im-bridge 的 fail-open 相反)的理由:专属工具的默认态就是"隐藏",
147
+ * 判据读不到 = 没有证据表明该 CCC 要它;而配置损坏本身已在 `loadSerenityConfig` 响亮告警
148
+ * (不会静默)。im-bridge 相反——隐藏一个**已在使用**的通道才会静默损害能力。
149
+ * @param root CCC 根
150
+ * @param paths 候选配置相对路径
151
+ * @returns 去空白后的工具名列表(非字符串项被丢弃)
152
+ */
153
+ function readExclusiveTools(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
154
+ const list = loadSerenityConfig(root, paths).exclusiveTools;
155
+ if (!Array.isArray(list)) return [];
156
+ return list.filter((x) => typeof x === "string" && x.trim() !== "").map((x) => x.trim());
157
+ }
142
158
  const SAFE_MODE_MARKER = ".serenity-safe-on";
143
159
  function isSafeModeOn(root) {
144
160
  return existsSync(resolve(root, SAFE_MODE_MARKER));
@@ -177,4 +193,4 @@ function matchBlacklist(relPath, rules) {
177
193
  return null;
178
194
  }
179
195
  //#endregion
180
- export { findSerenityRoot as a, matchBlacklist as c, readCccName as d, readHandymanConfig as f, findGitRoot as i, pathInside as l, resolveInside as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readUtf8 as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
196
+ export { findSerenityRoot as a, matchBlacklist as c, readCccName as d, readExclusiveTools as f, resolveInside as h, findGitRoot as i, pathInside as l, readUtf8 as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readHandymanConfig as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
@@ -0,0 +1,152 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
+ import { i as hostSessions, r as hostService } from "./access-fiehjxV6.js";
3
+ import { a as findSerenityRoot } from "./ccc-NlLr_sxy.js";
4
+ import { basename } from "node:path";
5
+ //#region src/ccc-roots.ts
6
+ /**
7
+ * ccc-roots.ts — CCC 根解析的**唯一判断层**(L1 单根解析 + L2 全量枚举)
8
+ *
9
+ * 为什么存在(C2「CCC 发现面归一」,S142 2026-09-15 Q5 裁决):
10
+ * 「这个会话/路径属于哪个 CCC 根」(L1)与「本机有哪些 CCC」(L2)这两层判断此前被
11
+ * 独立实现在 ~19 处,且互有分歧(同一进程内三个面可指向不同 CCC;"live CCC 列表"有三份
12
+ * 不同答案;三层发现是**短路**而非合并 ⇒ 名单不全)。本模块把这两层**收成一个出口**:
13
+ * 调用方只问本模块,不再各写各的上溯 + 兜底。
14
+ *
15
+ * 分层(唯一真相源边界,**不要**把下层搬进本模块):
16
+ * - **L0 原语** `findSerenityRoot(cwd)`(`ccc.ts:16`)——**仍是唯一** L0 实现,本模块从它 import。
17
+ * - **L1 单根解析** —— 本模块:{@link agentCwdFor}(agent/exec → cwd)与
18
+ * {@link cccRootForCwd}/{@link cccRootForExec}(cwd → 根)。
19
+ * - **L2 全量枚举** —— 本模块:{@link listCccs}。
20
+ * - **L3 CCC 名解析** `readCccName`(`ccc.ts`)——不在本模块,也不与 `skills-discovery` 的
21
+ * 第二份实现合并(那是另一条开放项)。
22
+ *
23
+ * 不新增持久状态(Q5 裁决:**不新增"本机 CCC 名单"**、不扫盘、不写路径假设)——
24
+ * 枚举只读 **DSH 已有的三个来源**。
25
+ */
26
+ var ccc_roots_exports = /* @__PURE__ */ __exportAll({
27
+ NO_CCC_FROM_AGENT_CWD: () => NO_CCC_FROM_AGENT_CWD,
28
+ agentCwdFor: () => agentCwdFor,
29
+ cccRootForCwd: () => cccRootForCwd,
30
+ cccRootForExec: () => cccRootForExec,
31
+ listCccs: () => listCccs
32
+ });
33
+ /**
34
+ * 取调用方的工作目录(**语义逐字保留**自原先散落 10 份的工具内副本):
35
+ * `exec.agent?.session?.header?.cwd ?? process.cwd()`。
36
+ *
37
+ * ⚠️ 无 `exec.agent` 时回落进程 cwd —— "用服务启动目录冒充会话目录"是**既有语义**,
38
+ * 不是本模块的选择(改它会动到 9 个工具的根解析行为)。已知副作用与规避见
39
+ * `tools/handyman.ts` 的「调用形态检查前移」补偿(`?? process.cwd()` 会把真因
40
+ * "exec.agent 不存在"盖成 `No CCC found`)。
41
+ */
42
+ function agentCwdFor(execOrAgent) {
43
+ return execOrAgent.agent?.session?.header?.cwd ?? execOrAgent.session?.header?.cwd ?? process.cwd();
44
+ }
45
+ /**
46
+ * cwd → CCC 根(L0 原语的唯一调用形态;`ccc.ts` 之外不要再各写一遍)。
47
+ * @param cwd 起始路径(目录或文件;文件会靠 `dirname` 上溯)
48
+ * @returns CCC 根绝对路径;不在任何 CCC 内 → `null`
49
+ */
50
+ function cccRootForCwd(cwd) {
51
+ return findSerenityRoot(cwd);
52
+ }
53
+ /**
54
+ * 工具/缝入口的根解析(L1 标准形态):`agentCwdFor(...)` 再上溯。
55
+ * @returns CCC 根;解析不到 → `null`(**不**回落假根——"回落进程 cwd 当根"是 `rebuild.ts`
56
+ * 独有的另一条语义,见该处注释)
57
+ */
58
+ function cccRootForExec(execOrAgent) {
59
+ return findSerenityRoot(agentCwdFor(execOrAgent));
60
+ }
61
+ /**
62
+ * 工具根解析失败时抛出的**统一**错误文案。
63
+ * 9 个工具原先各写一份字面量(内容完全相同);抽成常量以保单一真相源,
64
+ * **文案逐字不变**(外在行为不变)。
65
+ */
66
+ const NO_CCC_FROM_AGENT_CWD = "No CCC found: no .serenity file from agent cwd";
67
+ /** 起点路径集合会被逐条 `findSerenityRoot` 上溯并按根去重(保序) */
68
+ function makeCollector() {
69
+ const roots = [];
70
+ return {
71
+ roots,
72
+ push(cwd) {
73
+ if (typeof cwd !== "string" || cwd === "") return;
74
+ const r = findSerenityRoot(cwd);
75
+ if (r && !roots.includes(r)) roots.push(r);
76
+ }
77
+ };
78
+ }
79
+ /**
80
+ * ① **dsh 工作区注册表**(`workspaceRegistry.list`,持久化——所有工作目录即使无 live 会话)。
81
+ * S142 用户 2026-08-29:应直接拉 dsh 工作区,且只列具体 CCC。
82
+ */
83
+ function workspaceRegistryPaths(ctx) {
84
+ const out = [];
85
+ try {
86
+ const registry = hostService(ctx, "workspaceRegistry");
87
+ for (const ws of registry?.list?.() ?? []) if (typeof ws?.path === "string") out.push(ws.path);
88
+ } catch {}
89
+ return out;
90
+ }
91
+ /**
92
+ * ② **dsh 持久化会话**(`sessionPersistence.list`,必装配服务——覆盖所有历史会话工作目录)。
93
+ *
94
+ * v1.31.6 适配 0.1.5-rc.1:`list()` 返回形状变了(**静默突破**——形状由本处断言自写,
95
+ * 宿主改名/改形在类型检查里看不见):
96
+ * 0.1.2 → `Promise<SessionHeader[]>` → `h.cwd`
97
+ * 0.1.5-rc.1 → `Promise<SessionPersistenceSnapshot[]>` → `h.header.cwd`
98
+ */
99
+ async function sessionPersistencePaths(ctx) {
100
+ const out = [];
101
+ try {
102
+ const sp = hostService(ctx, "sessionPersistence");
103
+ for (const h of await sp?.list?.() ?? []) if (typeof h?.header?.cwd === "string") out.push(h.header.cwd);
104
+ } catch {}
105
+ return out;
106
+ }
107
+ /** ③ **live 会话**(`sessions.list()` 的 cwd;`host/access.ts` 收口) */
108
+ function liveSessionPaths(ctx) {
109
+ const out = [];
110
+ try {
111
+ for (const s of hostSessions(ctx)?.list?.() ?? []) {
112
+ const cwd = s?.header?.cwd;
113
+ if (typeof cwd === "string") out.push(cwd);
114
+ }
115
+ } catch {}
116
+ return out;
117
+ }
118
+ /**
119
+ * 列出本机已知的 CCC 根(**L2 的唯一权威列举器**)。
120
+ *
121
+ * 三层来源**各自都跑、结果取并集**(来源顺序 `workspaceRegistry → sessionPersistence → live`,
122
+ * 按 root 去重**保留首次出现**)。`defaultRoot` 若不在结果里则 unshift 到首位。
123
+ *
124
+ * ⚠️ **为什么是并集而不是短路**(C2 修正的核心,现状稿 §2.B E1 + §2.D 第 4 条):
125
+ * 旧实现是 `if (roots.length === 0)` 三层门控 ⇒ 只要工作区注册表里有**任意一条**,
126
+ * 就永远不看 `sessionPersistence`/live;注册表里有一个 CCC 而另一 CCC 只存在于历史会话时,
127
+ * **整个列表都不含后者**("名单不全")。并集修正它,且**不改**任何消费方的输入契约。
128
+ *
129
+ * @param ctx 插件上下文
130
+ * @param opts.defaultRoot 默认根(不在列表 → 置首);opts.withRoles 是否读 `skiff.roles`
131
+ * @returns 按上述顺序去重的 CCC 条目(无任何来源 → 空数组 + 可选默认根)
132
+ */
133
+ async function listCccs(ctx, opts = {}) {
134
+ const c = makeCollector();
135
+ for (const path of workspaceRegistryPaths(ctx)) c.push(path);
136
+ for (const path of await sessionPersistencePaths(ctx)) c.push(path);
137
+ for (const path of liveSessionPaths(ctx)) c.push(path);
138
+ const defaultRoot = opts.defaultRoot;
139
+ if (typeof defaultRoot === "string" && defaultRoot !== "" && !c.roots.includes(defaultRoot)) c.roots.unshift(defaultRoot);
140
+ let rolesOf = null;
141
+ if (opts.withRoles === true) {
142
+ const { readSkiffRoles } = await import("./skiff-role-CEHL5cek.js").then((n) => n.u);
143
+ rolesOf = (root) => [...readSkiffRoles(root).keys()];
144
+ }
145
+ return c.roots.map((root) => ({
146
+ root,
147
+ name: basename(root) || root,
148
+ roles: rolesOf ? rolesOf(root) : []
149
+ }));
150
+ }
151
+ //#endregion
152
+ export { ccc_roots_exports as a, cccRootForExec as i, agentCwdFor as n, listCccs as o, cccRootForCwd as r, NO_CCC_FROM_AGENT_CWD as t };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * ccc-roots.ts — CCC 根解析的**唯一判断层**(L1 单根解析 + L2 全量枚举)
3
+ *
4
+ * 为什么存在(C2「CCC 发现面归一」,S142 2026-09-15 Q5 裁决):
5
+ * 「这个会话/路径属于哪个 CCC 根」(L1)与「本机有哪些 CCC」(L2)这两层判断此前被
6
+ * 独立实现在 ~19 处,且互有分歧(同一进程内三个面可指向不同 CCC;"live CCC 列表"有三份
7
+ * 不同答案;三层发现是**短路**而非合并 ⇒ 名单不全)。本模块把这两层**收成一个出口**:
8
+ * 调用方只问本模块,不再各写各的上溯 + 兜底。
9
+ *
10
+ * 分层(唯一真相源边界,**不要**把下层搬进本模块):
11
+ * - **L0 原语** `findSerenityRoot(cwd)`(`ccc.ts:16`)——**仍是唯一** L0 实现,本模块从它 import。
12
+ * - **L1 单根解析** —— 本模块:{@link agentCwdFor}(agent/exec → cwd)与
13
+ * {@link cccRootForCwd}/{@link cccRootForExec}(cwd → 根)。
14
+ * - **L2 全量枚举** —— 本模块:{@link listCccs}。
15
+ * - **L3 CCC 名解析** `readCccName`(`ccc.ts`)——不在本模块,也不与 `skills-discovery` 的
16
+ * 第二份实现合并(那是另一条开放项)。
17
+ *
18
+ * 不新增持久状态(Q5 裁决:**不新增"本机 CCC 名单"**、不扫盘、不写路径假设)——
19
+ * 枚举只读 **DSH 已有的三个来源**。
20
+ */
21
+ import type { Context } from 'cordis';
22
+ /**
23
+ * 工具/缝里"取会话工作目录"的那段形状的最小模型。
24
+ * 多数工具只带 `agent`(`exec.agent.session.header.cwd`);少数缝把 agent 本身传进来。
25
+ */
26
+ export interface ExecOrAgent {
27
+ agent?: {
28
+ session?: {
29
+ header?: {
30
+ cwd?: string;
31
+ };
32
+ };
33
+ };
34
+ session?: {
35
+ header?: {
36
+ cwd?: string;
37
+ };
38
+ };
39
+ }
40
+ /**
41
+ * 取调用方的工作目录(**语义逐字保留**自原先散落 10 份的工具内副本):
42
+ * `exec.agent?.session?.header?.cwd ?? process.cwd()`。
43
+ *
44
+ * ⚠️ 无 `exec.agent` 时回落进程 cwd —— "用服务启动目录冒充会话目录"是**既有语义**,
45
+ * 不是本模块的选择(改它会动到 9 个工具的根解析行为)。已知副作用与规避见
46
+ * `tools/handyman.ts` 的「调用形态检查前移」补偿(`?? process.cwd()` 会把真因
47
+ * "exec.agent 不存在"盖成 `No CCC found`)。
48
+ */
49
+ export declare function agentCwdFor(execOrAgent: ExecOrAgent): string;
50
+ /**
51
+ * cwd → CCC 根(L0 原语的唯一调用形态;`ccc.ts` 之外不要再各写一遍)。
52
+ * @param cwd 起始路径(目录或文件;文件会靠 `dirname` 上溯)
53
+ * @returns CCC 根绝对路径;不在任何 CCC 内 → `null`
54
+ */
55
+ export declare function cccRootForCwd(cwd: string): string | null;
56
+ /**
57
+ * 工具/缝入口的根解析(L1 标准形态):`agentCwdFor(...)` 再上溯。
58
+ * @returns CCC 根;解析不到 → `null`(**不**回落假根——"回落进程 cwd 当根"是 `rebuild.ts`
59
+ * 独有的另一条语义,见该处注释)
60
+ */
61
+ export declare function cccRootForExec(execOrAgent: ExecOrAgent): string | null;
62
+ /**
63
+ * 工具根解析失败时抛出的**统一**错误文案。
64
+ * 9 个工具原先各写一份字面量(内容完全相同);抽成常量以保单一真相源,
65
+ * **文案逐字不变**(外在行为不变)。
66
+ */
67
+ export declare const NO_CCC_FROM_AGENT_CWD = "No CCC found: no .serenity file from agent cwd";
68
+ /** 候选 CCC 条目(调试页 CCC 切换器 / 微信桥 / ACP 问答页 / `/serenity/cccs` 共用数据形状) */
69
+ export interface CccEntry {
70
+ /** CCC 根(绝对路径) */
71
+ root: string;
72
+ /** 目录名(展示用) */
73
+ name: string;
74
+ /**
75
+ * 该 CCC 的 skiff 角色名列表。
76
+ * ⚠️ **仅 `withRoles: true` 时非空**;缺省 `false` ⇒ 恒为 `[]` 且**不读任何
77
+ * `serenity.json`**(唤醒调度器每 tick 都要枚举,不该为角色付 IO;见 listCccs 说明)。
78
+ */
79
+ roles: string[];
80
+ }
81
+ export interface ListCccsOptions {
82
+ /**
83
+ * 默认根:不在结果里时 **unshift 到首位**(既有语义,逐字保留)。
84
+ * 传 `undefined`/`''` 表示没有默认根。
85
+ */
86
+ defaultRoot?: string;
87
+ /** 是否实时读取各 CCC 的 `skiff.roles`(缺省 false)。 */
88
+ withRoles?: boolean;
89
+ }
90
+ /**
91
+ * 列出本机已知的 CCC 根(**L2 的唯一权威列举器**)。
92
+ *
93
+ * 三层来源**各自都跑、结果取并集**(来源顺序 `workspaceRegistry → sessionPersistence → live`,
94
+ * 按 root 去重**保留首次出现**)。`defaultRoot` 若不在结果里则 unshift 到首位。
95
+ *
96
+ * ⚠️ **为什么是并集而不是短路**(C2 修正的核心,现状稿 §2.B E1 + §2.D 第 4 条):
97
+ * 旧实现是 `if (roots.length === 0)` 三层门控 ⇒ 只要工作区注册表里有**任意一条**,
98
+ * 就永远不看 `sessionPersistence`/live;注册表里有一个 CCC 而另一 CCC 只存在于历史会话时,
99
+ * **整个列表都不含后者**("名单不全")。并集修正它,且**不改**任何消费方的输入契约。
100
+ *
101
+ * @param ctx 插件上下文
102
+ * @param opts.defaultRoot 默认根(不在列表 → 置首);opts.withRoles 是否读 `skiff.roles`
103
+ * @returns 按上述顺序去重的 CCC 条目(无任何来源 → 空数组 + 可选默认根)
104
+ */
105
+ export declare function listCccs(ctx: Context, opts?: ListCccsOptions): Promise<CccEntry[]>;
package/lib/ccc.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  export declare function findSerenityRoot(cwd: string): string | null;
11
11
  export declare function findGitRoot(cwd: string): string | null;
12
- export type PathClass = 'inside' | 'outside' | 'same';
12
+ type PathClass = 'inside' | 'outside' | 'same';
13
13
  /**
14
14
  * 前缀判定:abs 是否位于 rootAbs 之内。
15
15
  * caseInsensitive(Windows 盘符/路径大小写不敏感)由调用方按平台传入;
@@ -31,7 +31,7 @@ export declare function resolveInside(root: string, p: string): string;
31
31
  * @returns CCC 名;无标记/读失败/全注释空行 → null
32
32
  */
33
33
  export declare function readCccName(root: string): string | null;
34
- export interface SerenityConfig {
34
+ interface SerenityConfig {
35
35
  /** handyman(杂工)配置:模型白名单(v1.24.0 取代 loop) */
36
36
  handyman?: {
37
37
  /** 可用模型白名单(provider/model 列表)——handyman 只能使用其中模型(用户拍板:CCC 配置控制) */
@@ -102,6 +102,14 @@ export interface SerenityConfig {
102
102
  * 未配置或 enabled=false → 该 CCC 桥完全不启动(零资源占用)。
103
103
  */
104
104
  weixin?: WeixinSettings;
105
+ /**
106
+ * 专属工具声明(v1.33,S142 §32.11):**ACC 注册但默认对所有 CCC 隐藏**的工具,
107
+ * 只有在自己这里声明的 CCC 可见(例:`["acc-diag"]` = ACC 负责人的运行态诊断)。
108
+ *
109
+ * 归属判据(D23):**机制在 ACC(通用,任何工具名皆可),声明在 CCC**——
110
+ * ACC 代码里不出现任何具体 CCC 的名字;未声明(含未配置/配置损坏)⇒ 隐藏。
111
+ */
112
+ exclusiveTools?: string[];
105
113
  }
106
114
  /**
107
115
  * Autopilot Trajectory 配置(CCC 定义;S142 用户拍板 v0.5 + 正式化 v1.27.4):
@@ -260,8 +268,6 @@ export declare const DEFAULT_SERENITY_CONFIG_PATHS: string[];
260
268
  * 写出的 BOM(\uFEFF)会让 JSON.parse 抛错(配置静默变空)或 frontmatter 检测失败(技能被丢弃)。
261
269
  */
262
270
  export declare function readUtf8(path: string): string;
263
- /** 测试辅助:清空"已告警"记忆(生产零调用) */
264
- export declare function __resetBrokenConfigWarningsForTest(): void;
265
271
  /**
266
272
  * 读取 CCC 配置(第一个存在且可解析的候选路径)。
267
273
  *
@@ -274,6 +280,18 @@ export declare function __resetBrokenConfigWarningsForTest(): void;
274
280
  * @returns 解析后的配置;无文件/损坏 → `{}`
275
281
  */
276
282
  export declare function loadSerenityConfig(root: string, paths?: string[]): SerenityConfig;
283
+ /**
284
+ * 读取 CCC 声明的「专属工具」清单(`exclusiveTools`,v1.33)。
285
+ *
286
+ * 失败语义:无配置/字段缺失/配置损坏 → **空数组**(= 不声明任何专属工具 ⇒ 专属工具保持隐藏)。
287
+ * 这里选 **fail-closed**(与 im-bridge 的 fail-open 相反)的理由:专属工具的默认态就是"隐藏",
288
+ * 判据读不到 = 没有证据表明该 CCC 要它;而配置损坏本身已在 `loadSerenityConfig` 响亮告警
289
+ * (不会静默)。im-bridge 相反——隐藏一个**已在使用**的通道才会静默损害能力。
290
+ * @param root CCC 根
291
+ * @param paths 候选配置相对路径
292
+ * @returns 去空白后的工具名列表(非字符串项被丢弃)
293
+ */
294
+ export declare function readExclusiveTools(root: string, paths?: string[]): string[];
277
295
  export declare const SAFE_MODE_MARKER = ".serenity-safe-on";
278
296
  export declare function isSafeModeOn(root: string): boolean;
279
297
  /** 黑名单条目(对齐 osp BlacklistEntry):pattern + 可选自定义拦截提示 message */
@@ -290,6 +308,5 @@ export interface BlacklistRule {
290
308
  export declare function readBlacklist(root: string, paths?: string[]): BlacklistRule[];
291
309
  /** 匹配黑名单规则;命中返回条目,未命中返回 null。前缀匹配 / regex: 前缀(对齐 osp)。Windows:规则反斜杠归一化为正斜杠与 rel 对齐 */
292
310
  export declare function matchBlacklist(relPath: string, rules: BlacklistRule[]): BlacklistRule | null;
293
- /** 写类工具名(safe-mode 下禁止) */
294
- export declare const WRITE_TOOLS: Set<string>;
295
311
  export declare function isWriteTool(toolName: string): boolean;
312
+ export {};