dsh-ledger-memory 0.3.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/lib/index.js +61 -5
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -3073,9 +3073,61 @@ function renderAfterCompaction(info, capsule, meta) {
3073
3073
  * @returns {string|undefined} 绝对路径;拿不到 cwd 且没锁 ⇒ undefined
3074
3074
  */
3075
3075
  function projectRootOf(agent) {
3076
- const cwd = agent?.session?.header?.cwd;
3076
+ const h = agent?.session?.header;
3077
+ return resolveProjectRoot(h?.id, h?.cwd);
3078
+ }
3079
+
3080
+ /**
3081
+ * ★★★ 2026-10-10(用户报的真缺口):**采集器那条路的同一个判定**。
3082
+ *
3083
+ * **为什么需要它**:`ctx.on("session/event", (session, event) => …)` 拿到的是
3084
+ * **`session`**,不是 `agent` —— 所以它压根够不到 `projectRootOf(agent)`,
3085
+ * 旧代码就自己写了 `const cwd = session?.header?.cwd` **直接取原始 cwd**。
3086
+ * ⇒ 后果:**项目锁对活动日志(含 L4 用户消息存档)完全无效**。
3087
+ * 实测(本机,2026-10-10 用户让我把会话锁到插件目录之后):
3088
+ * · `ledger_status` 报的是**锁定目录**(工具那条路走了锁);
3089
+ * · 但 `ledger_log({view:"users"})` **一条 L4 都没有** ——
3090
+ * 因为采集器拿的是容器根,那里没启用日志 ⇒ `logDirOf` undefined ⇒ 直接 return。
3091
+ * ⇒ 锁着子项目,**用户在总工作区说的话一句都不进 L4**(而那正是 L4 最该记的东西)。
3092
+ *
3093
+ * ★ 修法不是"在采集器里再抄一遍判定",而是**抽成 `resolveProjectRoot` 一个出口**:
3094
+ * 两处各写一套判据 = 迟早分叉(本文件反复强调这条)。
3095
+ * ★ 这就是本项目「一个事实只能有一个判定出口」(D25)的又一次应用 ——
3096
+ * 而且这次是**我自己先违反了它**才出的缺口:`projectRootOf` 被当成唯一出口写在注释里,
3097
+ * 却漏了这条不经过 `agent` 的路。
3098
+ *
3099
+ * @param {object} session 官方 `session/event` 的第一个入参
3100
+ * @returns {string|undefined}
3101
+ */
3102
+ function projectRootOfSession(session) {
3103
+ const h = session?.header ?? session;
3104
+ return resolveProjectRoot(h?.id ?? session?.id, h?.cwd);
3105
+ }
3106
+
3107
+ /**
3108
+ * 判定项目根 —— **唯一的出口**(`projectRootOf` 与 `projectRootOfSession` 都走这里)。
3109
+ *
3110
+ * ★★★ 2026-10-10(用户拍板)加了「项目锁」:用户说的
3111
+ * *"把对话建在总的工作区下面…或者说把项目注册到对应的文件夹上,
3112
+ * 以后各种工具的使用都在那个文件夹上"*。
3113
+ *
3114
+ * ★★ 优先级:**cwd 自己有台账 > 锁 > cwd**(用户选的"只在这场对话内接管")。
3115
+ * 为什么不无条件以锁为准:用户说得很明确 ——
3116
+ * 锁是给"cwd 没有台账(典型:建在总工作区)"这种情况用的**接管**手段;
3117
+ * 若当前目录本来就是一个有台账的项目,那它自己就是答案,锁不该压过它。
3118
+ * ⇒ 这条判据让"锁"**只会扩大能用的情况,不会改变已有情况**(与 D49 默认 steady
3119
+ * 同一条思路:新能力不许悄悄改掉旧行为)。
3120
+ *
3121
+ * ★ 代价(如实记下):这里多了一次 `readManifest`(只读一个小 JSON)。
3122
+ * 它只在**真的锁了**的时候才走,没锁的会话行为与以前**逐字节相同**。
3123
+ *
3124
+ * @param {string|undefined} sid 会话 id(锁是按会话记的)
3125
+ * @param {string|undefined} cwd 会话工作目录
3126
+ * @returns {string|undefined} 绝对路径;拿不到 cwd 且没锁 ⇒ undefined
3127
+ */
3128
+ function resolveProjectRoot(sid, cwd) {
3077
3129
  const here = typeof cwd === "string" && cwd !== "" ? path.resolve(cwd) : undefined;
3078
- const locked = lockedRootOf(agent);
3130
+ const locked = typeof sid === "string" && sid !== "" ? sessionProjectLock.get(sid) : undefined;
3079
3131
  if (locked === undefined) return here; // 没锁 ⇒ 与以前完全一致
3080
3132
  // 锁了:当前目录**自己有台账**就以它为准(锁只接管"没有台账"的情况)。
3081
3133
  if (here !== undefined && readManifest(here) !== undefined) return here;
@@ -9476,13 +9528,17 @@ export function apply(ctx, config) {
9476
9528
  // ── ② 活动日志采集 ───────────────────────────────────────────────────
9477
9529
  try {
9478
9530
  if (collector !== undefined) {
9479
- const cwd = session?.header?.cwd;
9480
9531
  const sid = session?.header?.id ?? session?.id;
9481
- if (typeof cwd === "string" && cwd !== "" && typeof sid === "string" && sid !== "") {
9532
+ // ★★★ 必须走 `projectRootOfSession`(**不是**直接取 `session.header.cwd`):
9533
+ // 项目锁是按会话记的,而这条链手里只有 `session`。
9534
+ // 旧代码直取 cwd ⇒ 锁对 L0–L4 **全部无效** ⇒ 实测
9535
+ // "锁着子项目、用户的话一句都不进 L4"(2026-10-10 用户让我锁之后暴露的)。
9536
+ const root = projectRootOfSession(session);
9537
+ if (typeof root === "string" && root !== "" && typeof sid === "string" && sid !== "") {
9482
9538
  // ★ 压缩来源由插件自己记的(内核那条事件里没有)—— 见 `state.hfSource`。
9483
9539
  // 取出来立刻删:它只描述"紧接着这一次压缩"。
9484
9540
  const hfSource = state.hfSource?.get?.(String(sid));
9485
- collector.consume(path.resolve(cwd), sid, event, { hfSource });
9541
+ collector.consume(root, sid, event, { hfSource });
9486
9542
  if (event?.type === "compaction/end") state.hfSource?.delete?.(String(sid));
9487
9543
  }
9488
9544
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ledger-memory",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "台账记忆(Ledger Memory):为 DSH 提供工程化的项目记忆 —— 台账的检测/注入/归档/Git 配合,活动日志(L0–L4),三层上下文压缩与逐字胶囊落盘,两种压缩模式(正常推进 / 反复追究),把会话锁定到某个项目目录,以及跨会话的交接单闭环。可回溯、可追查。",
5
5
  "keywords": [
6
6
  "dsh",