@shgroup/dsh-serenity-hooks 1.30.12 → 1.30.14

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.30.12",
3
+ "version": "1.30.14",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——10 个工具(container_fs/logbook/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/autopilot-trajectory)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/自主巡航。适配 DSH 0.1.2-rc.1(deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { a as findSerenityRoot, c as matchBlacklist, d as readCccName$2, f as readHandymanConfig, i as findGitRoot, l as pathInside, m as resolveInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-DAsSHsub.js";
3
3
  import { a as resolveRoleSystemPrompt, i as readSkiffRoles, l as systemPromptSource, r as isSkiffSessionId, s as roleToolWhitelist } from "./skiff-role-DlrbHPLD.js";
4
- import { C as newStopToken, D as writeFailedStatus, E as splitModel, O as writeProgress, S as listActiveHandymen, T as requireWhitelistedModel, _ as workspaceTrajectoryLine, a as startSkiffDebugServer, b as buildRoundPrompt, c as askSkiff, d as getSkiffAgent, f as skiffMsmGate, g as unregisterSkiffSession, h as skiffTrajectoryEnabled, k as skiffRoleFor, l as createSkiffAgent, m as skiffSessionSnapshot, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffSessionInfo, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as ensureSkiffSession, v as waitAgentIdle, w as readProgress, x as handymanProgressPaths, y as HANDYMAN_GUIDE } from "./skiff-debug-CizJBLeZ.js";
4
+ import { A as skiffRoleFor, C as listActiveHandymen, D as splitModel, E as requireWhitelistedModel, O as writeFailedStatus, S as handymanProgressPaths, T as readProgress, _ as unregisterSkiffSession, a as startSkiffDebugServer, b as HANDYMAN_GUIDE, c as askSkiff, d as ensureWorkspacePromptSection, f as getSkiffAgent, g as skiffTrajectoryEnabled, h as skiffSessionSnapshot, k as writeProgress, l as createSkiffAgent, m as skiffSessionInfo, n as jscSafeJsonText, o as stopSkiffDebugServer, p as skiffMsmGate, r as renderSkiffMarkdown, s as stripThink, t as discoverCccs, u as ensureSkiffSession, v as workspaceTrajectoryLine, w as newStopToken, x as buildRoundPrompt, y as waitAgentIdle } from "./skiff-debug-DYXDdzu3.js";
5
5
  import { i as hostSessions, n as hostInjected, o as hostWeb, r as hostService, s as hostWebServer, t as hostAgents } from "./access-dU_vG1q8.js";
6
6
  import { a as readWeixinCredential, c as weixinInboundDir, d as LOCALSTORE_SCOPES, f as checkLocalstoreGitCompliance, h as runLocalStore, i as matchWeixinRoute, l as weixinSessionIdFor, m as readGitTrack, n as extractWeixinText, o as readWeixinSettings, p as localstorePath, r as hasVoiceItem, s as sanitizeFileName$1, t as extractWeixinMedia } from "./weixin-route-BD_yYAZz.js";
7
- import { C as summarize, S as showSession, _ as readActiveSessionMd, a as archiveSessions, b as sessionsRoot, c as createSession, d as findSession, f as getActiveSessionInfo, g as qaCheck, h as parseSessionContextFromEvents, i as SESSION_ACTIONS, l as extractSessionMdPathFromText, m as listSessions, n as readLastBound, o as clearActiveSessionInfo, p as healthCheck, r as DEFAULT_SESSION_SCOPE, s as closeSession, t as appendBound, u as findLatestActiveSessionMd, v as resolveSessionByTitle, w as useSession, x as setActiveSessionInfo, y as sessionEvents } from "./session-bound-D2ANqVn-.js";
7
+ import { C as useSession, S as summarize, _ as resolveSessionByTitle, a as archiveSessions, b as setActiveSessionInfo, c as createSession, d as getActiveSessionInfo, f as healthCheck, g as readActiveSessionMd, h as qaCheck, i as SESSION_ACTIONS, l as extractSessionMdPathFromText, m as parseSessionContextFromEvents, n as readLastBound, o as clearActiveSessionInfo, p as listSessions, r as DEFAULT_SESSION_SCOPE, s as closeSession, t as appendBound, u as findSession, v as sessionEvents, x as showSession, y as sessionsRoot } from "./session-bound-BpTayaIS.js";
8
8
  import { n as registerSettingsSection, t as readSimpleSettings } from "./settings-section-CP4uMJf_.js";
9
9
  import { a as markdownToPlainText, c as sniffImageExt, i as getUpdates, n as downloadMedia, o as sendTextMessage, r as getConfig, s as sendTyping, t as TypingStatus } from "./weixin-api-BMpljodH.js";
10
10
  import z from "@deepseek-ai/schemastery";
@@ -6478,7 +6478,7 @@ function registerStatusApi(ctx, opts = {}) {
6478
6478
  workspace: url.searchParams.get("workspace") ?? void 0
6479
6479
  });
6480
6480
  const root = findSerenityRoot(workspace) ?? "";
6481
- const { discoverCccs } = await import("./skiff-debug-CizJBLeZ.js").then((n) => n.i);
6481
+ const { discoverCccs } = await import("./skiff-debug-DYXDdzu3.js").then((n) => n.i);
6482
6482
  sendJson$2(res, 200, { cccs: await discoverCccs(ctx, root) });
6483
6483
  } catch (err) {
6484
6484
  sendJson$2(res, 400, { error: err.message ?? String(err) });
@@ -7962,7 +7962,17 @@ function sessionNameFromMdPath(mdPath) {
7962
7962
  /**
7963
7963
  * 稳固的 SESSION.md 定位(多层候选 + 存在性校验,**绝不输出虚假路径**):
7964
7964
  * ① 内存活跃会话(本会话显式 use 过)→ ② events 恢复(use 标记 + 重建锚点规范行,进程重启后)
7965
- * → ③ surface 首条锚点(events 异常时兜底)→ ④ AGENT_SESSIONS 约定回退(最新未完成活动目录)。
7965
+ * → ②b **权威绑定**(`AGENT_SESSIONS/.bindings.json`,本会话持久绑定)→ ③ surface 首条锚点
7966
+ * (events 异常时兜底)。
7967
+ *
7968
+ * v1.30.13(S142 用户"微信桥 + skiff 会话锚定要准确",诊断 D4):**移除**原 ④「全局最新未完成
7969
+ * 会话」(`findLatestActiveSessionMd`)。理由(R↓):④ 是**跨轨迹**猜测——候选①②③全空时,
7970
+ * 它会把 rebuild 静默接到 AGENT_SESSIONS 里最新的另一个会话(临时 skiff 会话 persistent=false、
7971
+ * 重启后首条消息前、Danica 新会话都命中该形态),而"接错轨迹"比重建失败危险得多
7972
+ * (错误轨迹被继续写入 + 正确轨迹静默丢失)。现在全部候选失败 → 返回 null → 调用方
7973
+ * 报错引导用户显式 `logbook use`(响亮失败优于静默接错)。
7974
+ * 备选:④ 仅在"同 scope 确无绑定"时兜底——同样会接错轨迹,故不采纳。
7975
+ *
7966
7976
  * 每候选 resolve 后 existsSync 校验(相对路径按 root 解析);全部失败返回 null → 调用方报错引导。
7967
7977
  */
7968
7978
  function resolveSessionMdPath(root, scope, session) {
@@ -7970,8 +7980,8 @@ function resolveSessionMdPath(root, scope, session) {
7970
7980
  candidates.push(getActiveSessionInfo(scope)?.mdPath ?? null);
7971
7981
  const events = sessionEvents(session);
7972
7982
  if (events.length > 0) candidates.push(parseSessionContextFromEvents(events)?.mdPath ?? null);
7983
+ candidates.push(readLastBound(session)?.mdPath ?? null);
7973
7984
  candidates.push(parseAnchorMdPath(session));
7974
- candidates.push(findLatestActiveSessionMd(root));
7975
7985
  for (const c of candidates) {
7976
7986
  if (!c) continue;
7977
7987
  const abs = c.startsWith(root) ? c : resolve(root, c);
@@ -8021,7 +8031,7 @@ async function queueRebuild(ctx, opts) {
8021
8031
  const session = ctx.sessions?.get?.(dshSessionId);
8022
8032
  if (!session) throw new Error(`Unable to locate dsh session ${dshSessionId} (session may be closed)`);
8023
8033
  const mdPath = resolveSessionMdPath(root, dshSessionId, session);
8024
- if (!mdPath) throw new Error("Unable to determine the active SESSION.md no session context found in this conversation. Run \"logbook use <S###> --summary <内容概括 ≤20 字>\" first to activate the trajectory to resume, then retry logbook rebuild.");
8034
+ if (!mdPath) throw new Error("Unable to determine the active SESSION.md for this session checked (in order): in-memory activation (logbook use in this process), [SESSION CONTEXT] events in this conversation, AGENT_SESSIONS/.bindings.json (authoritative binding), rebuild anchor in the surface. None matched an existing SESSION.md. Run \"logbook use <S###> --summary <内容概括 ≤20 字>\" to bind this conversation to a trajectory, then retry logbook rebuild. (No global fallback is applied on purpose — guessing could resume a different trajectory.)");
8025
8035
  const sessionName = getActiveSessionInfo(dshSessionId)?.sessionId ?? sessionNameFromMdPath(mdPath);
8026
8036
  const focus = note && note.trim() !== "" ? note : void 0;
8027
8037
  const anchor = buildRebuildAnchor(root, sessionName, mdPath, DEFAULT_ANCHOR_MESSAGES, focus);
@@ -9548,7 +9558,9 @@ async function handleIncoming(ctx, root, accountId, cred, msg) {
9548
9558
  }
9549
9559
  const parts = [];
9550
9560
  const activeWorkspace = getActiveSessionInfo(sessionId);
9551
- if (activeWorkspace?.mdPath) parts.push(workspaceTrajectoryLine(activeWorkspace.mdPath));
9561
+ if (activeWorkspace?.mdPath) {
9562
+ if (!ensureWorkspacePromptSection(ref.agent, sessionId)) parts.push(workspaceTrajectoryLine(activeWorkspace.mdPath));
9563
+ }
9552
9564
  const manualOutput = settings.autoReplyWithLastMessage === false;
9553
9565
  if (manualOutput) parts.push(weixinManualOutputLine(root, accountId, fromUserId));
9554
9566
  if (text) parts.push(text);
@@ -9885,7 +9897,7 @@ function matchCcc(input, candidates) {
9885
9897
  }
9886
9898
  /** 组装候选列表(discoverCccs 投影 + `.serenity` 名;动态 import 保持本模块静态依赖轻量) */
9887
9899
  async function collectCandidates(ctx) {
9888
- const { discoverCccs } = await import("./skiff-debug-CizJBLeZ.js").then((n) => n.i);
9900
+ const { discoverCccs } = await import("./skiff-debug-DYXDdzu3.js").then((n) => n.i);
9889
9901
  const entries = await discoverCccs(ctx, process.cwd());
9890
9902
  const seen = /* @__PURE__ */ new Set();
9891
9903
  const out = [];
@@ -10444,33 +10456,100 @@ function apply(ctx, config) {
10444
10456
  * F4 Skiff 调试服务装配:启停 = 人工(设置面板 Skiff 区块开关,settings 持久化)。
10445
10457
  * settings-changed 事件触发同步(skiffEnabled 开 → 启动调试服务;关 → 停止)。
10446
10458
  * 角色配置(skiff.roles)从当前 CCC 根读取(进程 cwd 优先,live 会话兜底)。
10459
+ *
10460
+ * v1.30.13(S142 诊断 D1):**CCC root 解析失败要重试**。旧实现只在 apply 时同步一次——
10461
+ * 此刻通常还没有 live 会话,进程 cwd(服务启动目录)也不在 CCC 内 → `resolveSkiffRoot`
10462
+ * 返回 null → 打印一行警告后**永不重试**(实证:重启日志
10463
+ * `✗ Skiff 调试服务未启动:无法定位 CCC root`,`ss -ltn` 无 3099;同批 ACP 因容忍
10464
+ * `root ?? undefined` 而正常启动)。现形态三层触发(用户"锚定要准确"同源要求):
10465
+ * ① 启动时同步一次 ② 定位失败 → 定时退避重试(1s/3s/8s/20s/40s,共 5 次)
10466
+ * ③ 首个 live 会话就绪(`agent/session-start` / `session/created`)→ 立即再试一次。
10467
+ *
10468
+ * v1.30.14(重启日志实证后的收敛):**并发启动守卫** + 首次未定位降级为信息级日志。
10469
+ * - 实证(v1.30.13 重启日志):重试定时器与"会话就绪"事件几乎同时触发 → 两次 sync 都
10470
+ * 看到 `started === false` → **两次 start**(第一次绑定成功、第二次 `EADDRINUSE` 刷错误日志)。
10471
+ * → 加 `starting` 在飞标志(启动 Promise settle 前不再重复发起)。
10472
+ * - 首次未定位**不是失败**(apply 时无 live 会话是常态)→ 用 `console.log` 说明"等待 live 会话",
10473
+ * 只有**退避重试耗尽**才 `console.warn` 报错(失败语义只留给真正失败)。
10447
10474
  */
10448
10475
  function registerSkiff(ctx) {
10449
10476
  let started = false;
10450
- const sync = () => {
10477
+ let starting = false;
10478
+ let retryTimer = null;
10479
+ let retries = 0;
10480
+ let informedRoot = false;
10481
+ const clearRetry = () => {
10482
+ if (retryTimer !== null) {
10483
+ clearTimeout(retryTimer);
10484
+ retryTimer = null;
10485
+ }
10486
+ };
10487
+ /** 退避重试定位 CCC root(幂等:已有待执行重试则不再排) */
10488
+ const scheduleRootRetry = () => {
10489
+ if (retryTimer !== null || started || starting) return;
10490
+ if (retries >= SKIFF_ROOT_RETRY_DELAYS_MS.length) {
10491
+ console.warn(`[serenity-hooks] ✗ Skiff 调试服务未启动:重试 ${retries} 次仍无法定位 CCC root(进程 cwd 与 live 会话均无 .serenity)`);
10492
+ return;
10493
+ }
10494
+ const delay = SKIFF_ROOT_RETRY_DELAYS_MS[retries] ?? 0;
10495
+ retryTimer = setTimeout(() => {
10496
+ retryTimer = null;
10497
+ retries += 1;
10498
+ sync();
10499
+ }, delay);
10500
+ retryTimer.unref?.();
10501
+ };
10502
+ function sync() {
10451
10503
  const s = readSimpleSettings();
10452
- if (s.skiffEnabled && !started) {
10504
+ if (s.skiffEnabled && !started && !starting) {
10453
10505
  const root = resolveSkiffRoot(ctx);
10454
10506
  if (!root) {
10455
- console.warn("[serenity-hooks] ✗ Skiff 调试服务未启动:无法定位 CCC root(进程 cwd 与 live 会话均无 .serenity)");
10507
+ if (!informedRoot) {
10508
+ informedRoot = true;
10509
+ console.log("[serenity-hooks] Skiff 调试服务等待 CCC root(此刻无 live 会话;已排入退避重试,会话就绪即试)");
10510
+ }
10511
+ scheduleRootRetry();
10456
10512
  return;
10457
10513
  }
10514
+ clearRetry();
10458
10515
  const webPort = readWebPort(ctx);
10516
+ starting = true;
10459
10517
  startSkiffDebugServer(ctx, root, s.skiffDebugPort, webPort).then(() => {
10460
10518
  started = true;
10519
+ if (retries > 0) console.info(`[serenity-hooks] ✓ Skiff 调试服务已启动(重试 ${retries} 次后定位到 CCC root: ${root})`);
10461
10520
  }).catch((err) => {
10462
10521
  console.error(`[serenity-hooks] ✗ Skiff 调试服务启动失败: ${String(err?.message ?? err)}`);
10522
+ }).finally(() => {
10523
+ starting = false;
10463
10524
  });
10464
10525
  } else if (!s.skiffEnabled && started) {
10465
10526
  stopSkiffDebugServer();
10466
10527
  started = false;
10528
+ } else if (!s.skiffEnabled) {
10529
+ clearRetry();
10530
+ retries = 0;
10531
+ informedRoot = false;
10467
10532
  }
10468
- };
10533
+ }
10469
10534
  try {
10470
10535
  ctx.on("serenity/settings-changed", sync);
10471
10536
  } catch {}
10537
+ for (const eventName of ["agent/session-start", "session/created"]) try {
10538
+ ctx.on(eventName, () => {
10539
+ if (!started) sync();
10540
+ });
10541
+ } catch {}
10472
10542
  sync();
10473
- }
10543
+ registerDisposer(ctx, "skiff root retry timer", clearRetry);
10544
+ }
10545
+ /** Skiff 调试服务 CCC root 退避重试间隔(毫秒;累计 ~72s 后放弃并响亮告警) */
10546
+ const SKIFF_ROOT_RETRY_DELAYS_MS = [
10547
+ 1e3,
10548
+ 3e3,
10549
+ 8e3,
10550
+ 2e4,
10551
+ 4e4
10552
+ ];
10474
10553
  /**
10475
10554
  * 解析 Skiff 调试服务绑定的 CCC 根(v1.25.2 用户指出:skiff 必须绑定 CCC):
10476
10555
  * ① live 会话中**配置了 skiff.roles 的 CCC 优先**(用户认知中的绑定目标)
package/lib/rebuild.d.ts CHANGED
@@ -71,7 +71,17 @@ export interface RebuildResult {
71
71
  /**
72
72
  * 稳固的 SESSION.md 定位(多层候选 + 存在性校验,**绝不输出虚假路径**):
73
73
  * ① 内存活跃会话(本会话显式 use 过)→ ② events 恢复(use 标记 + 重建锚点规范行,进程重启后)
74
- * → ③ surface 首条锚点(events 异常时兜底)→ ④ AGENT_SESSIONS 约定回退(最新未完成活动目录)。
74
+ * → ②b **权威绑定**(`AGENT_SESSIONS/.bindings.json`,本会话持久绑定)→ ③ surface 首条锚点
75
+ * (events 异常时兜底)。
76
+ *
77
+ * v1.30.13(S142 用户"微信桥 + skiff 会话锚定要准确",诊断 D4):**移除**原 ④「全局最新未完成
78
+ * 会话」(`findLatestActiveSessionMd`)。理由(R↓):④ 是**跨轨迹**猜测——候选①②③全空时,
79
+ * 它会把 rebuild 静默接到 AGENT_SESSIONS 里最新的另一个会话(临时 skiff 会话 persistent=false、
80
+ * 重启后首条消息前、Danica 新会话都命中该形态),而"接错轨迹"比重建失败危险得多
81
+ * (错误轨迹被继续写入 + 正确轨迹静默丢失)。现在全部候选失败 → 返回 null → 调用方
82
+ * 报错引导用户显式 `logbook use`(响亮失败优于静默接错)。
83
+ * 备选:④ 仅在"同 scope 确无绑定"时兜底——同样会接错轨迹,故不采纳。
84
+ *
75
85
  * 每候选 resolve 后 existsSync 校验(相对路径按 root 解析);全部失败返回 null → 调用方报错引导。
76
86
  */
77
87
  export declare function resolveSessionMdPath(root: string, scope: string, session: Session): string | null;
@@ -412,19 +412,6 @@ function resolveSessionByTitle(title, sessionsDir) {
412
412
  if (fuzzy.length === 1) return join(fuzzy[0].path, SESSION_MD);
413
413
  return null;
414
414
  }
415
- /**
416
- * 约定回退(v1.24.11):AGENT_SESSIONS 下最新修改的**未完成**会话的 SESSION.md。
417
- * readAllSessions 已按「未完成优先 + mtime 降序」排序 → 首个未完成且含 SESSION.md 即最新活动。
418
- * 只作最后手段(内存/events/锚点全缺时),保证重建锚点至少指向一个真实存在的轨迹。
419
- */
420
- function findLatestActiveSessionMd(root) {
421
- for (const s of readAllSessions(sessionsRoot(root))) {
422
- if (s.status.completed) continue;
423
- const md = join(s.path, SESSION_MD);
424
- if (existsSync(md)) return md;
425
- }
426
- return null;
427
- }
428
415
  /** health 子命令(对齐 osp healthCheck:stale/stalled/ghost/drift 四类检查,文本输出) */
429
416
  function healthCheck(root) {
430
417
  const sessions = readAllSessions(sessionsRoot(root));
@@ -776,4 +763,4 @@ function appendBound(session, action, rec) {
776
763
  }
777
764
  }
778
765
  //#endregion
779
- export { summarize as C, showSession as S, readActiveSessionMd as _, archiveSessions as a, sessionsRoot as b, createSession as c, findSession as d, getActiveSessionInfo as f, qaCheck as g, parseSessionContextFromEvents as h, SESSION_ACTIONS as i, extractSessionMdPathFromText as l, listSessions as m, readLastBound as n, clearActiveSessionInfo as o, healthCheck as p, DEFAULT_SESSION_SCOPE as r, closeSession as s, appendBound as t, findLatestActiveSessionMd as u, resolveSessionByTitle as v, useSession as w, setActiveSessionInfo as x, sessionEvents as y };
766
+ export { useSession as C, summarize as S, resolveSessionByTitle as _, archiveSessions as a, setActiveSessionInfo as b, createSession as c, getActiveSessionInfo as d, healthCheck as f, readActiveSessionMd as g, qaCheck as h, SESSION_ACTIONS as i, extractSessionMdPathFromText as l, parseSessionContextFromEvents as m, readLastBound as n, clearActiveSessionInfo as o, listSessions as p, DEFAULT_SESSION_SCOPE as r, closeSession as s, appendBound as t, findSession as u, sessionEvents as v, showSession as x, sessionsRoot as y };
@@ -31,10 +31,36 @@ export declare function skiffSessionSnapshot(): ReadonlyMap<string, {
31
31
  * 系统提示词中的**工作台纪律块**(v1.30.4 升级:从单行路径 → 完整 ACC 层约束)。
32
32
  * 用户点破(S142):只给路径 LLM 不会主动用——需注入「已绑定 + 使用纪律 + 动作指引」。
33
33
  * SESSION = 工作台——skiff 与主舱同一套机制(零特调,用户拍板):
34
- * 自动绑定已生效(无需 logbook use)、SESSION.md 是持久记忆载体(write/edit 维护)、
35
- * rebuild 自动从本 SESSION 续接。注入 agent 系统提示词(用户对话面不可见)。
34
+ * 自动绑定已生效(无需 logbook use)、SESSION.md 是持久记忆载体、rebuild 自动从本 SESSION 续接。
35
+ * 注入 agent 系统提示词(用户对话面不可见)。
36
+ *
37
+ * v1.30.13(S142 诊断 D5):写入纪律**不点名具体工具**。旧文案写死 "with write/edit",
38
+ * 而 zhaocai 角色的工具白名单已移除 write/edit(写能力收归 CCC 的 session-write MSM)
39
+ * → 指令指向它根本没有的工具(注入纪律与能力面矛盾,模型只能猜)。
40
+ * 现文案改为「用本角色被授权的写入通道」——ACC 只声明义务(写进工作台),
41
+ * 具体通道由 CCC 的角色配置与角色提示词决定(ACC/CCC 归属二分;也不把某个 CCC 的
42
+ * MSM 名硬编码进 ACC 源码)。
36
43
  */
37
44
  export declare function workspaceTrajectoryLine(mdPath: string): string;
45
+ /**
46
+ * 把**工作台纪律块**挂到 agent 的系统提示词段(单一真相源,v1.30.13 用户需求)。
47
+ *
48
+ * 用户原话(S142,2026-09-08):"微信桥的注入机制,每个用户消息都会注入,skiff 本身也会注入,
49
+ * 这样就重复,能否微信桥情况下,注入内容直接取 skiff 的,这样不用配两遍"。
50
+ *
51
+ * 现状(改前):`createSkiffAgent` 把 `workspaceTrajectoryLine(mdPath)` 塞进基础段
52
+ * (创建时一次快照),微信桥又**每条消息**把同一文本拼进 question(v1.30.4 为覆盖
53
+ * "live/existing 快路径不重挂提示词"而加)→ 新建 agent 首轮起即重复,且每轮重复付 token。
54
+ *
55
+ * 现形态(单一注入点):工作台行**只**经本函数挂系统提示词段(name `serenity-skiff-workspace`,
56
+ * 动态 `text()` 每轮按 scope 读当前活跃 mdPath → 绑定变化自动跟随,无需重挂):
57
+ * - `createSkiffAgent` 调它(不再把工作台行拼进基础段);
58
+ * - 微信桥对 live/existing agent 调它(不再拼 question)。
59
+ *
60
+ * @returns true = 段已在位(本次注册成功或此前已注册);false = 该 agent 无法挂系统提示词段
61
+ * (宿主 systemPrompt 缺失/注册抛错)→ 调用方降级为 question 前缀注入(约束必须在场)
62
+ */
63
+ export declare function ensureWorkspacePromptSection(agent: Agent, scope: string): boolean;
38
64
  /**
39
65
  * 确保启用 session 能力的 skiff 会话拥有**专属 SESSION**(工作台):
40
66
  * 懒绑定 + 幂等(per skiff 会话 scope 隔离——微信多用户各自独立,永不共享同一份)。
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { a as findSerenityRoot, f as readHandymanConfig } from "./ccc-DAsSHsub.js";
3
3
  import { a as resolveRoleSystemPrompt, i as readSkiffRoles, n as buildSkiffBasePrompt, o as roleMsmWhitelist, r as isSkiffSessionId, u as trajectorySubset } from "./skiff-role-DlrbHPLD.js";
4
4
  import { i as hostSessions, r as hostService, t as hostAgents } from "./access-dU_vG1q8.js";
5
- import { c as createSession, f as getActiveSessionInfo, n as readLastBound, t as appendBound, x as setActiveSessionInfo } from "./session-bound-D2ANqVn-.js";
5
+ import { b as setActiveSessionInfo, c as createSession, d as getActiveSessionInfo, n as readLastBound, t as appendBound } from "./session-bound-BpTayaIS.js";
6
6
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
7
7
  import { basename, join } from "node:path";
8
8
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
@@ -293,8 +293,15 @@ const AUTO_BOUND_NOTE_PREFIX = "auto-created for skiff role";
293
293
  * 系统提示词中的**工作台纪律块**(v1.30.4 升级:从单行路径 → 完整 ACC 层约束)。
294
294
  * 用户点破(S142):只给路径 LLM 不会主动用——需注入「已绑定 + 使用纪律 + 动作指引」。
295
295
  * SESSION = 工作台——skiff 与主舱同一套机制(零特调,用户拍板):
296
- * 自动绑定已生效(无需 logbook use)、SESSION.md 是持久记忆载体(write/edit 维护)、
297
- * rebuild 自动从本 SESSION 续接。注入 agent 系统提示词(用户对话面不可见)。
296
+ * 自动绑定已生效(无需 logbook use)、SESSION.md 是持久记忆载体、rebuild 自动从本 SESSION 续接。
297
+ * 注入 agent 系统提示词(用户对话面不可见)。
298
+ *
299
+ * v1.30.13(S142 诊断 D5):写入纪律**不点名具体工具**。旧文案写死 "with write/edit",
300
+ * 而 zhaocai 角色的工具白名单已移除 write/edit(写能力收归 CCC 的 session-write MSM)
301
+ * → 指令指向它根本没有的工具(注入纪律与能力面矛盾,模型只能猜)。
302
+ * 现文案改为「用本角色被授权的写入通道」——ACC 只声明义务(写进工作台),
303
+ * 具体通道由 CCC 的角色配置与角色提示词决定(ACC/CCC 归属二分;也不把某个 CCC 的
304
+ * MSM 名硬编码进 ACC 源码)。
298
305
  */
299
306
  function workspaceTrajectoryLine(mdPath) {
300
307
  return [
@@ -305,8 +312,10 @@ function workspaceTrajectoryLine(mdPath) {
305
312
  "Rules of the workspace:",
306
313
  " 1. This SESSION.md is your persistent memory carrier (like the main cabin SESSION) — ",
307
314
  " read it first when context feels thin or work spans turns.",
308
- " 2. Record key decisions/progress/unresolved items INTO this SESSION.md with write/edit",
309
- " (sections: 目标 / 状态 / 关键决策 / 进度记录 / 未解决的问题). Keep it currentit is",
315
+ " 2. Record key decisions/progress/unresolved items INTO this SESSION.md using the write channel",
316
+ " your role is actually granted (use whatever write tool or MSM this role has never assume",
317
+ " a specific write tool exists; if none is granted, report that instead of pretending to write).",
318
+ " Sections: 目标 / 状态 / 关键决策 / 进度记录 / 未解决的问题. Keep it current — it is",
310
319
  " what a rebuild resumes from.",
311
320
  " 3. When context pressure is high, run logbook rebuild — it auto-resumes from THIS SESSION.md",
312
321
  " (no manual use; do not switch to or read other SESSIONs unless the user explicitly asks).",
@@ -315,6 +324,50 @@ function workspaceTrajectoryLine(mdPath) {
315
324
  ].join("\n");
316
325
  }
317
326
  /**
327
+ * 已挂工作台提示词段的 agent(幂等:同一 agent 只注册一次,避免 duplicate section)。
328
+ * WeakSet → agent 被回收即自动清理,无泄漏。
329
+ */
330
+ const workspaceSectionAgents = /* @__PURE__ */ new WeakSet();
331
+ /**
332
+ * 把**工作台纪律块**挂到 agent 的系统提示词段(单一真相源,v1.30.13 用户需求)。
333
+ *
334
+ * 用户原话(S142,2026-09-08):"微信桥的注入机制,每个用户消息都会注入,skiff 本身也会注入,
335
+ * 这样就重复,能否微信桥情况下,注入内容直接取 skiff 的,这样不用配两遍"。
336
+ *
337
+ * 现状(改前):`createSkiffAgent` 把 `workspaceTrajectoryLine(mdPath)` 塞进基础段
338
+ * (创建时一次快照),微信桥又**每条消息**把同一文本拼进 question(v1.30.4 为覆盖
339
+ * "live/existing 快路径不重挂提示词"而加)→ 新建 agent 首轮起即重复,且每轮重复付 token。
340
+ *
341
+ * 现形态(单一注入点):工作台行**只**经本函数挂系统提示词段(name `serenity-skiff-workspace`,
342
+ * 动态 `text()` 每轮按 scope 读当前活跃 mdPath → 绑定变化自动跟随,无需重挂):
343
+ * - `createSkiffAgent` 调它(不再把工作台行拼进基础段);
344
+ * - 微信桥对 live/existing agent 调它(不再拼 question)。
345
+ *
346
+ * @returns true = 段已在位(本次注册成功或此前已注册);false = 该 agent 无法挂系统提示词段
347
+ * (宿主 systemPrompt 缺失/注册抛错)→ 调用方降级为 question 前缀注入(约束必须在场)
348
+ */
349
+ function ensureWorkspacePromptSection(agent, scope) {
350
+ if (!agent || typeof agent !== "object") return false;
351
+ if (workspaceSectionAgents.has(agent)) return true;
352
+ const api = agent.ctx?.systemPrompt;
353
+ if (typeof api?.section !== "function") return false;
354
+ try {
355
+ api.section({
356
+ name: "serenity-skiff-workspace",
357
+ order: -55,
358
+ text: () => {
359
+ const mdPath = getActiveSessionInfo(scope)?.mdPath;
360
+ return mdPath ? workspaceTrajectoryLine(mdPath) : "";
361
+ }
362
+ });
363
+ workspaceSectionAgents.add(agent);
364
+ return true;
365
+ } catch (err) {
366
+ console.warn(`[serenity-hooks] skiff 工作台提示词段注册失败(回退 question 注入): ${String(err?.message ?? err)}`);
367
+ return false;
368
+ }
369
+ }
370
+ /**
318
371
  * 确保启用 session 能力的 skiff 会话拥有**专属 SESSION**(工作台):
319
372
  * 懒绑定 + 幂等(per skiff 会话 scope 隔离——微信多用户各自独立,永不共享同一份)。
320
373
  *
@@ -421,15 +474,12 @@ async function createSkiffAgent(ctx, root, roleName, role, defaultModel, session
421
474
  agent.ctx.systemPrompt.section({
422
475
  name: "serenity-skiff",
423
476
  order: -60,
424
- text: () => [
425
- buildSkiffBasePrompt(roleName, role),
426
- workspaceMdPath ? workspaceTrajectoryLine(workspaceMdPath) : "",
427
- cccPrompt
428
- ].filter(Boolean).join("\n")
477
+ text: () => [buildSkiffBasePrompt(roleName, role), cccPrompt].filter(Boolean).join("\n")
429
478
  });
430
479
  } catch (err) {
431
480
  console.warn(`[serenity-hooks] skiff 系统提示词注册失败: ${String(err?.message ?? err)}`);
432
481
  }
482
+ if (workspaceMdPath) ensureWorkspacePromptSection(agent, id);
433
483
  registerSkiffSession(id, roleName, root, agent);
434
484
  return {
435
485
  handle,
@@ -2793,4 +2843,4 @@ async function handle(ctx, defaultRoot, webPort, req, res) {
2793
2843
  }
2794
2844
  }
2795
2845
  //#endregion
2796
- export { newStopToken as C, writeFailedStatus as D, splitModel as E, writeProgress as O, listActiveHandymen as S, requireWhitelistedModel as T, workspaceTrajectoryLine as _, startSkiffDebugServer as a, buildRoundPrompt as b, askSkiff as c, getSkiffAgent as d, skiffMsmGate as f, unregisterSkiffSession as g, skiffTrajectoryEnabled as h, skiff_debug_exports as i, skiffRoleFor$1 as k, createSkiffAgent as l, skiffSessionSnapshot as m, jscSafeJsonText as n, stopSkiffDebugServer as o, skiffSessionInfo as p, renderSkiffMarkdown as r, stripThink as s, discoverCccs as t, ensureSkiffSession as u, waitAgentIdle as v, readProgress as w, handymanProgressPaths as x, HANDYMAN_GUIDE as y };
2846
+ export { skiffRoleFor$1 as A, listActiveHandymen as C, splitModel as D, requireWhitelistedModel as E, writeFailedStatus as O, handymanProgressPaths as S, readProgress as T, unregisterSkiffSession as _, startSkiffDebugServer as a, HANDYMAN_GUIDE as b, askSkiff as c, ensureWorkspacePromptSection as d, getSkiffAgent as f, skiffTrajectoryEnabled as g, skiffSessionSnapshot as h, skiff_debug_exports as i, writeProgress as k, createSkiffAgent as l, skiffSessionInfo as m, jscSafeJsonText as n, stopSkiffDebugServer as o, skiffMsmGate as p, renderSkiffMarkdown as r, stripThink as s, discoverCccs as t, ensureSkiffSession as u, workspaceTrajectoryLine as v, newStopToken as w, buildRoundPrompt as x, waitAgentIdle as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.30.12",
3
+ "version": "1.30.14",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:10 个工具(container_fs/logbook/dashboard/container_git/msm/praxis/handyman/localstore/container_admin/autopilot-trajectory)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/自主巡航。适配 DSH 0.1.2-rc.1。",
5
5
  "license": "MIT",
6
6
  "repository": {