@a9i5k4/dsh-auto-memory 0.1.16 → 0.1.18
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 +69 -4
- package/lib/client.js.bak-20260814 +568 -0
- package/lib/index.js +95 -0
- 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 按时段向你汇报当天进展',
|
|
@@ -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: '生成于 ',
|
|
@@ -205,7 +205,7 @@ window.__ModuleLoader__.load({
|
|
|
205
205
|
kvAvail: 'available', kvUnavail: 'unavailable', kvDup: 'Duplicate note headings', kvDupCount: '',
|
|
206
206
|
kvMem: 'Memory files', kvMemUser: 'user', kvMemNotes: 'notes', kvMemLog: 'log', kvMemMissing: 'missing',
|
|
207
207
|
kvWs: 'Current workspace', kvWsUnknown: '(unknown)', kvApi: 'API probes', unknown: '(unknown)',
|
|
208
|
-
guideTitle: 'Welcome to dsh-auto-memory', guideSub: 'What this plugin does:',
|
|
208
|
+
guideTitle: 'Welcome to dsh-auto-memory (dev)', guideSub: 'What this plugin does:',
|
|
209
209
|
gFeat1: 'Three-layer memory with automatic injection & retrieval: user / project notes / daily logs',
|
|
210
210
|
gFeat2: 'Auto-consolidation after every turn: bugs fixed and decisions made are written into today\'s log automatically',
|
|
211
211
|
gFeat3: 'AI period greetings with three-level drawers: open the Memory panel for a period-by-period digest',
|
|
@@ -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,28 @@ window.__ModuleLoader__.load({
|
|
|
313
313
|
|
|
314
314
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
315
315
|
var CHANGELOG = {
|
|
316
|
+
'0.1.18': { zh: [
|
|
317
|
+
'稳定版:整合 0.1.13~0.1.17 全部修复与优化',
|
|
318
|
+
'核心:上下文缓存策略重写——记忆注入迁至运行时上下文快照,系统提示词保持稳定,DeepSeek 前缀缓存全程命中,不再白白消耗 token(命中率恢复 95%+)',
|
|
319
|
+
'修复:秒级时间戳击穿前缀缓存(改日期级)',
|
|
320
|
+
'修复:npm 安装后客户端面板无法加载(bundle 注册名)',
|
|
321
|
+
'新增:更新说明弹窗/首次安装指导/暂离回归自动打开记忆窗口/动态通知中心',
|
|
322
|
+
'提示:pnpm v11 默认限制安装发布不足 1 天的版本,想立即获取新版请在 pnpm-workspace.yaml 设 minimumReleaseAge: 0 或使用显式版本号',
|
|
323
|
+
], en: [
|
|
324
|
+
'Stable release: consolidates 0.1.13~0.1.17 fixes and polish',
|
|
325
|
+
'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%+)',
|
|
326
|
+
'Fix: second-level timestamp breaking prefix cache (now date-level)',
|
|
327
|
+
'Fix: client panel not loading after npm install (bundle registration id)',
|
|
328
|
+
'New: update dialog / first-run guide / memory panel auto-opens after >1h away / dynamic notice center',
|
|
329
|
+
'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',
|
|
330
|
+
] },
|
|
331
|
+
'0.1.17': { zh: [
|
|
332
|
+
'新增:动态通知中心——插件自动拉取发布者的重要提醒(重大 bug/升级建议),特定时间窗口内显示,无需等待发版即可收到推送',
|
|
333
|
+
'优化:0.1.14 及以下旧版本已全部弃用(缓存击穿浪费 token),升级到 0.1.16+ 彻底解决',
|
|
334
|
+
], en: [
|
|
335
|
+
'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',
|
|
336
|
+
'Polish: all versions below 0.1.14 are deprecated (cache-breaking token waste); upgrade to 0.1.16+ fixes it',
|
|
337
|
+
] },
|
|
316
338
|
'0.1.16': { zh: [
|
|
317
339
|
'稳定版:整合今日 0.1.13~0.1.15 全部修复与优化',
|
|
318
340
|
'核心:上下文缓存策略重写——记忆注入迁至运行时上下文快照,系统提示词保持稳定,DeepSeek 前缀缓存全程命中,不再白白消耗 token(命中率恢复 95%+)',
|
|
@@ -376,6 +398,7 @@ window.__ModuleLoader__.load({
|
|
|
376
398
|
externalImport: '/api/dsh-auto-memory/external-import',
|
|
377
399
|
summarize: '/api/dsh-auto-memory/summarize',
|
|
378
400
|
greet: '/api/dsh-auto-memory/greet',
|
|
401
|
+
notices: '/api/dsh-auto-memory/notices',
|
|
379
402
|
}
|
|
380
403
|
function query(params) {
|
|
381
404
|
var search = new URLSearchParams()
|
|
@@ -1303,6 +1326,29 @@ window.__ModuleLoader__.load({
|
|
|
1303
1326
|
h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', opacity: .8, marginTop: '4px' } }, t('guideTip')),
|
|
1304
1327
|
h('button', { 'data-dam-btn': '', style: close, onClick: closeDialog }, t('gotIt'))))
|
|
1305
1328
|
}
|
|
1329
|
+
if (dialogState.kind === 'notice') {
|
|
1330
|
+
var n = dialogState.notice || {}
|
|
1331
|
+
var zh = locale === 'zh' || !n.titleEn
|
|
1332
|
+
var nTitle = zh ? (n.title || '') : (n.titleEn || n.title || '')
|
|
1333
|
+
var nMsg = zh ? (n.message || '') : (n.messageEn || n.message || '')
|
|
1334
|
+
var isUrgent = n.level === 'urgent'
|
|
1335
|
+
var accent = isUrgent ? 'var(--dsw-alias-danger, #e5534b)' : 'var(--dsw-alias-brand-primary, #4f7cff)'
|
|
1336
|
+
return h('div', { style: overlay },
|
|
1337
|
+
h('div', { style: box },
|
|
1338
|
+
h('div', { style: Object.assign({}, head, isUrgent ? { color: accent } : {}) }, nTitle),
|
|
1339
|
+
h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.6, opacity: .92, whiteSpace: 'pre-wrap', marginTop: '4px' } }, nMsg),
|
|
1340
|
+
h('div', { style: { display: 'flex', gap: '8px', justifyContent: 'flex-end', marginTop: '6px' } },
|
|
1341
|
+
n.link ? h('a', { href: n.link, target: '_blank', rel: 'noreferrer', style: Object.assign({}, close, { textDecoration: 'none' }) }, t('noticeOpen')) : null,
|
|
1342
|
+
h('button', { 'data-dam-btn': '', style: close, onClick: function () {
|
|
1343
|
+
try {
|
|
1344
|
+
var arr = []
|
|
1345
|
+
try { arr = JSON.parse(localStorage.getItem('dsh-auto-memory.seenNotices') || '[]') } catch (e3) {}
|
|
1346
|
+
if (n.id && arr.indexOf(n.id) < 0) arr.push(n.id)
|
|
1347
|
+
localStorage.setItem('dsh-auto-memory.seenNotices', JSON.stringify(arr))
|
|
1348
|
+
} catch (e3) {}
|
|
1349
|
+
closeDialog()
|
|
1350
|
+
} }, t('gotIt')))))
|
|
1351
|
+
}
|
|
1306
1352
|
var versions = dialogState.versions || []
|
|
1307
1353
|
var lastV = versions.length ? versions[versions.length - 1].version : ''
|
|
1308
1354
|
return h('div', { style: overlay },
|
|
@@ -1539,6 +1585,25 @@ window.__ModuleLoader__.load({
|
|
|
1539
1585
|
}
|
|
1540
1586
|
}).catch(function () {})
|
|
1541
1587
|
|
|
1588
|
+
// 动态通知(发布者→用户:重大 bug 提醒,不依赖发版):启动检查 + 每小时刷新;按 id 去重,urgent 优先
|
|
1589
|
+
function checkNotices() {
|
|
1590
|
+
apiGet(API.notices).then(function (d) {
|
|
1591
|
+
try {
|
|
1592
|
+
if (!d || !Array.isArray(d.notices) || !d.notices.length) return
|
|
1593
|
+
var seen = []
|
|
1594
|
+
try { seen = JSON.parse(localStorage.getItem('dsh-auto-memory.seenNotices') || '[]') } catch (e2) {}
|
|
1595
|
+
var fresh = d.notices.filter(function (n) { return n && n.id && seen.indexOf(n.id) < 0 })
|
|
1596
|
+
if (!fresh.length) return
|
|
1597
|
+
var target = null
|
|
1598
|
+
for (var i = 0; i < fresh.length; i++) { if (fresh[i].level === 'urgent') { target = fresh[i]; break } }
|
|
1599
|
+
if (!target) target = fresh[0]
|
|
1600
|
+
openDialog({ kind: 'notice', notice: target })
|
|
1601
|
+
} catch (e) {}
|
|
1602
|
+
}).catch(function () {})
|
|
1603
|
+
}
|
|
1604
|
+
checkNotices()
|
|
1605
|
+
var noticesTimer = setInterval(checkNotices, 3600 * 1000)
|
|
1606
|
+
|
|
1542
1607
|
var surfaceDisposers = []
|
|
1543
1608
|
function registerSurfaces() {
|
|
1544
1609
|
try {
|
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
/* dsh-auto-memory — browser half (hand-written __ModuleLoader__ bundle).
|
|
2
|
+
* Registers three additive surfaces:
|
|
3
|
+
* 1. sidebar.footer.action — 「记忆」入口按钮(开关左下角浮层面板)
|
|
4
|
+
* 2. shell.overlay — 记忆面板:概览 / 日志 / 笔记 / 反思 / 检索
|
|
5
|
+
* 液态玻璃视觉(backdrop-filter + --dsw-alias-* 主题令牌),
|
|
6
|
+
* 可拖动 / 右下角缩放 / 开关缩放动画 / 位置大小持久化。
|
|
7
|
+
* 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
|
|
8
|
+
* Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
|
|
9
|
+
*/
|
|
10
|
+
window.__ModuleLoader__.load({
|
|
11
|
+
id: '@deepseek-ai/dsh-auto-memory',
|
|
12
|
+
factory: (require) => {
|
|
13
|
+
var module = { exports: {} }
|
|
14
|
+
var exports = module.exports
|
|
15
|
+
var React = require('react')
|
|
16
|
+
var h = React.createElement
|
|
17
|
+
var useState = React.useState
|
|
18
|
+
var useEffect = React.useEffect
|
|
19
|
+
var useReducer = React.useReducer
|
|
20
|
+
|
|
21
|
+
// ───────────────────────── 控制器 ─────────────────────────
|
|
22
|
+
var listeners = new Set()
|
|
23
|
+
var panelOpen = false
|
|
24
|
+
var panelClosing = false
|
|
25
|
+
var closeTimer = null
|
|
26
|
+
// 几何状态:left/top/width/height,持久化到 localStorage(用户可拖动/缩放)
|
|
27
|
+
var GEOM_KEY = 'dsh-auto-memory.panel.geom'
|
|
28
|
+
var DEFAULT_W = 440
|
|
29
|
+
var DEFAULT_H = 560
|
|
30
|
+
var DEFAULT_GAP = 16
|
|
31
|
+
function defaultGeom() {
|
|
32
|
+
var vh = window.innerHeight || 800
|
|
33
|
+
return {
|
|
34
|
+
left: DEFAULT_GAP,
|
|
35
|
+
top: Math.max(DEFAULT_GAP, vh - DEFAULT_H - DEFAULT_GAP),
|
|
36
|
+
width: DEFAULT_W,
|
|
37
|
+
height: DEFAULT_H,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
var geom = null
|
|
41
|
+
function clampGeom(g) {
|
|
42
|
+
var vw = window.innerWidth || 1280
|
|
43
|
+
var vh = window.innerHeight || 800
|
|
44
|
+
var w = Math.max(300, Math.min(g.width || DEFAULT_W, vw - DEFAULT_GAP * 2))
|
|
45
|
+
var h = Math.max(240, Math.min(g.height || DEFAULT_H, vh - DEFAULT_GAP * 2))
|
|
46
|
+
return {
|
|
47
|
+
left: Math.max(DEFAULT_GAP, Math.min(g.left !== undefined ? g.left : DEFAULT_GAP, vw - w - DEFAULT_GAP)),
|
|
48
|
+
top: Math.max(DEFAULT_GAP, Math.min(g.top !== undefined ? g.top : DEFAULT_GAP, vh - h - DEFAULT_GAP)),
|
|
49
|
+
width: w,
|
|
50
|
+
height: h,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function loadGeom() {
|
|
54
|
+
try {
|
|
55
|
+
var raw = localStorage.getItem(GEOM_KEY)
|
|
56
|
+
if (raw) return clampGeom(JSON.parse(raw))
|
|
57
|
+
} catch (e) {}
|
|
58
|
+
return clampGeom(defaultGeom())
|
|
59
|
+
}
|
|
60
|
+
function persistGeom() { if (geom) { try { localStorage.setItem(GEOM_KEY, JSON.stringify(geom)) } catch (e) {} } }
|
|
61
|
+
function emit() { listeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
|
|
62
|
+
var controller = {
|
|
63
|
+
isOpen: function () { return panelOpen },
|
|
64
|
+
isClosing: function () { return panelClosing },
|
|
65
|
+
geom: function () { if (!geom) geom = loadGeom(); return geom },
|
|
66
|
+
setGeom: function (partial) {
|
|
67
|
+
var cur = controller.geom()
|
|
68
|
+
geom = clampGeom({
|
|
69
|
+
left: partial.left !== undefined ? partial.left : cur.left,
|
|
70
|
+
top: partial.top !== undefined ? partial.top : cur.top,
|
|
71
|
+
width: partial.width !== undefined ? partial.width : cur.width,
|
|
72
|
+
height: partial.height !== undefined ? partial.height : cur.height,
|
|
73
|
+
})
|
|
74
|
+
emit()
|
|
75
|
+
},
|
|
76
|
+
flushGeom: function () { persistGeom() },
|
|
77
|
+
resetGeom: function () { geom = clampGeom(defaultGeom()); persistGeom(); emit() },
|
|
78
|
+
toggle: function () { if (panelOpen) controller.close(); else controller.open() },
|
|
79
|
+
open: function () {
|
|
80
|
+
if (closeTimer) { clearTimeout(closeTimer); closeTimer = null }
|
|
81
|
+
panelOpen = true; panelClosing = false; emit()
|
|
82
|
+
},
|
|
83
|
+
close: function () {
|
|
84
|
+
if (!panelOpen || panelClosing) return
|
|
85
|
+
panelClosing = true; emit()
|
|
86
|
+
closeTimer = setTimeout(function () {
|
|
87
|
+
panelOpen = false; panelClosing = false; closeTimer = null; emit()
|
|
88
|
+
}, 170)
|
|
89
|
+
},
|
|
90
|
+
subscribe: function (fn) { listeners.add(fn); return function () { listeners.delete(fn) } },
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ───────────────────────── API ─────────────────────────
|
|
94
|
+
var API = {
|
|
95
|
+
state: '/api/dsh-auto-memory/state',
|
|
96
|
+
list: '/api/dsh-auto-memory/list',
|
|
97
|
+
file: '/api/dsh-auto-memory/file',
|
|
98
|
+
recall: '/api/dsh-auto-memory/recall',
|
|
99
|
+
config: '/api/dsh-auto-memory/config',
|
|
100
|
+
reflect: '/api/dsh-auto-memory/reflect',
|
|
101
|
+
note: '/api/dsh-auto-memory/note',
|
|
102
|
+
external: '/api/dsh-auto-memory/external',
|
|
103
|
+
externalImport: '/api/dsh-auto-memory/external-import',
|
|
104
|
+
}
|
|
105
|
+
function query(params) {
|
|
106
|
+
var search = new URLSearchParams()
|
|
107
|
+
for (var key in params) if (params[key] !== undefined && params[key] !== '') search.set(key, String(params[key]))
|
|
108
|
+
var text = search.toString()
|
|
109
|
+
return text ? '?' + text : ''
|
|
110
|
+
}
|
|
111
|
+
async function apiGet(path, params) {
|
|
112
|
+
var res = await fetch(path + query(params))
|
|
113
|
+
var body = await res.json().catch(function () { return {} })
|
|
114
|
+
if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status))
|
|
115
|
+
return body
|
|
116
|
+
}
|
|
117
|
+
async function apiPost(path, payload) {
|
|
118
|
+
var res = await fetch(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
|
|
119
|
+
var body = await res.json().catch(function () { return {} })
|
|
120
|
+
if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status))
|
|
121
|
+
return body
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ───────────────────────── 样式 ─────────────────────────
|
|
125
|
+
// 视觉:液态玻璃(毛玻璃)—— backdrop-filter + DSH 主题令牌(--dsw-alias-*),
|
|
126
|
+
// 跟随亮/暗主题与页面背景自适应;位置/尺寸由 JS 几何状态驱动(可拖动、可缩放)。
|
|
127
|
+
var CSS = [
|
|
128
|
+
'[data-dam-panel] { position: fixed; left: 16px; width: 440px; height: 560px;',
|
|
129
|
+
' max-width: calc(100vw - 32px); max-height: calc(100vh - 32px);',
|
|
130
|
+
' display: flex; flex-direction: column; overflow: hidden; z-index: 3000; pointer-events: auto;',
|
|
131
|
+
' border-radius: 16px; font: 13px/1.55 system-ui, "Segoe UI", sans-serif;',
|
|
132
|
+
' background: color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(255,255,255,.86)) 58%, transparent);',
|
|
133
|
+
' -webkit-backdrop-filter: blur(28px) saturate(1.55); backdrop-filter: blur(28px) saturate(1.55);',
|
|
134
|
+
' border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.35)) 65%, transparent);',
|
|
135
|
+
' box-shadow: 0 24px 64px rgba(0,0,0,.22), 0 4px 16px rgba(0,0,0,.10), inset 0 1px 0 rgba(255,255,255,.22);',
|
|
136
|
+
' color: var(--dsw-alias-label-primary, #1f2328);',
|
|
137
|
+
' animation: dam-in .2s cubic-bezier(.2,.9,.3,1.15) both; transform-origin: left bottom; }',
|
|
138
|
+
'@keyframes dam-in { from { opacity: 0; transform: scale(.92) translateY(10px); } to { opacity: 1; transform: none; } }',
|
|
139
|
+
'@keyframes dam-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(.95) translateY(6px); } }',
|
|
140
|
+
'[data-dam-panel][data-closing="true"] { animation: dam-out .16s ease-in both; }',
|
|
141
|
+
'[data-dam-panel]::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 64%; pointer-events: none;',
|
|
142
|
+
' background: linear-gradient(180deg, rgba(255,255,255,.13), rgba(255,255,255,0) 70%); border-radius: 16px 16px 0 0; }',
|
|
143
|
+
'[data-dam-panel][data-dragging="true"] { user-select: none; }',
|
|
144
|
+
'[data-dam-panel] header { display: flex; align-items: center; gap: 8px; padding: 10px 14px; cursor: grab;',
|
|
145
|
+
' border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.25)) 55%, transparent); }',
|
|
146
|
+
'[data-dam-panel][data-dragging="true"] header { cursor: grabbing; }',
|
|
147
|
+
'[data-dam-panel] header strong { font-size: 14px; }',
|
|
148
|
+
'[data-dam-panel] header .dam-spacer { flex: 1; }',
|
|
149
|
+
'[data-dam-resize] { position: absolute; right: 0; bottom: 0; width: 22px; height: 22px; cursor: nwse-resize;',
|
|
150
|
+
' opacity: .55; z-index: 2; border-radius: 0 0 16px 0;',
|
|
151
|
+
' background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 45%, transparent) 50%); }',
|
|
152
|
+
'[data-dam-resize]:hover { opacity: 1; }',
|
|
153
|
+
'[data-dam-btn] { border: none; background: transparent; cursor: pointer; color: inherit; opacity: .75; font-size: 13px; padding: 4px 8px; border-radius: 6px; }',
|
|
154
|
+
'[data-dam-btn]:hover { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 16%, transparent); }',
|
|
155
|
+
'[data-dam-tabs] { display: flex; gap: 2px; padding: 6px 10px; border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.2)) 55%, transparent); }',
|
|
156
|
+
'[data-dam-tab] { border: none; background: transparent; cursor: pointer; color: inherit; opacity: .6; padding: 5px 10px; border-radius: 7px; font-size: 12.5px; }',
|
|
157
|
+
'[data-dam-tab][data-active="true"] { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 18%, transparent); font-weight: 600; }',
|
|
158
|
+
'[data-dam-body] { flex: 1; overflow: auto; padding: 12px 14px; }',
|
|
159
|
+
'[data-dam-kv] { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 12.5px; }',
|
|
160
|
+
'[data-dam-kv] b { opacity: .55; font-weight: 500; }',
|
|
161
|
+
'[data-dam-card] { border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.22)) 60%, transparent); border-radius: 9px; padding: 8px 10px; margin-bottom: 8px; font-size: 12.5px;',
|
|
162
|
+
' background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent); }',
|
|
163
|
+
'[data-dam-card] .dam-date { font-weight: 600; margin-bottom: 4px; }',
|
|
164
|
+
'[data-dam-card] .dam-content { white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; }',
|
|
165
|
+
'[data-dam-banner] { border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e6a23c) 55%, transparent);',
|
|
166
|
+
' background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e6a23c) 13%, transparent); border-radius: 9px; padding: 8px 10px; margin-bottom: 10px; font-size: 12.5px; }',
|
|
167
|
+
'[data-dam-input], [data-dam-select] { width: 100%; box-sizing: border-box; background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.08)) 45%, transparent); color: inherit; border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.3)) 60%, transparent); border-radius: 7px; padding: 6px 8px; font: inherit; }',
|
|
168
|
+
'[data-dam-row] { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }',
|
|
169
|
+
'[data-dam-row] label { flex: 0 0 110px; opacity: .8; font-size: 12.5px; }',
|
|
170
|
+
'[data-dam-hint] { opacity: .5; font-size: 11.5px; margin-top: 2px; }',
|
|
171
|
+
'[data-dam-sidebar-btn] { display: flex; align-items: center; gap: 6px; width: 100%; border: none; background: transparent; color: inherit; cursor: pointer; padding: 6px 10px; border-radius: 8px; font: inherit; font-size: 13px; opacity: .8; }',
|
|
172
|
+
'[data-dam-sidebar-btn]:hover { background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 14%, transparent); opacity: 1; }',
|
|
173
|
+
'[data-dam-sidebar-btn][data-active="true"] { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 16%, transparent); color: var(--dsw-alias-brand-primary, #4f7cff); }',
|
|
174
|
+
'[data-dam-error] { color: var(--dsw-alias-state-error-primary, #d64545); font-size: 12px; margin-top: 6px; white-space: pre-wrap; }',
|
|
175
|
+
'[data-dam-muted] { opacity: .55; }',
|
|
176
|
+
'@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {',
|
|
177
|
+
' [data-dam-panel] { background: var(--dsw-alias-bg-overlay, #ffffff); } }',
|
|
178
|
+
].join('\n')
|
|
179
|
+
var STYLE_ID = 'dsh-auto-memory-css'
|
|
180
|
+
function ensureStyle() {
|
|
181
|
+
if (document.getElementById(STYLE_ID)) return
|
|
182
|
+
var tag = document.createElement('style')
|
|
183
|
+
tag.id = STYLE_ID
|
|
184
|
+
tag.dataset.plugin = '@deepseek-ai/dsh-auto-memory'
|
|
185
|
+
tag.textContent = CSS
|
|
186
|
+
document.head.appendChild(tag)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ───────────────────────── 通用小组件 ─────────────────────────
|
|
190
|
+
function useTick() { return useReducer(function (x) { return x + 1 }, 0) }
|
|
191
|
+
|
|
192
|
+
function Banner(props) {
|
|
193
|
+
return h('div', { 'data-dam-banner': '' }, props.children)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function Card(props) {
|
|
197
|
+
return h('div', { 'data-dam-card': '' },
|
|
198
|
+
h('div', { className: 'dam-date' }, props.title),
|
|
199
|
+
h('div', { className: 'dam-content' }, props.children))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function Loading() { return h('div', { 'data-dam-muted': '' }, '加载中…') }
|
|
203
|
+
|
|
204
|
+
// ───────────────────────── 侧边栏入口 ─────────────────────────
|
|
205
|
+
function SidebarButton() {
|
|
206
|
+
var tick = useTick()
|
|
207
|
+
useEffect(function () { return controller.subscribe(tick[1]) }, [])
|
|
208
|
+
return h('button', {
|
|
209
|
+
'data-dam-sidebar-btn': '',
|
|
210
|
+
title: '记忆面板',
|
|
211
|
+
'data-active': (panelOpen || panelClosing) ? 'true' : undefined,
|
|
212
|
+
onClick: function () { controller.toggle() },
|
|
213
|
+
}, h('span', null, '记忆'))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ───────────────────────── 记忆面板 ─────────────────────────
|
|
217
|
+
function fmtSize(n) {
|
|
218
|
+
if (n >= 1024 * 1024) return (n / 1024 / 1024).toFixed(1) + ' MB'
|
|
219
|
+
if (n >= 1024) return (n / 1024).toFixed(1) + ' KB'
|
|
220
|
+
return n + ' B'
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function OverviewTab() {
|
|
224
|
+
var tick = useTick()
|
|
225
|
+
var state = null
|
|
226
|
+
useEffect(function () {
|
|
227
|
+
var alive = true
|
|
228
|
+
apiGet(API.state).then(function (s) { if (alive) { state = s; tick[1]() } }).catch(function () {})
|
|
229
|
+
return function () { alive = false }
|
|
230
|
+
}, [])
|
|
231
|
+
if (!state) return h(Loading)
|
|
232
|
+
return h('div', null,
|
|
233
|
+
state.pendingReflection
|
|
234
|
+
? h(Banner, null, '待生成反思: ' + state.pendingReflection + ' —— 打开/继续会话,agent 会在会话开始时主动呈现昨日反思。')
|
|
235
|
+
: null,
|
|
236
|
+
h('div', { 'data-dam-kv': '' },
|
|
237
|
+
h('b', null, '工作区'), h('span', null, state.ws),
|
|
238
|
+
h('b', null, '用户级记忆'), h('span', null, state.userFile + ' (' + fmtSize(state.sizes.user) + ')'),
|
|
239
|
+
h('b', null, '项目笔记'), h('span', null, state.notesPath + ' (' + fmtSize(state.sizes.notes) + ')'),
|
|
240
|
+
h('b', null, '今日日志'), h('span', null, state.logPath + ' · ' + state.todayEntries + ' 条 (' + fmtSize(state.sizes.log) + ')'),
|
|
241
|
+
h('b', null, '最近反思'), h('span', null, state.latestReflectionDate || '(无)'),
|
|
242
|
+
h('b', null, '配置文件'), h('span', null, state.configReadError ? ('读取失败: ' + state.configReadError) : '正常')),
|
|
243
|
+
h('div', { 'data-dam-hint': '' }, '上次刷新: ' + (state.refreshedAt ? new Date(state.refreshedAt).toLocaleString() : '尚未')))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function LogsTab() {
|
|
247
|
+
var tick = useTick()
|
|
248
|
+
var data = null
|
|
249
|
+
var open = null
|
|
250
|
+
var content = ''
|
|
251
|
+
useEffect(function () {
|
|
252
|
+
var alive = true
|
|
253
|
+
apiGet(API.list).then(function (d) { if (alive) { data = d; tick[1]() } }).catch(function () {})
|
|
254
|
+
return function () { alive = false }
|
|
255
|
+
}, [])
|
|
256
|
+
useEffect(function () {
|
|
257
|
+
if (!open) return
|
|
258
|
+
var alive = true
|
|
259
|
+
apiGet(API.file, { path: open }).then(function (d) { if (alive) { content = d.content; tick[1]() } }).catch(function () {})
|
|
260
|
+
return function () { alive = false }
|
|
261
|
+
}, [open])
|
|
262
|
+
if (!data) return h(Loading)
|
|
263
|
+
var rows = []
|
|
264
|
+
if (open) {
|
|
265
|
+
rows.push(h(Card, { title: pathName(open) },
|
|
266
|
+
h('div', { 'data-dam-content': '' }, content || '(空)'),
|
|
267
|
+
h('button', { 'data-dam-btn': '', onClick: function () { open = null; content = ''; tick[1]() } }, '← 返回')))
|
|
268
|
+
} else {
|
|
269
|
+
rows.push(h('div', { 'data-dam-hint': '' }, '点击日期查看当日日志(append-only):'))
|
|
270
|
+
for (var i = 0; i < data.logs.length; i++) {
|
|
271
|
+
(function (log) {
|
|
272
|
+
rows.push(h(Card, { title: log.date + ' · ' + fmtSize(log.size) },
|
|
273
|
+
h('button', { 'data-dam-btn': '', onClick: function () { open = log.date + '.md'; tick[1]() } }, '查看')))
|
|
274
|
+
})(data.logs[i])
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return h('div', null, rows)
|
|
278
|
+
}
|
|
279
|
+
function pathName(p) { var parts = String(p).split(/[\\/]/); return parts[parts.length - 1] }
|
|
280
|
+
|
|
281
|
+
function NotesTab() {
|
|
282
|
+
var tick = useTick()
|
|
283
|
+
var data = null
|
|
284
|
+
var draft = ''
|
|
285
|
+
var saving = false
|
|
286
|
+
var msg = ''
|
|
287
|
+
var err = ''
|
|
288
|
+
if (!data) {
|
|
289
|
+
apiGet(API.state).then(function (s) { data = s; tick[1]() }).catch(function () {})
|
|
290
|
+
return h(Loading)
|
|
291
|
+
}
|
|
292
|
+
function save() {
|
|
293
|
+
if (!draft.trim() || saving) return
|
|
294
|
+
saving = true; msg = ''; err = ''
|
|
295
|
+
apiPost(API.note, { content: draft.trim() }).then(function (d) {
|
|
296
|
+
msg = d.result || '已追加 ✓'; draft = ''; saving = false; tick[1]()
|
|
297
|
+
}).catch(function (e) { err = e.message; saving = false; tick[1]() })
|
|
298
|
+
}
|
|
299
|
+
return h('div', null,
|
|
300
|
+
h('div', { 'data-dam-hint': '' }, '项目笔记: ' + data.notesPath),
|
|
301
|
+
h('textarea', { 'data-dam-input': '', rows: 6, placeholder: '想追加到项目笔记的内容…(保存时自动带日期标题)', value: draft, onChange: function (e) { draft = e.target.value; tick[1]() } }),
|
|
302
|
+
h('div', { 'data-dam-row': '' },
|
|
303
|
+
h('button', { 'data-dam-btn': '', onClick: save, disabled: saving }, saving ? '保存中…' : '追加'),
|
|
304
|
+
h('span', { 'data-dam-hint': '' }, '建议直接用对话让 agent 调 memory_note;此处为手动追加。')),
|
|
305
|
+
msg ? h('div', null, msg) : null,
|
|
306
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function ReflectionsTab() {
|
|
310
|
+
var tick = useTick()
|
|
311
|
+
var data = null
|
|
312
|
+
var open = null
|
|
313
|
+
var content = ''
|
|
314
|
+
useEffect(function () {
|
|
315
|
+
var alive = true
|
|
316
|
+
apiGet(API.list).then(function (d) { if (alive) { data = d; tick[1]() } }).catch(function () {})
|
|
317
|
+
return function () { alive = false }
|
|
318
|
+
}, [])
|
|
319
|
+
useEffect(function () {
|
|
320
|
+
if (!open) return
|
|
321
|
+
var alive = true
|
|
322
|
+
apiGet(API.file, { path: open }).then(function (d) { if (alive) { content = d.content; tick[1]() } }).catch(function () {})
|
|
323
|
+
return function () { alive = false }
|
|
324
|
+
}, [open])
|
|
325
|
+
if (!data) return h(Loading)
|
|
326
|
+
var rows = []
|
|
327
|
+
if (open) {
|
|
328
|
+
rows.push(h(Card, { title: '反思 ' + pathName(open) }, h('div', { 'data-dam-content': '' }, content || '(空)')))
|
|
329
|
+
rows.push(h('button', { 'data-dam-btn': '', onClick: function () { open = null; content = ''; tick[1]() } }, '← 返回'))
|
|
330
|
+
} else {
|
|
331
|
+
if (!data.reflections.length) rows.push(h('div', { 'data-dam-muted': '' }, '还没有反思。每天第一次会话时,agent 会主动呈现前一天的工作反思。'))
|
|
332
|
+
for (var i = 0; i < data.reflections.length; i++) {
|
|
333
|
+
(function (r) {
|
|
334
|
+
rows.push(h(Card, { title: r.date + ' · ' + fmtSize(r.size) },
|
|
335
|
+
h('button', { 'data-dam-btn': '', onClick: function () { open = 'reflections/' + r.name; tick[1]() } }, '查看')))
|
|
336
|
+
})(data.reflections[i])
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return h('div', null, rows)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function SearchTab() {
|
|
343
|
+
var tick = useTick()
|
|
344
|
+
var q = ''
|
|
345
|
+
var result = null
|
|
346
|
+
var busy = false
|
|
347
|
+
function search() {
|
|
348
|
+
if (!q.trim() || busy) return
|
|
349
|
+
busy = true; result = null; tick[1]()
|
|
350
|
+
apiPost(API.recall, { query: q.trim() }).then(function (d) { result = d.result; busy = false; tick[1]() })
|
|
351
|
+
.catch(function (e) { result = '检索失败: ' + e.message; busy = false; tick[1]() })
|
|
352
|
+
}
|
|
353
|
+
return h('div', null,
|
|
354
|
+
h('div', { 'data-dam-row': '' },
|
|
355
|
+
h('input', { 'data-dam-input': '', placeholder: '搜记忆:关键词或自包含描述…', value: q, onChange: function (e) { q = e.target.value; tick[1]() }, onKeyDown: function (e) { if (e.key === 'Enter') search() } }),
|
|
356
|
+
h('button', { 'data-dam-btn': '', onClick: search, disabled: busy }, '检索')),
|
|
357
|
+
result ? h(Card, { title: '结果' }, h('div', { 'data-dam-content': '' }, result)) : null)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
var TOOL_LABEL = { workbuddy: 'WorkBuddy', codebuddy: 'CodeBuddy', claude: 'Claude Code', codex: 'Codex', 'project-files': '项目文件' }
|
|
361
|
+
function ConnectTab() {
|
|
362
|
+
var tick = useTick()
|
|
363
|
+
var sources = null
|
|
364
|
+
var busy = {}
|
|
365
|
+
var msg = ''
|
|
366
|
+
var err = ''
|
|
367
|
+
function load() {
|
|
368
|
+
apiGet(API.external).then(function (d) { sources = d.sources || []; tick[1]() }).catch(function (e) { err = e.message; tick[1]() })
|
|
369
|
+
}
|
|
370
|
+
useEffect(function () { load(); var t = setInterval(load, 60000); return function () { clearInterval(t) } }, [])
|
|
371
|
+
if (!sources) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
|
|
372
|
+
function doImport(source, target) {
|
|
373
|
+
busy[source] = true; msg = ''; err = ''
|
|
374
|
+
apiPost(API.externalImport, { source: source, target: target }).then(function (d) {
|
|
375
|
+
msg = d.result || '接入完成'; busy[source] = false; tick[1]()
|
|
376
|
+
}).catch(function (e) { err = e.message; busy[source] = false; tick[1]() })
|
|
377
|
+
}
|
|
378
|
+
function importAll() {
|
|
379
|
+
var md = sources.filter(function (s) { return s.kind !== 'sessions' && s.enabled !== false })
|
|
380
|
+
msg = '正在接入 ' + md.length + ' 个源…'; err = ''
|
|
381
|
+
var chain = Promise.resolve()
|
|
382
|
+
md.forEach(function (s) { chain = chain.then(function () { return apiPost(API.externalImport, { source: s.id, target: 'project' }) }) })
|
|
383
|
+
chain.then(function () { msg = '全部接入完成 ✓'; tick[1]() }).catch(function (e) { err = e.message; tick[1]() })
|
|
384
|
+
}
|
|
385
|
+
var cards = []
|
|
386
|
+
if (!sources.length) {
|
|
387
|
+
cards.push(h('div', { 'data-dam-muted': '' }, '未检测到其他 AI 工具的记忆文件(WorkBuddy/CodeBuddy/Claude Code/Codex)。'))
|
|
388
|
+
}
|
|
389
|
+
for (var i = 0; i < sources.length; i++) {
|
|
390
|
+
(function (s) {
|
|
391
|
+
var actions = []
|
|
392
|
+
if (s.kind === 'sessions') {
|
|
393
|
+
actions.push(h('span', { 'data-dam-hint': '' }, '会话源:共 ' + s.fileCount + ' 个会话文件,用 memory_recall 按需检索'))
|
|
394
|
+
} else {
|
|
395
|
+
actions.push(h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, 'project') } }, busy[s.id] ? '接入中…' : '接入项目笔记'))
|
|
396
|
+
actions.push(h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, 'user') } }, '接入用户记忆'))
|
|
397
|
+
}
|
|
398
|
+
cards.push(h(Card, { title: s.name + ' · ' + s.tool + (s.enabled === false ? ' · 已停用' : '') },
|
|
399
|
+
h('div', { 'data-dam-hint': '' }, s.kind + ' · ' + s.fileCount + ' 个文件 · ' + fmtSize(s.size)),
|
|
400
|
+
s.preview ? h('div', { 'data-dam-content': '' }, s.preview) : null,
|
|
401
|
+
h('div', { 'data-dam-row': '' }, actions)))
|
|
402
|
+
})(sources[i])
|
|
403
|
+
}
|
|
404
|
+
return h('div', null,
|
|
405
|
+
h('div', { 'data-dam-hint': '' }, '把其他 AI 工具(WorkBuddy / CodeBuddy / Claude Code / Codex / 项目约定文件)积累的记忆接入当前 DSH 工作。接入后内容写入本地记忆并自动标注来源,后续会随会话自动注入。'),
|
|
406
|
+
h('div', { 'data-dam-row': '' },
|
|
407
|
+
h('button', { 'data-dam-btn': '', onClick: importAll }, '一键接入全部'),
|
|
408
|
+
h('button', { 'data-dam-btn': '', onClick: load }, '重新扫描')),
|
|
409
|
+
msg ? h('div', null, msg) : null,
|
|
410
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null,
|
|
411
|
+
cards)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ───────────────────────── 拖动/缩放辅助 ─────────────────────────
|
|
415
|
+
var dragActive = false
|
|
416
|
+
function startPointerDrag(onMove, interactiveSel) {
|
|
417
|
+
return function (e) {
|
|
418
|
+
// 命中交互控件(按钮/输入框等)时不启动拖动
|
|
419
|
+
if (interactiveSel && e.target && e.target.closest && e.target.closest(interactiveSel)) return
|
|
420
|
+
e.preventDefault()
|
|
421
|
+
e.stopPropagation()
|
|
422
|
+
var startX = e.clientX
|
|
423
|
+
var startY = e.clientY
|
|
424
|
+
var moved = false
|
|
425
|
+
function move(ev) {
|
|
426
|
+
var dx = ev.clientX - startX
|
|
427
|
+
var dy = ev.clientY - startY
|
|
428
|
+
if (!moved && Math.abs(dx) < 2 && Math.abs(dy) < 2) return
|
|
429
|
+
moved = true
|
|
430
|
+
if (!dragActive) { dragActive = true; emit() }
|
|
431
|
+
onMove(dx, dy)
|
|
432
|
+
}
|
|
433
|
+
function up() {
|
|
434
|
+
window.removeEventListener('pointermove', move)
|
|
435
|
+
window.removeEventListener('pointerup', up)
|
|
436
|
+
if (dragActive) { dragActive = false; emit() }
|
|
437
|
+
controller.flushGeom()
|
|
438
|
+
}
|
|
439
|
+
window.addEventListener('pointermove', move)
|
|
440
|
+
window.addEventListener('pointerup', up)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function MemoryPanel() {
|
|
445
|
+
var tick = useTick()
|
|
446
|
+
var tab = 'overview'
|
|
447
|
+
var g = controller.geom()
|
|
448
|
+
useEffect(function () { return controller.subscribe(tick[1]) }, [])
|
|
449
|
+
if (!panelOpen && !panelClosing) return null
|
|
450
|
+
var body
|
|
451
|
+
if (tab === 'overview') body = h(OverviewTab)
|
|
452
|
+
else if (tab === 'logs') body = h(LogsTab)
|
|
453
|
+
else if (tab === 'notes') body = h(NotesTab)
|
|
454
|
+
else if (tab === 'reflections') body = h(ReflectionsTab)
|
|
455
|
+
else if (tab === 'connect') body = h(ConnectTab)
|
|
456
|
+
else body = h(SearchTab)
|
|
457
|
+
var tabs = [['overview', '概览'], ['logs', '日志'], ['notes', '笔记'], ['reflections', '反思'], ['connect', '接续'], ['search', '检索']]
|
|
458
|
+
var style = {
|
|
459
|
+
left: g.left + 'px',
|
|
460
|
+
top: g.top + 'px',
|
|
461
|
+
width: g.width + 'px',
|
|
462
|
+
height: g.height + 'px',
|
|
463
|
+
}
|
|
464
|
+
var dragMove = startPointerDrag(function (dx, dy) {
|
|
465
|
+
controller.setGeom({ left: g.left + dx, top: g.top + dy })
|
|
466
|
+
}, '[data-dam-btn], [data-dam-tab], [data-dam-input], [data-dam-select], textarea')
|
|
467
|
+
var resizeMove = startPointerDrag(function (dx, dy) {
|
|
468
|
+
controller.setGeom({ width: g.width + dx, height: g.height + dy })
|
|
469
|
+
})
|
|
470
|
+
return h('div', {
|
|
471
|
+
'data-dam-panel': '',
|
|
472
|
+
style: style,
|
|
473
|
+
'data-closing': panelClosing ? 'true' : undefined,
|
|
474
|
+
'data-dragging': dragActive ? 'true' : undefined,
|
|
475
|
+
},
|
|
476
|
+
h('header', {
|
|
477
|
+
title: '拖动移动',
|
|
478
|
+
onPointerDown: dragMove,
|
|
479
|
+
},
|
|
480
|
+
h('strong', null, '自动记忆'),
|
|
481
|
+
h('span', { className: 'dam-spacer' }),
|
|
482
|
+
h('button', { 'data-dam-btn': '', title: '恢复默认位置', onClick: function () { controller.resetGeom() } }, '⤾'),
|
|
483
|
+
h('button', { 'data-dam-btn': '', title: '刷新', onClick: function () { tick[1]() } }, '⟳'),
|
|
484
|
+
h('button', { 'data-dam-btn': '', title: '关闭', onClick: function () { controller.close() } }, '✕')),
|
|
485
|
+
h('div', { 'data-dam-tabs': '' }, tabs.map(function (t) {
|
|
486
|
+
return h('button', { key: t[0], 'data-dam-tab': '', 'data-active': tab === t[0] ? 'true' : undefined, onClick: function () { tab = t[0]; tick[1]() } }, t[1])
|
|
487
|
+
})),
|
|
488
|
+
h('div', { 'data-dam-body': '' }, body),
|
|
489
|
+
h('div', { 'data-dam-resize': '', title: '拖动调整大小', onPointerDown: resizeMove }))
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ───────────────────────── 设置页 ─────────────────────────
|
|
493
|
+
var STYLE_OPTIONS = [['auto', '由内容决定'], ['life', '生活化'], ['professional', '专业性']]
|
|
494
|
+
function SettingsPage() {
|
|
495
|
+
var tick = useTick()
|
|
496
|
+
var cfg = null
|
|
497
|
+
var busy = false
|
|
498
|
+
var msg = ''
|
|
499
|
+
var err = ''
|
|
500
|
+
useEffect(function () {
|
|
501
|
+
var alive = true
|
|
502
|
+
apiGet(API.config).then(function (d) { if (alive) { cfg = d.config; tick[1]() } }).catch(function (e) { err = e.message; tick[1]() })
|
|
503
|
+
return function () { alive = false }
|
|
504
|
+
}, [])
|
|
505
|
+
if (!cfg) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
|
|
506
|
+
function set(key, value) { cfg[key] = value; tick[1]() }
|
|
507
|
+
function save() {
|
|
508
|
+
if (busy) return
|
|
509
|
+
busy = true; msg = ''; err = ''
|
|
510
|
+
apiPost(API.config, cfg).then(function (d) { cfg = d.config; msg = '已保存 ✓'; busy = false; tick[1]() })
|
|
511
|
+
.catch(function (e) { err = e.message; busy = false; tick[1]() })
|
|
512
|
+
}
|
|
513
|
+
function field(label, control, hint) {
|
|
514
|
+
return h('div', null,
|
|
515
|
+
h('div', { 'data-dam-row': '' }, h('label', null, label), control),
|
|
516
|
+
hint ? h('div', { 'data-dam-hint': '' }, hint) : null)
|
|
517
|
+
}
|
|
518
|
+
return h('div', null,
|
|
519
|
+
h('div', { 'data-dam-hint': '' }, '记忆存储与行为设置(保存到 DSH 主目录 dsh-auto-memory.json):'),
|
|
520
|
+
field('用户记忆目录', h('input', { 'data-dam-input': '', value: cfg.userMemoryDir, onChange: function (e) { set('userMemoryDir', e.target.value) } }), '跨项目规则存放处,支持 ~ 开头;需有文件写权限。'),
|
|
521
|
+
field('项目记忆目录', h('input', { 'data-dam-input': '', value: cfg.projectMemoryDir, onChange: function (e) { set('projectMemoryDir', e.target.value) } }), '相对各工作区的目录名(默认 .dsh-memory)。'),
|
|
522
|
+
field('注入记忆上下文', h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), '每次组装提示词时自动注入 <memory_system> 块。'),
|
|
523
|
+
field('注入预算(字符)', h('input', { 'data-dam-input': '', type: 'number', min: 400, value: cfg.injectBudgetChars, onChange: function (e) { set('injectBudgetChars', Number(e.target.value) || 2400) } }), '记忆块总预算,超出部分截断。'),
|
|
524
|
+
field('注入最近日志天数', h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 14, value: cfg.recentDaysInjected, onChange: function (e) { set('recentDaysInjected', Number(e.target.value) || 3) } }), '会话开始时注入最近 N 天的工作日志尾部。'),
|
|
525
|
+
field('每日反思', h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), '昨天有工作日志时,会话首轮主动呈现昨日反思。'),
|
|
526
|
+
field('反思风格', h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
|
|
527
|
+
STYLE_OPTIONS.map(function (o) { return h('option', { key: o[0], value: o[0] }, o[1]) })), '生活化 / 专业性 / 由内容决定。'),
|
|
528
|
+
h('div', { 'data-dam-row': '' },
|
|
529
|
+
h('button', { 'data-dam-btn': '', onClick: save, disabled: busy }, busy ? '保存中…' : '保存设置'),
|
|
530
|
+
msg ? h('span', null, msg) : null),
|
|
531
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ───────────────────────── 插件挂载 ─────────────────────────
|
|
535
|
+
function apply(ctx) {
|
|
536
|
+
try { ensureStyle() } catch (e) { console.warn('[dsh-auto-memory] style inject failed', e) }
|
|
537
|
+
ctx.effect(function () {
|
|
538
|
+
return function () {
|
|
539
|
+
var tag = document.getElementById(STYLE_ID)
|
|
540
|
+
if (tag) tag.remove()
|
|
541
|
+
if (closeTimer) { clearTimeout(closeTimer); closeTimer = null }
|
|
542
|
+
}
|
|
543
|
+
}, 'dsh-auto-memory: styles')
|
|
544
|
+
|
|
545
|
+
var slots = ctx.slots
|
|
546
|
+
if (!slots) { console.warn('[dsh-auto-memory] slots service unavailable'); return }
|
|
547
|
+
|
|
548
|
+
try {
|
|
549
|
+
slots.inject('sidebar.footer.action', function () {
|
|
550
|
+
return slots.register({ name: 'sidebar.footer.action', id: 'auto-memory', order: 5, label: '记忆' }, function () { return h(SidebarButton) })
|
|
551
|
+
})
|
|
552
|
+
slots.inject('shell.overlay', function () {
|
|
553
|
+
return slots.register({ name: 'shell.overlay', id: 'auto-memory', order: 5 }, function () { return h(MemoryPanel) })
|
|
554
|
+
})
|
|
555
|
+
slots.inject('settings.section', function () {
|
|
556
|
+
return slots.register({ name: 'settings.section', id: 'auto-memory', order: 25, label: '自动记忆' }, function (props) { return h(SettingsPage, { close: props && props.close }) })
|
|
557
|
+
})
|
|
558
|
+
} catch (e) {
|
|
559
|
+
console.warn('[dsh-auto-memory] slot registration failed', e)
|
|
560
|
+
}
|
|
561
|
+
console.log('[dsh-auto-memory] client ready: sidebar entry + panel + settings page')
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
exports.inject = ['slots']
|
|
565
|
+
exports.apply = apply
|
|
566
|
+
return module.exports
|
|
567
|
+
},
|
|
568
|
+
})
|
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,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.18",
|
|
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
|
+
}
|