@shgroup/dsh-serenity-hooks 1.25.1 → 1.25.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/lib/index.js +80 -21
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.3",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):真实 DSH 工具 cc_fs/session/acc_msm/eap/neat/cce/handyman/session_rebuild/skiff_admin + 拦截缝机械约束(safe-mode/路径守卫)+ 高级设定面板(双端口网关/账号管理)+ Skiff 认知子集角色(实验性)。适配 DSH 公开版(0.1.0-rc,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
package/lib/index.js
CHANGED
|
@@ -756,6 +756,7 @@ function isWriteTool(toolName, action) {
|
|
|
756
756
|
function decideGuard(input) {
|
|
757
757
|
const { root, toolName, safeModeOn, blacklist, pathArg, action } = input;
|
|
758
758
|
if (input.skiffSessionId !== void 0 && isSkiffSessionId(input.skiffSessionId)) {
|
|
759
|
+
if (toolName === "skill") return { kind: "allow" };
|
|
759
760
|
const roleName = input.skiffSessionId ? skiffRoleFor$1(input.skiffSessionId) : null;
|
|
760
761
|
if (!roleToolWhitelist(roleName ? readSkiffRoles(root).get(roleName) : void 0).has(toolName)) return {
|
|
761
762
|
deny: "tool not allowed in this skiff role",
|
|
@@ -2360,9 +2361,13 @@ function registerSkiffSession(sessionId, role, agent) {
|
|
|
2360
2361
|
skiffAgents.set(sessionId, agent);
|
|
2361
2362
|
registerSkiffSession$1(sessionId, role);
|
|
2362
2363
|
}
|
|
2364
|
+
/** Skiff agent 挂载的 DSH preset(v1.25.3 修复:read/grep/glob 等平台工具由 preset 决定工具面;
|
|
2365
|
+
* handyman 经 composeFrom 继承父、skiff 无父上下文——直接挂 DSH 默认 standard preset;
|
|
2366
|
+
* guard 角色白名单再按角色过滤可见/可用面——白名单外工具仍 deny) */
|
|
2367
|
+
const SKIFF_PRESET = "standard";
|
|
2363
2368
|
/**
|
|
2364
2369
|
* 创建 Skiff agent:标准 DSH agent + cwd=CCC root + 角色模型 +
|
|
2365
|
-
* scoped 系统提示词(基础提示词 + CCC 定义段,全替换 ACC 默认注入)。
|
|
2370
|
+
* standard preset(平台工具面)+ scoped 系统提示词(基础提示词 + CCC 定义段,全替换 ACC 默认注入)。
|
|
2366
2371
|
*/
|
|
2367
2372
|
async function createSkiffAgent(ctx, root, roleName, role, defaultModel) {
|
|
2368
2373
|
if (!ctx.agents) throw new Error("skiff: ctx.agents unavailable");
|
|
@@ -2370,7 +2375,15 @@ async function createSkiffAgent(ctx, root, roleName, role, defaultModel) {
|
|
|
2370
2375
|
const sessionId = `${SKIFF_SESSION_PREFIX}${roleName}-${randomUUID()}`;
|
|
2371
2376
|
const handle = await ctx.agents.create({
|
|
2372
2377
|
sessionId,
|
|
2373
|
-
meta: {
|
|
2378
|
+
meta: {
|
|
2379
|
+
cwd: root,
|
|
2380
|
+
agentPreset: SKIFF_PRESET
|
|
2381
|
+
},
|
|
2382
|
+
setup: async (agentCtx) => {
|
|
2383
|
+
try {
|
|
2384
|
+
await agentCtx.get("agentPresets")?.mount?.(agentCtx, SKIFF_PRESET);
|
|
2385
|
+
} catch {}
|
|
2386
|
+
},
|
|
2374
2387
|
...model ? { agentOptions: splitModel(model) } : {}
|
|
2375
2388
|
});
|
|
2376
2389
|
const agent = handle.agent;
|
|
@@ -6821,7 +6834,10 @@ dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页)
|
|
|
6821
6834
|
── 运行 ──
|
|
6822
6835
|
调试:设置面板「Serenity」页 Skiff 区块开启 → http://127.0.0.1:<debugPort> 问答页实测
|
|
6823
6836
|
(v1.25.0 唯一客户端面;ACP stdio 协议 F4c 后续,与调试页共用同一会话核心)
|
|
6824
|
-
启停 =
|
|
6837
|
+
启停 = 人工(设置面板开关,不随插件加载自动启动);未开启零资源占用
|
|
6838
|
+
生效机制:角色配置**实时读取**(改 .opencode/serenity.json → 刷新调试页即生效);
|
|
6839
|
+
改配置后用 skiff_admin apply 做显式校验 + 应用确认(绑定 CCC + 角色清单)
|
|
6840
|
+
注意:skill 加载对 skiff **恒可用**(不设白名单——读知识面,无写能力)`;
|
|
6825
6841
|
/** 校验当前 CCC 的 skiff 配置:roles schema 合法 / msms 均已注册 / model ∈ handyman.models / systemPrompt 非空 */
|
|
6826
6842
|
function validateSkiffConfig(root) {
|
|
6827
6843
|
const roles = readSkiffRoles(root);
|
|
@@ -6870,18 +6886,45 @@ function listSkiffRoles(root) {
|
|
|
6870
6886
|
count: items.length
|
|
6871
6887
|
};
|
|
6872
6888
|
}
|
|
6889
|
+
/**
|
|
6890
|
+
* 应用当前 skiff 配置(v1.25.3,S142 用户:改了 json 应该有个生效机制)。
|
|
6891
|
+
*
|
|
6892
|
+
* 语义:配置是**每次请求实时读取**的(v1.25.2)——apply 不是"推送",而是**显式校验 + 应用确认**:
|
|
6893
|
+
* ① 校验配置合法(复用 validate:msms 注册 / model 白名单 / systemPrompt 非空)
|
|
6894
|
+
* ② 非法 → 报告问题清单,不应用(提示修复后重跑 apply)
|
|
6895
|
+
* ③ 合法 → 应用确认:绑定 CCC + 角色清单 + 说明(调试页刷新即生效)
|
|
6896
|
+
*/
|
|
6897
|
+
function applySkiffConfig(root) {
|
|
6898
|
+
const validation = validateSkiffConfig(root);
|
|
6899
|
+
const roles = readSkiffRoles(root);
|
|
6900
|
+
if (!validation.ok) return {
|
|
6901
|
+
applied: false,
|
|
6902
|
+
cccRoot: root,
|
|
6903
|
+
roleCount: roles.size,
|
|
6904
|
+
issues: validation.issues,
|
|
6905
|
+
hint: "fix the issues above, then run skiff_admin apply again"
|
|
6906
|
+
};
|
|
6907
|
+
return {
|
|
6908
|
+
applied: true,
|
|
6909
|
+
cccRoot: root,
|
|
6910
|
+
roleCount: roles.size,
|
|
6911
|
+
roles: [...roles.keys()],
|
|
6912
|
+
note: "roles are read live from .opencode/serenity.json — the skiff debug page reflects changes on refresh; skill loading is always available to skiff sessions (not whitelisted)"
|
|
6913
|
+
};
|
|
6914
|
+
}
|
|
6873
6915
|
const skiffAdminTool = defineTool({
|
|
6874
6916
|
name: "skiff_admin",
|
|
6875
|
-
description: "Skiff (F4, experimental): CCC cognitive-subset roles (subsets of the full serenity trajectory). guide: definition tutorial (concept/role schema/cognitive MSM writing/dual whitelist/trajectory subset/examples); validate: check the CCC skiff config (roles schema legal / msms registered / model in handyman.models / systemPrompt non-empty); list: role summary (name/model/msms/tools/trajectory). Roles are defined by the CCC in .opencode/serenity.json skiff.roles.",
|
|
6917
|
+
description: "Skiff (F4, experimental): CCC cognitive-subset roles (subsets of the full serenity trajectory). guide: definition tutorial (concept/role schema/cognitive MSM writing/dual whitelist/trajectory subset/examples); validate: check the CCC skiff config (roles schema legal / msms registered / model in handyman.models / systemPrompt non-empty); apply: validate then confirm the config is live (bound CCC + role list; roles are read live on every request); list: role summary (name/model/msms/tools/trajectory). Roles are defined by the CCC in .opencode/serenity.json skiff.roles; skill loading is always available to skiff sessions (not whitelisted).",
|
|
6876
6918
|
parameters: { action: {
|
|
6877
6919
|
type: "string",
|
|
6878
6920
|
enum: [
|
|
6879
6921
|
"guide",
|
|
6880
6922
|
"validate",
|
|
6923
|
+
"apply",
|
|
6881
6924
|
"list"
|
|
6882
6925
|
],
|
|
6883
6926
|
required: true,
|
|
6884
|
-
description: "Subcommand: guide (definition tutorial) / validate (config check) / list (role summary)"
|
|
6927
|
+
description: "Subcommand: guide (definition tutorial) / validate (config check) / apply (validate + confirm live) / list (role summary)"
|
|
6885
6928
|
} },
|
|
6886
6929
|
output: {
|
|
6887
6930
|
schema: { type: "json" },
|
|
@@ -6893,8 +6936,9 @@ const skiffAdminTool = defineTool({
|
|
|
6893
6936
|
switch (args.action) {
|
|
6894
6937
|
case "guide": return { guide: SKIFF_GUIDE };
|
|
6895
6938
|
case "validate": return validateSkiffConfig(root);
|
|
6939
|
+
case "apply": return applySkiffConfig(root);
|
|
6896
6940
|
case "list": return listSkiffRoles(root);
|
|
6897
|
-
default: throw new Error("skiff_admin requires action: guide | validate | list");
|
|
6941
|
+
default: throw new Error("skiff_admin requires action: guide | validate | apply | list");
|
|
6898
6942
|
}
|
|
6899
6943
|
}
|
|
6900
6944
|
});
|
|
@@ -9333,8 +9377,8 @@ function sendHtml(res, html) {
|
|
|
9333
9377
|
});
|
|
9334
9378
|
res.end(html);
|
|
9335
9379
|
}
|
|
9336
|
-
/** 问答页 HTML:角色下拉 + 输入 + 答案区 + 轨迹区(JS 渲染)+ WebUI 链接 */
|
|
9337
|
-
function skiffDebugPage(roles, webPort) {
|
|
9380
|
+
/** 问答页 HTML:角色下拉 + 输入 + 答案区 + 轨迹区(JS 渲染)+ 绑定的 CCC + WebUI 链接 */
|
|
9381
|
+
function skiffDebugPage(roles, cccRoot, webPort) {
|
|
9338
9382
|
const roleOptions = [...roles.entries()].map(([name, r]) => `<option value="${escapeHtml(name)}">${escapeHtml(name)}${r.model ? ` (${escapeHtml(r.model)})` : ""}</option>`).join("\n");
|
|
9339
9383
|
const webUrl = `http://127.0.0.1:${webPort}`;
|
|
9340
9384
|
return `<!DOCTYPE html>
|
|
@@ -9350,6 +9394,8 @@ function skiffDebugPage(roles, webPort) {
|
|
|
9350
9394
|
main { max-width: 720px; margin: 0 auto; }
|
|
9351
9395
|
h1 { font-size: 18px; margin: 0 0 4px; }
|
|
9352
9396
|
.sub { opacity: .65; font-size: 13px; margin-bottom: 16px; }
|
|
9397
|
+
.ccc { display: inline-block; padding: 3px 10px; border-radius: 999px; background: rgba(11,168,117,.12); color: #0ba875; font-size: 12px; font-weight: 600; margin-bottom: 12px; word-break: break-all; }
|
|
9398
|
+
@media (prefers-color-scheme: dark) { .ccc { color: #3ddc9a; } }
|
|
9353
9399
|
label { font-size: 13px; font-weight: 600; display: block; margin: 12px 0 4px; }
|
|
9354
9400
|
select, textarea { width: 100%; box-sizing: border-box; padding: 8px 10px; border-radius: 8px; border: 1px solid #d0d7de; background: #fff; color: inherit; font-size: 14px; }
|
|
9355
9401
|
@media (prefers-color-scheme: dark) { select, textarea { background: #26282c; border-color: #3a3d42; } }
|
|
@@ -9371,7 +9417,8 @@ function skiffDebugPage(roles, webPort) {
|
|
|
9371
9417
|
<body>
|
|
9372
9418
|
<main>
|
|
9373
9419
|
<h1>Skiff Debug</h1>
|
|
9374
|
-
<div class="sub">宁静号 trajectory 子集角色问答页(v1.25.
|
|
9420
|
+
<div class="sub">宁静号 trajectory 子集角色问答页(v1.25.2 实验性)— 走 DSH agent-loop 会话核心,轨迹与 WebUI 同源</div>
|
|
9421
|
+
<div class="ccc">CCC: ${escapeHtml(cccRoot)}</div>
|
|
9375
9422
|
<label for="role">角色</label>
|
|
9376
9423
|
<select id="role">${roleOptions || "<option value=\"\">(未配置角色)</option>"}</select>
|
|
9377
9424
|
<label for="q">问题</label>
|
|
@@ -9431,15 +9478,20 @@ function escapeHtml(s) {
|
|
|
9431
9478
|
}
|
|
9432
9479
|
/**
|
|
9433
9480
|
* 启动调试问答服务(单实例;重复启动幂等返回既有实例)。
|
|
9434
|
-
*
|
|
9481
|
+
*
|
|
9482
|
+
* **CCC 绑定(v1.25.2 用户指出)**:服务绑定一个 CCC root(调用方 resolveSkiffRoot
|
|
9483
|
+
* 解析:live 会话中**含 skiff.roles 的 CCC 优先**);角色配置**每次请求实时读取**
|
|
9484
|
+
* (不缓存快照)——改 .opencode/serenity.json 后刷新页面即生效,无需重启服务。
|
|
9485
|
+
* 页面顶部显示绑定的 CCC root,绑定可核对。
|
|
9486
|
+
*
|
|
9487
|
+
* @param root 绑定的 CCC 根(角色配置读取 + skiff agent cwd)
|
|
9435
9488
|
* @param port 调试端口(仅 127.0.0.1)
|
|
9436
9489
|
* @param webPort 主 WebUI 端口(WebUI 链接)
|
|
9437
9490
|
*/
|
|
9438
9491
|
async function startSkiffDebugServer(ctx, root, port, webPort) {
|
|
9439
9492
|
if (active) return;
|
|
9440
|
-
const roles = readSkiffRoles(root);
|
|
9441
9493
|
const server = createServer((req, res) => {
|
|
9442
|
-
handle(ctx, root,
|
|
9494
|
+
handle(ctx, root, webPort, req, res);
|
|
9443
9495
|
});
|
|
9444
9496
|
await new Promise((resolve, reject) => {
|
|
9445
9497
|
server.once("error", reject);
|
|
@@ -9449,7 +9501,7 @@ async function startSkiffDebugServer(ctx, root, port, webPort) {
|
|
|
9449
9501
|
server,
|
|
9450
9502
|
port
|
|
9451
9503
|
};
|
|
9452
|
-
console.log(`[serenity-hooks] ✓ Skiff 调试问答页: http://127.0.0.1:${port}
|
|
9504
|
+
console.log(`[serenity-hooks] ✓ Skiff 调试问答页: http://127.0.0.1:${port}(CCC: ${root},WebUI: ${webPort})`);
|
|
9453
9505
|
}
|
|
9454
9506
|
function stopSkiffDebugServer() {
|
|
9455
9507
|
if (!active) return;
|
|
@@ -9458,11 +9510,11 @@ function stopSkiffDebugServer() {
|
|
|
9458
9510
|
} catch {}
|
|
9459
9511
|
active = null;
|
|
9460
9512
|
}
|
|
9461
|
-
async function handle(ctx, root,
|
|
9513
|
+
async function handle(ctx, root, webPort, req, res) {
|
|
9462
9514
|
try {
|
|
9463
9515
|
const url = (req.url ?? "/").split("?")[0] ?? "/";
|
|
9464
9516
|
if (req.method === "GET" && url === "/") {
|
|
9465
|
-
sendHtml(res, skiffDebugPage(
|
|
9517
|
+
sendHtml(res, skiffDebugPage(readSkiffRoles(root), root, webPort));
|
|
9466
9518
|
return;
|
|
9467
9519
|
}
|
|
9468
9520
|
if (req.method === "POST" && url === "/ask") {
|
|
@@ -9477,7 +9529,7 @@ async function handle(ctx, root, roles, webPort, req, res) {
|
|
|
9477
9529
|
}
|
|
9478
9530
|
const roleName = typeof body.role === "string" ? body.role : "";
|
|
9479
9531
|
const question = typeof body.question === "string" ? body.question : "";
|
|
9480
|
-
const role =
|
|
9532
|
+
const role = readSkiffRoles(root).get(roleName);
|
|
9481
9533
|
if (!roleName || !role) {
|
|
9482
9534
|
sendJson(res, 400, { error: `unknown role: ${roleName}` });
|
|
9483
9535
|
return;
|
|
@@ -9606,21 +9658,28 @@ function registerSkiff(ctx) {
|
|
|
9606
9658
|
} catch {}
|
|
9607
9659
|
sync();
|
|
9608
9660
|
}
|
|
9609
|
-
/**
|
|
9661
|
+
/**
|
|
9662
|
+
* 解析 Skiff 调试服务绑定的 CCC 根(v1.25.2 用户指出:skiff 必须绑定 CCC):
|
|
9663
|
+
* ① live 会话中**配置了 skiff.roles 的 CCC 优先**(用户认知中的绑定目标)
|
|
9664
|
+
* ② 回退进程 cwd 上溯 .serenity
|
|
9665
|
+
* ③ 再回退任一 live 会话的 CCC
|
|
9666
|
+
*/
|
|
9610
9667
|
function resolveSkiffRoot(ctx) {
|
|
9611
|
-
const
|
|
9612
|
-
if (fromCwd) return fromCwd;
|
|
9668
|
+
const liveRoots = [];
|
|
9613
9669
|
try {
|
|
9614
9670
|
const sessions = ctx.sessions;
|
|
9615
9671
|
for (const s of sessions?.list?.() ?? []) {
|
|
9616
9672
|
const cwd = s?.header?.cwd;
|
|
9617
9673
|
if (typeof cwd === "string") {
|
|
9618
9674
|
const r = findSerenityRoot(cwd);
|
|
9619
|
-
if (r
|
|
9675
|
+
if (r && !liveRoots.includes(r)) liveRoots.push(r);
|
|
9620
9676
|
}
|
|
9621
9677
|
}
|
|
9622
9678
|
} catch {}
|
|
9623
|
-
return
|
|
9679
|
+
for (const r of liveRoots) if (readSkiffRoles(r).size > 0) return r;
|
|
9680
|
+
const fromCwd = findSerenityRoot(process.cwd());
|
|
9681
|
+
if (fromCwd) return fromCwd;
|
|
9682
|
+
return liveRoots[0] ?? null;
|
|
9624
9683
|
}
|
|
9625
9684
|
/** 主 WebUI 端口(WebUI 链接;webServer 未装配回退 3080) */
|
|
9626
9685
|
function readWebPort(ctx) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.3",
|
|
4
4
|
"description": "宁静号 ACC harness — Native Cordis 插件(DeepSeek Harness 运行时)。真实 DSH 工具注册(cc_fs/session/acc_msm 等 9 工具)+ 拦截缝机械约束(safe-mode/路径守卫/会话落盘)+ 系统提示词注入(ACC/CCE/Constraints/SKILL/Session 五块)。适配 DSH 公开版(deepseek-ai/deepseek-harness 0.1.0-rc)。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|