@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/lib/invariant.d.ts
CHANGED
|
@@ -58,6 +58,6 @@ export declare function assertManifestProse(manifestPath: string, readmePaths: r
|
|
|
58
58
|
* `review`(C 档,调引擎 `compliance`)/`index`(调 P1 的生成器)/`status` ⇒ **14**。
|
|
59
59
|
* 同规格:**条件可见与否不影响这里列它**(本常量 = "插件**具备**的工具面")。
|
|
60
60
|
*/
|
|
61
|
-
export declare const REGISTERED_TOOLS: readonly ["container_fs", "container_trajectory", "dashboard", "container_git", "msm", "praxis", "ontology", "serenity-skill-creator", "
|
|
61
|
+
export declare const REGISTERED_TOOLS: readonly ["container_fs", "container_trajectory", "dashboard", "container_git", "msm", "praxis", "ontology", "serenity-skill-creator", "cheap-worker", "localstore", "container_admin", "im-bridge", "acc-diag", "diagram"];
|
|
62
62
|
/** 条件出现的工具数(`REGISTERED_TOOLS` 的**末段**;`assertManifestProse` 用) */
|
|
63
63
|
export declare const CONDITIONAL_TOOL_COUNT = 3;
|
package/lib/invariant.js
CHANGED
package/lib/localstore-ops.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* 存储结构(JSON 顶层分节):
|
|
15
15
|
* { "credentials": { "HOME_GITLAB_TOKEN": "xxx" }, "<config节>": { "<key>": "v" } }
|
|
16
16
|
* credentials 为保留节(credential 命名空间,key 大写蛇形);其余节归 config 命名空间
|
|
17
|
-
* (path = section.key,如
|
|
17
|
+
* (path = section.key,如 cheap-worker.models)。
|
|
18
18
|
*/
|
|
19
19
|
import type { JsonValue } from './json.js';
|
|
20
20
|
/** 命名空间:credential(凭据)| config(配置) */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as cccRootForCwd } from "./ccc-roots-
|
|
1
|
+
import { r as cccRootForCwd } from "./ccc-roots-BC8mn8iM.js";
|
|
2
2
|
import { basename, dirname, join } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
//#region src/time.ts
|
|
@@ -650,8 +650,8 @@ const migratedRoots = /* @__PURE__ */ new Set();
|
|
|
650
650
|
* **按 CCC 惰性补迁移**(§0L 修正,S142 2026-09-19)。
|
|
651
651
|
*
|
|
652
652
|
* 🔴 为什么需要它(实测缺陷,R↓):
|
|
653
|
-
* 装载期那次迁移用的是 `process.cwd()` —— 而 dsh **服务进程的 cwd
|
|
654
|
-
* **不是 CCC 根**(实测 `acc-diag`:`进程 cwd:
|
|
653
|
+
* 装载期那次迁移用的是 `process.cwd()` —— 而 dsh **服务进程的 cwd 是用户家目录**,
|
|
654
|
+
* **不是 CCC 根**(实测 `acc-diag`:`进程 cwd: <家目录> | 进程 CCC: (无)`)。
|
|
655
655
|
* ⇒ 迁移读的是一个**不存在的文件**,静默迁移 0 条
|
|
656
656
|
* (症状:发布后 `~/.dsh/storages/serenity_bindings.json` **没出现**)。
|
|
657
657
|
*
|
package/lib/rebuild.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ interface RebuildResult {
|
|
|
78
78
|
* v1.30.13(S142 用户"微信桥 + skiff 会话锚定要准确",诊断 D4):**移除**原 ④「全局最新未完成
|
|
79
79
|
* 会话」(`findLatestActiveSessionMd`)。理由(R↓):④ 是**跨轨迹**猜测——候选①②③全空时,
|
|
80
80
|
* 它会把 rebuild 静默接到 AGENT_SESSIONS 里最新的另一个会话(临时 skiff 会话 persistent=false、
|
|
81
|
-
*
|
|
81
|
+
* 重启后首条消息前、新会话首条消息都命中该形态),而"接错轨迹"比重建失败危险得多
|
|
82
82
|
* (错误轨迹被继续写入 + 正确轨迹静默丢失)。现在全部候选失败 → 返回 null → 调用方
|
|
83
83
|
* 报错引导用户显式 `container_trajectory use`(响亮失败优于静默接错)。
|
|
84
84
|
* 备选:④ 仅在"同 scope 确无绑定"时兜底——同样会接错轨迹,故不采纳。
|
package/lib/seams/context.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { UserMessage } from '@deepseek-ai/dsh-session';
|
|
|
15
15
|
export declare function accIdentityText(root: string, configPaths?: string[], entrySkillMaxChars?: number): string;
|
|
16
16
|
/**
|
|
17
17
|
* ACC 注入消息(S134 去重):**只含简短身份锚点**([ACC] 已激活 + CCC 根 + 约束 + Phase 2)。
|
|
18
|
-
* ⚠️ 原文案含「+
|
|
18
|
+
* ⚠️ 原文案含「+ cheap-worker 模型」,该注入已于 2026-09-16 删除(§0e-G)⇒ 此处同步订正(散文级残留)。
|
|
19
19
|
* 完整身份(ACC 5 块 + CCE + Constraints + EAP + SKILL 全文 + Session 块)由**系统提示词层**
|
|
20
20
|
* (systemPrompt.section,每轮 prompt 装配自动注入,含 subagent)承担——对话消息流/压缩重注入
|
|
21
21
|
* 不再重复注入同一内容(token 双倍浪费,见 S134 注入方案梳理)。
|
|
@@ -26,7 +26,7 @@ export declare function accMessage(root: string, configPaths: string[], entrySki
|
|
|
26
26
|
* 只有"conversation 根会话"才自动恢复最近激活的宁静号会话——
|
|
27
27
|
* - subagent:session header `origin === 'subagent'`(DSH 路由语义,agent-lookup.ts)
|
|
28
28
|
* - 派生会话:`parentSession` 存在(任何子会话)
|
|
29
|
-
* -
|
|
29
|
+
* - cheap-worker 杂工:sessionId 固定 `cheap-worker-` 前缀(tools/cheap-worker.ts 生成)
|
|
30
30
|
* 三者都不恢复(避免把主会话激活注入子上下文,违背 v1.16.2 scope 隔离)。
|
|
31
31
|
*/
|
|
32
32
|
export declare function shouldAutoRestore(agent: Agent): boolean;
|
package/lib/skiff-core.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export declare function skiffSessionSnapshot(): ReadonlyMap<string, {
|
|
|
35
35
|
* 注入 agent 系统提示词(用户对话面不可见)。
|
|
36
36
|
*
|
|
37
37
|
* v1.30.13(S142 诊断 D5):写入纪律**不点名具体工具**。旧文案写死 "with write/edit",
|
|
38
|
-
*
|
|
38
|
+
* 而某角色的工具白名单已移除 write/edit(写能力收归 CCC 的 session-write MSM)
|
|
39
39
|
* → 指令指向它根本没有的工具(注入纪律与能力面矛盾,模型只能猜)。
|
|
40
40
|
* 现文案改为「用本角色被授权的写入通道」——ACC 只声明义务(写进工作台),
|
|
41
41
|
* 具体通道由 CCC 的角色配置与角色提示词决定(ACC/CCC 归属二分;也不把某个 CCC 的
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { _ as resolveInside, g as readUtf8, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
+
import { _ as resolveInside, g as readUtf8, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-B0ZGL9lx.js";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
//#region src/skiff-role.ts
|
|
5
5
|
/**
|
|
@@ -27,7 +27,7 @@ var skiff_role_exports = /* @__PURE__ */ __exportAll({
|
|
|
27
27
|
});
|
|
28
28
|
/** Skiff agent 会话 id 前缀(agents.create 生成;seams 旁路/白名单判定用) */
|
|
29
29
|
const SKIFF_SESSION_PREFIX = "skiff-";
|
|
30
|
-
/** 判定 sessionId 是否为 Skiff 会话(仿
|
|
30
|
+
/** 判定 sessionId 是否为 Skiff 会话(仿 cheap-worker- 前缀排除模式) */
|
|
31
31
|
function isSkiffSessionId(sessionId) {
|
|
32
32
|
return typeof sessionId === "string" && sessionId.startsWith("skiff-");
|
|
33
33
|
}
|
|
@@ -116,7 +116,7 @@ function resolveSkiffKind(role, hasStableId) {
|
|
|
116
116
|
*
|
|
117
117
|
* 为什么是函数而不是字符串(R↓):旧实现 `createSkiffAgent` 在创建时把
|
|
118
118
|
* `resolveRoleSystemPrompt(...)` 的结果**快照**进系统提示词段闭包 → CCC 改了
|
|
119
|
-
*
|
|
119
|
+
* `<role>.md` 必须重启 dsh web 才生效(本轮实证:改完 18KB 提示词,live agent 仍用旧文本)。
|
|
120
120
|
* 现形态返回一个读取函数,由系统提示词段的 `text()` **每轮调用**:
|
|
121
121
|
* - `systemPromptFile`:按 **mtime + size** 缓存重读(命中缓存零 IO;未变不解析)
|
|
122
122
|
* → 改文件即生效,**无需重启**;变更时打一行 info 日志(可观测,便于确认热更生效)
|
package/lib/skiff-role.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { type SkiffRoleConfig } from './ccc.js';
|
|
|
12
12
|
export type { SkiffRoleConfig };
|
|
13
13
|
/** Skiff agent 会话 id 前缀(agents.create 生成;seams 旁路/白名单判定用) */
|
|
14
14
|
export declare const SKIFF_SESSION_PREFIX = "skiff-";
|
|
15
|
-
/** 判定 sessionId 是否为 Skiff 会话(仿
|
|
15
|
+
/** 判定 sessionId 是否为 Skiff 会话(仿 cheap-worker- 前缀排除模式) */
|
|
16
16
|
export declare function isSkiffSessionId(sessionId: string | undefined): boolean;
|
|
17
17
|
/**
|
|
18
18
|
* 读取 CCC 的 Skiff 角色配置(.opencode/serenity.json skiff.roles)。
|
|
@@ -56,7 +56,7 @@ export declare function resolveSkiffKind(role: SkiffRoleConfig | undefined, hasS
|
|
|
56
56
|
*
|
|
57
57
|
* 为什么是函数而不是字符串(R↓):旧实现 `createSkiffAgent` 在创建时把
|
|
58
58
|
* `resolveRoleSystemPrompt(...)` 的结果**快照**进系统提示词段闭包 → CCC 改了
|
|
59
|
-
*
|
|
59
|
+
* `<role>.md` 必须重启 dsh web 才生效(本轮实证:改完 18KB 提示词,live agent 仍用旧文本)。
|
|
60
60
|
* 现形态返回一个读取函数,由系统提示词段的 `text()` **每轮调用**:
|
|
61
61
|
* - `systemPromptFile`:按 **mtime + size** 缓存重读(命中缓存零 IO;未变不解析)
|
|
62
62
|
* → 改文件即生效,**无需重启**;变更时打一行 info 日志(可观测,便于确认热更生效)
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* skills-discovery.ts — CCC 入口 skill 自动发现(纯逻辑,零 DSH 依赖)
|
|
3
3
|
*
|
|
4
4
|
* 发现全部入口技能(原文全量,不截断)—— **现行三个来源**(原为四个,**来源 3 已于 2026-09-21 删除**):
|
|
5
|
-
* 0. **`.serenity` 记号文件内容 = 顶层入口 skill 名**(CCC
|
|
6
|
-
* tiangong-serenity 的 .serenity 内容为 `tg-serenity`)—— 最高优先
|
|
5
|
+
* 0. **`.serenity` 记号文件内容 = 顶层入口 skill 名**(CCC 记号文件的权威语义)—— 最高优先
|
|
7
6
|
* 1. `.dsh/entry-skill` 指针文件(内容 = skill 名)—— 兼容旧约定
|
|
8
7
|
* 2. `.opencode/skills/*-serenity/SKILL.md` —— 自动扫描该 CCC 的顶层入口
|
|
9
|
-
*
|
|
8
|
+
* (命名模式 `*-serenity`)
|
|
10
9
|
* ~~3. `.dsh/skills/*-serenity/SKILL.md`(扫 ACC 装进 CCC 的技能副本)~~ —— 🔴 **已删**:
|
|
11
10
|
* owner 2026-09-21 令「连模板和安装命令一起删」(B 案)。**判据、实测证据与日期逐字记在
|
|
12
11
|
* `findEntrySkills()` 体内那一段注释里**——本头部只留结论,**不复述判据**(避免两处会漂的真相源)。
|
package/lib/status.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* status.ts — 状态与安全模式操作(纯逻辑,零 DSH 依赖,可独立单测)
|
|
3
3
|
*
|
|
4
|
-
* WebUI 停靠栏的数据源:ACC 版本 / CCC 根 / safe-mode 状态 / 黑名单 / keeper 阈值 /
|
|
4
|
+
* WebUI 停靠栏的数据源:ACC 版本 / CCC 根 / safe-mode 状态 / 黑名单 / keeper 阈值 / cheap-worker 模型。
|
|
5
5
|
* setSafeMode 直接读写 .serenity-safe-on 标记(守卫实时读取,写即生效)。
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
@@ -49,7 +49,7 @@ interface SerenityStatus {
|
|
|
49
49
|
message?: string;
|
|
50
50
|
}[];
|
|
51
51
|
threshold: number | null;
|
|
52
|
-
|
|
52
|
+
cheapWorkerModel: string | null;
|
|
53
53
|
restrict: {
|
|
54
54
|
lastKey: string | null;
|
|
55
55
|
lastAttemptAt: string | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cheap-worker.ts — cheap-worker(杂工)真实 DSH 工具
|
|
3
|
+
*
|
|
4
|
+
* v1.24.0:loop(牛马)→ handyman(杂工);2026-10-14:handyman → cheap-worker。
|
|
5
|
+
* 🆕 2026-10-14(owner 令「删除后台运行能力」):**background 模式整条退场** ——
|
|
6
|
+
* 轮次循环 / stop-token 完成码 / 进度文件 / 重启计数 / maxRounds/maxParallel 全部删除。
|
|
7
|
+
* 现存只有**前台委派**一种形态:宿主起子 agent、等它跑完、取回最终文本。
|
|
8
|
+
*
|
|
9
|
+
* 机制:走宿主**委派正门** `ctx.subagents.start('spawn', …)`(深度上限、子 agent 所有权、
|
|
10
|
+
* `subagent/start|end` 事件、`dispose()` 收尾全部沿用宿主实现),模型经 `agentOptions` 注入。
|
|
11
|
+
* 前置:`exec.agent` 必须存在(前台委派要有发起方)。
|
|
12
|
+
*
|
|
13
|
+
* 🔴 **模型不可由调用方指定**(owner 令):**没有 `model` 参数**,一律读 CCC 配置
|
|
14
|
+
* `cheap-worker.models` / `cheap-worker.defaultModel`(单一真相源)。
|
|
15
|
+
*
|
|
16
|
+
* 递归防护:子 agent 的工具面 deny `cheap-worker` 本身(编排归主 agent,防无限嵌套)。
|
|
17
|
+
*/
|
|
18
|
+
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
19
|
+
import type { Context } from 'cordis';
|
|
20
|
+
/** 创建 cheap-worker 工具(闭包捕获插件 ctx → 可访问 ctx.subagents) */
|
|
21
|
+
export declare function createCheapWorkerTool(ctx: Context): ToolDefinition;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { JsonValue } from '../json.js';
|
|
11
11
|
/** Skiff 定义教程(核心):概念 / schema / 认知 MSM 写法 / 双白名单 / 轨迹纪律 / 示例角色 */
|
|
12
|
-
export declare const SKIFF_GUIDE = "\u2550\u2550\u2550 Skiff Definition Guide (F4, experimental) \u2550\u2550\u2550\n\nSkiff = \u5B81\u9759\u53F7\u653E\u51FA\u7684\u72EC\u7ACB\u5C0F\u8247\u2014\u2014\u5B8C\u6574 trajectory\uFF08\u5728\u5B81\u9759\u53F7\u5185\u5168\u77E5\u5168\u80FD\uFF09\u7684**\u4EFB\u610F\u5B50\u96C6\u89D2\u8272**\u3002\n\u7531\u672C CCC\uFF08.opencode/serenity.json skiff.roles\uFF09\u5B9A\u4E49\uFF1A\u80FD\u529B\u9762\uFF08\u53CC\u767D\u540D\u5355\uFF09+ \u8F68\u8FF9\u7EAA\u5F8B\u5B50\u96C6 + \u7CFB\u7EDF\u63D0\u793A\u8BCD\u3002\ndsp \u53EA\u63D0\u4F9B\u673A\u5236\uFF08\u53CC\u767D\u540D\u5355\u5F3A\u5236 + \u57FA\u7840\u63D0\u793A\u8BCD + \u8C03\u8BD5\u95EE\u7B54\u9875\uFF09\uFF1B\u89D2\u8272\u5185\u5BB9\u5B8C\u5168\u7531 CCC \u53D1\u6325\u3002\n\n\u2500\u2500 \u89D2\u8272\u914D\u7F6E schema \u2500\u2500\n { \"skiff\": { \"roles\": {\n \"<role-name>\": {\n \"model\": \"provider/model\", // \u89D2\u8272\u6A21\u578B\uFF08CCC \u76F4\u63A5\u6307\u5B9A\uFF0C\u65E0\u767D\u540D\u5355\u6821\u9A8C\uFF1B\u7F3A\u7701\u56DE\u9000
|
|
13
|
-
/** 校验当前 CCC 的 skiff 配置:roles schema 合法 / msms 均已注册 / model ∈
|
|
12
|
+
export declare const SKIFF_GUIDE = "\u2550\u2550\u2550 Skiff Definition Guide (F4, experimental) \u2550\u2550\u2550\n\nSkiff = \u5B81\u9759\u53F7\u653E\u51FA\u7684\u72EC\u7ACB\u5C0F\u8247\u2014\u2014\u5B8C\u6574 trajectory\uFF08\u5728\u5B81\u9759\u53F7\u5185\u5168\u77E5\u5168\u80FD\uFF09\u7684**\u4EFB\u610F\u5B50\u96C6\u89D2\u8272**\u3002\n\u7531\u672C CCC\uFF08.opencode/serenity.json skiff.roles\uFF09\u5B9A\u4E49\uFF1A\u80FD\u529B\u9762\uFF08\u53CC\u767D\u540D\u5355\uFF09+ \u8F68\u8FF9\u7EAA\u5F8B\u5B50\u96C6 + \u7CFB\u7EDF\u63D0\u793A\u8BCD\u3002\ndsp \u53EA\u63D0\u4F9B\u673A\u5236\uFF08\u53CC\u767D\u540D\u5355\u5F3A\u5236 + \u57FA\u7840\u63D0\u793A\u8BCD + \u8C03\u8BD5\u95EE\u7B54\u9875\uFF09\uFF1B\u89D2\u8272\u5185\u5BB9\u5B8C\u5168\u7531 CCC \u53D1\u6325\u3002\n\n\u2500\u2500 \u89D2\u8272\u914D\u7F6E schema \u2500\u2500\n { \"skiff\": { \"roles\": {\n \"<role-name>\": {\n \"model\": \"provider/model\", // \u89D2\u8272\u6A21\u578B\uFF08CCC \u76F4\u63A5\u6307\u5B9A\uFF0C\u65E0\u767D\u540D\u5355\u6821\u9A8C\uFF1B\u7F3A\u7701\u56DE\u9000 cheap-worker.defaultModel\uFF09\n \"msms\": [\"msm-a\", \"msm-b\"], // MSM \u767D\u540D\u5355\uFF08\u72EC\u7ACB\uFF09\uFF1Amsm \u5DE5\u5177\u53EA\u80FD\u6267\u884C\u8FD9\u4E9B\uFF1Bregister/deregister \u5FC5\u62D2\n \"tools\": [\"read\",\"grep\",\"glob\",...], // \u975E MSM \u5DE5\u5177\u767D\u540D\u5355\uFF08\u72EC\u7ACB\uFF09\uFF1A\u767D\u540D\u5355\u5916\u5DE5\u5177\u4E00\u5F8B\u4E0D\u53EF\u7528\uFF08guard \u5F3A\u5236\uFF09\n \"trajectory\": { \"session\": false, \"keeper\": false, \"rebuild\": false }, // \u8F68\u8FF9\u7EAA\u5F8B\u5B50\u96C6\uFF08\u7F3A\u7701\u5168\u5173 = \u5B8C\u5168\u72EC\u7ACB\uFF09\n // \u89D2\u8272\u4EBA\u683C/\u8BA4\u77E5\u8FB9\u754C/\u98CE\u683C\uFF08CCC \u5B8C\u6574\u5B9A\u4E49\uFF1Bdsp \u53EA\u7ED9\u57FA\u7840\u63D0\u793A\u8BCD\uFF09\n \"systemPromptFile\": \".opencode/skiff/<role>.md\" // \u2605 \u63A8\u8350\uFF1A\u5F15\u7528 md \u6587\u4EF6\uFF08\u76F8\u5BF9 CCC \u6839\uFF1B\u8D85\u957F\u63D0\u793A\u8BCD\u5728 JSON \u5185\u5D4C\u4E0D\u53EF\u8BFB\uFF09\n // \"systemPrompt\": \"...\" // \u517C\u5BB9\uFF1A\u5185\u5D4C\u5B57\u7B26\u4E32\u4ECD\u53EF\u7528\uFF1B\u4E24\u8005\u90FD\u8BBE\u65F6 systemPromptFile \u4F18\u5148\n }\n } } }\n\n\u2500\u2500 \u53CC\u767D\u540D\u5355\u8BED\u4E49\uFF08\u5168\u6309\u767D\u540D\u5355\u66B4\u9732\uFF0C\u767D\u540D\u5355\u5916\u5168\u9690\u85CF\uFF09\u2500\u2500\n - tools \u7A7A + msms \u975E\u7A7A = \u7EAF MSM \u89D2\u8272\uFF08\u8BA4\u77E5\u95EE\u7B54\u5178\u578B\u5F62\u6001\uFF09\n - msms \u975E\u7A7A \u2192 msm \u5DE5\u5177\u81EA\u52A8\u53EF\u7528\uFF08MSM \u901A\u9053\uFF09\n - \u767D\u540D\u5355\u5916\u5DE5\u5177\u5373\u4F7F DSH \u672A\u6765\u65B0\u589E\u4E5F\u81EA\u52A8\u88AB\u6321\uFF08guard \u6309\u89D2\u8272\u5224\u5B9A\uFF0C\u4E0D\u679A\u4E3E\u5DE5\u5177\u540D\u2014\u2014\u5B8C\u5907\u6027\uFF09\n\n\u2500\u2500 \u8BA4\u77E5 MSM \u5199\u6CD5\uFF08\u8BFB\u77E5\u8BC6 / \u64CD\u4F5C\u80FD\u529B\uFF09\u2500\u2500\n - \u8BFB\u77E5\u8BC6\uFF1A\u811A\u672C\u8BFB CCC \u5185\u6587\u4EF6\uFF08SERENITY_ROOT env \u6CE8\u5165\uFF09\u2192 \u8F93\u51FA\u7B54\u6848/\u6458\u8981\uFF08\u5982 cognitive-qa\uFF09\n - \u64CD\u4F5C\u80FD\u529B\uFF1A\u811A\u672C\u8C03\u7528\u65E2\u6709\u5BB6\u5EAD\u5DE5\u5177/\u670D\u52A1\uFF08SSH/API/\u6587\u4EF6\u64CD\u4F5C\uFF09\u2014\u2014\u89D2\u8272\u80FD\u529B\u4E0A\u9650 = \u767D\u540D\u5355 MSM \u7684\u5B9E\u9645\u884C\u4E3A\n - \u4F8B\uFF1Acognitive-qa\uFF08\u8BFB docs/references \u56DE\u7B54\uFF09\u3001review-scan\uFF08\u8BFB\u4EE3\u7801\u51FA\u5BA1\u67E5\u610F\u89C1\uFF09\u3001review-fix\uFF08\u5199\u4FEE\u590D\uFF09\n - \u6CE8\u610F\uFF1AMSM \u5728\u811A\u672C\u5C42\u6267\u884C\uFF08bun\uFF09\uFF0C\u6587\u4EF6\u8BBF\u95EE\u4E0D\u53D7 agent \u5DE5\u5177\u9762\u7EA6\u675F\u2014\u2014\"\u53EA\u8BFB\"\u8BED\u4E49\u9760 CCC \u81EA\u5199 MSM \u81EA\u89C9\n\n\u2500\u2500 \u8F68\u8FF9\u7EAA\u5F8B\u5B50\u96C6\uFF08trajectory\uFF09\u2500\u2500\n - session/keeper/rebuild \u9ED8\u8BA4 false\uFF1ASkiff \u5B8C\u5168\u72EC\u7ACB\uFF08\u4E0D\u5EFA SESSION.md\u3001\u65E0 keeper \u63D0\u9192\u3001\u65E0 rebuild \u538B\u529B\u68C0\u6D4B\uFF09\n - \u5F00\u542F\u67D0\u9879 = \u8BE5\u89D2\u8272\u53C2\u4E0E\u5BF9\u5E94\u8F68\u8FF9\u673A\u5236\uFF08\u5982 keeper=true \u8BA1\u5206\u63D0\u9192\u6309\u89D2\u8272\u751F\u6548\uFF09\n\n\u2500\u2500 \u793A\u4F8B\u89D2\u8272 \u2500\u2500\n qa-readonly: { \"msms\": [\"cognitive-qa\"], \"tools\": [], \"trajectory\": {} }\n \u2192 \u8BA4\u77E5\u95EE\u7B54\uFF08\u4EC5 MSM \u901A\u9053\uFF0C\u65E0\u76F4\u63A5\u5DE5\u5177\uFF0C\u5B8C\u5168\u72EC\u7ACB\uFF09\n code-review: { \"msms\": [\"review-scan\",\"review-fix\"], \"tools\": [\"read\",\"grep\",\"glob\",\"write\",\"edit\"], \"trajectory\": { \"keeper\": true } }\n \u2192 \u6709\u64CD\u4F5C\u80FD\u529B\uFF08\u53EF\u5199\u4FEE\u590D\uFF09\uFF0C\u53C2\u4E0E keeper \u8F68\u8FF9\u673A\u5236\n\n\u2500\u2500 \u8FD0\u884C \u2500\u2500\n \u8C03\u8BD5\uFF1A\u8BBE\u7F6E\u9762\u677F\u300CSerenity\u300D\u9875 Skiff \u533A\u5757\u5F00\u542F \u2192 http://127.0.0.1:<debugPort> \u95EE\u7B54\u9875\u5B9E\u6D4B\n ACP \u7A0B\u5E8F\u5316\u9762\uFF08v1.26.0\uFF09\uFF1A\u8BBE\u7F6E\u9762\u677F\u300CACP\u300D\u533A\u5757\u5F00\u542F \u2192 HTTP JSON-RPC \u7AEF\u70B9\uFF08\u9ED8\u8BA4 3100\uFF09\u2014\u2014\n POST / {jsonrpc, id, method, params}\n session/new {ccc, role, sessionId?} \u2192 \u521B\u5EFA/\u5EF6\u7EED\u4F1A\u8BDD\uFF08sessionId \u53EF\u9009=\u8FDB\u7A0B\u5185\u8FFD\u95EE\uFF09\n session/prompt {sessionId, question} \u2192 {answer, trajectory}\n session/cancel / session/close / session/list / request_permission\uFF08\u6052 allow\uFF09\n \u542F\u505C = \u4EBA\u5DE5\uFF08\u8BBE\u7F6E\u9762\u677F\u5F00\u5173\uFF0C\u4E0D\u968F\u63D2\u4EF6\u52A0\u8F7D\u81EA\u52A8\u542F\u52A8\uFF09\uFF1B\u672A\u5F00\u542F\u96F6\u8D44\u6E90\u5360\u7528\n \u751F\u6548\u673A\u5236\uFF1A\u89D2\u8272\u914D\u7F6E**\u5B9E\u65F6\u8BFB\u53D6**\uFF08\u6539 .opencode/serenity.json \u2192 \u5237\u65B0\u8C03\u8BD5\u9875\u5373\u751F\u6548\uFF09\uFF1B\n \u6539\u914D\u7F6E\u540E\u7528 container_admin role apply \u505A\u663E\u5F0F\u6821\u9A8C + \u5E94\u7528\u786E\u8BA4\uFF08\u7ED1\u5B9A CCC + \u89D2\u8272\u6E05\u5355\uFF09\n \u4F1A\u8BDD\u8FFD\u95EE\uFF08v1.25.10\uFF09\uFF1A\u8C03\u8BD5\u9875\u6301\u6709\u5F53\u524D\u4F1A\u8BDD\uFF0C\u8FFD\u95EE\u81EA\u52A8\u7EED\u63A5\uFF08\u540C\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u5EF6\u7EED\uFF09\uFF1B\n \u300C\u65B0\u5BF9\u8BDD\u300D\u6309\u94AE\u5F00\u65B0\u4F1A\u8BDD\uFF1B\u8FDB\u7A0B\u91CD\u542F\u540E\u65E7\u4F1A\u8BDD\u4E0D\u53EF\u7EED\uFF08WebUI \u4ECD\u53EF\u89C1\u5386\u53F2\uFF09\n \u6CE8\u610F\uFF1Askill \u52A0\u8F7D\u5BF9 skiff **\u6052\u53EF\u7528**\uFF08\u4E0D\u8BBE\u767D\u540D\u5355\u2014\u2014\u8BFB\u77E5\u8BC6\u9762\uFF0C\u65E0\u5199\u80FD\u529B\uFF09";
|
|
13
|
+
/** 校验当前 CCC 的 skiff 配置:roles schema 合法 / msms 均已注册 / model ∈ cheap-worker.models / systemPrompt 非空 */
|
|
14
14
|
export declare function validateSkiffConfig(root: string): JsonValue;
|
|
15
15
|
/** 列出当前 CCC 已定义角色(名 / 模型 / msms / tools / 轨迹纪律摘要) */
|
|
16
16
|
export declare function listSkiffRoles(root: string): JsonValue;
|
package/lib/unattended-ops.d.ts
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* 注入文本**自带**"请回复 ABCD" ⇒ 模型**复述指令**即**假完成**(高概率,不是小概率)
|
|
46
46
|
* ⇒ 用**每轮新生成**的 nonce + **包装 + 独占一行**(`[[UNATTENDED-DONE:<hex>]]`),
|
|
47
47
|
* 明写「**旧码作废**」,校验**只认当前轮**(防上下文里的旧码复活)。
|
|
48
|
-
* 先例 = `
|
|
48
|
+
* 先例 = `cheap-worker` background 的 `SERENITY_CHEAP_WORKER_DONE_<16hex>`(同族实现)。
|
|
49
49
|
*/
|
|
50
50
|
/** 完成码包装前缀(**独占一行**才有意义;见文件头"禁用 4 位字母码") */
|
|
51
51
|
export declare const UNATTENDED_DONE_PREFIX = "[[UNATTENDED-DONE:";
|
|
@@ -55,7 +55,7 @@ export declare const UNATTENDED_DONE_SUFFIX = "]]";
|
|
|
55
55
|
export declare const UNATTENDED_PROXY_MARK = "[[UNATTENDED-PROXY v1]]";
|
|
56
56
|
/** 🔴 合成消息**固定身份句**(owner 已裁:「形如用户消息,但首句自报身份与权限边界」) */
|
|
57
57
|
export declare const UNATTENDED_IDENTITY_LINE = "\u65E0\u4EBA\u503C\u5B88\u4EE3\u7406 \u00B7 \u975E\u672C\u4EBA";
|
|
58
|
-
/** nonce 字节数 ⇒ 16 个 hex 字符(与
|
|
58
|
+
/** nonce 字节数 ⇒ 16 个 hex 字符(与 cheap-worker 同量级;够长到不可能被复述猜中) */
|
|
59
59
|
export declare const UNATTENDED_NONCE_BYTES = 8;
|
|
60
60
|
/** 生成一枚新 nonce(**每轮一枚**;旧码作废由调用方在文本里声明) */
|
|
61
61
|
export declare function newNonce(): string;
|
|
@@ -68,7 +68,7 @@ export declare function doneToken(nonce: string): string;
|
|
|
68
68
|
export declare function parseDoneNonces(text: string): string[];
|
|
69
69
|
/** 本轮收尾文本里是否出现过**当前轮**那枚码(唯一合法收束判据) */
|
|
70
70
|
export declare function hasDoneToken(text: string, nonce: string): boolean;
|
|
71
|
-
/** 单个会话在一个"代理段"内的**轮次上限**(设计建议 10–20;取 15——远小于
|
|
71
|
+
/** 单个会话在一个"代理段"内的**轮次上限**(设计建议 10–20;取 15——远小于 cheap-worker 的 100) */
|
|
72
72
|
export declare const UNATTENDED_MAX_ROUNDS = 15;
|
|
73
73
|
/** 一个"代理段"的**墙钟上限**(自第一次注入起算;跨轮累计,防"轮次没到但烧了一天") */
|
|
74
74
|
export declare const UNATTENDED_MAX_WALL_MS: number;
|
|
@@ -98,7 +98,7 @@ export declare function claimsUserNotification(text: string): boolean;
|
|
|
98
98
|
/**
|
|
99
99
|
* **不许开**的会话(设计 §3 的"建议写死",本版**确实写死**):
|
|
100
100
|
* `skiff-`(F4 问答 / **微信桥会话也是这个前缀** —— 桥那侧的"用户"是**家人**,
|
|
101
|
-
* 代理替家人说话 = **冒充**)| `acp-`(对外程序化面)| `
|
|
101
|
+
* 代理替家人说话 = **冒充**)| `acp-`(对外程序化面)| `cheap-worker-`(自主 worker,已有自己的完成码循环)。
|
|
102
102
|
*
|
|
103
103
|
* ⚠️ **`rebuild-` 不在排除面**(与 `output-guard-seam.ts` 的取向**刻意不同**):重建会话是
|
|
104
104
|
* **同一轨迹的载体**(内部维护面),不是对外面;把它排除会让"重建后就没人推了"。
|
package/lib/voyage-page.d.ts
CHANGED
|
@@ -16,10 +16,9 @@
|
|
|
16
16
|
* ③ 独立路由 ⇒ **惰性加载**(只在卡片展开时请求一次)+ 可长缓存,关闭 iframe 即卸载,
|
|
17
17
|
* WebGL 上下文随之释放。
|
|
18
18
|
*
|
|
19
|
-
* 来历(R
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* 与实测容器规模数字。
|
|
19
|
+
* 来历(R↓):作品由**另一个 Serenity CCC 实例**赠予;**本仓库只携带
|
|
20
|
+
* 构建产物**,源与构建脚本住在该实例侧(详见 `assets/README.md`)。内容已重播为宁静号自己的
|
|
21
|
+
* 技能/公理/MSM/本体词元与实测容器规模数字。
|
|
23
22
|
*/
|
|
24
23
|
import type { Context } from 'cordis';
|
|
25
24
|
export declare const VOYAGE_PATH = "/serenity/voyage";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-
|
|
2
|
+
import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-B0ZGL9lx.js";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -20,7 +20,7 @@ import { createHash } from "node:crypto";
|
|
|
20
20
|
* 存储结构(JSON 顶层分节):
|
|
21
21
|
* { "credentials": { "HOME_GITLAB_TOKEN": "xxx" }, "<config节>": { "<key>": "v" } }
|
|
22
22
|
* credentials 为保留节(credential 命名空间,key 大写蛇形);其余节归 config 命名空间
|
|
23
|
-
* (path = section.key,如
|
|
23
|
+
* (path = section.key,如 cheap-worker.models)。
|
|
24
24
|
*/
|
|
25
25
|
const LOCALSTORE_SCOPES = ["credential", "config"];
|
|
26
26
|
/** 存储文件名(CCC 根根目录) */
|
|
@@ -113,7 +113,7 @@ function assertCredentialKey(key) {
|
|
|
113
113
|
/** 校验 config 路径(节.key);不合法抛错 */
|
|
114
114
|
function assertConfigPath(path) {
|
|
115
115
|
const idx = path.indexOf(".");
|
|
116
|
-
if (idx <= 0 || idx === path.length - 1) throw new Error(`config path "${path}" must be section.key (e.g.
|
|
116
|
+
if (idx <= 0 || idx === path.length - 1) throw new Error(`config path "${path}" must be section.key (e.g. cheap-worker.models)`);
|
|
117
117
|
const section = path.slice(0, idx);
|
|
118
118
|
const key = path.slice(idx + 1);
|
|
119
119
|
if (!CONFIG_SECTION_RE.test(section)) throw new Error(`config section "${section}" must match ^[a-z][a-z0-9-]*$`);
|
|
@@ -197,7 +197,7 @@ function docText(root) {
|
|
|
197
197
|
" \"credentials\": {",
|
|
198
198
|
" \"HOME_GITLAB_TOKEN\": \"xxx\"",
|
|
199
199
|
" },",
|
|
200
|
-
" \"
|
|
200
|
+
" \"cheap-worker\": {",
|
|
201
201
|
" \"models\": [\"minimax-cn-coding-plan/MiniMax-M3\"]",
|
|
202
202
|
" }",
|
|
203
203
|
"}",
|
|
@@ -206,7 +206,7 @@ function docText(root) {
|
|
|
206
206
|
"",
|
|
207
207
|
"## Key conventions",
|
|
208
208
|
"- credential key: ^[A-Z][A-Z0-9_]*$ (e.g. HOME_GITLAB_TOKEN)",
|
|
209
|
-
"- config path: section.key (section ^[a-z][a-z0-9-]*$, key lowerCamel ^[a-z][a-zA-Z0-9_]*$, e.g.
|
|
209
|
+
"- config path: section.key (section ^[a-z][a-z0-9-]*$, key lowerCamel ^[a-z][a-zA-Z0-9_]*$, e.g. cheap-worker.models)",
|
|
210
210
|
"",
|
|
211
211
|
"## Reading (agents may use fs tools directly)",
|
|
212
212
|
`- Read ${path} with the read tool → JSON.parse`,
|
|
@@ -353,7 +353,8 @@ function readWeixinSettings(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
353
353
|
name: typeof a.name === "string" ? a.name : void 0,
|
|
354
354
|
enabled: a.enabled !== false
|
|
355
355
|
})) : [],
|
|
356
|
-
routes: Array.isArray(w.routes) ? w.routes.filter((r) => typeof r === "object" && r !== null && typeof r.user === "string" && r.user !== "" && typeof r.role === "string" && r.role !== "") : []
|
|
356
|
+
routes: Array.isArray(w.routes) ? w.routes.filter((r) => typeof r === "object" && r !== null && typeof r.user === "string" && r.user !== "" && typeof r.role === "string" && r.role !== "") : [],
|
|
357
|
+
aliases: readAliases(w)
|
|
357
358
|
};
|
|
358
359
|
} catch {
|
|
359
360
|
return {
|
|
@@ -363,6 +364,21 @@ function readWeixinSettings(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
363
364
|
};
|
|
364
365
|
}
|
|
365
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* 别名表清洗(v1.56.10):键归一为小写、值必须是非空字符串;非法项静默丢弃。
|
|
369
|
+
* 空/缺省 → `{}`(此时别名解析不可用,只能用裸 iLink 用户 id)。
|
|
370
|
+
*/
|
|
371
|
+
function readAliases(w) {
|
|
372
|
+
const raw = w.aliases;
|
|
373
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return {};
|
|
374
|
+
const out = {};
|
|
375
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
376
|
+
const alias = k.trim().toLowerCase();
|
|
377
|
+
if (alias === "" || typeof v !== "string" || v.trim() === "") continue;
|
|
378
|
+
out[alias] = v.trim();
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
366
382
|
/** localstore 凭据键(credential scope: WEIXIN_<ACCOUNT_ID>_<FIELD>) */
|
|
367
383
|
function lsKey(accountId, field) {
|
|
368
384
|
return `${LS_PREFIX}${accountKeyPart(accountId)}_${field}`;
|
package/lib/weixin-send-api.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 形态(用户拍板 A2):**独立监听器,只绑 127.0.0.1**(默认 3082,plugin 全局配置 weixinApi)。
|
|
5
5
|
* 为什么不挂在主 webServer(3080) 上:3080 会被 3081 公网网关原样反代(含全部请求头),
|
|
6
|
-
* 而 3081 的登录态属于"
|
|
6
|
+
* 而 3081 的登录态属于"对外账号"——一旦挂上去,任何已登录的外部用户都能冒充 bot 发消息。
|
|
7
7
|
* 独立 loopback 监听器则**不经网关、公网不可达**,因此不做共享密钥;仍校验来源地址。
|
|
8
8
|
*
|
|
9
9
|
* 调用者 = **非 agent 的调用者**(外部脚本/集成/运维)。**agent 侧已不走这里**:v1.31.0 起
|
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 工作区」:14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/
|
|
3
|
+
"version": "1.57.0",
|
|
4
|
+
"description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「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(双档)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/lib/handyman-ops.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* handyman-ops.ts — handyman(杂工)纯逻辑层(零 DSH 依赖,可独立单测)
|
|
3
|
-
*
|
|
4
|
-
* v1.24.0:loop(牛马)→ handyman(杂工)重命名。语义对齐 osp loop:
|
|
5
|
-
* 进度文件(handyman-<label>.md/.json)、续跑、轮次 prompt 结构、stop token。
|
|
6
|
-
* 不兼容旧 loop- 进度文件(用户拍板:仅新 handyman- 前缀)。
|
|
7
|
-
*/
|
|
8
|
-
interface HandymanProgress {
|
|
9
|
-
round: number;
|
|
10
|
-
done: boolean;
|
|
11
|
-
label: string;
|
|
12
|
-
model: string;
|
|
13
|
-
updated: string;
|
|
14
|
-
lastResponse: string;
|
|
15
|
-
/** 对齐 osp writeFailedStatus:failed 时 done=true + status=failed + errorCode */
|
|
16
|
-
status?: 'running' | 'done' | 'failed';
|
|
17
|
-
errorCode?: string;
|
|
18
|
-
errorMessage?: string;
|
|
19
|
-
}
|
|
20
|
-
/** label 脱敏(Windows 审计问题 17):非法字符 → '-',去尾点/空格,限长(按码点截断,修复代理对切散 U+FFFD) */
|
|
21
|
-
export declare function sanitizeLabel(label: string): string;
|
|
22
|
-
export declare function handymanProgressPaths(root: string, label: string): {
|
|
23
|
-
md: string;
|
|
24
|
-
json: string;
|
|
25
|
-
};
|
|
26
|
-
/** 读取进度(续跑);无文件返回 round 0 */
|
|
27
|
-
export declare function readProgress(root: string, label: string): HandymanProgress | null;
|
|
28
|
-
export declare function writeProgress(root: string, label: string, p: HandymanProgress): void;
|
|
29
|
-
/** 失败状态落盘(对齐 osp writeFailedStatus:done=true / status=failed / errorCode) */
|
|
30
|
-
export declare function writeFailedStatus(root: string, label: string, info: {
|
|
31
|
-
errorCode: string;
|
|
32
|
-
errorMessage?: string;
|
|
33
|
-
}): void;
|
|
34
|
-
export declare function newStopToken(): string;
|
|
35
|
-
/** 解析 model 字符串(provider/model)→ {provider, model};无 / 视为 model-only */
|
|
36
|
-
export declare function splitModel(model: string): {
|
|
37
|
-
provider: string | undefined;
|
|
38
|
-
model: string;
|
|
39
|
-
};
|
|
40
|
-
/** 校验模型在白名单内;不在 → 抛错(用户拍板:只能使用 CCC 配置的模型) */
|
|
41
|
-
export declare function requireWhitelistedModel(model: string, models: string[]): void;
|
|
42
|
-
/**
|
|
43
|
-
* providerAvailabilityError — 判定「**本机有没有该 provider 的 adapter**」,并产出**可执行**的错误文本。
|
|
44
|
-
* 返回 `null` = 全部可用(调用方照常继续)。
|
|
45
|
-
*
|
|
46
|
-
* 🔴 为什么需要它(S142 2026-09-25,owner 在 **Mac** 上实测):`handyman` 的模型白名单写在
|
|
47
|
-
* **CCC**(`.opencode/serenity.json`,**随 git 走**),而 provider adapter 由**每台机器的宿主**
|
|
48
|
-
* 注册(profile 配置 / settings 命名空间)⇒ 同一份白名单在一台机器可用、在另一台必失败。
|
|
49
|
-
* 实测症状:宿主抛 `no adapter registered for provider "<p>"`(`dsh-llm`,code `NO_ADAPTER`),
|
|
50
|
-
* 而**前台路径的 `diagnostic` 是空的** ⇒ 调用方(LLM)拿到 `stopReason:"error"` 却**不知道为什么**,
|
|
51
|
-
* 也就无法修正自己的配置 —— 本条把「沉默的失败」变成「可执行的错误」。
|
|
52
|
-
*
|
|
53
|
-
* 数据来源(宿主 `LlmRuntime`):`listProviders()` = **有 adapter 的路由**;
|
|
54
|
-
* `listConfigurableProviders()` = **已声明但未必激活**的路由(含激活用的 settings 命名空间)。
|
|
55
|
-
*
|
|
56
|
-
* 判据边界:`model` 串里**没有 `/`** 时取不到 provider(沿用 agent 默认路由)⇒ **不判定**(跳过)。
|
|
57
|
-
*/
|
|
58
|
-
export declare function providerAvailabilityError(models: readonly string[], facts: {
|
|
59
|
-
registered: readonly {
|
|
60
|
-
id: string;
|
|
61
|
-
name?: string;
|
|
62
|
-
}[];
|
|
63
|
-
declared?: readonly {
|
|
64
|
-
provider: string;
|
|
65
|
-
displayName?: string;
|
|
66
|
-
settingsNs?: string;
|
|
67
|
-
}[];
|
|
68
|
-
configPath?: string;
|
|
69
|
-
}): string | null;
|
|
70
|
-
/** 轮次 prompt(对齐老 loop 结构:回顾进度 → 自由工作 → 汇报;S134 EAP 化:固定详尽) */
|
|
71
|
-
export declare function buildRoundPrompt(opts: {
|
|
72
|
-
root: string;
|
|
73
|
-
session?: string;
|
|
74
|
-
label: string;
|
|
75
|
-
round: number;
|
|
76
|
-
stopToken: string;
|
|
77
|
-
progress?: HandymanProgress | null;
|
|
78
|
-
task?: string;
|
|
79
|
-
}): string;
|
|
80
|
-
/**
|
|
81
|
-
* handyman 规模化使用指引(guide 子命令输出;S134 继承 + v1.24.0 更新):
|
|
82
|
-
* 使用 handyman 前必须先加载 eap 设计方案;并行策略(jobs 编排);提示词规范(详尽固定 EAP);
|
|
83
|
-
* 阅读/文字编写类 handyman 内部也加载 eap。
|
|
84
|
-
*/
|
|
85
|
-
export declare const HANDYMAN_GUIDE = "# handyman \u2014 Scale-Up Usage Guide (guide)\n\n## Two modes (v1.31.3; default = foreground)\n| | foreground (default) | background |\n|---|---|---|\n| What | child agent(s) run the task ONCE and return their final text (jobs=[...] \u21D2 parallel, cap 10) | loop-validated worker: hard while-loop + random completion code |\n| Use when | you need result(s) back now (per-case testers, single conversions, parallel fan-out) | long/unattended work needing anti-early-finish, resumability, or parallel jobs |\n| Loop / validation | none | stop-token is the ONLY completion condition; round cap (default 100); auto-restart (\u2264100) |\n| Progress file | none | AGENT_SESSIONS/handyman-<label>.md/.json (same label resumes) |\n| Parallel jobs | **yes** \u2014 jobs=[{task,label?,model?},...], **cap 10 (fixed)** | yes (jobs=[...], handyman.maxParallel default 10) |\n| Mechanism | host delegation service ctx.subagents.start(\"spawn\") + agentOptions | ctx.agents.create + internal while-loop |\n| Model source | CCC handyman.models whitelist (same for both) | same |\n\n## \u26A0\uFE0F Before using: load eap and design the plan\nBefore calling handyman, load eap (acc-eap skill) and design the \"scale-up handyman 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 (both modes)\n\n### 2. Prompt design (handyman's task parameter)\n- task must be detailed, fixed, and EAP-compliant: clear goal, drawn boundaries, decidable acceptance criteria\n- foreground mode: 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 handyman-internal agent is also required to load eap and organize output per the EAP standard\n\n### 3. Model whitelist (CCC-configured, mandatory)\n- handyman only uses models listed in .opencode/serenity.json \"handyman.models\" \u2014 never arbitrary models\n- The CCC is expected to point this whitelist at LOW-COST models: handyman is the bulk-execution channel\n- background mode: recursive subagents inside a worker inherit the worker's model automatically (DSH native)\n- foreground mode: the child is started through the host delegation service with the resolved model\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, handyman 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 foreground puts this in \"diagnostic\", background throws it.\n- \u21D2 If you see \"has no adapter registered on this machine\": edit \".opencode/serenity.json\" \"handyman.models\"\n (and \"handyman.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 (both modes)\n- Independent subtasks can run in parallel:\n \u00B7 **foreground** \u2014 handyman(jobs=[{task,label?,model?},...]) \u21D2 fan-out of custom-model subagents, **cap 10**,\n results come back **in the order you passed them** (label is only a display name here; per-job \"model\" allowed)\n \u00B7 **background** \u2014 handyman(mode=\"background\", jobs=[...]): each job gets its own label + task + stop token + progress file\n- Concurrency safety guaranteed: unique sessionId (handyman-<label>-<uuid>), progress files isolated per label\n (AGENT_SESSIONS/handyman-<label>.json) \u2014 same label resumes, different labels never interfere\n- Parallel cap: **foreground 10 (fixed)** / background handyman.maxParallel (default 10 \u2014 cheap models are cheap)\n- Aggregation: foreground returns all outputs in one result; background: after each parallel job produces progress, the main agent merges (or spawns one aggregation handyman)\n- For programmable pipeline/phase orchestration at scale, use the platform's workflow tool instead\n\n## Completion criteria\n- foreground: the child's run settles; done=true only when stopReason=\"completed\" (otherwise read diagnostic)\n- background (osp loop standard): the only completion condition = the worker echoes this round's random verification\n code (stop token); dialogue round cap (default 100, osp fail-safe, forced stop beyond the cap, resumable);\n automatic restart on abnormal agent stop (\u2264100 restarts, anti-infinite-loop)\n\n## Waiting UI\n- The WebUI session-header Serenity detail card shows running background handymen's progress\n (label / round / last response), one line per parallel job, ~3s refresh (foreground mode writes no progress)\n";
|
|
86
|
-
/** handyman 运行状态(进度文件摘要;WebUI 等待界面数据源) */
|
|
87
|
-
interface HandymanRunInfo {
|
|
88
|
-
label: string;
|
|
89
|
-
round: number;
|
|
90
|
-
done: boolean;
|
|
91
|
-
model: string;
|
|
92
|
-
updated: string;
|
|
93
|
-
lastResponse: string;
|
|
94
|
-
}
|
|
95
|
-
/** 列出 AGENT_SESSIONS/handyman-*.json 的全部进度(按 updated 倒序;坏文件跳过) */
|
|
96
|
-
export declare function listActiveHandymen(root: string): HandymanRunInfo[];
|
|
97
|
-
export {};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* handyman-preset-inherit.ts — handyman worker 的 preset 继承 + 工具面收窄(纯函数,可单测)
|
|
3
|
-
*
|
|
4
|
-
* v1.24.0:loop → handyman 重命名。对齐 subagent 先例(applyChildComposition →
|
|
5
|
-
* agentPresets.composeFrom):handyman worker 在创建窗口内 join 发起方 agent 的 standing
|
|
6
|
-
* preset 组合,从而获得 preset 层工具(read/write/edit 等)。
|
|
7
|
-
*
|
|
8
|
-
* **工具面收窄(用户拍板)**:worker 内部**不含 handyman 工具本身**——递归编排归主
|
|
9
|
-
* agent,worker 内部只走 subagent(模型继承 worker,同样低能)。实现:setup 钩子
|
|
10
|
-
* composeFrom 后对子 scope `tools.restrict({ deny: ['handyman'] })`(同 scope 注册的
|
|
11
|
-
* 规则,最近 scope 胜出——worker 看不到 handyman,主 agent 不受影响)。
|
|
12
|
-
*
|
|
13
|
-
* agentPresets 是可选服务(ctx.get 读取):
|
|
14
|
-
* - 无该服务 / 父未 join preset → 返回空(worker 落全局工具层 + deny handyman);
|
|
15
|
-
* - 有且父已 join → 返回 preset id(写入 session meta 供重建)+ setup 钩子
|
|
16
|
-
* (在 agent 未发布前执行 composeFrom + restrict,失败由创建事务回滚)。
|
|
17
|
-
*
|
|
18
|
-
* 仅依赖 cordis 的 Context 类型(type-only,运行时擦除)——保持可被 vitest
|
|
19
|
-
* 直接加载(与 ccc.ts / handyman-ops.ts 同级的纯模块约定)。
|
|
20
|
-
*/
|
|
21
|
-
import type { Context } from 'cordis';
|
|
22
|
-
/** 从父 agent ctx 解析 preset 继承的创建期组合。 */
|
|
23
|
-
interface HandymanPresetInheritance {
|
|
24
|
-
/** 父 agent 的 preset id(写入子 session meta.agentPreset,供持久化重建);无则不写。 */
|
|
25
|
-
readonly agentPreset?: string;
|
|
26
|
-
/** 创建 setup 钩子:子 scope join 父的 standing preset + deny handyman 工具;无 preset 继承时仅 deny。 */
|
|
27
|
-
readonly setup?: (childCtx: Context) => void;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* 解析 handyman worker 从父 agent 继承的 preset,并组装创建 setup 钩子。
|
|
31
|
-
* @param parentCtx - 发起 handyman 的 agent 的 scope ctx;无(headless/非 agent 上下文)时为 undefined。
|
|
32
|
-
* @returns 继承结果:meta 用的 agentPreset 与创建 setup 钩子。
|
|
33
|
-
*/
|
|
34
|
-
export declare function handymanPresetInheritance(parentCtx: Context | undefined): HandymanPresetInheritance;
|
|
35
|
-
export {};
|
package/lib/tools/handyman.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* handyman.ts — handyman(杂工)真实 DSH 工具(v1.24.0:loop 牛马 → handyman 杂工)
|
|
3
|
-
*
|
|
4
|
-
* 用户需求(S142 拍板):
|
|
5
|
-
* - **同步**(用户明确:这个事一般不会异步——工具阻塞到完成)
|
|
6
|
-
* - **指定模型**(CCC 白名单内——handyman.models,未配置报错)
|
|
7
|
-
* - **自主循环到完成**(stop-token 唯一完成判据,对齐 osp loop)
|
|
8
|
-
* - **内部递归同样低能 subagent**(worker 工具面含 subagent,DSH 原生模型继承)
|
|
9
|
-
* - **workflow 能力**(jobs 编排:主 agent 一次并行多个杂工,maxParallel 默认 10)
|
|
10
|
-
* - **worker 不含 handyman 本身**(递归编排归主 agent,防无限嵌套)
|
|
11
|
-
* - **不兼容旧 loop 进度文件**(仅 handyman- 前缀)
|
|
12
|
-
*
|
|
13
|
-
* 机制:ctx.agents.create()(带 setup 钩子)创建专用 agent(进程内),
|
|
14
|
-
* 每轮 followup → agent/status idle → 读 session.events 响应 → 写进度 → stop token 检查 →
|
|
15
|
-
* 未完成继续下一轮;followup/等待空闲抛错(非正常停止)→ dispose 并重新 create agent
|
|
16
|
-
* (重启计数,≤100),同一轮重试。工厂模式:apply 时闭包捕获插件 ctx(工具 execute 无 ctx 参数)。
|
|
17
|
-
*
|
|
18
|
-
* preset 继承 + 工具收窄:setup 钩子里对子 agent 执行 agentPresets.composeFrom(对齐
|
|
19
|
-
* subagent 先例)+ tools.restrict deny handyman(worker 内部看不到 handyman 工具)。
|
|
20
|
-
*/
|
|
21
|
-
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
22
|
-
import type { Context } from 'cordis';
|
|
23
|
-
/** 创建 handyman 工具(闭包捕获插件 ctx → 可访问 ctx.agents) */
|
|
24
|
-
export declare function createHandymanTool(ctx: Context): ToolDefinition;
|