dsh-memory-eternal 0.7.0-rc.8 → 0.7.0

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "dsh-memory-eternal",
4
- "version": "0.7.0-rc.8",
4
+ "version": "0.7.0",
5
5
  "description": "璁板繂鏍稿績锛圡emory Eternal锛夛細鐭ヨ瘑鍗¤嚜鍔ㄦ矇娣€/鍙洖锛岀粺涓€ ~/.dsh/memory-vault锛屼細璇濈粨鏉熻嚜鍔ㄥ叆搴撹繘寰呭鏍搞€?,
6
6
  "author": {
7
7
  "name": "EternalNight996",
@@ -21,3 +21,14 @@
21
21
 
22
22
 
23
23
 
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-memory-eternal",
3
- "version": "0.7.0-rc.8",
3
+ "version": "0.7.0",
4
4
  "description": "璁板繂鏍稿績锛圡emory Eternal锛夛細鐭ヨ瘑鍗¤嚜鍔ㄦ矇娣€/鍙洖锛岀粺涓€ ~/.dsh/memory-vault锛屼細璇濈粨鏉熻嚜鍔ㄥ叆搴撹繘寰呭鏍搞€?,
5
5
  "author": {
6
6
  "name": "EternalNight996",
@@ -16,3 +16,14 @@
16
16
 
17
17
 
18
18
 
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-memory-eternal",
3
- "version": "0.7.0-rc.8",
3
+ "version": "0.7.0",
4
4
  "description": "璁板繂鏍稿績锛圡emory Eternal锛夛細鐭ヨ瘑鍗¤嚜鍔ㄦ矇娣€/鍙洖锛岀粺涓€ ~/.dsh/memory-vault锛屼細璇濈粨鏉熻嚜鍔ㄥ叆搴撹繘寰呭鏍搞€?,
5
5
  "author": {
6
6
  "name": "EternalNight996",
@@ -15,3 +15,14 @@
15
15
 
16
16
 
17
17
 
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
package/README.en.md CHANGED
@@ -41,9 +41,9 @@ After **restarting dsh web**, three things are live immediately:
41
41
  | `memory_recall` tool | Agent calls it automatically when it needs history |
42
42
  | Visual UI | Sidebar bottom `Memory` button / Settings → Memory |
43
43
 
44
- **The sidebar now has 2 entries**: `Memory` (browse the vault) and `⚙ Config` (edit settings + see each Agent's mount status).
44
+ **Entry**: The sidebar-footer `Memory` button opens the vault (its left rail includes Cards / Graph / Usage / **Audit Center** / Recycle Bin / **Memory Config**); "DSH Settings → Memory" is the pure config page.
45
45
 
46
- **Edit config**: Sidebar `⚙ Config` → DSH memory config (dedup threshold / recall limit etc.) + self-hosting (web port / keep-alive mode / watchdog etc.) — just edit and hit save. `autoWebMode` / `watchdogAutoSpawn` changes require a DSH restart to take effect.
46
+ **Edit config**: Vault left rail "Memory Config" (or DSH Settings → Memory) → DSH memory config / cost control / auto-audit config / self-hosting — just hit "Save Config". `autoWebMode` / `watchdogAutoSpawn` changes require a DSH restart.
47
47
 
48
48
  ### 🟨 Claude Code
49
49
 
@@ -98,7 +98,7 @@ Three concepts, don't mix them:
98
98
  - **Watchdog process** (`watchdogAutoSpawn`, default on) → a **standalone** node process that can pull web up even after DSH exits (~+47 MB RAM). Only turn on for 7×24 keep-alive.
99
99
  - **Auto-mount MCP** (`autoMcpSetup`, default off) → whether to auto-write MCP into Claude Code/Codex/Cursor config. Off = don't touch your machine config; run `dsh-memory setup` manually when needed.
100
100
 
101
- **Change these**: Sidebar `⚙ Config` → edit the table and save; or edit Settings → Memory directly.
101
+ **Change these**: Vault left rail "Memory Config" (or DSH Settings → Memory) → edit the table and save; `autoWebMode` / `watchdogAutoSpawn` need a DSH restart.
102
102
 
103
103
  **MCP is a protocol, not a resident service**: the agent spawns it per session and exits when done — no "auto-start on boot" concept.
104
104
 
@@ -114,7 +114,7 @@ Three concepts, don't mix them:
114
114
 
115
115
  ## ⚙️ Memory Config (plain words)
116
116
 
117
- > All settings live in the **left sidebar "Memory Config"** (or Settings → Memory top) — edit and hit "Save". Here's the full page:
117
+ > All settings live in the **vault left rail "Memory Config"** (or DSH Settings → Memory) — edit and hit "Save". Here's the full page (plugin info / Agent MCP mount status / auto-audit config):
118
118
 
119
119
  <p align="center">
120
120
  <img src="https://raw.githubusercontent.com/EternalNight996/dsh-memory-eternal/main/assets/screen/memory-config.png" width="880" alt="Memory config page" />
@@ -161,6 +161,19 @@ Top of the config page: **🟢 A Light / 💰 B Budget / ⭐ C Premium** — cli
161
161
 
162
162
  ---
163
163
 
164
+ ## 🛡️ Audit Center & Recycle Bin
165
+
166
+ New cards go to the **Audit Center** (`pending`) by default and enter the main vault only after you approve them; rejected ones move to "Rejected", where you can restore or delete to the recycle bin. Cards matching an exemption (audit mode = skip all / exempt agents / exempt kinds) go straight in; recycle-bin cards are recoverable within 30 days, then auto-purged.
167
+
168
+ <p align="center">
169
+ <img src="https://raw.githubusercontent.com/EternalNight996/dsh-memory-eternal/main/assets/screen/audit-center.png" width="880" alt="Audit Center" />
170
+ </p>
171
+
172
+ - **Pending / Rejected** tabs, filtered by type / date / agent; select all and approve / reject / delete-to-recycle in one click.
173
+ - Rules in "Memory Config → Auto-audit config": `audit mode` (audit all / skip all) + `exempt agents` + `exempt kinds` + `recycle retention days`.
174
+
175
+ ---
176
+
164
177
  ## 🧬 Why Fully Self-built
165
178
 
166
179
  Most memory solutions lean on third-party frameworks / spin up an MCP service / lock memory in a private store. This plugin builds the skeleton itself — **zero third-party runtime deps**, logic readable line by line:
package/README.md CHANGED
@@ -41,9 +41,9 @@ cd ~/.dsh/profiles/web && pnpm add dsh-memory-eternal@latest
41
41
  | `memory_recall` 工具 | Agent 需要历史时自动调用 |
42
42
  | 图形化界面 | 侧边栏底部「记忆」按钮 / 设置 → 记忆 |
43
43
 
44
- **侧边栏现在有 2 个入口**:`记忆`(看库)和`⚙ 配置`(改配置 + 看各 Agent 挂载状态)。
44
+ **入口**:侧边栏底部「记忆」按钮 → 记忆库(左栏含 知识卡 / 知识图谱 / 用量 / **审核中心** / 回收中心 / **记忆配置**);「DSH 设置 → 记忆」= 纯配置页。
45
45
 
46
- **改配置**:左侧栏 `⚙ 配置` → DSH 记忆配置(去重阈值/召回条数等)+ 服务自管理(web 端口/保活模式/看门狗等)直接改,点保存即写入。`autoWebMode`/`watchdogAutoSpawn` 的改动需要重启 DSH 才生效。
46
+ **改配置**:记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)→ DSH 记忆配置 / 成本控制 / 自动审核配置 / 服务自管理,点「保存配置」即写入。`autoWebMode`/`watchdogAutoSpawn` 的改动需重启 DSH 生效。
47
47
 
48
48
  ### 🟨 Claude Code
49
49
 
@@ -110,7 +110,7 @@ dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
110
110
  - **看门狗进程**(`watchdogAutoSpawn`,默认开)→ 一个**独立** node 进程,DSH 退出了它也能拉起 web(约 +47 MB 内存)。只在要 7×24 保活时开。
111
111
  - **自动挂载 MCP**(`autoMcpSetup`,默认关)→ 是否自动把 MCP 写进 Claude Code/Codex/Cursor 配置。关 = 不碰你本机配置文件,需要时手动 `dsh-memory setup`。
112
112
 
113
- **改这些**:左侧栏 `⚙ 配置` → 表格里改,点保存;或直接编辑 设置 → 记忆。
113
+ **改这些**:记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)→ 表格里改,点「保存配置」;`autoWebMode`/`watchdogAutoSpawn` 需重启 DSH 生效。
114
114
 
115
115
  **MCP 是协议不是常驻服务**:agent 开会话才 spawn,用完即退,没有「开机自启」一说。
116
116
 
@@ -126,7 +126,7 @@ dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
126
126
 
127
127
  ## ⚙️ 记忆配置(大白话)
128
128
 
129
- > 所有配置都在 **左侧栏「记忆配置」**(或 设置 → 记忆 顶部)里改,点「保存配置」生效。下图就是配置页全貌:
129
+ > 所有配置都在 **记忆库左栏「记忆配置」**(或 DSH 设置 → 记忆)里改,点「保存配置」生效。下图就是配置页全貌(含插件信息 / Agent MCP 挂载状态 / 自动审核配置):
130
130
 
131
131
  <p align="center">
132
132
  <img src="https://raw.githubusercontent.com/EternalNight996/dsh-memory-eternal/main/assets/screen/memory-config.png" width="880" alt="记忆配置页" />
@@ -173,6 +173,19 @@ dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
173
173
 
174
174
  ---
175
175
 
176
+ ## 🛡️ 审核中心 & 回收中心
177
+
178
+ 新卡默认进**审核中心**(`pending`),由你确认后才入主库;驳回的进「已驳回」,可恢复或删除进回收站。命中免审条件(审核模式=全部免审 / 免审智能体 / 免审类型)的新卡直接入库;回收站软删卡 30 天内可恢复,超期自动永久删除。
179
+
180
+ <p align="center">
181
+ <img src="https://raw.githubusercontent.com/EternalNight996/dsh-memory-eternal/main/assets/screen/audit-center.png" width="880" alt="审核中心" />
182
+ </p>
183
+
184
+ - **待审核 / 已驳回** 双页签,按类型 / 日期 / 智能体筛选,全选后一键「批准 / 驳回 / 删除进回收站」。
185
+ - 审核规则在「记忆配置 → 自动审核配置」:`审核模式`(全部要审 / 全部免审)+ `免审智能体` + `免审类型` + `回收保留天数`。
186
+
187
+ ---
188
+
176
189
  ## 🧬 为什么全自研
177
190
 
178
191
  市面上记忆方案多,但大多依赖第三方框架 / 动不动起 MCP 服务 / 记忆锁私有库。本插件把骨架自己搭,**零第三方运行时依赖**,逻辑逐行可读:
package/index.js CHANGED
@@ -91,7 +91,7 @@ export const Config = z.object({
91
91
 
92
92
  const API_PREFIX = '/memory-eternal/api'
93
93
  // DSH 宿主自动沉淀卡的署名:用可读名而非 agent 会话 id,便于在智能体筛选中归组。
94
- const DSH_AGENT = 'DeepSeek Harness'
94
+ const DSH_AGENT = 'deepseek-harness'
95
95
 
96
96
  export function apply(ctx, config) {
97
97
  const settings = ctx.settings.register('memory-eternal', Config, { base: config ?? {} })
@@ -124,6 +124,15 @@ export function apply(ctx, config) {
124
124
  }
125
125
  return roots
126
126
  }
127
+ // 把完整配置写入共享文件,使独立 web / MCP hook 捕获与 DSH 设置同步(不同步修复)。
128
+ const syncConfigFile = async () => {
129
+ try {
130
+ const cfg = settings.get() ?? {}
131
+ const { configFilePath } = await import('./lib/capture-run.js')
132
+ await (await import('node:fs')).promises.writeFile(configFilePath(process.env), JSON.stringify(cfg, null, 2), 'utf8')
133
+ } catch { /* 静默 */ }
134
+ }
135
+ syncConfigFile()
127
136
  // agent/turn-stopping 是 serial 事件:不在监听器里 await LLM(会拖慢收尾),
128
137
  // 同步抓取增量事件快照后,把真正的捕获调度到后台队列执行。
129
138
  const pending = new Map() // sessionId -> merged events array
@@ -413,7 +422,7 @@ export function apply(ctx, config) {
413
422
  vaultDir: vaultDir(),
414
423
  version: versionRef,
415
424
  }
416
- return json(res, 200, { ok: true, config: safe, revision: me?.revision ?? 0, schema: me?.schema ?? null, dsh: dshInfo, version: versionRef })
425
+ return json(res, 200, { ok: true, config: safe, revision: me?.revision ?? 0, writable: true, readonly: false, schema: me?.schema ?? null, dsh: dshInfo, version: versionRef })
417
426
  }
418
427
  if (method === 'POST') {
419
428
  let raw = ''
@@ -430,6 +439,8 @@ export function apply(ctx, config) {
430
439
  if (typeof settings.update === 'function') {
431
440
  try {
432
441
  await settings.update(clean)
442
+ // 把完整配置写入共享文件,让独立 web / MCP hook 与 DSH 设置同步(不同步修复)
443
+ syncConfigFile()
433
444
  return json(res, 200, { ok: true, applied: Object.keys(clean), note: '已保存。autoWebMode/watchdogAutoSpawn 等需重启 DSH 生效' })
434
445
  } catch (e) {
435
446
  if (e && e.code === 'SETTINGS_CONFLICT') return json(res, 409, { ok: false, error: '配置已被外部修改,请刷新后重试(revision conflict)' })
package/lib/api.js CHANGED
@@ -38,8 +38,11 @@ export function createApi(deps = {}) {
38
38
  case '/cards': {
39
39
  const kind = query.get('kind') || ''
40
40
  const q = query.get('q') || ''
41
+ const status = (query.get('status') || 'approved').toLowerCase()
41
42
  const limit = Math.min(Number(query.get('limit')) || 200, 500)
42
- let cards = await listCards(vaultRoot)
43
+ // all = 全部(含 pending/rejected,排除回收站);默认/approved = 已审核。
44
+ const want = status === 'all' ? ['approved', 'pending', 'rejected'] : status === 'pending' || status === 'rejected' ? [status] : ['approved']
45
+ let cards = await listCards(vaultRoot, { status: want })
43
46
  if (kind) cards = cards.filter((c) => c.kind === kind)
44
47
  if (q.trim()) {
45
48
  const hits = await search(vaultRoot, q, { limit: 200 })
@@ -166,7 +169,7 @@ export function createApi(deps = {}) {
166
169
  const text = c.text || ''
167
170
  let kind = c.kind || 'knowledge', title = c.title || '导入记忆', tags = [], body = text, source = ''
168
171
  try { const p = parseCard(text); kind = p.meta.kind || kind; title = p.meta.title || title; tags = p.meta.tags || tags; body = p.body; source = p.meta.source } catch {}
169
- const r = await writeCard(vaultRoot, { kind, title, tags, body, source })
172
+ const r = await writeCard(vaultRoot, { kind, title, tags, body, source, status: 'pending' })
170
173
  if (r.ok) imported++; else skipped++
171
174
  }
172
175
  json(res, 200, { ok: true, imported, skipped })
@@ -278,7 +281,7 @@ export function createApi(deps = {}) {
278
281
  autoWeb: cfg.autoWeb ?? env.MEMORY_AUTO_WEB !== '0', autoWebMode: cfg.autoWebMode || 'init', webPort: Number(cfg.webPort) || Number(env.MEMORY_WEB_PORT) || 7999, webCheckIntervalMs: Number(cfg.webCheckIntervalMs) || 5000, webMaxRestart: Number(cfg.webMaxRestart) || 10, watchdogAutoSpawn: cfg.watchdogAutoSpawn ?? env.MEMORY_WATCHDOG === '1', autoMcpSetup: cfg.autoMcpSetup ?? false,
279
282
  auditMode: cfg.auditMode ?? 'all', auditExemptAgents: cfg.auditExemptAgents ?? [], auditExemptKinds: cfg.auditExemptKinds ?? [], recycleRetentionDays: cfg.recycleRetentionDays ?? 30,
280
283
  }
281
- json(res, 200, { ok: true, config: safe, revision: 0, version: env.MEMORY_ETERNAL_VERSION || '' })
284
+ json(res, 200, { ok: true, config: safe, revision: 0, writable: false, readonly: true, version: env.MEMORY_ETERNAL_VERSION || '' })
282
285
  return
283
286
  }
284
287
  case '/compress': {
@@ -7,6 +7,7 @@
7
7
 
8
8
  import path from 'node:path'
9
9
  import os from 'node:os'
10
+ import fs from 'node:fs'
10
11
  import { captureCard, captureUpdate, pickNeighbors, summarizeTurn } from './capture.js'
11
12
  import { createLlmFromEnv } from './llm-openai.js'
12
13
 
@@ -16,6 +17,18 @@ export function defaultVaultDir(env = process.env) {
16
17
  return path.join(home, 'memory-vault')
17
18
  }
18
19
 
20
+ // 读取 DSH 设置的完整配置(写入的共享文件),使独立 web / MCP hook 捕获与 DSH 设置同步。
21
+ const CONFIG_FILE = 'memory-eternal-config.json'
22
+ export function configFilePath(env = process.env) {
23
+ return path.join(env.DSH_HOME || path.join(os.homedir(), '.dsh'), CONFIG_FILE)
24
+ }
25
+ function readConfig(env = process.env) {
26
+ try {
27
+ return JSON.parse(fs.readFileSync(configFilePath(env), 'utf8'))
28
+ } catch { return null }
29
+ }
30
+ function splitList(x) { return (Array.isArray(x) ? x : String(x || '').split(',')).map((s) => String(s).trim()).filter(Boolean) }
31
+
19
32
  /**
20
33
  * 沉淀一段对话文本到 vault。
21
34
  * @returns {Promise<{ok:boolean, action:'created'|'appended'|'skipped'|'failed', path?:string, reason?:string, degraded?:boolean}>}
@@ -24,13 +37,15 @@ export async function runStandaloneCapture(vaultRoot, text, { source = '', llm,
24
37
  const clean = String(text || '').trim()
25
38
  if (clean.length < minChars) return { ok: false, action: 'skipped', reason: 'text too short' }
26
39
 
27
- // 自动审核:从 env 读免审配置(独立进程无 DSH settings)
40
+ // 自动审核:共享配置文件(DSH)优先,其次 env,默认 all(独立进程无 DSH settings)
28
41
  const resolveAuditStatus = (kind, sub) => {
29
- if ((env.MEMORY_AUDIT_MODE || 'all') === 'none') return 'approved'
30
- const agents = (env.MEMORY_AUDIT_EXEMPT_AGENTS || '').split(',').map((x) => x.trim()).filter(Boolean)
31
- const kinds = (env.MEMORY_AUDIT_EXEMPT_KINDS || '').split(',').map((x) => x.trim()).filter(Boolean)
32
- if (agents.includes(sub)) return 'approved'
33
- if (kinds.includes(kind)) return 'approved'
42
+ const shared = readConfig(env)
43
+ const mode = shared?.auditMode || env.MEMORY_AUDIT_MODE || 'all'
44
+ const agents = shared?.auditExemptAgents ? splitList(shared.auditExemptAgents) : splitList(env.MEMORY_AUDIT_EXEMPT_AGENTS || '')
45
+ const kinds = shared?.auditExemptKinds ? splitList(shared.auditExemptKinds) : splitList(env.MEMORY_AUDIT_EXEMPT_KINDS || '')
46
+ if (mode === 'none') return 'approved'
47
+ if (agents.includes('__all__') || agents.includes(sub)) return 'approved'
48
+ if (kinds.includes('__all__') || kinds.includes(kind)) return 'approved'
34
49
  return 'pending'
35
50
  }
36
51