@a9i5k4/dsh-auto-memory 2.1.9 → 2.2.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # dsh-auto-memory — She remembers, unbidden
1
+ # dsh-auto-memory — She remembers, unbidden · 无问自忆
2
2
 
3
- > **Now, across windows, too.**
4
- > Context that survives windows, sessions, and tools
3
+ > **EN** Now, across windows, too. Context that survives windows, sessions, and tools
4
+ > **中文** 无问自忆——该想起的,自己浮现。跨窗口 · 跨会话 · 跨工具,记忆不断线
5
5
 
6
6
  <p align="center">
7
7
  <a href="https://htmlpreview.github.io/?https://github.com/Aik358/dsh-auto-memory/blob/preview/docs/landing/index.html"><strong>🌐 Landing page (full feature tour · data flow · papers · screenshots)</strong></a>
package/README.zh-CN.md CHANGED
@@ -1,7 +1,7 @@
1
- # dsh-auto-memory — 她记得,不必吩咐
1
+ # dsh-auto-memory — 无问自忆 · She remembers, unbidden
2
2
 
3
- > **现在,换窗口也不必。**
4
- > 跨窗口 · 跨会话 · 跨工具,记忆不断线
3
+ > **中文** 现在,换窗口也不必。跨窗口 · 跨会话 · 跨工具,记忆不断线
4
+ > **EN** She remembers, unbidden — now, across windows, too. Context that survives windows, sessions, and tools
5
5
 
6
6
  <p align="center">
7
7
  <a href="https://htmlpreview.github.io/?https://github.com/Aik358/dsh-auto-memory/blob/preview/docs/landing/index.html"><strong>🌐 宣传主页(功能全景 · 数据流 · 论文 · 截图)</strong></a>
package/lib/client.js CHANGED
@@ -262,6 +262,14 @@ window.__ModuleLoader__.load({
262
262
  fWaterWindow: '水位估计窗口(token)', fWaterWindowHint: '会话消息按官方公式(4 字符≈1 token)估算达到该值即视为上下文将满;0=关闭。默认 65536(128K 窗口的保守半量),按所用模型调整。',
263
263
  fWaterAdvisory: '水位交接建议', fWaterAdvisoryHint: '水位越阈时在动态快照注入交接建议(写账本/刷新白板/建议开新窗);无人值守时静默。',
264
264
  fWaterAuto: '水位自动骨架账本', fWaterAutoHint: '越阈时自动写一篇系统骨架账本(每会话一次),防止模型忽视建议时交接材料缺失。',
265
+ fWaterThreshold: '水位建议阈值', fWaterThresholdHint: '水位比例超过该值(0.1-1.5)即触发交接建议/骨架账本;默认 0.8。',
266
+ fHandoffPlan: '白板注入预算(字符)', fHandoffPlanHint: 'PLAN.md 全貌注入动态快照的硬截断预算;全文可经 memory_read 或面板白板页查看。默认 1200。',
267
+ fHandoffLedger: '账本注入预算(字符)', fHandoffLedgerHint: '最新一篇交接账本注入动态快照的硬截断预算。默认 800。',
268
+ fConsSchedule: '定时做梦式固化', fConsScheduleHint: '每天到点自动读最近日志发散提炼长期要点,写入项目笔记/用户级记忆(等价 memory_consolidate)。',
269
+ fConsScheduleTime: '固化触发时间', fConsScheduleTimeHint: 'HH:MM,插件日界内每天一次;默认 09:30。命中时刻需宿主在线。',
270
+ fConsScheduleDays: '固化回看天数', fConsScheduleDaysHint: '定时固化读取最近 N 天日志;默认 7。',
271
+ fMaintSchedule: '定时 30 天蒸馏', fMaintScheduleHint: '每天到点自动把超过 30 天的旧日志蒸馏归档(等价 memory_maintain);无旧日志时零成本跳过。',
272
+ fMaintScheduleTime: '蒸馏触发时间', fMaintScheduleTimeHint: 'HH:MM,默认 10:00(与固化时间错开)。',
265
273
  waterCardTitle: '上下文水位', waterAutoSrc: '自动检测', waterManualSrc: '手动设定', waterThresholdHint: '水位达到 {t} 时注入交接建议并自动补写账本;窗口大小自动取自当前模型(settings.yaml contextWindow),也可在下方手动覆盖。',
266
274
  fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
267
275
  fLocale: '界面语言', fLocaleHint: '默认跟随 DSH 系统语言;也可手动指定中文 / English。', followSystem: '跟随系统语言',
@@ -400,6 +408,14 @@ window.__ModuleLoader__.load({
400
408
  fWaterWindow: 'Water-level window (tokens)', fWaterWindowHint: 'Session messages priced with the official meter (4 chars ≈ 1 token); past this, context is considered nearly full; 0 disables. Default 65536 — a conservative half of a 128K window.',
401
409
  fWaterAdvisory: 'Water-level handoff advisory', fWaterAdvisoryHint: 'Injects a handoff advisory past the threshold (write ledger / refresh plan / suggest a new window); silent in unattended mode.',
402
410
  fWaterAuto: 'Auto skeleton ledger', fWaterAutoHint: 'Past the threshold, auto-writes one system skeleton ledger per session as a safety net if the model ignores the advisory.',
411
+ fWaterThreshold: 'Water-level threshold', fWaterThresholdHint: 'Inject the handoff advisory / skeleton ledger once the ratio passes this value (0.1-1.5); default 0.8.',
412
+ fHandoffPlan: 'Plan injection budget (chars)', fHandoffPlanHint: 'Hard truncation budget for injecting the PLAN.md snapshot into the dynamic memory; full text via memory_read or the Whiteboard tab. Default 1200.',
413
+ fHandoffLedger: 'Ledger injection budget (chars)', fHandoffLedgerHint: 'Hard truncation budget for injecting the latest handoff ledger. Default 800.',
414
+ fConsSchedule: 'Scheduled deep consolidation', fConsScheduleHint: 'Once a day at the set time, read recent logs and distill long-term value into project notes / user memory (same flow as memory_consolidate).',
415
+ fConsScheduleTime: 'Consolidation time', fConsScheduleTimeHint: 'HH:MM, once per plugin day; default 09:30. The host must be online at that moment.',
416
+ fConsScheduleDays: 'Consolidation lookback (days)', fConsScheduleDaysHint: 'How many recent days of logs the scheduled consolidation reads; default 7.',
417
+ fMaintSchedule: 'Scheduled 30-day distill', fMaintScheduleHint: 'Once a day at the set time, distill & archive logs older than 30 days (same flow as memory_maintain); zero cost when nothing to archive.',
418
+ fMaintScheduleTime: 'Distill time', fMaintScheduleTimeHint: 'HH:MM, default 10:00 (staggered from the consolidation time).',
403
419
  waterCardTitle: 'Context water level', waterAutoSrc: 'auto-detected', waterManualSrc: 'manual', waterThresholdHint: 'Past {t} of the window, the handoff advisory is injected and the skeleton ledger is backfilled; the window auto-follows the active model (settings.yaml contextWindow) or your manual override below.',
404
420
  fStyle: 'Reflection style', fStyleHint: 'Life-style / Professional / Auto.',
405
421
  fLocale: 'UI language', fLocaleHint: 'Follows the DSH system language by default; you can also pin Chinese / English.', followSystem: 'Follow system language',
@@ -480,6 +496,17 @@ window.__ModuleLoader__.load({
480
496
 
481
497
  // ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
482
498
  var CHANGELOG = {
499
+ '2.2.0': { zh: [
500
+ '★ 外部记忆继承扩展:新增 ZCode / Kimi Code / TRAE 三个来源——自动扫描常见数据目录(~/.zcode、~/.kimi(.kimi-code)、~/.trae 及工作区 .trae/rules),目录存在即出现在「接续」页签,一键链接导入;沿用链接模式,只注入绝对路径,不灌内容。',
501
+ '★ 定时记忆固化:新增「定时做梦式固化」(默认每天 09:30,读最近 7 天日志提炼长期要点)与「定时 30 天蒸馏」(默认 10:00,无旧日志时零成本跳过)——设置 → 自动化 可调。',
502
+ '设置页新增「上下文管理」分区:交接白板/水位感知全套设置归拢一处,并新暴露白板注入预算、账本注入预算、水位阈值三个字段。',
503
+ 'README 中英双语主副标题;动态快照的外部记忆源展示上限 3 → 6。',
504
+ ], en: [
505
+ '★ External memory expanded: three new sources — ZCode / Kimi Code / TRAE. Common data directories (~/.zcode, ~/.kimi(.kimi-code), ~/.trae and workspace .trae/rules) are auto-scanned; sources appear in the Connect tab when present and import as pure path links — link mode, no content dumping.',
506
+ '★ Scheduled consolidation: "deep consolidation" (daily at 09:30 by default, distills long-term value from the last 7 days of logs) and "30-day distill" (10:00, zero cost when nothing to archive) — Settings → Automation.',
507
+ 'Settings gains a "Context management" section: handoff whiteboard + water-level settings in one place, with plan/ledger injection budgets and the water threshold newly exposed.',
508
+ 'Bilingual (EN/中文) main title & subtitle in the READMEs; dynamic snapshot now lists up to 6 external memory sources (was 3).',
509
+ ] },
483
510
  '2.1.0': { zh: [
484
511
  '★ 新增「交接白板」:模型理解项目全貌后自动写 PLAN.md 白板(旧版自动归档),阶段产出写四段式交接账本——任务状态/目标/已试方案与失败原因/进度与下一步;面板新增「白板」页签实时查看。上下文窗口换页不丢线索,对标 GPT-6 Astra 的上下文管理。',
485
512
  '★ 新增「水位感知与交接助产」:按官方公式估算会话 token,窗口自动取自当前模型(如 1M);水位越阈自动注入交接建议并补写账本;memory_recall 新增 handoff/sessions 直达;无人值守静默。',
@@ -3340,6 +3367,7 @@ window.__ModuleLoader__.load({
3340
3367
  memoryHub: locale === 'zh' ? '记忆中枢' : 'Memory Hub',
3341
3368
  appearance: locale === 'zh' ? '外观' : 'Appearance', storage: locale === 'zh' ? '存储' : 'Storage',
3342
3369
  injection: locale === 'zh' ? '记忆窗口' : 'Memory window', automation: locale === 'zh' ? '自动化' : 'Automation',
3370
+ context: locale === 'zh' ? '上下文管理' : 'Context management',
3343
3371
  maintenance: locale === 'zh' ? '维护' : 'Maintenance'
3344
3372
  }
3345
3373
  function section(key, title, content) { return h('section', { id: 'dam-settings-' + key, 'data-dam-settings-group': '' }, h('h3', null, title), content) }
@@ -3534,10 +3562,11 @@ window.__ModuleLoader__.load({
3534
3562
  field(t('fAutoSum'), h('input', { 'data-dam-input': '', value: (cfg.autoSummaryTimes || []).join(','), onChange: function (e) { set('autoSummaryTimes', String(e.target.value || '').split(',').map(function (s) { return s.trim() }).filter(Boolean)) } }), t('fAutoSumHint')),
3535
3563
  field(t('fDayBoundary'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 1439, value: (cfg.dayBoundaryMinutes === undefined ? 450 : cfg.dayBoundaryMinutes), onChange: function (e) { set('dayBoundaryMinutes', parseInt(e.target.value || '0', 10)) } }), t('fDayBoundaryHint')),
3536
3564
  field(t('fReflect'), h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), t('fReflectHint')),
3537
- field(t('fHandoff'), h('input', { type: 'checkbox', checked: cfg.handoffEnabled !== false, onChange: function (e) { set('handoffEnabled', e.target.checked) } }), t('fHandoffHint')),
3538
- field(t('fWaterWindow'), h('input', { 'data-dam-input': '', type: 'number', min: 0, value: cfg.waterLevelWindowTokens === undefined ? 65536 : cfg.waterLevelWindowTokens, onChange: function (e) { set('waterLevelWindowTokens', Number(e.target.value) || 0) } }), t('fWaterWindowHint')),
3539
- field(t('fWaterAdvisory'), h('input', { type: 'checkbox', checked: cfg.waterLevelAdvisory !== false, onChange: function (e) { set('waterLevelAdvisory', e.target.checked) } }), t('fWaterAdvisoryHint')),
3540
- field(t('fWaterAuto'), h('input', { type: 'checkbox', checked: cfg.waterLevelAutoHandoff !== false, onChange: function (e) { set('waterLevelAutoHandoff', e.target.checked) } }), t('fWaterAutoHint')),
3565
+ field(t('fConsSchedule'), h('input', { type: 'checkbox', checked: cfg.consolidateScheduleEnabled !== false, onChange: function (e) { set('consolidateScheduleEnabled', e.target.checked) } }), t('fConsScheduleHint')),
3566
+ field(t('fConsScheduleTime'), h('input', { 'data-dam-input': '', placeholder: '09:30', value: cfg.consolidateScheduleTime === undefined ? '09:30' : cfg.consolidateScheduleTime, onChange: function (e) { set('consolidateScheduleTime', String(e.target.value || '').trim()) } }), t('fConsScheduleTimeHint')),
3567
+ field(t('fConsScheduleDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 60, value: cfg.consolidateScheduleDays === undefined ? 7 : cfg.consolidateScheduleDays, onChange: function (e) { set('consolidateScheduleDays', Number(e.target.value) || 7) } }), t('fConsScheduleDaysHint')),
3568
+ field(t('fMaintSchedule'), h('input', { type: 'checkbox', checked: cfg.maintainScheduleEnabled !== false, onChange: function (e) { set('maintainScheduleEnabled', e.target.checked) } }), t('fMaintScheduleHint')),
3569
+ field(t('fMaintScheduleTime'), h('input', { 'data-dam-input': '', placeholder: '10:00', value: cfg.maintainScheduleTime === undefined ? '10:00' : cfg.maintainScheduleTime, onChange: function (e) { set('maintainScheduleTime', String(e.target.value || '').trim()) } }), t('fMaintScheduleTimeHint')),
3541
3570
  field(t('fStyle'), h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
3542
3571
  STYLE_IDS.map(function (id) { return h('option', { key: id, value: id }, t('style' + id.charAt(0).toUpperCase() + id.slice(1))) })), t('fStyleHint')),
3543
3572
  field(locale === 'zh' ? '总结/问候默认模型' : 'Summary & greeting model', h('div', { 'data-dam-row': '', style: { flex: 1 } },
@@ -3546,6 +3575,13 @@ window.__ModuleLoader__.load({
3546
3575
  locale === 'zh' ? '用于时段总结、问候语、自动沉淀等 subagent 功能;从检测到的模型中选择,或留空跟随路由默认。保存后生效。' : 'For scheduled summaries, greetings and auto-consolidation subagents; pick a detected model or leave empty for the routing default. Applies after saving.'),
3547
3576
  mdlOpen ? buildModelDrawer() : null,
3548
3577
  ]),
3578
+ section('context', sectionLabels.context, [field(t('fHandoff'), h('input', { type: 'checkbox', checked: cfg.handoffEnabled !== false, onChange: function (e) { set('handoffEnabled', e.target.checked) } }), t('fHandoffHint')),
3579
+ field(t('fHandoffPlan'), h('input', { 'data-dam-input': '', type: 'number', min: 200, value: cfg.handoffPlanChars === undefined ? 1200 : cfg.handoffPlanChars, onChange: function (e) { set('handoffPlanChars', Number(e.target.value) || 1200) } }), t('fHandoffPlanHint')),
3580
+ field(t('fHandoffLedger'), h('input', { 'data-dam-input': '', type: 'number', min: 100, value: cfg.handoffLedgerChars === undefined ? 800 : cfg.handoffLedgerChars, onChange: function (e) { set('handoffLedgerChars', Number(e.target.value) || 800) } }), t('fHandoffLedgerHint')),
3581
+ field(t('fWaterWindow'), h('input', { 'data-dam-input': '', type: 'number', min: 0, value: cfg.waterLevelWindowTokens === undefined ? 65536 : cfg.waterLevelWindowTokens, onChange: function (e) { set('waterLevelWindowTokens', Number(e.target.value) || 0) } }), t('fWaterWindowHint')),
3582
+ field(t('fWaterThreshold'), h('input', { 'data-dam-input': '', type: 'number', min: 0.1, max: 1.5, step: 0.05, value: cfg.waterLevelThreshold === undefined ? 0.8 : cfg.waterLevelThreshold, onChange: function (e) { set('waterLevelThreshold', Number(e.target.value) || 0.8) } }), t('fWaterThresholdHint')),
3583
+ field(t('fWaterAdvisory'), h('input', { type: 'checkbox', checked: cfg.waterLevelAdvisory !== false, onChange: function (e) { set('waterLevelAdvisory', e.target.checked) } }), t('fWaterAdvisoryHint')),
3584
+ field(t('fWaterAuto'), h('input', { type: 'checkbox', checked: cfg.waterLevelAutoHandoff !== false, onChange: function (e) { set('waterLevelAutoHandoff', e.target.checked) } }), t('fWaterAutoHint'))]),
3549
3585
  section('maintenance', sectionLabels.maintenance, [field(t('fVersion'), h('div', { 'data-dam-row': '' },
3550
3586
  h('span', { style: { flex: 1 } }, verInfo ? (verInfo.current || '?') + (verInfo.latest ? ' → ' + verInfo.latest + (verInfo.upToDate ? ' ' + t('upToDate') : ' ' + t('hasUpdate')) : '') : (checkingUpdate ? t('checking') : '—')),
3551
3587
  h('button', { 'data-dam-btn': '', onClick: checkUpdate, disabled: checkingUpdate }, checkingUpdate ? t('checking') : t('checkUpdate')),
package/lib/index.js CHANGED
@@ -64,7 +64,7 @@ const SECTION_ORDER = 10000
64
64
  const NOTICES_URL = 'https://raw.githubusercontent.com/Aik358/dsh-auto-memory/main/notices.json'
65
65
 
66
66
  /** Model-facing announcement (tools + engine). */
67
- export const GUIDANCE = '本机已安装 dsh-auto-memory 插件(集中式自动记忆 + 外部记忆继承):三层本地记忆(用户级 ~/.dsh/memory/MEMORY.md、项目笔记与每日日志 .dsh-memory/)+ 会话自动注入 + 每日反思 + 其他 AI 工具记忆接入。能力:memory_log 追加今日日志(append-only,完成实质性工作后必须调用);memory_note 更新项目笔记;memory_user 更新用户级规则;memory_recall 检索本地记忆 + 外部记忆(WorkBuddy/CodeBuddy/Claude Code/Codex 历史会话与画像)+ 历史 DSH 会话;memory_external 查看/接入外部记忆源;memory_maintain 归档 30 天前日志;memory_reflect 保存每日反思;memory_status 查看状态;memory_consolidate 让 AI 读日志发散提炼长期要点固化进笔记。自动沉淀:每轮对话结束插件自动评估本轮内容并写今日日志/升格长期记忆(寒暄轮跳过,间隔与每日额度可在设置页「自动化」分组调整),无需你手动调 memory_log。主动性纪律:任务开始遇到不熟悉的代码/领域/历史决策时,先 memory_recall 检索本机全部 AI 工具历史,不凭空猜测;新工作区主动探索历史。限制:记忆文件为明文 Markdown;不存密钥除非用户明确要求;外部会话检索为关键词级(非语义);GUI 侧边栏「记忆」面板(含「接续」页签,可查看来源内容、从记忆 prompt 移除已导入段落)与设置页可查看/配置/接入。用户提到「记忆 / 昨天做了什么 / 之前怎么做的 / 每日反思 / 接续 / 其他 AI 的记忆」时即指本插件,请据此协作。'
67
+ export const GUIDANCE = '本机已安装 dsh-auto-memory 插件(集中式自动记忆 + 外部记忆继承):三层本地记忆(用户级 ~/.dsh/memory/MEMORY.md、项目笔记与每日日志 .dsh-memory/)+ 会话自动注入 + 每日反思 + 其他 AI 工具记忆接入。能力:memory_log 追加今日日志(append-only,完成实质性工作后必须调用);memory_note 更新项目笔记;memory_user 更新用户级规则;memory_recall 检索本地记忆 + 外部记忆(WorkBuddy/CodeBuddy/Claude Code/Codex/ZCode/Kimi Code/TRAE 记忆与会话)+ 历史 DSH 会话;memory_external 查看/接入外部记忆源;memory_maintain 归档 30 天前日志;memory_reflect 保存每日反思;memory_status 查看状态;memory_consolidate 让 AI 读日志发散提炼长期要点固化进笔记。自动沉淀:每轮对话结束插件自动评估本轮内容并写今日日志/升格长期记忆(寒暄轮跳过,间隔与每日额度可在设置页「自动化」分组调整),无需你手动调 memory_log。主动性纪律:任务开始遇到不熟悉的代码/领域/历史决策时,先 memory_recall 检索本机全部 AI 工具历史,不凭空猜测;新工作区主动探索历史。限制:记忆文件为明文 Markdown;不存密钥除非用户明确要求;外部会话检索为关键词级(非语义);GUI 侧边栏「记忆」面板(含「接续」页签,可查看来源内容、从记忆 prompt 移除已导入段落)与设置页可查看/配置/接入。用户提到「记忆 / 昨天做了什么 / 之前怎么做的 / 每日反思 / 接续 / 其他 AI 的记忆」时即指本插件,请据此协作。'
68
68
 
69
69
  /** Route family. */
70
70
  export const API = {
@@ -141,6 +141,16 @@ const DEFAULT_CONFIG = {
141
141
  autoConsolidateCooldownMinutes: 30,
142
142
  /** 自动沉淀每日最多调用次数(跨插件实例应只保留一个实例)。 */
143
143
  autoConsolidateDailyMax: 8,
144
+ /** 定时做梦式固化(默认开):每天到点自动跑一次 memory_consolidate 等价流程(读最近日志→发散提炼→项目笔记/用户记忆)。 */
145
+ consolidateScheduleEnabled: true,
146
+ /** 做梦式固化触发时间(HH:MM,插件日界内每天一次;命中时刻需宿主在线)。 */
147
+ consolidateScheduleTime: '09:30',
148
+ /** 做梦式固化回看天数(读最近 N 天日志)。 */
149
+ consolidateScheduleDays: 7,
150
+ /** 定时 30 天蒸馏(默认开):每天到点自动跑一次 memory_maintain 等价流程;无超过 30 天的旧日志时零成本跳过(不调 AI)。 */
151
+ maintainScheduleEnabled: true,
152
+ /** 30 天蒸馏触发时间(HH:MM)。 */
153
+ maintainScheduleTime: '10:00',
144
154
  /** M-CM1 交接白板:PLAN.md(项目全貌快照,模型重写+旧版归档)+四段式交接账本;注入动态快照首位。false=完全不读写。实验特性:出厂默认关闭,设置 → 自动化 → 交接白板 开启。 */
145
155
  handoffEnabled: false,
146
156
  /** 白板 PLAN.md 注入预算(字符,硬截断;全文可经 memory_read/文件读取)。 */
@@ -209,6 +219,12 @@ const DEFAULT_CONFIG = {
209
219
  'workbuddy-sessions': true,
210
220
  'claude-sessions': true,
211
221
  'codex-sessions': true,
222
+ // 2026-09-08 新增:ZCode / Kimi Code / TRAE(自动扫描,目录存在即出源;链接模式,只注入路径不注内容)
223
+ 'zcode-memory': true,
224
+ 'zcode-sessions': true,
225
+ 'kimi-global': true,
226
+ 'kimi-sessions': true,
227
+ 'trae-rules': true,
212
228
  },
213
229
  // ---------- 主动联想记忆实验基线(M0-R 恢复;全部默认关闭,关闭时现有行为零变化) ----------
214
230
  /** 主动联想记忆总开关(观察账本之外的检索/注入行为门)。 */
@@ -2494,7 +2510,7 @@ class MemoryEngine {
2494
2510
  const paths = (x.files || []).map((f) => f.path).slice(0, 2).join(' ; ')
2495
2511
  return '· ' + x.name + '(' + x.tool + '): 绝对路径 ' + (paths || '(未知)')
2496
2512
  })
2497
- .slice(0, 3)
2513
+ .slice(0, 6)
2498
2514
  if (ext.length) lines.push('\n' + L('snapshotExternalTitle') + '\n' + ext.join('\n') + '\n需要这些记忆时:直接读取上述绝对路径文件(你有文件读取能力),或用 memory_recall 按需检索;不要凭空猜测其内容。')
2499
2515
  const sess = this.external.cache.filter((x) => x.kind === 'sessions')
2500
2516
  if (sess.length) {
@@ -2530,7 +2546,7 @@ class MemoryEngine {
2530
2546
  lines.push('思维链=本轮推理(用完即焚);铭文=落盘的记忆文件(跨会话永久)。你的记忆更新必须落在铭文层——显式调用工具写盘,不能只"想过"。')
2531
2547
  lines.push('本提醒由框架在每一轮对话开始时重新注入(记忆动态快照变化即刷新,衰减期只有一轮):读写只走工具、路径写死、每轮结束框架自动评估沉淀兜底——记忆无法被绕过,也不依赖自觉。')
2532
2548
  lines.push('\n[记忆写入纪律 — 必须遵守]')
2533
- lines.push('- 会话开始:若任务与历史工作/历史决策相关,先回顾以上记忆;**遇到不熟悉的代码、领域或项目时,主动调用 memory_recall 检索本机所有 AI 工具的历史记忆(WorkBuddy/CodeBuddy/Claude Code/Codex 会话),或直接读取外部记忆标注的绝对路径文件,不要凭空猜测**。')
2549
+ lines.push('- 会话开始:若任务与历史工作/历史决策相关,先回顾以上记忆;**遇到不熟悉的代码、领域或项目时,主动调用 memory_recall 检索本机所有 AI 工具的历史记忆(WorkBuddy/CodeBuddy/Claude Code/Codex/ZCode/Kimi Code/TRAE 会话),或直接读取外部记忆标注的绝对路径文件,不要凭空猜测**。')
2534
2550
  lines.push('- 新工作区(无历史日志/笔记):主动用 memory_recall 探索本机历史,判断该项目是否曾在其他 AI 工具中工作过;也可调用 memory_external 查看并接入外部记忆;检索时在正文中说明"我先查一下之前的记录"。')
2535
2551
  lines.push('- 完成实质性工作后立即调用 memory_log 追加今日日志(append-only,绝不覆盖):建/改应用、修 bug、写文档、重构、技术选型、用户约定或偏好。')
2536
2552
  lines.push('- progress 与 memory 一起写:写日志的同时,把有跨会话长期价值的内容一并写入记忆——跨项目规则 → memory_user,仅本项目 → memory_note;两者在同一轮完成,互不冲突、不遗漏。')
@@ -3359,6 +3375,41 @@ class MemoryEngine {
3359
3375
  }
3360
3376
  }
3361
3377
  }
3378
+ // 4) 定时做梦式固化 + 定时 30 天蒸馏(2026-09-08):模式同自动总结——命中 HH:MM 且当天未跑过才触发;
3379
+ // subagent 需要 agent 承载(取 _lastAgent,tickTime 前面 restoreLastAgent 已兜底恢复),没有则本分钟跳过,下个 tick 重试。
3380
+ const nowS = new Date()
3381
+ const hhmmS = String(nowS.getHours()).padStart(2, '0') + ':' + String(nowS.getMinutes()).padStart(2, '0')
3382
+ const todayS = this.memToday()
3383
+ if (this.config.consolidateScheduleEnabled !== false) {
3384
+ const ct = String(this.config.consolidateScheduleTime || '09:30').trim()
3385
+ const cKey = todayS + '|consolidate|' + ct
3386
+ if (ct && hhmmS === ct && this._consolidateScheduleDone !== cKey && !this._scheduleBusy) {
3387
+ const agentC = this._lastAgent
3388
+ if (agentC && agentC.session) {
3389
+ this._consolidateScheduleDone = cKey
3390
+ this._scheduleBusy = true
3391
+ void this.consolidateMemory(agentC, Math.max(1, Number(this.config.consolidateScheduleDays) || 7))
3392
+ .then((msg) => { diag('scheduled consolidate: ' + String(msg || '').split('\n')[0].slice(0, 120)) })
3393
+ .catch(() => {})
3394
+ .finally(() => { this._scheduleBusy = false })
3395
+ }
3396
+ }
3397
+ }
3398
+ if (this.config.maintainScheduleEnabled !== false) {
3399
+ const mt = String(this.config.maintainScheduleTime || '10:00').trim()
3400
+ const mKey = todayS + '|maintain|' + mt
3401
+ if (mt && hhmmS === mt && this._maintainScheduleDone !== mKey && !this._scheduleBusy) {
3402
+ const agentM = this._lastAgent
3403
+ if (agentM && agentM.session) {
3404
+ this._maintainScheduleDone = mKey
3405
+ this._scheduleBusy = true
3406
+ void this.maintain(30, agentM)
3407
+ .then((msg) => { diag('scheduled maintain: ' + String(msg || '').split('\n')[0].slice(0, 120)) })
3408
+ .catch(() => {})
3409
+ .finally(() => { this._scheduleBusy = false })
3410
+ }
3411
+ }
3412
+ }
3362
3413
  } catch (e) {}
3363
3414
  }
3364
3415
 
@@ -4144,6 +4195,30 @@ class ExternalMemory {
4144
4195
  return out.slice(0, limit)
4145
4196
  }
4146
4197
 
4198
+ /** 递归收集某目录下的 markdown 记忆文件(按 mtime 取最新 N 个)——ZCode/TRAE 等 2026-09-08 新增。 */
4199
+ async listMdFiles(rootDir, limit = 12, maxDepth = 4) {
4200
+ const out = []
4201
+ const walk = async (dir, depth) => {
4202
+ if (depth > maxDepth || out.length >= limit * 3) return
4203
+ let entries
4204
+ try { entries = await readdir(dir, { withFileTypes: true }) } catch (e) { return }
4205
+ for (const e of entries) {
4206
+ if (out.length >= limit * 3) return
4207
+ const full = path.join(dir, e.name)
4208
+ if (e.isDirectory()) await walk(full, depth + 1)
4209
+ else if (e.isFile() && e.name.toLowerCase().endsWith('.md')) {
4210
+ try {
4211
+ const info = await stat(full)
4212
+ out.push({ path: full, size: info.size, mtime: info.mtimeMs })
4213
+ } catch (err) {}
4214
+ }
4215
+ }
4216
+ }
4217
+ await walk(rootDir, 0)
4218
+ out.sort((a, b) => b.mtime - a.mtime)
4219
+ return out.slice(0, limit)
4220
+ }
4221
+
4147
4222
  /** 从单个 jsonl 会话文件提取可检索文本(行数/字节上限,防重)。 */
4148
4223
  async extractSessionText(file, maxLines = 400, maxChars = 60000) {
4149
4224
  let text = ''
@@ -4200,7 +4275,12 @@ class ExternalMemory {
4200
4275
  }
4201
4276
  const pushSessions = async (id, name, tool, rootDir) => {
4202
4277
  if (!this.enabled(id)) return
4203
- const files = await this.listSessionFiles(rootDir)
4278
+ // 2026-09-08:支持候选目录数组(如 Kimi Code 的 .kimi / .kimi-code 变体),存在且非空的才收集
4279
+ const roots = Array.isArray(rootDir) ? rootDir : [rootDir]
4280
+ let files = []
4281
+ for (const r of roots) files = files.concat(await this.listSessionFiles(r))
4282
+ files.sort((a, b) => b.mtime - a.mtime)
4283
+ files = files.slice(0, 20)
4204
4284
  if (!files.length) return
4205
4285
  srcs.push({
4206
4286
  id, name, tool, kind: 'sessions', files,
@@ -4227,6 +4307,22 @@ class ExternalMemory {
4227
4307
  await pushSessions('workbuddy-sessions', 'WorkBuddy 历史会话', 'WorkBuddy', path.join(home, '.workbuddy', 'projects'))
4228
4308
  await pushSessions('claude-sessions', 'Claude Code 历史会话', 'Claude Code', path.join(home, '.claude', 'projects'))
4229
4309
  await pushSessions('codex-sessions', 'Codex 历史会话', 'Codex', path.join(home, '.codex', 'sessions'))
4310
+ // —— 2026-09-08 新增:ZCode / Kimi Code / TRAE(自动扫描,目录存在即出源;链接模式,只注入路径不注内容) ——
4311
+ const zcodeMds = await this.listMdFiles(path.join(home, '.zcode', 'cli', 'memories', 'projects'), 12)
4312
+ await pushMd('zcode-memory', 'ZCode 项目记忆', 'ZCode', 'project', zcodeMds.map((f) => f.path))
4313
+ await pushSessions('zcode-sessions', 'ZCode 历史会话', 'ZCode', path.join(home, '.zcode', 'cli', 'rollout'))
4314
+ await pushMd('kimi-global', 'Kimi Code 全局记忆', 'Kimi Code', 'user', [
4315
+ path.join(home, '.kimi', 'MEMORY.md'), path.join(home, '.kimi-code', 'MEMORY.md'),
4316
+ path.join(home, '.kimi-code', 'memory', 'MEMORY.md'), path.join(home, '.kimicode', 'MEMORY.md'),
4317
+ ])
4318
+ await pushSessions('kimi-sessions', 'Kimi Code 历史会话', 'Kimi Code', [
4319
+ path.join(home, '.kimi', 'sessions'), path.join(home, '.kimi-code', 'sessions'),
4320
+ ])
4321
+ const traeRules = [
4322
+ ...(await globOne(path.join(ws, '.trae', 'rules'), /\.(mdc|md)$/, 10)),
4323
+ ...(await this.listMdFiles(path.join(home, '.trae', 'rules'), 10, 2)).map((f) => f.path),
4324
+ ]
4325
+ await pushMd('trae-rules', 'TRAE 规则', 'TRAE', 'project', traeRules)
4230
4326
 
4231
4327
  this.cache = srcs
4232
4328
  this.cachedAt = Date.now()
@@ -5448,14 +5544,14 @@ export function apply(ctx, config) {
5448
5544
  text: { type: 'string', required: true, description: '完整反思内容:成果回顾 / 教训改进 / 今日可延续要点。' },
5449
5545
  }, async (args, exec) => engine.saveReflection(args.date, args.text, exec.agent)),
5450
5546
 
5451
- defineTool('memory_external', '查看/接入其他 AI 工具(AI 助手/CodeBuddy/Claude Code/Codex/项目约定文件)的记忆。action=list 列出全部检测到的外部记忆源(路径/大小/预览/会话数);action=import 以纯链接模式接入(source 为源 id,target=project 接进项目笔记 / user 接进用户级记忆,只记录源文件路径指针、不写入内容,需要时按需读取;防止外部脏内容混入本地记忆)。首次在新工作区工作、或用户提到其他软件里做过的事时调用。', {
5547
+ defineTool('memory_external', '查看/接入其他 AI 工具(AI 助手/CodeBuddy/Claude Code/Codex/ZCode/Kimi Code/TRAE/项目约定文件)的记忆。action=list 列出全部检测到的外部记忆源(路径/大小/预览/会话数);action=import 以纯链接模式接入(source 为源 id,target=project 接进项目笔记 / user 接进用户级记忆,只记录源文件路径指针、不写入内容,需要时按需读取;防止外部脏内容混入本地记忆)。首次在新工作区工作、或用户提到其他软件里做过的事时调用。', {
5452
5548
  action: { type: 'string', enum: ['list', 'import'], required: true, description: 'list=列出外部记忆源; import=接入指定源。' },
5453
5549
  source: { type: 'string', description: '要接入的源 id(action=import 时必填,来自 list 结果)。' },
5454
5550
  target: { type: 'string', enum: ['project', 'user'], description: '接入目标: project=项目笔记(默认), user=用户级记忆。' },
5455
5551
  }, async (args, exec) => {
5456
5552
  if (args.action === 'list') {
5457
5553
  const list = await engine.external.summarize()
5458
- if (!list.length) return '未检测到其他 AI 工具的记忆文件(可检查 ~/.workbuddy、~/.codebuddy、~/.claude、~/.codex 是否存在)。'
5554
+ if (!list.length) return '未检测到其他 AI 工具的记忆文件(可检查 ~/.workbuddy、~/.codebuddy、~/.claude、~/.codex、~/.zcode、~/.kimi 或 ~/.kimi-code、~/.trae 是否存在)。'
5459
5555
  const lines = []
5460
5556
  lines.push('检测到 ' + list.length + ' 个外部记忆源:')
5461
5557
  for (const s of list) {
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": "2.1.9",
4
+ "version": "2.2.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {