@shgroup/dsh-serenity-hooks 1.56.9 → 1.57.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 +2 -2
- package/assets/README.md +4 -4
- package/assets/serenity-voyage.html +3 -4
- package/dsh.plugin.json +3 -3
- package/lib/agent-idle.d.ts +2 -2
- package/lib/api.d.ts +0 -1
- package/lib/{ccc-DXky9UZu.js → ccc-B0ZGL9lx.js} +30 -13
- package/lib/{ccc-roots-Bo0HKLlM.js → ccc-roots-BC8mn8iM.js} +3 -3
- package/lib/ccc-roots.d.ts +1 -1
- package/lib/ccc.d.ts +34 -12
- package/lib/cheap-worker-ops.d.ts +49 -0
- package/lib/cheap-worker-preset-inherit.d.ts +35 -0
- package/lib/client.js +11 -99
- package/lib/cro-guide.d.ts +4 -2
- package/lib/git-ops.d.ts +1 -1
- package/lib/host/access.d.ts +2 -2
- package/lib/im-weixin.d.ts +10 -3
- package/lib/index.js +211 -592
- package/lib/invariant.d.ts +1 -1
- package/lib/invariant.js +1 -1
- package/lib/localstore-ops.d.ts +1 -1
- package/lib/{message-source-BExngUPX.js → message-source-BcCRsEOh.js} +3 -3
- package/lib/rebuild.d.ts +1 -1
- package/lib/seams/context.d.ts +2 -2
- package/lib/skiff-core.d.ts +1 -1
- package/lib/{skiff-role-BSg6Qu9k.js → skiff-role-B615L9MV.js} +3 -3
- package/lib/skiff-role.d.ts +2 -2
- package/lib/skills-discovery.d.ts +2 -3
- package/lib/status.d.ts +2 -2
- package/lib/tools/cheap-worker.d.ts +21 -0
- package/lib/tools/skiff-admin.d.ts +2 -2
- package/lib/unattended-ops.d.ts +4 -4
- package/lib/voyage-page.d.ts +3 -4
- package/lib/{weixin-route-BNyH5ghC.js → weixin-route-B9gU1YXz.js} +22 -6
- package/lib/weixin-send-api.d.ts +1 -1
- package/package.json +2 -2
- package/lib/handyman-ops.d.ts +0 -97
- package/lib/handyman-preset-inherit.d.ts +0 -35
- package/lib/tools/handyman.d.ts +0 -24
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ dsh plugin --profile web add link:$(pwd)/hooks/dsh-serenity-hooks
|
|
|
78
78
|
| `praxis` | 按需向 AI 注入四套"做事方法":输出自检(eap)、设计对齐(neat)、认知连续性(cce)、本体工程(ontology) | 需要它把话说清楚 / 先对齐再动手 / 动手前先把领域结构显式化时 |
|
|
79
79
|
| `ontology` | 本体工程**引擎**(可执行者):读取 `.ttl` 本体文件,执行结构校验、生成摘要、检查"声明与实况是否一致"、输出语义审查提示词、输出合规清单,并可写入一份新本体模板 | 需要为某个领域建立或校验本体时。⚠️ 与 `praxis ontology` 是**同一领域的两个面**:前者提供**方法文本**(如何建模),后者**实际执行检查**。路径必须在工作区内 |
|
|
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
83
|
| `container_admin` | 机务舱:管理子角色、管理小工具注册表、查看全部配置 | 定义"子角色"、注册新小工具、修改配置时 |
|
|
84
84
|
| `im-bridge` | 向 IM 联系人发消息(目前为微信):发文本、发文件、查已配置联系人、查通道状态。每次成功发送自动写入工作区的消息记录 | 需要 AI 主动给家人/同事发消息时(见 §6.2)。**条件出现**:仅在工作区配置了微信桥时可见 |
|
|
@@ -386,7 +386,7 @@ pnpm build # 打包(lib/index.js + client.js)
|
|
|
386
386
|
| 跑在 | OpenCode | DeepSeek Harness |
|
|
387
387
|
| 实现 | 独立 | **独立**(不复用源码,但遵循同一套标准) |
|
|
388
388
|
| 系统提示词 | `system.transform` | `systemPrompt.section`,平台无关的部分逐字对齐 |
|
|
389
|
-
| 工具 | msm / container_fs / logbook 等 | container_fs / container_trajectory / dashboard / container_git / msm / praxis / ontology / serenity-skill-creator /
|
|
389
|
+
| 工具 | msm / container_fs / logbook 等 | container_fs / container_trajectory / dashboard / container_git / msm / praxis / ontology / serenity-skill-creator / cheap-worker / localstore / container_admin + 三个条件出现的(`im-bridge` / `acc-diag` / `diagram`) |
|
|
390
390
|
|
|
391
391
|
**同一个工作区可以随时换运行时**:`.serenity` 标记、`.opencode/skills/`、配置、`AGENT_SESSIONS/` 的文件格式都一致;
|
|
392
392
|
差别只在平台层(工具名、注入方式),换过去以后 AI 收到的约束是一样的。
|
package/assets/README.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
│ three.core.min.js + three.module.min.js(构建输入)· build.ts(构建脚本)
|
|
28
28
|
├── serenity-voyage.html ← 与本目录同物(改源后重建,再拷过来)
|
|
29
29
|
├── preview.png · preview-card.png 两种形态的真实渲染帧
|
|
30
|
-
└── provenance/S132-ORIGIN-SESSION.md
|
|
30
|
+
└── provenance/S132-ORIGIN-SESSION.md 赠予方的原始会话记录
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
**重建**:在该目录下 `bun build/build.ts`(脚本自定位,目录挪动不会断),产物写出后
|
|
@@ -35,15 +35,15 @@
|
|
|
35
35
|
|
|
36
36
|
### 来历与内容口径
|
|
37
37
|
|
|
38
|
-
-
|
|
38
|
+
- **来历**:作品由**另一个 Serenity CCC 实例**赠予,属跨容器赠品。
|
|
39
39
|
它的三维建模、运镜与渲染质量是原作者的功劳;本仓库承载的是**内容重播 + 卡片模式**。
|
|
40
40
|
- **内容重播(S142 §12.42/§12.43)**:星空词元已从赠予方的名字换成**宁静号自己的**
|
|
41
41
|
技能 / 公理 / MSM / 本体文件名(102 条,全部有出处、无自造);事实面板六个数
|
|
42
42
|
(仓库 19 / 技能 35 / 工具 84 / 本体 12 / 公理 56 / 会话 190)为实测快照,口径写在
|
|
43
43
|
`app.js` 的注释里。
|
|
44
|
-
-
|
|
44
|
+
- **外来内容已归零**:赠予方的技能名前缀、公理名前缀、铭牌字样等**全部替换**。
|
|
45
45
|
该判据已被 `tests/voyage-page.test.ts` 自动化(**大小写不敏感**——上一轮正因人工
|
|
46
|
-
grep
|
|
46
|
+
grep 用了小写而漏掉全大写的铭牌字样),改动资产若带回外来内容,测试即红。
|
|
47
47
|
|
|
48
48
|
### 版本口径
|
|
49
49
|
|
|
@@ -1098,10 +1098,9 @@ function makeCodeSlabTex(name) {
|
|
|
1098
1098
|
g.x.fillRect(24, 72, W2 - 48, 3);
|
|
1099
1099
|
return mkTex(g.c, true);
|
|
1100
1100
|
}
|
|
1101
|
-
// 🔴
|
|
1102
|
-
//
|
|
1103
|
-
//
|
|
1104
|
-
// (同 §12.25 那次"漏相对导入形态"的教训)。已改为我们自己的容器名,验收改用 `grep -i`。
|
|
1101
|
+
// 🔴 内容扫尾留档:原铭牌词表里有一项是**赠予方的容器名**,属"他们的内容"。
|
|
1102
|
+
// 上一轮残留验收漏过它(grep 用了小写而此处是全大写 ⇒ 判据覆盖面失守)。
|
|
1103
|
+
// 已改为我们自己的容器名,验收改用 `grep -i`(大小写不敏感,判据才成立)。
|
|
1105
1104
|
var SLAB_NAMES = ['SERENITY', '03-K64', 'HOME-SERENITY', '认知星空', '宁静号'];
|
|
1106
1105
|
var slabTexes = SLAB_NAMES.map(makeCodeSlabTex);
|
|
1107
1106
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.57.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
|
-
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/
|
|
5
|
+
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1 与 0.2.0-rc.1(双档,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
7
7
|
"dsh": ">=0.1.7-rc.1"
|
|
8
8
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"container_git",
|
|
15
15
|
"msm",
|
|
16
16
|
"praxis",
|
|
17
|
-
"
|
|
17
|
+
"cheap-worker",
|
|
18
18
|
"localstore",
|
|
19
19
|
"container_admin",
|
|
20
20
|
"im-bridge",
|
package/lib/agent-idle.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* agent-idle.ts — 等待 agent 空闲(含销毁竞速,S142 review F-08)
|
|
3
3
|
*
|
|
4
|
-
* 为什么单独成模块:`askSkiff`(skiff 问答)与 `
|
|
4
|
+
* 为什么单独成模块:`askSkiff`(skiff 问答)与 `cheap-worker`(杂工循环)都要"等这一轮做完",
|
|
5
5
|
* 此前各自复制了一份只监听 `agent/status → idle` 的实现。该实现有一个静默挂死:
|
|
6
6
|
* **agent 先被销毁(会话关闭/进程回收/显式 dispose)就永远不会再发 idle 事件**,
|
|
7
7
|
* 等待方 Promise 永不结算 → 问答/杂工永久卡住(宿主的 disposal 语义:
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* ② `agent/disposed` 且是同一个 agent —— 销毁竞速(宿主 `runtime-types.ts:175`)
|
|
13
13
|
* ③ `session/disposed` 且是同一会话 —— 会话先于 agent 消失(宿主 `core/session/src/index.ts:62`)
|
|
14
14
|
*
|
|
15
|
-
* 无超时:agent 工作多久等多久(
|
|
15
|
+
* 无超时:agent 工作多久等多久(cheap-worker 可永续)。订阅后再查一次 `agent.status`——
|
|
16
16
|
* 宿主 `followup`/`steer` 会同步把 phase 置为 running(`agent.ts:181-201`),
|
|
17
17
|
* 因此此刻读到 idle 只可能是"唤醒已在订阅前收敛",立即结算以避免漏事件挂死。
|
|
18
18
|
*
|
package/lib/api.d.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* 路由:
|
|
5
5
|
* GET /serenity/status ?workspace=<路径> → 状态(root/accVersion/safeModeOn/...)
|
|
6
6
|
* POST /serenity/status { workspace, on } → 切换安全模式(WebUI 专属头)
|
|
7
|
-
* GET /serenity/handymen → handyman 运行状态(v1.24.0:loop → handyman)
|
|
8
7
|
* POST /serenity/image-upload { workspace?, mediaType, data(base64) } → 图片写 _tmp/images_from_user/(client 专属)
|
|
9
8
|
*
|
|
10
9
|
* 经 ctx.webServer(webserver 服务,公开版由 httpServer 改名)注册;客户端与服务器同源,调用无信任围栏问题。
|
|
@@ -77,24 +77,41 @@ function readCccName(root) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
|
-
* 解析
|
|
81
|
-
*
|
|
80
|
+
* 解析 cheap-worker 配置。
|
|
81
|
+
*
|
|
82
|
+
* 🔴 **键的优先级(2026-10-14,owner 令「配置兼容旧配置 key」)**:
|
|
83
|
+
* ① 新键 `cheap-worker`;② **读不到(键缺失或 models 非有效数组)⇒ 回落旧键 `handyman`**。
|
|
84
|
+
* 「读不到」的判据是**同一套**(models 必须是至少含一个非空字符串的数组)—— 这样"新键存在但写错了"
|
|
85
|
+
* 与"没写新键"行为一致,都不会静默走一个空白名单。
|
|
86
|
+
*
|
|
87
|
+
* ⚠️ `maxRounds`/`maxParallel` 已随 background 模式退场,**不再被本函数读取**(旧配置里留着无害)。
|
|
88
|
+
*
|
|
89
|
+
* @returns 白名单 + 缺省模型;两处都取不到 models 时返回 null(工具应报错要求配置)
|
|
82
90
|
*/
|
|
83
|
-
function
|
|
91
|
+
function readCheapWorkerConfig(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
84
92
|
const cfg = loadSerenityConfig(root, paths);
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
const clean = models.map((m) => m.trim()).filter(Boolean);
|
|
93
|
+
const section = pickModelSection(cfg["cheap-worker"]) ?? pickModelSection(cfg.handyman) ?? null;
|
|
94
|
+
if (section === null) return null;
|
|
95
|
+
const clean = section.models.map((m) => m.trim()).filter(Boolean);
|
|
88
96
|
if (clean.length === 0) return null;
|
|
89
|
-
const defaultModel =
|
|
97
|
+
const defaultModel = section.defaultModel?.trim();
|
|
90
98
|
return {
|
|
91
99
|
models: clean,
|
|
92
|
-
defaultModel: defaultModel !== void 0 && clean.includes(defaultModel) ? defaultModel : clean[0]
|
|
93
|
-
maxRounds: cfg.handyman?.maxRounds ?? 100,
|
|
94
|
-
maxParallel: cfg.handyman?.maxParallel ?? 10
|
|
100
|
+
defaultModel: defaultModel !== void 0 && clean.includes(defaultModel) ? defaultModel : clean[0]
|
|
95
101
|
};
|
|
96
102
|
}
|
|
97
103
|
/**
|
|
104
|
+
* 从一段配置里取出可用的模型白名单段;不满足「models = 至少一个非空字符串的数组」⇒ `null`。
|
|
105
|
+
* 抽成函数是为了让**新键与旧键走同一套判据**(不同判据会让"回落"产生两类行为差异)。
|
|
106
|
+
*/
|
|
107
|
+
function pickModelSection(section) {
|
|
108
|
+
if (section === void 0 || section === null) return null;
|
|
109
|
+
const models = section.models;
|
|
110
|
+
if (!Array.isArray(models) || models.length === 0) return null;
|
|
111
|
+
if (models.every((m) => typeof m !== "string" || m.trim() === "")) return null;
|
|
112
|
+
return section;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
98
115
|
* ACC 依赖的 CCC 配置路径(S134 修正:兼容历史——历史在 .opencode,规范位置 = .opencode;
|
|
99
116
|
* .dsh 仅作 dsh 运行时回退,不优先)。
|
|
100
117
|
*/
|
|
@@ -108,7 +125,7 @@ function readUtf8(path) {
|
|
|
108
125
|
}
|
|
109
126
|
/**
|
|
110
127
|
* 已告警过的损坏配置路径(进程级去重:同一坏文件不刷屏,但绝不静默)。
|
|
111
|
-
* S142 review F-07:此前损坏 → 静默返回 `{}`——安全模式黑名单/
|
|
128
|
+
* S142 review F-07:此前损坏 → 静默返回 `{}`——安全模式黑名单/cheap-worker 白名单/skiff 角色
|
|
112
129
|
* 全部**无声失效**(用户以为配了,实际没配)。守卫的输入失败必须响亮。
|
|
113
130
|
*/
|
|
114
131
|
const warnedBrokenConfigs = /* @__PURE__ */ new Set();
|
|
@@ -132,7 +149,7 @@ function loadSerenityConfig(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
132
149
|
} catch (err) {
|
|
133
150
|
if (!warnedBrokenConfigs.has(p)) {
|
|
134
151
|
warnedBrokenConfigs.add(p);
|
|
135
|
-
console.warn(`[serenity-hooks] ✗ CCC 配置解析失败,已按空配置继续(该 CCC 的安全模式黑名单/
|
|
152
|
+
console.warn(`[serenity-hooks] ✗ CCC 配置解析失败,已按空配置继续(该 CCC 的安全模式黑名单/cheap-worker 白名单/skiff 角色等全部失效,请修 JSON): ${p} — ${String(err?.message ?? err)}`);
|
|
136
153
|
}
|
|
137
154
|
return {};
|
|
138
155
|
}
|
|
@@ -239,4 +256,4 @@ function matchBlacklist(relPath, rules) {
|
|
|
239
256
|
return null;
|
|
240
257
|
}
|
|
241
258
|
//#endregion
|
|
242
|
-
export { resolveInside as _, findSerenityRoot as a, matchBlacklist as c, readCccName as d,
|
|
259
|
+
export { resolveInside as _, findSerenityRoot as a, matchBlacklist as c, readCccName as d, readCheapWorkerConfig as f, readUtf8 as g, readTrajectorySkills as h, findGitRoot as i, pathInside as l, readSafeModeHiddenTools as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readExclusiveTools as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { a as hostSessions, i as hostService } from "./access-CNccNtgf.js";
|
|
3
|
-
import { a as findSerenityRoot } from "./ccc-
|
|
3
|
+
import { a as findSerenityRoot } from "./ccc-B0ZGL9lx.js";
|
|
4
4
|
import { basename } from "node:path";
|
|
5
5
|
//#region src/ccc-roots.ts
|
|
6
6
|
/**
|
|
@@ -47,7 +47,7 @@ var ccc_roots_exports = /* @__PURE__ */ __exportAll({
|
|
|
47
47
|
*
|
|
48
48
|
* ⚠️ 无 `exec.agent` 时回落进程 cwd —— "用服务启动目录冒充会话目录"是**既有语义**,
|
|
49
49
|
* 不是本模块的选择(改它会动到 9 个工具的根解析行为)。已知副作用与规避见
|
|
50
|
-
* `tools/
|
|
50
|
+
* `tools/cheap-worker.ts` 的「调用形态检查前移」补偿(`?? process.cwd()` 会把真因
|
|
51
51
|
* "exec.agent 不存在"盖成 `No CCC found`)。
|
|
52
52
|
*/
|
|
53
53
|
function agentCwdFor(execOrAgent) {
|
|
@@ -150,7 +150,7 @@ async function listCccs(ctx, opts = {}) {
|
|
|
150
150
|
if (typeof defaultRoot === "string" && defaultRoot !== "" && !c.roots.includes(defaultRoot)) c.roots.unshift(defaultRoot);
|
|
151
151
|
let rolesOf = null;
|
|
152
152
|
if (opts.withRoles === true) {
|
|
153
|
-
const { readSkiffRoles } = await import("./skiff-role-
|
|
153
|
+
const { readSkiffRoles } = await import("./skiff-role-B615L9MV.js").then((n) => n.u);
|
|
154
154
|
rolesOf = (root) => [...readSkiffRoles(root).keys()];
|
|
155
155
|
}
|
|
156
156
|
return c.roots.map((root) => ({
|
package/lib/ccc-roots.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface ExecOrAgent {
|
|
|
53
53
|
*
|
|
54
54
|
* ⚠️ 无 `exec.agent` 时回落进程 cwd —— "用服务启动目录冒充会话目录"是**既有语义**,
|
|
55
55
|
* 不是本模块的选择(改它会动到 9 个工具的根解析行为)。已知副作用与规避见
|
|
56
|
-
* `tools/
|
|
56
|
+
* `tools/cheap-worker.ts` 的「调用形态检查前移」补偿(`?? process.cwd()` 会把真因
|
|
57
57
|
* "exec.agent 不存在"盖成 `No CCC found`)。
|
|
58
58
|
*/
|
|
59
59
|
export declare function agentCwdFor(execOrAgent: ExecOrAgent): string;
|
package/lib/ccc.d.ts
CHANGED
|
@@ -32,15 +32,22 @@ export declare function resolveInside(root: string, p: string): string;
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function readCccName(root: string): string | null;
|
|
34
34
|
interface SerenityConfig {
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
/** 可用模型白名单(provider/model 列表)——
|
|
35
|
+
/** cheap-worker(杂工)配置:模型白名单(v1.24.0 取代 loop;2026-10-14 取代 handyman) */
|
|
36
|
+
'cheap-worker'?: {
|
|
37
|
+
/** 可用模型白名单(provider/model 列表)——cheap-worker 只能使用其中模型(用户拍板:CCC 配置控制) */
|
|
38
38
|
models?: string[];
|
|
39
39
|
/** 缺省模型(必须 ∈ models) */
|
|
40
40
|
defaultModel?: string;
|
|
41
|
-
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* 🔴 **旧键(历史名)**:2026-10-14 起本插件读 `cheap-worker`,**读不到时回落到这里**
|
|
44
|
+
* (owner 令「配置兼容旧配置 key」)。仍可含 `maxRounds`/`maxParallel` —— 那两个键已随
|
|
45
|
+
* background 模式退场,**不再被读取**,但**留在文件里不会报错**(宽松解析,见 loadSerenityConfig)。
|
|
46
|
+
*/
|
|
47
|
+
handyman?: {
|
|
48
|
+
models?: string[];
|
|
49
|
+
defaultModel?: string;
|
|
42
50
|
maxRounds?: number;
|
|
43
|
-
/** jobs 编排并行上限(缺省 10——便宜模型便宜) */
|
|
44
51
|
maxParallel?: number;
|
|
45
52
|
};
|
|
46
53
|
sessionKeeper?: {
|
|
@@ -91,7 +98,7 @@ interface SerenityConfig {
|
|
|
91
98
|
* 多 CCC 独立(v1.27.4):每 CCC 自己的配置,时钟遍历所有 live+enabled CCC 各自唤起。
|
|
92
99
|
*/
|
|
93
100
|
autopilotTrajectory?: AutopilotTrajectorySettings;
|
|
94
|
-
/** 旧键兼容(正式化前存量 CCC
|
|
101
|
+
/** 旧键兼容(正式化前存量 CCC——新键 autopilotTrajectory 优先,旧键回退) */
|
|
95
102
|
autotrajectory?: AutopilotTrajectorySettings;
|
|
96
103
|
/**
|
|
97
104
|
* trajectory 族配置(D58,S142 2026-09-13):`trajectory` 是一等概念,
|
|
@@ -187,6 +194,15 @@ export interface WeixinSettings {
|
|
|
187
194
|
accounts?: WeixinAccountConfig[];
|
|
188
195
|
/** 消息路由:微信用户 → 该 CCC 的 skiff role(不绑定具体 role——用户自选) */
|
|
189
196
|
routes?: WeixinRouteConfig[];
|
|
197
|
+
/**
|
|
198
|
+
* 用户别名表(v1.56.10):`{ "<别名>": "<localstore 凭据键>" }`。
|
|
199
|
+
*
|
|
200
|
+
* 🔴 **为什么搬到这里**(2026-10-14 隐私核查):别名(家人称呼)**是 CCC 的数据,不是 ACC 的机制**
|
|
201
|
+
* ——按 D23 归属判据「改它要不要发版」:加一个家人别名**不该**要动插件源码并发版。
|
|
202
|
+
* 此前别名硬编码在 ACC 源码里,导致**家人称呼随 npm 包公开发布**。
|
|
203
|
+
* 归位后 ACC 不认识任何具体人名;别名由各 CCC 自配(留空 = 只用裸 iLink 用户 id)。
|
|
204
|
+
*/
|
|
205
|
+
aliases?: Record<string, string>;
|
|
190
206
|
/**
|
|
191
207
|
* 消息记录 hook(v1.27.13,用户"微信桥所有消息记录,支持 hook,CCC 自写持久性保存"):
|
|
192
208
|
* 相对 CCC 根的脚本路径——微信桥每收/发一条消息触发一次(事件 JSON 经 stdin 传入,
|
|
@@ -229,7 +245,7 @@ export interface WeixinSettings {
|
|
|
229
245
|
export interface WeixinAccountConfig {
|
|
230
246
|
/** 本地唯一键(如 wechat-1;localstore 凭据键 weixin.accounts.<accountId>.*) */
|
|
231
247
|
accountId: string;
|
|
232
|
-
/** 展示名(可选;如"
|
|
248
|
+
/** 展示名(可选;如"家庭助手") */
|
|
233
249
|
name?: string;
|
|
234
250
|
/** 该账号轮询开关(缺省 true) */
|
|
235
251
|
enabled?: boolean;
|
|
@@ -277,14 +293,20 @@ export interface SkiffRoleConfig {
|
|
|
277
293
|
systemPromptFile?: string;
|
|
278
294
|
}
|
|
279
295
|
/**
|
|
280
|
-
* 解析
|
|
281
|
-
*
|
|
296
|
+
* 解析 cheap-worker 配置。
|
|
297
|
+
*
|
|
298
|
+
* 🔴 **键的优先级(2026-10-14,owner 令「配置兼容旧配置 key」)**:
|
|
299
|
+
* ① 新键 `cheap-worker`;② **读不到(键缺失或 models 非有效数组)⇒ 回落旧键 `handyman`**。
|
|
300
|
+
* 「读不到」的判据是**同一套**(models 必须是至少含一个非空字符串的数组)—— 这样"新键存在但写错了"
|
|
301
|
+
* 与"没写新键"行为一致,都不会静默走一个空白名单。
|
|
302
|
+
*
|
|
303
|
+
* ⚠️ `maxRounds`/`maxParallel` 已随 background 模式退场,**不再被本函数读取**(旧配置里留着无害)。
|
|
304
|
+
*
|
|
305
|
+
* @returns 白名单 + 缺省模型;两处都取不到 models 时返回 null(工具应报错要求配置)
|
|
282
306
|
*/
|
|
283
|
-
export declare function
|
|
307
|
+
export declare function readCheapWorkerConfig(root: string, paths?: string[]): {
|
|
284
308
|
models: string[];
|
|
285
309
|
defaultModel: string;
|
|
286
|
-
maxRounds: number;
|
|
287
|
-
maxParallel: number;
|
|
288
310
|
} | null;
|
|
289
311
|
/**
|
|
290
312
|
* ACC 依赖的 CCC 配置路径(S134 修正:兼容历史——历史在 .opencode,规范位置 = .opencode;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cheap-worker-ops.ts — cheap-worker(杂工)纯逻辑层(零 DSH 依赖,可独立单测)
|
|
3
|
+
*
|
|
4
|
+
* 🆕 2026-10-14(owner 令「删除后台运行能力」):**进度文件 / stop token / 轮次循环**整族已退场 ——
|
|
5
|
+
* 它们**只**服务于 background 模式。本模块现存 = 前台委派真正需要的三件:
|
|
6
|
+
* 模型串解析 / 白名单校验 / provider 可用性判读(+ 使用指引文本)。
|
|
7
|
+
*/
|
|
8
|
+
/** 解析 model 字符串(provider/model)→ {provider, model};无 / 视为 model-only */
|
|
9
|
+
export declare function splitModel(model: string): {
|
|
10
|
+
provider: string | undefined;
|
|
11
|
+
model: string;
|
|
12
|
+
};
|
|
13
|
+
/** 校验模型在白名单内;不在 → 抛错(用户拍板:只能使用 CCC 配置的模型) */
|
|
14
|
+
export declare function requireWhitelistedModel(model: string, models: string[]): void;
|
|
15
|
+
/**
|
|
16
|
+
* providerAvailabilityError — 判定「**本机有没有该 provider 的 adapter**」,并产出**可执行**的错误文本。
|
|
17
|
+
* 返回 `null` = 全部可用(调用方照常继续)。
|
|
18
|
+
*
|
|
19
|
+
* 🔴 为什么需要它(S142 2026-09-25,owner 在 **Mac** 上实测):`cheap-worker` 的模型白名单写在
|
|
20
|
+
* **CCC**(`.opencode/serenity.json`,**随 git 走**),而 provider adapter 由**每台机器的宿主**
|
|
21
|
+
* 注册(profile 配置 / settings 命名空间)⇒ 同一份白名单在一台机器可用、在另一台必失败。
|
|
22
|
+
* 实测症状:宿主抛 `no adapter registered for provider "<p>"`(`dsh-llm`,code `NO_ADAPTER`),
|
|
23
|
+
* 而**前台路径的 `diagnostic` 是空的** ⇒ 调用方(LLM)拿到 `stopReason:"error"` 却**不知道为什么**,
|
|
24
|
+
* 也就无法修正自己的配置 —— 本条把「沉默的失败」变成「可执行的错误」。
|
|
25
|
+
*
|
|
26
|
+
* 数据来源(宿主 `LlmRuntime`):`listProviders()` = **有 adapter 的路由**;
|
|
27
|
+
* `listConfigurableProviders()` = **已声明但未必激活**的路由(含激活用的 settings 命名空间)。
|
|
28
|
+
*
|
|
29
|
+
* 判据边界:`model` 串里**没有 `/`** 时取不到 provider(沿用 agent 默认路由)⇒ **不判定**(跳过)。
|
|
30
|
+
*/
|
|
31
|
+
export declare function providerAvailabilityError(models: readonly string[], facts: {
|
|
32
|
+
registered: readonly {
|
|
33
|
+
id: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
}[];
|
|
36
|
+
declared?: readonly {
|
|
37
|
+
provider: string;
|
|
38
|
+
displayName?: string;
|
|
39
|
+
settingsNs?: string;
|
|
40
|
+
}[];
|
|
41
|
+
configPath?: string;
|
|
42
|
+
}): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* cheap-worker 规模化使用指引(guide 子命令输出)。
|
|
45
|
+
*
|
|
46
|
+
* 🆕 2026-10-14:**双模式段已整段退场**(owner 令「删除后台运行能力」)。本指引只描述现行机制:
|
|
47
|
+
* 前台委派(单任务)/前台并行扇出(jobs)/白名单与 provider 预检。
|
|
48
|
+
*/
|
|
49
|
+
export declare const CHEAP_WORKER_GUIDE = "# cheap-worker \u2014 Scale-Up Usage Guide (guide)\n\n## What it is\nOne delegation primitive: **foreground spawn**. The host starts child agent(s) through its delegation\nservice, awaits them, and returns their final text. There is no loop, no completion code, no progress\nfile, and no unattended/background run.\n\n| | single | parallel (jobs) |\n|---|---|---|\n| Call | cheap-worker(task, [label]) | cheap-worker(jobs=[{task, label?}, ...]) |\n| Behaviour | one child runs the task once | all children start at once, results keep your order |\n| Cap | \u2014 | **10 (fixed, hardcoded)** |\n| Model | CCC config (single source) | same |\n\n## \u26A0\uFE0F Before using: load eap and design the plan\nBefore calling cheap-worker, load eap (acc-eap skill) and design the \"scale-up cheap-worker plan\" based on the EAP framework.\n\n### 1. Task decomposition (E\u2191 Explicit)\n- Split large tasks into explicit subtasks: each subtask defines goal / input / boundaries (what to do, what not to do) / acceptance criteria\n- Make dependencies explicit: dependent tasks run serially, independent ones can run in parallel via `jobs`\n\n### 2. Prompt design (cheap-worker's task parameter)\n- task must be detailed, fixed, and EAP-compliant: clear goal, drawn boundaries, decidable acceptance criteria\n- the child does NOT share your conversation \u2014 restate everything it needs in task\n- Anti-example \"handle this file\" \u2014 ambiguous; good example \"read <path>, extract all rows of the\u300C\u5173\u952E\u51B3\u7B56\u300Dtable,\n output a JSON array (fields id/conclusion/evidence), do not modify the original file\"\n- Reading/curating or text-writing work (extracting from files, summarizing, writing docs, generating text, etc.):\n the cheap-worker-internal agent is also required to load eap and organize output per the EAP standard\n\n### 3. Model (CCC-configured, not selectable per call)\n- The model is read from .opencode/serenity.json \"cheap-worker.models\" \u2014 **the caller cannot pass one**\n (there is no \"model\" parameter). \"cheap-worker.defaultModel\" names which entry is used; it falls back to models[0].\n- The CCC is expected to point this whitelist at LOW-COST models: cheap-worker is the bulk-execution channel\n- A child's own recursive subagents inherit the child's model automatically (DSH native)\n\n### 3b. \uD83D\uDD34 Provider availability (per-machine \u2014 the whitelist is NOT enough)\n- The whitelist lives in the **CCC** (\"serenity.json\", it **travels with the repository**), but provider\n **adapters are registered per machine by the host** (profile config / settings namespace). An entry that\n works on one machine can fail on another with: \"no adapter registered for provider <p>\".\n- Before spawning, cheap-worker verifies the provider of the resolved model against the adapters registered\n **on this machine**; if it is missing it reports the requested model, where it is configured, **the\n providers that ARE available here**, any declared-but-dormant route (with its settings namespace), and the\n exact fix \u2014 delivered in the result's \"diagnostic\" field (never an opaque failure, never a created child).\n- \u21D2 If you see \"has no adapter registered on this machine\": edit \".opencode/serenity.json\" \"cheap-worker.models\"\n (and \"cheap-worker.defaultModel\") to a provider listed as available, or activate/register the provider on this\n machine. **Do not retry the same call unchanged** \u2014 it fails identically.\n\n### 4. Parallel strategy\n- Independent subtasks: cheap-worker(jobs=[{task,label?},...]) \u21D2 fan-out, **cap 10**, results come back\n **in the order you passed them** (label is only a display name).\n- For programmable pipeline/phase orchestration at scale, use the platform's workflow tool instead.\n\n## Completion criteria\n- The child's run settles; done=true only when stopReason=\"completed\" \u2014 otherwise read \"diagnostic\"\n (and \"output\" for whatever partial text was produced).\n";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cheap-worker-preset-inherit.ts — cheap-worker worker 的 preset 继承 + 工具面收窄(纯函数,可单测)
|
|
3
|
+
*
|
|
4
|
+
* v1.24.0:loop → cheap-worker 重命名。对齐 subagent 先例(applyChildComposition →
|
|
5
|
+
* agentPresets.composeFrom):cheap-worker worker 在创建窗口内 join 发起方 agent 的 standing
|
|
6
|
+
* preset 组合,从而获得 preset 层工具(read/write/edit 等)。
|
|
7
|
+
*
|
|
8
|
+
* **工具面收窄(用户拍板)**:worker 内部**不含 cheap-worker 工具本身**——递归编排归主
|
|
9
|
+
* agent,worker 内部只走 subagent(模型继承 worker,同样低能)。实现:setup 钩子
|
|
10
|
+
* composeFrom 后对子 scope `tools.restrict({ deny: ['cheap-worker'] })`(同 scope 注册的
|
|
11
|
+
* 规则,最近 scope 胜出——worker 看不到 cheap-worker,主 agent 不受影响)。
|
|
12
|
+
*
|
|
13
|
+
* agentPresets 是可选服务(ctx.get 读取):
|
|
14
|
+
* - 无该服务 / 父未 join preset → 返回空(worker 落全局工具层 + deny cheap-worker);
|
|
15
|
+
* - 有且父已 join → 返回 preset id(写入 session meta 供重建)+ setup 钩子
|
|
16
|
+
* (在 agent 未发布前执行 composeFrom + restrict,失败由创建事务回滚)。
|
|
17
|
+
*
|
|
18
|
+
* 仅依赖 cordis 的 Context 类型(type-only,运行时擦除)——保持可被 vitest
|
|
19
|
+
* 直接加载(与 ccc.ts / cheap-worker-ops.ts 同级的纯模块约定)。
|
|
20
|
+
*/
|
|
21
|
+
import type { Context } from 'cordis';
|
|
22
|
+
/** 从父 agent ctx 解析 preset 继承的创建期组合。 */
|
|
23
|
+
interface CheapWorkerPresetInheritance {
|
|
24
|
+
/** 父 agent 的 preset id(写入子 session meta.agentPreset,供持久化重建);无则不写。 */
|
|
25
|
+
readonly agentPreset?: string;
|
|
26
|
+
/** 创建 setup 钩子:子 scope join 父的 standing preset + deny cheap-worker 工具;无 preset 继承时仅 deny。 */
|
|
27
|
+
readonly setup?: (childCtx: Context) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 解析 cheap-worker worker 从父 agent 继承的 preset,并组装创建 setup 钩子。
|
|
31
|
+
* @param parentCtx - 发起 cheap-worker 的 agent 的 scope ctx;无(headless/非 agent 上下文)时为 undefined。
|
|
32
|
+
* @returns 继承结果:meta 用的 agentPreset 与创建 setup 钩子。
|
|
33
|
+
*/
|
|
34
|
+
export declare function cheapWorkerPresetInheritance(parentCtx: Context | undefined): CheapWorkerPresetInheritance;
|
|
35
|
+
export {};
|