@a9i5k4/dsh-auto-memory 0.1.17 → 0.1.19

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/cordis.patch.yml CHANGED
@@ -1,9 +1,9 @@
1
- # dsh-auto-memory bundle patch: inserts the dual-face plugin row into the web
2
- # profile roster. Applied as a profile bundle layer (the `dsh.bundle.patch`
3
- # manifest field). The node half (exports ".") runs in the host process
4
- # (memory engine, /api/dsh-auto-memory routes, agent tools, prompt injection);
5
- # the `dsh.client` declaration in package.json makes the browser half
6
- # (exports "./client", served at /plugins/<id>/client.js) load in the web GUI.
7
- - insert:
8
- - id: auto-memory
9
- name: '@a9i5k4/dsh-auto-memory'
1
+ # dsh-auto-memory bundle patch: inserts the dual-face plugin row into the web
2
+ # profile roster. Applied as a profile bundle layer (the `dsh.bundle.patch`
3
+ # manifest field). The node half (exports ".") runs in the host process
4
+ # (memory engine, /api/dsh-auto-memory routes, agent tools, prompt injection);
5
+ # the `dsh.client` declaration in package.json makes the browser half
6
+ # (exports "./client", served at /plugins/<id>/client.js) load in the web GUI.
7
+ - insert:
8
+ - id: auto-memory-dev
9
+ name: '@deepseek-ai/dsh-auto-memory'
package/lib/client.js CHANGED
@@ -115,7 +115,7 @@ window.__ModuleLoader__.load({
115
115
  kvAvail: '可用', kvUnavail: '不可用', kvDup: '项目笔记重复标题', kvDupCount: ' 个',
116
116
  kvMem: '记忆文件', kvMemUser: '用户', kvMemNotes: '笔记', kvMemLog: '日志', kvMemMissing: '缺失',
117
117
  kvWs: '当前工作区', kvWsUnknown: '(未取到)', kvApi: 'API 连通', unknown: '(未知)',
118
- guideTitle: '欢迎使用 dsh-auto-memory', guideSub: '这个插件能干什么:',
118
+ guideTitle: '欢迎使用 dsh-auto-memory(开发版)', guideSub: '这个插件能干什么:',
119
119
  gFeat1: '三层记忆自动注入与检索:用户级 / 项目笔记 / 每日日志,每轮对话自动读、自动写',
120
120
  gFeat2: '每轮对话结束自动沉淀:修了什么 bug、做了哪些决策,自动按主题写进今日日志',
121
121
  gFeat3: 'AI 时段问候与三级抽屉:打开「记忆」面板,AI 按时段向你汇报当天进展',
@@ -171,6 +171,10 @@ window.__ModuleLoader__.load({
171
171
  fInject: '注入记忆上下文', fInjectHint: '每次组装提示词时自动注入 <memory_system> 块。',
172
172
  fBudget: '注入预算(字符)', fBudgetHint: '记忆块总预算,超出部分截断。',
173
173
  fDays: '注入最近日志天数', fDaysHint: '会话开始时注入最近 N 天的工作日志尾部。',
174
+ fAway: '暂离阈值(分钟)', fAwayHint: '距上次活动超过该值视为暂离,回归时自动弹出记忆窗口。默认 60。',
175
+ fAutoSum: '自动总结时间点(HH:MM,逗号分隔)', fAutoSumHint: '到点自动生成本时段总结并弹窗展示,如 12:00,18:00,22:00。空=关闭。',
176
+ awayTitle: '欢迎回来', awayMsg: '你离开的这段时间,我已经帮你把日志整理好了。记忆窗口已打开,可以看看这段时间的状况。',
177
+ sumTitle: '时段总结',
174
178
  fReflect: '每日反思', fReflectHint: '昨天有工作日志时,会话首轮主动呈现昨日反思。',
175
179
  fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
176
180
  fLocale: '界面语言', fLocaleHint: '默认跟随 DSH 系统语言;也可手动指定中文 / English。', followSystem: '跟随系统语言',
@@ -205,7 +209,7 @@ window.__ModuleLoader__.load({
205
209
  kvAvail: 'available', kvUnavail: 'unavailable', kvDup: 'Duplicate note headings', kvDupCount: '',
206
210
  kvMem: 'Memory files', kvMemUser: 'user', kvMemNotes: 'notes', kvMemLog: 'log', kvMemMissing: 'missing',
207
211
  kvWs: 'Current workspace', kvWsUnknown: '(unknown)', kvApi: 'API probes', unknown: '(unknown)',
208
- guideTitle: 'Welcome to dsh-auto-memory', guideSub: 'What this plugin does:',
212
+ guideTitle: 'Welcome to dsh-auto-memory (dev)', guideSub: 'What this plugin does:',
209
213
  gFeat1: 'Three-layer memory with automatic injection & retrieval: user / project notes / daily logs',
210
214
  gFeat2: 'Auto-consolidation after every turn: bugs fixed and decisions made are written into today\'s log automatically',
211
215
  gFeat3: 'AI period greetings with three-level drawers: open the Memory panel for a period-by-period digest',
@@ -261,6 +265,10 @@ window.__ModuleLoader__.load({
261
265
  fInject: 'Inject memory context', fInjectHint: 'Auto-inject <memory_system> block into every prompt.',
262
266
  fBudget: 'Injection budget (chars)', fBudgetHint: 'Total budget for the memory block; excess is truncated.',
263
267
  fDays: 'Recent days injected', fDaysHint: 'Inject tails of the last N days of work logs at session start.',
268
+ fAway: 'Away threshold (minutes)', fAwayHint: 'Marked away when inactive longer than this; the memory panel auto-opens on return. Default 60.',
269
+ fAutoSum: 'Auto summary times (HH:MM, comma-separated)', fAutoSumHint: 'Auto-generate and show a period summary at these times, e.g. 12:00,18:00,22:00. Empty = off.',
270
+ awayTitle: 'Welcome back', awayMsg: 'While you were away, I tidied up your logs. The memory panel is open — take a look at what happened.',
271
+ sumTitle: 'Period summary',
264
272
  fReflect: 'Daily reflection', fReflectHint: 'When yesterday has logs, the agent presents the reflection at session start.',
265
273
  fStyle: 'Reflection style', fStyleHint: 'Life-style / Professional / Auto.',
266
274
  fLocale: 'UI language', fLocaleHint: 'Follows the DSH system language by default; you can also pin Chinese / English.', followSystem: 'Follow system language',
@@ -304,8 +312,11 @@ window.__ModuleLoader__.load({
304
312
  function t(key) { return (I18N[locale] && I18N[locale][key]) || I18N.zh[key] || key }
305
313
  function setLocale(l) { if (l !== 'zh' && l !== 'en') return; if (l === locale) return; locale = l; localeListeners.forEach(function (fn) { try { fn(l) } catch (e) {} }) }
306
314
  function onLocale(fn) { localeListeners.add(fn); return function () { localeListeners.delete(fn) } }
307
- // 暂离检测:距离上次关闭面板超过 1 小时视为暂离回来
315
+ // 暂离检测:优先用 host 定时检测的 away 状态(阈值可配 awayMinutes),回退本地 lastActive 旧逻辑
316
+ var hostAway = false
317
+ var hostAwayReady = false
308
318
  function isAway() {
319
+ if (hostAwayReady) return hostAway
309
320
  var lastSeen = 0
310
321
  try { lastSeen = Number(localStorage.getItem('dsh-auto-memory.lastActive') || 0) } catch (e) {}
311
322
  return lastSeen > 0 && (Date.now() - lastSeen) > 3600000
@@ -313,6 +324,36 @@ window.__ModuleLoader__.load({
313
324
 
314
325
  // ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
315
326
  var CHANGELOG = {
327
+ '0.1.19': { zh: [
328
+ '稳定版:整合 0.1.16~0.1.19 全部修复与优化',
329
+ '核心:上下文缓存策略重写——记忆注入迁至运行时上下文快照,系统提示词保持稳定,DeepSeek 前缀缓存全程命中,不再白白消耗 token(命中率恢复 95%+)',
330
+ '新增:时间检测——暂离阈值可配置(回归自动弹出记忆窗口并欢迎),自动总结时间点(到点自动生成时段总结并弹窗展示),自动沉淀定时兜底恢复',
331
+ '新增:动态通知中心——发布者重要提醒(重大 bug/升级建议)自动推送,无需等待发版',
332
+ '修复:自动沉淀在重启恢复会话后失效(agent 引用定时恢复,双保险)',
333
+ '提示:pnpm v11 默认限制安装发布不足 1 天的版本,想立即获取新版请在 pnpm-workspace.yaml 设 minimumReleaseAge: 0 或使用显式版本号',
334
+ ], en: [
335
+ 'Stable release: consolidates 0.1.16~0.1.19 fixes and polish',
336
+ 'Core: context-cache strategy rewrite — memory injection moved to runtime-context snapshot, system prompt stays byte-stable, DeepSeek prefix cache hits throughout, no more wasted tokens (hit rate back to 95%+)',
337
+ 'New: time detection — configurable away threshold (auto-open memory panel with welcome on return), auto summary times (auto-generate period summary popup), timed fallback recovery for auto-consolidation',
338
+ 'New: dynamic notice center — publisher alerts (major bugs / upgrade advice) pushed automatically, no need to wait for a release',
339
+ 'Fix: auto-consolidation failing after restart-resumed sessions (agent reference restored by timer, double insurance)',
340
+ 'Note: pnpm v11 blocks packages published <1 day ago by default; set minimumReleaseAge: 0 in pnpm-workspace.yaml or use an explicit version for the latest immediately',
341
+ ] },
342
+ '0.1.18': { zh: [
343
+ '稳定版:整合 0.1.13~0.1.17 全部修复与优化',
344
+ '核心:上下文缓存策略重写——记忆注入迁至运行时上下文快照,系统提示词保持稳定,DeepSeek 前缀缓存全程命中,不再白白消耗 token(命中率恢复 95%+)',
345
+ '修复:秒级时间戳击穿前缀缓存(改日期级)',
346
+ '修复:npm 安装后客户端面板无法加载(bundle 注册名)',
347
+ '新增:更新说明弹窗/首次安装指导/暂离回归自动打开记忆窗口/动态通知中心',
348
+ '提示:pnpm v11 默认限制安装发布不足 1 天的版本,想立即获取新版请在 pnpm-workspace.yaml 设 minimumReleaseAge: 0 或使用显式版本号',
349
+ ], en: [
350
+ 'Stable release: consolidates 0.1.13~0.1.17 fixes and polish',
351
+ 'Core: context-cache strategy rewrite — memory injection moved to runtime-context snapshot, system prompt stays byte-stable, DeepSeek prefix cache hits throughout, no more wasted tokens (hit rate back to 95%+)',
352
+ 'Fix: second-level timestamp breaking prefix cache (now date-level)',
353
+ 'Fix: client panel not loading after npm install (bundle registration id)',
354
+ 'New: update dialog / first-run guide / memory panel auto-opens after >1h away / dynamic notice center',
355
+ 'Note: pnpm v11 blocks packages published <1 day ago by default; set minimumReleaseAge: 0 in pnpm-workspace.yaml or use an explicit version for the latest immediately',
356
+ ] },
316
357
  '0.1.17': { zh: [
317
358
  '新增:动态通知中心——插件自动拉取发布者的重要提醒(重大 bug/升级建议),特定时间窗口内显示,无需等待发版即可收到推送',
318
359
  '优化:0.1.14 及以下旧版本已全部弃用(缓存击穿浪费 token),升级到 0.1.16+ 彻底解决',
@@ -1334,6 +1375,26 @@ window.__ModuleLoader__.load({
1334
1375
  closeDialog()
1335
1376
  } }, t('gotIt')))))
1336
1377
  }
1378
+ if (dialogState.kind === 'welcomeBack') {
1379
+ return h('div', { style: overlay },
1380
+ h('div', { style: box },
1381
+ h('div', { style: head }, t('awayTitle')),
1382
+ h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.6, opacity: .92, marginTop: '4px' } }, t('awayMsg')),
1383
+ h('div', { style: { display: 'flex', justifyContent: 'flex-end', marginTop: '6px' } },
1384
+ h('button', { 'data-dam-btn': '', style: close, onClick: closeDialog }, t('gotIt')))))
1385
+ }
1386
+ if (dialogState.kind === 'summary') {
1387
+ var sum = dialogState.summary || {}
1388
+ return h('div', { style: overlay },
1389
+ h('div', { style: box },
1390
+ h('div', { style: head }, t('sumTitle') + ' ' + (sum.time || '')),
1391
+ h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.6, opacity: .92, whiteSpace: 'pre-wrap', marginTop: '4px' } }, sum.summary || ''),
1392
+ (sum.works || []).slice(0, 6).map(function (w) {
1393
+ return h('div', { style: item }, h('span', { style: dot }), w.title || '')
1394
+ }),
1395
+ h('div', { style: { display: 'flex', justifyContent: 'flex-end', marginTop: '6px' } },
1396
+ h('button', { 'data-dam-btn': '', style: close, onClick: closeDialog }, t('gotIt')))))
1397
+ }
1337
1398
  var versions = dialogState.versions || []
1338
1399
  var lastV = versions.length ? versions[versions.length - 1].version : ''
1339
1400
  return h('div', { style: overlay },
@@ -1480,6 +1541,8 @@ window.__ModuleLoader__.load({
1480
1541
  field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
1481
1542
  field(t('fBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 400, value: cfg.injectBudgetChars, onChange: function (e) { set('injectBudgetChars', Number(e.target.value) || 2400) } }), t('fBudgetHint')),
1482
1543
  field(t('fDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 14, value: cfg.recentDaysInjected, onChange: function (e) { set('recentDaysInjected', Number(e.target.value) || 3) } }), t('fDaysHint')),
1544
+ field(t('fAway'), h('input', { 'data-dam-input': '', type: 'number', min: 1, value: cfg.awayMinutes || 60, onChange: function (e) { set('awayMinutes', Number(e.target.value) || 60) } }), t('fAwayHint')),
1545
+ 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')),
1483
1546
  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')),
1484
1547
  field(t('fReflect'), h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), t('fReflectHint')),
1485
1548
  field(t('fStyle'), h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
@@ -1541,9 +1604,21 @@ window.__ModuleLoader__.load({
1541
1604
  })
1542
1605
  // 语言切换时通知所有订阅者重渲染
1543
1606
  onLocale(function () { emit() })
1544
- // 暂离回来自动弹开记忆窗口:距离上次关闭面板超过 1 小时,页面加载或切回标签页时自动打开(欢迎语在 GreetingCard 展示)
1607
+ // 暂离回来自动弹开记忆窗口:host 判定暂离(阈值可配 awayMinutes),页面加载/切回标签页/轮询发现回归时自动打开
1608
+ // 回归检测:host away 状态从 true → false(暂离结束)时打开并欢迎
1609
+ var prevAwayState = null
1545
1610
  function autoOpenOnReturn() {
1546
1611
  try {
1612
+ if (hostAwayReady && prevAwayState === true && hostAway === false) {
1613
+ // 暂离回归:打开窗口 + 欢迎弹窗(若有待展示总结一并显示)
1614
+ if (!controller.isOpen()) controller.open()
1615
+ var pend = lastPendingSummary
1616
+ if (pend) { openDialog({ kind: 'summary', summary: pend }); lastPendingSummary = null }
1617
+ else if (!document.hidden) openDialog({ kind: 'welcomeBack' })
1618
+ prevAwayState = hostAway
1619
+ return
1620
+ }
1621
+ prevAwayState = hostAwayReady ? hostAway : prevAwayState
1547
1622
  if (isAway() && !controller.isOpen()) controller.open()
1548
1623
  } catch (e) {}
1549
1624
  }
@@ -1589,6 +1664,32 @@ window.__ModuleLoader__.load({
1589
1664
  checkNotices()
1590
1665
  var noticesTimer = setInterval(checkNotices, 3600 * 1000)
1591
1666
 
1667
+ // 时间检测轮询(30s):拉 host 的暂离状态/待展示总结,检测暂离回归并自动弹窗
1668
+ var lastPendingSummary = null
1669
+ var awayPollTimer = null
1670
+ function pollTimeState() {
1671
+ apiGet(API.state).then(function (d) {
1672
+ try {
1673
+ if (d && typeof d.away === 'boolean') {
1674
+ hostAway = d.away
1675
+ hostAwayReady = true
1676
+ }
1677
+ if (d && d.pendingSummary && d.pendingSummary.summary) {
1678
+ var seenKey = 'dsh-auto-memory.seenSummary.' + (d.pendingSummary.date || '') + '.' + (d.pendingSummary.time || '')
1679
+ try {
1680
+ if (!localStorage.getItem(seenKey)) {
1681
+ localStorage.setItem(seenKey, '1')
1682
+ lastPendingSummary = d.pendingSummary
1683
+ }
1684
+ } catch (e2) {}
1685
+ }
1686
+ autoOpenOnReturn()
1687
+ } catch (e) {}
1688
+ }).catch(function () {})
1689
+ }
1690
+ pollTimeState()
1691
+ awayPollTimer = setInterval(pollTimeState, 30000)
1692
+
1592
1693
  var surfaceDisposers = []
1593
1694
  function registerSurfaces() {
1594
1695
  try {
package/lib/index.js CHANGED
@@ -83,6 +83,10 @@ const DEFAULT_CONFIG = {
83
83
  autoConsolidate: true,
84
84
  /** 自动沉淀内容门槛:本轮 user+assistant 文本总字符数低于此值视为寒暄,跳过。 */
85
85
  autoConsolidateMinChars: 60,
86
+ /** 暂离阈值(分钟):距上次活动超过该值视为暂离,回归时自动弹出记忆窗口并欢迎。默认 60。 */
87
+ awayMinutes: 60,
88
+ /** 自动总结时间点(24h "HH:MM" 数组,如 ["12:00","18:00","22:00"]):到点自动生成本时段总结并弹窗展示。空数组=关闭。 */
89
+ autoSummaryTimes: [],
86
90
  /** 日界(分钟,从 0 点起算):凌晨在此之前的活儿归前一天日志;默认 450=早上 7:30 后才进入新一天。 */
87
91
  dayBoundaryMinutes: 450,
88
92
  /** 是否启用每日反思。 */
@@ -146,6 +150,7 @@ class MemoryEngine {
146
150
  reflectionShownSession: undefined,
147
151
  todayGreeting: '', greetingShownSession: undefined,
148
152
  calendarText: '', calendarPath: undefined,
153
+ away: false, pendingSummary: undefined, // 时间检测:暂离标记 / 待展示的自动时段总结
149
154
  loadedAt: 0, loading: undefined, configLoaded: false,
150
155
  }
151
156
  this._configPath = path.join(dshHome(), 'dsh-auto-memory.json')
@@ -1385,6 +1390,70 @@ class MemoryEngine {
1385
1390
  return result
1386
1391
  }
1387
1392
 
1393
+ // ---------- 时间检测(每 15s 心跳 tick):暂离状态 / _lastAgent 定时兜底 / 自动总结时间点 ----------
1394
+ tickTime() {
1395
+ try {
1396
+ // 1) 暂离检测:距上次活动(lastActive)超过 awayMinutes 视为暂离
1397
+ const awayMs = Math.max(Number(this.config.awayMinutes) || 60, 1) * 60000
1398
+ this.state.away = !!(this._lastActiveAt && Date.now() - this._lastActiveAt > awayMs)
1399
+ // 2) _lastAgent 定时兜底(重启恢复会话不触发 session-start;pre-step 兜底之外的第二保险)
1400
+ if (!this._lastAgent) this.restoreLastAgent()
1401
+ // 3) 自动总结时间点:命中配置的 HH:MM 且当天未生成过 → 生成时段总结并标记展示
1402
+ const times = Array.isArray(this.config.autoSummaryTimes) ? this.config.autoSummaryTimes : []
1403
+ if (times.length) {
1404
+ const now = new Date()
1405
+ const hhmm = String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0')
1406
+ const today = this.memToday()
1407
+ for (const t of times) {
1408
+ if (hhmm === t) {
1409
+ const key = today + '|' + t
1410
+ if (this._summaryDone !== key) {
1411
+ this._summaryDone = key
1412
+ void this.triggerAutoSummary(t)
1413
+ }
1414
+ }
1415
+ }
1416
+ }
1417
+ } catch (e) {}
1418
+ }
1419
+
1420
+ /** 从 sessions/agent 服务恢复 _lastAgent(定时兜底)。 */
1421
+ restoreLastAgent() {
1422
+ try {
1423
+ const sessionsSvc = this._sessionsSvc
1424
+ const agentSvc = this._agentSvc
1425
+ if (!sessionsSvc || !agentSvc || typeof sessionsSvc.list !== 'function') return
1426
+ const sessions = sessionsSvc.list()
1427
+ if (!sessions || !sessions.length) return
1428
+ // 找最近活跃的顶层会话(log 最后事件时间最大)
1429
+ let best = null, bestTime = 0
1430
+ for (const s of sessions) {
1431
+ let t = 0
1432
+ try { const last = s.log && s.log[s.log.length - 1]; if (last && last.time) t = last.time } catch (e) {}
1433
+ if (t >= bestTime) { bestTime = t; best = s }
1434
+ }
1435
+ if (!best || typeof agentSvc.get !== 'function') return
1436
+ const a = agentSvc.get(best.id)
1437
+ if (a && a.session && a.session.header && a.session.header.parentSession === undefined) this._lastAgent = a
1438
+ } catch (e) {}
1439
+ }
1440
+
1441
+ /** 自动总结:按时间点推断时段,生成总结并置 pendingSummary(供 client 弹窗)。 */
1442
+ async triggerAutoSummary(timePoint) {
1443
+ try {
1444
+ const h = Number(String(timePoint).slice(0, 2)) || 0
1445
+ const period = h < 6 ? 'morning' : h < 9 ? 'morning' : h < 12 ? 'forenoon' : h < 14 ? 'noon' : h < 18 ? 'afternoon' : 'evening'
1446
+ const out = await this.summarizePeriod(period, undefined, false)
1447
+ if (out && out.summary) {
1448
+ this.state.pendingSummary = {
1449
+ time: timePoint, date: this.memToday(), period,
1450
+ summary: out.summary, works: out.works || [], generatedAt: out.generatedAt || Date.now(),
1451
+ }
1452
+ console.log('[dsh-auto-memory] auto summary triggered at ' + timePoint + ' (period ' + period + ')')
1453
+ }
1454
+ } catch (e) {}
1455
+ }
1456
+
1388
1457
  /** 今日 AI 问候(按时段,每天每时段生成一次并缓存到 greetings/{date}.json)。 */
1389
1458
  async greetToday(agent) {
1390
1459
  const hour = new Date().getHours()
@@ -1816,6 +1885,11 @@ class MemoryEngine {
1816
1885
  todayEntries,
1817
1886
  latestReflectionDate: this.state.latestReflectionDate,
1818
1887
  pendingReflection: this.state.pendingReflection ? this.state.pendingReflection.date : undefined,
1888
+ // 时间检测:暂离状态 / 待展示的自动总结 / 相关配置
1889
+ away: !!this.state.away,
1890
+ awayMinutes: Math.max(Number(this.config.awayMinutes) || 60, 1),
1891
+ autoSummaryTimes: Array.isArray(this.config.autoSummaryTimes) ? this.config.autoSummaryTimes : [],
1892
+ pendingSummary: this.state.pendingSummary || null,
1819
1893
  autoStats: this.autoStats,
1820
1894
  greeting: this.greetingData(),
1821
1895
  calendar: this.parseCalendar(this.state.calendarText),
@@ -2205,6 +2279,9 @@ export function apply(ctx, config) {
2205
2279
  const sessionQuery = ctx.get('sessionQuery')
2206
2280
  engine._sessionQuery = sessionQuery
2207
2281
  engine._subagents = ctx.get('subagents') || undefined
2282
+ // 时间检测用的服务(软获取,缺失时定时兜底自动降级)
2283
+ engine._sessionsSvc = ctx.get('sessions') || undefined
2284
+ engine._agentSvc = ctx.get('agent') || undefined
2208
2285
 
2209
2286
  // 生命周期刷新
2210
2287
  const refreshAll = (agent) => { void engine.refresh(agent) }
@@ -2237,6 +2314,9 @@ export function apply(ctx, config) {
2237
2314
  ctx.on('agent/pre-step', async (payload, next) => {
2238
2315
  try {
2239
2316
  const agent = (payload && payload.agent) || engine._lastAgent
2317
+ // 兜底恢复 _lastAgent(仅启动后首次为空时设置一次,不每步覆盖):重启后恢复的会话不触发 session-start,
2318
+ // 导致 turn-stopping 自动沉淀拿不到 agent 而永久失效;首次 pre-step 即恢复,之后由 session-start 正常维护
2319
+ try { if (!engine._lastAgent && agent && agent.session && agent.session.header && agent.session.header.parentSession === undefined) engine._lastAgent = agent } catch (e) {}
2240
2320
  // 只刷新顶层会话:子代理(自动沉淀/固化的 subagent)session 无 cwd,刷新会把 state 切到错误工作区
2241
2321
  let skip = false
2242
2322
  try { if (agent && agent.session && agent.session.header && agent.session.header.parentSession) skip = true } catch (e) {}
@@ -2786,7 +2866,7 @@ export function apply(ctx, config) {
2786
2866
  })()
2787
2867
  }, 5 * 60 * 1000)
2788
2868
  // 心跳独立 15 秒一次(对齐 polling-lease 心跳节奏),证明轮询机制活着
2789
- const heartbeatTimer = setInterval(() => { void writeHeartbeat() }, 15000)
2869
+ const heartbeatTimer = setInterval(() => { void writeHeartbeat(); try { engine.tickTime() } catch (e) {} }, 15000)
2790
2870
  void writeHeartbeat() // 立即心跳一次:重启后马上可见轮询存活
2791
2871
 
2792
2872
  // ---------- 注册与清理 ----------
package/package.json CHANGED
@@ -1,46 +1,46 @@
1
- {
2
- "name": "@a9i5k4/dsh-auto-memory",
3
- "description": "DSH 自动记忆插件:三层记忆(用户级/项目笔记/每日日志)自动注入与检索、每轮对话自动沉淀、每日反思、可视化面板与设置页,支持继承其他 AI 工具的记忆。",
4
- "version": "0.1.17",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "exports": {
8
- ".": "./lib/index.js",
9
- "./client": "./lib/client.js",
10
- "./package.json": "./package.json"
11
- },
12
- "files": [
13
- "lib",
14
- "cordis.patch.yml"
15
- ],
16
- "dsh": {
17
- "bundle": {
18
- "patch": "./cordis.patch.yml"
19
- },
20
- "client": {
21
- "inject": [
22
- "@deepseek-ai/dsh-client-runtime",
23
- "@deepseek-ai/dsh-client-connection",
24
- "@deepseek-ai/dsh-client-ui-slots",
25
- "@deepseek-ai/dsh-client-ui-settings",
26
- "@deepseek-ai/dsh-client-ui-sidebar"
27
- ],
28
- "platform": "web"
29
- }
30
- },
31
- "keywords": [
32
- "dsh",
33
- "deepseek-harness",
34
- "memory",
35
- "plugin",
36
- "auto-memory"
37
- ],
38
- "repository": {
39
- "type": "git",
40
- "url": "git+https://github.com/Aik358/dsh-auto-memory.git"
41
- },
42
- "peerDependencies": {
43
- "@deepseek-ai/cordis": "^4.0.1"
44
- },
45
- "license": "BSD-3-Clause"
46
- }
1
+ {
2
+ "name": "@a9i5k4/dsh-auto-memory",
3
+ "description": "DSH 自动记忆插件:三层记忆(用户级/项目笔记/每日日志)自动注入与检索、每轮对话自动沉淀、每日反思、可视化面板与设置页,支持继承其他 AI 工具的记忆。",
4
+ "version": "0.1.19",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./client": "./lib/client.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "lib",
14
+ "cordis.patch.yml"
15
+ ],
16
+ "dsh": {
17
+ "bundle": {
18
+ "patch": "./cordis.patch.yml"
19
+ },
20
+ "client": {
21
+ "inject": [
22
+ "@deepseek-ai/dsh-client-runtime",
23
+ "@deepseek-ai/dsh-client-connection",
24
+ "@deepseek-ai/dsh-client-ui-slots",
25
+ "@deepseek-ai/dsh-client-ui-settings",
26
+ "@deepseek-ai/dsh-client-ui-sidebar"
27
+ ],
28
+ "platform": "web"
29
+ }
30
+ },
31
+ "keywords": [
32
+ "dsh",
33
+ "deepseek-harness",
34
+ "memory",
35
+ "plugin",
36
+ "auto-memory"
37
+ ],
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/Aik358/dsh-auto-memory.git"
41
+ },
42
+ "peerDependencies": {
43
+ "@deepseek-ai/cordis": "^4.0.1"
44
+ },
45
+ "license": "BSD-3-Clause"
46
+ }