@a9i5k4/dsh-auto-memory 0.1.6 → 0.1.8
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 +36 -9
- package/README.zh-CN.md +35 -8
- package/lib/client.js +78 -43
- package/lib/index.js +56 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# dsh-auto-memory — DSH Auto Memory Plugin / DSH 自动记忆插件
|
|
2
2
|
|
|
3
|
-
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, a
|
|
3
|
+
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, smart period greetings, a calendar view and settings page, and inheritance of memories from other AI tools.
|
|
4
4
|
|
|
5
|
-
DSH Web GUI
|
|
5
|
+
DSH Web GUI 的记忆插件:三层记忆自动注入与检索、每日反思、智能时段问候、日历视图与设置页,支持继承其他 AI 工具的历史记忆。
|
|
6
6
|
|
|
7
7
|
[**English**](README.md) | [中文版](README.zh-CN.md)
|
|
8
8
|
|
|
@@ -10,18 +10,45 @@ DSH Web GUI 的记忆插件:三层记忆(用户级 / 项目笔记 / 每日
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
+
### Three-layer Memory
|
|
14
|
+
|
|
13
15
|
| Layer | Location | Description |
|
|
14
16
|
|---|---|---|
|
|
15
17
|
| User-level memory | `~/.dsh/memory/MEMORY.md` | Cross-project rules & preferences |
|
|
16
18
|
| Project notes | `{workspace}/.dsh-memory/MEMORY.md` | Project conventions & decisions |
|
|
17
19
|
| Daily logs | `{workspace}/.dsh-memory/YYYY-MM-DD.md` | Append-only work log |
|
|
18
|
-
| Reflections | `{workspace}/.dsh-memory/reflections/YYYY-MM-DD.md` | Daily reflection,
|
|
20
|
+
| Reflections | `{workspace}/.dsh-memory/reflections/YYYY-MM-DD.md` | Daily reflection (structured, kept in background) |
|
|
21
|
+
|
|
22
|
+
- **Auto injection**: injects a `<memory_system>` block into every system prompt (user rules + project notes + recent reflections + recent N days of log tails + pending calendar items + writing discipline)
|
|
23
|
+
- **Visible memory ops**: when the AI updates or searches memory, it says so in plain text in the chat reply (e.g. "Logged X to today's journal", "I checked memory and found..."), not hidden inside tool calls
|
|
24
|
+
|
|
25
|
+
### Smart Period Greetings (Overview page)
|
|
26
|
+
|
|
27
|
+
The first thing you see when opening the memory panel is a friendly period greeting, not technical info:
|
|
28
|
+
|
|
29
|
+
- **Multiple times a day**: morning shows "yesterday summary + early morning", forenoon shows the morning's work, noon/afternoon/evening follow suit
|
|
30
|
+
- **Smart timing**: if you were away for more than 1 hour (off work / stepped away) and come back, it automatically shows "Welcome back" with what was finished meanwhile
|
|
31
|
+
- **Drawer layout**: the outer layer stays casual and greeting-like; each period is a drawer — tap to expand the professional details
|
|
32
|
+
- **Daily reflection stays in background**: structured reflections (results / lessons / next steps) are kept, while the front page only shows a light greeting
|
|
33
|
+
|
|
34
|
+
### Calendar View (Four Quadrants)
|
|
35
|
+
|
|
36
|
+
New 「Calendar」tab (liquid-glass monthly view):
|
|
37
|
+
|
|
38
|
+
- Monthly grid, today highlighted, click any date to add an item
|
|
39
|
+
- **Four-quadrant colors**: Urgent & Important (red) / Important (blue) / Urgent (orange) / Neither (gray)
|
|
40
|
+
- Click an item to toggle done, click again to delete; legend + weekday header
|
|
41
|
+
- **Cross-conversation persistence**: data lives at user level `~/.dsh/memory/CALENDAR.md`, shared across workspaces, survives DSH reinstall
|
|
42
|
+
- **AI-maintained**: the AI extracts deadlines and appointments from conversations and writes them to the calendar automatically (`calendar_add` / `calendar_list` / `calendar_done` / `calendar_remove`), restating it in plain text; pending items are injected into every session's system prompt
|
|
43
|
+
|
|
44
|
+
### Agent Tools
|
|
45
|
+
|
|
46
|
+
`memory_log` / `memory_note` / `memory_user` / `memory_recall` / `memory_external` / `memory_maintain` / `memory_status` / `memory_reflect` / `calendar_add` / `calendar_list` / `calendar_done` / `calendar_remove`
|
|
47
|
+
|
|
48
|
+
### UI
|
|
19
49
|
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- **Agent tools**: `memory_log` / `memory_note` / `memory_user` / `memory_recall` / `memory_external` / `memory_maintain` / `memory_status` / `memory_reflect`
|
|
23
|
-
- **GUI**: sidebar 「Memory」entry → floating panel (Overview / Logs / Notes / Reflections / Connect / Search); Settings page (Settings → Auto Memory)
|
|
24
|
-
- **UI language**: switch between 中文 / English in Settings → Auto Memory → UI language
|
|
50
|
+
- Sidebar 「Memory」entry → floating panel (Overview / Logs / Notes / Reflections / Connect / Calendar / Search)
|
|
51
|
+
- Settings page (Settings → Auto Memory): storage paths, injection budget, reflection style, **UI language (中文 / English)**
|
|
25
52
|
- **External memory inheritance**: import memories accumulated by other AI tools (CodeBuddy / Claude Code / Codex / project convention files)
|
|
26
53
|
|
|
27
54
|
---
|
|
@@ -86,7 +113,7 @@ Adjustable in the GUI (Settings → Auto Memory), including the UI language (zh
|
|
|
86
113
|
## Structure
|
|
87
114
|
|
|
88
115
|
- `lib/index.js` — Host half: engine, injection, tools, routes (zero runtime deps, Node built-ins only)
|
|
89
|
-
- `lib/client.js` — Browser half: memory panel + settings page (built-in zh/en i18n)
|
|
116
|
+
- `lib/client.js` — Browser half: memory panel (with calendar view) + settings page (built-in zh/en i18n)
|
|
90
117
|
- `cordis.patch.yml` — Plugin row (`auto-memory`)
|
|
91
118
|
|
|
92
119
|
---
|
package/README.zh-CN.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-auto-memory — DSH 自动记忆插件
|
|
2
2
|
|
|
3
|
-
DSH Web GUI 的记忆插件:三层记忆(用户级 / 项目笔记 /
|
|
3
|
+
DSH Web GUI 的记忆插件:三层记忆(用户级 / 项目笔记 / 每日日志)自动注入与检索、每日反思、智能时段问候、日历视图与设置页,支持继承其他 AI 工具的历史记忆。
|
|
4
4
|
|
|
5
5
|
[English](README.md) | [中文版](README.zh-CN.md)
|
|
6
6
|
|
|
@@ -8,18 +8,45 @@ DSH Web GUI 的记忆插件:三层记忆(用户级 / 项目笔记 / 每日
|
|
|
8
8
|
|
|
9
9
|
## 功能
|
|
10
10
|
|
|
11
|
+
### 三层记忆
|
|
12
|
+
|
|
11
13
|
| 层 | 位置 | 说明 |
|
|
12
14
|
|---|---|---|
|
|
13
15
|
| 用户级记忆 | `~/.dsh/memory/MEMORY.md` | 跨项目规则/偏好(用户明确要求时写) |
|
|
14
16
|
| 项目笔记 | `{工作区}/.dsh-memory/MEMORY.md` | 项目长期约定、决策、架构要点 |
|
|
15
17
|
| 每日日志 | `{工作区}/.dsh-memory/YYYY-MM-DD.md` | append-only 工作日志 |
|
|
16
|
-
| 反思 | `{工作区}/.dsh-memory/reflections/YYYY-MM-DD.md` |
|
|
18
|
+
| 反思 | `{工作区}/.dsh-memory/reflections/YYYY-MM-DD.md` | 每日反思(后台结构化积累) |
|
|
19
|
+
|
|
20
|
+
- **自动注入**:每次组装系统提示词时注入 `<memory_system>` 块(用户规则 + 项目笔记 + 最近反思 + 最近 N 天日志尾部 + 未完成日历事项 + 写入纪律)
|
|
21
|
+
- **记忆操作可见**:更新/检索记忆时,AI 会在对话正文中明文说明(如"已把 X 记入今日日志""我查了记忆,发现…"),不藏在工具调用里
|
|
22
|
+
|
|
23
|
+
### 智能时段问候(概览页)
|
|
24
|
+
|
|
25
|
+
打开记忆面板第一眼看到的是生活化的时段问候,而不是严肃的技术信息:
|
|
26
|
+
|
|
27
|
+
- **一天多次**:早晨显示"昨天摘要 + 今天早晨"、上午显示上午的工作、中午/下午/晚上依次类推
|
|
28
|
+
- **智能时机**:离开超过 1 小时(下班/暂离)再打开,自动显示"欢迎回来"并列出期间的完成事项
|
|
29
|
+
- **抽屉式展开**:外层保持生活化问候风格;每个时段做成抽屉,点开才显示专业的工作明细
|
|
30
|
+
- **每日反思**:后台保留结构化反思(成果/教训/要点),前台只有轻松问候
|
|
31
|
+
|
|
32
|
+
### 日历视图(四象限)
|
|
33
|
+
|
|
34
|
+
新增「日历」页签(液态玻璃风格月视图):
|
|
35
|
+
|
|
36
|
+
- 月视图网格,今日高亮,点击任意日期添加事项
|
|
37
|
+
- **四象限色标**:重要紧急(红)/ 重要不紧急(蓝)/ 紧急不重要(橙)/ 不重要不紧急(灰)
|
|
38
|
+
- 点条目切换完成状态,再点删除;图例 + 星期头
|
|
39
|
+
- **跨对话持久**:数据存用户级 `~/.dsh/memory/CALENDAR.md`,所有工作区共享,重装 DSH 不丢
|
|
40
|
+
- **AI 主动维护**:AI 会从对话中提取 deadline、约定时间等自动写入日历(`calendar_add` / `calendar_list` / `calendar_done` / `calendar_remove`),并在正文转述;未完成事项注入每次会话的系统提示词
|
|
41
|
+
|
|
42
|
+
### Agent 工具
|
|
43
|
+
|
|
44
|
+
`memory_log` / `memory_note` / `memory_user` / `memory_recall` / `memory_external` / `memory_maintain` / `memory_status` / `memory_reflect` / `calendar_add` / `calendar_list` / `calendar_done` / `calendar_remove`
|
|
45
|
+
|
|
46
|
+
### 界面
|
|
17
47
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- **Agent 工具**:`memory_log` / `memory_note` / `memory_user` / `memory_recall` / `memory_external` / `memory_maintain` / `memory_status` / `memory_reflect`
|
|
21
|
-
- **GUI**:侧边栏「记忆」入口 → 浮层面板(概览/日志/笔记/反思/接续/检索);设置页(设置 → 自动记忆)
|
|
22
|
-
- **界面语言**:中文 / English 可切换(设置 → 自动记忆 → 界面语言)
|
|
48
|
+
- 侧边栏「记忆」入口 → 浮层面板(概览/日志/笔记/反思/接续/日历/检索)
|
|
49
|
+
- 设置页(设置 → 自动记忆):存储位置、注入预算、反思风格、**界面语言(中文 / English)**
|
|
23
50
|
- **外部记忆继承**:接入其他 AI 工具(CodeBuddy / Claude Code / Codex / 项目约定文件)积累的记忆
|
|
24
51
|
|
|
25
52
|
---
|
|
@@ -84,7 +111,7 @@ pnpm add @a9i5k4/dsh-auto-memory
|
|
|
84
111
|
## 结构
|
|
85
112
|
|
|
86
113
|
- `lib/index.js` — Host 半:引擎、注入、工具、路由(零运行时依赖,仅 node 内置模块)
|
|
87
|
-
- `lib/client.js` —
|
|
114
|
+
- `lib/client.js` — 浏览器半:记忆面板(含日历视图)+ 设置页(内置中英双语)
|
|
88
115
|
- `cordis.patch.yml` — 插件行(`auto-memory`)
|
|
89
116
|
|
|
90
117
|
---
|
package/lib/client.js
CHANGED
|
@@ -148,6 +148,9 @@ window.__ModuleLoader__.load({
|
|
|
148
148
|
segMorning: '早晨', segForenoon: '上午', segNoon: '中午', segAfternoon: '下午', segEvening: '晚上',
|
|
149
149
|
segPrefix: '今日', segMorningHint: '(昨日摘要)',
|
|
150
150
|
welcomeBack: '欢迎回来!这段时间你完成了这些工作:',
|
|
151
|
+
yesterdayDrawer: '昨天',
|
|
152
|
+
greetMorning: '新的一天开始啦,先看看昨天做了什么。', greetForenoon: '上午好,今天也在稳步推进。', greetNoon: '中午好,歇口气再继续。', greetAfternoon: '下午好,下午也要元气满满。', greetEvening: '晚上好,辛苦一天了。',
|
|
153
|
+
greetSummary: '今天已经完成了 ', greetThings: ' 件工作,点开看看:',
|
|
151
154
|
qUrgentImportant: '重要紧急', qImportant: '重要不紧急', qUrgent: '紧急不重要', qNone: '不重要不紧急', qUncategorized: '未分类',
|
|
152
155
|
},
|
|
153
156
|
en: {
|
|
@@ -206,6 +209,9 @@ window.__ModuleLoader__.load({
|
|
|
206
209
|
segMorning: 'Morning', segForenoon: 'Forenoon', segNoon: 'Noon', segAfternoon: 'Afternoon', segEvening: 'Evening',
|
|
207
210
|
segPrefix: 'Today ', segMorningHint: ' (yesterday summary)',
|
|
208
211
|
welcomeBack: 'Welcome back! While you were away, you finished:',
|
|
212
|
+
yesterdayDrawer: 'Yesterday',
|
|
213
|
+
greetMorning: 'A fresh day! Let us look back at yesterday first.', greetForenoon: 'Good morning, steady progress today.', greetNoon: 'Good noon, take a break and keep going.', greetAfternoon: 'Good afternoon, keep up the good energy.', greetEvening: 'Good evening, well done today.',
|
|
214
|
+
greetSummary: 'You have completed ', greetThings: ' things today. Tap to expand:',
|
|
209
215
|
qUrgentImportant: 'Urgent & Important', qImportant: 'Important', qUrgent: 'Urgent', qNone: 'Neither', qUncategorized: 'Uncategorized',
|
|
210
216
|
}
|
|
211
217
|
}
|
|
@@ -227,6 +233,7 @@ window.__ModuleLoader__.load({
|
|
|
227
233
|
note: '/api/dsh-auto-memory/note',
|
|
228
234
|
external: '/api/dsh-auto-memory/external',
|
|
229
235
|
externalImport: '/api/dsh-auto-memory/external-import',
|
|
236
|
+
summarize: '/api/dsh-auto-memory/summarize',
|
|
230
237
|
}
|
|
231
238
|
function query(params) {
|
|
232
239
|
var search = new URLSearchParams()
|
|
@@ -350,59 +357,87 @@ window.__ModuleLoader__.load({
|
|
|
350
357
|
var g = props.greeting
|
|
351
358
|
var ps = props.periodSummary
|
|
352
359
|
if (!g) return null
|
|
360
|
+
var openPair = useState({})
|
|
361
|
+
var openMap = openPair[0]
|
|
362
|
+
var setOpenMap = openPair[1]
|
|
353
363
|
// 智能时段判定
|
|
354
364
|
var hour = new Date().getHours()
|
|
355
365
|
var seg = hour < 9 ? 'morning' : hour < 12 ? 'forenoon' : hour < 14 ? 'noon' : hour < 18 ? 'afternoon' : 'evening'
|
|
356
366
|
var segLabel = { morning: t('segMorning'), forenoon: t('segForenoon'), noon: t('segNoon'), afternoon: t('segAfternoon'), evening: t('segEvening') }[seg]
|
|
357
|
-
|
|
367
|
+
// 收集抽屉: {key, label, hint, items, withTime}
|
|
368
|
+
var drawers = []
|
|
369
|
+
var curSegName = { morning: '早晨', forenoon: '上午', noon: '中午', afternoon: '下午', evening: '晚上' }[seg]
|
|
370
|
+
// 昨天抽屉(早晨显示)
|
|
371
|
+
if (seg === 'morning' && g.entries.length) {
|
|
372
|
+
drawers.push({ key: 'yesterday', label: t('yesterdayDrawer') + (g.yesterdayDate || ''), hint: '', items: g.entries, withTime: true, defaultOpen: true })
|
|
373
|
+
}
|
|
374
|
+
// 今天各时段抽屉(已过的时段)
|
|
375
|
+
var seenSeg = { '早晨': seg !== 'morning', '上午': (seg === 'forenoon' || seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '中午': (seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '下午': (seg === 'afternoon' || seg === 'evening'), '晚上': seg === 'evening' }
|
|
376
|
+
var segOrder = ['早晨', '上午', '中午', '下午', '晚上']
|
|
377
|
+
for (var si = 0; si < segOrder.length; si++) {
|
|
378
|
+
var sname = segOrder[si]
|
|
379
|
+
if (!seenSeg[sname]) continue
|
|
380
|
+
var items = (ps && ps.groups && ps.groups[sname]) || []
|
|
381
|
+
if (!items.length) continue
|
|
382
|
+
drawers.push({ key: 'seg-' + sname, label: t('segPrefix') + sname, hint: '', items: items.map(function (x) { return { time: '', text: x } }), withTime: false, defaultOpen: sname === curSegName })
|
|
383
|
+
}
|
|
384
|
+
// 外层:生活化问候
|
|
385
|
+
var totalCount = 0
|
|
386
|
+
for (var d0 = 0; d0 < drawers.length; d0++) totalCount += drawers[d0].items.length
|
|
387
|
+
var greetLine = g.greeting && String(g.greeting).trim()
|
|
388
|
+
? String(g.greeting).trim()
|
|
389
|
+
: (function () {
|
|
390
|
+
if (away()) return t('welcomeBack')
|
|
391
|
+
var base = { morning: t('greetMorning'), forenoon: t('greetForenoon'), noon: t('greetNoon'), afternoon: t('greetAfternoon'), evening: t('greetEvening') }[seg]
|
|
392
|
+
if (totalCount > 0) base += t('greetSummary') + totalCount + t('greetThings')
|
|
393
|
+
return base
|
|
394
|
+
})()
|
|
395
|
+
// 离开>1小时后回来
|
|
396
|
+
function away() {
|
|
397
|
+
var lastSeen = 0
|
|
398
|
+
try { lastSeen = Number(localStorage.getItem('dsh-auto-memory.lastActive') || 0) } catch (e) {}
|
|
399
|
+
return lastSeen > 0 && (Date.now() - lastSeen) > 3600000
|
|
400
|
+
}
|
|
358
401
|
var rows = []
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
var
|
|
363
|
-
var
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
var seen = { '凌晨': true, '早晨': seg === 'morning' ? false : true, '上午': (seg === 'forenoon' || seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '中午': (seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '下午': (seg === 'afternoon' || seg === 'evening'), '晚上': seg === 'evening' }
|
|
370
|
-
var segRows = []
|
|
371
|
-
var segOrder = ['早晨', '上午', '中午', '下午', '晚上']
|
|
372
|
-
for (var si = 0; si < segOrder.length; si++) {
|
|
373
|
-
var sname = segOrder[si]
|
|
374
|
-
if (!seen[sname]) continue
|
|
375
|
-
var items = (ps.groups && ps.groups[sname]) || []
|
|
376
|
-
if (!items.length) continue
|
|
377
|
-
if (segRows.length) segRows.push(h('div', { 'data-dam-hint': '' }, ''))
|
|
378
|
-
segRows.push(h('div', { 'data-dam-hint': '' }, t('segPrefix') + sname + (sname === '早晨' ? t('segMorningHint') : '') + ':'))
|
|
379
|
-
for (var ii = 0; ii < items.length; ii++) {
|
|
380
|
-
(function (txt) {
|
|
381
|
-
segRows.push(h('div', { 'data-dam-row': '', style: { marginBottom: '3px', alignItems: 'flex-start' } },
|
|
382
|
-
h('span', { 'data-dam-muted': '', style: { flex: '0 0 10px', marginTop: '2px' } }, '·'),
|
|
383
|
-
h('span', { style: { flex: '1', wordBreak: 'break-word' } }, txt)))
|
|
384
|
-
})(items[ii])
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
if (segRows.length) rows.push(h('div', null, segRows))
|
|
402
|
+
rows.push(h('div', { 'data-dam-content': '' }, greetLine))
|
|
403
|
+
// 抽屉列表(AI 生活化总结缓存)
|
|
404
|
+
var sumCachePair = useState({})
|
|
405
|
+
var sumCache = sumCachePair[0]
|
|
406
|
+
var setSumCache = sumCachePair[1]
|
|
407
|
+
function requestSummary(key, period) {
|
|
408
|
+
if (sumCache[key] !== undefined) return
|
|
409
|
+
apiPost(API.summarize, { period: period }).then(function (d) {
|
|
410
|
+
if (d && d.result) { var n = Object.assign({}, sumCache); n[key] = d.result; setSumCache(n) }
|
|
411
|
+
}).catch(function () {})
|
|
388
412
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
413
|
+
for (var d = 0; d < drawers.length; d++) {
|
|
414
|
+
(function (dr) {
|
|
415
|
+
var isOpen = openMap[dr.key] !== undefined ? openMap[dr.key] : dr.defaultOpen
|
|
416
|
+
if (isOpen && sumCache[dr.key] === undefined) {
|
|
417
|
+
requestSummary(dr.key, dr.key === 'yesterday' ? '昨天' : dr.key.replace('seg-', ''))
|
|
418
|
+
}
|
|
419
|
+
rows.push(h('div', { key: dr.key, style: { marginTop: '8px' } },
|
|
420
|
+
h('button', {
|
|
421
|
+
'data-dam-btn': '',
|
|
422
|
+
onClick: function () { var n = Object.assign({}, openMap); n[dr.key] = !isOpen; setOpenMap(n) },
|
|
423
|
+
style: { width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '5px 8px', borderRadius: '7px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.08)) 45%, transparent)', border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.3)) 50%, transparent)' },
|
|
424
|
+
},
|
|
425
|
+
h('span', { style: { fontWeight: 600, fontSize: '12.5px' } }, (isOpen ? '▼ ' : '▶ ') + dr.label),
|
|
426
|
+
h('span', { 'data-dam-muted': '', style: { fontSize: '11.5px' } }, dr.items.length + t('logEntries'))),
|
|
427
|
+
isOpen ? h('div', { style: { padding: '6px 4px 2px 10px' } },
|
|
428
|
+
sumCache[dr.key] ? h('div', { style: { marginBottom: '8px', padding: '8px 10px', borderRadius: '8px', fontSize: '12.5px', lineHeight: 1.65, background: 'color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 8%, transparent)', borderLeft: '3px solid var(--dsw-alias-brand-primary, #4f7cff)' } }, sumCache[dr.key]) : null,
|
|
429
|
+
dr.items.map(function (en) {
|
|
430
|
+
return h('div', { key: (en.time || '') + en.text, style: { marginBottom: '3px', fontSize: '12.5px', lineHeight: 1.5 } },
|
|
431
|
+
dr.withTime && en.time ? h('span', { 'data-dam-muted': '', style: { fontFamily: 'monospace', fontSize: '11px', marginRight: '6px' } }, en.time) : null,
|
|
432
|
+
h('span', { style: { wordBreak: 'break-word' } }, en.text))
|
|
433
|
+
})) : null))
|
|
434
|
+
})(drawers[d])
|
|
399
435
|
}
|
|
400
436
|
// 待反思提醒
|
|
401
437
|
if (g.pendingReflectionDate) {
|
|
402
|
-
rows.push(h('div', { 'data-dam-hint': '' }, t('pendingReflectionShort') + g.pendingReflectionDate))
|
|
438
|
+
rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '8px' } }, t('pendingReflectionShort') + g.pendingReflectionDate))
|
|
403
439
|
}
|
|
404
|
-
|
|
405
|
-
return h(Card, { title: title }, rows)
|
|
440
|
+
return h(Card, { title: (g.period || '') + ' · ' + segLabel }, rows)
|
|
406
441
|
}
|
|
407
442
|
|
|
408
443
|
function OverviewTab() {
|
package/lib/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import path from 'node:path'
|
|
|
24
24
|
export const name = 'auto-memory'
|
|
25
25
|
|
|
26
26
|
/** Services required before the memory surfaces can mount. */
|
|
27
|
-
export const inject = ['webServer', 'tools', 'systemPrompt']
|
|
27
|
+
export const inject = ['webServer', 'tools', 'systemPrompt', 'subagents']
|
|
28
28
|
|
|
29
29
|
/** Prompt order of the memory section (before the tool-guidance band 100+). */
|
|
30
30
|
const SECTION_ORDER = 90
|
|
@@ -45,6 +45,7 @@ export const API = {
|
|
|
45
45
|
external: '/api/dsh-auto-memory/external',
|
|
46
46
|
'external-import': '/api/dsh-auto-memory/external-import',
|
|
47
47
|
calendar: '/api/dsh-auto-memory/calendar',
|
|
48
|
+
summarize: '/api/dsh-auto-memory/summarize',
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const DEFAULT_CONFIG = {
|
|
@@ -341,13 +342,18 @@ class MemoryEngine {
|
|
|
341
342
|
lines.push('\n[日历与日程(未完成)]\n' + calLines + '\n主动关注这些安排:对话中若提及相关时间点,主动用 calendar_add 补充新事项、calendar_done 标记完成;回复正文中向用户转述日历变更。')
|
|
342
343
|
}
|
|
343
344
|
}
|
|
345
|
+
// 暂离回来提示:距上次活动>1小时,要求 agent 在回复开头写欢迎语并提示打开记忆窗口
|
|
346
|
+
if (this._lastActiveAt && Date.now() - this._lastActiveAt > 3600000) {
|
|
347
|
+
lines.push('\n[欢迎回来]')
|
|
348
|
+
lines.push('用户离开已超过 1 小时(暂离/下班后回来)。在本轮回复的开头,先用一句简短温暖的话欢迎用户回来(如"欢迎回来!你离开的这段时间,我已经帮你把日志整理好了。"),然后提示"自动记忆窗口将打开,方便你了解这段时间的状况"(如已由 GUI 弹出概览则不必重复提示)。语气自然,一两句即可,不要长篇大论。')
|
|
349
|
+
}
|
|
344
350
|
lines.push('\n[记忆写入纪律 — 必须遵守]')
|
|
345
351
|
lines.push('- 会话开始:若任务与历史工作/历史决策相关,先回顾以上记忆;**遇到不熟悉的代码、领域或项目时,主动调用 memory_recall 检索本机所有 AI 工具的历史记忆(CodeBuddy/Claude Code/Codex 等会话),不要凭空猜测**。')
|
|
346
352
|
lines.push('- 新工作区(无历史日志/笔记):主动用 memory_recall 探索本机历史,判断该项目是否曾在其他 AI 工具中工作过;也可调用 memory_external 查看并接入外部记忆;检索时在正文中说明"我先查一下之前的记录"。')
|
|
347
353
|
lines.push('- 完成实质性工作后立即调用 memory_log 追加今日日志(append-only,绝不覆盖):建/改应用、修 bug、写文档、重构、技术选型、用户约定或偏好。')
|
|
348
354
|
lines.push('- progress 与 memory 一起写:写日志的同时,把有跨会话长期价值的内容一并写入记忆——跨项目规则 → memory_user,仅本项目 → memory_note;两者在同一轮完成,互不冲突、不遗漏。')
|
|
349
355
|
lines.push('- 只记录有跨会话长期价值的;不记临时信息(搜索结果、临时路径、工具报错)。')
|
|
350
|
-
lines.push('- **记忆操作必须在正文可见(摘要链)**:调用 memory_log/note/user/reflect 更新记忆后,必须把结果写进本轮回复的正文文本(用户直接看到的那段文字,不是工具调用区)
|
|
356
|
+
lines.push('- **记忆操作必须在正文可见(摘要链)**:调用 memory_log/note/user/reflect 更新记忆后,必须把结果写进本轮回复的正文文本(用户直接看到的那段文字,不是工具调用区),并在**回复末尾**用加粗或换行使其醒目(如"**已更新今日日志**\n新增:修复了XXX");调用 memory_recall/memory_external 检索时,在正文开头写明"我查了记忆,发现..."。工具返回值只是辅助,正文转述是强制要求。')
|
|
351
357
|
lines.push('- 用户明确要求长期记住:跨项目规则 → memory_user;仅本项目 → memory_note。')
|
|
352
358
|
lines.push('- 定期调用 memory_maintain 归档 30 天前日志;不存密钥,除非用户明确要求。')
|
|
353
359
|
lines.push('- 记忆仅作补充,不替代正常回复与交付物。')
|
|
@@ -587,6 +593,36 @@ class MemoryEngine {
|
|
|
587
593
|
return { entries, groups, todayDate: todayStr() }
|
|
588
594
|
}
|
|
589
595
|
|
|
596
|
+
/** 生活化总结:调用 DSH 的 AI(subagent)发散地总结某时段的工作(完成/收尾/烦恼/暂停的事)。 */
|
|
597
|
+
async summarizePeriod(period, agent) {
|
|
598
|
+
const ps = this.periodSummary()
|
|
599
|
+
const groups = ps.groups || {}
|
|
600
|
+
const items = (period === '昨天') ? (this.state.recentLogs[0] ? this.state.recentLogs[0].text.split('\n').map((l) => l.trim()).filter((l) => l.startsWith('- ')).map((l) => l.replace(/^- /, '')) : []) : (groups[period] || [])
|
|
601
|
+
if (!items.length) return ''
|
|
602
|
+
const subagents = this._subagents
|
|
603
|
+
if (!subagents) return '' // 无 subagents 服务时前端只显示条目
|
|
604
|
+
const parent = agent && agent.session ? agent.session.id : undefined
|
|
605
|
+
const prompt = [
|
|
606
|
+
'你是一个温暖、细腻的生活助理。请根据下面这位用户的时段工作记录,写一段自然、口语化的总结(80-160字),像朋友聊天一样:',
|
|
607
|
+
'- 概括这个时段完成了什么(挑重要的说,不用逐条罗列)',
|
|
608
|
+
'- 提到是否收尾/完成(如果有明显没做完的迹象就轻轻点一下,不要断言)',
|
|
609
|
+
'- 如果看得出可能的烦恼或卡住的事,温和地提一句;看不出就跳过',
|
|
610
|
+
'- 语气轻松自然,不要列表、不要小标题、不要"总结:"前缀',
|
|
611
|
+
'',
|
|
612
|
+
'用户时段工作记录(' + period + '):',
|
|
613
|
+
items.map((x) => '- ' + x).join('\n'),
|
|
614
|
+
].join('\n')
|
|
615
|
+
try {
|
|
616
|
+
const run = await subagents.start('spawn', { label: 'auto-memory-summarize', prompt, ...(parent ? { parent } : {}) })
|
|
617
|
+
const result = await run
|
|
618
|
+
const text = result && result.text ? String(result.text) : ''
|
|
619
|
+
return text.trim()
|
|
620
|
+
} catch (e) {
|
|
621
|
+
console.error('[dsh-auto-memory] summarize failed', e && e.message ? e.message : e)
|
|
622
|
+
return ''
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
590
626
|
/** 一键反思:自动取"有日志但无反思"的最早日期,按日志条目生成反思草稿并落盘。 */
|
|
591
627
|
async reflectAuto(agent) {
|
|
592
628
|
const p = await this.resolvePaths(agent)
|
|
@@ -995,7 +1031,7 @@ async function globOne(dir, re, limit) {
|
|
|
995
1031
|
return out
|
|
996
1032
|
}
|
|
997
1033
|
|
|
998
|
-
/** 从一条 jsonl 会话行提取文本片段(兼容 claude/codex
|
|
1034
|
+
/** 从一条 jsonl 会话行提取文本片段(兼容 claude/codex 等格式)。 */
|
|
999
1035
|
function extractJsonText(line) {
|
|
1000
1036
|
try {
|
|
1001
1037
|
const obj = JSON.parse(line)
|
|
@@ -1027,12 +1063,17 @@ export function apply(ctx, config) {
|
|
|
1027
1063
|
const engine = new MemoryEngine()
|
|
1028
1064
|
const sessionQuery = ctx.get('sessionQuery')
|
|
1029
1065
|
engine._sessionQuery = sessionQuery
|
|
1066
|
+
engine._subagents = ctx.get('subagents') || undefined
|
|
1030
1067
|
|
|
1031
1068
|
// 生命周期刷新
|
|
1032
1069
|
const refreshAll = (agent) => { void engine.refresh(agent) }
|
|
1033
1070
|
refreshAll()
|
|
1034
1071
|
ctx.on('agent/session-start', (payload) => refreshAll(payload && payload.agent))
|
|
1035
|
-
ctx.on('agent/turn-stopping', (payload) =>
|
|
1072
|
+
ctx.on('agent/turn-stopping', (payload) => {
|
|
1073
|
+
refreshAll(payload && payload.agent)
|
|
1074
|
+
// 记录最后活动时间(判断暂离回来用)
|
|
1075
|
+
try { engine._lastActiveAt = Date.now() } catch (e) {}
|
|
1076
|
+
})
|
|
1036
1077
|
|
|
1037
1078
|
// ---------- 系统提示词注入 ----------
|
|
1038
1079
|
const disposeSection = ctx.systemPrompt.section({
|
|
@@ -1355,6 +1396,17 @@ export function apply(ctx, config) {
|
|
|
1355
1396
|
writeJson(res, 405, { error: 'method not allowed' })
|
|
1356
1397
|
},
|
|
1357
1398
|
},
|
|
1399
|
+
{
|
|
1400
|
+
kind: 'exact',
|
|
1401
|
+
path: API.summarize,
|
|
1402
|
+
handler: async (req, res) => {
|
|
1403
|
+
if (!isLoopbackRequest(req)) return writeJson(res, 403, { error: 'forbidden: loopback-only' })
|
|
1404
|
+
if ((req.method || 'POST') !== 'POST') return writeJson(res, 405, { error: 'method not allowed' })
|
|
1405
|
+
const body = await readJsonBody(req)
|
|
1406
|
+
if (!body || typeof body.period !== 'string') return writeJson(res, 400, { error: 'invalid body' })
|
|
1407
|
+
try { writeJson(res, 200, { result: await engine.summarizePeriod(body.period) }) } catch (e) { writeJson(res, 500, { error: String(e && e.message ? e.message : e) }) }
|
|
1408
|
+
},
|
|
1409
|
+
},
|
|
1358
1410
|
]
|
|
1359
1411
|
|
|
1360
1412
|
// ---------- 注册与清理 ----------
|