@a9i5k4/dsh-auto-memory 0.1.16 → 0.1.17

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/lib/client.js CHANGED
@@ -123,7 +123,7 @@ window.__ModuleLoader__.load({
123
123
  gFeat5: '接续其他 AI 工具的记忆:CodeBuddy / Claude Code / Codex 等的历史记忆一键接入',
124
124
  gFeat6: '自动检查更新:启动时检测新版本,设置页可一键更新',
125
125
  guideTip: '打开侧边栏「记忆」按钮即可使用;所有记忆都是本地明文文件,可随时查看修改。',
126
- updateTitle: 'dsh-auto-memory 已更新', updateSub: '本次更新内容:', gotIt: '知道了',
126
+ updateTitle: 'dsh-auto-memory 已更新', updateSub: '本次更新内容:', gotIt: '知道了', noticeOpen: '了解更多',
127
127
  refresh: '刷新', refreshing: '刷新中…', close: '关闭', dragMove: '拖动移动', dragResize: '拖动调整大小', resetPos: '恢复默认位置',
128
128
  generated: '已生成', failed: '失败: ',
129
129
  generatedAt: '生成于 ',
@@ -213,7 +213,7 @@ window.__ModuleLoader__.load({
213
213
  gFeat5: 'Inherit memories from other AI tools: CodeBuddy / Claude Code / Codex etc. in one click',
214
214
  gFeat6: 'Auto update check at boot, one-click update in settings',
215
215
  guideTip: 'Click the Memory button in the sidebar to start; all memories are plain local Markdown files you can inspect anytime.',
216
- updateTitle: 'dsh-auto-memory Updated', updateSub: 'What\'s new in this update:', gotIt: 'Got it',
216
+ updateTitle: 'dsh-auto-memory Updated', updateSub: 'What\'s new in this update:', gotIt: 'Got it', noticeOpen: 'Learn more',
217
217
  refresh: 'Refresh', refreshing: 'Refreshing…', close: 'Close', dragMove: 'Drag to move', dragResize: 'Drag to resize', resetPos: 'Reset position',
218
218
  generated: 'Generated', failed: 'Failed: ',
219
219
  generatedAt: 'Generated ',
@@ -313,6 +313,13 @@ window.__ModuleLoader__.load({
313
313
 
314
314
  // ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
315
315
  var CHANGELOG = {
316
+ '0.1.17': { zh: [
317
+ '新增:动态通知中心——插件自动拉取发布者的重要提醒(重大 bug/升级建议),特定时间窗口内显示,无需等待发版即可收到推送',
318
+ '优化:0.1.14 及以下旧版本已全部弃用(缓存击穿浪费 token),升级到 0.1.16+ 彻底解决',
319
+ ], en: [
320
+ 'New: dynamic notice center — the plugin auto-fetches publisher alerts (major bugs / upgrade advice), shown within the configured time window, no need to wait for a release',
321
+ 'Polish: all versions below 0.1.14 are deprecated (cache-breaking token waste); upgrade to 0.1.16+ fixes it',
322
+ ] },
316
323
  '0.1.16': { zh: [
317
324
  '稳定版:整合今日 0.1.13~0.1.15 全部修复与优化',
318
325
  '核心:上下文缓存策略重写——记忆注入迁至运行时上下文快照,系统提示词保持稳定,DeepSeek 前缀缓存全程命中,不再白白消耗 token(命中率恢复 95%+)',
@@ -376,6 +383,7 @@ window.__ModuleLoader__.load({
376
383
  externalImport: '/api/dsh-auto-memory/external-import',
377
384
  summarize: '/api/dsh-auto-memory/summarize',
378
385
  greet: '/api/dsh-auto-memory/greet',
386
+ notices: '/api/dsh-auto-memory/notices',
379
387
  }
380
388
  function query(params) {
381
389
  var search = new URLSearchParams()
@@ -1303,6 +1311,29 @@ window.__ModuleLoader__.load({
1303
1311
  h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', opacity: .8, marginTop: '4px' } }, t('guideTip')),
1304
1312
  h('button', { 'data-dam-btn': '', style: close, onClick: closeDialog }, t('gotIt'))))
1305
1313
  }
1314
+ if (dialogState.kind === 'notice') {
1315
+ var n = dialogState.notice || {}
1316
+ var zh = locale === 'zh' || !n.titleEn
1317
+ var nTitle = zh ? (n.title || '') : (n.titleEn || n.title || '')
1318
+ var nMsg = zh ? (n.message || '') : (n.messageEn || n.message || '')
1319
+ var isUrgent = n.level === 'urgent'
1320
+ var accent = isUrgent ? 'var(--dsw-alias-danger, #e5534b)' : 'var(--dsw-alias-brand-primary, #4f7cff)'
1321
+ return h('div', { style: overlay },
1322
+ h('div', { style: box },
1323
+ h('div', { style: Object.assign({}, head, isUrgent ? { color: accent } : {}) }, nTitle),
1324
+ h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.6, opacity: .92, whiteSpace: 'pre-wrap', marginTop: '4px' } }, nMsg),
1325
+ h('div', { style: { display: 'flex', gap: '8px', justifyContent: 'flex-end', marginTop: '6px' } },
1326
+ n.link ? h('a', { href: n.link, target: '_blank', rel: 'noreferrer', style: Object.assign({}, close, { textDecoration: 'none' }) }, t('noticeOpen')) : null,
1327
+ h('button', { 'data-dam-btn': '', style: close, onClick: function () {
1328
+ try {
1329
+ var arr = []
1330
+ try { arr = JSON.parse(localStorage.getItem('dsh-auto-memory.seenNotices') || '[]') } catch (e3) {}
1331
+ if (n.id && arr.indexOf(n.id) < 0) arr.push(n.id)
1332
+ localStorage.setItem('dsh-auto-memory.seenNotices', JSON.stringify(arr))
1333
+ } catch (e3) {}
1334
+ closeDialog()
1335
+ } }, t('gotIt')))))
1336
+ }
1306
1337
  var versions = dialogState.versions || []
1307
1338
  var lastV = versions.length ? versions[versions.length - 1].version : ''
1308
1339
  return h('div', { style: overlay },
@@ -1539,6 +1570,25 @@ window.__ModuleLoader__.load({
1539
1570
  }
1540
1571
  }).catch(function () {})
1541
1572
 
1573
+ // 动态通知(发布者→用户:重大 bug 提醒,不依赖发版):启动检查 + 每小时刷新;按 id 去重,urgent 优先
1574
+ function checkNotices() {
1575
+ apiGet(API.notices).then(function (d) {
1576
+ try {
1577
+ if (!d || !Array.isArray(d.notices) || !d.notices.length) return
1578
+ var seen = []
1579
+ try { seen = JSON.parse(localStorage.getItem('dsh-auto-memory.seenNotices') || '[]') } catch (e2) {}
1580
+ var fresh = d.notices.filter(function (n) { return n && n.id && seen.indexOf(n.id) < 0 })
1581
+ if (!fresh.length) return
1582
+ var target = null
1583
+ for (var i = 0; i < fresh.length; i++) { if (fresh[i].level === 'urgent') { target = fresh[i]; break } }
1584
+ if (!target) target = fresh[0]
1585
+ openDialog({ kind: 'notice', notice: target })
1586
+ } catch (e) {}
1587
+ }).catch(function () {})
1588
+ }
1589
+ checkNotices()
1590
+ var noticesTimer = setInterval(checkNotices, 3600 * 1000)
1591
+
1542
1592
  var surfaceDisposers = []
1543
1593
  function registerSurfaces() {
1544
1594
  try {
package/lib/index.js CHANGED
@@ -35,6 +35,9 @@ export const inject = ['webServer', 'tools', 'systemPrompt', 'subagents']
35
35
  /** Prompt order of the memory section. 10000 = 末尾注入(紧跟用户消息,recency 最高,保证记忆纪律/自动沉淀说明被模型最后读到,遵循度更高)。 */
36
36
  const SECTION_ORDER = 10000
37
37
 
38
+ /** 动态通知源:仓库根目录 notices.json(发布者随时更新并 push,插件自动拉取,不依赖发版即可向用户推送重大提醒;GitHub raw CDN 数分钟内生效)。 */
39
+ const NOTICES_URL = 'https://raw.githubusercontent.com/Aik358/dsh-auto-memory/main/notices.json'
40
+
38
41
  /** Model-facing announcement (tools + engine). */
39
42
  export const GUIDANCE = '本机已安装 dsh-auto-memory 插件(集中式自动记忆 + 外部记忆继承):三层本地记忆(用户级 ~/.dsh/memory/MEMORY.md、项目笔记与每日日志 .dsh-memory/)+ 会话自动注入 + 每日反思 + 其他 AI 工具记忆接入。能力:memory_log 追加今日日志(append-only,完成实质性工作后必须调用);memory_note 更新项目笔记;memory_user 更新用户级规则;memory_recall 检索本地记忆 + 外部记忆(AI 助手/CodeBuddy/Claude Code/Codex 历史会话与画像)+ 历史 DSH 会话;memory_external 查看/接入外部记忆源;memory_maintain 归档 30 天前日志;memory_reflect 保存每日反思;memory_status 查看状态;memory_consolidate 让 AI 读日志发散提炼长期要点固化进笔记。自动沉淀:每轮对话结束插件自动评估本轮内容并写今日日志/升格长期记忆(寒暄轮跳过,可在设置关闭),无需你手动调 memory_log。主动性纪律:任务开始遇到不熟悉的代码/领域/历史决策时,先 memory_recall 检索本机全部 AI 工具历史,不凭空猜测;新工作区主动探索历史。限制:记忆文件为明文 Markdown;不存密钥除非用户明确要求;外部会话检索为关键词级(非语义);GUI 侧边栏「记忆」面板(含「接续」页签)与设置页可查看/配置/接入。用户提到「记忆 / 昨天做了什么 / 之前怎么做的 / 每日反思 / 接续 / 其他 AI 的记忆」时即指本插件,请据此协作。'
40
43
 
@@ -60,6 +63,7 @@ export const API = {
60
63
  calendar: '/api/dsh-auto-memory/calendar',
61
64
  summarize: '/api/dsh-auto-memory/summarize',
62
65
  greet: '/api/dsh-auto-memory/greet',
66
+ notices: '/api/dsh-auto-memory/notices',
63
67
  }
64
68
 
65
69
  const DEFAULT_CONFIG = {
@@ -109,6 +113,15 @@ const nowHm = () => { const d = new Date(); return `${pad(d.getHours())}:${pad(d
109
113
 
110
114
  const dateStrOf = (ts) => { const d = new Date(ts); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` }
111
115
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/
116
+ const cmpVersion = (a, b) => {
117
+ const pa = String(a || '').split('.').map(Number)
118
+ const pb = String(b || '').split('.').map(Number)
119
+ for (let i = 0; i < 3; i++) {
120
+ const x = pa[i] || 0, y = pb[i] || 0
121
+ if (x !== y) return x > y ? 1 : -1
122
+ }
123
+ return 0
124
+ }
112
125
  const truncateHead = (s, n) => (s && s.length > n) ? s.slice(0, n) + '\n…(截断,完整内容用 memory_recall 或 GUI 面板)' : (s || '')
113
126
  const truncateTail = (s, n) => (s && s.length > n) ? '…(截断,完整内容用 memory_recall 或 GUI 面板)\n' + s.slice(-n) : (s || '')
114
127
  const fmtBytes = (n) => (n >= 1024 * 1024 ? (n / 1024 / 1024).toFixed(1) + ' MB' : n >= 1024 ? (n / 1024).toFixed(1) + ' KB' : n + ' B')
@@ -347,6 +360,69 @@ class MemoryEngine {
347
360
  return result
348
361
  }
349
362
 
363
+ /** 当前插件版本(从 package.json 读,缓存)。 */
364
+ async configVersion() {
365
+ try {
366
+ const indexPath = fileURLToPath(import.meta.url)
367
+ const pkgPath = path.join(path.dirname(indexPath), '..', 'package.json')
368
+ const raw = await this.readTextSafe(pkgPath)
369
+ if (raw) { try { return String(JSON.parse(raw).version || '') } catch (e) {} }
370
+ } catch (e) {}
371
+ return ''
372
+ }
373
+
374
+ // ---------- 动态通知(发布者→用户:重大 bug 提醒,不依赖发版) ----------
375
+ /** 拉取通知源(notices.json),缓存 1 小时;失败回退旧缓存。 */
376
+ async fetchNotices(force) {
377
+ const cacheFile = path.join(dshHome(), 'memory', 'notices-cache.json')
378
+ if (!force) {
379
+ try {
380
+ const raw = await this.readTextSafe(cacheFile)
381
+ if (raw) {
382
+ const j = JSON.parse(raw)
383
+ if (j && j.fetchedAt && Date.now() - j.fetchedAt < 3600 * 1000) return j.notices || []
384
+ }
385
+ } catch (e) {}
386
+ }
387
+ try {
388
+ const rr = await fetch(NOTICES_URL, { signal: AbortSignal.timeout(10000) })
389
+ if (rr.ok) {
390
+ const j = await rr.json()
391
+ const list = Array.isArray(j && j.notices) ? j.notices : []
392
+ try {
393
+ await mkdir(path.dirname(cacheFile), { recursive: true })
394
+ await writeFile(cacheFile, JSON.stringify({ fetchedAt: Date.now(), notices: list }), 'utf8')
395
+ } catch (e) {}
396
+ return list
397
+ }
398
+ } catch (e) {}
399
+ try {
400
+ const raw = await this.readTextSafe(cacheFile)
401
+ if (raw) { const j = JSON.parse(raw); if (j && j.notices) return j.notices }
402
+ } catch (e) {}
403
+ return []
404
+ }
405
+
406
+ /** 过滤出当前生效的通知(时间窗口 + 版本范围)。 */
407
+ matchNotices() {
408
+ const now = Date.now()
409
+ const out = []
410
+ for (const n of (this._noticesCache || [])) {
411
+ try {
412
+ if (!n || !n.id) continue
413
+ if (n.startAt && new Date(n.startAt).getTime() > now) continue
414
+ if (n.endAt && new Date(n.endAt).getTime() < now) continue
415
+ const current = this._noticesVersion || ''
416
+ if (current) {
417
+ if (n.minVersion && cmpVersion(current, n.minVersion) < 0) continue
418
+ if (n.maxVersion && cmpVersion(current, n.maxVersion) > 0) continue
419
+ }
420
+ out.push(n)
421
+ } catch (e) {}
422
+ }
423
+ return out
424
+ }
425
+
350
426
  // ---------- 读取 ----------
351
427
  async readTextSafe(p) {
352
428
  if (!p) return ''
@@ -2135,6 +2211,10 @@ export function apply(ctx, config) {
2135
2211
  refreshAll()
2136
2212
  // 自动检查更新:host 启动时查一次 npm registry(结果缓存 12 小时,设置页打开直接读缓存显示)
2137
2213
  void engine.checkUpdate(false)
2214
+ // 动态通知:启动拉取一次 + 每小时刷新(发布者 push notices.json 即可向用户推送重大提醒)
2215
+ const noticesRefresh = () => { void engine.fetchNotices(true).then((l) => { engine._noticesCache = l }).catch(() => {}) }
2216
+ const noticesTimer = setInterval(noticesRefresh, 3600 * 1000)
2217
+ void engine.fetchNotices(false).then((l) => { engine._noticesCache = l }).catch(() => {})
2138
2218
  ctx.on('agent/session-start', (payload) => {
2139
2219
  refreshAll(payload && payload.agent)
2140
2220
  // 缓存最近 agent 引用(subagent parent 需要完整 agent 对象)
@@ -2661,6 +2741,20 @@ export function apply(ctx, config) {
2661
2741
  } catch (e) { writeJson(res, 500, { error: String(e && e.message ? e.message : e) }) }
2662
2742
  },
2663
2743
  },
2744
+ {
2745
+ kind: 'exact',
2746
+ path: API.notices,
2747
+ handler: async (req, res) => {
2748
+ if (!isLoopbackRequest(req)) return writeJson(res, 403, { error: 'forbidden: loopback-only' })
2749
+ if ((req.method || 'GET') !== 'GET') return writeJson(res, 405, { error: 'method not allowed' })
2750
+ try {
2751
+ const list = await engine.fetchNotices(false)
2752
+ engine._noticesCache = list
2753
+ engine._noticesVersion = await engine.configVersion()
2754
+ writeJson(res, 200, { current: engine._noticesVersion, notices: engine.matchNotices() })
2755
+ } catch (e) { writeJson(res, 500, { error: String(e && e.message ? e.message : e) }) }
2756
+ },
2757
+ },
2664
2758
  ]
2665
2759
 
2666
2760
  // ---------- 后台轮询兜底(对标外部助手的心跳轮询):每 5 分钟重试失败的自动沉淀 + 心跳文件 ----------
@@ -2703,6 +2797,7 @@ export function apply(ctx, config) {
2703
2797
  ctx.effect(() => () => {
2704
2798
  clearInterval(retryTimer)
2705
2799
  clearInterval(heartbeatTimer)
2800
+ clearInterval(noticesTimer)
2706
2801
  for (const dispose of disposers) { try { dispose() } catch (e) {} }
2707
2802
  }, 'dsh-auto-memory: surfaces')
2708
2803
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@a9i5k4/dsh-auto-memory",
3
3
  "description": "DSH 自动记忆插件:三层记忆(用户级/项目笔记/每日日志)自动注入与检索、每轮对话自动沉淀、每日反思、可视化面板与设置页,支持继承其他 AI 工具的记忆。",
4
- "version": "0.1.16",
4
+ "version": "0.1.17",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {