@shgroup/dsh-serenity-hooks 1.25.3 → 1.25.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.25.3",
3
+ "version": "1.25.4",
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
@@ -9338,13 +9338,17 @@ function registerGateway(ctx) {
9338
9338
  //#endregion
9339
9339
  //#region src/skiff-debug.ts
9340
9340
  /**
9341
- * skiff-debug.ts — Skiff 调试问答页(F4a',v1.25.0 实验性)
9341
+ * skiff-debug.ts — Skiff 调试问答页(F4a',v1.25.x 实验性)
9342
9342
  *
9343
9343
  * node:http 调试端口(默认关,仅监听 127.0.0.1;启停 = 人工——设置面板「Serenity」
9344
9344
  * 页 Skiff 区块开关,不随插件加载自动启动)。
9345
9345
  *
9346
- * - GET / → 问答 HTML 页(角色下拉 + 输入框 + 答案区 + 轨迹区 + WebUI 链接)
9347
- * - POST /ask → {role, question} → 走会话核心(skiff-core)→ {answer, sessionId, trajectory}
9346
+ * - GET / → 问答 HTML 页(**CCC 选择器** + 角色下拉 + 输入框 + 答案区 + 轨迹区 + WebUI 链接)
9347
+ * - POST /ask → {ccc?, role, question} → 走会话核心(skiff-core)→ {answer, sessionId, trajectory}
9348
+ *
9349
+ * **多 CCC 手工切换(v1.25.4,S142 用户)**:dsh 管理多个 CCC 时,调试页顶部 CCC
9350
+ * 下拉列出全部候选(live 会话 cwd 上溯 .serenity 去重 + 默认绑定 root 兜底),
9351
+ * 切换后角色下拉联动(各 CCC 的 skiff.roles 实时读取),提问按所选 CCC 创建 agent。
9348
9352
  *
9349
9353
  * 与 ACP stdio 协议(F4c 后续)共用同一会话核心(createSkiffAgent + askSkiff),
9350
9354
  * 协议层后加不返工。轨迹 = session.events 结构化返回(与 dsh WebUI 同源数据),
@@ -9377,10 +9381,32 @@ function sendHtml(res, html) {
9377
9381
  });
9378
9382
  res.end(html);
9379
9383
  }
9380
- /** 问答页 HTML:角色下拉 + 输入 + 答案区 + 轨迹区(JS 渲染)+ 绑定的 CCC + WebUI 链接 */
9381
- function skiffDebugPage(roles, cccRoot, webPort) {
9382
- const roleOptions = [...roles.entries()].map(([name, r]) => `<option value="${escapeHtml(name)}">${escapeHtml(name)}${r.model ? ` (${escapeHtml(r.model)})` : ""}</option>`).join("\n");
9383
- const webUrl = `http://127.0.0.1:${webPort}`;
9384
+ /**
9385
+ * 发现候选 CCC 列表(多 CCC 手工切换,v1.25.4):
9386
+ * live 会话 cwd 上溯 .serenity 去重 + 默认绑定 root 兜底(不在列表时放首位)。
9387
+ */
9388
+ function discoverCccs(ctx, defaultRoot) {
9389
+ const roots = [];
9390
+ try {
9391
+ const sessions = ctx.sessions;
9392
+ for (const s of sessions?.list?.() ?? []) {
9393
+ const cwd = s?.header?.cwd;
9394
+ if (typeof cwd === "string") {
9395
+ const r = findSerenityRoot(cwd);
9396
+ if (r && !roots.includes(r)) roots.push(r);
9397
+ }
9398
+ }
9399
+ } catch {}
9400
+ if (!roots.includes(defaultRoot)) roots.unshift(defaultRoot);
9401
+ return roots.map((root) => ({
9402
+ root,
9403
+ name: basename(root) || root,
9404
+ roles: [...readSkiffRoles(root).keys()]
9405
+ }));
9406
+ }
9407
+ /** 问答页 HTML:CCC 切换器 + 角色下拉 + 输入 + 答案区 + 轨迹区(JS 渲染)+ WebUI 链接 */
9408
+ function skiffDebugPage(cccs, defaultRoot, webPort) {
9409
+ const data = JSON.stringify(cccs);
9384
9410
  return `<!DOCTYPE html>
9385
9411
  <html lang="zh">
9386
9412
  <head>
@@ -9417,32 +9443,61 @@ function skiffDebugPage(roles, cccRoot, webPort) {
9417
9443
  <body>
9418
9444
  <main>
9419
9445
  <h1>Skiff Debug</h1>
9420
- <div class="sub">宁静号 trajectory 子集角色问答页(v1.25.2 实验性)— DSH agent-loop 会话核心,轨迹与 WebUI 同源</div>
9421
- <div class="ccc">CCC: ${escapeHtml(cccRoot)}</div>
9446
+ <div class="sub">宁静号 trajectory 子集角色问答页(v1.25.4 实验性)— CCC 可切换,走 DSH agent-loop 会话核心,轨迹与 WebUI 同源</div>
9447
+ <div class="ccc" id="cccBadge"></div>
9448
+ <label for="ccc">认知容器</label>
9449
+ <select id="ccc"></select>
9422
9450
  <label for="role">角色</label>
9423
- <select id="role">${roleOptions || "<option value=\"\">(未配置角色)</option>"}</select>
9451
+ <select id="role"></select>
9424
9452
  <label for="q">问题</label>
9425
9453
  <textarea id="q" placeholder="向该角色提问…"></textarea>
9426
9454
  <button id="ask">提问</button>
9427
9455
  <div id="answer" class="muted">等待提问…</div>
9428
9456
  <div id="trajectory"></div>
9429
- <p class="muted"><a href="${webUrl}" target="_blank" rel="noopener">在 dsh WebUI 查看完整会话</a>(会话列表搜索 sessionId;WebUI 有完整交互)</p>
9457
+ <p class="muted"><a href="${`http://127.0.0.1:${webPort}`}" target="_blank" rel="noopener">在 dsh WebUI 查看完整会话</a>(会话列表搜索 sessionId;WebUI 有完整交互)</p>
9430
9458
  </main>
9459
+ <script id="skiff-data" type="application/json" data-default="${escapeHtml(defaultRoot)}">${escapeHtml(data)}<\/script>
9431
9460
  <script>
9461
+ const CCCS = JSON.parse(document.getElementById('skiff-data').textContent)
9462
+ const defaultRoot = document.getElementById('skiff-data').dataset.default
9463
+ const cccSel = document.getElementById('ccc')
9464
+ const roleSel = document.getElementById('role')
9465
+ const badge = document.getElementById('cccBadge')
9432
9466
  const btn = document.getElementById('ask')
9433
9467
  const answer = document.getElementById('answer')
9434
9468
  const traj = document.getElementById('trajectory')
9435
9469
  const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]))
9470
+ function currentCcc() {
9471
+ return CCCS.find((c) => c.root === cccSel.value) || (CCCS[0] || { root: '', name: '', roles: [] })
9472
+ }
9473
+ function fillCccs() {
9474
+ cccSel.innerHTML = CCCS.map((c) => {
9475
+ const n = c.roles.length
9476
+ return '<option value="' + esc(c.root) + '">' + esc(c.name) + (n ? ' (' + n + ' 角色)' : ' (无角色)') + '</option>'
9477
+ }).join('') || '<option value="">(未发现 CCC)</option>'
9478
+ if (defaultRoot) cccSel.value = CCCS.some((c) => c.root === defaultRoot) ? defaultRoot : (CCCS[0] && CCCS[0].root)
9479
+ fillRoles()
9480
+ }
9481
+ function fillRoles() {
9482
+ const c = currentCcc()
9483
+ badge.textContent = 'CCC: ' + c.root
9484
+ roleSel.innerHTML = (c.roles && c.roles.length)
9485
+ ? c.roles.map((r) => '<option value="' + esc(r) + '">' + esc(r) + '</option>').join('')
9486
+ : '<option value="">(未配置角色)</option>'
9487
+ }
9488
+ cccSel.addEventListener('change', fillRoles)
9489
+ fillCccs()
9436
9490
  btn.addEventListener('click', async () => {
9437
- const role = document.getElementById('role').value
9491
+ const c = currentCcc()
9492
+ const role = roleSel.value
9438
9493
  const q = document.getElementById('q').value.trim()
9439
- if (!role || !q) { answer.className = 'err'; answer.textContent = '请选择角色并输入问题'; return }
9494
+ if (!c.root || !role || !q) { answer.className = 'err'; answer.textContent = '请选择认知容器与角色并输入问题'; return }
9440
9495
  btn.disabled = true
9441
9496
  answer.className = 'muted'
9442
9497
  answer.textContent = '运行中…'
9443
9498
  traj.innerHTML = ''
9444
9499
  try {
9445
- const res = await fetch('/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ role, question: q }) })
9500
+ const res = await fetch('/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ccc: c.root, role, question: q }) })
9446
9501
  const data = await res.json()
9447
9502
  if (!res.ok) throw new Error(data.error || ('HTTP ' + res.status))
9448
9503
  answer.className = ''
@@ -9479,12 +9534,11 @@ function escapeHtml(s) {
9479
9534
  /**
9480
9535
  * 启动调试问答服务(单实例;重复启动幂等返回既有实例)。
9481
9536
  *
9482
- * **CCC 绑定(v1.25.2 用户指出)**:服务绑定一个 CCC root(调用方 resolveSkiffRoot
9483
- * 解析:live 会话中**含 skiff.roles 的 CCC 优先**);角色配置**每次请求实时读取**
9484
- * (不缓存快照)——改 .opencode/serenity.json 后刷新页面即生效,无需重启服务。
9485
- * 页面顶部显示绑定的 CCC root,绑定可核对。
9537
+ * **CCC 绑定(v1.25.2 用户指出)**:服务绑定一个默认 CCC root(调用方 resolveSkiffRoot
9538
+ * 解析:live 会话中**含 skiff.roles 的 CCC 优先**);v1.25.4 起页面可**手工切换**到
9539
+ * 其它候选 CCC(live 会话发现的全部 CCC);角色配置**每次请求实时读取**(不缓存快照)。
9486
9540
  *
9487
- * @param root 绑定的 CCC 根(角色配置读取 + skiff agent cwd)
9541
+ * @param root 默认绑定的 CCC 根(首次加载选中;角色配置读取 + skiff agent cwd)
9488
9542
  * @param port 调试端口(仅 127.0.0.1)
9489
9543
  * @param webPort 主 WebUI 端口(WebUI 链接)
9490
9544
  */
@@ -9501,7 +9555,7 @@ async function startSkiffDebugServer(ctx, root, port, webPort) {
9501
9555
  server,
9502
9556
  port
9503
9557
  };
9504
- console.log(`[serenity-hooks] ✓ Skiff 调试问答页: http://127.0.0.1:${port}CCC: ${root},WebUI: ${webPort})`);
9558
+ console.log(`[serenity-hooks] ✓ Skiff 调试问答页: http://127.0.0.1:${port}(默认 CCC: ${root},WebUI: ${webPort})`);
9505
9559
  }
9506
9560
  function stopSkiffDebugServer() {
9507
9561
  if (!active) return;
@@ -9510,11 +9564,11 @@ function stopSkiffDebugServer() {
9510
9564
  } catch {}
9511
9565
  active = null;
9512
9566
  }
9513
- async function handle(ctx, root, webPort, req, res) {
9567
+ async function handle(ctx, defaultRoot, webPort, req, res) {
9514
9568
  try {
9515
9569
  const url = (req.url ?? "/").split("?")[0] ?? "/";
9516
9570
  if (req.method === "GET" && url === "/") {
9517
- sendHtml(res, skiffDebugPage(readSkiffRoles(root), root, webPort));
9571
+ sendHtml(res, skiffDebugPage(discoverCccs(ctx, defaultRoot), defaultRoot, webPort));
9518
9572
  return;
9519
9573
  }
9520
9574
  if (req.method === "POST" && url === "/ask") {
@@ -9527,18 +9581,19 @@ async function handle(ctx, root, webPort, req, res) {
9527
9581
  sendJson(res, 400, { error: "invalid JSON body" });
9528
9582
  return;
9529
9583
  }
9584
+ const ccc = typeof body.ccc === "string" && body.ccc !== "" ? body.ccc : defaultRoot;
9530
9585
  const roleName = typeof body.role === "string" ? body.role : "";
9531
9586
  const question = typeof body.question === "string" ? body.question : "";
9532
- const role = readSkiffRoles(root).get(roleName);
9587
+ const role = readSkiffRoles(ccc).get(roleName);
9533
9588
  if (!roleName || !role) {
9534
- sendJson(res, 400, { error: `unknown role: ${roleName}` });
9589
+ sendJson(res, 400, { error: `unknown role: ${roleName} (ccc: ${ccc})` });
9535
9590
  return;
9536
9591
  }
9537
9592
  if (!question.trim()) {
9538
9593
  sendJson(res, 400, { error: "empty question" });
9539
9594
  return;
9540
9595
  }
9541
- const result = await askSkiff(ctx, (await createSkiffAgent(ctx, root, roleName, role, readHandymanConfig(root)?.defaultModel)).agent, question);
9596
+ const result = await askSkiff(ctx, (await createSkiffAgent(ctx, ccc, roleName, role, readHandymanConfig(ccc)?.defaultModel)).agent, question);
9542
9597
  sendJson(res, 200, {
9543
9598
  answer: result.answer,
9544
9599
  sessionId: result.sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.25.3",
3
+ "version": "1.25.4",
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": {