@a9i5k4/dsh-auto-memory 0.1.10 → 0.1.11

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,9 +1,15 @@
1
1
  # dsh-auto-memory — DSH Auto Memory Plugin / DSH 自动记忆插件
2
2
 
3
+ <p align="center">
4
+ <img width="820" alt="dsh-auto-memory banner" src="docs/banner.jpg">
5
+ </p>
6
+
3
7
  An auto-memory plugin for the DeepSeek Harness Web GUI: three-layer memory (user-level / project notes / daily logs) with automatic injection and retrieval, daily reflections, AI period greetings with three-level drawers, auto-consolidation after every turn, smart search, a calendar view and settings page, and inheritance of memories from other AI tools.
4
8
 
5
9
  DSH Web GUI 的记忆插件:三层记忆自动注入与检索、每日反思、AI 时段问候与三级抽屉、每轮自动沉淀、智能检索、日历视图与设置页,支持继承其他 AI 工具的历史记忆。
6
10
 
11
+ > **Quick install**: `cd ~/.dsh/profiles/web` → `pnpm add @a9i5k4/dsh-auto-memory` → append `"@a9i5k4/dsh-auto-memory"` to `dsh.profile.bundles` in that directory's `package.json` → restart **dsh web** (the 「Memory」entry appears in the sidebar). Full instructions in [Installation](#installation-one-command); no-pnpm alternative: `npm install @a9i5k4/dsh-auto-memory`.
12
+
7
13
  [**English**](README.md) | [中文版](README.zh-CN.md)
8
14
 
9
15
  ---
@@ -80,109 +86,42 @@ The Search tab adds a **Smart search** button next to the keyword search:
80
86
 
81
87
  ---
82
88
 
83
- ## Demos
84
-
85
- ### 1. Auto-consolidation in action
86
-
87
- You finish a conversation turn — no memory commands needed. The moment the turn ends, the plugin writes a topic-grouped entry into today's log:
88
-
89
- ```
90
- ── Chat ──────────────────────────────────────────────────
91
-
92
- You: 我把抽屉的 bug 修好了,顺便加了字号功能,设置页可以调,默认大号。
93
-
94
- AI: 已把 X 记入今日日志。 (回复末尾加粗转述,记忆操作对用户可见)
95
-
96
- ── Turn ends → auto-consolidation writes to .dsh-memory/2026-08-14.md ──
97
-
98
- ## 修复抽屉bug与字号功能(22:30)
99
- - 修复了抽屉 bug
100
- - 新增界面字号功能(设置页,默认大号)
101
- ```
102
-
103
- Long-term decisions are promoted to project notes with a `## YYYY-MM-DD` heading automatically. If the AI call fails, the entry is queued and retried by the 5-minute polling loop.
104
-
105
- ### 2. AI greeting & three-level drawers
89
+ ## Screenshots
106
90
 
107
- Click the 「Memory」 button the floating panel opens with an AI-written greeting, cross-workspace summaries, and drawers titled by the AI summaries themselves:
91
+ All screenshots below are real captures of the plugin running inside the DSH Web GUI.
108
92
 
109
- ```
110
- ┌──────────────────────────────────────────────────────┐
111
- │ 自动记忆 ⤾ ⟳ ✕ │
112
- ├──────────────────────────────────────────────────────┤
113
- │ 概览 │ 日志 │ 笔记 │ 反思 │ 接续 │ 日历 │ 检索 │ 工作区 │
114
- ├──────────────────────────────────────────────────────┤
115
- │ 晚上好 · 晚上 │
116
- │ │
117
- │ 晚上好,这一晚上你收获满满呀。把 Ark9Tools 扩展框架整个 │
118
- │ 捋了一遍,冒烟测试 18 项全过,还一口气把 dsh-auto-memory │
119
- │ 推到 0.1.9… │
120
- │ │
121
- │ ┌ 工作区总结(跨工作区) ────────────────────────────┐ │
122
- │ │ dsh_debug · 8-10 ~ 8-14 │ │
123
- │ │ 主要在做 dsh-auto-memory 插件,逐步发布 v0.1.x… │ │
124
- │ │ · 8-14: v0.1.9 系列功能落地 │ │
125
- │ │ · 8-13: 中文乱码与 no-branches 修复 │ │
126
- │ └────────────────────────────────────────────────┘ │
127
- │ │
128
- │ ▼ 下午把 dsh-auto-memory 完整发布出去了… │
129
- │ 今日下午 · 10 条日志 · 生成于 21:28 │
130
- │ ▸ 发布与收尾 (3 项) │
131
- │ ▸ 修复发布过程中的坑 (2 项) │
132
- │ │
133
- │ ▼ 晚上搭建了扩展框架… │
134
- │ 今日晚上 · 7 条日志 · 生成于 21:28 │
135
- │ ▸ EventBus 与服务容器 (4 项) │
136
- └──────────────────────────────────────────────────────┘
137
- ```
93
+ ### Main interface — the Auto Memory panel (Connect tab)
138
94
 
139
- Tap any small drawer (▸) to read its detail points. Greetings and summaries are cached per period — reopening the panel costs no API calls; the button forces fresh generation.
95
+ <img width="720" alt="Auto Memory panel in the DSH web UI" src="docs/screenshots/main-connect-en.png">
140
96
 
141
- ### 3. Smart search
97
+ ### Connect tab — inherit memories accumulated by other AI tools
142
98
 
143
- Type a natural-language question in the Search tab and hit 「智能检索」:
99
+ <img width="720" alt="Connect tab" src="docs/screenshots/connect-en.png">
144
100
 
145
- ```
146
- ┌──────────────────────────────────────────────────────┐
147
- │ 检索: [上次发布 npm 包踩了什么坑 ] [检索] [智能检索]│
148
- ├──────────────────────────────────────────────────────┤
149
- │ ┌ 智能回答 ───────────────────────────────────────┐ │
150
- │ │ 上次发布踩了三个坑:GitHub 中文 description 乱码 │ │
151
- │ │ (PowerShell GBK,改用 Node fetch);第一次 push │ │
152
- │ │ 实际没成功(凭据管理器拦截,需 -c credential.helper=);│ │
153
- │ │ npm 2FA 需要 Automation 令牌。以上来自 │ │
154
- │ │ 2026-08-14 日志。 │ │
155
- │ └────────────────────────────────────────────────┘ │
156
- │ 关键词: 发布 / 踩坑 / GitHub / npm / 推送 │
157
- │ · [2026-08-14.md] 发布完成:①GitHub 仓库已建… │
158
- │ · [reflections/2026-08-14.md] 成果回顾:… │
159
- └──────────────────────────────────────────────────────┘
160
- ```
101
+ ### Overview — AI-written greeting, today's drawers & cross-workspace summaries
161
102
 
162
- The AI expands your query into 3-6 keywords, scans all memory layers, then composes a conversational answer citing its sources — never fabricating facts.
103
+ <img width="720" alt="Overview tab" src="docs/screenshots/overview-en.png">
163
104
 
164
- ### 4. AI-maintained calendar
105
+ ### Calendar AI-maintained four-quadrant schedule
165
106
 
166
- Mention a deadline in chat — the AI writes it into the calendar and says so in plain text:
107
+ <img width="720" alt="Calendar tab" src="docs/screenshots/calendar-en.png">
167
108
 
168
- ```
169
- ── Chat ──────────────────────────────────────────────────
109
+ ### Reflections — structured daily reviews
170
110
 
171
- You: 周五 10 点要交周报,别忘了。
111
+ <img width="720" alt="Reflections tab" src="docs/screenshots/reflections-en.png">
172
112
 
173
- AI: 已把『2026-08-15 10:00 交周报』记入日历(重要不紧急)。
113
+ ### Settings memory storage, day boundary, daily budget, update check
174
114
 
175
- ── Calendar tab · 2026 年 8 月 ──────────────────────────
115
+ <img width="720" alt="Auto Memory settings" src="docs/screenshots/settings-en.png">
176
116
 
177
- 一 二 三 四 五 六 日
178
- 11 12 13 14 [15]
179
- ● 交周报
180
- ```
117
+ ## Beyond the screenshots
181
118
 
182
- Pending items are injected into every future session's system prompt until completedthe AI reminds you without being asked.
119
+ - **Auto-consolidation**: every finished turn is evaluated by a small subagent and topic-grouped entries are written to today's log automatically (`## 主题(HH:MM)` + bullet points) no `memory_log` needed for routine work. Long-term value is promoted to project notes / user-level memory, small talk is skipped, failures are queued and retried every 5 minutes (a 15-second heartbeat file proves the loop is alive).
120
+ - **Smart search**: ask in natural language — the AI expands your query into keywords, scans every memory layer, then answers conversationally with sources cited.
121
+ - **Calendar reminders**: pending items are injected into future sessions' system prompts until completed — the AI reminds you without being asked.
122
+ - **One-click update**: the settings page shows your installed version vs. the npm registry latest; registry installs get a one-click update button (pnpm/npm runs under the hood), then restart to apply.
183
123
 
184
124
  ---
185
-
186
125
  ## Installation (one command)
187
126
 
188
127
  > Prerequisite: install [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) and start `dsh web` at least once.
package/README.zh-CN.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # dsh-auto-memory — DSH 自动记忆插件
2
2
 
3
+ <p align="center">
4
+ <img width="820" alt="dsh-auto-memory 宣传图" src="docs/banner.jpg">
5
+ </p>
6
+
3
7
  DSH Web GUI 的记忆插件:三层记忆(用户级 / 项目笔记 / 每日日志)自动注入与检索、每日反思、AI 时段问候与三级抽屉、每轮自动沉淀、智能检索、日历视图与设置页,支持继承其他 AI 工具的历史记忆。
4
8
 
9
+ > **快速安装**:`cd ~/.dsh/profiles/web` → `pnpm add @a9i5k4/dsh-auto-memory` → 在该目录 `package.json` 的 `dsh.profile.bundles` 里追加 `"@a9i5k4/dsh-auto-memory"` → 重启 **dsh web**(侧边栏出现「记忆」入口)。完整步骤见 [安装](#安装npm-一键);没有 pnpm 可用 `npm install @a9i5k4/dsh-auto-memory`。
10
+
5
11
  [English](README.md) | [中文版](README.zh-CN.md)
6
12
 
7
13
  ---
@@ -78,109 +84,48 @@ DSH Web GUI 的记忆插件:三层记忆(用户级 / 项目笔记 / 每日
78
84
 
79
85
  ---
80
86
 
81
- ## 演示
87
+ ## 界面截图
82
88
 
83
- ### 1. 每轮自动沉淀
89
+ 以下都是插件在 DSH Web GUI 中的真实运行截图。
84
90
 
85
- 你结束一轮对话,什么都不用做——本轮一结束,插件自动把按主题分组的条目写进今日日志:
91
+ ### 主界面 — 「自动记忆」浮窗(接续页签)
86
92
 
87
- ```
88
- ── 对话 ──────────────────────────────────────────────────
93
+ <img width="720" alt="自动记忆浮窗" src="docs/screenshots/main-connect-zh.png">
89
94
 
90
- 你: 我把抽屉的 bug 修好了,顺便加了字号功能,设置页可以调,默认大号。
95
+ ### 接续 继承其他 AI 工具积累的记忆
91
96
 
92
- AI: 已把 X 记入今日日志。 (回复末尾加粗转述,记忆操作对用户可见)
97
+ <img width="720" alt="接续页签" src="docs/screenshots/connect-zh.png">
93
98
 
94
- ── 本轮结束 自动沉淀写入 .dsh-memory/2026-08-14.md ──
99
+ ### 概览 AI 问候语、今日抽屉与跨工作区总结
95
100
 
96
- ## 修复抽屉bug与字号功能(22:30)
97
- - 修复了抽屉 bug
98
- - 新增界面字号功能(设置页,默认大号)
99
- ```
101
+ <img width="720" alt="概览页签" src="docs/screenshots/overview-zh.png">
100
102
 
101
- 长期决策会自动升格进项目笔记(带 `## YYYY-MM-DD` 日期标题)。如果 AI 调用失败,条目会进入重试队列,由每 5 分钟的轮询兜底补写。
103
+ ### 日历 AI 维护的四象限日程
102
104
 
103
- ### 2. AI 问候与三级抽屉
105
+ <img width="720" alt="日历页签" src="docs/screenshots/calendar-zh.png">
104
106
 
105
- 点击侧边栏「记忆」按钮——浮层面板弹出,AI 写的问候语、跨工作区总结、以及标题就是 AI 总结的抽屉:
107
+ ### 检索 关键词检索与智能检索
106
108
 
107
- ```
108
- ┌──────────────────────────────────────────────────────┐
109
- │ 自动记忆 ⤾ ⟳ ✕ │
110
- ├──────────────────────────────────────────────────────┤
111
- │ 概览 │ 日志 │ 笔记 │ 反思 │ 接续 │ 日历 │ 检索 │ 工作区 │
112
- ├──────────────────────────────────────────────────────┤
113
- │ 晚上好 · 晚上 │
114
- │ │
115
- │ 晚上好,这一晚上你收获满满呀。把 Ark9Tools 扩展框架整个 │
116
- │ 捋了一遍,冒烟测试 18 项全过,还一口气把 dsh-auto-memory │
117
- │ 推到 0.1.9… │
118
- │ │
119
- │ ┌ 工作区总结(跨工作区) ────────────────────────────┐ │
120
- │ │ dsh_debug · 8-10 ~ 8-14 │ │
121
- │ │ 主要在做 dsh-auto-memory 插件,逐步发布 v0.1.x… │ │
122
- │ │ · 8-14: v0.1.9 系列功能落地 │ │
123
- │ │ · 8-13: 中文乱码与 no-branches 修复 │ │
124
- │ └────────────────────────────────────────────────┘ │
125
- │ │
126
- │ ▼ 下午把 dsh-auto-memory 完整发布出去了… │
127
- │ 今日下午 · 10 条日志 · 生成于 21:28 │
128
- │ ▸ 发布与收尾 (3 项) │
129
- │ ▸ 修复发布过程中的坑 (2 项) │
130
- │ │
131
- │ ▼ 晚上搭建了扩展框架… │
132
- │ 今日晚上 · 7 条日志 · 生成于 21:28 │
133
- │ ▸ EventBus 与服务容器 (4 项) │
134
- └──────────────────────────────────────────────────────┘
135
- ```
109
+ <img width="720" alt="检索页签" src="docs/screenshots/search-zh.png">
136
110
 
137
- 点击任意小抽屉(▸)阅读该项工作的细点。问候与总结按时段缓存——再次打开面板不消耗 API;⟳ 按钮强制重新生成。
111
+ ### 反思(英文界面示例)
138
112
 
139
- ### 3. 智能检索
113
+ <img width="720" alt="反思页签" src="docs/screenshots/reflections-en.png">
140
114
 
141
- 在检索页输入自然语言问题,点「智能检索」:
115
+ ### 设置 — 记忆存储、日界、每日预算、更新检查
142
116
 
143
- ```
144
- ┌──────────────────────────────────────────────────────┐
145
- │ 检索: [上次发布 npm 包踩了什么坑 ] [检索] [智能检索]│
146
- ├──────────────────────────────────────────────────────┤
147
- │ ┌ 智能回答 ───────────────────────────────────────┐ │
148
- │ │ 上次发布踩了三个坑:GitHub 中文 description 乱码 │ │
149
- │ │ (PowerShell GBK,改用 Node fetch);第一次 push │ │
150
- │ │ 实际没成功(凭据管理器拦截,需 -c credential.helper=);│ │
151
- │ │ npm 2FA 需要 Automation 令牌。以上来自 │ │
152
- │ │ 2026-08-14 日志。 │ │
153
- │ └────────────────────────────────────────────────┘ │
154
- │ 关键词: 发布 / 踩坑 / GitHub / npm / 推送 │
155
- │ · [2026-08-14.md] 发布完成:①GitHub 仓库已建… │
156
- │ · [reflections/2026-08-14.md] 成果回顾:… │
157
- └──────────────────────────────────────────────────────┘
158
- ```
117
+ <img width="720" alt="自动记忆设置" src="docs/screenshots/settings-zh.png">
159
118
 
160
- AI 把你的查询扩散成 3-6 个关键词,扫描全部记忆层,再综合成一段自然语言回答并注明来源——绝不编造记忆里没有的事实。
119
+ <img width="720" alt="自动记忆设置(调试中心)" src="docs/screenshots/settings-debug-zh.png">
161
120
 
162
- ### 4. AI 维护日历
121
+ ## 截图之外
163
122
 
164
- 对话里提一句 deadline,AI 就写进日历并明文转述:
165
-
166
- ```
167
- ── 对话 ──────────────────────────────────────────────────
168
-
169
- 你: 周五 10 点要交周报,别忘了。
170
-
171
- AI: 已把『2026-08-15 10:00 交周报』记入日历(重要不紧急)。
172
-
173
- ── 日历页签 · 2026 年 8 月 ──────────────────────────────
174
-
175
- 一 二 三 四 五 六 日
176
- 11 12 13 14 [15]
177
- ● 交周报
178
- ```
179
-
180
- 未完成事项会注入之后每次会话的系统提示词——AI 不用你提醒就会主动提及。
123
+ - **每轮自动沉淀**:每轮对话结束由小代理自动评估,按主题分组写进今日日志(`## 主题(HH:MM)` + 要点列表)——常规工作不需要手动 memory_log。有长期价值的内容自动升格项目笔记 / 用户级记忆;寒暄轮跳过;AI 失败入队,每 5 分钟重试(15 秒心跳文件证明轮询存活)。
124
+ - **智能检索**:自然语言提问,AI 扩成关键词扫描全部记忆层,再综合成带出处的自然语言回答。
125
+ - **日历提醒**:未完成事项注入之后每次会话的系统提示词——AI 不用你提醒就会主动提及。
126
+ - **一键更新**:设置页对比本地版本与 npm registry 最新版;registry 安装的用户可直接「一键更新」(后台自动跑 pnpm/npm),重启后生效。
181
127
 
182
128
  ---
183
-
184
129
  ## 安装(NPM 一键)
185
130
 
186
131
  > 前提:已安装 DeepSeek Harness(dsh)并至少启动过一次 `dsh web`。
package/lib/client.js CHANGED
@@ -107,6 +107,12 @@ window.__ModuleLoader__.load({
107
107
  fDayBoundary: '日界(分钟)', fDayBoundaryHint: '从 0 点起算:凌晨在此之前的活儿归前一天。默认 450=早上 7:30 进入新一天;改 480=8:00;0=按午夜切日。',
108
108
  fVersion: '插件版本', checkUpdate: '检查更新', checking: '检查中…', upToDate: '(已是最新)', hasUpdate: '(有新版本)', versionError: '版本检查失败: ', versionCmdHint: '更新命令: cd ~/.dsh/profiles/web && pnpm up @a9i5k4/dsh-auto-memory,然后重启 dsh web 生效。',
109
109
  updateNow: '一键更新', updating: '更新中…', updateDone: '更新完成,请重启 dsh web 生效。', updateFailed: '更新失败: ', devLinkHint: '(当前为本地开发链接,请直接同步开发源码)', noProfileHint: '(未找到 dsh profile,无法自动更新)',
110
+ kvVersion: '插件版本', kvUnreadable: '(读不到)', kvPid: 'PID / 启动', kvRestart: 'Host 需重启?', kvRestartYes: '是(代码比进程新)', kvRestartNo: '否',
111
+ kvHeartbeat: '轮询心跳', hbAliveAgo: '存活(上次 ', hbSecAgo: ' 秒前)', hbAlive: '存活', hbNone: '无心跳文件',
112
+ kvQueue: '沉淀重试队列', kvCountSuffix: ' 条', kvToday: '今日沉淀', kvRecentPrefix: ' 条 / 最近 ', kvBusy: '正在沉淀中', kvYes: '是', kvNo: '否',
113
+ kvAvail: '可用', kvUnavail: '不可用', kvDup: '项目笔记重复标题', kvDupCount: ' 个',
114
+ kvMem: '记忆文件', kvMemUser: '用户', kvMemNotes: '笔记', kvMemLog: '日志', kvMemMissing: '缺失',
115
+ kvWs: '当前工作区', kvWsUnknown: '(未取到)', kvApi: 'API 连通', unknown: '(未知)',
110
116
  refresh: '刷新', refreshing: '刷新中…', close: '关闭', dragMove: '拖动移动', dragResize: '拖动调整大小', resetPos: '恢复默认位置',
111
117
  generated: '已生成', failed: '失败: ',
112
118
  generatedAt: '生成于 ',
@@ -156,7 +162,7 @@ window.__ModuleLoader__.load({
156
162
  fDays: '注入最近日志天数', fDaysHint: '会话开始时注入最近 N 天的工作日志尾部。',
157
163
  fReflect: '每日反思', fReflectHint: '昨天有工作日志时,会话首轮主动呈现昨日反思。',
158
164
  fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
159
- fLocale: '界面语言', fLocaleHint: '切换插件面板与设置页的显示语言。',
165
+ fLocale: '界面语言', fLocaleHint: '默认跟随 DSH 系统语言;也可手动指定中文 / English。', followSystem: '跟随系统语言',
160
166
  saveSettings: '保存设置',
161
167
  zh: '中文', en: 'English',
162
168
  calendar: '日历', addItem: '添加', save: '保存', cancel: '取消', needTitle: '请填写事项标题', itemTitle: '事项标题…',
@@ -182,6 +188,12 @@ window.__ModuleLoader__.load({
182
188
  fDayBoundary: 'Day boundary (minutes)', fDayBoundaryHint: 'Minutes from midnight: work before it counts as the previous day. Default 450 = 7:30 AM starts the new day; 480 = 8:00; 0 = midnight cut.',
183
189
  fVersion: 'Plugin version', checkUpdate: 'Check for updates', checking: 'Checking…', upToDate: '(up to date)', hasUpdate: '(update available)', versionError: 'Version check failed: ', versionCmdHint: 'Update: cd ~/.dsh/profiles/web && pnpm up @a9i5k4/dsh-auto-memory, then restart dsh web.',
184
190
  updateNow: 'Update now', updating: 'Updating…', updateDone: 'Update finished, restart dsh web to apply.', updateFailed: 'Update failed: ', devLinkHint: '(local dev link, sync source instead)', noProfileHint: '(no dsh profile found, cannot auto-update)',
191
+ kvVersion: 'Plugin version', kvUnreadable: '(unreadable)', kvPid: 'PID / Started', kvRestart: 'Host restart needed?', kvRestartYes: 'yes (code newer than process)', kvRestartNo: 'no',
192
+ kvHeartbeat: 'Heartbeat', hbAliveAgo: 'alive (last ', hbSecAgo: ' s ago)', hbAlive: 'alive', hbNone: 'no heartbeat file',
193
+ kvQueue: 'Consolidation retry queue', kvCountSuffix: ' item(s)', kvToday: 'Consolidated today', kvRecentPrefix: ' item(s) / latest ', kvBusy: 'Consolidating now', kvYes: 'yes', kvNo: 'no',
194
+ kvAvail: 'available', kvUnavail: 'unavailable', kvDup: 'Duplicate note headings', kvDupCount: '',
195
+ kvMem: 'Memory files', kvMemUser: 'user', kvMemNotes: 'notes', kvMemLog: 'log', kvMemMissing: 'missing',
196
+ kvWs: 'Current workspace', kvWsUnknown: '(unknown)', kvApi: 'API probes', unknown: '(unknown)',
185
197
  refresh: 'Refresh', refreshing: 'Refreshing…', close: 'Close', dragMove: 'Drag to move', dragResize: 'Drag to resize', resetPos: 'Reset position',
186
198
  generated: 'Generated', failed: 'Failed: ',
187
199
  generatedAt: 'Generated ',
@@ -231,7 +243,7 @@ window.__ModuleLoader__.load({
231
243
  fDays: 'Recent days injected', fDaysHint: 'Inject tails of the last N days of work logs at session start.',
232
244
  fReflect: 'Daily reflection', fReflectHint: 'When yesterday has logs, the agent presents the reflection at session start.',
233
245
  fStyle: 'Reflection style', fStyleHint: 'Life-style / Professional / Auto.',
234
- fLocale: 'UI language', fLocaleHint: 'Switch the display language of the panel and settings page.',
246
+ fLocale: 'UI language', fLocaleHint: 'Follows the DSH system language by default; you can also pin Chinese / English.', followSystem: 'Follow system language',
235
247
  saveSettings: 'Save settings',
236
248
  zh: '中文', en: 'English',
237
249
  calendar: 'Calendar', addItem: 'Add', save: 'Save', cancel: 'Cancel', needTitle: 'Title is required', itemTitle: 'Item title…',
@@ -245,6 +257,14 @@ window.__ModuleLoader__.load({
245
257
  }
246
258
  }
247
259
  var locale = 'zh'
260
+ var localeMode = 'system' // 'system'=跟随 DSH 系统语言 | 'zh' | 'en'
261
+ var sysLocale = 'zh' // DSH 系统语言(跟随模式使用)
262
+ function applyLocalePref(m) {
263
+ if (m !== 'zh' && m !== 'en' && m !== 'system') m = 'system'
264
+ localeMode = m
265
+ var target = m === 'system' ? sysLocale : m
266
+ if (locale !== target) { locale = target; localeListeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
267
+ }
248
268
  var sessions = null
249
269
  // 当前会话工作区(跟随 GUI 切换工作区):从 sessions 服务拿,供 state/summarize/greet 请求使用
250
270
  function currentWs() {
@@ -437,7 +457,8 @@ window.__ModuleLoader__.load({
437
457
  if (!seenSeg[sname]) continue
438
458
  var items = (ps && ps.groups && ps.groups[sname]) || []
439
459
  if (!items.length) continue
440
- drawers.push({ key: 'seg-' + sname, label: t('segPrefix') + sname, hint: '', items: items.map(function (x) { return { time: '', text: x } }), withTime: false, defaultOpen: sname === curSegName, period: sname })
460
+ var segEn = { '早晨': 'morning', '上午': 'forenoon', '中午': 'noon', '下午': 'afternoon', '晚上': 'evening' }[sname] || sname
461
+ drawers.push({ key: 'seg-' + sname, label: t('segPrefix') + t('seg' + segEn.charAt(0).toUpperCase() + segEn.slice(1)), hint: '', items: items.map(function (x) { return { time: '', text: x } }), withTime: false, defaultOpen: sname === curSegName, period: sname })
441
462
  }
442
463
  // 外层:生活化问候
443
464
  var totalCount = 0
@@ -838,12 +859,13 @@ window.__ModuleLoader__.load({
838
859
  }
839
860
 
840
861
  // ───────────────────────── 日历页签 ─────────────────────────
862
+ var QUADRANT_I18N = { '重要紧急': 'qUrgentImportant', '重要不紧急': 'qImportant', '紧急不重要': 'qUrgent', '不重要不紧急': 'qNone', '未分类': 'qUncategorized' }
841
863
  var QUADRANT_STYLE = {
842
- '重要紧急': { color: 'var(--dsw-alias-state-error-primary, #d64545)', label: t('qUrgentImportant') },
843
- '重要不紧急': { color: 'var(--dsw-alias-brand-primary, #4f7cff)', label: t('qImportant') },
844
- '紧急不重要': { color: 'var(--dsw-alias-state-warn-primary, #e6a23c)', label: t('qUrgent') },
845
- '不重要不紧急': { color: 'var(--dsw-alias-label-secondary, #8a94a6)', label: t('qNone') },
846
- '未分类': { color: 'var(--dsw-alias-label-secondary, #8a94a6)', label: t('qUncategorized') },
864
+ '重要紧急': { color: 'var(--dsw-alias-state-error-primary, #d64545)' },
865
+ '重要不紧急': { color: 'var(--dsw-alias-brand-primary, #4f7cff)' },
866
+ '紧急不重要': { color: 'var(--dsw-alias-state-warn-primary, #e6a23c)' },
867
+ '不重要不紧急': { color: 'var(--dsw-alias-label-secondary, #8a94a6)' },
868
+ '未分类': { color: 'var(--dsw-alias-label-secondary, #8a94a6)' },
847
869
  }
848
870
  function CalendarTab() {
849
871
  var dataPair = useState(null)
@@ -897,7 +919,7 @@ window.__ModuleLoader__.load({
897
919
  rows.push(h('div', { 'data-dam-row': '', style: { flexWrap: 'wrap' } },
898
920
  Object.keys(QUADRANT_STYLE).map(function (q) {
899
921
  return h('span', { key: q, style: { fontSize: 'calc(11px * var(--dam-scale))', opacity: 0.8, marginRight: '10px' } },
900
- h('span', { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: QUADRANT_STYLE[q].color, marginRight: 4 } }), QUADRANT_STYLE[q].label)
922
+ h('span', { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: QUADRANT_STYLE[q].color, marginRight: 4 } }), t(QUADRANT_I18N[q]))
901
923
  })))
902
924
  // 星期表头
903
925
  rows.push(h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '3px', marginBottom: '4px' } },
@@ -970,7 +992,7 @@ window.__ModuleLoader__.load({
970
992
  h('div', { 'data-dam-row': '' },
971
993
  h('input', { 'data-dam-input': '', type: 'time', value: draft.time, onChange: function (e) { var n = Object.assign({}, draft); n.time = e.target.value || '09:00'; setDraft(n) } }),
972
994
  h('select', { 'data-dam-select': '', value: draft.quadrant, onChange: function (e) { var n = Object.assign({}, draft); n.quadrant = e.target.value; setDraft(n) } },
973
- ['重要紧急', '重要不紧急', '紧急不重要', '不重要不紧急'].map(function (q) { return h('option', { key: q, value: q }, QUADRANT_STYLE[q].label) }))),
995
+ ['重要紧急', '重要不紧急', '紧急不重要', '不重要不紧急'].map(function (q) { return h('option', { key: q, value: q }, t(QUADRANT_I18N[q])) }))),
974
996
  h('div', { 'data-dam-row': '' },
975
997
  h('button', { 'data-dam-btn': '', onClick: saveDraft }, t('save')),
976
998
  h('button', { 'data-dam-btn': '', onClick: function () { setDraft(null) } }, t('cancel')))))
@@ -1175,23 +1197,23 @@ window.__ModuleLoader__.load({
1175
1197
  h('div', { 'data-dam-row': '' },
1176
1198
  h('button', { 'data-dam-btn': '', onClick: refresh, disabled: busy }, busy ? t('dbgRefreshing') : t('dbgRefresh')),
1177
1199
  h('span', { 'data-dam-hint': '' }, data.host.indexPath || '')),
1178
- kv('插件版本', data.host.version || '(读不到)', false),
1179
- kv('PID / 启动', data.host.pid + ' / ' + (data.host.startTime ? new Date(data.host.startTime).toLocaleTimeString() : ''), false),
1180
- kv('Host 需重启?', data.host.needsRestart ? '是(代码比进程新)' : '', data.host.needsRestart),
1181
- kv('轮询心跳', hb.exists ? (hbAge >= 0 ? '存活(上次 ' + hbAge + ' 秒前)' : '存活') : '无心跳文件', !hb.exists || hbAge > 360),
1182
- kv('沉淀重试队列', data.autoConsolidate.pendingQueue + '', data.autoConsolidate.pendingQueue > 0),
1183
- kv('今日沉淀', data.autoConsolidate.stats.count + ' 条 / 最近 ' + (data.autoConsolidate.stats.lastAt ? new Date(data.autoConsolidate.stats.lastAt).toLocaleTimeString() : ''), false),
1184
- kv('正在沉淀中', data.autoConsolidate.consolidating ? '' : '', data.autoConsolidate.consolidating),
1185
- kv('subagents', data.subagents.available ? (data.subagents.providers.join(', ') || '可用') : '不可用', !data.subagents.available),
1186
- kv('项目笔记重复标题', data.duplicateHeadings + '', data.duplicateHeadings > 0),
1187
- kv('记忆文件', '用户 ' + (data.memoryFiles.user.exists ? fmtSize(data.memoryFiles.user.size) : '缺失') + ' · 笔记 ' + (data.memoryFiles.notes.exists ? fmtSize(data.memoryFiles.notes.size) : '缺失') + ' · 日志 ' + (data.memoryFiles.log.exists ? fmtSize(data.memoryFiles.log.size) : '缺失'), !data.memoryFiles.log.exists),
1188
- kv('当前工作区', currentWs() || '(未取到)', false),
1189
- kv('API 连通', probes ? Object.keys(probes).map(function (k) { return k + '=' + (probes[k].status !== undefined ? probes[k].status : probes[k].error) }).join(' ') : '…', apiBad))
1200
+ kv(t('kvVersion'), data.host.version || t('kvUnreadable'), false),
1201
+ kv(t('kvPid'), data.host.pid + ' / ' + (data.host.startTime ? new Date(data.host.startTime).toLocaleTimeString() : ''), false),
1202
+ kv(t('kvRestart'), data.host.needsRestart ? t('kvRestartYes') : t('kvRestartNo'), data.host.needsRestart),
1203
+ kv(t('kvHeartbeat'), hb.exists ? (hbAge >= 0 ? t('hbAliveAgo') + hbAge + t('hbSecAgo') : t('hbAlive')) : t('hbNone'), !hb.exists || hbAge > 360),
1204
+ kv(t('kvQueue'), data.autoConsolidate.pendingQueue + t('kvCountSuffix'), data.autoConsolidate.pendingQueue > 0),
1205
+ kv(t('kvToday'), data.autoConsolidate.stats.count + t('kvRecentPrefix') + (data.autoConsolidate.stats.lastAt ? new Date(data.autoConsolidate.stats.lastAt).toLocaleTimeString() : ''), false),
1206
+ kv(t('kvBusy'), data.autoConsolidate.consolidating ? t('kvYes') : t('kvNo'), data.autoConsolidate.consolidating),
1207
+ kv('subagents', data.subagents.available ? (data.subagents.providers.join(', ') || t('kvAvail')) : t('kvUnavail'), !data.subagents.available),
1208
+ kv(t('kvDup'), data.duplicateHeadings + t('kvDupCount'), data.duplicateHeadings > 0),
1209
+ kv(t('kvMem'), t('kvMemUser') + ' ' + (data.memoryFiles.user.exists ? fmtSize(data.memoryFiles.user.size) : t('kvMemMissing')) + ' · ' + t('kvMemNotes') + ' ' + (data.memoryFiles.notes.exists ? fmtSize(data.memoryFiles.notes.size) : t('kvMemMissing')) + ' · ' + t('kvMemLog') + ' ' + (data.memoryFiles.log.exists ? fmtSize(data.memoryFiles.log.size) : t('kvMemMissing')), !data.memoryFiles.log.exists),
1210
+ kv(t('kvWs'), currentWs() || t('kvWsUnknown'), false),
1211
+ kv(t('kvApi'), probes ? Object.keys(probes).map(function (k) { return k + '=' + (probes[k].status !== undefined ? probes[k].status : probes[k].error) }).join(' ') : '…', apiBad))
1190
1212
  }
1191
1213
 
1192
1214
  // ───────────────────────── 设置页 ─────────────────────────
1193
- var STYLE_OPTIONS = [['auto', t('styleAuto')], ['life', t('styleLife')], ['professional', t('styleProfessional')]]
1194
- var LOCALE_OPTIONS = [['zh', t('zh')], ['en', t('en')]]
1215
+ var STYLE_IDS = ['auto', 'life', 'professional']
1216
+ var LOCALE_IDS_LIST = ['system', 'zh', 'en']
1195
1217
  function SettingsPage() {
1196
1218
  var tickPair = useTick()
1197
1219
  var cfgPair = useState(null)
@@ -1246,6 +1268,18 @@ window.__ModuleLoader__.load({
1246
1268
  browseTo(cfg.memoryRoot || '')
1247
1269
  })
1248
1270
  }
1271
+ var verPair = useState(null)
1272
+ var verInfo = verPair[0]
1273
+ var setVerInfo = verPair[1]
1274
+ var checkingPair = useState(false)
1275
+ var checkingUpdate = checkingPair[0]
1276
+ var setCheckingUpdate = checkingPair[1]
1277
+ var upBusyPair = useState(false)
1278
+ var upBusy = upBusyPair[0]
1279
+ var setUpBusy = upBusyPair[1]
1280
+ var upMsgPair = useState('')
1281
+ var upMsg = upMsgPair[0]
1282
+ var setUpMsg = upMsgPair[1]
1249
1283
  useEffect(function () { return controller.subscribe(tickPair[1]) }, [])
1250
1284
  useEffect(function () {
1251
1285
  var alive = true
@@ -1256,37 +1290,25 @@ window.__ModuleLoader__.load({
1256
1290
  }, [])
1257
1291
  if (!cfg) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
1258
1292
  function set(key, value) { var next = Object.assign({}, cfg); next[key] = value; setCfg(next) }
1259
- var verPair = useState(null)
1260
- var verInfo = verPair[0]
1261
- var setVerInfo = verPair[1]
1262
- var checkingPair = useState(false)
1263
- var checkingUpdate = checkingPair[0]
1264
- var setCheckingUpdate = checkingPair[1]
1265
1293
  function checkUpdate() {
1266
1294
  if (checkingUpdate) return
1267
1295
  setCheckingUpdate(true)
1268
1296
  apiGet(API.updateCheck + '?force=1').then(function (d) { setVerInfo(d); setCheckingUpdate(false) })
1269
1297
  .catch(function (e) { setVerInfo({ error: e.message }); setCheckingUpdate(false) })
1270
1298
  }
1271
- var upBusyPair = useState(false)
1272
- var upBusy = upBusyPair[0]
1273
- var setUpBusy = upBusyPair[1]
1274
- var upMsgPair = useState('')
1275
- var upMsg = upMsgPair[0]
1276
- var setUpMsg = upMsgPair[1]
1277
1299
  function doUpdate() {
1278
1300
  if (upBusy) return
1279
1301
  setUpBusy(true); setUpMsg('')
1280
1302
  apiPost(API.update, {}).then(function (d) {
1281
1303
  setUpBusy(false)
1282
1304
  if (d && d.ok) { setUpMsg(t('updateDone')); setVerInfo(null); checkUpdate() }
1283
- else setUpMsg(t('updateFailed') + (d && d.message ? d.message : '(未知)'))
1305
+ else setUpMsg(t('updateFailed') + (d && d.message ? d.message : t('unknown')))
1284
1306
  }).catch(function (e) { setUpBusy(false); setUpMsg(t('updateFailed') + e.message) })
1285
1307
  }
1286
1308
  function save() {
1287
1309
  if (busy) return
1288
1310
  setBusy(true); setMsg(''); setErr('')
1289
- apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false) })
1311
+ apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false); if (d && d.config && d.config.locale) applyLocalePref(d.config.locale) })
1290
1312
  .catch(function (e) { setErr(e.message); setBusy(false) })
1291
1313
  }
1292
1314
  function field(label, control, hint) {
@@ -1296,8 +1318,8 @@ window.__ModuleLoader__.load({
1296
1318
  }
1297
1319
  return h('div', null,
1298
1320
  h('div', { 'data-dam-hint': '' }, t('settingsHeader')),
1299
- field(t('fLocale'), h('select', { 'data-dam-select': '', value: cfg.locale || 'zh', onChange: function (e) { set('locale', e.target.value); setLocale(e.target.value) } },
1300
- LOCALE_OPTIONS.map(function (o) { return h('option', { key: o[0], value: o[0] }, o[1]) })), t('fLocaleHint')),
1321
+ field(t('fLocale'), h('select', { 'data-dam-select': '', value: cfg.locale || 'system', onChange: function (e) { set('locale', e.target.value) } },
1322
+ LOCALE_IDS_LIST.map(function (id) { return h('option', { key: id, value: id }, id === 'system' ? t('followSystem') : t(id)) })), t('fLocaleHint')),
1301
1323
  field(t('fFontSize'), h('select', { 'data-dam-select': '', value: fontScale, onChange: function (e) { fontScale = e.target.value; try { localStorage.setItem('dsh-auto-memory.fontScale.v2', fontScale) } catch (ee) {}; try { var pp = document.querySelector('[data-dam-panel]'); if (pp) pp.style.setProperty('--dam-scale', FONT_SCALE_VALUES[fontScale] || '1') } catch (ee2) {}; emit() } },
1302
1324
  Object.keys(FONT_SCALES).map(function (k) { return h('option', { key: k, value: k }, t('fs' + k.charAt(0).toUpperCase() + k.slice(1))) })), t('fFontSizeHint')),
1303
1325
  field(t('fUserDir'), h('input', { 'data-dam-input': '', value: cfg.userMemoryDir, onChange: function (e) { set('userMemoryDir', e.target.value) } }), t('fUserDirHint')),
@@ -1323,7 +1345,7 @@ window.__ModuleLoader__.load({
1323
1345
  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')),
1324
1346
  field(t('fReflect'), h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), t('fReflectHint')),
1325
1347
  field(t('fStyle'), h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
1326
- STYLE_OPTIONS.map(function (o) { return h('option', { key: o[0], value: o[0] }, o[1]) })), t('fStyleHint')),
1348
+ STYLE_IDS.map(function (id) { return h('option', { key: id, value: id }, t('style' + id.charAt(0).toUpperCase() + id.slice(1))) })), t('fStyleHint')),
1327
1349
  field(t('fVersion'), h('div', { 'data-dam-row': '' },
1328
1350
  h('span', { style: { flex: 1 } }, verInfo ? (verInfo.current || '?') + (verInfo.latest ? ' → ' + verInfo.latest + (verInfo.upToDate ? ' ' + t('upToDate') : ' ' + t('hasUpdate')) : '') : (checkingUpdate ? t('checking') : '—')),
1329
1351
  h('button', { 'data-dam-btn': '', onClick: checkUpdate, disabled: checkingUpdate }, checkingUpdate ? t('checking') : t('checkUpdate')),
@@ -1360,33 +1382,50 @@ window.__ModuleLoader__.load({
1360
1382
  var savedScale = localStorage.getItem('dsh-auto-memory.fontScale.v2')
1361
1383
  if (savedScale in FONT_SCALES) fontScale = savedScale
1362
1384
  } catch (e3) {}
1363
- // 初始化界面语言( host 配置读取,持久化到 localStorage 以便即时生效)
1385
+ // 界面语言:默认跟随 DSH 系统语言(config.locale 可手动指定 zh / en / system)
1364
1386
  try {
1365
- var savedLocale = localStorage.getItem('dsh-auto-memory.locale')
1366
- if (savedLocale === 'zh' || savedLocale === 'en') locale = savedLocale
1367
- apiGet(API.config).then(function (d) {
1368
- if (d && d.config && d.config.locale) {
1369
- var l = d.config.locale
1370
- if (l !== locale) { locale = l; try { localStorage.setItem('dsh-auto-memory.locale', l) } catch (e2) {}; emit() }
1387
+ var sl0 = ctx.locale && ctx.locale.getLocale ? ctx.locale.getLocale() : null
1388
+ if (sl0 && sl0.active) sysLocale = sl0.active
1389
+ } catch (e) {}
1390
+ applyLocalePref('system')
1391
+ apiGet(API.config).then(function (d) {
1392
+ if (d && d.config && d.config.locale) applyLocalePref(d.config.locale)
1393
+ }).catch(function () {})
1394
+ // DSH 系统语言变化:跟随更新 + 重渲染 + 重注册入口 label
1395
+ ctx.on('locale/change', function (snap) {
1396
+ try {
1397
+ if (snap && snap.active && snap.active !== sysLocale) {
1398
+ sysLocale = snap.active
1399
+ if (localeMode !== 'zh' && localeMode !== 'en') { locale = sysLocale; emit() }
1400
+ refreshSurfaces()
1371
1401
  }
1372
- }).catch(function () {})
1373
- } catch (e1) {}
1402
+ } catch (e) {}
1403
+ })
1374
1404
  // 语言切换时通知所有订阅者重渲染
1375
1405
  onLocale(function () { emit() })
1376
1406
 
1377
- try {
1378
- slots.inject('sidebar.footer.action', function () {
1379
- return slots.register({ name: 'sidebar.footer.action', id: 'auto-memory', order: 5, label: t('memory') }, function () { return h(SidebarButton) })
1380
- })
1381
- slots.inject('shell.overlay', function () {
1382
- return slots.register({ name: 'shell.overlay', id: 'auto-memory', order: 5 }, function () { return h(MemoryPanel) })
1383
- })
1384
- slots.inject('settings.section', function () {
1385
- return slots.register({ name: 'settings.section', id: 'auto-memory', order: 25, label: t('autoMemory') }, function (props) { return h(SettingsPage, { close: props && props.close }) })
1386
- })
1387
- } catch (e) {
1388
- console.warn('[dsh-auto-memory] slot registration failed', e)
1407
+ var surfaceDisposers = []
1408
+ function registerSurfaces() {
1409
+ try {
1410
+ surfaceDisposers.push(slots.inject('sidebar.footer.action', function () {
1411
+ return slots.register({ name: 'sidebar.footer.action', id: 'auto-memory', order: 5, label: t('memory') }, function () { return h(SidebarButton) })
1412
+ }))
1413
+ surfaceDisposers.push(slots.inject('shell.overlay', function () {
1414
+ return slots.register({ name: 'shell.overlay', id: 'auto-memory', order: 5 }, function () { return h(MemoryPanel) })
1415
+ }))
1416
+ surfaceDisposers.push(slots.inject('settings.section', function () {
1417
+ return slots.register({ name: 'settings.section', id: 'auto-memory', order: 25, label: t('autoMemory') }, function (props) { return h(SettingsPage, { close: props && props.close }) })
1418
+ }))
1419
+ } catch (e) {
1420
+ console.warn('[dsh-auto-memory] slot registration failed', e)
1421
+ }
1422
+ }
1423
+ function refreshSurfaces() {
1424
+ for (var i = 0; i < surfaceDisposers.length; i++) { try { surfaceDisposers[i]() } catch (e) {} }
1425
+ surfaceDisposers = []
1426
+ registerSurfaces()
1389
1427
  }
1428
+ registerSurfaces()
1390
1429
  console.log('[dsh-auto-memory] client ready: sidebar entry + panel + settings page')
1391
1430
  }
1392
1431
 
package/lib/index.js CHANGED
@@ -85,8 +85,8 @@ const DEFAULT_CONFIG = {
85
85
  reflectEnabled: true,
86
86
  /** 反思风格: auto=由内容决定 / life=生活化 / professional=专业性。 */
87
87
  reflectStyle: 'auto',
88
- /** UI 语言: zh=中文 / en=English。 */
89
- locale: 'zh',
88
+ /** UI 语言: system=跟随 DSH 系统语言(默认) / zh=中文 / en=English。 */
89
+ locale: 'system',
90
90
  /** 外部记忆注入预算(字符)。 */
91
91
  externalInjectionChars: 1400,
92
92
  /** 外部记忆源开关(AI 助手/CodeBuddy/Claude Code/Codex/项目约定)。 */
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.10",
4
+ "version": "0.1.11",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {