@shgroup/dsh-serenity-hooks 1.105.0 → 1.108.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 +4 -3
- package/dsh.plugin.json +2 -3
- package/lib/{profile-Dpg3wYO5.js → bind-file-B0TuOP89.js} +1 -333
- package/lib/{ccc-B-h_A7uc.js → ccc-BmpE3cQz.js} +64 -12
- package/lib/{ccc-roots-BPicjCsj.js → ccc-roots-DPMwBywc.js} +2 -2
- package/lib/{channels-bAiZlY07.js → channels-Dty_NIl_.js} +2 -146
- package/lib/client.js +125 -0
- package/lib/contract-BiNzzd77.js +2197 -0
- package/lib/index.js +217 -6361
- package/lib/invariant.js +1 -2
- package/lib/kernel/ccc.d.ts +60 -13
- package/lib/kernel/invariant.d.ts +2 -2
- package/lib/message-source-BTIe2cVO.js +254 -0
- package/lib/observation-BWpkA865.js +210 -0
- package/lib/profile-BjBn_-be.js +336 -0
- package/lib/{route-CmEoKp9w.js → route-reyv8qcB.js} +2 -2
- package/lib/rules-BLkYTPUy.js +261 -0
- package/lib/scheduler-B0pB9mxI.js +3603 -0
- package/lib/seams/guards.d.ts +0 -4
- package/lib/{service-Cndtf7q4.js → service-Z3aSR1cn.js} +1 -1
- package/lib/service-xvjwsX3p.js +249 -0
- package/lib/{session-CYqyod3z.js → session-BlgUdKH4.js} +7 -5
- package/lib/settings-DY4_DYYY.js +145 -0
- package/lib/subscriptions-CyrfNpcG.js +273 -0
- package/lib/tools/diag/service.d.ts +74 -24
- package/lib/tools/kit/service.d.ts +1 -1
- package/lib/tools/msm/service.d.ts +2 -2
- package/lib/trajectory/subscriptions.d.ts +170 -0
- package/package.json +2 -2
- package/lib/message-source-DoVtXnoe.js +0 -513
- package/lib/tools/diag/index.d.ts +0 -25
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trajectory/subscriptions.ts — 轨迹消息订阅(v1.107.0,owner 2026-11-01 令)
|
|
3
|
+
*
|
|
4
|
+
* ## 这是什么(一句话)
|
|
5
|
+
*
|
|
6
|
+
* **观察者盯一组成员;任一成员「一轮结束」,观察者被唤醒并收到一条通知。**
|
|
7
|
+
*
|
|
8
|
+
* 🔴 **它是通知,不是控制**(owner 原话「loose notification, not control」):
|
|
9
|
+
* 观察者收到的是「某人刚干完一轮活儿」,**不带他干了什么**(owner:「不需要带它做了什么」),
|
|
10
|
+
* 也不赋予它任何指挥权。要不要看、看了做什么,全由观察者自己决定。
|
|
11
|
+
*
|
|
12
|
+
* ## 与既有机制的关系(⛔ 别混)
|
|
13
|
+
*
|
|
14
|
+
* | 机制 | 主语 | 判据 |
|
|
15
|
+
* |---|---|---|
|
|
16
|
+
* | **CRO** | **自己**决定把自己叫起来(自编程) | `continuous-re-occurrence.ts` 文件存在 |
|
|
17
|
+
* | **本模块** | **别人**干完活了,通知我(互观察) | CCC 配置 `trajectorySubscriptions` |
|
|
18
|
+
* | `send-later` | 我预约未来的某一刻 | `wake-registry.json` 的 `at` |
|
|
19
|
+
*
|
|
20
|
+
* 三者都走唤醒链路,但**主语与判据都不同**。⛔ 本模块**不写 `wake-registry.json`** ——
|
|
21
|
+
* 那会制造第二真相源:订阅是"事件驱动的即时通知",注册表是"时刻驱动的预约",
|
|
22
|
+
* 混在一张表里,"这条条目为什么在等"就再也分不出来了。
|
|
23
|
+
*
|
|
24
|
+
* ## 🔴 「一轮结束」的判据——复用,不新造
|
|
25
|
+
*
|
|
26
|
+
* **`agent.status` 由 running → idle**,且这是**唯一权威**。判据已在 `cro/turns.ts` 的
|
|
27
|
+
* D243/D244 两段付过代价定死:
|
|
28
|
+
* · `agent/created` 证明「载体被发布」,不是「有轮次在跑」(重启会把它打成假在跑);
|
|
29
|
+
* · `agent/turn-stopping` 是「轮末**暂停点**」不是「轮结束」——监听者在里面 `steer` 会让同轮继续跑,
|
|
30
|
+
* 实测导致一条轮连跑 9 分钟而这期间表是空的。
|
|
31
|
+
* ⇒ **本模块直接订阅 `agent/status`**,⛔ 不另立判据(两个判据并存会立刻分叉)。
|
|
32
|
+
*
|
|
33
|
+
* 频率**不设上限**(owner:「不设定上限,结束了就通知」)。之所以不需要额外防抖:
|
|
34
|
+
* `agent/status` **只在真实状态跃迁时发**(宿主 `AgentLoop.setPhase`)。
|
|
35
|
+
*
|
|
36
|
+
* ## 三个失败面(**一个都不许静默**)
|
|
37
|
+
*
|
|
38
|
+
* | # | 失败面 | 形态 | 处置 |
|
|
39
|
+
* |---|---|---|---|
|
|
40
|
+
* | ① | 成员解析不出轨迹 | 会话没绑任何轨迹 / 轨迹编解码不出 | 跳过(**这不是错误**:没绑定的会话不归任何轨迹,它"一轮结束"与订阅无关) |
|
|
41
|
+
* | ② | 观察者唤不醒 | 无载体 / 载体起不来 | **响亮日志**(`console.warn`)——否则"订阅配了但从不生效"与"没人干活"同形 |
|
|
42
|
+
* | ③ | 投递失败 | `sendToCognitiveTrajectory` 返回 `ok:false` | **响亮日志** + 计数(供 `dashboard diag` 后续读) |
|
|
43
|
+
*
|
|
44
|
+
* 🔴 **为什么 ① 不算失败**:一条 CCC 里绝大多数会话**没有**绑轨迹(临时会话、别的 agent 的会话)。
|
|
45
|
+
* 把它们当失败会每天刷出上百条噪声,反而**淹没**②③那种真失败(穷举噪声 = 另一种静默)。
|
|
46
|
+
* ⇒ 判据:**"解析不出"不是错误,"解析出了但送不到"才是**。
|
|
47
|
+
*
|
|
48
|
+
* ## 清理(本容器栽过 5 次"只增不清")
|
|
49
|
+
*
|
|
50
|
+
* 本模块**不持有跨事件的状态表** —— 每次事件现读配置、现解析、现投递,投递完即忘。
|
|
51
|
+
* ⇒ 结构上不可能"只增不清"。⚠️ 唯一的进程态是**投递计数**(诊断用,固定几条键),
|
|
52
|
+
* 它有上界(键 = 订阅数),且随插件卸载清空({@link registerTrajectorySubscriptions} 的拆卸)。
|
|
53
|
+
*/
|
|
54
|
+
import { type TrajectorySubscription } from '../kernel/ccc.js';
|
|
55
|
+
/** 投递计数(诊断用;键 = `watcher←member`,上界 = 订阅数 × 成员数) */
|
|
56
|
+
interface DeliveryStat {
|
|
57
|
+
/** 成功投递次数 */
|
|
58
|
+
ok: number;
|
|
59
|
+
/** 失败次数(② 唤不醒 / ③ 投递返回 false 都记这里) */
|
|
60
|
+
failed: number;
|
|
61
|
+
/** 最后一次失败的说明(成功时不清空 —— 保留最近一次失败证据) */
|
|
62
|
+
lastError: string | null;
|
|
63
|
+
/** 最后一次投递尝试的时刻 */
|
|
64
|
+
lastAt: number | null;
|
|
65
|
+
}
|
|
66
|
+
/** 投递统计快照(`dashboard diag` 与测试读;空 = 从未有投递尝试) */
|
|
67
|
+
export declare function subscriptionStats(): Array<{
|
|
68
|
+
key: string;
|
|
69
|
+
} & DeliveryStat>;
|
|
70
|
+
/** 测试用:复位(避免用例间串味) */
|
|
71
|
+
export declare function __resetSubscriptionStatsForTest(): void;
|
|
72
|
+
/**
|
|
73
|
+
* 从一条「成员一轮结束」事件里,找出**该通知谁**。
|
|
74
|
+
*
|
|
75
|
+
* 纯函数(不读盘、不投递)—— 便于单测"谁该被通知"这件事本身。
|
|
76
|
+
*
|
|
77
|
+
* @param subs 订阅表
|
|
78
|
+
* @param memberDir 刚结束一轮的成员(轨迹**目录名**,非编码)
|
|
79
|
+
* @returns 需要通知的订阅(同一个 watcher 的多条订阅**各自保留**:`note` 不同)
|
|
80
|
+
*/
|
|
81
|
+
export declare function pickSubscriptionsFor(subs: readonly TrajectorySubscription[], memberDir: string): TrajectorySubscription[];
|
|
82
|
+
/**
|
|
83
|
+
* 通知正文。
|
|
84
|
+
*
|
|
85
|
+
* 🔴 **刻意不带"它干了什么"**(owner:「不需要带它做了什么」)。带上就得先解析对方的产出,
|
|
86
|
+
* 那会让"通知"变成"代它说话"——而本机制明确只是通知。
|
|
87
|
+
* ⚠️ 但**必须**带够定位信息:谁说的、什么时候、是哪条订阅触发的(否则观察者拿到一句话无从查起)。
|
|
88
|
+
*
|
|
89
|
+
* @param memberDir 刚结束一轮的成员目录名
|
|
90
|
+
* @param sub 命中的订阅(用它的 `note` 区分是哪条订阅)
|
|
91
|
+
* @param atMs 事件时刻
|
|
92
|
+
* @param iso 时刻格式化函数(注入便于测试固定时刻)
|
|
93
|
+
*/
|
|
94
|
+
export declare function buildSubscriptionText(memberDir: string, sub: TrajectorySubscription, atMs: number, iso: (ms: number) => string): string;
|
|
95
|
+
/** 装配依赖(注入便于单测;生产由 `defaultSubscriptionDeps` 填真实现) */
|
|
96
|
+
export interface SubscriptionDeps {
|
|
97
|
+
/**
|
|
98
|
+
* 某会话所属的 CCC 根(解析不出 ⇒ null)。
|
|
99
|
+
*
|
|
100
|
+
* 🔴 **为什么不固定传一个 root**:一个宿主进程可能同时服务**多个 CCC**
|
|
101
|
+
* (本机实测 ≥2 个不同的根本同时活跃)。装配期只能拿得到一个根,
|
|
102
|
+
* 拿它去服务全部会话 ⇒ 别的 CCC 的成员永远命中不了自己的订阅表。
|
|
103
|
+
* ⇒ 根必须**按事件发生的那条会话**解析。
|
|
104
|
+
*/
|
|
105
|
+
rootForSession: (sessionId: string) => string | null;
|
|
106
|
+
/** 读订阅表(含坏项) */
|
|
107
|
+
readSubs: (root: string) => {
|
|
108
|
+
subs: TrajectorySubscription[];
|
|
109
|
+
problems: string[];
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* 会话 id → 该会话所承载的轨迹**目录名**(解析不出 ⇒ null)。
|
|
113
|
+
* ⚠️ 返回目录名而不是编码:配置里写的是 `S###` **或目录名**,统一到目录名再比,
|
|
114
|
+
* 否则「配置写 S142、实际解析出 2026-08-24--S142--xxx」这种就会永远不命中。
|
|
115
|
+
*/
|
|
116
|
+
dirForSession: (root: string, sessionId: string) => string | null;
|
|
117
|
+
/** 即时投递(`sendToCognitiveTrajectory`) */
|
|
118
|
+
deliver: (root: string, target: string, message: string) => Promise<{
|
|
119
|
+
ok: boolean;
|
|
120
|
+
detail?: string;
|
|
121
|
+
}>;
|
|
122
|
+
/** 时刻格式化 */
|
|
123
|
+
iso: (ms: number) => string;
|
|
124
|
+
/** 日志(注入便于断言"响亮"确实发生) */
|
|
125
|
+
warn: (msg: string) => void;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 处理一次「某会话一轮结束」。
|
|
129
|
+
*
|
|
130
|
+
* 这是本模块**唯一的业务入口**(事件适配在 {@link registerTrajectorySubscriptions} 里)。
|
|
131
|
+
* 分成"事件适配"与"业务处理"两半,是为了让后者**完全可单测**:
|
|
132
|
+
* 不需要真宿主、不需要真 agent,只要喂一个 `sessionId`。
|
|
133
|
+
*
|
|
134
|
+
* @param deps 注入的依赖
|
|
135
|
+
* @param sessionId 刚结束一轮的会话 id
|
|
136
|
+
* @param nowMs 事件时刻
|
|
137
|
+
* @returns 本次处理的结果摘要(**只用于测试与诊断**,生产调用方忽略)
|
|
138
|
+
*/
|
|
139
|
+
export declare function onTurnFinished(deps: SubscriptionDeps, sessionId: string, nowMs: number): Promise<{
|
|
140
|
+
skipped: 'no-ccc' | 'no-subscriptions' | 'unbound-member' | null;
|
|
141
|
+
delivered: number;
|
|
142
|
+
failed: number;
|
|
143
|
+
}>;
|
|
144
|
+
/**
|
|
145
|
+
* 装配:订阅 `agent/status`,把「running → idle」翻译成一次 {@link onTurnFinished}。
|
|
146
|
+
*
|
|
147
|
+
* 🔴 **只认 `agent/status`**(理由见文件头「一轮结束的判据」)。具体是:
|
|
148
|
+
* 记住上一次观察到的状态,**只在 running → idle 这一次跃迁**上报。
|
|
149
|
+
* ⚠️ 为什么要自己记上一次状态:宿主虽然在**状态变化时**才发事件,但多条订阅/多条轨迹会
|
|
150
|
+
* 各自收到同一事件;更要紧的是**进程刚启动时**可能直接收到 `idle`(载体被载入即 idle),
|
|
151
|
+
* 那不是"一轮结束",那是"它一直闲着"。⇒ **没有前置 running 就不算结束**。
|
|
152
|
+
*
|
|
153
|
+
* @param ctx 插件上下文(结构化访问 `on`)
|
|
154
|
+
* @param deps 依赖(生产实现在 {@link defaultSubscriptionDeps})
|
|
155
|
+
*/
|
|
156
|
+
export declare function registerTrajectorySubscriptions(ctx: unknown, deps: SubscriptionDeps): void;
|
|
157
|
+
/**
|
|
158
|
+
* 生产依赖:把注入面接到真实现。
|
|
159
|
+
*
|
|
160
|
+
* ⚠️ **动态 import**:本模块被 `index.ts` 静态引入,而 `deliver` 需要调度器
|
|
161
|
+
* (`wake/scheduler.ts` 静态链很重)⇒ 放到调用时再取,避免装配期把整条调度器链路拖进来。
|
|
162
|
+
*
|
|
163
|
+
* 🔴 **根按会话解析**(不是装配期固定):见 {@link SubscriptionDeps.rootForSession} 的理由。
|
|
164
|
+
* 取 cwd 的方式与 `trajectory/live.ts` 同源(`ctx.sessions.list()` 的 `header.cwd`)
|
|
165
|
+
* —— ⛔ 不另写一份"找会话 cwd"的逻辑(会漂)。
|
|
166
|
+
*
|
|
167
|
+
* @param ctx 插件上下文(投递与读会话都用它)
|
|
168
|
+
*/
|
|
169
|
+
export declare function defaultSubscriptionDeps(ctx: unknown): SubscriptionDeps;
|
|
170
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:
|
|
3
|
+
"version": "1.108.0",
|
|
4
|
+
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:12 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/localstore/container_admin;另两个 im-bridge/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": {
|
|
7
7
|
"type": "git",
|