@shgroup/dsh-serenity-hooks 1.25.11 → 1.26.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/dsh.plugin.json +1 -1
- package/lib/client.js +31 -0
- package/lib/index.js +381 -55
- 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.
|
|
3
|
+
"version": "1.26.0",
|
|
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/client.js
CHANGED
|
@@ -3895,6 +3895,9 @@ Instruction-following style:
|
|
|
3895
3895
|
const setSkiffPort = (v) => {
|
|
3896
3896
|
scope.set("skiffDebugPort", Math.min(65535, Math.max(1024, Math.round(v))));
|
|
3897
3897
|
};
|
|
3898
|
+
const setAcpPort = (v) => {
|
|
3899
|
+
scope.set("acpHttpPort", Math.min(65535, Math.max(1024, Math.round(v))));
|
|
3900
|
+
};
|
|
3898
3901
|
if (!ready) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3899
3902
|
className: "ss-section",
|
|
3900
3903
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -3908,6 +3911,8 @@ Instruction-following style:
|
|
|
3908
3911
|
const namingOn = value?.namingEnabled ?? true;
|
|
3909
3912
|
const skiffOn = value?.skiffEnabled ?? false;
|
|
3910
3913
|
const skiffPort = value?.skiffDebugPort ?? 3099;
|
|
3914
|
+
const acpOn = value?.acpEnabled ?? false;
|
|
3915
|
+
const acpPort = value?.acpHttpPort ?? 3100;
|
|
3911
3916
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3912
3917
|
className: "ss-section",
|
|
3913
3918
|
children: [
|
|
@@ -3996,6 +4001,32 @@ Instruction-following style:
|
|
|
3996
4001
|
})
|
|
3997
4002
|
}) })]
|
|
3998
4003
|
}),
|
|
4004
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Group, {
|
|
4005
|
+
title: "ACP",
|
|
4006
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
|
|
4007
|
+
title: "ACP JSON-RPC 端点",
|
|
4008
|
+
desc: "程序化调用 Skiff 角色(指定 CCC+角色+会话;实验性,仅监听 127.0.0.1)",
|
|
4009
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
|
|
4010
|
+
checked: acpOn,
|
|
4011
|
+
onChange: (on) => toggle("acpEnabled", on)
|
|
4012
|
+
})
|
|
4013
|
+
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
|
|
4014
|
+
title: "HTTP 端口",
|
|
4015
|
+
desc: "ACP JSON-RPC 端口(1024 ~ 65535,默认 3100)",
|
|
4016
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4017
|
+
className: "ss-threshold",
|
|
4018
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4019
|
+
className: "ss-portInput",
|
|
4020
|
+
type: "number",
|
|
4021
|
+
min: 1024,
|
|
4022
|
+
max: 65535,
|
|
4023
|
+
value: acpPort,
|
|
4024
|
+
disabled: !acpOn,
|
|
4025
|
+
onChange: (e) => setAcpPort(Number(e.target.value))
|
|
4026
|
+
})
|
|
4027
|
+
})
|
|
4028
|
+
}) })]
|
|
4029
|
+
}),
|
|
3999
4030
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4000
4031
|
className: "ss-group",
|
|
4001
4032
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
package/lib/index.js
CHANGED
|
@@ -736,6 +736,13 @@ function registerSkiffSession$1(sessionId, role, ccc) {
|
|
|
736
736
|
ccc
|
|
737
737
|
});
|
|
738
738
|
}
|
|
739
|
+
function unregisterSkiffSession$1(sessionId) {
|
|
740
|
+
skiffSessions.delete(sessionId);
|
|
741
|
+
}
|
|
742
|
+
/** 测试/调试:注册表快照(sessionId → {role, ccc}) */
|
|
743
|
+
function skiffSessionSnapshot$1() {
|
|
744
|
+
return new Map(skiffSessions);
|
|
745
|
+
}
|
|
739
746
|
//#endregion
|
|
740
747
|
//#region src/seams/guards.ts
|
|
741
748
|
/**
|
|
@@ -2394,6 +2401,19 @@ function registerSkiffSession(sessionId, role, ccc, agent) {
|
|
|
2394
2401
|
skiffAgents.set(sessionId, agent);
|
|
2395
2402
|
registerSkiffSession$1(sessionId, role, ccc);
|
|
2396
2403
|
}
|
|
2404
|
+
function unregisterSkiffSession(sessionId) {
|
|
2405
|
+
skiffAgents.delete(sessionId);
|
|
2406
|
+
unregisterSkiffSession$1(sessionId);
|
|
2407
|
+
}
|
|
2408
|
+
/** 测试/调试:注册表快照(sessionId → {role} 兼容展示) */
|
|
2409
|
+
function skiffSessionSnapshot() {
|
|
2410
|
+
const out = /* @__PURE__ */ new Map();
|
|
2411
|
+
for (const [id, b] of skiffSessionSnapshot$1()) out.set(id, {
|
|
2412
|
+
role: b.role,
|
|
2413
|
+
ccc: b.ccc
|
|
2414
|
+
});
|
|
2415
|
+
return out;
|
|
2416
|
+
}
|
|
2397
2417
|
/** Skiff agent 挂载的 DSH preset(v1.25.3 修复:read/grep/glob 等平台工具由 preset 决定工具面;
|
|
2398
2418
|
* handyman 经 composeFrom 继承父、skiff 无父上下文——直接挂 DSH 默认 standard preset;
|
|
2399
2419
|
* guard 角色白名单再按角色过滤可见/可用面——白名单外工具仍 deny) */
|
|
@@ -5237,7 +5257,9 @@ const simpleSettingsSchema = z.object({
|
|
|
5237
5257
|
rebuildThreshold: z.number().min(.01).max(1).default(.9),
|
|
5238
5258
|
namingEnabled: z.boolean().default(true),
|
|
5239
5259
|
skiffEnabled: z.boolean().default(false),
|
|
5240
|
-
skiffDebugPort: z.number().min(1024).max(65535).default(3099)
|
|
5260
|
+
skiffDebugPort: z.number().min(1024).max(65535).default(3099),
|
|
5261
|
+
acpEnabled: z.boolean().default(false),
|
|
5262
|
+
acpHttpPort: z.number().min(1024).max(65535).default(3100)
|
|
5241
5263
|
});
|
|
5242
5264
|
/** 从插件 Config 提取 entry 默认(settings base 层) */
|
|
5243
5265
|
function entryDefaults(config) {
|
|
@@ -5247,7 +5269,9 @@ function entryDefaults(config) {
|
|
|
5247
5269
|
rebuildThreshold: config.rebuild?.thresholdRatio ?? .9,
|
|
5248
5270
|
namingEnabled: config.naming?.enabled ?? true,
|
|
5249
5271
|
skiffEnabled: config.skiff?.enabled ?? false,
|
|
5250
|
-
skiffDebugPort: config.skiff?.debugPort ?? 3099
|
|
5272
|
+
skiffDebugPort: config.skiff?.debugPort ?? 3099,
|
|
5273
|
+
acpEnabled: config.acp?.enabled ?? false,
|
|
5274
|
+
acpHttpPort: config.acp?.httpPort ?? 3100
|
|
5251
5275
|
};
|
|
5252
5276
|
}
|
|
5253
5277
|
/** 运行时源(installSettingsSection 注入:settings scope 或 entry fallback) */
|
|
@@ -5260,7 +5284,9 @@ function defaultSimpleSettings() {
|
|
|
5260
5284
|
rebuildThreshold: .9,
|
|
5261
5285
|
namingEnabled: true,
|
|
5262
5286
|
skiffEnabled: false,
|
|
5263
|
-
skiffDebugPort: 3099
|
|
5287
|
+
skiffDebugPort: 3099,
|
|
5288
|
+
acpEnabled: false,
|
|
5289
|
+
acpHttpPort: 3100
|
|
5264
5290
|
};
|
|
5265
5291
|
}
|
|
5266
5292
|
/**
|
|
@@ -6898,7 +6924,11 @@ dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页)
|
|
|
6898
6924
|
|
|
6899
6925
|
── 运行 ──
|
|
6900
6926
|
调试:设置面板「Serenity」页 Skiff 区块开启 → http://127.0.0.1:<debugPort> 问答页实测
|
|
6901
|
-
|
|
6927
|
+
ACP 程序化面(v1.26.0):设置面板「ACP」区块开启 → HTTP JSON-RPC 端点(默认 3100)——
|
|
6928
|
+
POST / {jsonrpc, id, method, params}
|
|
6929
|
+
session/new {ccc, role, sessionId?} → 创建/延续会话(sessionId 可选=进程内追问)
|
|
6930
|
+
session/prompt {sessionId, question} → {answer, trajectory}
|
|
6931
|
+
session/cancel / session/close / session/list / request_permission(恒 allow)
|
|
6902
6932
|
启停 = 人工(设置面板开关,不随插件加载自动启动);未开启零资源占用
|
|
6903
6933
|
生效机制:角色配置**实时读取**(改 .opencode/serenity.json → 刷新调试页即生效);
|
|
6904
6934
|
改配置后用 skiff_admin apply 做显式校验 + 应用确认(绑定 CCC + 角色清单)
|
|
@@ -8298,7 +8328,7 @@ function saveFileToTmp(root, fileName, data) {
|
|
|
8298
8328
|
writeFileSync(join(dir, filename), bytes);
|
|
8299
8329
|
return `${FILE_UPLOAD_DIR}/${filename}`;
|
|
8300
8330
|
}
|
|
8301
|
-
function readBody$
|
|
8331
|
+
function readBody$3(req, maxBytes) {
|
|
8302
8332
|
return new Promise((resolve, reject) => {
|
|
8303
8333
|
let data = "";
|
|
8304
8334
|
req.on("data", (chunk) => {
|
|
@@ -8312,7 +8342,7 @@ function readBody$2(req, maxBytes) {
|
|
|
8312
8342
|
req.on("error", reject);
|
|
8313
8343
|
});
|
|
8314
8344
|
}
|
|
8315
|
-
function sendJson$
|
|
8345
|
+
function sendJson$2(res, code, body) {
|
|
8316
8346
|
const payload = JSON.stringify(body);
|
|
8317
8347
|
res.writeHead(code, {
|
|
8318
8348
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -8350,7 +8380,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8350
8380
|
handler: async (req, res) => {
|
|
8351
8381
|
try {
|
|
8352
8382
|
if (req.method !== "GET") {
|
|
8353
|
-
sendJson$
|
|
8383
|
+
sendJson$2(res, 405, { error: "method not allowed" });
|
|
8354
8384
|
return;
|
|
8355
8385
|
}
|
|
8356
8386
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
@@ -8359,12 +8389,12 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8359
8389
|
workspace: url.searchParams.get("workspace") ?? void 0
|
|
8360
8390
|
}));
|
|
8361
8391
|
if (!root) {
|
|
8362
|
-
sendJson$
|
|
8392
|
+
sendJson$2(res, 200, { handymen: [] });
|
|
8363
8393
|
return;
|
|
8364
8394
|
}
|
|
8365
|
-
sendJson$
|
|
8395
|
+
sendJson$2(res, 200, { handymen: listActiveHandymen(root) });
|
|
8366
8396
|
} catch (err) {
|
|
8367
|
-
sendJson$
|
|
8397
|
+
sendJson$2(res, 400, { error: err.message ?? String(err) });
|
|
8368
8398
|
}
|
|
8369
8399
|
}
|
|
8370
8400
|
});
|
|
@@ -8374,14 +8404,14 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8374
8404
|
handler: async (req, res) => {
|
|
8375
8405
|
try {
|
|
8376
8406
|
if (req.method !== "POST") {
|
|
8377
|
-
sendJson$
|
|
8407
|
+
sendJson$2(res, 405, { error: "method not allowed" });
|
|
8378
8408
|
return;
|
|
8379
8409
|
}
|
|
8380
8410
|
if (req.headers["x-serenity-ui"] !== "1") {
|
|
8381
|
-
sendJson$
|
|
8411
|
+
sendJson$2(res, 403, { error: "图片落盘仅限 WebUI(client 专用)" });
|
|
8382
8412
|
return;
|
|
8383
8413
|
}
|
|
8384
|
-
const raw = await readBody$
|
|
8414
|
+
const raw = await readBody$3(req, 20971520);
|
|
8385
8415
|
const body = JSON.parse(raw);
|
|
8386
8416
|
const workspace = resolveWorkspace(ctx, {
|
|
8387
8417
|
sessionId: body.sessionId,
|
|
@@ -8389,12 +8419,12 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8389
8419
|
});
|
|
8390
8420
|
const root = findSerenityRoot(workspace);
|
|
8391
8421
|
if (!root) {
|
|
8392
|
-
sendJson$
|
|
8422
|
+
sendJson$2(res, 404, { error: `no CCC found from workspace: ${workspace}` });
|
|
8393
8423
|
return;
|
|
8394
8424
|
}
|
|
8395
|
-
sendJson$
|
|
8425
|
+
sendJson$2(res, 200, { path: saveImageToTmp(root, body.mediaType ?? "", body.data ?? "") });
|
|
8396
8426
|
} catch (err) {
|
|
8397
|
-
sendJson$
|
|
8427
|
+
sendJson$2(res, 400, { error: err.message ?? String(err) });
|
|
8398
8428
|
}
|
|
8399
8429
|
}
|
|
8400
8430
|
});
|
|
@@ -8404,14 +8434,14 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8404
8434
|
handler: async (req, res) => {
|
|
8405
8435
|
try {
|
|
8406
8436
|
if (req.method !== "POST") {
|
|
8407
|
-
sendJson$
|
|
8437
|
+
sendJson$2(res, 405, { error: "method not allowed" });
|
|
8408
8438
|
return;
|
|
8409
8439
|
}
|
|
8410
8440
|
if (req.headers["x-serenity-ui"] !== "1") {
|
|
8411
|
-
sendJson$
|
|
8441
|
+
sendJson$2(res, 403, { error: "文件落盘仅限 WebUI(client 专用)" });
|
|
8412
8442
|
return;
|
|
8413
8443
|
}
|
|
8414
|
-
const raw = await readBody$
|
|
8444
|
+
const raw = await readBody$3(req, 20971520);
|
|
8415
8445
|
const body = JSON.parse(raw);
|
|
8416
8446
|
const workspace = resolveWorkspace(ctx, {
|
|
8417
8447
|
sessionId: body.sessionId,
|
|
@@ -8419,12 +8449,12 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8419
8449
|
});
|
|
8420
8450
|
const root = findSerenityRoot(workspace);
|
|
8421
8451
|
if (!root) {
|
|
8422
|
-
sendJson$
|
|
8452
|
+
sendJson$2(res, 404, { error: `no CCC found from workspace: ${workspace}` });
|
|
8423
8453
|
return;
|
|
8424
8454
|
}
|
|
8425
|
-
sendJson$
|
|
8455
|
+
sendJson$2(res, 200, { path: saveFileToTmp(root, body.name ?? "", body.data ?? "") });
|
|
8426
8456
|
} catch (err) {
|
|
8427
|
-
sendJson$
|
|
8457
|
+
sendJson$2(res, 400, { error: err.message ?? String(err) });
|
|
8428
8458
|
}
|
|
8429
8459
|
}
|
|
8430
8460
|
});
|
|
@@ -8440,7 +8470,7 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8440
8470
|
workspace: url.searchParams.get("workspace") ?? void 0
|
|
8441
8471
|
}), configPaths);
|
|
8442
8472
|
const runtime = ctx.get("codeRuntime");
|
|
8443
|
-
sendJson$
|
|
8473
|
+
sendJson$2(res, 200, {
|
|
8444
8474
|
...status,
|
|
8445
8475
|
...runtime === void 0 ? { codeRuntime: null } : { codeRuntime: {
|
|
8446
8476
|
language: runtime.language,
|
|
@@ -8451,10 +8481,10 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8451
8481
|
}
|
|
8452
8482
|
if (req.method === "POST") {
|
|
8453
8483
|
if (req.headers["x-serenity-ui"] !== "1") {
|
|
8454
|
-
sendJson$
|
|
8484
|
+
sendJson$2(res, 403, { error: "safe-mode 切换仅限 WebUI(agent 不可自行开关)" });
|
|
8455
8485
|
return;
|
|
8456
8486
|
}
|
|
8457
|
-
const raw = await readBody$
|
|
8487
|
+
const raw = await readBody$3(req, 65536);
|
|
8458
8488
|
const body = JSON.parse(raw);
|
|
8459
8489
|
const workspace = resolveWorkspace(ctx, {
|
|
8460
8490
|
sessionId: body.sessionId,
|
|
@@ -8462,19 +8492,19 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8462
8492
|
});
|
|
8463
8493
|
const root = findSerenityRoot(workspace);
|
|
8464
8494
|
if (!root) {
|
|
8465
|
-
sendJson$
|
|
8495
|
+
sendJson$2(res, 404, { error: `no CCC found from workspace: ${workspace}` });
|
|
8466
8496
|
return;
|
|
8467
8497
|
}
|
|
8468
8498
|
const result = setSafeMode(root, body.on === true);
|
|
8469
|
-
sendJson$
|
|
8499
|
+
sendJson$2(res, 200, {
|
|
8470
8500
|
...getStatus(workspace, configPaths),
|
|
8471
8501
|
...result
|
|
8472
8502
|
});
|
|
8473
8503
|
return;
|
|
8474
8504
|
}
|
|
8475
|
-
sendJson$
|
|
8505
|
+
sendJson$2(res, 405, { error: "method not allowed" });
|
|
8476
8506
|
} catch (err) {
|
|
8477
|
-
sendJson$
|
|
8507
|
+
sendJson$2(res, 400, { error: err.message ?? String(err) });
|
|
8478
8508
|
}
|
|
8479
8509
|
}
|
|
8480
8510
|
});
|
|
@@ -8484,25 +8514,25 @@ function registerStatusApi(ctx, opts = {}) {
|
|
|
8484
8514
|
handler: async (req, res) => {
|
|
8485
8515
|
try {
|
|
8486
8516
|
if (req.headers["x-serenity-ui"] !== "1") {
|
|
8487
|
-
sendJson$
|
|
8517
|
+
sendJson$2(res, 403, { error: "高级设定仅限 WebUI(client 专用)" });
|
|
8488
8518
|
return;
|
|
8489
8519
|
}
|
|
8490
8520
|
if (req.method === "GET") {
|
|
8491
|
-
sendJson$
|
|
8521
|
+
sendJson$2(res, 200, { config: toWire(readAdvancedSettings()) });
|
|
8492
8522
|
return;
|
|
8493
8523
|
}
|
|
8494
8524
|
if (req.method === "PUT") {
|
|
8495
|
-
const raw = await readBody$
|
|
8525
|
+
const raw = await readBody$3(req, 131072);
|
|
8496
8526
|
const saved = applyWirePatch(JSON.parse(raw).config ?? {});
|
|
8497
8527
|
try {
|
|
8498
8528
|
ctx.emit?.("serenity/config-updated");
|
|
8499
8529
|
} catch {}
|
|
8500
|
-
sendJson$
|
|
8530
|
+
sendJson$2(res, 200, { config: toWire(saved) });
|
|
8501
8531
|
return;
|
|
8502
8532
|
}
|
|
8503
|
-
sendJson$
|
|
8533
|
+
sendJson$2(res, 405, { error: "method not allowed" });
|
|
8504
8534
|
} catch (err) {
|
|
8505
|
-
sendJson$
|
|
8535
|
+
sendJson$2(res, 400, { error: err.message ?? String(err) });
|
|
8506
8536
|
}
|
|
8507
8537
|
}
|
|
8508
8538
|
});
|
|
@@ -8958,7 +8988,7 @@ function buildProxyHeaders(reqHeaders, mainPort, bodyOverride) {
|
|
|
8958
8988
|
//#endregion
|
|
8959
8989
|
//#region src/gateway.ts
|
|
8960
8990
|
/** 读取请求体(≤ maxBytes;超限 reject) */
|
|
8961
|
-
function readBody$
|
|
8991
|
+
function readBody$2(req, maxBytes) {
|
|
8962
8992
|
return new Promise((resolve, reject) => {
|
|
8963
8993
|
let data = "";
|
|
8964
8994
|
req.on("data", (chunk) => {
|
|
@@ -9091,7 +9121,7 @@ function startGateway(config, getAccounts) {
|
|
|
9091
9121
|
if (session !== void 0) {
|
|
9092
9122
|
if (req.method === "POST" && url.pathname === "/api/workspace.create") {
|
|
9093
9123
|
if (!allowWorkspaceCreate) {
|
|
9094
|
-
readBody$
|
|
9124
|
+
readBody$2(req, 131072).then((body) => {
|
|
9095
9125
|
let rpcId = "unknown";
|
|
9096
9126
|
try {
|
|
9097
9127
|
const parsed = JSON.parse(body);
|
|
@@ -9127,7 +9157,7 @@ function startGateway(config, getAccounts) {
|
|
|
9127
9157
|
});
|
|
9128
9158
|
return;
|
|
9129
9159
|
}
|
|
9130
|
-
readBody$
|
|
9160
|
+
readBody$2(req, 131072).then((body) => {
|
|
9131
9161
|
let rpcId = "unknown";
|
|
9132
9162
|
let path;
|
|
9133
9163
|
try {
|
|
@@ -11079,8 +11109,8 @@ x.lex;
|
|
|
11079
11109
|
* 实验性质:未开启时零资源占用(无监听、无 agent 创建)。
|
|
11080
11110
|
*/
|
|
11081
11111
|
/** 运行中的调试服务(单实例;进程级) */
|
|
11082
|
-
let active = null;
|
|
11083
|
-
function readBody(req) {
|
|
11112
|
+
let active$1 = null;
|
|
11113
|
+
function readBody$1(req) {
|
|
11084
11114
|
return new Promise((resolve, reject) => {
|
|
11085
11115
|
const chunks = [];
|
|
11086
11116
|
req.on("data", (c) => chunks.push(c));
|
|
@@ -11088,7 +11118,7 @@ function readBody(req) {
|
|
|
11088
11118
|
req.on("error", reject);
|
|
11089
11119
|
});
|
|
11090
11120
|
}
|
|
11091
|
-
function sendJson(res, status, payload) {
|
|
11121
|
+
function sendJson$1(res, status, payload) {
|
|
11092
11122
|
const body = JSON.stringify(payload);
|
|
11093
11123
|
res.writeHead(status, {
|
|
11094
11124
|
"Content-Type": "application/json; charset=utf-8",
|
|
@@ -11356,28 +11386,28 @@ function renderSkiffMarkdown(raw) {
|
|
|
11356
11386
|
* @param webPort 主 WebUI 端口(WebUI 链接)
|
|
11357
11387
|
*/
|
|
11358
11388
|
async function startSkiffDebugServer(ctx, root, port, webPort) {
|
|
11359
|
-
if (active) return;
|
|
11389
|
+
if (active$1) return;
|
|
11360
11390
|
const server = createServer((req, res) => {
|
|
11361
|
-
handle(ctx, root, webPort, req, res);
|
|
11391
|
+
handle$1(ctx, root, webPort, req, res);
|
|
11362
11392
|
});
|
|
11363
11393
|
await new Promise((resolve, reject) => {
|
|
11364
11394
|
server.once("error", reject);
|
|
11365
11395
|
server.listen(port, "127.0.0.1", () => resolve());
|
|
11366
11396
|
});
|
|
11367
|
-
active = {
|
|
11397
|
+
active$1 = {
|
|
11368
11398
|
server,
|
|
11369
11399
|
port
|
|
11370
11400
|
};
|
|
11371
11401
|
console.log(`[serenity-hooks] ✓ Skiff 调试问答页: http://127.0.0.1:${port}(默认 CCC: ${root},WebUI: ${webPort})`);
|
|
11372
11402
|
}
|
|
11373
11403
|
function stopSkiffDebugServer() {
|
|
11374
|
-
if (!active) return;
|
|
11404
|
+
if (!active$1) return;
|
|
11375
11405
|
try {
|
|
11376
|
-
active.server.close();
|
|
11406
|
+
active$1.server.close();
|
|
11377
11407
|
} catch {}
|
|
11378
|
-
active = null;
|
|
11408
|
+
active$1 = null;
|
|
11379
11409
|
}
|
|
11380
|
-
async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
11410
|
+
async function handle$1(ctx, defaultRoot, webPort, req, res) {
|
|
11381
11411
|
try {
|
|
11382
11412
|
const url = (req.url ?? "/").split("?")[0] ?? "/";
|
|
11383
11413
|
if (req.method === "GET" && url === "/") {
|
|
@@ -11387,11 +11417,11 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11387
11417
|
if (req.method === "POST" && url === "/ask") {
|
|
11388
11418
|
let body;
|
|
11389
11419
|
try {
|
|
11390
|
-
const parsed = JSON.parse(await readBody(req));
|
|
11420
|
+
const parsed = JSON.parse(await readBody$1(req));
|
|
11391
11421
|
if (parsed === null || typeof parsed !== "object") throw new Error("not an object");
|
|
11392
11422
|
body = parsed;
|
|
11393
11423
|
} catch {
|
|
11394
|
-
sendJson(res, 400, { error: "invalid JSON body" });
|
|
11424
|
+
sendJson$1(res, 400, { error: "invalid JSON body" });
|
|
11395
11425
|
return;
|
|
11396
11426
|
}
|
|
11397
11427
|
const ccc = typeof body.ccc === "string" && body.ccc !== "" ? body.ccc : defaultRoot;
|
|
@@ -11400,11 +11430,11 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11400
11430
|
const sessionId = typeof body.sessionId === "string" && body.sessionId !== "" ? body.sessionId : void 0;
|
|
11401
11431
|
const role = readSkiffRoles(ccc).get(roleName);
|
|
11402
11432
|
if (!roleName || !role) {
|
|
11403
|
-
sendJson(res, 400, { error: `unknown role: ${roleName} (ccc: ${ccc})` });
|
|
11433
|
+
sendJson$1(res, 400, { error: `unknown role: ${roleName} (ccc: ${ccc})` });
|
|
11404
11434
|
return;
|
|
11405
11435
|
}
|
|
11406
11436
|
if (!question.trim()) {
|
|
11407
|
-
sendJson(res, 400, { error: "empty question" });
|
|
11437
|
+
sendJson$1(res, 400, { error: "empty question" });
|
|
11408
11438
|
return;
|
|
11409
11439
|
}
|
|
11410
11440
|
let agent;
|
|
@@ -11413,11 +11443,11 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11413
11443
|
const info = skiffSessionInfo(sessionId);
|
|
11414
11444
|
const live = getSkiffAgent(sessionId);
|
|
11415
11445
|
if (!info || !live) {
|
|
11416
|
-
sendJson(res, 400, { error: "session is not recoverable (process restarted or session unknown) — start a new conversation" });
|
|
11446
|
+
sendJson$1(res, 400, { error: "session is not recoverable (process restarted or session unknown) — start a new conversation" });
|
|
11417
11447
|
return;
|
|
11418
11448
|
}
|
|
11419
11449
|
if (info.role !== roleName || info.ccc !== ccc) {
|
|
11420
|
-
sendJson(res, 400, { error: `session "${sessionId}" belongs to role "${info.role}" in another CCC — start a new conversation` });
|
|
11450
|
+
sendJson$1(res, 400, { error: `session "${sessionId}" belongs to role "${info.role}" in another CCC — start a new conversation` });
|
|
11421
11451
|
return;
|
|
11422
11452
|
}
|
|
11423
11453
|
agent = live;
|
|
@@ -11426,7 +11456,7 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11426
11456
|
const hc = readHandymanConfig(ccc);
|
|
11427
11457
|
if (!agent) agent = (await createSkiffAgent(ctx, ccc, roleName, role, hc?.defaultModel)).agent;
|
|
11428
11458
|
const result = await askSkiff(ctx, agent, question, 0);
|
|
11429
|
-
sendJson(res, 200, {
|
|
11459
|
+
sendJson$1(res, 200, {
|
|
11430
11460
|
answer: result.answer,
|
|
11431
11461
|
answer_html: renderSkiffMarkdown(result.answer),
|
|
11432
11462
|
sessionId: result.sessionId,
|
|
@@ -11435,6 +11465,273 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11435
11465
|
});
|
|
11436
11466
|
return;
|
|
11437
11467
|
}
|
|
11468
|
+
sendJson$1(res, 404, { error: "not found" });
|
|
11469
|
+
} catch (err) {
|
|
11470
|
+
sendJson$1(res, 500, { error: err?.message ?? String(err) });
|
|
11471
|
+
}
|
|
11472
|
+
}
|
|
11473
|
+
//#endregion
|
|
11474
|
+
//#region src/acp-core.ts
|
|
11475
|
+
/** JSON-RPC 错误码(对齐标准) */
|
|
11476
|
+
const RPC_ERROR = {
|
|
11477
|
+
PARSE: -32700,
|
|
11478
|
+
INVALID_REQUEST: -32600,
|
|
11479
|
+
METHOD_NOT_FOUND: -32601,
|
|
11480
|
+
INVALID_PARAMS: -32602,
|
|
11481
|
+
INTERNAL: -32603
|
|
11482
|
+
};
|
|
11483
|
+
/**
|
|
11484
|
+
* ACP 会话管理器:持有 skiff agent 会话(与 skiff-core 注册表同源——
|
|
11485
|
+
* 创建走 createSkiffAgent(自动注册),关闭走 unregisterSkiffSession)。
|
|
11486
|
+
* 传输无关:HTTP 端点/企业微信桥/未来 stdio 都调 handle(method, params)。
|
|
11487
|
+
*/
|
|
11488
|
+
var AcpServer = class {
|
|
11489
|
+
ctx;
|
|
11490
|
+
constructor(ctx) {
|
|
11491
|
+
this.ctx = ctx;
|
|
11492
|
+
}
|
|
11493
|
+
/** 实现级方法调度(返回 result 或抛 JSON-RPC 语义错误) */
|
|
11494
|
+
async handle(method, params) {
|
|
11495
|
+
switch (method) {
|
|
11496
|
+
case "initialize": return this.initialize();
|
|
11497
|
+
case "authenticate": return {};
|
|
11498
|
+
case "session/new": return this.newSession(params);
|
|
11499
|
+
case "session/list": return this.listSessions();
|
|
11500
|
+
case "session/prompt": return this.prompt(params);
|
|
11501
|
+
case "session/cancel": return this.cancel(params);
|
|
11502
|
+
case "session/close": return this.closeSession(params);
|
|
11503
|
+
case "session/request_permission": return { allow: true };
|
|
11504
|
+
default: throw new RpcMethodError(`method not found: ${method}`);
|
|
11505
|
+
}
|
|
11506
|
+
}
|
|
11507
|
+
initialize() {
|
|
11508
|
+
return {
|
|
11509
|
+
protocolVersion: 1,
|
|
11510
|
+
capabilities: {
|
|
11511
|
+
sessionId: true,
|
|
11512
|
+
ccc: true,
|
|
11513
|
+
role: true
|
|
11514
|
+
},
|
|
11515
|
+
serverInfo: {
|
|
11516
|
+
name: "dsh-serenity-hooks-acp",
|
|
11517
|
+
version: "1.26.0"
|
|
11518
|
+
}
|
|
11519
|
+
};
|
|
11520
|
+
}
|
|
11521
|
+
/**
|
|
11522
|
+
* session/new:{ ccc, role, sessionId? } → 创建或延续 skiff 会话。
|
|
11523
|
+
* 无 sessionId → createSkiffAgent(新会话,continued:false);
|
|
11524
|
+
* 有 → 进程内延续(getSkiffAgent 命中 + role/ccc 绑定校验;未命中 → 错误)。
|
|
11525
|
+
*/
|
|
11526
|
+
async newSession(params) {
|
|
11527
|
+
const ccc = typeof params?.ccc === "string" && params.ccc !== "" ? params.ccc : void 0;
|
|
11528
|
+
const roleName = typeof params?.role === "string" ? params.role : void 0;
|
|
11529
|
+
const sessionId = typeof params?.sessionId === "string" && params.sessionId !== "" ? params.sessionId : void 0;
|
|
11530
|
+
if (!ccc) throw new RpcInvalidParams("session/new requires ccc (CCC root path)");
|
|
11531
|
+
if (!roleName) throw new RpcInvalidParams("session/new requires role");
|
|
11532
|
+
const role = readSkiffRoles(ccc).get(roleName);
|
|
11533
|
+
if (!role) throw new RpcInvalidParams(`unknown role: ${roleName} (ccc: ${ccc})`);
|
|
11534
|
+
if (sessionId) {
|
|
11535
|
+
const info = skiffSessionInfo(sessionId);
|
|
11536
|
+
const live = getSkiffAgent(sessionId);
|
|
11537
|
+
if (!info || !live) throw new RpcInvalidParams("session is not recoverable (process restarted or session unknown) — start a new conversation");
|
|
11538
|
+
if (info.role !== roleName || info.ccc !== ccc) throw new RpcInvalidParams(`session "${sessionId}" belongs to role "${info.role}" in another CCC`);
|
|
11539
|
+
return {
|
|
11540
|
+
sessionId,
|
|
11541
|
+
role: roleName,
|
|
11542
|
+
ccc,
|
|
11543
|
+
continued: true
|
|
11544
|
+
};
|
|
11545
|
+
}
|
|
11546
|
+
const hc = readHandymanConfig(ccc);
|
|
11547
|
+
return {
|
|
11548
|
+
sessionId: (await createSkiffAgent(this.ctx, ccc, roleName, role, hc?.defaultModel)).sessionId,
|
|
11549
|
+
role: roleName,
|
|
11550
|
+
ccc,
|
|
11551
|
+
continued: false
|
|
11552
|
+
};
|
|
11553
|
+
}
|
|
11554
|
+
listSessions() {
|
|
11555
|
+
const sessions = [];
|
|
11556
|
+
for (const [id, b] of skiffSessionSnapshot()) sessions.push({
|
|
11557
|
+
sessionId: id,
|
|
11558
|
+
role: b.role,
|
|
11559
|
+
ccc: b.ccc
|
|
11560
|
+
});
|
|
11561
|
+
return { sessions };
|
|
11562
|
+
}
|
|
11563
|
+
/** session/prompt:{ sessionId, question } → 答案 + 全量轨迹 */
|
|
11564
|
+
async prompt(params) {
|
|
11565
|
+
const sessionId = typeof params?.sessionId === "string" ? params.sessionId : void 0;
|
|
11566
|
+
const question = typeof params?.question === "string" ? params.question : void 0;
|
|
11567
|
+
if (!sessionId) throw new RpcInvalidParams("session/prompt requires sessionId");
|
|
11568
|
+
if (!question?.trim()) throw new RpcInvalidParams("session/prompt requires question");
|
|
11569
|
+
const agent = getSkiffAgent(sessionId);
|
|
11570
|
+
if (!agent) throw new RpcInvalidParams(`unknown session: ${sessionId} (not recoverable — process restarted?)`);
|
|
11571
|
+
const result = await askSkiff(this.ctx, agent, question, 0);
|
|
11572
|
+
return {
|
|
11573
|
+
answer: result.answer,
|
|
11574
|
+
sessionId: result.sessionId,
|
|
11575
|
+
trajectory: result.trajectory
|
|
11576
|
+
};
|
|
11577
|
+
}
|
|
11578
|
+
/** session/cancel:{ sessionId } → 中断当前 prompt(DSH interrupt) */
|
|
11579
|
+
cancel(params) {
|
|
11580
|
+
const sessionId = typeof params?.sessionId === "string" ? params.sessionId : void 0;
|
|
11581
|
+
if (!sessionId) return { cancelled: false };
|
|
11582
|
+
const agent = getSkiffAgent(sessionId);
|
|
11583
|
+
if (!agent) return { cancelled: false };
|
|
11584
|
+
try {
|
|
11585
|
+
agent.interrupt?.();
|
|
11586
|
+
} catch {}
|
|
11587
|
+
return { cancelled: true };
|
|
11588
|
+
}
|
|
11589
|
+
/** session/close:{ sessionId } → 释放 agent + 注册表清理(进程内) */
|
|
11590
|
+
closeSession(params) {
|
|
11591
|
+
const sessionId = typeof params?.sessionId === "string" ? params.sessionId : void 0;
|
|
11592
|
+
if (!sessionId) return { closed: false };
|
|
11593
|
+
if (!getSkiffAgent(sessionId)) return { closed: false };
|
|
11594
|
+
unregisterSkiffSession(sessionId);
|
|
11595
|
+
return { closed: true };
|
|
11596
|
+
}
|
|
11597
|
+
};
|
|
11598
|
+
/** 方法不存在错误(JSON-RPC -32601) */
|
|
11599
|
+
var RpcMethodError = class extends Error {
|
|
11600
|
+
constructor(message) {
|
|
11601
|
+
super(message);
|
|
11602
|
+
this.name = "RpcMethodError";
|
|
11603
|
+
}
|
|
11604
|
+
};
|
|
11605
|
+
/** 参数错误(JSON-RPC -32602) */
|
|
11606
|
+
var RpcInvalidParams = class extends Error {
|
|
11607
|
+
constructor(message) {
|
|
11608
|
+
super(message);
|
|
11609
|
+
this.name = "RpcInvalidParams";
|
|
11610
|
+
}
|
|
11611
|
+
};
|
|
11612
|
+
/**
|
|
11613
|
+
* JSON-RPC 2.0 批处理(单帧):解析请求/通知 → 调 AcpServer.handle → 响应。
|
|
11614
|
+
* 传输无关:acp-http 端点 / 未来 stdio 共用。
|
|
11615
|
+
* @returns 响应帧数组(请求有响应;通知无;解析失败 → 单错误响应)
|
|
11616
|
+
*/
|
|
11617
|
+
async function dispatchRpc(server, frame) {
|
|
11618
|
+
if (frame === null || typeof frame !== "object") return [rpcError(null, RPC_ERROR.INVALID_REQUEST, "invalid request")];
|
|
11619
|
+
const req = frame;
|
|
11620
|
+
if (req.jsonrpc !== "2.0" || typeof req.method !== "string") return [rpcError(req.id ?? null, RPC_ERROR.INVALID_REQUEST, "invalid request")];
|
|
11621
|
+
if (req.id === void 0) {
|
|
11622
|
+
try {
|
|
11623
|
+
await server.handle(req.method, req.params);
|
|
11624
|
+
} catch {}
|
|
11625
|
+
return [];
|
|
11626
|
+
}
|
|
11627
|
+
try {
|
|
11628
|
+
const result = await server.handle(req.method, req.params);
|
|
11629
|
+
return [{
|
|
11630
|
+
jsonrpc: "2.0",
|
|
11631
|
+
id: req.id,
|
|
11632
|
+
result
|
|
11633
|
+
}];
|
|
11634
|
+
} catch (error) {
|
|
11635
|
+
return [rpcError(req.id, rpcCodeOf(error), error instanceof Error ? error.message : String(error))];
|
|
11636
|
+
}
|
|
11637
|
+
}
|
|
11638
|
+
function rpcCodeOf(error) {
|
|
11639
|
+
if (error instanceof RpcMethodError) return RPC_ERROR.METHOD_NOT_FOUND;
|
|
11640
|
+
if (error instanceof RpcInvalidParams) return RPC_ERROR.INVALID_PARAMS;
|
|
11641
|
+
return RPC_ERROR.INTERNAL;
|
|
11642
|
+
}
|
|
11643
|
+
function rpcError(id, code, message) {
|
|
11644
|
+
return {
|
|
11645
|
+
jsonrpc: "2.0",
|
|
11646
|
+
id,
|
|
11647
|
+
error: {
|
|
11648
|
+
code,
|
|
11649
|
+
message
|
|
11650
|
+
}
|
|
11651
|
+
};
|
|
11652
|
+
}
|
|
11653
|
+
//#endregion
|
|
11654
|
+
//#region src/acp-http.ts
|
|
11655
|
+
/**
|
|
11656
|
+
* acp-http.ts — ACP HTTP JSON-RPC 端点(F4c,v1.26.0 实验性)
|
|
11657
|
+
*
|
|
11658
|
+
* node:http 服务(默认关,仅监听 127.0.0.1;启停 = 人工——设置面板「Serenity」
|
|
11659
|
+
* 页 ACP 区块开关,不随插件加载自动启动;仿 skiff 调试服务模式)。
|
|
11660
|
+
*
|
|
11661
|
+
* - POST / → JSON-RPC 2.0 单帧/批处理(body 为 JSON)→ JSON-RPC 响应数组
|
|
11662
|
+
*
|
|
11663
|
+
* 与 skiff 调试页(人工测试面)共享同一会话核心(acp-core → skiff-core);
|
|
11664
|
+
* 企业微信桥(F4c-2)同进程直调 acp-core 处理器函数,不经本端点。
|
|
11665
|
+
* stdio 形态(独立进程部署)后续复用同一 acp-core(官方 NDJSON 帧格式)。
|
|
11666
|
+
*
|
|
11667
|
+
* 实验性质:未开启时零资源占用(无监听、无 agent 创建)。
|
|
11668
|
+
*/
|
|
11669
|
+
/** 运行中的 ACP HTTP 服务(单实例;进程级) */
|
|
11670
|
+
let active = null;
|
|
11671
|
+
function readBody(req) {
|
|
11672
|
+
return new Promise((resolve, reject) => {
|
|
11673
|
+
const chunks = [];
|
|
11674
|
+
req.on("data", (c) => chunks.push(c));
|
|
11675
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
11676
|
+
req.on("error", reject);
|
|
11677
|
+
});
|
|
11678
|
+
}
|
|
11679
|
+
function sendJson(res, status, payload) {
|
|
11680
|
+
const body = JSON.stringify(payload);
|
|
11681
|
+
res.writeHead(status, {
|
|
11682
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
11683
|
+
"Cache-Control": "no-store"
|
|
11684
|
+
});
|
|
11685
|
+
res.end(body);
|
|
11686
|
+
}
|
|
11687
|
+
/**
|
|
11688
|
+
* 启动 ACP HTTP JSON-RPC 端点(单实例;重复启动幂等返回既有实例)。
|
|
11689
|
+
* @param ctx 插件上下文(AcpServer 内部经 skiff-core 创建 agent)
|
|
11690
|
+
* @param port 监听端口(仅 127.0.0.1)
|
|
11691
|
+
*/
|
|
11692
|
+
async function startAcpHttpServer(ctx, port) {
|
|
11693
|
+
if (active) return;
|
|
11694
|
+
const server = createServer((req, res) => {
|
|
11695
|
+
handle(ctx, req, res);
|
|
11696
|
+
});
|
|
11697
|
+
await new Promise((resolve, reject) => {
|
|
11698
|
+
server.once("error", reject);
|
|
11699
|
+
server.listen(port, "127.0.0.1", () => resolve());
|
|
11700
|
+
});
|
|
11701
|
+
active = {
|
|
11702
|
+
server,
|
|
11703
|
+
port
|
|
11704
|
+
};
|
|
11705
|
+
console.log(`[serenity-hooks] ✓ ACP HTTP JSON-RPC: http://127.0.0.1:${port}(session/new 支持 {ccc, role, sessionId?})`);
|
|
11706
|
+
}
|
|
11707
|
+
function stopAcpHttpServer() {
|
|
11708
|
+
if (!active) return;
|
|
11709
|
+
try {
|
|
11710
|
+
active.server.close();
|
|
11711
|
+
} catch {}
|
|
11712
|
+
active = null;
|
|
11713
|
+
}
|
|
11714
|
+
async function handle(ctx, req, res) {
|
|
11715
|
+
try {
|
|
11716
|
+
const url = (req.url ?? "/").split("?")[0] ?? "/";
|
|
11717
|
+
if (req.method === "POST" && url === "/") {
|
|
11718
|
+
let body;
|
|
11719
|
+
try {
|
|
11720
|
+
body = JSON.parse(await readBody(req));
|
|
11721
|
+
} catch {
|
|
11722
|
+
sendJson(res, 400, [{
|
|
11723
|
+
jsonrpc: "2.0",
|
|
11724
|
+
id: null,
|
|
11725
|
+
error: {
|
|
11726
|
+
code: -32700,
|
|
11727
|
+
message: "parse error"
|
|
11728
|
+
}
|
|
11729
|
+
}]);
|
|
11730
|
+
return;
|
|
11731
|
+
}
|
|
11732
|
+
sendJson(res, 200, await dispatchRpc(new AcpServer(ctx), body));
|
|
11733
|
+
return;
|
|
11734
|
+
}
|
|
11438
11735
|
sendJson(res, 404, { error: "not found" });
|
|
11439
11736
|
} catch (err) {
|
|
11440
11737
|
sendJson(res, 500, { error: err?.message ?? String(err) });
|
|
@@ -11476,6 +11773,10 @@ const Config = z.object({
|
|
|
11476
11773
|
skiff: z.object({
|
|
11477
11774
|
enabled: z.boolean().default(false),
|
|
11478
11775
|
debugPort: z.number().min(1024).max(65535).default(3099)
|
|
11776
|
+
}),
|
|
11777
|
+
acp: z.object({
|
|
11778
|
+
enabled: z.boolean().default(false),
|
|
11779
|
+
httpPort: z.number().min(1024).max(65535).default(3100)
|
|
11479
11780
|
})
|
|
11480
11781
|
});
|
|
11481
11782
|
function apply(ctx, config) {
|
|
@@ -11515,6 +11816,7 @@ function apply(ctx, config) {
|
|
|
11515
11816
|
if (config.opencodeSkills) registerOpencodeSkills(ctx);
|
|
11516
11817
|
registerBootstrap(ctx);
|
|
11517
11818
|
registerSkiff(ctx);
|
|
11819
|
+
registerAcp(ctx);
|
|
11518
11820
|
}
|
|
11519
11821
|
/**
|
|
11520
11822
|
* F4 Skiff 调试服务装配:启停 = 人工(设置面板 Skiff 区块开关,settings 持久化)。
|
|
@@ -11579,5 +11881,29 @@ function readWebPort(ctx) {
|
|
|
11579
11881
|
return 3080;
|
|
11580
11882
|
}
|
|
11581
11883
|
}
|
|
11884
|
+
/**
|
|
11885
|
+
* F4c ACP HTTP JSON-RPC 端点装配(v1.26.0 实验性):启停 = 人工(设置面板
|
|
11886
|
+
* 「Serenity」页 ACP 区块开关,settings 持久化)。会话创建/延续走 acp-core →
|
|
11887
|
+
* skiff-core(与调试页同核心);仅监听 127.0.0.1;未开启零资源占用。
|
|
11888
|
+
*/
|
|
11889
|
+
function registerAcp(ctx) {
|
|
11890
|
+
let started = false;
|
|
11891
|
+
const sync = () => {
|
|
11892
|
+
const s = readSimpleSettings();
|
|
11893
|
+
if (s.acpEnabled && !started) startAcpHttpServer(ctx, s.acpHttpPort).then(() => {
|
|
11894
|
+
started = true;
|
|
11895
|
+
}).catch((err) => {
|
|
11896
|
+
console.error(`[serenity-hooks] ✗ ACP HTTP 端点启动失败: ${String(err?.message ?? err)}`);
|
|
11897
|
+
});
|
|
11898
|
+
else if (!s.acpEnabled && started) {
|
|
11899
|
+
stopAcpHttpServer();
|
|
11900
|
+
started = false;
|
|
11901
|
+
}
|
|
11902
|
+
};
|
|
11903
|
+
try {
|
|
11904
|
+
ctx.on("serenity/settings-changed", sync);
|
|
11905
|
+
} catch {}
|
|
11906
|
+
sync();
|
|
11907
|
+
}
|
|
11582
11908
|
//#endregion
|
|
11583
11909
|
export { Config, apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shgroup/dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
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": {
|