@shgroup/dsh-serenity-hooks 1.25.9 → 1.25.10
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 +137 -31
- 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.10",
|
|
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
|
@@ -647,7 +647,8 @@ function readSkiffRoles(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
|
|
|
647
647
|
keeper: role.trajectory.keeper === true,
|
|
648
648
|
rebuild: role.trajectory.rebuild === true
|
|
649
649
|
} : void 0,
|
|
650
|
-
systemPrompt: typeof role.systemPrompt === "string" ? role.systemPrompt : void 0
|
|
650
|
+
systemPrompt: typeof role.systemPrompt === "string" ? role.systemPrompt : void 0,
|
|
651
|
+
systemPromptFile: typeof role.systemPromptFile === "string" ? role.systemPromptFile : void 0
|
|
651
652
|
});
|
|
652
653
|
}
|
|
653
654
|
} catch {}
|
|
@@ -672,6 +673,32 @@ function roleMsmWhitelist(role) {
|
|
|
672
673
|
return new Set(role?.msms ?? []);
|
|
673
674
|
}
|
|
674
675
|
/**
|
|
676
|
+
* 解析角色的系统提示词全文(v1.25.10,S142 用户:超长提示词 JSON 内嵌不可读):
|
|
677
|
+
* ① `systemPromptFile` 存在 → 读取文件内容(**推荐配置方法**;相对 CCC 根,
|
|
678
|
+
* 路径逃逸拒绝(resolveInside)+ BOM 剥除(readUtf8)+ 存在性校验)
|
|
679
|
+
* ② 否则 → 内嵌 `systemPrompt`(兼容旧配置)
|
|
680
|
+
* ③ 都无 → 空字符串
|
|
681
|
+
* 文件缺失/逃逸 → 抛错(调用方 catch 降级 + validate 报 issue)。
|
|
682
|
+
* 懒读取:readSkiffRoles 不读文件(guards/seams 每次工具调用查询的热路径零 IO),
|
|
683
|
+
* 仅在本函数(创建 agent / validate / list 时)读取。
|
|
684
|
+
*/
|
|
685
|
+
function resolveRoleSystemPrompt(root, role) {
|
|
686
|
+
if (!role) return "";
|
|
687
|
+
const file = role.systemPromptFile?.trim();
|
|
688
|
+
if (file) {
|
|
689
|
+
const abs = resolveInside(root, file);
|
|
690
|
+
if (!existsSync(abs)) throw new Error(`skiff role "${role.systemPrompt ?? "(unnamed)"}": systemPromptFile "${file}" not found (resolved: ${abs})`);
|
|
691
|
+
return readUtf8(abs).trim();
|
|
692
|
+
}
|
|
693
|
+
return role.systemPrompt ?? "";
|
|
694
|
+
}
|
|
695
|
+
/** 角色系统提示词来源(validate/list 展示用) */
|
|
696
|
+
function systemPromptSource(role) {
|
|
697
|
+
if (role?.systemPromptFile?.trim()) return "file";
|
|
698
|
+
if (role?.systemPrompt?.trim()) return "inline";
|
|
699
|
+
return "none";
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
675
702
|
* Skiff 基础提示词(dsp 只给这部分;CCC 的 systemPrompt 段由调用方拼接):
|
|
676
703
|
* 身份 + 可用 MSM/工具清单 + 调用协议 + 边界声明。动态生成(清单来自角色白名单)。
|
|
677
704
|
*/
|
|
@@ -694,22 +721,20 @@ function buildSkiffBasePrompt(roleName, role) {
|
|
|
694
721
|
}
|
|
695
722
|
//#endregion
|
|
696
723
|
//#region src/skiff-registry.ts
|
|
697
|
-
/**
|
|
698
|
-
* skiff-registry.ts — Skiff 会话注册表(sessionId → role;零 DSH 依赖,可被任何 seams 安全 import)
|
|
699
|
-
*
|
|
700
|
-
* F4b 会话映射:Skiff agent 创建时注册(skiff-core),guards/seams 按 sessionId 查角色。
|
|
701
|
-
* 独立成模块的原因:guards.ts 等拦截缝需要查角色而不引入 skiff-core 的运行时依赖
|
|
702
|
-
* (skiff-core 依赖 @deepseek-ai/dsh-llm,测试/装配级联成本高)——注册表本身纯内存 Map。
|
|
703
|
-
*
|
|
704
|
-
* 生命周期:进程内存态;连接/页面关闭时 unregister;进程重启自然清空(skiff 会话不复存在)。
|
|
705
|
-
*/
|
|
706
724
|
const skiffSessions = /* @__PURE__ */ new Map();
|
|
707
|
-
/** 查 sessionId 的 Skiff 角色名(无 → null
|
|
725
|
+
/** 查 sessionId 的 Skiff 角色名(无 → null)——向后兼容(guards/seams 依赖) */
|
|
708
726
|
function skiffRoleFor$1(sessionId) {
|
|
727
|
+
return skiffSessions.get(sessionId)?.role ?? null;
|
|
728
|
+
}
|
|
729
|
+
/** 查 sessionId 的完整绑定(role + ccc;无 → null)——v1.25.10 会话追问校验 */
|
|
730
|
+
function skiffSessionInfo$1(sessionId) {
|
|
709
731
|
return skiffSessions.get(sessionId) ?? null;
|
|
710
732
|
}
|
|
711
|
-
function registerSkiffSession$1(sessionId, role) {
|
|
712
|
-
skiffSessions.set(sessionId,
|
|
733
|
+
function registerSkiffSession$1(sessionId, role, ccc) {
|
|
734
|
+
skiffSessions.set(sessionId, {
|
|
735
|
+
role,
|
|
736
|
+
ccc
|
|
737
|
+
});
|
|
713
738
|
}
|
|
714
739
|
//#endregion
|
|
715
740
|
//#region src/seams/guards.ts
|
|
@@ -2357,9 +2382,17 @@ const skiffAgents = /* @__PURE__ */ new Map();
|
|
|
2357
2382
|
function skiffRoleFor(sessionId) {
|
|
2358
2383
|
return skiffRoleFor$1(sessionId);
|
|
2359
2384
|
}
|
|
2360
|
-
|
|
2385
|
+
/** 查 sessionId 的会话绑定(role + ccc;无 → null)——v1.25.10 追问校验 */
|
|
2386
|
+
function skiffSessionInfo(sessionId) {
|
|
2387
|
+
return skiffSessionInfo$1(sessionId);
|
|
2388
|
+
}
|
|
2389
|
+
/** 查 sessionId 的活体 agent(进程内会话延续用;未注册/已清理 → undefined) */
|
|
2390
|
+
function getSkiffAgent(sessionId) {
|
|
2391
|
+
return skiffAgents.get(sessionId);
|
|
2392
|
+
}
|
|
2393
|
+
function registerSkiffSession(sessionId, role, ccc, agent) {
|
|
2361
2394
|
skiffAgents.set(sessionId, agent);
|
|
2362
|
-
registerSkiffSession$1(sessionId, role);
|
|
2395
|
+
registerSkiffSession$1(sessionId, role, ccc);
|
|
2363
2396
|
}
|
|
2364
2397
|
/** Skiff agent 挂载的 DSH preset(v1.25.3 修复:read/grep/glob 等平台工具由 preset 决定工具面;
|
|
2365
2398
|
* handyman 经 composeFrom 继承父、skiff 无父上下文——直接挂 DSH 默认 standard preset;
|
|
@@ -2387,16 +2420,22 @@ async function createSkiffAgent(ctx, root, roleName, role, defaultModel) {
|
|
|
2387
2420
|
...model ? { agentOptions: splitModel(model) } : {}
|
|
2388
2421
|
});
|
|
2389
2422
|
const agent = handle.agent;
|
|
2423
|
+
let cccPrompt = "";
|
|
2424
|
+
try {
|
|
2425
|
+
cccPrompt = resolveRoleSystemPrompt(root, role);
|
|
2426
|
+
} catch (err) {
|
|
2427
|
+
console.warn(`[serenity-hooks] skiff 角色 "${roleName}" 系统提示词解析失败(回退仅基础段): ${String(err?.message ?? err)}`);
|
|
2428
|
+
}
|
|
2390
2429
|
try {
|
|
2391
2430
|
agent.ctx.systemPrompt.section({
|
|
2392
2431
|
name: "serenity-skiff",
|
|
2393
2432
|
order: -60,
|
|
2394
|
-
text: () => [buildSkiffBasePrompt(roleName, role),
|
|
2433
|
+
text: () => [buildSkiffBasePrompt(roleName, role), cccPrompt].filter(Boolean).join("\n")
|
|
2395
2434
|
});
|
|
2396
2435
|
} catch (err) {
|
|
2397
2436
|
console.warn(`[serenity-hooks] skiff 系统提示词注册失败: ${String(err?.message ?? err)}`);
|
|
2398
2437
|
}
|
|
2399
|
-
registerSkiffSession(sessionId, roleName, agent);
|
|
2438
|
+
registerSkiffSession(sessionId, roleName, root, agent);
|
|
2400
2439
|
return {
|
|
2401
2440
|
handle,
|
|
2402
2441
|
agent,
|
|
@@ -2478,11 +2517,12 @@ function truncate(s, n) {
|
|
|
2478
2517
|
return s.length > n ? `${s.slice(0, n)}…` : s;
|
|
2479
2518
|
}
|
|
2480
2519
|
/**
|
|
2481
|
-
* 提问一轮:followup → 等 idle → 读答案 +
|
|
2482
|
-
* @param eventsStart
|
|
2520
|
+
* 提问一轮:followup → 等 idle → 读答案 + 轨迹。
|
|
2521
|
+
* @param eventsStart 轨迹起点:显式传 0 = 全量轨迹(会话追问时页面重绘完整时间线,
|
|
2522
|
+
* v1.25.10 用户拍板);不传(undefined)= 本轮增量(followup 前 events 之后)
|
|
2483
2523
|
*/
|
|
2484
|
-
async function askSkiff(ctx, agent, question, eventsStart
|
|
2485
|
-
const before = eventsStart
|
|
2524
|
+
async function askSkiff(ctx, agent, question, eventsStart) {
|
|
2525
|
+
const before = eventsStart === void 0 ? agent.session.events.length : eventsStart;
|
|
2486
2526
|
agent.followup(createUserMessage({
|
|
2487
2527
|
content: [{
|
|
2488
2528
|
type: "text",
|
|
@@ -6806,7 +6846,9 @@ dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页)
|
|
|
6806
6846
|
"msms": ["msm-a", "msm-b"], // MSM 白名单(独立):acc_msm exec 只能跑这些;register/deregister 必拒;list 只显示这些
|
|
6807
6847
|
"tools": ["read","grep","glob",...], // 非 MSM 工具白名单(独立):白名单外工具一律不可用(guard 强制)
|
|
6808
6848
|
"trajectory": { "session": false, "keeper": false, "rebuild": false }, // 轨迹纪律子集(缺省全关 = 完全独立)
|
|
6809
|
-
|
|
6849
|
+
// 角色人格/认知边界/风格(CCC 完整定义;dsp 只给基础提示词)
|
|
6850
|
+
"systemPromptFile": ".opencode/skiff/<role>.md" // ★ 推荐:引用 md 文件(相对 CCC 根;超长提示词在 JSON 内嵌不可读)
|
|
6851
|
+
// "systemPrompt": "..." // 兼容:内嵌字符串仍可用;两者都设时 systemPromptFile 优先
|
|
6810
6852
|
}
|
|
6811
6853
|
} } }
|
|
6812
6854
|
|
|
@@ -6837,6 +6879,8 @@ dsp 只提供机制(双白名单强制 + 基础提示词 + 调试问答页)
|
|
|
6837
6879
|
启停 = 人工(设置面板开关,不随插件加载自动启动);未开启零资源占用
|
|
6838
6880
|
生效机制:角色配置**实时读取**(改 .opencode/serenity.json → 刷新调试页即生效);
|
|
6839
6881
|
改配置后用 skiff_admin apply 做显式校验 + 应用确认(绑定 CCC + 角色清单)
|
|
6882
|
+
会话追问(v1.25.10):调试页持有当前会话,追问自动续接(同会话上下文延续);
|
|
6883
|
+
「新对话」按钮开新会话;进程重启后旧会话不可续(WebUI 仍可见历史)
|
|
6840
6884
|
注意:skill 加载对 skiff **恒可用**(不设白名单——读知识面,无写能力)`;
|
|
6841
6885
|
/** 校验当前 CCC 的 skiff 配置:roles schema 合法 / msms 均已注册 / model ∈ handyman.models / systemPrompt 非空 */
|
|
6842
6886
|
function validateSkiffConfig(root) {
|
|
@@ -6852,7 +6896,11 @@ function validateSkiffConfig(root) {
|
|
|
6852
6896
|
const hc = readHandymanConfig(root);
|
|
6853
6897
|
const allowedModels = hc ? new Set(hc.models) : null;
|
|
6854
6898
|
for (const [name, role] of roles) {
|
|
6855
|
-
|
|
6899
|
+
try {
|
|
6900
|
+
if (!resolveRoleSystemPrompt(root, role).trim()) issues.push(`role "${name}": system prompt is empty (define systemPromptFile (recommended) or systemPrompt)`);
|
|
6901
|
+
} catch (err) {
|
|
6902
|
+
issues.push(`role "${name}": ${String(err?.message ?? err)}`);
|
|
6903
|
+
}
|
|
6856
6904
|
for (const m of role.msms ?? []) if (!registered.has(m)) issues.push(`role "${name}": msms "${m}" is not registered in mech-registry.json`);
|
|
6857
6905
|
if (role.model && allowedModels && !allowedModels.has(role.model)) issues.push(`role "${name}": model "${role.model}" not in handyman.models (${[...allowedModels].join(", ") || "(none)"})`);
|
|
6858
6906
|
}
|
|
@@ -6879,7 +6927,8 @@ function listSkiffRoles(root) {
|
|
|
6879
6927
|
keeper: role.trajectory?.keeper === true,
|
|
6880
6928
|
rebuild: role.trajectory?.rebuild === true
|
|
6881
6929
|
},
|
|
6882
|
-
|
|
6930
|
+
promptSource: systemPromptSource(role),
|
|
6931
|
+
...role.systemPromptFile ? { systemPromptFile: role.systemPromptFile } : {}
|
|
6883
6932
|
}));
|
|
6884
6933
|
return {
|
|
6885
6934
|
roles: items,
|
|
@@ -6914,7 +6963,7 @@ function applySkiffConfig(root) {
|
|
|
6914
6963
|
}
|
|
6915
6964
|
const skiffAdminTool = defineTool({
|
|
6916
6965
|
name: "skiff_admin",
|
|
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).",
|
|
6966
|
+
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 / system prompt resolvable — systemPromptFile (recommended) or 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/prompt source). Roles are defined by the CCC in .opencode/serenity.json skiff.roles; skill loading is always available to skiff sessions (not whitelisted).",
|
|
6918
6967
|
parameters: { action: {
|
|
6919
6968
|
type: "string",
|
|
6920
6969
|
enum: [
|
|
@@ -11084,6 +11133,13 @@ function skiffDebugPage(cccs, defaultRoot, webPort) {
|
|
|
11084
11133
|
.sub { opacity: .65; font-size: 13px; margin-bottom: 16px; }
|
|
11085
11134
|
.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; }
|
|
11086
11135
|
@media (prefers-color-scheme: dark) { .ccc { color: #3ddc9a; } }
|
|
11136
|
+
.sessionbar { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
|
|
11137
|
+
.session-badge { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 12px; padding: 3px 10px; border-radius: 999px; background: rgba(127,127,127,.12); word-break: break-all; }
|
|
11138
|
+
.session-badge.continued { background: rgba(11,168,117,.12); color: #0ba875; }
|
|
11139
|
+
@media (prefers-color-scheme: dark) { .session-badge.continued { color: #3ddc9a; } }
|
|
11140
|
+
button.ghost { background: transparent; color: #57606a; border: 1px solid #d0d7de; padding: 4px 12px; font-size: 12px; border-radius: 8px; cursor: pointer; margin-top: 0; }
|
|
11141
|
+
@media (prefers-color-scheme: dark) { button.ghost { color: #8b949e; border-color: #3a3d42; } }
|
|
11142
|
+
button.ghost:hover { background: rgba(127,127,127,.1); }
|
|
11087
11143
|
label { font-size: 13px; font-weight: 600; display: block; margin: 12px 0 4px; }
|
|
11088
11144
|
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; }
|
|
11089
11145
|
@media (prefers-color-scheme: dark) { select, textarea { background: #26282c; border-color: #3a3d42; } }
|
|
@@ -11120,14 +11176,18 @@ function skiffDebugPage(cccs, defaultRoot, webPort) {
|
|
|
11120
11176
|
<body>
|
|
11121
11177
|
<main>
|
|
11122
11178
|
<h1>Skiff Debug</h1>
|
|
11123
|
-
<div class="sub">宁静号 trajectory 子集角色问答页(v1.25.
|
|
11179
|
+
<div class="sub">宁静号 trajectory 子集角色问答页(v1.25.10 实验性)— 多 CCC 可切换,同会话可追问(新对话按钮开新会话),回答 marked 服务端渲染 + think 折叠</div>
|
|
11124
11180
|
<div class="ccc" id="cccBadge"></div>
|
|
11181
|
+
<div class="sessionbar">
|
|
11182
|
+
<span id="sessionBadge" class="session-badge muted">(新会话)</span>
|
|
11183
|
+
<button id="newChat" type="button" class="ghost">新对话</button>
|
|
11184
|
+
</div>
|
|
11125
11185
|
<label for="ccc">认知容器</label>
|
|
11126
11186
|
<select id="ccc"></select>
|
|
11127
11187
|
<label for="role">角色</label>
|
|
11128
11188
|
<select id="role"></select>
|
|
11129
11189
|
<label for="q">问题</label>
|
|
11130
|
-
<textarea id="q" placeholder="
|
|
11190
|
+
<textarea id="q" placeholder="向该角色提问…(追问直接继续输入)"></textarea>
|
|
11131
11191
|
<button id="ask">提问</button>
|
|
11132
11192
|
<div id="answer" class="muted">等待提问…</div>
|
|
11133
11193
|
<div id="trajectory"></div>
|
|
@@ -11140,10 +11200,26 @@ const defaultRoot = document.getElementById('skiff-data').dataset.default
|
|
|
11140
11200
|
const cccSel = document.getElementById('ccc')
|
|
11141
11201
|
const roleSel = document.getElementById('role')
|
|
11142
11202
|
const badge = document.getElementById('cccBadge')
|
|
11203
|
+
const sessionBadge = document.getElementById('sessionBadge')
|
|
11204
|
+
const newChatBtn = document.getElementById('newChat')
|
|
11143
11205
|
const btn = document.getElementById('ask')
|
|
11144
11206
|
const answer = document.getElementById('answer')
|
|
11145
11207
|
const traj = document.getElementById('trajectory')
|
|
11146
11208
|
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))
|
|
11209
|
+
let sessionId = ''
|
|
11210
|
+
function shortId(id) { return id.length > 24 ? id.slice(0, 18) + '…' + id.slice(-6) : id }
|
|
11211
|
+
function renderSessionBadge() {
|
|
11212
|
+
sessionBadge.textContent = sessionId ? '会话 ' + shortId(sessionId) + '(追问续接)' : '(新会话)'
|
|
11213
|
+
sessionBadge.className = 'session-badge' + (sessionId ? ' continued' : '')
|
|
11214
|
+
}
|
|
11215
|
+
function newConversation() {
|
|
11216
|
+
sessionId = ''
|
|
11217
|
+
renderSessionBadge()
|
|
11218
|
+
answer.className = 'muted'
|
|
11219
|
+
answer.textContent = '已开启新对话'
|
|
11220
|
+
traj.innerHTML = ''
|
|
11221
|
+
document.getElementById('q').value = ''
|
|
11222
|
+
}
|
|
11147
11223
|
function currentCcc() {
|
|
11148
11224
|
return CCCS.find((c) => c.root === cccSel.value) || (CCCS[0] || { root: '', name: '', roles: [] })
|
|
11149
11225
|
}
|
|
@@ -11162,8 +11238,11 @@ function fillRoles() {
|
|
|
11162
11238
|
? c.roles.map((r) => '<option value="' + esc(r) + '">' + esc(r) + '</option>').join('')
|
|
11163
11239
|
: '<option value="">(未配置角色)</option>'
|
|
11164
11240
|
}
|
|
11165
|
-
cccSel.addEventListener('change', fillRoles)
|
|
11241
|
+
cccSel.addEventListener('change', () => { fillRoles(); newConversation() })
|
|
11242
|
+
roleSel.addEventListener('change', () => newConversation())
|
|
11243
|
+
newChatBtn.addEventListener('click', newConversation)
|
|
11166
11244
|
fillCccs()
|
|
11245
|
+
renderSessionBadge()
|
|
11167
11246
|
btn.addEventListener('click', async () => {
|
|
11168
11247
|
const c = currentCcc()
|
|
11169
11248
|
const role = roleSel.value
|
|
@@ -11174,11 +11253,18 @@ btn.addEventListener('click', async () => {
|
|
|
11174
11253
|
answer.textContent = '运行中…'
|
|
11175
11254
|
traj.innerHTML = ''
|
|
11176
11255
|
try {
|
|
11177
|
-
const res = await fetch('/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ccc: c.root, role, question: q }) })
|
|
11256
|
+
const res = await fetch('/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ccc: c.root, role, question: q, sessionId: sessionId || undefined }) })
|
|
11178
11257
|
const data = await res.json()
|
|
11179
|
-
if (!res.ok)
|
|
11258
|
+
if (!res.ok) {
|
|
11259
|
+
// 会话不可恢复/绑定不匹配 → 前端清空 sessionId 重建(用户拍板:切换即新对话)
|
|
11260
|
+
sessionId = ''
|
|
11261
|
+
renderSessionBadge()
|
|
11262
|
+
throw new Error(data.error || ('HTTP ' + res.status))
|
|
11263
|
+
}
|
|
11180
11264
|
answer.className = ''
|
|
11181
11265
|
answer.innerHTML = data.answer_html || esc(data.answer || '') || '(空回答)'
|
|
11266
|
+
sessionId = data.sessionId || sessionId
|
|
11267
|
+
renderSessionBadge()
|
|
11182
11268
|
renderTrajectory(data.trajectory || [], data.sessionId || '')
|
|
11183
11269
|
} catch (err) {
|
|
11184
11270
|
answer.className = 'err'
|
|
@@ -11288,6 +11374,7 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11288
11374
|
const ccc = typeof body.ccc === "string" && body.ccc !== "" ? body.ccc : defaultRoot;
|
|
11289
11375
|
const roleName = typeof body.role === "string" ? body.role : "";
|
|
11290
11376
|
const question = typeof body.question === "string" ? body.question : "";
|
|
11377
|
+
const sessionId = typeof body.sessionId === "string" && body.sessionId !== "" ? body.sessionId : void 0;
|
|
11291
11378
|
const role = readSkiffRoles(ccc).get(roleName);
|
|
11292
11379
|
if (!roleName || !role) {
|
|
11293
11380
|
sendJson(res, 400, { error: `unknown role: ${roleName} (ccc: ${ccc})` });
|
|
@@ -11297,11 +11384,30 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
|
|
|
11297
11384
|
sendJson(res, 400, { error: "empty question" });
|
|
11298
11385
|
return;
|
|
11299
11386
|
}
|
|
11300
|
-
|
|
11387
|
+
let agent;
|
|
11388
|
+
let continued = false;
|
|
11389
|
+
if (sessionId) {
|
|
11390
|
+
const info = skiffSessionInfo(sessionId);
|
|
11391
|
+
const live = getSkiffAgent(sessionId);
|
|
11392
|
+
if (!info || !live) {
|
|
11393
|
+
sendJson(res, 400, { error: "session is not recoverable (process restarted or session unknown) — start a new conversation" });
|
|
11394
|
+
return;
|
|
11395
|
+
}
|
|
11396
|
+
if (info.role !== roleName || info.ccc !== ccc) {
|
|
11397
|
+
sendJson(res, 400, { error: `session "${sessionId}" belongs to role "${info.role}" in another CCC — start a new conversation` });
|
|
11398
|
+
return;
|
|
11399
|
+
}
|
|
11400
|
+
agent = live;
|
|
11401
|
+
continued = true;
|
|
11402
|
+
}
|
|
11403
|
+
const hc = readHandymanConfig(ccc);
|
|
11404
|
+
if (!agent) agent = (await createSkiffAgent(ctx, ccc, roleName, role, hc?.defaultModel)).agent;
|
|
11405
|
+
const result = await askSkiff(ctx, agent, question, 0);
|
|
11301
11406
|
sendJson(res, 200, {
|
|
11302
11407
|
answer: result.answer,
|
|
11303
11408
|
answer_html: renderSkiffMarkdown(result.answer),
|
|
11304
11409
|
sessionId: result.sessionId,
|
|
11410
|
+
continued,
|
|
11305
11411
|
trajectory: result.trajectory
|
|
11306
11412
|
});
|
|
11307
11413
|
return;
|
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.10",
|
|
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": {
|