@shgroup/dsh-serenity-hooks 1.70.0 → 1.72.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 +21 -19
- package/dsh.plugin.json +2 -2
- package/lib/{ccc-C33QWoWZ.js → ccc-Dkoa2oW3.js} +16 -2
- package/lib/{ccc-roots-RzasspMr.js → ccc-roots-jZYR2GVE.js} +16 -10
- package/lib/client.js +2 -2
- package/lib/{gateway-auth.d.ts → gateway/auth.d.ts} +3 -3
- package/lib/{gateway-dsh-auth.d.ts → gateway/dsh-auth.d.ts} +1 -1
- package/lib/{gateway-proxy.d.ts → gateway/proxy.d.ts} +3 -3
- package/lib/{gateway.d.ts → gateway/server.d.ts} +5 -5
- package/lib/{totp.d.ts → gateway/totp.d.ts} +1 -1
- package/lib/{output-guard.d.ts → guard/filter.d.ts} +2 -2
- package/lib/{output-guard-seam.d.ts → guard/seam.d.ts} +2 -2
- package/lib/{weixin-api-BrRcPiQs.js → ilink-DEpmE3yi.js} +6 -6
- package/lib/im/accounts.d.ts +105 -0
- package/lib/{weixin-bridge.d.ts → im/bridge.d.ts} +16 -9
- package/lib/{im-bridge.d.ts → im/channels.d.ts} +3 -3
- package/lib/{weixin-send-endpoint.d.ts → im/endpoint.d.ts} +2 -2
- package/lib/{weixin-output-guard.d.ts → im/guard.d.ts} +1 -1
- package/lib/{weixin-hook.d.ts → im/hook.d.ts} +1 -1
- package/lib/{weixin-api.d.ts → im/ilink.d.ts} +3 -3
- package/lib/{human-channel.d.ts → im/inbound.d.ts} +4 -4
- package/lib/{weixin-route.d.ts → im/route.d.ts} +52 -11
- package/lib/{weixin-send-api.d.ts → im/send.d.ts} +3 -3
- package/lib/im/weixin-account.d.ts +45 -0
- package/lib/{im-weixin.d.ts → im/weixin-channel.d.ts} +2 -2
- package/lib/index.js +1008 -815
- package/lib/invariant.js +2 -2
- package/lib/{ccc-roots.d.ts → kernel/ccc-roots.d.ts} +10 -10
- package/lib/{ccc.d.ts → kernel/ccc.d.ts} +114 -44
- package/lib/{config-ops.d.ts → kernel/config.d.ts} +2 -2
- package/lib/kernel/constants.d.ts +20 -0
- package/lib/{invariant.d.ts → kernel/invariant.d.ts} +1 -1
- package/lib/{json.d.ts → kernel/json.d.ts} +1 -1
- package/lib/{message-source.d.ts → kernel/message-source.d.ts} +1 -1
- package/lib/{ports.d.ts → kernel/ports.d.ts} +1 -1
- package/lib/{time.d.ts → kernel/time.d.ts} +1 -1
- package/lib/message-source-kVoNK0C5.js +432 -0
- package/lib/{deepseek-vision-patch.d.ts → patches/deepseek-vision.d.ts} +3 -3
- package/lib/{opencode-provider.d.ts → patches/opencode-provider.d.ts} +1 -1
- package/lib/{web-fetch-provider.d.ts → patches/web-fetch.d.ts} +1 -1
- package/lib/{message-source-DhA1dVBi.js → profile-DyMJ_bfr.js} +277 -463
- package/lib/role/skiff/config.d.ts +79 -0
- package/lib/role/skiff/core.d.ts +155 -0
- package/lib/{skiff-debug.d.ts → role/skiff/debug.d.ts} +4 -4
- package/lib/role/skiff/profile.d.ts +115 -0
- package/lib/role/skiff/registry.d.ts +41 -0
- package/lib/{cheap-worker-ops.d.ts → role/worker/core.d.ts} +1 -1
- package/lib/{agent-idle.d.ts → role/worker/idle.d.ts} +2 -2
- package/lib/{cheap-worker-preset-inherit.d.ts → role/worker/preset.d.ts} +2 -2
- package/lib/{weixin-route-D_DvgPqy.js → route-f7JlinIt.js} +132 -47
- package/lib/seams/context.d.ts +3 -3
- package/lib/seams/guards.d.ts +18 -12
- package/lib/seams/keeper.d.ts +1 -1
- package/lib/seams/system-prompt.d.ts +3 -1
- package/lib/settings-lt1i3OZv.js +403 -0
- package/lib/{skills-discovery.d.ts → skills/discovery.d.ts} +1 -1
- package/lib/skills/resource-index.d.ts +2 -2
- package/lib/{face-host.d.ts → surface/faces.d.ts} +4 -4
- package/lib/{api.d.ts → surface/http.d.ts} +2 -2
- package/lib/{container-status.d.ts → surface/observation.d.ts} +16 -16
- package/lib/{settings-section.d.ts → surface/settings.d.ts} +1 -1
- package/lib/{status.d.ts → surface/status.d.ts} +2 -2
- package/lib/{usage-stats.d.ts → surface/usage.d.ts} +4 -4
- package/lib/{voyage-page.d.ts → surface/voyage/page.d.ts} +2 -2
- package/lib/{voyage-vocab.d.ts → surface/voyage/vocab.d.ts} +1 -1
- package/lib/tools/cognitive-trajectory.d.ts +2 -2
- package/lib/tools/{acc-diag.d.ts → diag/index.d.ts} +2 -2
- package/lib/{diag-ops.d.ts → tools/diag/service.d.ts} +7 -7
- package/lib/tools/eap.d.ts +1 -1
- package/lib/tools/fs/index.d.ts +7 -0
- package/lib/{fs-ops.d.ts → tools/fs/service.d.ts} +2 -2
- package/lib/tools/git/index.d.ts +4 -0
- package/lib/{git-ops.d.ts → tools/git/service.d.ts} +2 -2
- package/lib/tools/im-bridge.d.ts +2 -2
- package/lib/tools/{kit.d.ts → kit/index.d.ts} +1 -1
- package/lib/{kit-ops.d.ts → tools/kit/service.d.ts} +6 -6
- package/lib/tools/{localstore.d.ts → localstore/index.d.ts} +1 -1
- package/lib/{localstore-ops.d.ts → tools/localstore/service.d.ts} +2 -2
- package/lib/tools/{msm.d.ts → msm/index.d.ts} +1 -1
- package/lib/{msm-ops.d.ts → tools/msm/service.d.ts} +2 -2
- package/lib/tools/skiff-admin.d.ts +19 -14
- package/lib/{cognitive-trajectory-assistant.d.ts → trajectory/assistant.d.ts} +2 -2
- package/lib/{cognitive-trajectory-bound.d.ts → trajectory/bound.d.ts} +2 -2
- package/lib/{session-cleanup.d.ts → trajectory/cleanup.d.ts} +1 -1
- package/lib/{cognitive-trajectory-ops.d.ts → trajectory/core.d.ts} +55 -25
- package/lib/{live-sessions.d.ts → trajectory/live.d.ts} +1 -1
- package/lib/trajectory/prompt.d.ts +101 -0
- package/lib/{rebuild.d.ts → trajectory/rebuild.d.ts} +1 -1
- package/lib/{cognitive-trajectory-skills.d.ts → trajectory/skills.d.ts} +1 -1
- package/lib/{clock-runtime.d.ts → wake/clock.d.ts} +3 -3
- package/lib/{cro-guide.d.ts → wake/cro-guide.d.ts} +1 -1
- package/lib/{cro-log.d.ts → wake/cro-log.d.ts} +3 -3
- package/lib/{cro-turns.d.ts → wake/cro-turns.d.ts} +5 -5
- package/lib/{cro.d.ts → wake/cro.d.ts} +6 -6
- package/lib/{wake-registry.d.ts → wake/registry.d.ts} +2 -2
- package/lib/{wake-scheduler.d.ts → wake/scheduler.d.ts} +3 -3
- package/package.json +2 -2
- package/lib/constants.d.ts +0 -6
- package/lib/settings-section-0TPYWug9.js +0 -238
- package/lib/skiff-core.d.ts +0 -145
- package/lib/skiff-registry.d.ts +0 -26
- package/lib/skiff-role-cwxAt0gE.js +0 -203
- package/lib/skiff-role.d.ts +0 -89
- package/lib/tools/cc-fs.d.ts +0 -7
- package/lib/tools/git.d.ts +0 -4
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ dsh plugin --profile web add link:$(pwd)/hooks/dsh-serenity-hooks
|
|
|
80
80
|
| `serenity-skill-creator` | 增强型技能标准(serenity-skill)的创建器:`guide` 编写方法 / `create` 生成骨架(基础链路径由它算)/ `check` 结构检查(三态可分辨:已建模通过 · 普通技能 · 建模了但检查未执行)/ `review` 逐条合规问题清单 / `index` 预览**模型实际会看到**的参考件清单 / `status` 全容器一览 | 新建或改造技能、为技能建本体、需要确认"模型究竟能否看到指定参考件"时 |
|
|
81
81
|
| `cheap-worker` | 杂工:委派一个低成本模型的助手执行任务,取回其最终文本。一次调用一个串行子代理;`jobs` 可一次扇出多个并行(上限 10)。**不循环、不校验完成码、不写进度文件** | 大批量、重复性的工作(扫描数十个技能、逐用例回归) |
|
|
82
82
|
| `localstore` | 存储密钥与配置(凭据、偏好两个命名空间) | API key、密码集中放一处,不进 git |
|
|
83
|
-
| `container_admin` |
|
|
83
|
+
| `container_admin` | 机务舱:管理受限轨迹(skiff)、管理小工具注册表、查看全部配置 | 定义 skiff、注册新小工具、修改配置时 |
|
|
84
84
|
| `im-bridge` | 向 IM 联系人发消息(目前为微信):发文本、发文件、查已配置联系人、查通道状态。每次成功发送自动写入工作区的消息记录 | 需要 AI 主动给家人/同事发消息时(见 §6.2)。**条件出现**:仅在工作区配置了微信桥时可见 |
|
|
85
85
|
| `acc-diag` | ACC 运行态诊断:一次调用输出全报告——当前活跃会话数、各自所属工作区、唤醒时钟的武装态与 tick 次数、唤醒登记表的每一条 | **ACC 维护者专用**。**条件出现**:默认对所有工作区隐藏,仅在工作区配置中点名(`exclusiveTools`)时出现 |
|
|
86
86
|
| `diagram` | 绘图:AI 给出一段**标签式源码**,插件**在进程内**将其绘制为 PNG 并**贴入对话**——不启动浏览器、不截图、正常路径不落文件。坐标 = 画布像素、y 向下;写错的图元逐条报错并跳过,其余照常绘制;自带四项检查(块重叠 / 标签溢出 / 悬空端点 / 线压字),**只告警不拦图** | 需要把关系图/示意图直接放进对话时。**条件出现**:实验性 demo,**默认关**(关闭时该工具**根本不注册**,AI 看不到它、不占上下文);开关在设置面板「工具」组,拨动即时生效。中英文与常用符号均可绘制,因字体随插件内置(约 5 MB) |
|
|
@@ -100,7 +100,7 @@ dsh plugin --profile web add link:$(pwd)/hooks/dsh-serenity-hooks
|
|
|
100
100
|
| **工作区围墙** | 墙内可用,墙外一律拒绝(路径无法解析) | AI 不应接触工作区之外的内容 |
|
|
101
101
|
| **黑名单 / 治理文件** | 黑名单可配置;`.serenity` 等治理文件禁止 AI 写入 | 防止 AI 破坏自身所在的"地基" |
|
|
102
102
|
| **密钥文件守卫** | `localstore.json` 对**所有**工具拒绝(包括 read/grep/glob) | 密钥值在结构上无法外流 |
|
|
103
|
-
| **对外输出守卫** |
|
|
103
|
+
| **对外输出守卫** | 对外向会话(受限轨迹 / 重建会话)若答出敏感词,会被打回重答,并告知命中的词与修改方向 | 外部使用者不应看到内部机制 |
|
|
104
104
|
| **轨迹提醒** | 持续一段时间后会提醒 AI"把进度写回工作日志",并要求它回一个确认码 | 提醒是机制,不依赖自觉 |
|
|
105
105
|
| **工作日志体积提醒** | 工作日志(SESSION.md)超过 200KB 时提醒 AI"停下重写",并附四条重写原则(保留骨架 / 细节移至附件文件 / 可合并的合并 / 其余自行判断) | 日志持续增厚则无人读得完;重写成本低于堆积。阈值按工作区可调(`sessionKeeper.sessionMdMaxKB`,0 = 关) |
|
|
106
106
|
| **重建前交接** | 重建上下文前,要求 AI 先把"手头做到哪了 / 还差什么 / 下一步做什么"写入**本轨迹目录下的 `REBUILD-TODO.md`**(固定文件名);重建后的 AI 首先读取该文件并继续 | 上下文被清空,但在办的事不能丢。该便签是**只用一次的交界材料**:无格式义务、随时可丢;超过 10 KB 会提醒清理(`SCRATCH PRUNE`,整份删除亦可) |
|
|
@@ -112,7 +112,7 @@ dsh plugin --profile web add link:$(pwd)/hooks/dsh-serenity-hooks
|
|
|
112
112
|
| DSH 主界面 | 3080 | 本机自用(插件不碰这个端口,也不起这个监听器) |
|
|
113
113
|
| **网页登录入口** | 3081 | 外部/手机访问完整界面:登录后反向代理到 3080,可配工作区白名单 |
|
|
114
114
|
| **微信主动发送入口** | 3082(只绑 127.0.0.1) | 工作区外的脚本/集成用它给微信发消息(公网不可达,所以不需要密钥)。工作区内的 AI 不走这个端口,直接使用 `im-bridge` 工具 |
|
|
115
|
-
|
|
|
115
|
+
| **受限轨迹调试页** | 3099(只绑 127.0.0.1) | 调试 skiff 时使用,可切换工作区、查看对话轨迹 |
|
|
116
116
|
| **微信桥** | 无需端口(出站长轮询) | 家人在微信里直接和 AI 说话 |
|
|
117
117
|
|
|
118
118
|
> 3081 / 3099 **默认关**(须自行开启);3082 **默认开**(微信桥启用时才有意义)。默认只监听 127.0.0.1 的入口,如需暴露到公网,由使用者自行决定方式(隧道 / 反代 / 端口映射都行),插件不绑定任何特定做法。
|
|
@@ -233,8 +233,9 @@ my-workspace/ ← 工作区根目录(放一个 .serenity
|
|
|
233
233
|
(图片和文件会从微信 CDN 下载并解密,落到 `_tmp/weixin-inbound/`,随后把路径告知 AI)
|
|
234
234
|
- **正在输入**:处理期间微信会显示"正在输入…"
|
|
235
235
|
- **回复精简**:自动剥掉思考过程,微信只看到正文
|
|
236
|
-
-
|
|
237
|
-
- **路由**:微信号 →
|
|
236
|
+
- **会话保持**:同一条轨迹对应固定会话,重启后恢复历史,不会"失忆"(身份挂在**轨迹**上,不挂在人上)
|
|
237
|
+
- **路由**:微信号 → **认知轨迹编码**(精确匹配优先,`*` 兜底)
|
|
238
|
+
—— `routes: [{ "user": "*", "trajectory": "qa" }]`
|
|
238
239
|
- **主动发消息**(v1.31.0):用 `im-bridge` 工具,AI 自己就能发——
|
|
239
240
|
`im-bridge({channel:"weixin", action:"send", user:"yh", text:"内容"})`
|
|
240
241
|
(发文件:`action:"send-file"` + `file:"<工作区内的路径>"`,可加 `caption`)。
|
|
@@ -250,28 +251,29 @@ my-workspace/ ← 工作区根目录(放一个 .serenity
|
|
|
250
251
|
`source: "reply-fallback"` 表示"AI 没发、插件兜底发的"。
|
|
251
252
|
- **排障**:`msm("weixin-doctor", ["status"|"diag"|"verify"|"guide"])`
|
|
252
253
|
|
|
253
|
-
### 6.3
|
|
254
|
+
### 6.3 受限轨迹(Skiff)
|
|
254
255
|
|
|
255
|
-
|
|
256
|
+
**skiff 就是一条「目录里放了 `skiff.json` 的认知轨迹」** —— 没有别的定义。
|
|
257
|
+
放进去,这条轨迹立刻变成受限形态;删掉它,当场恢复成普通轨迹。
|
|
256
258
|
|
|
257
259
|
```jsonc
|
|
260
|
+
// AGENT_SESSIONS/2026-10-19--qa--问答/ ← 轨迹目录(编码 = 目录名固定位置那一段)
|
|
261
|
+
// ├─ SESSION.md ← 它自己的身体(工作台就是它,不另建)
|
|
262
|
+
// ├─ skiff.json ← 约束面(这个文件的存在,就是"它是 skiff"的判据)
|
|
258
263
|
{
|
|
259
|
-
"
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"tools": ["read", "grep", "glob"], // 它能用哪些平台工具
|
|
265
|
-
"systemPromptFile": "roles/qa.md" // 它的人格与边界(也可以直接内联)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
264
|
+
"model": "provider/model", // 这条轨迹单独用哪个模型
|
|
265
|
+
"msms": ["web-search", "vlm-describe"], // 它能调哪些小工具(MSM)
|
|
266
|
+
"tools": ["read", "grep", "glob"], // 它能用哪些平台工具
|
|
267
|
+
"systemPromptFile": "roles/qa.md", // 它的人格与边界(也可以直接内联 systemPrompt)
|
|
268
|
+
"cognitiveTrajectory": { "session": true, "keeper": true, "rebuild": true } // 缺省即全 true
|
|
269
269
|
}
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
-
- 两份白名单分开配(小工具 /
|
|
272
|
+
- 两份白名单分开配(小工具 / 平台工具),**没列出来的一律从工具清单里移除**(不是"看得见调不动")
|
|
273
|
+
- **`cognitiveTrajectory` 缺省全 `true`**:工作台就是它自己的 `SESSION.md`,关掉等于把它的身体摘了
|
|
273
274
|
- 调试页(3099)可以切工作区、看轨迹;回答用 markdown 渲染,思考过程折叠
|
|
274
|
-
- `container_admin role validate`
|
|
275
|
+
- `container_admin role validate` 校验,`apply` 才真正生效;两者都**实时读盘**,改完下一轮即生效
|
|
276
|
+
- 微信路由也指向它:`routes: [{ "user": "*", "trajectory": "qa" }]`(**user → 轨迹编码**)
|
|
275
277
|
|
|
276
278
|
### 6.4 对外接入(官方 ACP)
|
|
277
279
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.72.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
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 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 +
|
|
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 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/认知轨迹唤醒注册表。skiff = 目录下有 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": {
|
|
7
7
|
"dsh": ">=0.1.7-rc.1"
|
|
8
8
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { dirname, resolve } from "node:path";
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
-
//#region src/ccc.ts
|
|
3
|
+
//#region src/kernel/ccc.ts
|
|
4
4
|
/**
|
|
5
|
-
* ccc.ts — CCC 纯逻辑层(零 DSH 依赖,可独立单测)
|
|
5
|
+
* kernel/ccc.ts — CCC 纯逻辑层(零 DSH 依赖,可独立单测)
|
|
6
6
|
*
|
|
7
7
|
* 职责:CCC 根检测(P1)、git 检测(P2)、serenity.json 配置读取(.opencode 规范位置,.dsh 回退)、
|
|
8
8
|
* 路径守卫(P3 语义)、安全模式黑名单匹配。
|
|
@@ -77,6 +77,20 @@ function readCccName(root) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
|
+
* 🔴 **`SkiffRoleConfig` 已于 2026-10-19 退役**(owner 重做令)。
|
|
81
|
+
*
|
|
82
|
+
* 它的语义整体搬进**轨迹目录下的 `skiff.json`**,类型现在是
|
|
83
|
+
* `role/skiff/profile.ts` 的 `SkiffProfile`(在那里还多了 `code`/`dirName`/`path`
|
|
84
|
+
* 三个与轨迹身份相关的字段)。
|
|
85
|
+
*
|
|
86
|
+
* 退役理由(一句话):skiff 的约束面是**某一条轨迹的属性**,不是容器级配置表里的一个 entry。
|
|
87
|
+
* 放进 `serenity.json` 会让"这条轨迹是不是 skiff"分散在**两个文件**里,而 owner 要的是
|
|
88
|
+
* 一条判据(「目录下拥有 `skiff.json` 的认知轨迹自动是一个 skiff」)。
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ 旧字段里 `kind: temporary|persistent` **整件无对应物**:它区分"有没有工作台",
|
|
91
|
+
* 而新模型里 skiff 恒等于一条轨迹 ⇒ 恒有 SESSION.md ⇒ 该区分没有指称对象(静默忽略)。
|
|
92
|
+
*/
|
|
93
|
+
/**
|
|
80
94
|
* 解析 cheap-worker 配置。
|
|
81
95
|
*
|
|
82
96
|
* 🔴 **键的优先级(2026-10-14,owner 令「配置兼容旧配置 key」)**:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { a as findSerenityRoot } from "./ccc-
|
|
2
|
+
import { a as findSerenityRoot } from "./ccc-Dkoa2oW3.js";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
//#region src/host/access.ts
|
|
5
5
|
/**
|
|
@@ -79,9 +79,9 @@ function hostLlm(ctx) {
|
|
|
79
79
|
return hostInjected(ctx, "llm");
|
|
80
80
|
}
|
|
81
81
|
//#endregion
|
|
82
|
-
//#region src/ccc-roots.ts
|
|
82
|
+
//#region src/kernel/ccc-roots.ts
|
|
83
83
|
/**
|
|
84
|
-
* ccc-roots.ts — CCC 根解析的**唯一判断层**(L1 单根解析 + L2 全量枚举)
|
|
84
|
+
* kernel/ccc-roots.ts — CCC 根解析的**唯一判断层**(L1 单根解析 + L2 全量枚举)
|
|
85
85
|
*
|
|
86
86
|
* 为什么存在(C2「CCC 发现面归一」,S142 2026-09-15 Q5 裁决):
|
|
87
87
|
* 「这个会话/路径属于哪个 CCC 根」(L1)与「本机有哪些 CCC」(L2)这两层判断此前被
|
|
@@ -90,11 +90,11 @@ function hostLlm(ctx) {
|
|
|
90
90
|
* 调用方只问本模块,不再各写各的上溯 + 兜底。
|
|
91
91
|
*
|
|
92
92
|
* 分层(唯一真相源边界,**不要**把下层搬进本模块):
|
|
93
|
-
* - **L0 原语** `findSerenityRoot(cwd)`(`ccc.ts:16`)——**仍是唯一** L0 实现,本模块从它 import。
|
|
93
|
+
* - **L0 原语** `findSerenityRoot(cwd)`(`kernel/ccc.ts:16`)——**仍是唯一** L0 实现,本模块从它 import。
|
|
94
94
|
* - **L1 单根解析** —— 本模块:{@link agentCwdFor}(agent/exec → cwd)与
|
|
95
95
|
* {@link cccRootForCwd}/{@link cccRootForExec}(cwd → 根)。
|
|
96
96
|
* - **L2 全量枚举** —— 本模块:{@link listCccs}。
|
|
97
|
-
* - **L3 CCC 名解析** `readCccName`(`ccc.ts`)——不在本模块,也不与 `skills-discovery` 的
|
|
97
|
+
* - **L3 CCC 名解析** `readCccName`(`kernel/ccc.ts`)——不在本模块,也不与 `skills-discovery` 的
|
|
98
98
|
* 第二份实现合并(那是另一条开放项)。
|
|
99
99
|
*
|
|
100
100
|
* 不新增持久状态(Q5 裁决:**不新增"本机 CCC 名单"**、不扫盘、不写路径假设)——
|
|
@@ -103,7 +103,7 @@ function hostLlm(ctx) {
|
|
|
103
103
|
* ── 🔴 2026-09-25 I2 收敛(约束文档 §2.7 违反清单,L1 违反之一)────────────────
|
|
104
104
|
* **本模块的 `ctx` 参数类型从 `Context` 改为 `unknown`** —— 消除本文件唯一的 `W2` 接触。
|
|
105
105
|
*
|
|
106
|
-
* **判据(与 `live
|
|
106
|
+
* **判据(与 `trajectory/live.ts` 同款,但本文件要多过一道):** 约束文档 §2.7 原记本处
|
|
107
107
|
* `Context` 是"**实测真被用到**"(因它调 `hostService(ctx,…)`/`hostSessions(ctx)`)。
|
|
108
108
|
* 🔴 **该判断把"透传"与"解引用"混为一谈了** —— 实测本文件 `ctx` 的**唯一**去向是
|
|
109
109
|
* **原样透传**给两个 L0 取数口(签名均为 `ctx: unknown`),**零处** `ctx.xxx` 属性访问。
|
|
@@ -131,7 +131,7 @@ function agentCwdFor(execOrAgent) {
|
|
|
131
131
|
return execOrAgent.agent?.session?.header?.cwd ?? execOrAgent.session?.header?.cwd ?? process.cwd();
|
|
132
132
|
}
|
|
133
133
|
/**
|
|
134
|
-
* cwd → CCC 根(L0 原语的唯一调用形态;`ccc.ts` 之外不要再各写一遍)。
|
|
134
|
+
* cwd → CCC 根(L0 原语的唯一调用形态;`kernel/ccc.ts` 之外不要再各写一遍)。
|
|
135
135
|
* @param cwd 起始路径(目录或文件;文件会靠 `dirname` 上溯)
|
|
136
136
|
* @returns CCC 根绝对路径;不在任何 CCC 内 → `null`
|
|
137
137
|
*/
|
|
@@ -140,7 +140,7 @@ function cccRootForCwd(cwd) {
|
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
142
142
|
* 工具/缝入口的根解析(L1 标准形态):`agentCwdFor(...)` 再上溯。
|
|
143
|
-
* @returns CCC 根;解析不到 → `null`(**不**回落假根——"回落进程 cwd 当根"是 `rebuild.ts`
|
|
143
|
+
* @returns CCC 根;解析不到 → `null`(**不**回落假根——"回落进程 cwd 当根"是 `trajectory/rebuild.ts`
|
|
144
144
|
* 独有的另一条语义,见该处注释)
|
|
145
145
|
*/
|
|
146
146
|
function cccRootForExec(execOrAgent) {
|
|
@@ -227,8 +227,14 @@ 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 {
|
|
231
|
-
rolesOf = (root) =>
|
|
230
|
+
const { listSkiffProfiles } = await import("./profile-DyMJ_bfr.js").then((n) => n.i);
|
|
231
|
+
rolesOf = (root) => {
|
|
232
|
+
try {
|
|
233
|
+
return listSkiffProfiles(root).map((p) => p.code);
|
|
234
|
+
} catch {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
};
|
|
232
238
|
}
|
|
233
239
|
return c.roots.map((root) => ({
|
|
234
240
|
root,
|
package/lib/client.js
CHANGED
|
@@ -273,7 +273,7 @@ window.__ModuleLoader__.load({
|
|
|
273
273
|
}
|
|
274
274
|
//#endregion
|
|
275
275
|
//#region src/client/image-fallback-api.ts
|
|
276
|
-
/** 图片落盘接口路径(node half
|
|
276
|
+
/** 图片落盘接口路径(node half surface/http.ts,client 专属 x-serenity-ui 头) */
|
|
277
277
|
const UPLOAD_PATH = "/serenity/image-upload";
|
|
278
278
|
/**
|
|
279
279
|
* 判断是否触发图片落盘补救(纯函数,可单测)。
|
|
@@ -428,7 +428,7 @@ window.__ModuleLoader__.load({
|
|
|
428
428
|
* - uploadFile(file, sessionId) → POST /serenity/file-upload(node half 写 _tmp/files_from_user/)
|
|
429
429
|
* - fileNoteTemplate(paths) → 消息模板(draft 追加,随发送进消息——用户拍板)
|
|
430
430
|
*/
|
|
431
|
-
/** 文件落盘接口路径(node half
|
|
431
|
+
/** 文件落盘接口路径(node half surface/http.ts,client 专属 x-serenity-ui 头) */
|
|
432
432
|
const FILE_UPLOAD_PATH = "/serenity/file-upload";
|
|
433
433
|
/**
|
|
434
434
|
* 从剪贴板收集非图片文件(图片交给 DSH 原生 rail 链路;仅非图片需要落盘)。
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gateway
|
|
2
|
+
* gateway/auth.ts — F1 双端口网关:认证域纯逻辑(v1.22.4 安全加固)
|
|
3
3
|
*
|
|
4
|
-
* 从 gateway.ts 拆分(S142 熵点治理,v1.22.8):认证纯逻辑独立成模块——
|
|
4
|
+
* 从 gateway/server.ts 拆分(S142 熵点治理,v1.22.8):认证纯逻辑独立成模块——
|
|
5
5
|
* 登录验证(scrypt)/ 会话 token(滑动 TTL + 吊销)/ 失败锁定(账号维度指数退避)/
|
|
6
6
|
* CSRF(双提交 + Origin 校验)/ cookie 解析 / 登录页 HTML。
|
|
7
|
-
* gateway.ts 保留 HTTP 装配(startGateway/registerGateway)并从本模块 import。
|
|
7
|
+
* gateway/server.ts 保留 HTTP 装配(startGateway/registerGateway)并从本模块 import。
|
|
8
8
|
*
|
|
9
9
|
* 纯函数设计(可单测):无 socket/无 HTTP 依赖,全部导出可注入测试。
|
|
10
10
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gateway
|
|
2
|
+
* gateway/dsh-auth.ts — 3081 网关对 DSH 主端口 BrowserAuth 的适配(v1.28.2,S142 用户 bug)
|
|
3
3
|
*
|
|
4
4
|
* 背景:DSH 0.1.2-rc.1 新增 BrowserAuth(browser-auth.ts)——主端口所有 /api 请求与
|
|
5
5
|
* index 渲染前必须持有 authority 绑定的签名 cookie(`dsh-auth-<sha256(Host)>`)。
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gateway
|
|
2
|
+
* gateway/proxy.ts — F1 双端口网关:代理辅助纯逻辑(v1.22.1-1.22.3 稳定性修复)
|
|
3
3
|
*
|
|
4
|
-
* 从 gateway.ts 拆分(S142 熵点治理,v1.22.8):
|
|
4
|
+
* 从 gateway/server.ts 拆分(S142 熵点治理,v1.22.8):
|
|
5
5
|
* - crypto.randomUUID polyfill(非安全上下文修复,v1.22.0)
|
|
6
6
|
* - 反代请求头构造(信任栅栏修复:Host + Origin 同步改写 loopback,v1.22.1)
|
|
7
7
|
* - workspace 白名单过滤(响应 items 前缀过滤 + create 校验 + 403 RPC 响应,v1.22)
|
|
8
8
|
* - HTML polyfill 注入(幂等 marker)
|
|
9
|
-
* 纯函数设计(可单测);gateway.ts 装配层 import 使用。
|
|
9
|
+
* 纯函数设计(可单测);gateway/server.ts 装配层 import 使用。
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
12
|
* 经第二端口 http://LAN-IP:3081 访问 = 非安全上下文 → DSH client 的
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gateway.ts — F1 双端口网关(v1.21)+ 安全加固(v1.22.4)——**装配层**
|
|
2
|
+
* gateway/server.ts — F1 双端口网关(v1.21)+ 安全加固(v1.22.4)——**装配层**
|
|
3
3
|
*
|
|
4
4
|
* 需求(S142 用户拍板):dsh web 维持 127.0.0.1 主端口现状不变,**额外监听一个端口**;
|
|
5
5
|
* 额外端口需网页登录(账号+密码),登录后即原生 Web UI 使用;适应任何部署情况。
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
* - S7:cookieSecure 配置项(反代 TLS 时开启;默认关保持明文 HTTP 可用)
|
|
21
21
|
* - S9:登录失败审计日志(时间/IP/账号/原因,console.warn)
|
|
22
22
|
*
|
|
23
|
-
* v1.22.8 熵点治理(S142):认证域纯逻辑拆到 gateway
|
|
24
|
-
* cookie/登录页),代理辅助纯逻辑拆到 gateway
|
|
23
|
+
* v1.22.8 熵点治理(S142):认证域纯逻辑拆到 gateway/auth.ts(会话/锁定/CSRF/Origin/
|
|
24
|
+
* cookie/登录页),代理辅助纯逻辑拆到 gateway/proxy.ts(polyfill/headers/workspace 过滤);
|
|
25
25
|
* 本文件只保留 HTTP 装配(startGateway/registerGateway/readBody)——re-export 两模块
|
|
26
26
|
* 保持既有 import 面兼容(tests/gateway.test.ts 直接 import 两模块导出)。
|
|
27
27
|
*/
|
|
28
28
|
import type { Context } from 'cordis';
|
|
29
|
-
export { RANDOM_UUID_POLYFILL, injectPolyfillHtml, filterWorkspaceList, workspaceAllowed, workspaceDenyResponse, isWorkspaceCreatePath, parseWorkspaceCreateBody, buildProxyHeaders, transformHtmlForProxy, } from './
|
|
30
|
-
export { loginPageHtml, verifyGatewayLogin, SESSION_TTL_MS, issueToken, revokeToken, validateToken, FAIL_LOCK_THRESHOLD, FAIL_LOCK_BASE_MS, FAIL_LOCK_MAX_MS, getFailState, resetFailState, isAccountLocked, recordLoginFailure, accountLockRemaining, newCsrfToken, isCsrfValid, csrfFromRequest, safeEqual, originAllowed, cookieValue, } from './
|
|
29
|
+
export { RANDOM_UUID_POLYFILL, injectPolyfillHtml, filterWorkspaceList, workspaceAllowed, workspaceDenyResponse, isWorkspaceCreatePath, parseWorkspaceCreateBody, buildProxyHeaders, transformHtmlForProxy, } from './proxy.js';
|
|
30
|
+
export { loginPageHtml, verifyGatewayLogin, SESSION_TTL_MS, issueToken, revokeToken, validateToken, FAIL_LOCK_THRESHOLD, FAIL_LOCK_BASE_MS, FAIL_LOCK_MAX_MS, getFailState, resetFailState, isAccountLocked, recordLoginFailure, accountLockRemaining, newCsrfToken, isCsrfValid, csrfFromRequest, safeEqual, originAllowed, cookieValue, } from './auth.js';
|
|
31
31
|
declare module 'cordis' {
|
|
32
32
|
interface Events {
|
|
33
33
|
/** /serenity/config PUT(账号/监听/白名单变化)→ 强制重建 gateway(lastSig=null) */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* guard/filter.ts — 最终输出敏感词检测 + 打回重生成(v1.26.3,S142 用户需求)
|
|
3
3
|
*
|
|
4
4
|
* 认知容器对外提供认知结果时,**支撑宁静号自身的凭据与机制**不应透露给用户。
|
|
5
5
|
* 用户拍板方案(讨论稿定稿):不做工具结果 redaction(那是模型工作内存),
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*
|
|
22
22
|
* 🔴 C4 块 B(2026-09-15)**缺陷修复**:此处原为硬写常量 `['3080','3081','3099','3100']`
|
|
23
23
|
* ——**漏了 3082**(微信主动发送面,**默认开**)⇒ 守卫拦不住 agent 在用户可见输出里提及 3082。
|
|
24
|
-
* 现改为**由集中端口表派生**(`ports.ts` 的 `MECHANISM_PORTS`):五面齐全,且新增面时
|
|
24
|
+
* 现改为**由集中端口表派生**(`kernel/ports.ts` 的 `MECHANISM_PORTS`):五面齐全,且新增面时
|
|
25
25
|
* 自动进表——不再存在"另抄一份词表 → 静默漂移"的面。
|
|
26
26
|
*/
|
|
27
27
|
/** 敏感词分类(v1.26.11:打回消息按类给规避指引——裸词不够,模型要知道词是什么/怎么规避) */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* guard/seam.ts — 输出守卫拦截缝(v1.26.3,S142 用户需求)
|
|
3
3
|
*
|
|
4
4
|
* `agent/turn-stopping`(serial)时:取该 turn 最后 assistant 文本 → 敏感词表检测 →
|
|
5
5
|
* 命中 → `agent.steer(buildRebuke(hits))` 打回重生成(turn 不关闭,模型同轮重答)。
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* 🔴 **2026-10-15 补注(面 E 退役)**:`acp-` 前缀分支**已成孤儿,但本批不动**。
|
|
14
14
|
* 判据(两条,实测):① **该前缀从来没有生产者** —— 历史 `acp-core.ts` 建会话走的是
|
|
15
|
-
* `createSkiffAgent`,其 id = `` `skiff-${role}-${uuid}` ``(`skiff
|
|
15
|
+
* `createSkiffAgent`,其 id = `` `skiff-${role}-${uuid}` ``(`role/skiff/core.ts:269`)⇒ `acp-` **从不出现**;
|
|
16
16
|
* ② 面 E 退役后连"本可能产生它的模块"也不在了。⇒ 该分支是**恒 false 的死判据**。
|
|
17
17
|
* ⚠️ **仍不擅自删**:这是**运行时字符串比较**,删它属**行为变更**(不是纯清理),
|
|
18
18
|
* 依本轨既有先例(`cheap-worker-` 四处置孤儿,同族、同判据)⇒ **登记待裁**(见 S142 §当前状态「观察」)。
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/im/ilink.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* im/ilink.ts — 微信个人号 iLink Bot API 客户端(F4c-3,v1.27.0 实验性)
|
|
6
6
|
*
|
|
7
7
|
* 纯 fetch 客户端(Node ≥ 18 原生 fetch + node:crypto),零第三方依赖——
|
|
8
|
-
* 对齐 openclaw-weixin 插件
|
|
8
|
+
* 对齐 openclaw-weixin 插件 surface/http.ts 语义(协议全解见 docs/weixin-bot-api.md)。
|
|
9
9
|
*
|
|
10
10
|
* 定位:**Mech 传输层**——只做 HTTP 编解码与请求头,不含业务逻辑(路由/会话映射
|
|
11
|
-
* 在
|
|
11
|
+
* 在 im/route.ts / im/bridge.ts)。所有函数可单测(fetch 注入 mock)。
|
|
12
12
|
*
|
|
13
13
|
* 关键协议事实(实证裸调 2026-08-31):
|
|
14
14
|
* - get_bot_qrcode 无需任何凭据即可取码(无 OpenClaw 账号体系/平台审核)
|
|
@@ -17,7 +17,7 @@ import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:
|
|
|
17
17
|
* - getupdates 长轮询 35s hold;游标 get_updates_buf 从响应原样带回续传
|
|
18
18
|
* - sendmessage 必须回带 context_token 关联对话
|
|
19
19
|
*/
|
|
20
|
-
var
|
|
20
|
+
var ilink_exports = /* @__PURE__ */ __exportAll({
|
|
21
21
|
TypingStatus: () => TypingStatus,
|
|
22
22
|
aes128EcbDecrypt: () => aes128EcbDecrypt,
|
|
23
23
|
buildClientVersion: () => buildClientVersion,
|
|
@@ -443,4 +443,4 @@ function markdownToPlainText(text) {
|
|
|
443
443
|
return result.trim();
|
|
444
444
|
}
|
|
445
445
|
//#endregion
|
|
446
|
-
export {
|
|
446
|
+
export { ilink_exports as a, sendTextMessage as c, getUpdates as i, sendTyping as l, downloadMedia as n, markdownToPlainText as o, getConfig as r, sendFileMessage as s, TypingStatus as t, sniffImageExt as u };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* im/accounts.ts — **账号实现派发表**(P2,2026-10-19)
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么是"账号"这一层(owner 定向,逐字)
|
|
5
|
+
*
|
|
6
|
+
* > 「飞书继承可以很方便的加到当前的体系里,**和微信一样,只不过是 account 方实现不同**。
|
|
7
|
+
* > **要整合到统一的配置中**。」
|
|
8
|
+
*
|
|
9
|
+
* 微信与飞书的差别**只在账号侧**:怎么连、怎么收、怎么发。而它们的**身份、路由、会话**
|
|
10
|
+
* 从来就是通道无关的:
|
|
11
|
+
* - 路由表 = `user → 轨迹编码`(不认渠道)
|
|
12
|
+
* - 会话 id = `skiff-weixin-<轨迹编码>`(由轨迹决定,不认渠道)
|
|
13
|
+
* - 一账号一 poller 的归属判据 = `(root, accountId, identity)`(不含渠道概念)
|
|
14
|
+
*
|
|
15
|
+
* ⇒ 抽象点是 `account.channel`,**不是**新造一个平行的"通道接口"。
|
|
16
|
+
* 本文件的全部职责:**channel 字符串 → 那个实现**。
|
|
17
|
+
*
|
|
18
|
+
* ## 这不是新设计(源码里早就留了口)
|
|
19
|
+
*
|
|
20
|
+
* `bridge.ts` 的机器级装配处原本写着:
|
|
21
|
+
* *「只有已实现的渠道能起 poller(**P2 才加第二个实现**)⇒ 未实现的出声但不静默跳过」*
|
|
22
|
+
* 且账号行 `HumanChannelAccountSettings` **本来就有 `channel` 字段**。
|
|
23
|
+
* ⇒ 本文件就是把那个"P2 预留位"填上。
|
|
24
|
+
*
|
|
25
|
+
* ## 为什么用「注册表 + 未命中响亮」而不是 switch
|
|
26
|
+
*
|
|
27
|
+
* switch 会把"支持哪些渠道"写死在装配逻辑里;注册表让**新增一个渠道实现 = 新增一个文件
|
|
28
|
+
* + 一次 register**,装配处一行不改(R3 家族式扩展)。而未命中**必须出声**——
|
|
29
|
+
* 静默跳过会表现成"某个账号莫名其妙不工作",那是本轨反复踩过的一类最难查的故障。
|
|
30
|
+
*/
|
|
31
|
+
/** 一条账号的运行期凭据(各实现自解释;`weixin` 用 token/baseUrl/userId) */
|
|
32
|
+
export interface ImAccountCredential {
|
|
33
|
+
token?: string;
|
|
34
|
+
baseUrl?: string;
|
|
35
|
+
userId?: string;
|
|
36
|
+
/** 飞书等:appId(凭据本体仍在 localstore) */
|
|
37
|
+
appId?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 账号实现的**最小面**(只放装配处真正需要的东西)。
|
|
41
|
+
*
|
|
42
|
+
* 🔴 刻意**不**把"收消息"放进来:接收循环的形状(长轮询 vs WebSocket 回调)
|
|
43
|
+
* 两家根本不同,硬塞进一个签名只会让两边都别扭。装配处需要的是:
|
|
44
|
+
* ① 这个账号该不该起(凭据齐不齐)② 起起来 ③ 停 ④ 健康快照。
|
|
45
|
+
*/
|
|
46
|
+
export interface ImAccountImpl {
|
|
47
|
+
/** 实现名(= `account.channel` 的取值,如 `weixin`) */
|
|
48
|
+
readonly channel: string;
|
|
49
|
+
/**
|
|
50
|
+
* 凭据是否齐全到**可以起**(缺失 ⇒ 装配处跳过并出声,**不静默**)。
|
|
51
|
+
* @param cred 已从 localstore 读好的凭据(实现自己判断哪个字段必需)
|
|
52
|
+
*/
|
|
53
|
+
isReady(cred: ImAccountCredential): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* 凭据不全时**给人看的原因**(装配处的告警文案,可含 CC 特定措辞)。
|
|
56
|
+
* 缺省 ⇒ 装配处用通用文案。存在的理由:微信的文案是「无 token(未绑定)」,
|
|
57
|
+
* 那是有既有判据钉住的措辞(测试与排查手册都按它找),不该被本层统一措辞冲掉。
|
|
58
|
+
*/
|
|
59
|
+
notReadyReason?(cred: ImAccountCredential): string;
|
|
60
|
+
/**
|
|
61
|
+
* 起这条账号的接收循环(fire-and-forget:实现内部自行循环,不得阻塞装配)。
|
|
62
|
+
* @returns 该账号的循环控制对象(装配处用它做停止与快照)
|
|
63
|
+
*/
|
|
64
|
+
startLoop(input: ImAccountLoopInput): ImAccountLoop;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 起循环的输入。
|
|
68
|
+
*
|
|
69
|
+
* ⚠️ `root` 在**机器级模式**下是 `MACHINE_SCOPE` 哨兵(不是一个目录)——
|
|
70
|
+
* 这与既有语义一致(投到哪个 CCC 由**每条消息**的订阅表决定,不由账号决定)。
|
|
71
|
+
*/
|
|
72
|
+
export interface ImAccountLoopInput {
|
|
73
|
+
ctx: unknown;
|
|
74
|
+
root: string;
|
|
75
|
+
accountId: string;
|
|
76
|
+
cred: ImAccountCredential;
|
|
77
|
+
/** 真 = 机器级 poller(目标由订阅表逐条决定) */
|
|
78
|
+
machine: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 循环控制(装配处持有;`weixinBridgeStatus` 读它出健康快照)。
|
|
82
|
+
*
|
|
83
|
+
* 🔴 `lastPollAt` / `lastError` 是**面板读数面**的既有字段,名字不能改
|
|
84
|
+
* (`im-bridge status` 与设置面板都按这个形状读)。
|
|
85
|
+
*/
|
|
86
|
+
export interface ImAccountLoop {
|
|
87
|
+
stopped: boolean;
|
|
88
|
+
lastPollAt: number;
|
|
89
|
+
lastError?: string;
|
|
90
|
+
}
|
|
91
|
+
/** 注册账号实现(`index.ts` 装配时调用;重复 channel 覆盖——测试与热重载友好) */
|
|
92
|
+
export declare function registerImAccountImpl(impl: ImAccountImpl): void;
|
|
93
|
+
/** 取实现(未注册 ⇒ `null`,由调用方**响亮出声**,不要静默跳过) */
|
|
94
|
+
export declare function getImAccountImpl(channel: string): ImAccountImpl | null;
|
|
95
|
+
/** 已注册的实现名(诊断与错误文案用;顺序 = 注册顺序) */
|
|
96
|
+
export declare function imAccountChannels(): string[];
|
|
97
|
+
/** 测试辅助:清空派发表(生产零调用) */
|
|
98
|
+
export declare function __resetImAccountImplsForTest(): void;
|
|
99
|
+
/**
|
|
100
|
+
* 账号行的渠道归一(**单一处**)。
|
|
101
|
+
*
|
|
102
|
+
* 缺省 `weixin`:老配置平移到 `im` 段时不必补这个字段——owner 的"统一配置"
|
|
103
|
+
* 要能直接吃旧数据(与 `route.ts` 的 `normalizeImSection` 保持同一口径)。
|
|
104
|
+
*/
|
|
105
|
+
export declare function normalizeAccountChannel(channel: string | undefined): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* im/bridge.ts — 微信桥装配(F4c-3,v1.27.0 实验性)
|
|
3
3
|
*
|
|
4
4
|
* **CCC 级**:dsh 一个进程含多个 CCC,每个 CCC 独立对接微信桥(S142 用户拍板)——
|
|
5
5
|
* 扫描 live CCC → 每 CCC 读 weixin 配置(enabled + accounts + routes)→
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
*
|
|
19
19
|
* 🔴 **P0-2(2026-09-27,方案 §2.6):起桥仍按 CCC,但"谁轮询"按账号判定** ——
|
|
20
20
|
* 同一个账号(= 同一份凭据)在同一时刻**只允许一个 poller**,判定与归属表在
|
|
21
|
-
* `
|
|
21
|
+
* `im/inbound.ts`(机器级机制;本文件只接线)。这是"微信桥上移 ACC 层"的第一步。
|
|
22
22
|
*
|
|
23
23
|
* 外部面纯净(D9/D11 延续):微信桥会话 = 外部面——session/prompt 走
|
|
24
24
|
* includeCognitiveTrajectory:false(对外不返回轨迹);skiff 角色白名单即授权(G9)。
|
|
25
25
|
*/
|
|
26
26
|
import type { Context } from 'cordis';
|
|
27
|
-
import { type WeixinAccountCredential } from './
|
|
28
|
-
import { type WeixinMessage } from './
|
|
29
|
-
import { type
|
|
27
|
+
import { type WeixinAccountCredential } from './route.js';
|
|
28
|
+
import { type WeixinMessage } from './ilink.js';
|
|
29
|
+
import { type SkiffProfile } from '../role/skiff/profile.js';
|
|
30
30
|
/** 测试辅助:清空 typing_ticket 缓存(生产零调用) */
|
|
31
31
|
export declare function resetWeixinTypingCache(): void;
|
|
32
32
|
/**
|
|
@@ -45,13 +45,20 @@ export declare function manualOutputFallbackNeeded(input: {
|
|
|
45
45
|
answer: string;
|
|
46
46
|
}): boolean;
|
|
47
47
|
/**
|
|
48
|
-
* 路由解析结果:目标根 +
|
|
48
|
+
* 路由解析结果:目标根 + **轨迹编码** + **该轨迹的 skiff 约束面**。
|
|
49
49
|
* `null` 从本函数出去 ⇒ **不投递**(未命中已留痕 / 目标不存在已响亮出声)。
|
|
50
50
|
*/
|
|
51
51
|
interface ResolvedInboundTarget {
|
|
52
52
|
root: string;
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
/** 目标认知轨迹编码(= 新模型里的"身份";旧模型的角色名已退役) */
|
|
54
|
+
trajectory: string;
|
|
55
|
+
/**
|
|
56
|
+
* 该轨迹的 skiff 约束面。
|
|
57
|
+
* ⚠️ **可能为 `null`**:轨迹存在但没有 `skiff.json` ⇒ 它是一条**普通轨迹**,
|
|
58
|
+
* 微信桥照样可以往里投(投进一条普通认知轨迹是合法的),只是不施加 skiff 约束。
|
|
59
|
+
* ⇒ 这与旧模型的关键差别:旧模型里"角色不存在"是硬错,新模型里"没有 skiff.json"不是错。
|
|
60
|
+
*/
|
|
61
|
+
profile: SkiffProfile | null;
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
57
64
|
* 处理单条微信消息:路由 → skiff 会话(固定 id 创建/延续)→ 提问 → 回复回写。
|
|
@@ -107,7 +114,7 @@ export declare function handleMachineIncoming(ctx: Context, accountId: string, c
|
|
|
107
114
|
* 已存在(配置变化热重建)→ 先停旧循环再启动新的。
|
|
108
115
|
*
|
|
109
116
|
* 🔴 **P0-2(2026-09-27,方案 §2.6):一账号一 poller** —— 起循环**之前**先过机器级归属判定
|
|
110
|
-
* (`
|
|
117
|
+
* (`im/inbound.ts`):身份被**别的** CCC 占着的账号**不起循环**,只留一条响亮日志。
|
|
111
118
|
* 身份 = **凭据 token 的哈希**(不是 `accountId`:那是每 CCC 的本地标签,跨 CCC 无意义)。
|
|
112
119
|
* 先 `releasePollersOf(root)` 再重新 claim —— 热重建语义与"归属粘住不漂"的论证见该函数注释。
|
|
113
120
|
*/
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* im
|
|
2
|
+
* im/channels.ts — IM 发送能力层(v1.31.0,S142 用户洞察)
|
|
3
3
|
*
|
|
4
4
|
* 用户原话:「既然微信桥是我们 ACC 提供的,那么 weixin-send 应该是我们 ACC 提供的能力,
|
|
5
5
|
* 当用户配置了微信桥则可用,不配置则不可见」+「设定一个复杂的多层工具,叫 im-bridge,
|
|
6
6
|
* 里面有微信,然后保留后续可能兼容别的 im 的空间」。
|
|
7
7
|
*
|
|
8
8
|
* 分层(E↑):
|
|
9
|
-
* `tools/im
|
|
9
|
+
* `tools/im/channels.ts`(工具面:参数 → 结果渲染)
|
|
10
10
|
* → **本模块**(通道注册表 + 可见性判据 + 动作分发 + 错误码翻译)
|
|
11
|
-
* → 通道实现(`im-
|
|
11
|
+
* → 通道实现(`im/weixin-channel.ts`:协议/凭据/账号/记录)
|
|
12
12
|
*
|
|
13
13
|
* 边界(R↓):
|
|
14
14
|
* - **本模块零宿主依赖**(不 import cordis/agent)——可见性判定与分发都是纯逻辑,可独立单测。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* im/endpoint.ts — 主动发送入口地址的**零依赖叶模块**(v1.30.9,S142)
|
|
3
3
|
*
|
|
4
4
|
* 为什么单独一个模块(R↓):地址的读者是 `msm-ops.buildMsmEnv`(把地址注入 MSM 子进程 env),
|
|
5
5
|
* 写者才是 `weixin-send-api`(监听器启停)。若 msm-ops 直接 import weixin-send-api,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 后果实测:`acc-extras` / `ops` / `skiff-admin` 三个测试套件因解析不到宿主包而**整文件加载失败**
|
|
8
8
|
* (它们只用到 MSM 注册表逻辑)。叶模块把「读」与「写」解耦:读者零依赖,写者自己承担重量。
|
|
9
9
|
*
|
|
10
|
-
* 同款先例:`host/effect.ts`(拆卸登记)、`
|
|
10
|
+
* 同款先例:`host/effect.ts`(拆卸登记)、`role/worker/idle.ts`(等待结算)。
|
|
11
11
|
*/
|
|
12
12
|
/** 设置当前入口地址(weixin-send-api 启停时调用;null = 未启动) */
|
|
13
13
|
export declare function setWeixinSendEndpoint(value: string | null): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* im/guard.ts — 手动输出模式的**机械闸门**(v1.30.16,S142 用户"LLM 不听")
|
|
3
3
|
*
|
|
4
4
|
* 问题(实证):`weixin.autoReplyWithLastMessage: false` 时桥不回发最终文本,输出权归 agent
|
|
5
5
|
* (必须自己调 `im-bridge` 工具,v1.30.x 为 `msm("weixin-send", …)`)。提示词层面已说明,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* im/ilink.ts — 微信个人号 iLink Bot API 客户端(F4c-3,v1.27.0 实验性)
|
|
3
3
|
*
|
|
4
4
|
* 纯 fetch 客户端(Node ≥ 18 原生 fetch + node:crypto),零第三方依赖——
|
|
5
|
-
* 对齐 openclaw-weixin 插件
|
|
5
|
+
* 对齐 openclaw-weixin 插件 surface/http.ts 语义(协议全解见 docs/weixin-bot-api.md)。
|
|
6
6
|
*
|
|
7
7
|
* 定位:**Mech 传输层**——只做 HTTP 编解码与请求头,不含业务逻辑(路由/会话映射
|
|
8
|
-
* 在
|
|
8
|
+
* 在 im/route.ts / im/bridge.ts)。所有函数可单测(fetch 注入 mock)。
|
|
9
9
|
*
|
|
10
10
|
* 关键协议事实(实证裸调 2026-08-31):
|
|
11
11
|
* - get_bot_qrcode 无需任何凭据即可取码(无 OpenClaw 账号体系/平台审核)
|