@shgroup/dsh-serenity-hooks 1.56.10 → 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/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-CDW2dO0n.js → ccc-roots-BC8mn8iM.js} +3 -3
- package/lib/ccc-roots.d.ts +1 -1
- package/lib/ccc.d.ts +23 -10
- package/lib/cheap-worker-ops.d.ts +49 -0
- package/lib/cheap-worker-preset-inherit.d.ts +35 -0
- package/lib/client.js +4 -92
- package/lib/cro-guide.d.ts +1 -1
- package/lib/host/access.d.ts +2 -2
- package/lib/index.js +174 -561
- 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-CQNfQaTx.js → message-source-BcCRsEOh.js} +1 -1
- package/lib/seams/context.d.ts +2 -2
- package/lib/{skiff-role-Det66X16.js → skiff-role-B615L9MV.js} +2 -2
- package/lib/skiff-role.d.ts +1 -1
- 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/{weixin-route-CxCciqQl.js → weixin-route-B9gU1YXz.js} +5 -5
- 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/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?: {
|
|
@@ -286,14 +293,20 @@ export interface SkiffRoleConfig {
|
|
|
286
293
|
systemPromptFile?: string;
|
|
287
294
|
}
|
|
288
295
|
/**
|
|
289
|
-
* 解析
|
|
290
|
-
*
|
|
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(工具应报错要求配置)
|
|
291
306
|
*/
|
|
292
|
-
export declare function
|
|
307
|
+
export declare function readCheapWorkerConfig(root: string, paths?: string[]): {
|
|
293
308
|
models: string[];
|
|
294
309
|
defaultModel: string;
|
|
295
|
-
maxRounds: number;
|
|
296
|
-
maxParallel: number;
|
|
297
310
|
} | null;
|
|
298
311
|
/**
|
|
299
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 {};
|
package/lib/client.js
CHANGED
|
@@ -8,7 +8,7 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
//#region \0sp-css:src/client/SafeModePanel
|
|
11
|
-
const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n width: min(680px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n max-height: calc(100vh - 120px);\n}\n/* 左栏:信息(正文)——固定 340px,与旧卡片同宽,保证原有排印不变 */\n.sp-popVoyage > .sp-popBody {\n position: relative;\n z-index: 1;\n order: 1;\n flex: none;\n width: 340px;\n min-width: 0;\n}\n/* 右栏:动画——吃掉剩余宽度,并吃满左栏高度 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n min-height: 260px; /* 动画的下限:信息很少时也不塌成一条 */\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(420px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-popBody {\n width: auto; /* 单栏下不再固定 340px,跟随卡片宽度 */\n flex: none; /* 按内容高(仍受自身 max-height 约束) */\n }\n .sp-voyage {\n flex: none;\n height: 200px; /* 单栏时给动画明确高度,避免被 flex 压扁 */\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n flex: none;\n width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* handyman 运行状态区块 */\n\n.sp-handymenToggle {\n flex: none;\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n cursor: pointer;\n}\n\n.sp-handymenToggle:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.sp-handymenList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-top: 2px;\n}\n\n.sp-handymanItem {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n min-width: 0;\n}\n\n.sp-handymanHead {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.sp-handymanLabel {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.sp-handymanRound {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-handymanTime {\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n\n.sp-handymanResp {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sp-handymenMore {\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n padding: 4px 12px;\n}\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
11
|
+
const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n width: min(680px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n max-height: calc(100vh - 120px);\n}\n/* 左栏:信息(正文)——固定 340px,与旧卡片同宽,保证原有排印不变 */\n.sp-popVoyage > .sp-popBody {\n position: relative;\n z-index: 1;\n order: 1;\n flex: none;\n width: 340px;\n min-width: 0;\n}\n/* 右栏:动画——吃掉剩余宽度,并吃满左栏高度 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n min-height: 260px; /* 动画的下限:信息很少时也不塌成一条 */\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(420px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-popBody {\n width: auto; /* 单栏下不再固定 340px,跟随卡片宽度 */\n flex: none; /* 按内容高(仍受自身 max-height 约束) */\n }\n .sp-voyage {\n flex: none;\n height: 200px; /* 单栏时给动画明确高度,避免被 flex 压扁 */\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n flex: none;\n width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* 🔴 2026-10-14:`.sp-cheapWorkers*` / `.sp-cheapWorker*` 样式区块已随 background 模式删除\n —— 那是「运行中 cheap-worker」列表(进度文件驱动)的样式,而进度文件不再有生产者。 */\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
|
|
12
12
|
if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SafeModePanel\"]") === null) {
|
|
13
13
|
const tag = document.createElement("style");
|
|
14
14
|
tag.setAttribute("data-sp-css", "SafeModePanel");
|
|
@@ -24,7 +24,6 @@ window.__ModuleLoader__.load({
|
|
|
24
24
|
/** 尚未落位时的屏外坐标(`useLayoutEffect` 在绘制前就会覆写,肉眼看不到) */
|
|
25
25
|
const POP_OFFSCREEN = -9999;
|
|
26
26
|
const API = "/serenity/status";
|
|
27
|
-
const HANDYMEN_API = "/serenity/handymen";
|
|
28
27
|
/** 盾牌 SVG(v1.24.5 校准:fill=none 纯描边;ON=绿 emerald / OFF=灰 label-secondary 由状态类控制) */
|
|
29
28
|
const SHIELD_SVG = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
30
29
|
viewBox: "0 0 16 16",
|
|
@@ -54,8 +53,6 @@ window.__ModuleLoader__.load({
|
|
|
54
53
|
const [status, setStatus] = (0, react.useState)(null);
|
|
55
54
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
56
55
|
const [open, setOpen] = (0, react.useState)(false);
|
|
57
|
-
const [handymen, setHandymen] = (0, react.useState)([]);
|
|
58
|
-
const [handymenOpen, setHandymenOpen] = (0, react.useState)(false);
|
|
59
56
|
const rootRef = (0, react.useRef)(null);
|
|
60
57
|
const popRef = (0, react.useRef)(null);
|
|
61
58
|
const [popPos, setPopPos] = (0, react.useState)(null);
|
|
@@ -72,25 +69,6 @@ window.__ModuleLoader__.load({
|
|
|
72
69
|
(0, react.useEffect)(() => {
|
|
73
70
|
refresh();
|
|
74
71
|
}, [refresh]);
|
|
75
|
-
(0, react.useEffect)(() => {
|
|
76
|
-
if (!open) return;
|
|
77
|
-
let alive = true;
|
|
78
|
-
const qs = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
|
|
79
|
-
const tick = async () => {
|
|
80
|
-
try {
|
|
81
|
-
const data = await (await fetch(`${HANDYMEN_API}${qs}`, { headers: { accept: "application/json" } })).json();
|
|
82
|
-
if (alive) setHandymen(data.handymen ?? []);
|
|
83
|
-
} catch {
|
|
84
|
-
if (alive) setHandymen([]);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
tick();
|
|
88
|
-
const timer = setInterval(() => void tick(), 3e3);
|
|
89
|
-
return () => {
|
|
90
|
-
alive = false;
|
|
91
|
-
clearInterval(timer);
|
|
92
|
-
};
|
|
93
|
-
}, [open, sessionId]);
|
|
94
72
|
(0, react.useEffect)(() => {
|
|
95
73
|
if (!open) return;
|
|
96
74
|
const onDocMouseDown = (e) => {
|
|
@@ -164,7 +142,6 @@ window.__ModuleLoader__.load({
|
|
|
164
142
|
})]
|
|
165
143
|
});
|
|
166
144
|
const inCcc = status.root !== null;
|
|
167
|
-
const runningHandymen = handymen.filter((l) => !l.done);
|
|
168
145
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
169
146
|
className: cx("sp-root"),
|
|
170
147
|
ref: rootRef,
|
|
@@ -251,11 +228,11 @@ window.__ModuleLoader__.load({
|
|
|
251
228
|
className: cx("sp-row"),
|
|
252
229
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
253
230
|
className: cx("sp-label"),
|
|
254
|
-
children: "
|
|
231
|
+
children: "CheapWorker 模型"
|
|
255
232
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
256
233
|
className: cx("sp-value"),
|
|
257
|
-
title: status.
|
|
258
|
-
children: status.
|
|
234
|
+
title: status.cheapWorkerModel ? `cheap-worker.models: ${status.cheapWorkerModel}` : void 0,
|
|
235
|
+
children: status.cheapWorkerModel ?? "未配置"
|
|
259
236
|
})]
|
|
260
237
|
}),
|
|
261
238
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -302,71 +279,6 @@ window.__ModuleLoader__.load({
|
|
|
302
279
|
})]
|
|
303
280
|
})]
|
|
304
281
|
}),
|
|
305
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
306
|
-
className: cx("sp-group"),
|
|
307
|
-
children: [
|
|
308
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
309
|
-
className: cx("sp-groupHead"),
|
|
310
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
311
|
-
className: cx("sp-groupTitle"),
|
|
312
|
-
children: "Handyman 运行"
|
|
313
|
-
}), handymen.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
314
|
-
type: "button",
|
|
315
|
-
className: cx("sp-handymenToggle"),
|
|
316
|
-
onClick: () => setHandymenOpen((v) => !v),
|
|
317
|
-
children: handymenOpen ? "收起" : "详情"
|
|
318
|
-
})]
|
|
319
|
-
}),
|
|
320
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
321
|
-
className: cx("sp-rows"),
|
|
322
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
323
|
-
className: cx("sp-row"),
|
|
324
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
325
|
-
className: cx("sp-label"),
|
|
326
|
-
children: "状态"
|
|
327
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
328
|
-
className: cx("sp-value"),
|
|
329
|
-
children: runningHandymen.length > 0 ? `运行中 ${runningHandymen.length}${handymen.length > 0 ? ` / 共 ${handymen.length}` : ""}` : "无运行中 handyman"
|
|
330
|
-
})]
|
|
331
|
-
})
|
|
332
|
-
}),
|
|
333
|
-
handymenOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
334
|
-
className: cx("sp-handymenList"),
|
|
335
|
-
children: [handymen.slice(0, 3).map((l) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
336
|
-
className: cx("sp-handymanItem"),
|
|
337
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
338
|
-
className: cx("sp-handymanHead"),
|
|
339
|
-
children: [
|
|
340
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
341
|
-
className: cx("sp-handymanLabel"),
|
|
342
|
-
title: l.label,
|
|
343
|
-
children: l.label
|
|
344
|
-
}),
|
|
345
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
346
|
-
className: cx("sp-handymanRound"),
|
|
347
|
-
children: l.done ? "✓ 完成" : `R${l.round}`
|
|
348
|
-
}),
|
|
349
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
350
|
-
className: cx("sp-handymanTime"),
|
|
351
|
-
children: new Date(l.updated).toLocaleTimeString()
|
|
352
|
-
})
|
|
353
|
-
]
|
|
354
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
355
|
-
className: cx("sp-handymanResp"),
|
|
356
|
-
title: l.lastResponse,
|
|
357
|
-
children: l.lastResponse.slice(0, 80) || (l.done ? "已完成" : "(等待首轮响应)")
|
|
358
|
-
})]
|
|
359
|
-
}, l.label)), handymen.length > 3 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
360
|
-
className: cx("sp-handymenMore"),
|
|
361
|
-
children: [
|
|
362
|
-
"… 共 ",
|
|
363
|
-
handymen.length,
|
|
364
|
-
" 个 handyman(其余见等待界面)"
|
|
365
|
-
]
|
|
366
|
-
})]
|
|
367
|
-
})
|
|
368
|
-
]
|
|
369
|
-
}),
|
|
370
282
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
371
283
|
className: cx("sp-hint"),
|
|
372
284
|
children: [
|
package/lib/cro-guide.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export { CRO_FILENAME, CRO_TIMEOUT_MS } from './cro.js';
|
|
|
40
40
|
/**
|
|
41
41
|
* CRO 编写指南正文(ACC 内置手册;由工具的 guide 动作输出)。
|
|
42
42
|
*
|
|
43
|
-
* 结构照既有 guide 惯例(`MSM_GUIDE` / `
|
|
43
|
+
* 结构照既有 guide 惯例(`MSM_GUIDE` / `cheap-worker(guide=true)`):
|
|
44
44
|
* 用途 → 位置 → 我给什么 → 你给我什么 → 骨架 → 常见模式 → 坑 → **自测**。
|
|
45
45
|
*/
|
|
46
46
|
export declare const CRO_GUIDE: string;
|
package/lib/host/access.d.ts
CHANGED
|
@@ -84,7 +84,7 @@ export declare function hostSettings(ctx: unknown): HostSettings | undefined;
|
|
|
84
84
|
/** `ctx.web`(injected;v1.30.12 web_fetch provider 注册通道) */
|
|
85
85
|
export declare function hostWeb(ctx: unknown): HostWeb | undefined;
|
|
86
86
|
/**
|
|
87
|
-
* `ctx.subagents`(lazy;v1.31.3:
|
|
87
|
+
* `ctx.subagents`(lazy;v1.31.3:cheap-worker foreground 模式的委派正门)。
|
|
88
88
|
*
|
|
89
89
|
* 形状对照宿主 rc.1:`SubagentRuntime.start(name, request)` → `SubagentRun`
|
|
90
90
|
* (`result` / `dispose` / `id`)。本模块只做形状收口;语义与错误处理归调用方。
|
|
@@ -97,7 +97,7 @@ interface HostSubagents {
|
|
|
97
97
|
}
|
|
98
98
|
export declare function hostSubagents(ctx: unknown): HostSubagents | undefined;
|
|
99
99
|
/**
|
|
100
|
-
* `ctx.llm`(`LlmRuntime`;v1.48:
|
|
100
|
+
* `ctx.llm`(`LlmRuntime`;v1.48:cheap-worker 的 **provider 预检**取证面)。
|
|
101
101
|
*
|
|
102
102
|
* 形状对照宿主 rc.2(`@deepseek-ai/dsh-llm`):
|
|
103
103
|
* · `listProviders()` → `{ id, name }[]` —— **有 adapter** 的路由("本机真能用")
|