@shgroup/dsh-serenity-hooks 1.42.0 → 1.44.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/README.md +28 -1
- package/dsh.plugin.json +1 -1
- package/lib/{ccc-NlLr_sxy.js → ccc-DG4Oc7I1.js} +22 -1
- package/lib/{ccc-roots-Bd_SjEs7.js → ccc-roots-FxWXuZPw.js} +2 -2
- package/lib/ccc.d.ts +36 -0
- package/lib/cro-guide.d.ts +48 -0
- package/lib/cro-log.d.ts +135 -0
- package/lib/cro-turns.d.ts +95 -0
- package/lib/cro.d.ts +306 -0
- package/lib/index.js +4170 -3923
- package/lib/{skiff-registry-FTjoWQTQ.js → skiff-registry-BjIX_KD1.js} +19 -1
- package/lib/{skiff-role-CEHL5cek.js → skiff-role-KqQA6pkr.js} +1 -1
- package/lib/tools/trajectory.d.ts +4 -1
- package/lib/{trajectory-bound-4xkZDfmO.js → trajectory-bound-BzdzsDPI.js} +4 -3
- package/lib/trajectory-ops.d.ts +8 -2
- package/lib/trajectory-skills.d.ts +22 -5
- package/lib/{wake-scheduler-1AsvNQPW.js → wake-scheduler-C2GwdriD.js} +933 -6
- package/lib/wake-scheduler.d.ts +19 -0
- package/lib/{weixin-route-B2ajFypI.js → weixin-route-CWMRa36W.js} +1 -1
- package/package.json +1 -1
package/lib/cro.d.ts
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cro.ts — CRO(Continuous Re-Occurrence · 持续再发生)机制层(S142 §7.8,2026-09-19)
|
|
3
|
+
*
|
|
4
|
+
* ## 这东西用来干什么(白话)
|
|
5
|
+
*
|
|
6
|
+
* 今天叫醒一条轨迹的方式是「几点几分叫我」。但**该不该醒,往往不是时间说了算**:
|
|
7
|
+
* 比如 S185 应该「天亮 + 家里有人 + 非高峰」才醒;已经在干活就不该再叫;
|
|
8
|
+
* 日志太大了,下次叫它时该让它先整理。
|
|
9
|
+
*
|
|
10
|
+
* CRO 让**一条轨迹自己带一段程序**,由 ACC 在每次检查时跑它,
|
|
11
|
+
* **由这段程序决定「现在该不该叫我、叫我的时候说什么」**。
|
|
12
|
+
*
|
|
13
|
+
* ## 归属(所有者 2026-09-19 定:**机制属 ACC,CCC 是用户**)
|
|
14
|
+
*
|
|
15
|
+
* | 面 | 归谁 | 内容 |
|
|
16
|
+
* |---|---|---|
|
|
17
|
+
* | **机制** | 🔴 **ACC(本模块)** | 契约 / 调度 / 执行 / 状态暴露 / 容错 |
|
|
18
|
+
* | **程序** | 🔴 **CCC(用户)** | 那段判定的 TS 代码,放**轨迹自己的目录**里 |
|
|
19
|
+
*
|
|
20
|
+
* ⇒ 与 `send-later` 同构:**工具在 ACC,用它在 CCC**。
|
|
21
|
+
*
|
|
22
|
+
* ## 命名(防混淆,写死)
|
|
23
|
+
*
|
|
24
|
+
* **`CRO` = 那段程序**(实体名),**不是** ACC 标准 §0.1 三环节(发生/存储/**再发生**)的第三环本身。
|
|
25
|
+
* 命名来源 = §0.3「**Trajectory 在寻找 Agent**」——CRO 是「再发生」这一环的自动化。
|
|
26
|
+
*
|
|
27
|
+
* ## 位置与形态(设计 §2)
|
|
28
|
+
*
|
|
29
|
+
* `<CCC 根>/AGENT_SESSIONS/<轨迹目录>/continuous-re-occurrence.ts`
|
|
30
|
+
* · **文件名全写**(所有者令);· **程序只有一个**:**文件在 = 启用,不在 = 禁用**(无 enabled 字段、无注册表)。
|
|
31
|
+
*
|
|
32
|
+
* ## 🔴 运行契约 = B 案(**ACC 只 spawn,从不 import**)
|
|
33
|
+
*
|
|
34
|
+
* 所有者 2026-09-19:「**B 是设计,A 只是文档友好**」。
|
|
35
|
+
* 若 ACC **import** 那段 TS,会出现两件事:
|
|
36
|
+
* ① ACC 依赖 CCC 的**源码路径**(npm 装机版在别处 ⇒ 两条路径都要活 = 两个真相源);
|
|
37
|
+
* ② **用户程序语法错会让 ACC 启动失败** ⇒ **一个用户程序的错误放倒整个容器**。
|
|
38
|
+
* ⇒ 进程边界把这两件事同时挡掉:**路径只需一个(文件系统),错误被隔离在子进程里**。
|
|
39
|
+
*
|
|
40
|
+
* ## 🔴 铁律:CRO 的任何失败,绝不影响既有机制(设计 §5)
|
|
41
|
+
*
|
|
42
|
+
* `send-later` / `send-now` / 唤醒表的投递**照常工作**。
|
|
43
|
+
* 先例 = `weixin-hook.ts` 的**旁路容忍**("超时 kill / 非 0 退出 / spawn 失败 → 仅日志返回,绝不抛")。
|
|
44
|
+
* ⇒ 本模块的**每一个**导出函数**都不抛错**(返回结构化失败)。
|
|
45
|
+
*/
|
|
46
|
+
/** 入口文件名(**全写**,所有者令 2026-09-19;不改缩写) */
|
|
47
|
+
export declare const CRO_FILENAME = "continuous-re-occurrence.ts";
|
|
48
|
+
/** 硬超时(沿用 `biasProvider` 先例的 60s;设计 §5 情形 2) */
|
|
49
|
+
export declare const CRO_TIMEOUT_MS = 60000;
|
|
50
|
+
/**
|
|
51
|
+
* 轨迹状态快照 —— ACC 把它**能看到的全部**序列化后喂给程序(所有者令:「信息尽可能多」)。
|
|
52
|
+
*
|
|
53
|
+
* ⚠️ **边界(诚实标注)**:给满的是**「快照」**,不是「无限能力」——
|
|
54
|
+
* ACC **看不见**的东西(程序自己上次判了什么)**物理上给不了**(那是另一个进程的内存)。
|
|
55
|
+
* ⇒ 那类状态**归程序自己**(可在自己轨迹目录里写状态文件)。
|
|
56
|
+
*/
|
|
57
|
+
export interface CroSnapshot {
|
|
58
|
+
/** 快照格式版本(程序据此判兼容;未来加字段时递增) */
|
|
59
|
+
version: number;
|
|
60
|
+
/** 身份 */
|
|
61
|
+
identity: {
|
|
62
|
+
/** 🔴 硬锚:完整目录名(不解析编号格式,§0I U4) */
|
|
63
|
+
dirName: string;
|
|
64
|
+
/** 展示码(如 `S185`)——**派生**,不作识别依据 */
|
|
65
|
+
code: string;
|
|
66
|
+
/** CCC 根(绝对路径) */
|
|
67
|
+
cccRoot: string;
|
|
68
|
+
};
|
|
69
|
+
/** 时间(当地时区呈现,遵 D67) */
|
|
70
|
+
time: {
|
|
71
|
+
/** 当前时刻 — ISO(当地时区) */
|
|
72
|
+
now: string;
|
|
73
|
+
/** 当前 epoch 毫秒 */
|
|
74
|
+
nowMs: number;
|
|
75
|
+
/** 本地人读时刻 */
|
|
76
|
+
nowLocal: string;
|
|
77
|
+
};
|
|
78
|
+
/** 轨迹身体(SESSION.md 及其目录) */
|
|
79
|
+
body: {
|
|
80
|
+
sessionMdPath: string;
|
|
81
|
+
sessionMdBytes: number | null;
|
|
82
|
+
sessionMdMtime: string | null;
|
|
83
|
+
/** `references/` 目录清单(名 + 体积 + mtime);不存在 ⇒ 空数组 */
|
|
84
|
+
references: Array<{
|
|
85
|
+
name: string;
|
|
86
|
+
bytes: number;
|
|
87
|
+
mtime: string;
|
|
88
|
+
}>;
|
|
89
|
+
};
|
|
90
|
+
/** 绑定与载体 */
|
|
91
|
+
binding: {
|
|
92
|
+
/** 绑定该轨迹的载体会话 id(按绑定时间倒序) */
|
|
93
|
+
boundSessionIds: string[];
|
|
94
|
+
/** 其中**当前 live** 的 */
|
|
95
|
+
liveSessionIds: string[];
|
|
96
|
+
/**
|
|
97
|
+
* 🔴 其中**正在跑轮次**的(设计 §3.2 —— 本机制**唯一需要 ACC 新增的状态**)。
|
|
98
|
+
* ⚠️ `live ≠ 在跑`:一条会话可以 live 而空闲。
|
|
99
|
+
* 宿主没有现成的 turn 运行标志 ⇒ 由 `cro-turns.ts` 用 `agent/session-start` +
|
|
100
|
+
* `agent/turn-stopping` 两个**既有契约事件**夹出来。
|
|
101
|
+
*/
|
|
102
|
+
runningSessionIds: string[];
|
|
103
|
+
};
|
|
104
|
+
/** 调度面 */
|
|
105
|
+
scheduling: {
|
|
106
|
+
/** 本轨迹在办的唤醒条目(`wake-registry` 中 state=pending 且 target 命中本轨迹) */
|
|
107
|
+
pendingWakes: Array<{
|
|
108
|
+
id: string;
|
|
109
|
+
at: string;
|
|
110
|
+
createdAt: string;
|
|
111
|
+
createdBy: string;
|
|
112
|
+
}>;
|
|
113
|
+
/** 调度器状态(armed / 全局闸 / tick 次数 / 上次跳过原因) */
|
|
114
|
+
scheduler: {
|
|
115
|
+
armed: boolean;
|
|
116
|
+
enabled: boolean;
|
|
117
|
+
ticks: number;
|
|
118
|
+
lastSkipReason: string | null;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** 快照装配的输入(**纯数据** ⇒ `buildCroSnapshot` 可穷举测试,无需 fs) */
|
|
123
|
+
export interface CroSnapshotInput {
|
|
124
|
+
dirName: string;
|
|
125
|
+
cccRoot: string;
|
|
126
|
+
nowMs: number;
|
|
127
|
+
sessionMdPath: string;
|
|
128
|
+
sessionMdBytes: number | null;
|
|
129
|
+
sessionMdMtimeMs: number | null;
|
|
130
|
+
references: Array<{
|
|
131
|
+
name: string;
|
|
132
|
+
bytes: number;
|
|
133
|
+
mtimeMs: number;
|
|
134
|
+
}>;
|
|
135
|
+
boundSessionIds: string[];
|
|
136
|
+
liveSessionIds: string[];
|
|
137
|
+
runningSessionIds: string[];
|
|
138
|
+
pendingWakes: Array<{
|
|
139
|
+
id: string;
|
|
140
|
+
at: string;
|
|
141
|
+
createdAt: string;
|
|
142
|
+
createdBy: string;
|
|
143
|
+
}>;
|
|
144
|
+
scheduler: {
|
|
145
|
+
armed: boolean;
|
|
146
|
+
enabled: boolean;
|
|
147
|
+
ticks: number;
|
|
148
|
+
lastSkipReason: string | null;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 展示码派生(**只用于展示**,不作识别依据 —— §0I U4:锚永远是 `dirName`)。
|
|
153
|
+
*
|
|
154
|
+
* 判据(设计 §1):**编号不是固定格式,不同 CCC 格式不同**(本容器 `S###`,别处可能是 issue 号
|
|
155
|
+
* 或自定义前缀)⇒ 这里只做**尽力而为的展示性提取**,取不到就返回空串。
|
|
156
|
+
* 🔴 **绝不**因为取不到就拒绝服务——`dirName` 才是硬锚。
|
|
157
|
+
* @param dirName 轨迹目录名
|
|
158
|
+
* @returns `S###` 形态的展示码;取不到 ⇒ `''`
|
|
159
|
+
*/
|
|
160
|
+
export declare function deriveCode(dirName: string): string;
|
|
161
|
+
/**
|
|
162
|
+
* 装配快照(**纯函数**:只做数据整形,不读 fs、不看 ctx)⇒ 可穷举测试。
|
|
163
|
+
* @param input 已读好的原始数据
|
|
164
|
+
* @returns 喂给 CRO 程序的快照
|
|
165
|
+
*/
|
|
166
|
+
export declare function buildCroSnapshot(input: CroSnapshotInput): CroSnapshot;
|
|
167
|
+
/** CRO 程序输出的决策 */
|
|
168
|
+
export interface CroDecision {
|
|
169
|
+
/** 🔴 是否唤起 */
|
|
170
|
+
wake: boolean;
|
|
171
|
+
/** 🔴 唤起时的提示词(`wake=true` 时必填、非空) */
|
|
172
|
+
prompt: string | null;
|
|
173
|
+
/** 可选:判定理由(进日志,供事后重建 —— 设计 §4.2) */
|
|
174
|
+
reason: string | null;
|
|
175
|
+
}
|
|
176
|
+
/** 解析结果(**不抛错**) */
|
|
177
|
+
export type CroParseResult = {
|
|
178
|
+
ok: true;
|
|
179
|
+
decision: CroDecision;
|
|
180
|
+
} | {
|
|
181
|
+
ok: false;
|
|
182
|
+
error: string;
|
|
183
|
+
detail?: string;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* 解析 CRO 程序 stdout(**纯函数** ⇒ 可穷举测试)。
|
|
187
|
+
*
|
|
188
|
+
* ## 边界(设计 §4.1)
|
|
189
|
+
* · `wake` 缺省 / `false` ⇒ **不唤起**(**这是常态**)——`prompt` 可省;
|
|
190
|
+
* · `wake: true` 但 `prompt` 空 / 非串 ⇒ 🔴 **非法** ⇒ 返回错误(调用方**跳过本轮**,不投递空消息);
|
|
191
|
+
* · 非 JSON / 非对象 ⇒ 错误。
|
|
192
|
+
*
|
|
193
|
+
* ## 🔴 为什么 `reason` 重要(CCE:重建 > 保存)
|
|
194
|
+
* 改成程序判定后,**「当时为什么叫了」不再能从时间表重建**(原因在程序肚子里:
|
|
195
|
+
* 可能有随机、可能看了外部数据)⇒ 要求程序自报理由,把「决策依据」重新变成**可重建的**。
|
|
196
|
+
* ⇒ **强烈建议但不强制**(强制会让简单程序难写;设计 §9-6 待裁,本版取"建议")。
|
|
197
|
+
*
|
|
198
|
+
* @param stdout 程序标准输出(可含前后空白;允许多行 JSON 文本)
|
|
199
|
+
* @returns 决策 或 结构化错误
|
|
200
|
+
*/
|
|
201
|
+
export declare function parseCroOutput(stdout: string): CroParseResult;
|
|
202
|
+
/** 单次执行结果(**永不抛**) */
|
|
203
|
+
export interface CroRunResult {
|
|
204
|
+
ok: boolean;
|
|
205
|
+
/** 程序 stdout(已截断);失败时可能为部分输出 */
|
|
206
|
+
stdout: string;
|
|
207
|
+
/** 失败原因(人读;成功 ⇒ null) */
|
|
208
|
+
error: string | null;
|
|
209
|
+
/** 失败分类(稳定码,供日志/测试断言) */
|
|
210
|
+
code?: string;
|
|
211
|
+
}
|
|
212
|
+
/** runner 签名(**可注入** —— 测试捕获防真实 spawn flake;同 `weixin-hook.ts` 手法) */
|
|
213
|
+
export type CroRunner = (scriptAbs: string, stdinJson: string, timeoutMs: number) => Promise<CroRunResult>;
|
|
214
|
+
/**
|
|
215
|
+
* CRO 程序路径:`<CCC 根>/AGENT_SESSIONS/<dirName>/continuous-re-occurrence.ts`。
|
|
216
|
+
*
|
|
217
|
+
* ⚠️ 路径逃逸校验(`resolveInside`):`dirName` 来自配置/工具入参,
|
|
218
|
+
* 必须确保解析后仍在 CCC 根内(同 `weixin-hook.ts:160` 手法)。**抛错由调用方吞**。
|
|
219
|
+
* @param root CCC 根
|
|
220
|
+
* @param dirName 轨迹目录名
|
|
221
|
+
* @returns 绝对路径
|
|
222
|
+
*/
|
|
223
|
+
export declare function croScriptPath(root: string, dirName: string): string;
|
|
224
|
+
/** CRO 是否启用(**判据 = 文件在不在**;设计 §2.1:无 enabled 字段、无注册表) */
|
|
225
|
+
export declare function isCroEnabled(root: string, dirName: string): boolean;
|
|
226
|
+
/**
|
|
227
|
+
* 列出**启用了 CRO 的轨迹目录名**(供调度器每 tick 扫描,设计 §6.1)。
|
|
228
|
+
*
|
|
229
|
+
* ## 判据与形态(R↓)
|
|
230
|
+
* · 判据 = **文件在不在**(同 §2.1)——**没有注册表**,所以"谁启用了"只能靠**扫目录**:
|
|
231
|
+
* `AGENT_SESSIONS/` 下每个目录查一次 `<目录>/continuous-re-occurrence.ts`。
|
|
232
|
+
* · **一次 readdir + 每个目录一次 existsSync**:N 条轨迹的代价是 O(N) 次 `stat`,
|
|
233
|
+
* 每 5min 一次 —— 与既有 `listSessions` 同量级,可接受。
|
|
234
|
+
* · 跳过 **`_` 前缀**(`_archived` / `_skiff-logs` / `_weixin-logs` = 系统与日志目录)
|
|
235
|
+
* 与 **`.` 前缀**(隐藏)——它们**不是轨迹**,且扫它们纯属浪费。
|
|
236
|
+
* · 🔴 **本函数绝不抛错**(返回 `[]`)——它跑在调度器 tick 内,任何异常都可能影响既有链路
|
|
237
|
+
* (设计 §5 铁律)。目录读不到(不存在 / 权限)⇒ `[]` = "没有轨迹启用 CRO",语义正确。
|
|
238
|
+
*
|
|
239
|
+
* ⚠️ **为什么不做缓存**:缓存会引入"文件删了但缓存还在"的失效模式(本容器栽过的"第二真相源"),
|
|
240
|
+
* 而这里省下的只是一次 `readdir`——**不值当**。
|
|
241
|
+
*
|
|
242
|
+
* @param root CCC 根
|
|
243
|
+
* @returns 启用了 CRO 的轨迹目录名(**排序后**,保证同 tick 顺序稳定、便于日志比对)
|
|
244
|
+
*/
|
|
245
|
+
export declare function listCroTrajectories(root: string): string[];
|
|
246
|
+
/**
|
|
247
|
+
* 执行一次 CRO 程序(**永不抛** —— 旁路容忍铁律)。
|
|
248
|
+
*
|
|
249
|
+
* runner 顺序(照 `weixin-hook.ts:171-174` 的 bun 优先 / node 兜底):
|
|
250
|
+
* `bun` → `process.execPath`(同运行时)。
|
|
251
|
+
* 判据:`ENOENT`(二进制不存在)⇒ 试下一个;**其余失败视为最终结果**
|
|
252
|
+
* (程序自己报错就是报错,不该用另一个 runner 掩盖——与 hook 一致)。
|
|
253
|
+
* @param scriptAbs 程序绝对路径
|
|
254
|
+
* @param stdinJson 喂给程序的快照 JSON
|
|
255
|
+
* @param timeoutMs 硬超时
|
|
256
|
+
* @returns 执行结果(含输出的解析交由 `parseCroOutput`)
|
|
257
|
+
*/
|
|
258
|
+
export declare function runCroProcess(scriptAbs: string, stdinJson: string, timeoutMs?: number): Promise<CroRunResult>;
|
|
259
|
+
/** 读 SESSION.md 体积(供看门狗判据等复用;读不到 ⇒ null) */
|
|
260
|
+
export declare function readSessionMdBytes(mdPath: string): number | null;
|
|
261
|
+
/** 读取快照所需的 fs 侧数据(**纯读取,不抛**) */
|
|
262
|
+
export declare function readCroSnapshotInput(root: string, dirName: string, nowMs: number, extras: {
|
|
263
|
+
liveSessionIds: string[];
|
|
264
|
+
runningSessionIds: string[];
|
|
265
|
+
boundSessionIds: string[];
|
|
266
|
+
pendingWakes: CroSnapshot['scheduling']['pendingWakes'];
|
|
267
|
+
scheduler: CroSnapshot['scheduling']['scheduler'];
|
|
268
|
+
}): CroSnapshotInput;
|
|
269
|
+
/** 一次 CRO 评估的结果(四态,调用方按 status 分派) */
|
|
270
|
+
export type CroOutcome = {
|
|
271
|
+
status: 'disabled';
|
|
272
|
+
detail: string;
|
|
273
|
+
} | {
|
|
274
|
+
status: 'skipped';
|
|
275
|
+
detail: string;
|
|
276
|
+
code?: string;
|
|
277
|
+
} | {
|
|
278
|
+
status: 'no-wake';
|
|
279
|
+
decision: CroDecision;
|
|
280
|
+
detail: string;
|
|
281
|
+
} | {
|
|
282
|
+
status: 'wake';
|
|
283
|
+
decision: CroDecision;
|
|
284
|
+
prompt: string;
|
|
285
|
+
detail: string;
|
|
286
|
+
};
|
|
287
|
+
/** 评估依赖(可注入 ⇒ 单测无需真实 spawn/fs) */
|
|
288
|
+
export interface CroDeps {
|
|
289
|
+
runner?: CroRunner;
|
|
290
|
+
timeoutMs?: number;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 评估一条轨迹的 CRO 程序:**文件在 ⇒ 跑它;不在 ⇒ 未启用**(设计 §2.1)。
|
|
294
|
+
*
|
|
295
|
+
* 🔴 **本函数永不抛错**(旁路容忍铁律):任何失败都返回 `{status:'skipped'}`,
|
|
296
|
+
* 调用方据此**跳过本轮**、**不影响既有投递链路**。
|
|
297
|
+
*
|
|
298
|
+
* @param root CCC 根
|
|
299
|
+
* @param dirName 轨迹目录名
|
|
300
|
+
* @param snapshotInput 已读好的快照输入
|
|
301
|
+
* @param deps 可注入依赖(测试用)
|
|
302
|
+
* @returns 四态结果
|
|
303
|
+
*/
|
|
304
|
+
export declare function evaluateCro(root: string, dirName: string, snapshotInput: CroSnapshotInput, deps?: CroDeps): Promise<CroOutcome>;
|
|
305
|
+
/** 人读一行(日志用;`evaluateCro` 结果的稳定摘要) */
|
|
306
|
+
export declare function renderCroOutcome(dirName: string, outcome: CroOutcome): string;
|