@shgroup/dsh-serenity-hooks 1.85.0 → 1.87.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 +1 -1
- package/lib/{ccc-roots-VCJ3aYJA.js → ccc-roots-BI_4UVcj.js} +1 -1
- package/lib/im/channels.d.ts +55 -1
- package/lib/im/hook.d.ts +17 -0
- package/lib/im/log-scan.d.ts +53 -0
- package/lib/im/route.d.ts +8 -1
- package/lib/index.js +727 -494
- package/lib/{message-source-D7dVNDIh.js → message-source-DRkQwfgh.js} +2 -2
- package/lib/{profile-DnLSb7yv.js → profile-vHheCJiU.js} +1 -1
- package/lib/{route-Dr0SFD8A.js → route-3OzOAjec.js} +8 -1
- package/lib/seams/context.d.ts +13 -0
- package/lib/seams/guards.d.ts +51 -87
- package/lib/seams/tool-visibility.d.ts +76 -0
- package/lib/{session-I6SM2GT6.js → session-DZXSLLEx.js} +3 -3
- package/lib/{settings-4ag1jtR0.js → settings-H7TVDayZ.js} +1 -1
- package/lib/tools/im-docs.d.ts +20 -0
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.87.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": {
|
|
@@ -227,7 +227,7 @@ async function listCccs(ctx, opts = {}) {
|
|
|
227
227
|
if (typeof defaultRoot === "string" && defaultRoot !== "" && !c.roots.includes(defaultRoot)) c.roots.unshift(defaultRoot);
|
|
228
228
|
let rolesOf = null;
|
|
229
229
|
if (opts.withRoles === true) {
|
|
230
|
-
const { listTrajectoryConstraints } = await import("./profile-
|
|
230
|
+
const { listTrajectoryConstraints } = await import("./profile-vHheCJiU.js").then((n) => n.a);
|
|
231
231
|
rolesOf = (root) => {
|
|
232
232
|
try {
|
|
233
233
|
return listTrajectoryConstraints(root).map((p) => p.code);
|
package/lib/im/channels.d.ts
CHANGED
|
@@ -87,6 +87,39 @@ export interface ImChannel {
|
|
|
87
87
|
}>;
|
|
88
88
|
/** 通道健康快照(`status` 动作)——必须可 JSON 化(工具返回值契约) */
|
|
89
89
|
status(root: string): ImJson;
|
|
90
|
+
/**
|
|
91
|
+
* 列出**与这条 CCC 有过往来的对端用户**(`contacts` 动作,2026-10-22 新增)。
|
|
92
|
+
*
|
|
93
|
+
* 🔴 **它存在的理由**(owner 原话:「配路由需要的是账户到轨迹,轨迹 CCC 知道,
|
|
94
|
+
* 账户 CCC 不知道,需要有个能力让 CCC 查询用户配好的账户」):
|
|
95
|
+
* 路由要写 `{ user: "<通道用户 id>", trajectory: "<轨迹编码>" }`。轨迹编码 CCC 知道,
|
|
96
|
+
* **但"对方是谁"它不知道** —— 那个 id 只存在于聊天记录里。
|
|
97
|
+
* ⇒ 没有本动作,CCC 配路由时只能去翻日志或猜。
|
|
98
|
+
*
|
|
99
|
+
* ⚠️ **可选**:某通道若没有可扫的记录来源(例如未实现记录落盘),不实现即可,
|
|
100
|
+
* 工具会如实返回 `ACTION_UNSUPPORTED` —— 比返回空数组好(空数组会被读成"确实没有人")。
|
|
101
|
+
*/
|
|
102
|
+
listContacts?(root: string): ImContact[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 一个"有过往来的对端"(`contacts` 动作的条目)。
|
|
106
|
+
*
|
|
107
|
+
* 字段刻意**全部可 JSON 化**(工具返回值契约),且不引用内部类型。
|
|
108
|
+
*/
|
|
109
|
+
export interface ImContact {
|
|
110
|
+
/** 该对端属于哪个渠道(`weixin` / `feishu`) */
|
|
111
|
+
channel: string;
|
|
112
|
+
/** 对端在该通道内的稳定 id(微信 `xxx@im.wechat` / 飞书 `ou_…`) */
|
|
113
|
+
userId: string;
|
|
114
|
+
/** 收发过它的账号(本地唯一键,如 `wechat-1`)—— 多账号 CCC 靠它区分 */
|
|
115
|
+
accountIds: string[];
|
|
116
|
+
/** 当前路由到的轨迹编码;未配路由 ⇒ `null` */
|
|
117
|
+
route: string | null;
|
|
118
|
+
/** 首次/最近出现时间(ISO8601;取不到为 `null`) */
|
|
119
|
+
firstSeen: string | null;
|
|
120
|
+
lastSeen: string | null;
|
|
121
|
+
/** 见过的消息条数 */
|
|
122
|
+
messages: number;
|
|
90
123
|
}
|
|
91
124
|
/** 注册通道(index.ts apply 调用;重复 id 覆盖——测试与热重载友好) */
|
|
92
125
|
export declare function registerImChannel(channel: ImChannel): void;
|
|
@@ -95,8 +128,29 @@ export declare function __resetImChannelsForTest(): void;
|
|
|
95
128
|
/** 已注册通道 id(工具 `channel` 参数枚举) */
|
|
96
129
|
export declare function imChannelIds(): string[];
|
|
97
130
|
/**
|
|
98
|
-
* 可见性判据:本 CCC
|
|
131
|
+
* 可见性判据:本 CCC 是否**有任何可用的 IM 账号**。
|
|
99
132
|
* 未启用 → 工具面对该 agent **不可见**(`tools.restrict` 移除,而非调用时报错)。
|
|
133
|
+
*
|
|
134
|
+
* ## 🔴 2026-10-22 判据修正(owner 令:「把开关升级为有任意账号即可,而不是盯着微信」)
|
|
135
|
+
*
|
|
136
|
+
* **旧判据** = 遍历已注册通道、任一 `channel.isEnabled(root)` 为真即可见。
|
|
137
|
+
* **旧判据的缺陷**(实测于一个只配飞书的 CCC):`feishuChannel.isEnabled()` 首行是
|
|
138
|
+
* `if (settings.enabled !== true) return false` —— 总开关一关,**后面的账号判断根本不执行**
|
|
139
|
+
* ⇒ 账号明明配了、`enabled:true`,却因为一个**渠道无关的总开关**被永久判成"未启用"
|
|
140
|
+
* ⇒ `im-bridge` 在模型清单里彻底消失,而配置面看上去是对的(`accounts[]` 里躺着一条 feishu)。
|
|
141
|
+
* 这正是 D179 的形态:**配置说它有,行为说它没有**,且零报错。
|
|
142
|
+
*
|
|
143
|
+
* **新判据** = **账号即事实**:`readImSettings(root).accounts` 里存在
|
|
144
|
+
* 「`enabled !== false` 的账号」即视为可见。理由三条:
|
|
145
|
+
* ① **可见性问的是"这个 CCC 有没有 IM 能力"**,而能力的载体是**账号**,不是总开关;
|
|
146
|
+
* ② 总开关的语义是"**要不要让桥跑起来**",把它当成可见性上限,会让"配好了但暂时不想让它跑"
|
|
147
|
+
* 这种合法状态变成"工具凭空消失";
|
|
148
|
+
* ③ 判**按账号判**后,微信与飞书不再各有一套"总开关 + 账号"的双重判据 ⇒ 消除同族漂移。
|
|
149
|
+
*
|
|
150
|
+
* ⚠️ **不放宽的部分**:没有账号 ⇒ 仍然不可见(`accounts` 为空即 false)。
|
|
151
|
+
* ⚠️ 单个通道自己的 `isEnabled` 仍是**发送/启停**判据(那是通道自己的事),
|
|
152
|
+
* 只是**不再兼任**工具可见性判据 —— 两件事被这次修正分开了。
|
|
153
|
+
*
|
|
100
154
|
* @param root 本会话 CCC 根(由调用方解析)
|
|
101
155
|
*/
|
|
102
156
|
export declare function hasEnabledImChannel(root: string): boolean;
|
package/lib/im/hook.d.ts
CHANGED
|
@@ -27,6 +27,19 @@ interface WeixinHookEventBase {
|
|
|
27
27
|
ts: number;
|
|
28
28
|
/** 触发 CCC 根(多 CCC 一进程可区分) */
|
|
29
29
|
cccRoot: string;
|
|
30
|
+
/**
|
|
31
|
+
* 渠道 id(`weixin` / `feishu`)。
|
|
32
|
+
*
|
|
33
|
+
* 🔴 2026-10-22 新增(owner:「一个 CCC 绑定多个账户很常见,聊天记录落盘要完善」)。
|
|
34
|
+
* 此前**没有这个字段** ⇒ 三个后果:① 飞书事件落进同一份 jsonl 后**无法区分渠道**
|
|
35
|
+
* (只能靠 `ou_` / `@im.wechat` 的形状猜 = D167 违反:认族不认名);② 一个 CCC 配多个
|
|
36
|
+
* 账号时**不知是哪个渠道**收发的(`accountId` 是本地命名习惯,不携带渠道信息);
|
|
37
|
+
* ③ 按渠道分组统计在字段层就做不到。
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ **向后兼容**:存量事件没有它 ⇒ 读侧按 `accountId → im.accounts[].channel`
|
|
40
|
+
* 回填,仍取不到则按 `weixin`(那是本字段存在之前唯一的渠道)。
|
|
41
|
+
*/
|
|
42
|
+
channel: string;
|
|
30
43
|
/** 接收账号(incoming)/ 发送账号(outgoing) */
|
|
31
44
|
accountId: string;
|
|
32
45
|
/** 对端用户(from_user_id,形如 userA@im.wechat) */
|
|
@@ -71,6 +84,8 @@ type WeixinHookEvent = WeixinHookIncomingEvent | WeixinHookOutgoingEvent;
|
|
|
71
84
|
/** incoming 事件构造输入(bridge 提供实参) */
|
|
72
85
|
interface IncomingHookInput {
|
|
73
86
|
cccRoot: string;
|
|
87
|
+
/** 渠道 id(`weixin` / `feishu`);缺省 `weixin`(存量调用点零改动) */
|
|
88
|
+
channel?: string;
|
|
74
89
|
accountId: string;
|
|
75
90
|
userId: string;
|
|
76
91
|
sessionId: string;
|
|
@@ -81,6 +96,8 @@ interface IncomingHookInput {
|
|
|
81
96
|
/** outgoing 事件构造输入(bridge 提供实参) */
|
|
82
97
|
interface OutgoingHookInput {
|
|
83
98
|
cccRoot: string;
|
|
99
|
+
/** 渠道 id(`weixin` / `feishu`);缺省 `weixin` */
|
|
100
|
+
channel?: string;
|
|
84
101
|
accountId: string;
|
|
85
102
|
userId: string;
|
|
86
103
|
sessionId: string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* im/log-scan.ts — 读聊天记录落盘,汇总出「与谁有过往来」(`contacts` 的数据源)
|
|
3
|
+
*
|
|
4
|
+
* ## 这个文件解决什么
|
|
5
|
+
*
|
|
6
|
+
* owner 2026-10-22:「配路由需要的是**账户到轨迹**,轨迹 CCC 知道,**账户 CCC 不知道**,
|
|
7
|
+
* 需要有个能力让 CCC 查询用户配好的账户」。这里的"账户"= **通道用户**(对方是谁),
|
|
8
|
+
* 不是 IM 账号(我们自己登录的身份,`status` 早就能查)。
|
|
9
|
+
*
|
|
10
|
+
* 那个 id(微信 `xxx@im.wechat` / 飞书 `ou_…`)不在任何配置文件里 ——
|
|
11
|
+
* 它**只在聊天记录里出现过**。⇒ 本文件把记录读成联系人表。
|
|
12
|
+
*
|
|
13
|
+
* ## 🔴 两条硬边界
|
|
14
|
+
*
|
|
15
|
+
* 1. **只读**:本文件不写任何东西。真相源仍是那些 jsonl(append-only 事件流),
|
|
16
|
+
* 本文件只是它的一次投影 —— ⛔ 不产生第二真相源。
|
|
17
|
+
* 2. **不猜渠道**:事件的 `channel` 字段为准;缺它(存量事件)⇒ 按
|
|
18
|
+
* `accountId → im.accounts[].channel` 回填,仍取不到才落 `weixin`
|
|
19
|
+
* (那是该字段存在之前唯一的渠道)。⛔ **绝不按 userId 的形状猜**
|
|
20
|
+
* (`ou_` 前缀 / `@im.wechat` 后缀都是命名习惯,不是判据 —— D167 认族不认名)。
|
|
21
|
+
*/
|
|
22
|
+
/** 日志根目录族(**新旧两代并存**:`_im-logs/<channel>/` 与存量 `_weixin-logs/`) */
|
|
23
|
+
export declare const IM_LOG_DIR_NEW = "_im-logs";
|
|
24
|
+
export declare const IM_LOG_DIR_LEGACY = "_weixin-logs";
|
|
25
|
+
/** 汇总结果(可 JSON 化) */
|
|
26
|
+
export interface ScannedContact {
|
|
27
|
+
channel: string;
|
|
28
|
+
userId: string;
|
|
29
|
+
accountIds: string[];
|
|
30
|
+
/** 事件里记录的路由目标(`role` 字段;它是事件发生当时的轨迹编码,未必等于**当前**路由) */
|
|
31
|
+
roleAtEvent: string | null;
|
|
32
|
+
firstSeen: number | null;
|
|
33
|
+
lastSeen: number | null;
|
|
34
|
+
messages: number;
|
|
35
|
+
}
|
|
36
|
+
/** 扫描结果(含**扫描域**——否定性结论必须附它) */
|
|
37
|
+
export interface ScanResult {
|
|
38
|
+
contacts: ScannedContact[];
|
|
39
|
+
/** 实际读过的文件(相对 CCC 根) */
|
|
40
|
+
sources: string[];
|
|
41
|
+
/** 读失败的文件及其原因(不静默) */
|
|
42
|
+
errors: string[];
|
|
43
|
+
}
|
|
44
|
+
/** 某 CCC 下**存在**的日志目录(相对根),新代在前 */
|
|
45
|
+
export declare function imLogRoots(root: string): string[];
|
|
46
|
+
/**
|
|
47
|
+
* 扫描本 CCC 的全部聊天记录,按 `(channel, userId)` 汇总。
|
|
48
|
+
*
|
|
49
|
+
* @param root CCC 根
|
|
50
|
+
* @param accountChannel 存量事件缺 `channel` 时的回填表(`accountId → channel`)
|
|
51
|
+
* @returns 汇总结果 + **扫描域** + 读取错误(三者都不可省)
|
|
52
|
+
*/
|
|
53
|
+
export declare function scanImContacts(root: string, accountChannel?: Record<string, string>): ScanResult;
|
package/lib/im/route.d.ts
CHANGED
|
@@ -64,8 +64,15 @@ export declare function readImSettings(root: string, paths?: string[]): ImSettin
|
|
|
64
64
|
* ⚠️ 2026-10-19 起它是 {@link readImSettings} 的**薄别名**(签名与返回形状不变):
|
|
65
65
|
* 旧调用点无需改动,但**配置来源已经统一**——`im` 段优先、`weixin` 段回退。
|
|
66
66
|
* 保留名字是为了让既有调用点与测试**逐字不动**(P1 是纯重构,读数必须不变)。
|
|
67
|
+
*
|
|
68
|
+
* 🔴 2026-10-22:**返回类型由 `WeixinSettings` 改为 `ImSettings`**。
|
|
69
|
+
* 此前实现返回的就是 `readImSettings(...)`(已是归一形态:`accounts[].channel` 必填),
|
|
70
|
+
* 却把它断言成旧类型 —— 于是类型层"看不见"`channel` 字段,
|
|
71
|
+
* 调用方写 `a.channel` 会报 TS2339,而**运行时它一直在那儿**。
|
|
72
|
+
* ⇒ 那是**类型声明在撒谎**,不是调用方写错。改成真实类型后,多账户/多渠道的读侧才敢用它。
|
|
73
|
+
* ⚠️ 附带效应:`WeixinSettings` 的旧形状调用方若用到旧字段,会在此处暴露(好事,早暴露)。
|
|
67
74
|
*/
|
|
68
|
-
export declare function readWeixinSettings(root: string, paths?: string[]):
|
|
75
|
+
export declare function readWeixinSettings(root: string, paths?: string[]): ImSettings;
|
|
69
76
|
/** 账号凭据(localstore credential scope;baseUrl 缺省 = 官方默认) */
|
|
70
77
|
export interface WeixinAccountCredential {
|
|
71
78
|
token: string;
|