@a9i5k4/dsh-auto-memory 0.1.28 → 0.1.29

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
@@ -176,6 +176,7 @@ window.__ModuleLoader__.load({
176
176
  fExtBudget: '外部记忆注入预算(字符)', fExtBudgetHint: '外部记忆来源在上下文中的注入预算。默认 1400(路径模式下影响有限)。',
177
177
  fConsolidateMin: '自动沉淀内容门槛(字符)', fConsolidateMinHint: '本轮 user+assistant 总字符低于此值视为寒暄跳过。默认 240。',
178
178
  fAway: '暂离阈值(分钟)', fAwayHint: '距上次活动超过该值视为暂离,回归时自动弹出记忆窗口。默认 60。',
179
+ fAutoPopup: '自动弹出记忆窗口', fAutoPopupHint: '暂离/回归时自动弹出记忆窗口(corner)并欢迎;关闭后只能手动打开。默认开。',
179
180
  fAutoConsolidate: '自动沉淀(每轮对话结束AI评估)', fAutoConsolidateHint: '关闭后每轮对话结束不再自动调用 AI 评估与写入今日日志。',
180
181
  fConsolidate: '自动沉淀间隔(分钟)', fConsolidateHint: '两轮自动沉淀之间的最短间隔。默认 30;非工作时间(22:00-08:00)自动翻倍,避免短时间耗尽每日额度。',
181
182
  fConsolidateMax: '自动沉淀每日额度(次)', fConsolidateMaxHint: '每天最多触发自动沉淀的次数,到点后当天不再调用。默认 8。',
@@ -276,6 +277,7 @@ window.__ModuleLoader__.load({
276
277
  fExtBudget: 'External memory injection budget (chars)', fExtBudgetHint: 'Budget for external memory sources in context. Default 1400 (limited effect with path mode).',
277
278
  fConsolidateMin: 'Auto-consolidation content threshold (chars)', fConsolidateMinHint: 'Turns with fewer combined user+assistant chars are treated as chit-chat and skipped. Default 240.',
278
279
  fAway: 'Away threshold (minutes)', fAwayHint: 'Marked away when inactive longer than this; the memory panel auto-opens on return. Default 60.',
280
+ fAutoPopup: 'Auto-open memory panel', fAutoPopupHint: 'Auto-open the memory panel (corner) with a welcome when returning from away; disabled = open manually only. Default on.',
279
281
  fAutoConsolidate: 'Auto-consolidation (AI review each turn end)', fAutoConsolidateHint: 'When off, no automatic AI review or daily-log writes at turn end.',
280
282
  fConsolidate: 'Auto-consolidate interval (minutes)', fConsolidateHint: 'Minimum gap between auto-consolidations. Default 30; doubled automatically outside work hours (22:00-08:00).',
281
283
  fConsolidateMax: 'Daily auto-consolidate quota', fConsolidateMaxHint: 'Max auto-consolidation runs per day; no more runs after the quota is reached. Default 8.',
@@ -337,6 +339,7 @@ window.__ModuleLoader__.load({
337
339
  // 暂离检测:优先用 host 定时检测的 away 状态(阈值可配 awayMinutes),回退本地 lastActive 旧逻辑
338
340
  var hostAway = false
339
341
  var hostAwayReady = false
342
+ var autoPopupEnabled = true
340
343
  function isAway() {
341
344
  if (hostAwayReady) return hostAway
342
345
  var lastSeen = 0
@@ -346,6 +349,15 @@ window.__ModuleLoader__.load({
346
349
 
347
350
  // ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
348
351
  var CHANGELOG = {
352
+ '0.1.29': { zh: [
353
+ '修复:工作区总览一直显示「未发现带记忆的工作区」——DSH 新版会话持久化改为 session.jsonl.zstd 压缩帧,旧逻辑只认 .jsonl 扫描不到任何会话;现已用 zstd 解压读取首行提取 cwd,并让空的 summary 缓存短时失效,避免空结果被永久固化。',
354
+ '新增:设置页「自动化」新增「自动弹出记忆窗口」开关——关闭后暂离/回归不再自动弹出 corner 问候栏,只能手动打开。',
355
+ '提示:这是近期稳定版(0.1.x 维护线)的发布。实验版(下一大版本)预计未来几周内发布,在此之前稳定版仅做维护性更新。',
356
+ ], en: [
357
+ 'Fix: Workspace overview kept showing "No memory workspaces found" — DSH now stores sessions as session.jsonl.zstd compressed frames that were not recognized; the overview now decompresses them to read the first line and extract each workspace, and empty summary caches expire instead of being fixed forever.',
358
+ 'New: "Auto-open memory panel" toggle in Settings → Automation - turn it off to stop auto-opening the corner greeting on away/return; the panel stays manual-only.',
359
+ 'Note: this is a recent stable-line (0.1.x maintenance) release. The experimental next major version is expected within a few weeks; until then the stable line only receives maintenance updates.',
360
+ ] },
349
361
  '0.1.28': { zh: [
350
362
  '脏 token 检查器(prion-scan 四类启发式整合):mojibake GBK 残骸特征表补全至 34 项(与 prion-scan.mjs 逐字一致),写入闸门新增 raw JSON envelope(memoryBlock/"uid"/updatedAt/"role")与 base64 残骸行拒写——外部 AI 工具画像无法再整段混入。',
351
363
  '新增「扫描脏 token」:设置页调试中心一键扫描用户级/项目笔记/每日日志/反思,按行区间返回 mojibake / raw JSON / 超长行 / base64 / 重复块报告(只给位置,不含正文)。',
@@ -2058,6 +2070,7 @@ window.__ModuleLoader__.load({
2058
2070
  field(t('fAutoConsolidate'), h('input', { type: 'checkbox', checked: cfg.autoConsolidate !== false, onChange: function (e) { set('autoConsolidate', e.target.checked) } }), t('fAutoConsolidateHint')),
2059
2071
  field(t('fConsolidate'), h('input', { 'data-dam-input': '', type: 'number', min: 5, value: cfg.autoConsolidateCooldownMinutes === undefined ? 30 : cfg.autoConsolidateCooldownMinutes, onChange: function (e) { set('autoConsolidateCooldownMinutes', Number(e.target.value) || 30) } }), t('fConsolidateHint')),
2060
2072
  field(t('fConsolidateMax'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 50, value: cfg.autoConsolidateDailyMax === undefined ? 8 : cfg.autoConsolidateDailyMax, onChange: function (e) { set('autoConsolidateDailyMax', Number(e.target.value) || 8) } }), t('fConsolidateMaxHint')),
2073
+ field(t('fAutoPopup'), h('input', { type: 'checkbox', checked: cfg.autoPopupEnabled !== false, onChange: function (e) { set('autoPopupEnabled', e.target.checked) } }), t('fAutoPopupHint')),
2061
2074
  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')),
2062
2075
  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')),
2063
2076
  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')),
@@ -2109,6 +2122,7 @@ window.__ModuleLoader__.load({
2109
2122
  applyLocalePref('system')
2110
2123
  apiGet(API.config).then(function (d) {
2111
2124
  if (d && d.config && d.config.locale) applyLocalePref(d.config.locale)
2125
+ if (d && d.config && typeof d.config.autoPopupEnabled === 'boolean') autoPopupEnabled = d.config.autoPopupEnabled
2112
2126
  }).catch(function () {})
2113
2127
  // DSH 系统语言变化:跟随更新 + 重渲染 + 重注册入口 label
2114
2128
  ctx.on('locale/change', function (snap) {
@@ -2127,6 +2141,7 @@ window.__ModuleLoader__.load({
2127
2141
  var prevAwayState = null
2128
2142
  function autoOpenOnReturn() {
2129
2143
  try {
2144
+ if (autoPopupEnabled === false) { prevAwayState = hostAwayReady ? hostAway : prevAwayState; return }
2130
2145
  if (hostAwayReady && prevAwayState === true && hostAway === false) {
2131
2146
  // 暂离回归:打开窗口 + 欢迎弹窗(若有待展示总结一并显示)
2132
2147
  if (!controller.isOpen()) controller.open()
@@ -2193,6 +2208,7 @@ window.__ModuleLoader__.load({
2193
2208
  hostAway = d.away
2194
2209
  hostAwayReady = true
2195
2210
  }
2211
+ if (d && typeof d.autoPopupEnabled === 'boolean') autoPopupEnabled = d.autoPopupEnabled
2196
2212
  if (d && d.pendingSummary && d.pendingSummary.summary) {
2197
2213
  var seenKey = 'dsh-auto-memory.seenSummary.' + (d.pendingSummary.date || '') + '.' + (d.pendingSummary.time || '')
2198
2214
  try {
package/lib/index.js CHANGED
@@ -25,6 +25,10 @@ const execP = promisify(cpExec)
25
25
  import { homedir } from 'node:os'
26
26
  import path from 'node:path'
27
27
  import { fileURLToPath } from 'node:url'
28
+ import * as nodeZlib from 'node:zlib'
29
+
30
+ /** zstd 解压(DSH 新版会话持久化 session.jsonl.zstd);Node <22 无此能力时为空,自动回退明文读。 */
31
+ const zstdDec = typeof nodeZlib.zstdDecompressSync === 'function' ? nodeZlib.zstdDecompressSync : null
28
32
 
29
33
  /** Stable cordis plugin name. */
30
34
  export const name = 'auto-memory'
@@ -92,6 +96,8 @@ const DEFAULT_CONFIG = {
92
96
  autoConsolidateDailyMax: 8,
93
97
  /** 暂离阈值(分钟):距上次活动超过该值视为暂离,回归时自动弹出记忆窗口并欢迎。默认 60。 */
94
98
  awayMinutes: 60,
99
+ /** 暂离回来自动弹出记忆窗口(corner/问候栏)开关:false=关闭,只能手动打开。默认 true。 */
100
+ autoPopupEnabled: true,
95
101
  /** 自动总结时间点(24h "HH:MM" 数组,如 ["12:00","18:00","22:00"]):到点自动生成本时段总结并弹窗展示。空数组=关闭。 */
96
102
  autoSummaryTimes: [],
97
103
  /** 日界(分钟,从 0 点起算):凌晨在此之前的活儿归前一天日志;默认 450=早上 7:30 后才进入新一天。 */
@@ -1046,8 +1052,18 @@ class MemoryEngine {
1046
1052
  }
1047
1053
 
1048
1054
  // ---------- 工作区总览(跨工作区全局总结) ----------
1049
- /** 读 jsonl 首行(session header)。 */
1055
+ /** 读 jsonl 首行(session header);.jsonl 明文流式读首行,.jsonl.zstd 为 zstd 压缩帧(整块解压后取首行,与 dsh 核心 dsh-session-persistence-jsonl 一致)。 */
1050
1056
  readFirstLine(p) {
1057
+ if (p.endsWith('.zstd')) {
1058
+ return readFile(p).then((buf) => {
1059
+ try {
1060
+ const dec = zstdDec ? zstdDec(buf) : null
1061
+ const text = (dec || buf).toString('utf8')
1062
+ const i = text.indexOf('\n')
1063
+ return i >= 0 ? text.slice(0, i) : text
1064
+ } catch (e) { return '' }
1065
+ }).catch(() => '')
1066
+ }
1051
1067
  return new Promise((resolve) => {
1052
1068
  const rs = createReadStream(p, { encoding: 'utf8' })
1053
1069
  let buf = ''
@@ -1072,7 +1088,7 @@ class MemoryEngine {
1072
1088
  for (const en of entries) {
1073
1089
  if (out.size >= 30) return
1074
1090
  if (en.isDirectory()) await walk(path.join(dir, en.name), depth + 1)
1075
- else if (en.isFile() && en.name.endsWith('.jsonl')) {
1091
+ else if (en.isFile() && (en.name.endsWith('.jsonl.zstd') || en.name.endsWith('.jsonl'))) {
1076
1092
  try {
1077
1093
  const first = await this.readFirstLine(path.join(dir, en.name))
1078
1094
  if (first) {
@@ -1112,7 +1128,11 @@ class MemoryEngine {
1112
1128
  const raw = await this.readTextSafe(cacheFile)
1113
1129
  if (raw) {
1114
1130
  const j = JSON.parse(raw)
1115
- if (j && Array.isArray(j.workspaces) && j.graph && Array.isArray(j.graph.topics) && Array.isArray(j.graph.links)) return { workspaces: j.workspaces, graph: j.graph, cached: true, generatedAt: j.generatedAt }
1131
+ // 空结果只短时信任(≤30 分钟),防止旧版 bug 产出的"空缓存"被永久复用导致永远显示"未发现带记忆的工作区"
1132
+ const ageMs = Date.now() - (Number(j && j.generatedAt) || 0)
1133
+ const list = j && Array.isArray(j.workspaces) ? j.workspaces : null
1134
+ const valid = !!list && (list.length > 0 || (ageMs >= 0 && ageMs <= 30 * 60 * 1000))
1135
+ if (valid && j.graph && Array.isArray(j.graph.topics) && Array.isArray(j.graph.links)) return { workspaces: list, graph: j.graph, cached: true, generatedAt: j.generatedAt }
1116
1136
  }
1117
1137
  } catch (e) {}
1118
1138
  }
@@ -1125,6 +1145,11 @@ class MemoryEngine {
1125
1145
  const fallbackItems = mem.logs.flatMap((l) => l.text.split('\n').filter((x) => x.trim().startsWith('- ')).map((x) => x.trim().replace(/^- /, '').slice(0, 120))).slice(-5)
1126
1146
  return { path: cwd, name, input: { name, logs: logText.slice(0, 5200) || '(无)', notes: mem.notes || '(无)' }, fallbackItems, logCount: fallbackItems.length, dateRange: mem.logs.length ? (mem.logs[mem.logs.length - 1].date + ' ~ ' + mem.logs[0].date) : '' }
1127
1147
  }))).filter(Boolean)
1148
+ if (!records.length) {
1149
+ const empty = { workspaces: [], graph: { topics: [], links: [] }, generatedAt: Date.now() }
1150
+ try { await mkdir(path.dirname(cacheFile), { recursive: true }); await writeFile(cacheFile, JSON.stringify(empty, null, 2), 'utf8') } catch (e) {}
1151
+ return { workspaces: [], graph: empty.graph, cached: false, generatedAt: empty.generatedAt }
1152
+ }
1128
1153
  const fallback = records.map((r) => ({ path: r.path, name: r.name, summary: '', items: r.fallbackItems, graphTopics: r.fallbackItems.slice(0, 4).map((label) => ({ label, detail: '' })), logCount: r.logCount, dateRange: r.dateRange }))
1129
1154
  let workspaces = fallback
1130
1155
  let graph = { topics: [], links: [] }
@@ -1956,6 +1981,7 @@ class MemoryEngine {
1956
1981
  // 时间检测:暂离状态 / 待展示的自动总结 / 相关配置
1957
1982
  away: !!this.state.away,
1958
1983
  awayMinutes: Math.max(Number(this.config.awayMinutes) || 60, 1),
1984
+ autoPopupEnabled: this.config.autoPopupEnabled !== false,
1959
1985
  autoSummaryTimes: Array.isArray(this.config.autoSummaryTimes) ? this.config.autoSummaryTimes : [],
1960
1986
  pendingSummary: this.state.pendingSummary || null,
1961
1987
  autoStats: this.autoStats,
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.28",
4
+ "version": "0.1.29",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {