sage-mem 0.9.0 → 0.9.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.
@@ -28,14 +28,17 @@ import { join } from 'node:path'
28
28
  const INDEX_FILE = 'MEMORY.md'
29
29
 
30
30
  /**
31
- * 非记忆条目:不是四类记忆、也不该套「type 与前缀一致」这条规则。
31
+ * 审计**整个跳过**的文件(不读、不当被审对象)。
32
32
  *
33
- * session-log.md 是追加型流水,逐会话的账本而不是一条记忆;
34
- * 它没有 `user_/feedback_/project_/reference_` 前缀,扫进来只会白白读一大坨
35
- * 再报一条假问题(实测:报出 `session-log.md type=reference`)。
36
- * 同理 MEMORY.md 是索引本身,只作为链接来源读,不作为被审对象。
33
+ * session-log.md 是追加型流水,逐会话的账本而不是一条记忆;它没有
34
+ * `user_/feedback_/project_/reference_` 前缀,扫进来只会白白读一大坨再报假问题。
35
+ *
36
+ * ⚠️ 别和 `lib/memory/config.js` 的 `NON_ENTRY_FILES` 混为一谈 —— **同名不同义**:
37
+ * 那个管「列不列进设置页 / 星图 / 检索」,这个管「审计读不读」。它们的差集就是
38
+ * **索引 `MEMORY.md`**:它**要**被读(要当链接来源、要查行尾与链接),只是
39
+ * **不是一条记忆** —— 见下面 `f !== INDEX_FILE` 那两处。
37
40
  */
38
- const NON_ENTRY_FILES = new Set(['session-log.md'])
41
+ const AUDIT_SKIP_FILES = new Set(['session-log.md'])
39
42
 
40
43
  /** 归档区目录名(与 autodream-tools.js 的 ARCHIVE_DIR、宿主侧 memory/scan.js 同名)。 */
41
44
  const ARCHIVE_DIR = 'archive'
@@ -95,7 +98,7 @@ async function readDirSnapshot(dir) {
95
98
  const all = entries
96
99
  .filter((e) => e.isFile() && e.name.toLowerCase().endsWith('.md'))
97
100
  .map((e) => e.name)
98
- const files = all.filter((f) => !NON_ENTRY_FILES.has(f.toLowerCase()))
101
+ const files = all.filter((f) => !AUDIT_SKIP_FILES.has(f.toLowerCase()))
99
102
  let indexText = ''
100
103
  try {
101
104
  indexText = await readFile(join(dir, INDEX_FILE), 'utf8')
@@ -175,13 +178,28 @@ export async function auditMemoryDir(dir) {
175
178
  }
176
179
  for (const f of readable) linkKeys.add(f)
177
180
  for (const f of readable) linkKeys.add(f.replace(/\.md$/, ''))
181
+ /**
182
+ * ⚠️ 链接目标池还必须含**非条目文件**(`session-log.md` / `MEMORY.md`)。
183
+ *
184
+ * 它们不在 `readable` 里(不参与检索),但 `[[session-log]]` 指向的是**真实存在的文件** ——
185
+ * 只因它不参与检索就报成断链,是假阳性。这与上面 `dangling` 用 `allSet` 而非 `files`
186
+ * 判定悬空是同一个理由,别只在一处想到。(2026-10-09 实测:健康记忆库被报 1 条假断链)
187
+ */
188
+ for (const f of all) {
189
+ linkKeys.add(f)
190
+ linkKeys.add(f.replace(/\.md$/i, ''))
191
+ }
178
192
 
179
193
  for (const f of readable) {
180
194
  const raw = contents.get(f)
181
195
  if (raw.includes('\r')) crlf.push(f)
182
196
  const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)
183
197
  if (!fmMatch) {
184
- noFrontmatter.push(f)
198
+ /**
199
+ * 索引 `MEMORY.md` 是**纯清单、不是一条记忆**(见 `lib/memory/config.js` 的
200
+ * `NON_ENTRY_FILES` 说明),不要求 frontmatter。报它等于给每一个健康的记忆库挂一盏常驻红灯。
201
+ */
202
+ if (f !== INDEX_FILE) noFrontmatter.push(f)
185
203
  } else {
186
204
  const meta = parseFrontmatterLite(raw)
187
205
  const prefix = f.split('_')[0]
@@ -196,7 +214,14 @@ export async function auditMemoryDir(dir) {
196
214
  }
197
215
  }
198
216
  }
199
- for (const m of raw.matchAll(/\[\[([^\]]+)\]\]/g)) {
217
+ /**
218
+ * ⚠️ 只在**非代码**文本里找双链。
219
+ *
220
+ * `` `[[双链]]` `` 写在行内代码或围栏代码块里,是在**讲链接语法本身**,不是一条链接。
221
+ * 直接在原文上 `matchAll` 会把它报成断链(2026-10-09 实测的假阳性)。
222
+ */
223
+ const prose = raw.replace(/```[\s\S]*?```/g, '').replace(/`+[^`]*`+/g, '')
224
+ for (const m of prose.matchAll(/\[\[([^\]]+)\]\]/g)) {
200
225
  const target = m[1].trim()
201
226
  if (linkKeys.has(target)) continue
202
227
  // 指向归档区的链接:单列提示、不计问题 —— 归档不是删除,目标还在库里躺着。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sage-mem",
3
3
  "description": "File-based memory plugin for DeepSeek Harness (DSH) — cross-session memory in plain markdown files, with a memory star-map view, type/tag filtering, retrieval aliases, and an optional autodream pass that consolidates memories on demand or on a schedule — with per-run rollback points, a per-file change declaration (what changed and why), and a separately selectable model for the consolidation pass",
4
- "version": "0.9.0",
4
+ "version": "0.9.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {