@shgroup/dsh-serenity-hooks 1.111.0 → 1.113.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.111.0",
3
+ "version": "1.113.0",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——12 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/localstore/container_admin;另两个 im-bridge/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/IM 桥/认知轨迹唤醒注册表。轨迹声明 = 轨迹目录下的 trajectory.json(约束面 + 旧标识 uid + CRO 机器绑定 cro.host;旧名 trajectory-constraint.json / skiff.json 仍在读取族里);轨迹提示词 = 轨迹目录下的 PROMPT.md(一条轨迹「是谁、按什么章程工作」,所有轨迹通用,追加在容器层与质量层之后,不替换它们)。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
@@ -1,5 +1,5 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
2
- import { a as findSerenityRoot } from "./ccc-BmpE3cQz.js";
2
+ import { a as findSerenityRoot } from "./ccc-x94FCzOv.js";
3
3
  import { basename } from "node:path";
4
4
  //#region src/host/access.ts
5
5
  /**
@@ -235,7 +235,7 @@ async function listCccs(ctx, opts = {}) {
235
235
  if (typeof defaultRoot === "string" && defaultRoot !== "" && !c.roots.includes(defaultRoot)) c.roots.unshift(defaultRoot);
236
236
  let rolesOf = null;
237
237
  if (opts.withRoles === true) {
238
- const { listTrajectoryConstraints } = await import("./profile-BjBn_-be.js").then((n) => n.a);
238
+ const { listTrajectoryConstraints } = await import("./profile-Djqz4Noy.js").then((n) => n.a);
239
239
  rolesOf = (root) => {
240
240
  try {
241
241
  return listTrajectoryConstraints(root).map((p) => p.code);
@@ -220,6 +220,22 @@ function readTrajectorySubscriptions(root, paths = DEFAULT_SERENITY_CONFIG_PATHS
220
220
  };
221
221
  }
222
222
  /**
223
+ * 用户语录台账是否开启(`userQuotes.enabled`,v1.113.0)。
224
+ *
225
+ * 🔴 **判据是 `=== true`,不是"键存在"**:写成 `"true"` / `1` / 缺 `enabled`
226
+ * 一律当**关**。理由见 `SerenityConfig.userQuotes` —— 这是"长期留存用户原话",
227
+ * 含糊的配置必须落到**不写**那一侧(fail-closed,与订阅表的 fail-open 方向相反,
228
+ * 因为这里的危险方向是**多写**,那里是**少通知**)。
229
+ *
230
+ * ⚠️ 配置损坏 ⇒ `loadSerenityConfig` 已响亮告警并返回 `{}` ⇒ 这里得到 `false`(关)。
231
+ * 即"配置坏了就不记",而不是"配置坏了就记到默认位置"。
232
+ */
233
+ function readUserQuotesEnabled(root, paths = DEFAULT_SERENITY_CONFIG_PATHS) {
234
+ const raw = loadSerenityConfig(root, paths).userQuotes;
235
+ if (typeof raw !== "object" || raw === null) return false;
236
+ return raw.enabled === true;
237
+ }
238
+ /**
223
239
  * 读取 CCC 声明的「安全模式期间隐藏的工具」清单(`safeMode.hiddenTools`,v1.51.4)。
224
240
  *
225
241
  * 语义(R↓):**标记文件存在 ⇒ 才生效**。deny = `['bash', ...本清单]`(并集去重);
@@ -303,4 +319,4 @@ function matchBlacklist(relPath, rules) {
303
319
  return null;
304
320
  }
305
321
  //#endregion
306
- export { findSerenityRoot as a, matchBlacklist as c, readCccName as d, readCognitiveTrajectorySkills as f, resolveInside as g, readUtf8 as h, findGitRoot as i, pathInside as l, readTrajectorySubscriptions as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readSafeModeHiddenTools as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
322
+ export { resolveInside as _, findSerenityRoot as a, matchBlacklist as c, readCccName as d, readCognitiveTrajectorySkills as f, readUtf8 as g, readUserQuotesEnabled as h, findGitRoot as i, pathInside as l, readTrajectorySubscriptions as m, SAFE_MODE_MARKER as n, isSafeModeOn as o, readSafeModeHiddenTools as p, classifyPath as r, loadSerenityConfig as s, DEFAULT_SERENITY_CONFIG_PATHS as t, readBlacklist as u };
@@ -1,4 +1,4 @@
1
- import { c as readWeixinSettings, o as readImSettings } from "./route-reyv8qcB.js";
1
+ import { c as readWeixinSettings, o as readImSettings } from "./route-D-W3O2PF.js";
2
2
  import { isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
4
4
  //#region src/im/log-scan.ts
@@ -1,9 +1,9 @@
1
- import { l as hostInjected, r as cccRootForCwd, u as hostService } from "./ccc-roots-DPMwBywc.js";
2
- import { c as matchBlacklist, d as readCccName$1, l as pathInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readSafeModeHiddenTools, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-BmpE3cQz.js";
1
+ import { l as hostInjected, r as cccRootForCwd, u as hostService } from "./ccc-roots-CsSSQjSS.js";
2
+ import { c as matchBlacklist, d as readCccName$1, l as pathInside, n as SAFE_MODE_MARKER, o as isSafeModeOn, p as readSafeModeHiddenTools, r as classifyPath, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS, u as readBlacklist } from "./ccc-x94FCzOv.js";
3
3
  import { F as isoLocal, g as listActiveTrajectories } from "./bind-file-B0TuOP89.js";
4
- import { c as profileWriteWhitelist, s as profileToolWhitelist, u as ACC_VERSION } from "./rules-BLkYTPUy.js";
5
- import { l as trajectoryCodeForSession, u as trajectoryConstraintForSession } from "./profile-BjBn_-be.js";
6
- import { n as isImWiredTrajectory } from "./channels-Dty_NIl_.js";
4
+ import { c as profileWriteWhitelist, s as profileToolWhitelist, u as ACC_VERSION } from "./rules-DKlpuFN_.js";
5
+ import { l as trajectoryCodeForSession, u as trajectoryConstraintForSession } from "./profile-Djqz4Noy.js";
6
+ import { n as isImWiredTrajectory } from "./channels-DWZlcsE0.js";
7
7
  import { REGISTERED_TOOLS } from "./invariant.js";
8
8
  import { basename, dirname, join, relative, resolve } from "node:path";
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -185,7 +185,7 @@ Directory of ACC capabilities — each area lists where to go for details (guide
185
185
  ② 认知质量框架 → praxis(eap/neat/cce/ontology 四合一——可实践理论注入,section 渐进披露,无参即目录)
186
186
  ontology(v1.52 D121:本体工程**引擎**——init/check/explain/verify/review/compliance;
187
187
  与 praxis ontology 的分工 = 那个是**方法文本**、这个是**能跑的那个**)
188
- ③ 工具与执行 → container_fs(容器文件系统 15 子命令)/ container_git(git)/ dashboard(health 含注册表检查/time/wait)
188
+ ③ 工具与执行 → container_fs(容器文件系统 15 子命令)/ container_git(git)/ dashboard(health 含注册表检查/time/diag)
189
189
  acc_msm 已并入 msm(执行+发现)与 container_admin(管理)——msm("<name>", ["<args>"]);inspect=true 查用法
190
190
  (v1.106.0 已删 exclusiveTools 机制:原专属工具 acc-diag **已并入 dashboard diag**)
191
191
  ④ 轨迹约束与对外面 → container_admin config(轨迹约束四动作 guide/validate/apply/list——**原 role 域已并入 config**,旧名作过渡别名仍可用;说明类配置走**两层菜单** config docs 目录 / config docs <章节> 详情)
@@ -458,6 +458,32 @@ CCC 级微信个人号接入(iLink 协议):dsh 一进程多 CCC,每 CCC
458
458
  ▸ 旧配置里残留 exclusiveTools ⇒ **静默忽略**(schemastery 非 strict),不影响加载。
459
459
  ▸ ⚠️ 连带后果(owner 已知并确认):dashboard 是常驻工具 ⇒ diag 段对**所有轨迹**可见,
460
460
  而它的 live 会话清单是**全部 CCC** 的(跨容器)。这是「方案 C」的既定代价,不是缺陷。
461
+
462
+ ── 9. userQuotes.enabled(用户语录台账,v1.113.0)──
463
+ **把用户直接说的话按天、按轨迹存下来**,供之后反向分析用户。owner 原话:
464
+ 「我需要一个可开启关闭的功能,就是记录用户直接对谁说过什么,对谁是指对具体的某个轨迹……
465
+ 这个功能如果开启,则默认写到一个指定目录内,按天进行记录。这个功能是用于具体的宁静号CCC反向来分析用户的」。
466
+
467
+ Config:
468
+ { "userQuotes": { "enabled": true } }
469
+
470
+ 默认:**关**。🔴 判据是 \`enabled === true\`,⛔ 不是"键存在"——
471
+ 写成 "true" / 1 / 只写 userQuotes: {} 一律当**关**。理由:这是把用户原话原文**长期留存**,
472
+ 含糊的配置必须落到"不写"那一侧(fail-closed;与订阅表的 fail-open 方向相反,
473
+ 因为这里的危险方向是**多写**)。
474
+
475
+ 落点:\`AGENT_SESSIONS/_user-quotes/<YYYY-MM-DD>.jsonl\`(按天,一行一条):
476
+ {"ts":"…+08:00","trajectory":"<轨迹目录名 或 null>","sessionId":"…","messageId":"…","text":"(原话,一字不改)","chars":88}
477
+
478
+ ▸ **采集点** = \`agent/pre-step\` 的 \`payload.messages\`(本 step 从 inbox 取出、即将进本步的消息)。
479
+ ▸ **判别面 = \`source.kind === 'user'\`**(结构性,⛔ 不靠措辞黑名单)。
480
+ ▸ **"对谁" = 按轨迹**(Q1),取绑定真相源(\`current.dsh_session_bind\`);未绑定 ⇒ \`null\` 但**照常记**。
481
+ ▸ **全文**(Q3):一字不改,不做摘要;\`chars\` 是**码点数**不是 UTF-16 单元数。
482
+ ▸ 两个**冒充 \`kind:'user'\`** 的生产者(载体开场白/rebuild 锚点)按各自标记排除。
483
+ ▸ IM 入站(微信/飞书)**不会混进来**:它走 \`deliverToTrajectory\`,kind 是
484
+ \`plugin:dsh-serenity-hooks\`(已取证,非推演)。⇒ 台账 = **界面直话**。
485
+ ▸ 采集失败只 warn、**不阻断 step**(台账是旁路观测,不是被观测对象的组成部分)。
486
+ 判据全文(含取证与变异检验设计)⇒ \`src/trajectory/user-quotes.ts\` 头注释。
461
487
  `;
462
488
  function parseRegistry(raw) {
463
489
  const data = JSON.parse(raw.replace(/^\uFEFF/, ""));
package/lib/index.js CHANGED
@@ -1,19 +1,19 @@
1
1
  import { i as __toESM, n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
2
- import { c as hostAgents, d as hostSessions, f as hostSettings, i as cccRootForExec, m as hostWebServer, n as agentCwdFor, o as listCccs, p as hostWeb, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-DPMwBywc.js";
3
- import { a as findSerenityRoot$1, d as readCccName, g as resolveInside, l as pathInside, m as readTrajectorySubscriptions, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-BmpE3cQz.js";
2
+ import { c as hostAgents, d as hostSessions, f as hostSettings, i as cccRootForExec, m as hostWebServer, n as agentCwdFor, o as listCccs, p as hostWeb, r as cccRootForCwd, t as NO_CCC_FROM_AGENT_CWD, u as hostService } from "./ccc-roots-CsSSQjSS.js";
3
+ import { _ as resolveInside, a as findSerenityRoot$1, d as readCccName, h as readUserQuotesEnabled, l as pathInside, m as readTrajectorySubscriptions, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
4
4
  import { A as useSession, C as resolveSessionByTitle, D as showSession, E as setActiveSessionInfo, F as isoLocal, I as localFileStamp, M as TRAJECTORY_LEGACY_FILENAMES, O as summarize, S as readTrajectoryUid, T as sessionsRoot, _ as listSessions, b as parseTrajectoryCode, c as UID_FILENAME, d as createSession, f as extractSessionMdPathFromText, j as TRAJECTORY_FILENAME, k as trajectoryDescOf, l as archiveSession, m as getActiveSessionInfo, o as COGNITIVE_TRAJECTORY_ACTIONS, p as findSession, u as clearActiveSessionInfo, v as namingTitleFor, w as sessionEvents, y as parseSessionContextFromEvents } from "./bind-file-B0TuOP89.js";
5
- import { n as checkLocalstoreGitCompliance, o as readStore, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./service-Z3aSR1cn.js";
6
- import { d as findPackageRoot, i as constraintSystemPromptSource, l as resolveConstraintSystemPrompt, r as constraintCognitiveTrajectory, t as TRAJECTORY_CONSTRAINT_HINT, u as ACC_VERSION } from "./rules-BLkYTPUy.js";
7
- import { C as docsPointer, E as setWeixinSendEndpoint, S as runMsmAsync, T as renderDocsIndex, _ as CCC_CONFIG_REFERENCE, a as setSafeMode, b as loadMsmEntries, d as forgetNonCccToolVisibility, g as ACC_CATALOG, h as forgetToolVisibility, i as readDshVersion, m as syncToolVisibility, n as summarizeHostContract, o as listOpencodeSkillDirs, p as registerGuards, r as getStatus, s as loadOpencodeSkill, t as hostContractReport, w as findDocsChapter, x as runMsm, y as findEntry } from "./contract-BiNzzd77.js";
8
- import { c as replacesDefaultIdentity, i as listTrajectoryConstraints, l as trajectoryCodeForSession, n as findDirNameByCode, o as readTrajectoryConstraintAt, r as isTrajectoryConstrained, s as readTrajectoryConstraintByCode, t as TRAJECTORY_CONSTRAINT_FILENAME } from "./profile-BjBn_-be.js";
9
- import { A as truncateContent, C as verifyTotpCode, D as buildResourceIndex, E as SKILL_ONTOLOGY_FILE, M as hasTrajectoryPrompt, O as buildResourceNameSuffix, S as MECHANISM_PORTS, T as REFERENCE_DIR, _ as projectKnownWorkspaces, a as trajectoryConstraintFor, b as verifyPassword, c as trajectoryWorkspaceMdPath, d as splitModel, f as registerCognitiveTrajectorySkillSection, g as migrateLegacyLocalstore, h as applyWirePatch, i as getTrajectoryAgent, j as TRAJECTORY_PROMPT_FILENAME, k as isSafeSkillName, l as unregisterTrajectorySession, m as registerEntrySkillSectionGlobal, n as deliverToTrajectory, o as trajectoryConstraintMsmGate, p as registerEntrySkillSection, r as ensureWorkspacePromptSection, s as trajectoryConstraintParticipates, t as createTrajectoryAgent, u as workspaceCognitiveTrajectoryLine, v as readAdvancedSettings, w as recordToolUsage, x as FACE_PORTS, y as toWire } from "./session-BlgUdKH4.js";
10
- import { n as PLUGIN_SOURCE, o as readLastBound, r as appendBound, s as resolveSessionCognitiveTrajectoryLabel, t as ACC_MESSAGE_KIND } from "./message-source-BTIe2cVO.js";
11
- import { a as DEFAULT_LOOKBACK_MS, c as scanInterruptedSessions, d as registerDisposer, f as buildCroSnapshot, h as addWake, l as listRunningSessionIds, m as WAKE_CATCH_UP_MS, o as createResumeOne, r as sendToCognitiveTrajectory, s as projectDirNameForCwd, t as registerWakeScheduler, u as registerCroTurnTracking } from "./scheduler-B0pB9mxI.js";
5
+ import { n as checkLocalstoreGitCompliance, o as readStore, s as runLocalStore, t as LOCALSTORE_SCOPES } from "./service-Dv-8E2bI.js";
6
+ import { d as findPackageRoot, i as constraintSystemPromptSource, l as resolveConstraintSystemPrompt, r as constraintCognitiveTrajectory, t as TRAJECTORY_CONSTRAINT_HINT, u as ACC_VERSION } from "./rules-DKlpuFN_.js";
7
+ import { C as docsPointer, E as setWeixinSendEndpoint, S as runMsmAsync, T as renderDocsIndex, _ as CCC_CONFIG_REFERENCE, a as setSafeMode, b as loadMsmEntries, d as forgetNonCccToolVisibility, g as ACC_CATALOG, h as forgetToolVisibility, i as readDshVersion, m as syncToolVisibility, n as summarizeHostContract, o as listOpencodeSkillDirs, p as registerGuards, r as getStatus, s as loadOpencodeSkill, t as hostContractReport, w as findDocsChapter, x as runMsm, y as findEntry } from "./contract-DJSTR5Up.js";
8
+ import { c as replacesDefaultIdentity, i as listTrajectoryConstraints, l as trajectoryCodeForSession, n as findDirNameByCode, o as readTrajectoryConstraintAt, r as isTrajectoryConstrained, s as readTrajectoryConstraintByCode, t as TRAJECTORY_CONSTRAINT_FILENAME } from "./profile-Djqz4Noy.js";
9
+ import { A as truncateContent, C as verifyTotpCode, D as buildResourceIndex, E as SKILL_ONTOLOGY_FILE, M as hasTrajectoryPrompt, O as buildResourceNameSuffix, S as MECHANISM_PORTS, T as REFERENCE_DIR, _ as projectKnownWorkspaces, a as trajectoryConstraintFor, b as verifyPassword, c as trajectoryWorkspaceMdPath, d as splitModel, f as registerCognitiveTrajectorySkillSection, g as migrateLegacyLocalstore, h as applyWirePatch, i as getTrajectoryAgent, j as TRAJECTORY_PROMPT_FILENAME, k as isSafeSkillName, l as unregisterTrajectorySession, m as registerEntrySkillSectionGlobal, n as deliverToTrajectory, o as trajectoryConstraintMsmGate, p as registerEntrySkillSection, r as ensureWorkspacePromptSection, s as trajectoryConstraintParticipates, t as createTrajectoryAgent, u as workspaceCognitiveTrajectoryLine, v as readAdvancedSettings, w as recordToolUsage, x as FACE_PORTS, y as toWire } from "./session-C7-l8Kzs.js";
10
+ import { a as findTrajectoryOfSession, c as resolveSessionCognitiveTrajectoryLabel, n as PLUGIN_SOURCE, r as appendBound, s as readLastBound, t as ACC_MESSAGE_KIND } from "./message-source-DLei3MqR.js";
11
+ import { a as DEFAULT_LOOKBACK_MS, c as scanInterruptedSessions, d as registerDisposer, f as buildCroSnapshot, g as addWake, h as WAKE_CATCH_UP_MS, l as listRunningSessionIds, m as CARRIER_OPENING_MARK, o as createResumeOne, r as sendToCognitiveTrajectory, s as projectDirNameForCwd, t as registerWakeScheduler, u as registerCroTurnTracking } from "./scheduler-CB06u0Qc.js";
12
12
  import { i as sessionsRootDir } from "./cleanup-CPl1SSaK.js";
13
- import { n as readSimpleSettings, r as registerSettingsSection } from "./settings-DY4_DYYY.js";
14
- import { a as readFeishuCredential, c as readWeixinSettings, d as weixinInboundDir, f as weixinSessionIdForTrajectory, i as matchWeixinRoute, n as extractWeixinText, r as hasVoiceItem, s as readWeixinCredential, t as extractWeixinMedia, u as sanitizeFileName$1 } from "./route-reyv8qcB.js";
15
- import { i as runImBridge, n as isImWiredTrajectory, r as registerImChannel, t as imChannelIds } from "./channels-Dty_NIl_.js";
16
- import { i as subscriptionStats, n as registerTrajectorySubscriptions, r as subscriptionEval, t as defaultSubscriptionDeps } from "./subscriptions-DBR99OVm.js";
13
+ import { n as readSimpleSettings, r as registerSettingsSection } from "./settings-DibHCfEU.js";
14
+ import { a as readFeishuCredential, c as readWeixinSettings, d as weixinInboundDir, f as weixinSessionIdForTrajectory, i as matchWeixinRoute, n as extractWeixinText, r as hasVoiceItem, s as readWeixinCredential, t as extractWeixinMedia, u as sanitizeFileName$1 } from "./route-D-W3O2PF.js";
15
+ import { i as runImBridge, n as isImWiredTrajectory, r as registerImChannel, t as imChannelIds } from "./channels-DWZlcsE0.js";
16
+ import { i as subscriptionStats, n as registerTrajectorySubscriptions, r as subscriptionEval, t as defaultSubscriptionDeps } from "./subscriptions-D05XffxT.js";
17
17
  import { c as sendTextMessage, i as getUpdates, l as sendTyping, n as downloadMedia, o as markdownToPlainText, r as getConfig, s as sendFileMessage, t as TypingStatus, u as sniffImageExt } from "./ilink-CsZ-f64q.js";
18
18
  import { a as registerImAccountImpl, i as normalizeAccountChannel, n as getImAccountImpl, r as imAccountChannels } from "./accounts-BHQbakUh.js";
19
19
  import { createRequire } from "node:module";
@@ -511,13 +511,12 @@ const ccFsTool = defineTool({
511
511
  const KIT_ACTIONS = [
512
512
  "health",
513
513
  "time",
514
- "wait",
515
514
  "diag"
516
515
  ];
517
516
  async function runKit(root, args, hostCtx) {
518
517
  switch (args.action) {
519
518
  case "health": {
520
- const { containerStatus } = await import("./observation-BWpkA865.js");
519
+ const { containerStatus } = await import("./observation-BKsS1Qke.js");
521
520
  const s = containerStatus({
522
521
  root,
523
522
  hostCtx
@@ -561,15 +560,9 @@ async function runKit(root, args, hostCtx) {
561
560
  epoch_ms: now.getTime()
562
561
  };
563
562
  }
564
- case "wait": {
565
- const seconds = args.seconds ?? 1;
566
- if (!Number.isInteger(seconds) || seconds <= 0) throw new Error("wait requires a positive integer number of seconds (default 1)");
567
- await new Promise((r) => setTimeout(r, seconds * 1e3));
568
- return `waited ${seconds}s`;
569
- }
570
563
  case "diag": {
571
564
  if (hostCtx === void 0) throw new Error("diag requires the plugin context (live session data unavailable without it)");
572
- const { runDiag, renderDiag } = await import("./service-CCzsVeN_.js");
565
+ const { runDiag, renderDiag } = await import("./service-Cfv1byRk.js");
573
566
  return renderDiag(await runDiag(hostCtx, root ?? ""));
574
567
  }
575
568
  default: throw new Error(`Unknown action: ${args.action}`);
@@ -593,19 +586,13 @@ function renderText$9(value) {
593
586
  function createKitTool(ctx) {
594
587
  return defineTool({
595
588
  name: "dashboard",
596
- description: "Dashboard — always-on container instruments (works any time, no side effects): health (CCC three-principle check P1/P2/config + MSM registry integrity + host-contract probe, healthy/degraded) / time (now_iso/now_local/epoch_ms) / wait (wait N seconds, default 1) / diag (ACC runtime diagnosis, read-only: live sessions across all CCCs / wake scheduler clock / CRO-enabled trajectories / wake registry split into future vs overdue / trajectory-subscription delivery readings). Routine self-check before entering a CCC.",
597
- parameters: {
598
- action: {
599
- type: "string",
600
- enum: [...KIT_ACTIONS],
601
- required: true,
602
- description: "Subcommand"
603
- },
604
- seconds: {
605
- type: "integer",
606
- description: "Seconds to wait (positive integer, default 1)"
607
- }
608
- },
589
+ description: "Dashboard — always-on container instruments (works any time, no side effects): health (CCC three-principle check P1/P2/config + MSM registry integrity + host-contract probe, healthy/degraded) / time (now_iso/now_local/epoch_ms) / diag (ACC runtime diagnosis, read-only: live sessions across all CCCs / wake scheduler clock / CRO-enabled trajectories / wake registry split into future vs overdue / trajectory-subscription delivery readings). Routine self-check before entering a CCC.",
590
+ parameters: { action: {
591
+ type: "string",
592
+ enum: [...KIT_ACTIONS],
593
+ required: true,
594
+ description: "Subcommand"
595
+ } },
609
596
  output: {
610
597
  schema: { type: "json" },
611
598
  render: (args, value) => renderText$9(value)
@@ -7668,7 +7655,7 @@ const DOCS_CHAPTERS = [
7668
7655
  },
7669
7656
  {
7670
7657
  id: "config",
7671
- summary: "CCC 配置全表——9 段各是什么、怎么填、填错会怎样(.opencode/serenity.json)",
7658
+ summary: "CCC 配置全表——10 段各是什么、怎么填、填错会怎样(.opencode/serenity.json)",
7672
7659
  render: () => CCC_CONFIG_REFERENCE
7673
7660
  },
7674
7661
  {
@@ -8850,6 +8837,139 @@ function resolveInstallModelSelection() {
8850
8837
  return resolveCache;
8851
8838
  }
8852
8839
  //#endregion
8840
+ //#region src/trajectory/user-quotes.ts
8841
+ /** 台账目录(CCC 根相对)—— 与 `_im-logs/` 同族:`_` 前缀 = 非内容目录。 */
8842
+ const USER_QUOTES_DIR = "AGENT_SESSIONS/_user-quotes";
8843
+ /**
8844
+ * 冒充 `kind:'user'` 的两个生产者所打的标记。
8845
+ *
8846
+ * 🔴 判据来自**生产者自己**(它们本就为了让读者能分辨才打这个标记),
8847
+ * ⛔ 不是"我猜这些词像注入" —— 后者是措辞黑名单,会随措辞漂移而静默失效。
8848
+ */
8849
+ const USER_QUOTE_FAKE_MARKERS = [CARRIER_OPENING_MARK, ASSISTANT_PREFIX];
8850
+ /**
8851
+ * 当前进程内已入账的 message id(有界 LRU)。
8852
+ *
8853
+ * ⚠️ **只在进程内有效**:跨重启的去重靠"日志里已经有这一行"——但那需要读全文件,
8854
+ * 而 `pre-step` 是每步都跑的热路径 ⇒ 不为它加一次全文件扫描。
8855
+ * 跨重启的重复投递**实际不会发生**(重启后 inbox 是空的,宿主不会重放已落盘的 user/message)。
8856
+ */
8857
+ const seenMessageIds = /* @__PURE__ */ new Set();
8858
+ const SEEN_MAX = 4096;
8859
+ function rememberMessageId(id) {
8860
+ if (seenMessageIds.has(id)) return;
8861
+ seenMessageIds.add(id);
8862
+ if (seenMessageIds.size > SEEN_MAX) {
8863
+ const oldest = seenMessageIds.values().next().value;
8864
+ if (oldest !== void 0) seenMessageIds.delete(oldest);
8865
+ }
8866
+ }
8867
+ /**
8868
+ * 一条消息的正文文本(只取 `text` 块,按出现顺序以 `\n` 相连)。
8869
+ *
8870
+ * 判据与 `session.ts:496` / `guard/seam.ts:70` 同款 —— 不另造一套取文法(否则同一条消息
8871
+ * 在不同缝里"说了什么"会不一样)。图片等非文本块**不参与**:Q3 要的是"用户说过什么",
8872
+ * 图片没有可记录的文字面(媒体路径另有 `_im-logs/.../media/` 承载)。
8873
+ */
8874
+ function messageText(content) {
8875
+ if (!Array.isArray(content)) return "";
8876
+ const parts = [];
8877
+ for (const b of content) if (b?.type === "text" && typeof b.text === "string") parts.push(b.text);
8878
+ return parts.join("\n");
8879
+ }
8880
+ /**
8881
+ * 这条消息**是不是用户直接说的话**(结构性判据 + 冒充标记排除)。
8882
+ *
8883
+ * @returns `'quote'` 收录 / `'not-user'` 不是用户发言(ACC 注入等)/ `'fake-user'` 冒充 user 的生产者
8884
+ */
8885
+ function classifyUserMessage(source, text) {
8886
+ if (source?.kind !== "user") return "not-user";
8887
+ for (const marker of USER_QUOTE_FAKE_MARKERS) if (text.startsWith(marker)) return "fake-user";
8888
+ return "quote";
8889
+ }
8890
+ /** 本地时区 ISO8601(与 `cro/log.ts` 等处同款口径:不引第三方库,用 Date 的本地字段) */
8891
+ function localIso(ms) {
8892
+ const d = new Date(ms);
8893
+ const p = (n, w = 2) => String(n).padStart(w, "0");
8894
+ const offMin = -d.getTimezoneOffset();
8895
+ const sign = offMin >= 0 ? "+" : "-";
8896
+ const abs = Math.abs(offMin);
8897
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}${sign}${p(Math.floor(abs / 60))}:${p(abs % 60)}`;
8898
+ }
8899
+ /** 台账文件名(按天,本地时区) */
8900
+ function userQuoteFileName(ms) {
8901
+ const d = new Date(ms);
8902
+ const p = (n) => String(n).padStart(2, "0");
8903
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}.jsonl`;
8904
+ }
8905
+ /**
8906
+ * 把本 step 的 `messages` 里的**用户真发言**追加进当日台账。
8907
+ *
8908
+ * @param root CCC 根
8909
+ * @param sessionId 当前会话 id
8910
+ * @param messages `agent/pre-step` 的 `payload.messages`
8911
+ * @param nowMs 时间戳(测试注入;缺省 `Date.now()`)
8912
+ * @returns 落盘结果(**永不抛** —— 采集失败只 warn)
8913
+ */
8914
+ function recordUserQuotes(root, sessionId, messages, nowMs = Date.now()) {
8915
+ const file = join(root, USER_QUOTES_DIR, userQuoteFileName(nowMs));
8916
+ const out = {
8917
+ written: 0,
8918
+ excludedFake: 0,
8919
+ skippedDuplicate: 0,
8920
+ file
8921
+ };
8922
+ if (!Array.isArray(messages) || messages.length === 0) return out;
8923
+ const quotes = [];
8924
+ for (const m of messages) {
8925
+ const text = messageText(m?.content);
8926
+ if (text.trim() === "") continue;
8927
+ const cls = classifyUserMessage(m?.source, text);
8928
+ if (cls === "not-user") continue;
8929
+ if (cls === "fake-user") {
8930
+ out.excludedFake++;
8931
+ continue;
8932
+ }
8933
+ const id = typeof m?.id === "string" ? m.id : "";
8934
+ if (id !== "" && seenMessageIds.has(id)) {
8935
+ out.skippedDuplicate++;
8936
+ continue;
8937
+ }
8938
+ quotes.push({
8939
+ ts: localIso(nowMs),
8940
+ trajectory: safeFindTrajectory(root, sessionId),
8941
+ sessionId,
8942
+ messageId: id,
8943
+ text,
8944
+ chars: [...text].length
8945
+ });
8946
+ }
8947
+ if (quotes.length === 0) return out;
8948
+ try {
8949
+ mkdirSync(join(root, USER_QUOTES_DIR), { recursive: true });
8950
+ appendFileSync(file, quotes.map((q) => `${JSON.stringify(q)}\n`).join(""), {
8951
+ encoding: "utf8",
8952
+ flag: "a"
8953
+ });
8954
+ } catch (e) {
8955
+ out.error = String(e?.message ?? e);
8956
+ console.warn(`[serenity-hooks] user-quotes 写入失败(不影响本步):${file} — ${out.error}`);
8957
+ return out;
8958
+ }
8959
+ for (const q of quotes) if (q.messageId !== "") rememberMessageId(q.messageId);
8960
+ out.written = quotes.length;
8961
+ return out;
8962
+ }
8963
+ /** 查这次会话属于哪条轨迹;查不到/抛错 ⇒ `null`(⛔ 不让"查不到归属"毁掉整条录入) */
8964
+ function safeFindTrajectory(root, sessionId) {
8965
+ if (typeof sessionId !== "string" || sessionId === "") return null;
8966
+ try {
8967
+ return findTrajectoryOfSession(root, sessionId);
8968
+ } catch {
8969
+ return null;
8970
+ }
8971
+ }
8972
+ //#endregion
8853
8973
  //#region src/seams/context.ts
8854
8974
  /**
8855
8975
  * 该会话的轨迹**是否自带章程**(= 该不该旁路 ACC 身份类默认行为)。D183。
@@ -9122,6 +9242,27 @@ function registerContext(ctx, opts = {}) {
9122
9242
  const cwd = agent.session?.header?.cwd ?? process.cwd();
9123
9243
  const root = cccRootForCwd(cwd);
9124
9244
  const key = agentKey(agent);
9245
+ /**
9246
+ * 🔴 用户语录台账采集(v1.113.0,owner 2026-11-02 需求)。
9247
+ *
9248
+ * 放在 `next()` **之前**:`payload.messages` 是"本 step 从 inbox 取出、即将进入本步的消息"
9249
+ * (宿主 `runtime-types.d.ts` 逐字)。先记再放行 ⇒ **这一步真的会看见的话,就一定先记下了**;
9250
+ * 若下游 reject 掉这一步,台账上会多一条"它没看见"的记录 —— 那是**已知且可接受**的方向
9251
+ * (宁多记不漏记:owner 要的是"用户说过什么",不是"模型最终读到了什么")。
9252
+ *
9253
+ * ⚠️ **只旁路、不阻断**:`recordUserQuotes` 自己 catch 全部失败并 warn,
9254
+ * 这里再包一层 `try/catch` 是第二道保险 —— 台账坏了**绝不能让 step 跑不动**。
9255
+ * ⚠️ **必须排在 `next()` 之前且不改变 `messages`**:本函数是 waterfall,
9256
+ * 但我们**不修改** `payload.messages`(那是下游的输入,动它就是行为变更)。
9257
+ */
9258
+ if (root) try {
9259
+ if (readUserQuotesEnabled(root, configPaths)) {
9260
+ const sessionId = agent.session?.id;
9261
+ if (typeof sessionId === "string" && sessionId !== "") recordUserQuotes(root, sessionId, messages);
9262
+ }
9263
+ } catch (e) {
9264
+ console.warn(`[serenity-hooks] user-quotes 采集失败(不影响本步):${String(e?.message ?? e)}`);
9265
+ }
9125
9266
  const downstream = await next();
9126
9267
  try {
9127
9268
  syncToolVisibility(agent, root);
@@ -9591,11 +9732,11 @@ function registerStatusApi(ctx, opts = {}) {
9591
9732
  sendJson$2(res, 400, { error: chk.error });
9592
9733
  return;
9593
9734
  }
9594
- const { readWeixinSettings } = await import("./route-reyv8qcB.js").then((n) => n.l);
9735
+ const { readWeixinSettings } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9595
9736
  const { weixinBridgeStatus } = await Promise.resolve().then(() => bridge_exports);
9596
9737
  const settings = readWeixinSettings(chk.root);
9597
9738
  const bridge = weixinBridgeStatus().find((b) => b.ccc === chk.root);
9598
- const { imCredentialBound } = await import("./route-reyv8qcB.js").then((n) => n.l);
9739
+ const { imCredentialBound } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9599
9740
  const { normalizeAccountChannel } = await import("./accounts-BHQbakUh.js").then((n) => n.t);
9600
9741
  const accounts = (settings.accounts ?? []).map((a) => {
9601
9742
  const channel = normalizeAccountChannel(a.channel);
@@ -9632,7 +9773,7 @@ function registerStatusApi(ctx, opts = {}) {
9632
9773
  const { fetchQRCode } = await import("./ilink-CsZ-f64q.js").then((n) => n.a);
9633
9774
  const { randomUUID } = await import("node:crypto");
9634
9775
  for (const [key, v] of weixinLogins) if (Date.now() - v.startedAt > WEIXIN_LOGIN_TTL_MS) weixinLogins.delete(key);
9635
- const { readWeixinSettings } = await import("./route-reyv8qcB.js").then((n) => n.l);
9776
+ const { readWeixinSettings } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9636
9777
  const qr = await fetchQRCode({ botType: readWeixinSettings(root).botType ?? void 0 });
9637
9778
  const loginKey = randomUUID();
9638
9779
  weixinLogins.set(loginKey, {
@@ -9653,7 +9794,7 @@ function registerStatusApi(ctx, opts = {}) {
9653
9794
  sendJson$2(res, 400, { error: "missing accountId" });
9654
9795
  return;
9655
9796
  }
9656
- const { removeWeixinAccount } = await import("./route-reyv8qcB.js").then((n) => n.l);
9797
+ const { removeWeixinAccount } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9657
9798
  const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
9658
9799
  removeWeixinAccount(root, body.accountId);
9659
9800
  syncCccBridge(ctx, root);
@@ -9679,8 +9820,8 @@ function registerStatusApi(ctx, opts = {}) {
9679
9820
  trajectory: code
9680
9821
  });
9681
9822
  }
9682
- const { saveWeixinRoutes } = await import("./route-reyv8qcB.js").then((n) => n.l);
9683
- const { findDirNameByCode } = await import("./profile-BjBn_-be.js").then((n) => n.a);
9823
+ const { saveWeixinRoutes } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9824
+ const { findDirNameByCode } = await import("./profile-Djqz4Noy.js").then((n) => n.a);
9684
9825
  for (const r of routes) if (findDirNameByCode(root, r.trajectory) === null) {
9685
9826
  sendJson$2(res, 400, { error: `unknown trajectory: ${r.trajectory} (no such trajectory code in ${root})` });
9686
9827
  return;
@@ -9691,7 +9832,7 @@ function registerStatusApi(ctx, opts = {}) {
9691
9832
  }
9692
9833
  if (body.action === "set-enabled") {
9693
9834
  const enabled = body.enabled === true;
9694
- const { readWeixinSettings, setWeixinEnabled } = await import("./route-reyv8qcB.js").then((n) => n.l);
9835
+ const { readWeixinSettings, setWeixinEnabled } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9695
9836
  const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
9696
9837
  const settings = readWeixinSettings(root);
9697
9838
  if (enabled && (settings.accounts ?? []).length === 0) {
@@ -9714,7 +9855,7 @@ function registerStatusApi(ctx, opts = {}) {
9714
9855
  sendJson$2(res, 400, { error: "appId 与 appSecret 都必须填(只填一个等于没绑)" });
9715
9856
  return;
9716
9857
  }
9717
- const { readWeixinSettings, upsertWeixinAccount, writeFeishuCredential, nextFeishuAccountId } = await import("./route-reyv8qcB.js").then((n) => n.l);
9858
+ const { readWeixinSettings, upsertWeixinAccount, writeFeishuCredential, nextFeishuAccountId } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9718
9859
  const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
9719
9860
  const before = readWeixinSettings(root);
9720
9861
  const accountId = (body.accountId ?? "").trim() !== "" ? body.accountId.trim() : nextFeishuAccountId(before);
@@ -9764,7 +9905,7 @@ function registerStatusApi(ctx, opts = {}) {
9764
9905
  return;
9765
9906
  }
9766
9907
  const { pollQRStatus } = await import("./ilink-CsZ-f64q.js").then((n) => n.a);
9767
- const { readWeixinSettings } = await import("./route-reyv8qcB.js").then((n) => n.l);
9908
+ const { readWeixinSettings } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9768
9909
  const settings = readWeixinSettings(login.root);
9769
9910
  const status = await pollQRStatus({
9770
9911
  baseUrl: void 0,
@@ -9779,7 +9920,7 @@ function registerStatusApi(ctx, opts = {}) {
9779
9920
  });
9780
9921
  return;
9781
9922
  }
9782
- const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./route-reyv8qcB.js").then((n) => n.l);
9923
+ const { upsertWeixinAccount, writeWeixinCredential, nextWeixinAccountId } = await import("./route-D-W3O2PF.js").then((n) => n.l);
9783
9924
  const { syncCccBridge } = await Promise.resolve().then(() => bridge_exports);
9784
9925
  const accountId = nextWeixinAccountId(settings);
9785
9926
  upsertWeixinAccount(login.root, {
@@ -14269,7 +14410,7 @@ const MEDIA_MAX_BYTES = 20971520;
14269
14410
  */
14270
14411
  async function resolveCccRootByAlias(ctx, alias) {
14271
14412
  try {
14272
- const { listCccs } = await import("./ccc-roots-DPMwBywc.js").then((n) => n.a);
14413
+ const { listCccs } = await import("./ccc-roots-CsSSQjSS.js").then((n) => n.a);
14273
14414
  const entries = await listCccs(ctx);
14274
14415
  const byName = entries.find((e) => e.name === alias);
14275
14416
  if (byName) return byName.root;
@@ -14337,7 +14478,7 @@ async function resolveInboundTarget(ctx, input) {
14337
14478
  });
14338
14479
  return null;
14339
14480
  }
14340
- const { findDirNameByCode } = await import("./profile-BjBn_-be.js").then((n) => n.a);
14481
+ const { findDirNameByCode } = await import("./profile-Djqz4Noy.js").then((n) => n.a);
14341
14482
  if (findDirNameByCode(root, target.role) === null) {
14342
14483
  const detail = `目标 CCC "${target.ccc}" 内没有编码为 "${target.role}" 的认知轨迹`;
14343
14484
  console.error(`[serenity-hooks] ✗ human-channel: ${detail}(account=${input.account} user=${input.user})—— 该消息**未投递**,请改订阅表里的目标编码`);
@@ -15299,7 +15440,7 @@ function syncHumanChannel(ctx) {
15299
15440
  (async () => {
15300
15441
  try {
15301
15442
  stopMachineLoops();
15302
- const { listCccs } = await import("./ccc-roots-DPMwBywc.js").then((n) => n.a);
15443
+ const { listCccs } = await import("./ccc-roots-CsSSQjSS.js").then((n) => n.a);
15303
15444
  for (const entry of await listCccs(ctx)) syncCccBridge(ctx, entry.root);
15304
15445
  } catch {}
15305
15446
  })();
@@ -15592,7 +15733,7 @@ function matchCcc(input, candidates) {
15592
15733
  }
15593
15734
  /** 组装候选列表(`ccc-roots.listCccs` 投影 + `.serenity` 名;动态 import 保持本模块静态依赖轻量) */
15594
15735
  async function collectCandidates(ctx) {
15595
- const { listCccs } = await import("./ccc-roots-DPMwBywc.js").then((n) => n.a);
15736
+ const { listCccs } = await import("./ccc-roots-CsSSQjSS.js").then((n) => n.a);
15596
15737
  const entries = await listCccs(ctx, { defaultRoot: process.cwd() });
15597
15738
  const seen = /* @__PURE__ */ new Set();
15598
15739
  const out = [];
@@ -148,6 +148,23 @@ interface SerenityConfig {
148
148
  * 本节是「**别人**干完活了,告诉我一声」(互观察)。两者都走唤醒链路,但**主语相反**。
149
149
  */
150
150
  trajectorySubscriptions?: TrajectorySubscription[];
151
+ /**
152
+ * **用户语录台账**(v1.113.0,owner 2026-11-02 需求)。
153
+ *
154
+ * owner 原话:「我需要一个可开启关闭的功能,就是记录用户直接对谁说过什么……
155
+ * 这个功能如果开启,则默认写到一个指定目录内,按天进行记录。」
156
+ *
157
+ * **开关放 CCC 面(owner Q2 的答)**:改它不发版(D23)——机制在 ACC,声明在 CCC。
158
+ *
159
+ * **缺省 = 关**。这不只是保守:台账是**把用户原话原文长期留存**,
160
+ * 未经明示开启就去写它,等于替 owner 做了一个他没有同意的持久化决定。
161
+ * ⇒ `enabled` 必须**显式为 true** 才开;缺键/写错/写成字符串都算关。
162
+ *
163
+ * 采集点、判别面与落盘形态的判据 ⇒ `trajectory/user-quotes.ts` 头注释(单一真相源)。
164
+ */
165
+ userQuotes?: {
166
+ enabled?: boolean;
167
+ };
151
168
  }
152
169
  /**
153
170
  * 一条轨迹消息订阅:`watcher` 观察 `members`。
@@ -373,6 +390,18 @@ export declare function readTrajectorySubscriptions(root: string, paths?: string
373
390
  subs: TrajectorySubscription[];
374
391
  problems: string[];
375
392
  };
393
+ /**
394
+ * 用户语录台账是否开启(`userQuotes.enabled`,v1.113.0)。
395
+ *
396
+ * 🔴 **判据是 `=== true`,不是"键存在"**:写成 `"true"` / `1` / 缺 `enabled`
397
+ * 一律当**关**。理由见 `SerenityConfig.userQuotes` —— 这是"长期留存用户原话",
398
+ * 含糊的配置必须落到**不写**那一侧(fail-closed,与订阅表的 fail-open 方向相反,
399
+ * 因为这里的危险方向是**多写**,那里是**少通知**)。
400
+ *
401
+ * ⚠️ 配置损坏 ⇒ `loadSerenityConfig` 已响亮告警并返回 `{}` ⇒ 这里得到 `false`(关)。
402
+ * 即"配置坏了就不记",而不是"配置坏了就记到默认位置"。
403
+ */
404
+ export declare function readUserQuotesEnabled(root: string, paths?: string[]): boolean;
376
405
  /**
377
406
  * 读取 CCC 声明的「安全模式期间隐藏的工具」清单(`safeMode.hiddenTools`,v1.51.4)。
378
407
  *
@@ -1,5 +1,5 @@
1
- import { r as cccRootForCwd } from "./ccc-roots-DPMwBywc.js";
2
- import { T as sessionsRoot, a as readBindDeclaration, b as parseTrajectoryCode, h as getLastActiveSessionInfo, i as listBindDeclarations, m as getActiveSessionInfo, r as isBindUsable, t as claimBinding, w as sessionEvents } from "./bind-file-B0TuOP89.js";
1
+ import { r as cccRootForCwd } from "./ccc-roots-CsSSQjSS.js";
2
+ import { T as sessionsRoot, a as readBindDeclaration, b as parseTrajectoryCode, h as getLastActiveSessionInfo, i as listBindDeclarations, m as getActiveSessionInfo, n as findTrajectoryHolding, r as isBindUsable, t as claimBinding, w as sessionEvents } from "./bind-file-B0TuOP89.js";
3
3
  import { join } from "node:path";
4
4
  //#region src/trajectory/bound.ts
5
5
  /**
@@ -244,6 +244,10 @@ function carrierState(root, dirName, alive) {
244
244
  sessionId: decl.sessionId
245
245
  };
246
246
  }
247
+ /** 某会话 id 现在安家在哪个轨迹(唯一性查询的对外门面) */
248
+ function findTrajectoryOfSession(root, sessionId) {
249
+ return findTrajectoryHolding(sessionsRoot(root), sessionId);
250
+ }
247
251
  //#endregion
248
252
  //#region src/kernel/message-source.ts
249
253
  /** ACC 注入消息的 kind —— 唤醒 / 打回 / keeper 提醒 / 锚定轮 / rebuild 交接等一律同值。 */
@@ -251,4 +255,4 @@ const ACC_MESSAGE_KIND = "plugin:dsh-serenity-hooks";
251
255
  /** ACC 注入消息的统一来源值(各缝共用;需要 `form` 时按 `ContextFormed` 另加字段)。 */
252
256
  const PLUGIN_SOURCE = { kind: ACC_MESSAGE_KIND };
253
257
  //#endregion
254
- export { listBoundSessionIds as a, setBindingStore as c, carrierState as i, PLUGIN_SOURCE as n, readLastBound as o, appendBound as r, resolveSessionCognitiveTrajectoryLabel as s, ACC_MESSAGE_KIND as t };
258
+ export { findTrajectoryOfSession as a, resolveSessionCognitiveTrajectoryLabel as c, carrierState as i, setBindingStore as l, PLUGIN_SOURCE as n, listBoundSessionIds as o, appendBound as r, readLastBound as s, ACC_MESSAGE_KIND as t };
@@ -1,7 +1,7 @@
1
- import { d as readCccName, i as findGitRoot, l as pathInside, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-BmpE3cQz.js";
2
- import { u as ACC_VERSION } from "./rules-BLkYTPUy.js";
3
- import { i as readDshVersion, t as hostContractReport, v as checkRegistryQuality } from "./contract-BiNzzd77.js";
4
- import { g as listWakes, i as wakeSchedulerState } from "./scheduler-B0pB9mxI.js";
1
+ import { d as readCccName, i as findGitRoot, l as pathInside, s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
2
+ import { u as ACC_VERSION } from "./rules-DKlpuFN_.js";
3
+ import { i as readDshVersion, t as hostContractReport, v as checkRegistryQuality } from "./contract-DJSTR5Up.js";
4
+ import { _ as listWakes, i as wakeSchedulerState } from "./scheduler-CB06u0Qc.js";
5
5
  import { resolve } from "node:path";
6
6
  import { existsSync, readFileSync, statSync } from "node:fs";
7
7
  //#region src/surface/observation.ts
@@ -1,5 +1,5 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
2
- import { g as resolveInside, h as readUtf8 } from "./ccc-BmpE3cQz.js";
2
+ import { _ as resolveInside, g as readUtf8 } from "./ccc-x94FCzOv.js";
3
3
  import { N as findTrajectoryFile, T as sessionsRoot, b as parseTrajectoryCode, i as listBindDeclarations, j as TRAJECTORY_FILENAME } from "./bind-file-B0TuOP89.js";
4
4
  import { join } from "node:path";
5
5
  import { readdirSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.js";
2
- import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-BmpE3cQz.js";
3
- import { c as unsetEntry, l as writeEntry, r as getEntry } from "./service-Z3aSR1cn.js";
2
+ import { s as loadSerenityConfig, t as DEFAULT_SERENITY_CONFIG_PATHS } from "./ccc-x94FCzOv.js";
3
+ import { c as unsetEntry, l as writeEntry, r as getEntry } from "./service-Dv-8E2bI.js";
4
4
  import { join, resolve } from "node:path";
5
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { createHash } from "node:crypto";
@@ -1,4 +1,4 @@
1
- import { g as resolveInside, h as readUtf8 } from "./ccc-BmpE3cQz.js";
1
+ import { _ as resolveInside, g as readUtf8 } from "./ccc-x94FCzOv.js";
2
2
  import { dirname, join } from "node:path";
3
3
  import { existsSync, readFileSync, statSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";