@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 +9 -9
- package/lib/client.js +105 -4
- package/lib/index.js +81 -1
- package/package.json +46 -46
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: '@
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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.
|
|
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
|
+
}
|