@a9i5k4/dsh-auto-memory 3.0.0 → 3.0.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 (90) hide show
  1. package/README.md +19 -7
  2. package/README.zh-CN.md +19 -7
  3. package/docs/FRONTEND-CO-CREATION.md +191 -0
  4. package/docs/GM53-HOMEPAGE-PROMPT.md +323 -0
  5. package/docs/HOMEPAGE-CONTENT-FOR-GM53.md +299 -0
  6. package/docs/PROMO-PROMPT-3.0.md +100 -0
  7. package/docs/USER-GUIDE.en.md +2 -2
  8. package/docs/USER-GUIDE.zh-CN.md +2 -2
  9. package/docs/WHITEPAPER.md +207 -0
  10. package/docs/internal/ARCHITECTURE-FOR-ZCODE-20260920.md +397 -0
  11. package/docs/internal/ART-DIRECTION-DEEPSEEK-20260920.md +351 -0
  12. package/docs/internal/ART-DIRECTION-WIREFRAME.md +191 -181
  13. package/docs/internal/ART-DIRECTION-WIREFRAME.md.bak-superseded +181 -0
  14. package/docs/internal/BATTLE-PLAN-20260917.md +871 -0
  15. package/docs/internal/FEATURE-INVENTORY.md +531 -0
  16. package/docs/internal/G-SERIES-EXECUTION-20260917.md +248 -0
  17. package/docs/internal/G3-DESIGN-20260918.md +82 -0
  18. package/docs/internal/G3-DISK-FORMAT-GAP-20260919.md +92 -0
  19. package/docs/internal/HANDOFF-TO-ZCODE-20260920.md +309 -0
  20. package/docs/internal/HERMES-DATA-VERIFICATION-20260919.md +120 -0
  21. package/docs/internal/HERMES-LEGACY-STATUS-20260919.md +74 -0
  22. package/docs/internal/ISSUE-55-58-VERIFICATION-20260918.md +175 -0
  23. package/docs/internal/ISSUE10-FIX-EXECUTION-20260919.md +389 -0
  24. package/docs/internal/ISSUE10-PLAN-20260919.md +254 -0
  25. package/docs/internal/ISSUE10B-FORENSICS-20260919.md +468 -0
  26. package/docs/internal/ISSUE9-PURGE-AND-R1-PLAIN-20260919.md +150 -0
  27. package/docs/internal/ISSUE9-RESIDUAL-FORENSICS-20260919.md +114 -0
  28. package/docs/internal/LESSON-TO-CANDIDATE-STATUS-20260919.md +79 -0
  29. package/docs/internal/MEMORY-GOVERNANCE-20260917.md +309 -0
  30. package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md +705 -0
  31. package/docs/internal/PRE-FRONTEND-CHECKLIST-20260919.md.bak-s10 +649 -0
  32. package/docs/internal/PROCEDURAL-MEMORY-AND-APPROVAL-DESIGN-20260918.md +225 -0
  33. package/docs/internal/PROGRESS-20260917.md +93 -0
  34. package/docs/internal/PROMPT-GAP-AUDIT-20260920.md +128 -0
  35. package/docs/internal/R1-DEGRADE-AUDIT-20260918.md +163 -0
  36. package/docs/internal/R1-READABILITY-FORENSICS-20260919.md +127 -0
  37. package/docs/internal/R2-EVIDENCE-DEEP-AUDIT-20260918.md +140 -0
  38. package/docs/internal/R3-DEGRADE-LEDGER-DESIGN-20260918.md +138 -0
  39. package/docs/internal/R4-RECALL-QUOTA-PLAN-20260918.md +218 -0
  40. package/docs/internal/RESUME-20260918.md +171 -0
  41. package/docs/internal/RESUME-20260919.md +104 -0
  42. package/docs/internal/RHINELAB-TO-DEEPSEEK-FEASIBILITY.md +198 -0
  43. package/docs/internal/ROADMAP-20260917-WEEK.md +134 -0
  44. package/docs/internal/S10-CONSTRUCTION-HANDOFF-20260917.md +13 -3
  45. package/docs/internal/S10-GAP-INVENTORY-20260917.md +239 -0
  46. package/docs/internal/T6-EXECUTION-20260920.md +130 -0
  47. package/docs/internal/TELEMETRY-EFFECT-REPORT-DESIGN-20260918.md +146 -0
  48. package/docs/internal/THESIS-GAP-ANALYSIS-20260918.md +89 -0
  49. package/docs/internal/THESIS-OUTLINE-20260918.md +147 -0
  50. package/docs/internal/THREE-LAYER-CONTRACT.md +10 -1
  51. package/docs/internal/UPSTREAM-ISSUE-PR-TRIAGE-20260919.md +297 -0
  52. package/docs/internal/UPSTREAM-ISSUES-3RD-AUDIT-20260920.md +104 -0
  53. package/docs/screenshots/promo/promo-0-banner-v3.png +0 -0
  54. package/lib/activation-host.js +63 -9
  55. package/lib/board-mode.js +1 -1
  56. package/lib/client.js +892 -27
  57. package/lib/config-io.js +156 -0
  58. package/lib/context-bridge.js +3 -0
  59. package/lib/context-host.js +16 -9
  60. package/lib/degrade.js +385 -0
  61. package/lib/dsh-home.js +143 -0
  62. package/lib/episodic-store.js +52 -2
  63. package/lib/evidence-store.js +8 -1
  64. package/lib/fact-store.js +21 -2
  65. package/lib/index-sync.js +13 -1
  66. package/lib/index.js +1507 -158
  67. package/lib/intent-clean-safe.js +258 -40
  68. package/lib/l0-extract.js +231 -16
  69. package/lib/m4-corpus.js +8 -2
  70. package/lib/m7-index-sync-host.js +8 -1
  71. package/lib/memory-envelope.js +6 -1
  72. package/lib/memory-hub.js +127 -12
  73. package/lib/memory-index.js +4 -2
  74. package/lib/note-status-apply.js +118 -0
  75. package/lib/note-status.js +196 -0
  76. package/lib/procedure-store.js +84 -3
  77. package/lib/python-sidecar-client.js +29 -3
  78. package/lib/recall-fusion.js +83 -12
  79. package/lib/rules-edit.js +159 -0
  80. package/lib/semantic-decide.js +41 -8
  81. package/lib/semantic-js.js +51 -6
  82. package/lib/shadow-host.js +3 -5
  83. package/lib/skill-export-host.js +153 -0
  84. package/lib/skill-export.js +239 -0
  85. package/lib/storage-manage.js +6 -0
  86. package/lib/temporal-parse.js +191 -159
  87. package/lib/tier0-catalog.js +45 -3
  88. package/lib/wb-contract.js +198 -2
  89. package/lib/wb-sidecar.js +54 -3
  90. package/package.json +1 -1
@@ -42,7 +42,12 @@ export const WB_MARKERS_V1 = Object.freeze({
42
42
  userClose: '<!-- /user -->',
43
43
  })
44
44
 
45
- /** 卡片状态(与记忆条目状态同域;缺 `status` 视为 `current`,与 `isCurrentPre` 口径一致)。 */
45
+ /** 卡片状态(与记忆条目状态同域;缺 `status` 视为 `current`,与 `isCurrentPre` 口径一致)。
46
+ * ⚠️ 2026-09-17 L7 存活标注: 本常量**当前无仓内消费者**(全仓引用计数 = 1, 仅此定义)。
47
+ * 判定 = **对外契约保留**, 不删 —— 理由是它是 WB-FORMAT-CONVENTION 的**状态值域声明**
48
+ * (`current`/`superseded`/`retracted`), 属已发布插件 (@a9i5k4/dsh-auto-memory 3.0.0, 有真实用户)
49
+ * 的公开导出面; 第三方按契约读取状态时需要它作为**单一真源**, 删掉等于把值域契约降级成散落字面量。
50
+ * ⇒ 保留 + 显式标注, 不做"沉默留着"。 */
46
51
  export const WB_STATUSES_V1 = Object.freeze(['current', 'superseded', 'retracted'])
47
52
 
48
53
  /** 解析/校验的原因码 → 可读中文。 */
@@ -59,7 +64,11 @@ export const WB_REASONS_V1 = Object.freeze({
59
64
  'user-region-interleaved': '人机分区标记交错(user 段里又开 model 段)',
60
65
  })
61
66
 
62
- /** 原因码 → 可读中文(未知码原样返回,排障不吞信息)。 */
67
+ /** 原因码 → 可读中文(未知码原样返回,排障不吞信息)。
68
+ * ⚠️ 2026-09-17 L7 存活标注: 本函数**当前无仓内调用者**(全仓引用计数 = 1, 仅此定义)。
69
+ * 判定 = **对外契约保留**, 不删 —— 它是 `WB_REASONS_V1`(上文, **有消费者**) 的唯一
70
+ * 取值入口; 删掉函数会让那份码表的"怎么读"离开公开面, 而码表本身是契约的一部分
71
+ * (发布版已有真实用户)。⇒ 保留 + 显式标注。 */
63
72
  export function describeWbReasonPre(code) {
64
73
  const k = String(code == null ? '' : code)
65
74
  return WB_REASONS_V1[k] || k || '未知原因'
@@ -254,6 +263,193 @@ function emptyResult(kind, issues) {
254
263
  }
255
264
  }
256
265
 
266
+ // ─────────────────────────────────────────────────────────────────────────────
267
+ // ★M2(2026-09-18)白板 lint —— 契约 §6「零 token 可判的四类」
268
+ //
269
+ // **硬约束(契约 §6 尾注,不可越)**:lint **只报告,不自动改**。
270
+ // 「任何自动修正都会把白板变成状态机(违反边界)」
271
+ // ⇒ 本模块是**纯函数**:不读文件、不写文件、不 import fs。
272
+ // 输入什么就判什么,输出问题清单。任何写盘能力都不得塞进这里(报告与写入分离)。
273
+ //
274
+ // 四类判据(§6 原文)与**本实现的口径**(判据纪律:宁可漏判,不可误伤):
275
+ // ① 孤立条目:无入站引用 → 全部卡正文里都找不到它的 id/标题
276
+ // ② 陈旧:status≠current 或超期 current → 状态直读 + 与 now 比日期
277
+ // ③ 被提及却无独立卡 → ★ 只判**客观可证**的一种:正文引用了
278
+ // `mem_<32hex>` 但该 id 没有对应卡片("什么算概念"无法零 token 判定,不猜)
279
+ // ④ 缺交叉引用 → ★ 只判**客观可证**的一种:两卡共享 ≥N 个 tag
280
+ // 却互不引用("什么算相关"由 tag 交集代言,不猜语义)
281
+ // ⑤ 矛盾检测 → 需 LLM,**不在本模块**(契约 §6:必须手动触发,不进自动路径)
282
+ // ─────────────────────────────────────────────────────────────────────────────
283
+
284
+ export const WB_LINT_VERSION = 'wb_lint_v1'
285
+
286
+ /** lint 问题码(§6 四类;⑤ 矛盾检测需 LLM,不在此列)。 */
287
+ export const WB_LINT_CODES_V1 = Object.freeze({
288
+ ORPHAN: 'orphan', // ① 孤立条目
289
+ INACTIVE: 'inactive', // ② 陈旧(已标 superseded/retracted)
290
+ EXPIRED: 'expired', // ② 陈旧(仍标 current 但超阈值)
291
+ DANGLING_REF: 'dangling-ref', // ③ 被引用却无卡(悬空锚点引用)
292
+ NO_CROSS_REF: 'no-cross-ref', // ④ 缺交叉引用(共享 tag 却互不引用)
293
+ })
294
+
295
+ /** 默认口径。 */
296
+ export const WB_LINT_DEFAULTS_V1 = Object.freeze({
297
+ staleDays: 90, // ② 仍标 current 但日期早于 now-90d ⇒ 报 expired
298
+ minSharedTags: 2, // ④ 共享 tag 数 ≥ 2 才算"应互链"
299
+ })
300
+
301
+ const LINT_ANCHOR_RE = /mem_[0-9a-f]{32}/g
302
+
303
+ const asDateMs = (v) => {
304
+ const s = String(v == null ? '' : v)
305
+ const m = s.match(/^(\d{4})-(\d{2})-(\d{2})/)
306
+ if (!m) return NaN
307
+ const t = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]))
308
+ return Number.isFinite(t) ? t : NaN
309
+ }
310
+
311
+ /**
312
+ * 白板 lint(**只读纯函数**,契约 §6)。
313
+ *
314
+ * @param {Array<object>|{entries?:Array<object>}} entries 条目数组;每项可含
315
+ * `{id, title, status, date, ts, tags, body}`。`body` 为该卡正文(用于扫描引用);
316
+ * 缺 `body` 时退化为用 `title+id` 判引用(**不报错**——信息少就少判,不猜)。
317
+ * @param {{now?:Date|string|number, staleDays?:number, minSharedTags?:number, corpus?:string}} [opts]
318
+ * `corpus` 为白板之外的**额外文本**(如项目笔记/账本全文),一并参与"入站引用"扫描。
319
+ * @returns {{version:string, ok:boolean, findings:Array<object>, counts:object}}
320
+ */
321
+ export function lintWhiteboardPre(entries, opts = {}) {
322
+ const o = opts && typeof opts === 'object' ? opts : {}
323
+ const list = Array.isArray(entries) ? entries
324
+ : (entries && Array.isArray(entries.entries) ? entries.entries : [])
325
+ const staleDays = Number.isFinite(Number(o.staleDays)) ? Number(o.staleDays) : WB_LINT_DEFAULTS_V1.staleDays
326
+ const minSharedTags = Number.isFinite(Number(o.minSharedTags)) ? Number(o.minSharedTags) : WB_LINT_DEFAULTS_V1.minSharedTags
327
+
328
+ const nowMs0 = o.now instanceof Date ? o.now.getTime()
329
+ : (typeof o.now === 'number' ? o.now : asDateMs(o.now))
330
+ const nowMs = Number.isFinite(nowMs0) ? nowMs0 : Date.now()
331
+
332
+ // ── 归一化(**永不抛**:坏条目降级为跳过,不打断整轮 lint)──
333
+ const cards = []
334
+ let skipped = 0
335
+ for (const e of list) {
336
+ if (!e || typeof e !== 'object') { skipped++; continue }
337
+ const id = String(e.id || '')
338
+ const title = String(e.title || '').trim()
339
+ if (!id && !title) { skipped++; continue }
340
+ const status = String(e.status || 'current').trim() || 'current'
341
+ const dateSrc = e.date || e.ts || ''
342
+ const tags = Array.isArray(e.tags) ? e.tags.map((t) => String(t)).filter(Boolean) : []
343
+ cards.push({ id, title, status, tags, dateMs: asDateMs(dateSrc), dateRaw: String(dateSrc || '') })
344
+ }
345
+
346
+ // ── 引用语料:所有卡正文 + 外部 corpus ──
347
+ const parts = []
348
+ for (const e of list) {
349
+ if (e && typeof e === 'object') {
350
+ if (e.body != null) parts.push(String(e.body))
351
+ if (e.preview != null) parts.push(String(e.preview))
352
+ }
353
+ }
354
+ if (o.corpus != null) parts.push(String(o.corpus))
355
+ const haystack = parts.join('\n')
356
+
357
+ const findings = []
358
+ const push = (code, card, detail) => {
359
+ findings.push({ code, id: card ? card.id : '', title: card ? card.title : '', detail })
360
+ }
361
+
362
+ // ── ① 孤立条目:无入站引用 ──
363
+ // 判据:除自己以外,任何卡正文/外部语料都未出现它的 id,也未出现它的标题。
364
+ // 纪律:**只报"全无提及"**;标题过短(<2 字符)时跳过,避免单字误命中(宁可漏判)。
365
+ for (const c of cards) {
366
+ let referenced = false
367
+ if (c.id) {
368
+ const re = new RegExp(c.id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
369
+ const hits = haystack.match(re)
370
+ // 自己正文里出现自己的 id 不算入站引用
371
+ const selfBody = String((list.find((x) => x && x.id === c.id) || {}).body || '')
372
+ const selfHits = selfBody.match(re)
373
+ const inHits = (hits ? hits.length : 0) - (selfHits ? selfHits.length : 0)
374
+ if (inHits > 0) referenced = true
375
+ }
376
+ if (!referenced && c.title && c.title.length >= 2) {
377
+ if (haystack.includes(c.title)) {
378
+ // 标题出现次数 > 自身正文里的次数 ⇒ 有外部提及
379
+ const selfBody = String((list.find((x) => x && x.id === c.id) || {}).body || '')
380
+ const total = haystack.split(c.title).length - 1
381
+ const selfN = selfBody.split(c.title).length - 1
382
+ if (total - selfN > 0) referenced = true
383
+ }
384
+ }
385
+ if (!referenced) push(WB_LINT_CODES_V1.ORPHAN, c, '无任何其他卡片或语料引用它的 id/标题')
386
+ }
387
+
388
+ // ── ② 陈旧 ──
389
+ const cutoff = nowMs - staleDays * 86400000
390
+ for (const c of cards) {
391
+ if (c.status !== 'current') {
392
+ push(WB_LINT_CODES_V1.INACTIVE, c, 'status=' + c.status + '(已作废,仍在白板内)')
393
+ continue
394
+ }
395
+ if (Number.isFinite(c.dateMs) && c.dateMs < cutoff) {
396
+ const ageDays = Math.floor((nowMs - c.dateMs) / 86400000)
397
+ push(WB_LINT_CODES_V1.EXPIRED, c, '仍标 current 但日期为 ' + c.dateRaw + '(' + ageDays + ' 天前,超阈值 ' + staleDays + ' 天)')
398
+ }
399
+ }
400
+
401
+ // ── ③ 悬空锚点引用:正文引用了 mem_id 但没有对应卡片 ──
402
+ // 契约 §2 的 id 是"内容寻址、可复算"的 ⇒ 引用了不存在的 id 是**客观可证**的断裂。
403
+ const knownIds = new Set(cards.map((c) => c.id).filter(Boolean))
404
+ const allIds = new Set()
405
+ for (const m of haystack.match(LINT_ANCHOR_RE) || []) allIds.add(m)
406
+ for (const id of allIds) {
407
+ if (!knownIds.has(id)) {
408
+ findings.push({
409
+ code: WB_LINT_CODES_V1.DANGLING_REF, id, title: '',
410
+ detail: '正文引用了 ' + id + ',但白板内没有该 id 的卡片(悬空引用)',
411
+ })
412
+ }
413
+ }
414
+
415
+ // ── ④ 缺交叉引用:共享 ≥minSharedTags 个 tag 却互不引用 ──
416
+ for (let i = 0; i < cards.length; i++) {
417
+ for (let j = i + 1; j < cards.length; j++) {
418
+ const a = cards[i], b = cards[j]
419
+ if (!a.tags.length || !b.tags.length) continue
420
+ const shared = a.tags.filter((t) => b.tags.includes(t))
421
+ if (shared.length < minSharedTags) continue
422
+ const aRefB = (a.id && b.id && String((list.find((x) => x && x.id === a.id) || {}).body || '').includes(b.id)) ||
423
+ (b.title && String((list.find((x) => x && x.id === a.id) || {}).body || '').includes(b.title))
424
+ const bRefA = (a.id && b.id && String((list.find((x) => x && x.id === b.id) || {}).body || '').includes(a.id)) ||
425
+ (a.title && String((list.find((x) => x && x.id === b.id) || {}).body || '').includes(a.title))
426
+ if (!aRefB && !bRefA) {
427
+ findings.push({
428
+ code: WB_LINT_CODES_V1.NO_CROSS_REF, id: a.id, title: a.title,
429
+ detail: '与「' + (b.title || b.id) + '」共享 ' + shared.length + ' 个 tag(' + shared.join(', ') + ')但互不引用',
430
+ })
431
+ }
432
+ }
433
+ }
434
+
435
+ const byCode = {}
436
+ for (const f of findings) byCode[f.code] = (byCode[f.code] || 0) + 1
437
+
438
+ return {
439
+ version: WB_LINT_VERSION,
440
+ ok: findings.length === 0,
441
+ findings,
442
+ counts: {
443
+ cards: cards.length,
444
+ skipped,
445
+ findings: findings.length,
446
+ byCode,
447
+ // ⑤ 明确声明"未判" —— 不许静默省略(契约 §6:需 LLM,必须手动触发)
448
+ notChecked: ['contradiction'],
449
+ },
450
+ }
451
+ }
452
+
257
453
  /**
258
454
  * 从投影里抽出"**必须逐字节保留**的用户区"(供写入门做前后比对)。
259
455
  *
package/lib/wb-sidecar.js CHANGED
@@ -287,8 +287,17 @@ export function buildSectionCardsPre(workspaceKey, docs, opts = {}) {
287
287
  * 契约对齐 WB-GRAPH-INTEGRATION-PLAN §4.1(条目级): id/kind/source/section/tags/cues/preview/mtime/criteria。
288
288
  * 兼容旧字段: title/cue/chars/ts 保留(既有套件与工具描述在用)。
289
289
  *
290
+ * ★M1(2026-09-18)新增 `layer`/`status`:契约 §3 的索引派生行要求两者
291
+ * (`… · layer=whiteboard · status=current · 日期`)。此前 entry 只有 `id`(已是
292
+ * `mem_<32hex>` 形态),缺这两个字段 ⇒ 渲染侧拿不到值,一直退化成纯文本行。
293
+ * - `layer` 恒为 `'whiteboard'`:本模块**只服务于白板/账本**这一层,
294
+ * 不是从调用方传入的自由值(避免调用方各传各的、导致层级语义漂移)。
295
+ * - `status` 取 `o.status`,缺省 `'current'`(与契约 §3 尾注
296
+ * 「缺 status 视为 current」、与 `isCurrentPre` 口径一致)。
297
+ * 两个字段**只增不改**:既有字段顺序与语义逐字节不变。
298
+ *
290
299
  * @param {{workspaceKey:string, relPath:string, text:string, title:string, ts?:string,
291
- * kind?:string, mtime?:number, criteria?:string}} o
300
+ * kind?:string, mtime?:number, criteria?:string, status?:string}} o
292
301
  */
293
302
  export function buildSidecarEntryPre(o = {}) {
294
303
  const src = String(o.text || '')
@@ -308,6 +317,9 @@ export function buildSidecarEntryPre(o = {}) {
308
317
  preview: src.replace(/\s+/g, ' ').trim().slice(0, 120),
309
318
  mtime: Number(o.mtime) || 0,
310
319
  criteria: String(o.criteria || 'unknown'),
320
+ // ── ★M1:契约 §3 索引派生行所需(只增不改)──
321
+ layer: 'whiteboard',
322
+ status: String(o.status || 'current'),
311
323
  // ── 兼容旧套件/工具描述的字段 ──
312
324
  title,
313
325
  cue: cueLine.trim().slice(0, 120),
@@ -316,6 +328,45 @@ export function buildSidecarEntryPre(o = {}) {
316
328
  }
317
329
  }
318
330
 
331
+ /**
332
+ * ★M1(2026-09-18):sidecar 条目 → Tier-0 目录项(**契约 §8 的桥**)。
333
+ *
334
+ * 契约 §8 要求:「由页面派生出的 index 与 Tier-0 目录条目**一致**」。
335
+ * 实现方式 = **同一次派生、两种渲染** —— 本函数**只做字段映射,不复制任何逻辑**:
336
+ * entries 与 Tier-0 条目本就是同一份数据,映射后交给
337
+ * `renderCatalogLinePre` 渲染,一致性天然成立(无需额外同步机制)。
338
+ *
339
+ * 为什么不在此处直接渲染成字符串:渲染口径必须**只有一处实现**
340
+ * (`renderCatalogLinePre`),否则「两种渲染」会退化成「两个渲染器」,
341
+ * 长期必然漂移 —— 这正是 §8 想防的。
342
+ *
343
+ * 字段映射(只映射,不发明):
344
+ * - title ← entry.title
345
+ * - oneLine ← entry.preview(已是单行化 + 120 字截断,即「一句话摘要」)
346
+ * - layer ← entry.layer(本模块恒为 'whiteboard')
347
+ * - status ← entry.status(缺省 'current')
348
+ * - date ← entry.ts 的日期部分(无则空;不编造)
349
+ * - path ← entry.source(相对路径,渲染时拼锚点)
350
+ * - id ← entry.id(已是 mem_<32hex>)
351
+ *
352
+ * @param {object} entry buildSidecarEntryPre 的产物
353
+ * @returns {{title:string,oneLine:string,layer:string,status:string,date:string,path:string,id:string}}
354
+ */
355
+ export function sidecarEntryToCatalogItemPre(entry) {
356
+ const e = entry && typeof entry === 'object' ? entry : {}
357
+ const ts = String(e.ts || '')
358
+ const dateOnly = (ts.match(/^\d{4}-\d{2}-\d{2}/) || [''])[0]
359
+ return {
360
+ title: String(e.title || ''),
361
+ oneLine: String(e.preview || ''),
362
+ layer: String(e.layer || 'whiteboard'),
363
+ status: String(e.status || 'current'),
364
+ date: dateOnly,
365
+ path: String(e.source || ''),
366
+ id: String(e.id || ''),
367
+ }
368
+ }
369
+
319
370
  /** tag → 条目 id 倒排(§3.3 by_tag)。 */
320
371
  export function buildByTagPre(entries) {
321
372
  const byTag = {}
@@ -389,10 +440,10 @@ export function rebuildSidecarIndexPre(workspaceKey, docs) {
389
440
 
390
441
  /** 生成 §4.1 的 hint 文案(工具返回值里引导模型向下走)。 */
391
442
  function hintForExpand() {
392
- return '可用 memory_trace_pre(id) 回溯该条目的 cue/tag/邻居与归档版本'
443
+ return '可用 memory_trace(id) 回溯该条目的 cue/tag/邻居与归档版本'
393
444
  }
394
445
  function hintForTrace() {
395
- return '可用 memory_expand_pre(tag) 按 tag 展开同主题条目; neighbors 为同 tag/同 cue 的相邻条目'
446
+ return '可用 memory_expand(tag) 按 tag 展开同主题条目; neighbors 为同 tag/同 cue 的相邻条目'
396
447
  }
397
448
 
398
449
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@a9i5k4/dsh-auto-memory",
3
3
  "description": "Proactive associative memory for DSH: zero-prompt recall injected before the model speaks, three-layer auto-consolidation, skill crystallization, and Astra-style context management - handoff ledgers, PLAN whiteboard, water-level sensing. Local-first, model-agnostic, zero deps. 主动联想记忆+Astra 式上下文管理:自动唤回/自动沉淀/技能固化/交接账本与白板跨窗口续命/水位感知。",
4
- "version": "3.0.0",
4
+ "version": "3.0.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {