@yottameta/yotta-memory 0.12.1 → 0.13.1
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/CHANGELOG.md +27 -1
- package/README.md +9 -5
- package/README.zh-CN.md +8 -5
- package/SKILL.md +25 -5
- package/USER_GUIDE.md +10 -1
- package/bin/yotta-memory.js +270 -15
- package/package.json +3 -2
- package/references/faq.md +6 -3
- package/references/protocol.md +2 -2
- package/skill-manifest.json +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## v0.13.1 (2026-09-13)
|
|
2
|
+
|
|
3
|
+
- 清理发布文档与测试夹具中的本机专属盘符 / 路径示例,统一改为 `~/.yottamemory` 或占位路径。
|
|
4
|
+
- 修复 `forget --unsafe` 未透传到核心的缺陷,并补 CLI 回归测试;显式授权清理其它 owner 私密条目时行为与帮助文案一致。
|
|
5
|
+
- 发布前新增机器专属路径硬编码扫描闸门后,此类问题不允许再进入发布件。
|
|
6
|
+
|
|
7
|
+
## v0.13.0 (2026-09-13)
|
|
8
|
+
|
|
9
|
+
**P0-4.6 元忆 after_milestone 试点**:
|
|
10
|
+
|
|
11
|
+
- 新增 `skill-manifest.json`,声明 `after_milestone` / `remember_commit` / `fallback: explicit-unverified`。
|
|
12
|
+
- 里程碑记忆必须有真实 `file_path` 证据才允许标记 verified;缺证据或写入失败时输出 `explicit-unverified` + 一次纠偏。
|
|
13
|
+
- 元阁适配器回归覆盖成功与降级两条路径。
|
|
14
|
+
|
|
15
|
+
## v0.12.2 (2026-09-12)
|
|
16
|
+
|
|
17
|
+
**可靠性收口:开工 doctor + 破坏性操作前事务快照**
|
|
18
|
+
|
|
19
|
+
- 修正 `init --force` 的过期拒绝文案:明确已有记忆库的强制重建必须由完整备份与显式确认保护,当前版本不提供覆盖初始化路径。
|
|
20
|
+
- 新增 `yotta-memory doctor [--json]`:只读检查记忆库根目录、加密库密钥文件、公共索引、`agents.json` 与最近备份;严重异常返回非零退出码,并锁定破坏性写入。
|
|
21
|
+
- 新增 MCP `doctor` 工具;`context` 的开工可靠性提醒改为直接使用 doctor 结果,critical 时明确提示“破坏性写入已锁定”。
|
|
22
|
+
- `maintain --apply`(含 `--dedup --apply`)、`consolidate --apply`、`merge`、`archive` 与 `--purge` 在写入前自动创建新的整库事务快照;未配置独立备份目录、doctor critical 或快照失败时拒绝写入,原记忆保持不变。
|
|
23
|
+
- `--allow-same-volume` 仍只用于 `backup create` 的显式临时备份,不会绕过破坏性写入门;不存在 CLI 级跳过快照开关。
|
|
24
|
+
- 每次事务快照写入 `.archive/audit-<日期>.jsonl`(transaction / operation / snapshot 记录);`forget` 继续只进 `.trash/`,不重复做整库快照。
|
|
25
|
+
- 新增 `test/reliability-doctor.test.js` 与 `test/reliability-destructive-guard.test.js`;既有 consolidate / maintain 回归补齐临时快照目录。
|
|
26
|
+
|
|
1
27
|
## v0.12.1 (2026-09-12)
|
|
2
28
|
|
|
3
29
|
安装与更新文档修复(无功能变更):
|
|
@@ -10,7 +36,7 @@
|
|
|
10
36
|
|
|
11
37
|
**可靠性基线(误删事故整改)**:
|
|
12
38
|
|
|
13
|
-
- `init` 对已有记忆库默认拒绝覆盖;新增 `--attach` 接入现有库;`--force`
|
|
39
|
+
- `init` 对已有记忆库默认拒绝覆盖;新增 `--attach` 接入现有库;`--force` 也不能覆盖已有记忆库,防止初始化再次清空记忆。
|
|
14
40
|
- `forget` 不再物理删除,改为移动到 `.trash/<timestamp>/<原相对路径>`,并写 `audit-<date>.jsonl` 审计记录。
|
|
15
41
|
- 新增 `backup create / list / doctor / restore <id> --to <目录>`:备份 `facts/`、`private/`、`keys/`(排除 `keys/cache/` 授权缓存)、`agents.json`、`index.json`、`.archive/`,生成 SHA-256 清单;默认拒绝同卷备份;`restore` 默认只恢复到新目录,不覆盖正在使用的记忆库。
|
|
16
42
|
- 新增每日自动备份闭环:`backup volumes` 只枚举当前机器实际存在、可写、与记忆库异卷的路径;用户确认一次位置后 `backup setup` 默认启用每日备份(默认 03:30),Windows Task Scheduler / Linux systemd user timer(cron 降级)/ macOS LaunchAgent 负责无人值守,`serve` 启动与每 6 小时用幂等 `backup ensure-daily` 补跑。
|
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
> 📖 The user-facing operations manual lives in [USER_GUIDE.md](USER_GUIDE.md).
|
|
25
25
|
|
|
26
|
+
> 🆕 **v0.12.2**: reliability closure — `yotta-memory doctor` checks the store, key material, index, identity registry and latest backup; `maintain --apply`, `consolidate --apply`, `merge`, `archive` and `--purge` create a transaction snapshot before writing and refuse to proceed if the snapshot fails.
|
|
27
|
+
|
|
26
28
|
> 🆕 **v0.12.1**: installation and update docs now distinguish the engine CLI (`yotta-memory`) from the skill installer (`yotta-memory-install`), with copy-ready upgrade commands.
|
|
27
29
|
|
|
28
30
|
> 🆕 **v0.12.0**: reliability baseline — `init` refuses to overwrite an existing store (`--attach` to attach); `forget` moves entries into `.trash/`; `backup create / list / doctor / restore` provides independent-volume backups with SHA-256 manifests.
|
|
@@ -43,7 +45,8 @@ Most memory solutions treat "remembering" as a black box: data goes into a datab
|
|
|
43
45
|
- **Grows smarter (v0.6.0)** — `profile` aggregates a user profile (the engine infers nothing; it only groups verbatim text) + `context` generates a one-shot start-of-work package (identity + profile + recent memory + boundaries + commitments); the SKILL "memory discipline" injects rule layers (type red lines / trigger signals / know the user / bottom lines / host isolation) — rules and mechanisms only, no personality data; zero data out of the box.
|
|
44
46
|
- **Self-learning / self-evolving / self-improving (v0.8.0)** — `recall` semantic search (synonyms / pinyin full + initials / field weighting / fuzzy match, zero-dependency) with utility-score blended ranking; `feedback` explicit usage feedback loop (useful / useless adjusts weight / confidence / feedback_net); `maintain` rule-layer self-organization (unified utility score + age-based auto-archive / forget candidates / dedup, dry-run by default, immutable / BOUND exempt); `distill` psychological-log distillation (statistical summary / topic profile / knowledge map, optional `--model` external model enhancement).
|
|
45
47
|
- **Compression & forgetting (v0.10.0) — memory that never bloats** — `consolidate` summarizes old, low-use memories on the same topic into one **provenance-carrying periodic summary** that stays in active memory (every original file is listed as provenance; originals move to `.archive/`; `--undo <batch>` restores everything); `maintain --dedup` scores near-duplicates and `--apply` auto-merges high-confidence groups; the utility recency component now decays **per type** (FACT slow / PREF medium / COMMIT task-like fast / BOUND never) so durable facts are not wiped by time and stale commitments step aside quickly; every batch is auditable via `consolidate --batches`.
|
|
46
|
-
- **Reliability baseline (v0.12.0)** — `init` refuses to overwrite an existing store and `--attach` attaches instead; `forget` moves entries to `.trash/` with an audit record; `backup create / list / doctor / restore` backs up the store to an independent volume with a SHA-256 manifest and restores only to a new directory.
|
|
48
|
+
- **Reliability baseline (v0.12.0 / v0.12.2)** — `init` refuses to overwrite an existing store and `--attach` attaches instead; `forget` moves entries to `.trash/` with an audit record; `backup create / list / doctor / restore` backs up the store to an independent volume with a SHA-256 manifest and restores only to a new directory; `yotta-memory doctor` checks the store, key material, index, identity registry and latest backup, and destructive writes take a transaction snapshot first.
|
|
49
|
+
- **Milestone hook declaration** — `skill-manifest.json` declares `after_milestone` / `remember_commit`; a milestone memory write is verified only with a real file-path record.
|
|
47
50
|
- **Private-zone encryption (v0.7.0)** — private files AES-256-GCM envelope encrypted (passphrase-derived master key + recovery key + per-owner encrypted index); `yotta-memory view` user review platform (unlock with passphrase to see all AI memory).
|
|
48
51
|
|
|
49
52
|
### Memory types
|
|
@@ -193,14 +196,14 @@ This package exposes two separate commands:
|
|
|
193
196
|
**init**:
|
|
194
197
|
|
|
195
198
|
```text
|
|
196
|
-
Memory store initialized:
|
|
199
|
+
Memory store initialized: ~/.yottamemory
|
|
197
200
|
Master password set; recovery key: xxxx-xxxx-xxxx-xxxx (keep it safe)
|
|
198
201
|
```
|
|
199
202
|
|
|
200
203
|
**remember** (with verify):
|
|
201
204
|
|
|
202
205
|
```text
|
|
203
|
-
Recorded:
|
|
206
|
+
Recorded: ~/.yottamemory/facts/2026-09-01-0001.md
|
|
204
207
|
[verify] read-back OK: facts/2026-09-01-0001.md
|
|
205
208
|
```
|
|
206
209
|
|
|
@@ -208,7 +211,7 @@ Recorded: D:/.yottamemory/facts/2026-09-01-0001.md
|
|
|
208
211
|
|
|
209
212
|
```text
|
|
210
213
|
3 memories (top 3):
|
|
211
|
-
[FACT] subject: statement... (
|
|
214
|
+
[FACT] subject: statement... (~/.yottamemory/facts/xxx.md)
|
|
212
215
|
```
|
|
213
216
|
|
|
214
217
|
**context**:
|
|
@@ -267,6 +270,7 @@ Optional post-upgrade self-check: `yotta-memory config get` (confirm `memory_hom
|
|
|
267
270
|
| `yotta-memory profile [--owner <id>]` | Generate a user profile (aggregates `private/<owner>` verbatim, zero inference, writes `profile.md`; cross-owner denied by default) |
|
|
268
271
|
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <text>] [--explain] [--embedding <cmd>]` | Generate the start-of-work package (identity + multi-agent rules + profile + task-focused memory + recent memory + boundaries + commitments; --budget caps chars, --focus adds task relevance, --explain shows included/dropped) |
|
|
269
272
|
| `yotta-memory forget <file>` | Delete a memory (by type-dir path or file name) |
|
|
273
|
+
| `yotta-memory doctor [--json]` | Start-of-work reliability check (store / key material / index / identity / latest backup; critical issues lock destructive writes) |
|
|
270
274
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | Archive old memory (decay-blended utility + age; immutable / BOUND exempt; private to `.archive/private/<owner>/<type>/`) |
|
|
271
275
|
| `yotta-memory reindex` | Rebuild the index (after manually editing .md) |
|
|
272
276
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | Export / import |
|
|
@@ -348,7 +352,7 @@ Register the connection in the agent's MCP config (`url` + two headers):
|
|
|
348
352
|
}
|
|
349
353
|
```
|
|
350
354
|
|
|
351
|
-
Once connected, MCP tools (remember / recall / search / context / forget / archive / reindex / export / import / agent_info) read/write memory and confirm identity; management actions (init / config / token / lan / serve) are not exposed via MCP, and token management is never exposed remotely. MCP `export` / `import` paths are restricted inside the memory root, MCP `distill` does not support `--model`, and MCP never accepts a raw embedding command from remote callers — the local embedding plugin must be configured on the engine host with `config set embedding_cmd`. `X-Agent-Id` must match the token's registered agent; read-partition rules are the same as the CLI (FACT public-readable, PREF / BOUND / COMMIT private).
|
|
355
|
+
Once connected, MCP tools (remember / recall / search / context / doctor / forget / archive / reindex / export / import / agent_info) read/write memory and confirm identity; management actions (init / config / token / lan / serve) are not exposed via MCP, and token management is never exposed remotely. MCP `export` / `import` paths are restricted inside the memory root, MCP `distill` does not support `--model`, and MCP never accepts a raw embedding command from remote callers — the local embedding plugin must be configured on the engine host with `config set embedding_cmd`. `X-Agent-Id` must match the token's registered agent; read-partition rules are the same as the CLI (FACT public-readable, PREF / BOUND / COMMIT private).
|
|
352
356
|
|
|
353
357
|
### Location persistence
|
|
354
358
|
|
package/README.zh-CN.md
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
|
|
24
24
|
> 📖 面向用户的操作手册见 [USER_GUIDE.md](USER_GUIDE.md)。
|
|
25
25
|
|
|
26
|
+
> 🆕 **v0.12.2**:可靠性收口——新增 `yotta-memory doctor` 开工检查;`maintain --apply`、`consolidate --apply`、`merge`、`archive` 与 `--purge` 在写入前自动创建事务快照,快照失败或严重异常时拒绝写入。
|
|
27
|
+
|
|
26
28
|
> 🆕 **v0.12.1**:安装与更新文档明确区分引擎 CLI(`yotta-memory`)和技能安装器(`yotta-memory-install`),并补齐可直接复制的升级命令。
|
|
27
29
|
|
|
28
30
|
> 🆕 **v0.12.0**:可靠性基线——`init` 对已有记忆库默认拒绝覆盖(用 `--attach` 接入);`forget` 先移入 `.trash/` 并留审计;`backup create / list / doctor / restore` 支持独立盘备份、SHA-256 清单校验和只恢复到新目录。
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
- **越用越懂(v0.6.0)**:`profile` 聚合用户画像(引擎零推断,只归组原文)+ `context` 一键生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺);SKILL「记忆守则」注入规则层(类型红线 / 触发信号 / 了解用户 / 底线 / 宿主隔离),只注入规则与机制、不注入人格数据,出厂零数据。
|
|
48
50
|
- **自我学习 / 自我进化 / 自我提升(v0.8.0)**:`recall` 语义检索(同义词 / 拼音全拼+首字母 / 字段加权 / 模糊匹配,零依赖)+ 效用分融合排序;`feedback` 显式使用反馈闭环(useful / useless 调整 weight / confidence / feedback_net,越用越懂);`maintain` 规则层自组织(统一效用分 + 年龄自动归档 / 遗忘候选 / 去重,默认 dry-run,immutable / BOUND 豁免);`distill` 心理日志蒸馏(统计摘要 / 主题画像 / 知识地图,可选 `--model` 外部模型增强)。
|
|
49
51
|
- **压缩遗忘(v0.10.0)——记忆越用越精简**:`consolidate` 周期摘要压缩——把超龄 + 长期闲置 + 低效用的同主题旧记忆归纳成 **1 条带溯源的摘要**(每条原文路径都写在正文里)留在活跃区,原文整体进 `.archive/`,`--undo <batch>` 一键回滚;`maintain --dedup` 给近重复打分(≥0.85 高置信自动合并 / 0.65–0.85 建议手动),`--apply` 批量合并同归属重复组;时效分量改**分类型衰减**(FACT 慢 / PREF 中 / COMMIT 任务类快 / BOUND 永不衰减)——持久事实不被时间抹掉,过期承诺快速让位;每一步写批次审计(`--batches` 可查)。
|
|
50
|
-
- **可靠性基线(v0.12.0)**:`init` 遇到已有记忆库默认拒绝覆盖(`--attach` 用于接入);`forget` 先移入 `.trash/` 并写删除审计;`backup create / list / doctor / restore` 支持独立盘备份、SHA-256
|
|
52
|
+
- **可靠性基线(v0.12.0 / v0.12.2)**:`init` 遇到已有记忆库默认拒绝覆盖(`--attach` 用于接入);`forget` 先移入 `.trash/` 并写删除审计;`backup create / list / doctor / restore` 支持独立盘备份、SHA-256 清单校验、恢复默认只写新目录;`yotta-memory doctor` 检查根目录 / 密钥库 / 索引 / 身份 / 最近备份,破坏性操作在写入前自动创建事务快照。
|
|
51
53
|
- **私密区加密(v0.7.0)**:私密区文件 AES-256-GCM 信封加密(口令派生主密钥 + 恢复钥匙 + 每 owner 加密索引);`yotta-memory view` 用户查看平台(口令解锁看全部 AI 记忆);`migrate` 明文→密文迁移;`--no-encrypt` 可降级。跨 AI 私密从「纪律层隔离」升级为「机制层不可解」。
|
|
52
54
|
- **便携记忆盘**:记忆库本身就是记忆引擎——装在硬盘或主机上随盘走,局域网内其它主机上的智能体可远程读写;本地零进程与局域网常驻两种模式可并存,插上硬盘即恢复全部记忆。
|
|
53
55
|
|
|
@@ -233,14 +235,14 @@ bash install.sh --list # 列出智能体 -> 默认目录
|
|
|
233
235
|
**init(初始化记忆库)**:
|
|
234
236
|
|
|
235
237
|
```text
|
|
236
|
-
|
|
238
|
+
初始化记忆库成功:~/.yottamemory
|
|
237
239
|
主口令已设置;恢复钥匙:xxxx-xxxx-xxxx-xxxx(请妥善保存)
|
|
238
240
|
```
|
|
239
241
|
|
|
240
242
|
**remember(写入记忆,带 verify 回读)**:
|
|
241
243
|
|
|
242
244
|
```text
|
|
243
|
-
已记录:
|
|
245
|
+
已记录: ~/.yottamemory/facts/2026-09-01-0001.md
|
|
244
246
|
[verify] 已写回读 OK: facts/2026-09-01-0001.md
|
|
245
247
|
```
|
|
246
248
|
|
|
@@ -248,7 +250,7 @@ bash install.sh --list # 列出智能体 -> 默认目录
|
|
|
248
250
|
|
|
249
251
|
```text
|
|
250
252
|
共 3 条记忆(前 3 条):
|
|
251
|
-
[FACT] 项目名:
|
|
253
|
+
[FACT] 项目名: 描述……(~/.yottamemory/facts/xxx.md)
|
|
252
254
|
```
|
|
253
255
|
|
|
254
256
|
**context(开工上下文包)**:
|
|
@@ -309,6 +311,7 @@ bash install.sh --agent <智能体名称>
|
|
|
309
311
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
|
|
310
312
|
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <命令>]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 任务相关记忆 + 近期记忆 + 边界 + 承诺;--budget 字符预算;--focus 任务聚焦;--explain 输出 included/dropped 选择解释)|
|
|
311
313
|
| `yotta-memory forget <文件>` | 删除一条记忆(按类型目录路径或文件名)|
|
|
314
|
+
| `yotta-memory doctor [--json]` | 开工可靠性检查(根目录 / 密钥库 / 索引 / 身份 / 最近备份;严重异常时锁定破坏性写入)|
|
|
312
315
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(分类型衰减效用分 + 年龄;immutable / BOUND 豁免;私密入 `.archive/private/<owner>/<type>/`)|
|
|
313
316
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
314
317
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
|
|
@@ -390,7 +393,7 @@ yotta-memory recall --type FACT --limit 10
|
|
|
390
393
|
}
|
|
391
394
|
```
|
|
392
395
|
|
|
393
|
-
连接后可通过 MCP tools(remember / recall / search / context / forget / archive / reindex / export / import / agent_info)读写记忆与确认身份;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露;MCP export/import 路径限记忆库内、distill 不支持 `--model`,MCP 也不接受远端传入 embedding 命令——embedding 插件只能由引擎主机本地 `config set embedding_cmd` 配置。`X-Agent-Id` 必须与 token 登记的智能体一致;读取分区规则与 CLI 相同(FACT 公共可读,PREF / BOUND / COMMIT 私密隔离)。
|
|
396
|
+
连接后可通过 MCP tools(remember / recall / search / context / doctor / forget / archive / reindex / export / import / agent_info)读写记忆与确认身份;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露;MCP export/import 路径限记忆库内、distill 不支持 `--model`,MCP 也不接受远端传入 embedding 命令——embedding 插件只能由引擎主机本地 `config set embedding_cmd` 配置。`X-Agent-Id` 必须与 token 登记的智能体一致;读取分区规则与 CLI 相同(FACT 公共可读,PREF / BOUND / COMMIT 私密隔离)。
|
|
394
397
|
|
|
395
398
|
### 位置持久化
|
|
396
399
|
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yotta-memory
|
|
3
|
-
description: 元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/可回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、记忆守则、profile、context、越用越懂、语义检索、反馈、维护、蒸馏、feedback、maintain、distill、explain、自我学习、自我进化、自我提升、查看平台分页、recall 候选预过滤、任务相关记忆、--focus、--embedding、压缩遗忘、consolidate、周期摘要、自动合并、分类型衰减、回滚、备份、backup
|
|
4
|
-
version: 0.
|
|
3
|
+
description: 元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/可回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆、画像、开工上下文、记忆守则、profile、context、越用越懂、语义检索、反馈、维护、蒸馏、feedback、maintain、distill、explain、自我学习、自我进化、自我提升、查看平台分页、recall 候选预过滤、任务相关记忆、--focus、--embedding、压缩遗忘、consolidate、周期摘要、自动合并、分类型衰减、回滚、备份、backup、防误删、doctor、事务快照
|
|
4
|
+
version: 0.13.1
|
|
5
5
|
license: MIT
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -20,6 +20,8 @@ license: MIT
|
|
|
20
20
|
- **召回质量与上下文选择(v0.9.0)**:`recall` 支持可选本地 embedding 插件(`--embedding <command>` / `config set embedding_cmd <command>`);`context --focus <关键词>` 生成任务感知上下文;`--explain` 输出选择 trace,无插件时自动降级为词法检索。
|
|
21
21
|
- **压缩遗忘(v0.10.0)**:记忆库长期可用不膨胀——`consolidate` 周期摘要压缩(把超龄 + 低效用 + 长期闲置的同主题旧记忆归纳成**带溯源**的摘要,原文整体进 `.archive/`,`--undo` 一键回滚);`maintain --dedup` 近重复**自动合并**(置信度分档,`--apply` 批量执行高置信组);效用分时效改为**分类型衰减**(FACT 慢 / PREF 中 / COMMIT 任务类快 / BOUND 不衰减);`consolidate --batches` 批次审计可查。
|
|
22
22
|
- **可靠性基线(v0.12.0)**:`init` 对非空记忆库默认拒绝覆盖(`--attach` 接入现有库);`forget` 先移入 `.trash/` 并写审计;新增 `backup volumes / setup / status / ensure-daily / schedule / drill`(用户确认真实独立卷后默认每日自动备份)与 `backup create / list / doctor / restore`。
|
|
23
|
+
- **可靠性收口(v0.12.2)**:新增 `yotta-memory doctor` 开工检查(根目录 / 密钥库 / 索引 / 身份 / 最近备份);`maintain --apply`、`consolidate --apply`、`merge`、`archive`、`--purge` 在写入前自动创建事务快照,快照失败或严重检查异常时拒绝写入。
|
|
24
|
+
- **运行时 hook 声明(v0.13.0)**:manifest 声明 `after_milestone` / `remember_commit`;里程碑记忆必须有真实文件路径证据才标 verified,缺证据时输出 `explicit-unverified` + 一次纠偏。
|
|
23
25
|
|
|
24
26
|
## 安装 / 更新(两个 bin)
|
|
25
27
|
|
|
@@ -42,7 +44,7 @@ AI 更新流程:先运行 `yotta-memory --version` 记录当前引擎版本;
|
|
|
42
44
|
3. **收工归档**:写会话小结(COMMIT / 笔记);旧记录定期 `yotta-memory maintain --apply`(单条低效用归档)+ 记忆多了周期 `yotta-memory consolidate --apply`(同主题压缩成带溯源摘要,`--undo` 可回滚)。
|
|
43
45
|
4. **多智能体纪律**:FACT 写入公共区,PREF / BOUND / COMMIT 只写本智能体私密区;不读取其他智能体私密区。**一切读写一律走 `yotta-memory` CLI / MCP 工具**——禁止用 shell(`Get-ChildItem` / `Get-Content` / `cat` / `ls` / `type` 等)直接读或改记忆库目录下的 `.md` / `index.json` / `tokens.json` / `agents.json` / `grants.json` 等文件,否则会绕过权限边界、读到别的智能体私密内容。
|
|
44
46
|
|
|
45
|
-
## 可靠性基线(v0.12.0)
|
|
47
|
+
## 可靠性基线(v0.12.0 / v0.12.2)
|
|
46
48
|
|
|
47
49
|
**目的**:防止初始化覆盖、删除不可逆、备份缺失再次造成记忆库丢失。
|
|
48
50
|
|
|
@@ -50,7 +52,7 @@ AI 更新流程:先运行 `yotta-memory --version` 记录当前引擎版本;
|
|
|
50
52
|
|
|
51
53
|
- 目标已是记忆库时,`yotta-memory init` 默认拒绝覆盖。
|
|
52
54
|
- 接入已有库:`yotta-memory init --attach`。
|
|
53
|
-
- `--force`
|
|
55
|
+
- `--force` 不能覆盖已有记忆库;强制重建需要完整备份与显式确认保护,当前版本不提供覆盖初始化路径。
|
|
54
56
|
- 非 owner 智能体不得初始化、重建或清空记忆库。
|
|
55
57
|
|
|
56
58
|
### 2. forget 回收区
|
|
@@ -111,6 +113,23 @@ yotta-memory backup drill [<备份ID>]
|
|
|
111
113
|
- 恢复目标非空时拒绝覆盖;恢复后先校验,再决定是否替换正式记忆库。
|
|
112
114
|
- 备份是可靠性基线的一部分;没有备份时,不得执行永久删除或覆盖初始化。
|
|
113
115
|
|
|
116
|
+
### 5. 开工 doctor 与事务快照
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# 开工检查:只读,不修改记忆库
|
|
120
|
+
yotta-memory doctor
|
|
121
|
+
|
|
122
|
+
# 机器可读输出
|
|
123
|
+
yotta-memory doctor --json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `doctor` 检查记忆库根目录、加密库密钥文件、公共索引、`agents.json` 与最近备份。
|
|
127
|
+
- 严重异常(根目录缺失、密钥库缺文件、备份目录同卷)会返回非零退出码,并锁定破坏性写入。
|
|
128
|
+
- `maintain --apply`、`consolidate --apply`、`merge`、`archive` 与 `--purge` 在执行前自动创建事务快照;未配置独立备份目录或快照失败时拒绝写入,原记忆保持不变。
|
|
129
|
+
- 不提供 CLI 跳过快照的开关;`--allow-same-volume` 只用于 `backup create` 的显式临时备份,不会绕过破坏性写入门。
|
|
130
|
+
- `context` 会展示 doctor 的 warning / critical;critical 时明确提示“破坏性写入已锁定”。
|
|
131
|
+
- `forget` 仍只移入 `.trash/`,不重复创建整库快照。
|
|
132
|
+
|
|
114
133
|
|
|
115
134
|
## 记忆守则(Memory Doctrine,v0.6.0)
|
|
116
135
|
|
|
@@ -245,6 +264,7 @@ yotta-memory backup drill [<备份ID>]
|
|
|
245
264
|
| `yotta-memory forget <文件>` | 移入 `.trash/<时间>/` 回收区并写审计(v0.12.0;不再物理删除)|
|
|
246
265
|
| `yotta-memory backup volumes / setup --dir <目录> / status / ensure-daily / schedule enable|disable|status` | 每日自动备份(v0.12.0;只展示实际枚举的异卷、用户确认一次位置后默认每日执行,Windows Task Scheduler / systemd timer / launchd 调度,`serve` 补跑)|
|
|
247
266
|
| `yotta-memory backup create / list / doctor / restore <ID> --to <目录> / drill [<ID>]` | 备份、恢复与恢复演练(v0.12.0;独立盘校验、SHA-256 清单、排除 `keys/cache`、恢复默认只写新目录;drill 验证 manifest / 索引 / 测试私密解密)|
|
|
267
|
+
| `yotta-memory doctor [--json]` | 开工可靠性检查(v0.12.2;根目录 / 密钥库 / 索引 / 身份 / 最近备份;严重异常时锁定破坏性写入)|
|
|
248
268
|
| `yotta-memory archive [--days 180] [--threshold 0.35]` | 归档旧记忆(v0.8.0 统一效用分 + v0.10.0 分类型衰减;immutable / BOUND 豁免;私密归档入 `.archive/private/<owner>/<type>/`;阈值默认读 config `maintain_archived_utility`)|
|
|
249
269
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
250
270
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
|
|
@@ -320,7 +340,7 @@ yotta-memory backup drill [<备份ID>]
|
|
|
320
340
|
|---|---|---|
|
|
321
341
|
| 用户级(默认) | `~/.yottamemory` | 个人跨项目记忆 |
|
|
322
342
|
| 项目级 | `<repo>/.yottamemory` | 随项目提交共享 |
|
|
323
|
-
| 便携记忆盘 |
|
|
343
|
+
| 便携记忆盘 | 任意盘符或挂载点下的目录(如 `<memory-disk>/yottamemory`) | 记忆盘 / 局域网共享 |
|
|
324
344
|
|
|
325
345
|
**步骤 0.3 接入现有 vs 初始化新库(关键判断)**
|
|
326
346
|
1. 检查目标目录是否已是记忆库:存在 `facts/` 等子目录或 `index.json`。
|
package/USER_GUIDE.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
3.6 自我学习 / 自我进化 / 自我提升(v0.8.0)
|
|
12
12
|
3.7 查看平台分页与检索优化(v0.8.1)
|
|
13
13
|
3.8 可靠性基线:防覆盖、回收区与备份(v0.12.0)
|
|
14
|
+
3.9 开工 doctor 与事务快照(v0.12.2)
|
|
14
15
|
4. 便携记忆盘 · 记忆引擎主机篇(Linux / Windows)
|
|
15
16
|
5. 智能体接入篇(本机 / 局域网其它主机)
|
|
16
17
|
6. CLI 命令速查
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
- **便携记忆盘**:记忆装在固定主机上,本机与局域网其它主机共享同一份记忆(见第 4 / 5 篇)。
|
|
33
34
|
- **越用越懂(v0.6.0)**:AI 按「记忆守则」主动捕获信号,`profile` 聚合画像、`context` 开工注入——用得越久越懂你。
|
|
34
35
|
- **自我学习 / 自我进化 / 自我提升(v0.8.0 + v0.9.0)**:`recall` 语义检索(同义词 / 拼音 / 字段加权 / 模糊,v0.9.0 可选本地 embedding 插件)+ `feedback` 使用反馈闭环 + `maintain` 规则层自组织 + `distill` 心理日志蒸馏——记忆系统会自己整理、提炼、演化。
|
|
35
|
-
- **可靠性基线(v0.12.0)**:`init` 对已有库拒绝覆盖;`forget` 进回收区;`backup volumes/setup/status/ensure-daily/schedule/drill`
|
|
36
|
+
- **可靠性基线(v0.12.0 / v0.12.2)**:`init` 对已有库拒绝覆盖;`forget` 进回收区;`backup volumes/setup/status/ensure-daily/schedule/drill` 在用户确认真实独立卷后默认每日自动备份,并提供校验、恢复与恢复演练;`doctor` 开工检查风险,破坏性操作写入前自动创建事务快照。
|
|
36
37
|
|
|
37
38
|
## 2. 安装(CLI + 技能)
|
|
38
39
|
|
|
@@ -228,6 +229,13 @@ statement: 本周完成发布
|
|
|
228
229
|
- `yotta-memory backup status` 查看目录、计划、上次成功、最近失败与调度状态;`backup drill` 恢复到隔离副本,校验 manifest / 索引并解密一条测试私密。
|
|
229
230
|
- 备份目录与记忆库同卷时默认拒绝;这是一条防线,不替代独立盘备份。
|
|
230
231
|
|
|
232
|
+
## 3.9 开工 doctor 与事务快照(v0.12.2)
|
|
233
|
+
|
|
234
|
+
- `yotta-memory doctor`:只读检查记忆库根目录、加密库密钥文件、公共索引、`agents.json` 与最近备份;加 `--json` 可输出机器可读结果。
|
|
235
|
+
- 严重异常(根目录缺失、密钥库缺文件、备份目录同卷)会返回非零退出码;此时 `context` 也会显示“破坏性写入已锁定”。
|
|
236
|
+
- `maintain --apply`、`consolidate --apply`、`merge`、`archive` 与 `--purge` 在写入前自动创建新的整库事务快照,并在 `.archive/audit-<日期>.jsonl` 记录 transaction / operation / snapshot。
|
|
237
|
+
- 未配置独立备份目录、doctor critical 或快照失败时,命令直接拒绝执行,原记忆保持不变;`forget` 仍只移入 `.trash/`,不重复创建整库快照。
|
|
238
|
+
|
|
231
239
|
## 4. 便携记忆盘 · 记忆引擎主机篇
|
|
232
240
|
|
|
233
241
|
场景:记忆放在一台主机上(Linux / Windows 均可),本机直接 CLI 读写;局域网内其它主机上的 AI 智能体经 MCP 远程接入。引擎主机只需装 CLI,不需要装任何 AI 智能体。
|
|
@@ -365,6 +373,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
365
373
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(零推断,写 `profile.md`)|
|
|
366
374
|
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <命令>]` | 开工上下文包(身份+铁律+画像+任务相关记忆+近期记忆+边界+承诺;--focus 任务聚焦;--explain 输出 included/dropped 选择解释)|
|
|
367
375
|
| `yotta-memory forget <文件>` | 删除一条记忆 |
|
|
376
|
+
| `yotta-memory doctor [--json]` | 开工可靠性检查(v0.12.2:根目录 / 密钥库 / 索引 / 身份 / 最近备份;严重异常时锁定破坏性写入)|
|
|
368
377
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(分类型衰减效用分 + 年龄;immutable / BOUND 豁免;私密入 `.archive/private/<owner>/<type>/`)|
|
|
369
378
|
| `yotta-memory reindex` | 重建索引 |
|
|
370
379
|
| `yotta-memory export [--out 文件.json]` / `import <文件.json>` | 导出 / 导入 |
|
package/bin/yotta-memory.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// v0.7.0 新增:私密区机制级加密(AES-256-GCM 信封加密 + PBKDF2 主密钥 + 恢复钥匙)/ 每 owner 加密索引 / 用户查看平台 yotta-memory view / migrate 迁移 / reset-password / key 授权 / context 收工纪律 / init 新建默认加密(--no-encrypt 降级)
|
|
14
14
|
// v0.8.0 新增:自我学习/自我进化/自我提升——语义检索(同义词/拼音/字段加权/模糊 + 可选 embedding 插件协议预留)/ feedback 显式反馈闭环(weight/confidence/feedback_net 演化)/ maintain 规则层自组织(统一效用分 utility + 归档/遗忘/去重,默认 dry-run)/ distill 心理日志蒸馏(启发式统计/主题画像/知识地图 + 可选 --model)
|
|
15
15
|
// v0.6.2 修复:remember --verify 写后回读改为直查索引 + 权限判定 + 召回匹配性(不再依赖 recall top-N 排序,消除泛化 subject 下偶发误报「回读未命中」)
|
|
16
|
+
// v0.12.2 新增:开工 doctor(根目录/密钥库/索引/身份/最近备份)+ 破坏性操作前事务快照门(maintain/consolidate/merge/archive/purge)
|
|
16
17
|
'use strict';
|
|
17
18
|
|
|
18
19
|
const fs = require('fs');
|
|
@@ -22,7 +23,7 @@ const crypto = require('crypto');
|
|
|
22
23
|
const http = require('http');
|
|
23
24
|
const child_process = require('child_process');
|
|
24
25
|
|
|
25
|
-
const VERSION = '0.
|
|
26
|
+
const VERSION = '0.13.1';
|
|
26
27
|
// MCP 协议(2026-07-28 无状态 + 2025-11-25 legacy 握手,dual-era)
|
|
27
28
|
const MCP_PROTOCOL_MODERN = '2026-07-28';
|
|
28
29
|
const MCP_PROTOCOL_LEGACY = '2025-11-25';
|
|
@@ -1123,7 +1124,7 @@ function initCore(opts) {
|
|
|
1123
1124
|
return { error: false, text: '已接入现有记忆库: ' + root };
|
|
1124
1125
|
}
|
|
1125
1126
|
if (existing && opts.force) {
|
|
1126
|
-
return { error: true, text: '拒绝: --force
|
|
1127
|
+
return { error: true, text: '拒绝: --force 不能覆盖现有记忆库;已有记忆库的强制重建必须由完整备份与显式确认保护,当前版本不提供覆盖初始化路径。如只是接入,请使用 init --attach。' };
|
|
1127
1128
|
}
|
|
1128
1129
|
if (existing) {
|
|
1129
1130
|
return { error: true, text: '拒绝: 目标已是现有记忆库: ' + root + '。如只是接入,请使用 init --attach;不要执行覆盖初始化。' };
|
|
@@ -1615,7 +1616,8 @@ function backupStatusCore(opts) {
|
|
|
1615
1616
|
opts = opts || {};
|
|
1616
1617
|
const root = path.resolve(opts.root || userRoot());
|
|
1617
1618
|
const cfg = loadConfig();
|
|
1618
|
-
const
|
|
1619
|
+
const configuredDir = opts.dir || opts.backupDir || cfg.backup_dir;
|
|
1620
|
+
const dir = configuredDir ? path.resolve(String(configuredDir)) : '';
|
|
1619
1621
|
const maxAge = Number(cfg.backup_max_age_hours) || 36;
|
|
1620
1622
|
if (!dir) {
|
|
1621
1623
|
const manual = cfg.backup_setup_choice === 'manual';
|
|
@@ -1656,6 +1658,7 @@ function backupStatusCore(opts) {
|
|
|
1656
1658
|
configured: true,
|
|
1657
1659
|
enabled: !!cfg.backup_enabled,
|
|
1658
1660
|
healthy: healthy,
|
|
1661
|
+
independent: independent,
|
|
1659
1662
|
latest: latest,
|
|
1660
1663
|
ageHours: ageHours,
|
|
1661
1664
|
text: lines.join('\n'),
|
|
@@ -1776,14 +1779,15 @@ function backupEnsureDailyCore(opts) {
|
|
|
1776
1779
|
function backupHealthCore(opts) {
|
|
1777
1780
|
opts = opts || {};
|
|
1778
1781
|
const cfg = loadConfig();
|
|
1779
|
-
|
|
1782
|
+
const configuredDir = opts.dir || opts.backupDir || cfg.backup_dir;
|
|
1783
|
+
if (!configuredDir && cfg.backup_setup_choice !== 'manual') {
|
|
1780
1784
|
return {
|
|
1781
1785
|
level: 'warning',
|
|
1782
1786
|
needsSetup: true,
|
|
1783
1787
|
text: '尚未配置独立盘备份。AI 应先运行 yotta-memory backup volumes,只展示当前实际存在的可写异卷;由用户确认目录后,再运行 backup setup 启用每日自动备份。',
|
|
1784
1788
|
};
|
|
1785
1789
|
}
|
|
1786
|
-
if (cfg.backup_setup_choice === 'manual') return { level: 'none', needsSetup: false, text: '' };
|
|
1790
|
+
if (!configuredDir && cfg.backup_setup_choice === 'manual') return { level: 'none', needsSetup: false, text: '' };
|
|
1787
1791
|
const status = backupStatusCore(opts);
|
|
1788
1792
|
const maxAge = Number(cfg.backup_max_age_hours) || 36;
|
|
1789
1793
|
if (cfg.backup_last_error) return { level: 'warning', needsSetup: false, text: '最近一次备份失败: ' + cfg.backup_last_error };
|
|
@@ -1794,6 +1798,190 @@ function backupHealthCore(opts) {
|
|
|
1794
1798
|
}
|
|
1795
1799
|
return { level: 'none', needsSetup: false, text: '' };
|
|
1796
1800
|
}
|
|
1801
|
+
// 开工可靠性检查:只读检查根目录、密钥库、索引、身份登记与最近备份。
|
|
1802
|
+
function doctorCore(opts) {
|
|
1803
|
+
opts = opts || {};
|
|
1804
|
+
const root = path.resolve(opts.root || userRoot());
|
|
1805
|
+
const cfg = loadConfig();
|
|
1806
|
+
const checks = {};
|
|
1807
|
+
const warnings = [];
|
|
1808
|
+
const critical = [];
|
|
1809
|
+
const exists = isExistingStore(root);
|
|
1810
|
+
checks.root = { ok: exists, path: root };
|
|
1811
|
+
if (!exists) critical.push('记忆库不存在或基本结构缺失: ' + root);
|
|
1812
|
+
|
|
1813
|
+
const encrypted = isEncrypted(root);
|
|
1814
|
+
checks.encrypted = { enabled: encrypted };
|
|
1815
|
+
if (encrypted) {
|
|
1816
|
+
const saltOk = fs.existsSync(encSaltPath(root));
|
|
1817
|
+
const recoveryOk = fs.existsSync(encRecoveryEncPath(root));
|
|
1818
|
+
checks.keys = { salt: saltOk, recovery: recoveryOk };
|
|
1819
|
+
if (!saltOk) critical.push('密钥库异常: 缺少 keys/salt');
|
|
1820
|
+
if (!recoveryOk) critical.push('密钥库异常: 缺少 keys/recovery.key.enc(恢复钥匙文件)');
|
|
1821
|
+
} else if (exists && hasPlaintextPrivate(root)) {
|
|
1822
|
+
checks.keys = { mode: 'plaintext' };
|
|
1823
|
+
warnings.push('当前私密区为明文;建议运行 yotta-memory migrate 启用加密。');
|
|
1824
|
+
} else {
|
|
1825
|
+
checks.keys = { mode: 'plaintext' };
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
const idxFile = indexPath(root);
|
|
1829
|
+
if (!fs.existsSync(idxFile)) {
|
|
1830
|
+
checks.index = { exists: false, valid: false };
|
|
1831
|
+
warnings.push('公共索引缺失;可运行 yotta-memory reindex 重建。');
|
|
1832
|
+
} else if (!loadIndex(root)) {
|
|
1833
|
+
checks.index = { exists: true, valid: false };
|
|
1834
|
+
warnings.push('公共索引无法解析或版本过旧;可运行 yotta-memory reindex 重建。');
|
|
1835
|
+
} else {
|
|
1836
|
+
checks.index = { exists: true, valid: true };
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
if (!fs.existsSync(agentsPath(root))) {
|
|
1840
|
+
checks.agents = { exists: false, valid: false };
|
|
1841
|
+
warnings.push('agents.json 缺失;智能体身份登记不可用。');
|
|
1842
|
+
} else {
|
|
1843
|
+
try {
|
|
1844
|
+
JSON.parse(fs.readFileSync(agentsPath(root), 'utf8'));
|
|
1845
|
+
checks.agents = { exists: true, valid: true };
|
|
1846
|
+
} catch (error) {
|
|
1847
|
+
checks.agents = { exists: true, valid: false };
|
|
1848
|
+
warnings.push('agents.json 无法解析;请先恢复身份登记。');
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const configuredDir = opts.dir || opts.backupDir || cfg.backup_dir;
|
|
1853
|
+
if (!configuredDir) {
|
|
1854
|
+
if (cfg.backup_setup_choice === 'manual') {
|
|
1855
|
+
checks.backup = { configured: false, manual: true, independent: false };
|
|
1856
|
+
} else {
|
|
1857
|
+
const health = backupHealthCore({ root: root, now: opts.now, sameVolumeFn: opts.sameVolumeFn });
|
|
1858
|
+
checks.backup = { configured: false, manual: false, independent: false };
|
|
1859
|
+
if (health.text) warnings.push(health.text);
|
|
1860
|
+
}
|
|
1861
|
+
} else {
|
|
1862
|
+
const status = backupStatusCore({
|
|
1863
|
+
root: root,
|
|
1864
|
+
dir: configuredDir,
|
|
1865
|
+
now: opts.now,
|
|
1866
|
+
sameVolumeFn: opts.sameVolumeFn,
|
|
1867
|
+
});
|
|
1868
|
+
checks.backup = {
|
|
1869
|
+
configured: true,
|
|
1870
|
+
manual: false,
|
|
1871
|
+
independent: !!status.independent,
|
|
1872
|
+
latest: status.latest ? status.latest.id : '',
|
|
1873
|
+
ageHours: status.ageHours,
|
|
1874
|
+
healthy: !!status.healthy,
|
|
1875
|
+
};
|
|
1876
|
+
if (!status.independent) {
|
|
1877
|
+
critical.push('备份目录与记忆库在同一卷,不能作为可靠备份。');
|
|
1878
|
+
}
|
|
1879
|
+
if (!status.latest) {
|
|
1880
|
+
warnings.push('备份目录已配置,但还没有成功备份;请运行 yotta-memory backup create 或 backup ensure-daily。');
|
|
1881
|
+
} else {
|
|
1882
|
+
const maxAge = Number(cfg.backup_max_age_hours) || 36;
|
|
1883
|
+
if (status.ageHours !== null && status.ageHours > maxAge) {
|
|
1884
|
+
warnings.push('最近备份已过期(' + status.ageHours.toFixed(1) + ' 小时,超过 ' + maxAge + ' 小时);请检查备份盘与每日调度。');
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
if (cfg.backup_last_error) warnings.push('最近一次备份失败: ' + cfg.backup_last_error);
|
|
1888
|
+
if (cfg.backup_scheduler_error) warnings.push('每日备份调度异常: ' + cfg.backup_scheduler_error);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
const level = critical.length ? 'critical' : (warnings.length ? 'warning' : 'ok');
|
|
1892
|
+
const lines = [
|
|
1893
|
+
'# yotta-memory doctor(开工可靠性检查)',
|
|
1894
|
+
'',
|
|
1895
|
+
'- 结果: ' + (level === 'critical' ? '严重' : (level === 'warning' ? '警告' : '正常')),
|
|
1896
|
+
'- 记忆库: ' + root,
|
|
1897
|
+
'- 加密: ' + (encrypted ? '是' : '否'),
|
|
1898
|
+
'- 备份目录: ' + (configuredDir || (cfg.backup_setup_choice === 'manual' ? '手动模式' : '(未配置)')),
|
|
1899
|
+
];
|
|
1900
|
+
for (const message of critical) lines.push('- [严重] ' + message);
|
|
1901
|
+
for (const message of warnings) lines.push('- [警告] ' + message);
|
|
1902
|
+
if (!critical.length && !warnings.length) lines.push('- 检查项: 全部通过');
|
|
1903
|
+
if (level === 'critical') {
|
|
1904
|
+
lines.push('');
|
|
1905
|
+
lines.push('- 破坏性写入已锁定:先运行 yotta-memory doctor 修复严重问题。');
|
|
1906
|
+
}
|
|
1907
|
+
return {
|
|
1908
|
+
error: false,
|
|
1909
|
+
ok: critical.length === 0,
|
|
1910
|
+
level: level,
|
|
1911
|
+
critical: critical,
|
|
1912
|
+
warnings: warnings,
|
|
1913
|
+
checks: checks,
|
|
1914
|
+
root: root,
|
|
1915
|
+
text: lines.join('\n'),
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
// 破坏性写入前的统一快照门:先 doctor,再创建新的整库快照,最后写事务审计。
|
|
1919
|
+
function destructiveGuardCore(opts) {
|
|
1920
|
+
opts = opts || {};
|
|
1921
|
+
const root = path.resolve(opts.root || userRoot());
|
|
1922
|
+
// snapshotDir / allowSameVolumeForTest 仅用于 core 测试注入;CLI 的 --allow-same-volume 不进入破坏性写入门。
|
|
1923
|
+
const snapshotDir = opts.snapshotDir || opts.backupDir || loadConfig().backup_dir || '';
|
|
1924
|
+
const sameVolumeFn = opts.sameVolumeFn || (opts.allowSameVolumeForTest ? function () { return false; } : undefined);
|
|
1925
|
+
const report = doctorCore({
|
|
1926
|
+
root: root,
|
|
1927
|
+
backupDir: snapshotDir || undefined,
|
|
1928
|
+
now: opts.now,
|
|
1929
|
+
sameVolumeFn: sameVolumeFn,
|
|
1930
|
+
});
|
|
1931
|
+
if (!report.ok) {
|
|
1932
|
+
return {
|
|
1933
|
+
error: true,
|
|
1934
|
+
doctor: report,
|
|
1935
|
+
text: '拒绝: 开工可靠性检查未通过,破坏性操作已锁定。\n' + report.text,
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
if (!snapshotDir) {
|
|
1939
|
+
return {
|
|
1940
|
+
error: true,
|
|
1941
|
+
doctor: report,
|
|
1942
|
+
text: '拒绝: 未配置独立备份目录,不能执行破坏性操作。请先运行 backup volumes,由用户确认位置后执行 backup setup。',
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
const snapshot = backupCreateCore({
|
|
1946
|
+
root: root,
|
|
1947
|
+
dir: snapshotDir,
|
|
1948
|
+
now: opts.now,
|
|
1949
|
+
allowSameVolume: !!opts.allowSameVolumeForTest,
|
|
1950
|
+
sameVolumeFn: sameVolumeFn,
|
|
1951
|
+
});
|
|
1952
|
+
if (snapshot.error) {
|
|
1953
|
+
return {
|
|
1954
|
+
error: true,
|
|
1955
|
+
doctor: report,
|
|
1956
|
+
text: '拒绝: 事务快照失败,原记忆未改动。\n' + snapshot.text,
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
const transaction = 'txn-' + Date.now() + '-' + crypto.randomBytes(3).toString('hex');
|
|
1960
|
+
const auditOk = appendAudit(root, 'audit', {
|
|
1961
|
+
action: 'transaction_start',
|
|
1962
|
+
ts: new Date().toISOString(),
|
|
1963
|
+
transaction: transaction,
|
|
1964
|
+
operation: opts.action || 'destructive',
|
|
1965
|
+
root: root,
|
|
1966
|
+
snapshot: snapshot.id,
|
|
1967
|
+
snapshotPath: snapshot.path,
|
|
1968
|
+
});
|
|
1969
|
+
if (!auditOk) {
|
|
1970
|
+
return {
|
|
1971
|
+
error: true,
|
|
1972
|
+
doctor: report,
|
|
1973
|
+
snapshot: snapshot,
|
|
1974
|
+
text: '拒绝: 事务审计写入失败,原记忆未改动。快照已保留: ' + snapshot.id,
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
return {
|
|
1978
|
+
error: false,
|
|
1979
|
+
doctor: report,
|
|
1980
|
+
snapshot: snapshot,
|
|
1981
|
+
transaction: transaction,
|
|
1982
|
+
text: '事务快照: ' + snapshot.id,
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1797
1985
|
function sha256File(file) {
|
|
1798
1986
|
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
1799
1987
|
}
|
|
@@ -1830,7 +2018,8 @@ function backupCreateCore(opts) {
|
|
|
1830
2018
|
if (!isExistingStore(root)) return { error: true, text: '记忆库不存在或未初始化: ' + root };
|
|
1831
2019
|
const destBase = backupDestination(opts);
|
|
1832
2020
|
if (!destBase) return { error: true, text: '未配置备份目录。请用 --dir <目录>、YOTTA_MEMORY_BACKUP_DIR 或 config set backup_dir <目录>。' };
|
|
1833
|
-
|
|
2021
|
+
const same = opts.sameVolumeFn ? !!opts.sameVolumeFn(root, destBase) : sameVolume(root, destBase);
|
|
2022
|
+
if (!opts.allowSameVolume && same) {
|
|
1834
2023
|
return { error: true, text: '拒绝: 备份目录与记忆库在同一卷。请把备份放到独立盘或独立卷。' };
|
|
1835
2024
|
}
|
|
1836
2025
|
const createdAt = new Date(opts.now || Date.now()).toISOString();
|
|
@@ -2391,6 +2580,16 @@ function archiveCore(opts) {
|
|
|
2391
2580
|
const threshold = (opts.threshold !== undefined && opts.threshold !== null) ? opts.threshold : (parseFloat(cfg.maintain_archived_utility || 0.35));
|
|
2392
2581
|
const root = userRoot();
|
|
2393
2582
|
if (!fs.existsSync(root)) return { error: false, text: '记忆库不存在。' };
|
|
2583
|
+
const guard = destructiveGuardCore({
|
|
2584
|
+
root: root,
|
|
2585
|
+
action: 'archive',
|
|
2586
|
+
snapshotDir: opts.snapshotDir,
|
|
2587
|
+
backupDir: opts.backupDir,
|
|
2588
|
+
allowSameVolumeForTest: opts.allowSameVolumeForTest,
|
|
2589
|
+
sameVolumeFn: opts.sameVolumeFn,
|
|
2590
|
+
now: opts.now,
|
|
2591
|
+
});
|
|
2592
|
+
if (guard.error) return { error: true, text: guard.text };
|
|
2394
2593
|
const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
2395
2594
|
let moved = 0;
|
|
2396
2595
|
const movedFiles = [];
|
|
@@ -2415,7 +2614,7 @@ function archiveCore(opts) {
|
|
|
2415
2614
|
}
|
|
2416
2615
|
}
|
|
2417
2616
|
if (movedFiles.length) removeIndexEntries(root, movedFiles);
|
|
2418
|
-
return { error: false, text: '已归档 ' + moved + ' 条旧记忆到 ' + path.join(root, ARCHIVE_DIR) };
|
|
2617
|
+
return { error: false, text: '已归档 ' + moved + ' 条旧记忆到 ' + path.join(root, ARCHIVE_DIR) + '\n事务快照: ' + guard.snapshot.id };
|
|
2419
2618
|
}
|
|
2420
2619
|
|
|
2421
2620
|
// ---- v0.8.0 自我学习/自我进化/自我提升:feedback / explain / maintain / distill ----
|
|
@@ -2426,7 +2625,8 @@ function appendAudit(root, kind, rec) {
|
|
|
2426
2625
|
try {
|
|
2427
2626
|
fs.mkdirSync(path.join(root, ARCHIVE_DIR), { recursive: true });
|
|
2428
2627
|
fs.appendFileSync(auditPath(root, kind), JSON.stringify(rec) + '\n', 'utf8');
|
|
2429
|
-
|
|
2628
|
+
return true;
|
|
2629
|
+
} catch (e) { return false; }
|
|
2430
2630
|
}
|
|
2431
2631
|
function resolveMemoryTarget(ref) {
|
|
2432
2632
|
const roots = memoryRoots();
|
|
@@ -2561,6 +2761,19 @@ function maintainCore(opts) {
|
|
|
2561
2761
|
const purge = !!opts.purge;
|
|
2562
2762
|
const root = userRoot();
|
|
2563
2763
|
if (!fs.existsSync(root)) return { error: false, text: '记忆库不存在。' };
|
|
2764
|
+
let guard = null;
|
|
2765
|
+
if (apply) {
|
|
2766
|
+
guard = destructiveGuardCore({
|
|
2767
|
+
root: root,
|
|
2768
|
+
action: opts.dedup ? 'maintain --dedup --apply' : (purge ? 'maintain --apply --purge' : 'maintain --apply'),
|
|
2769
|
+
snapshotDir: opts.snapshotDir,
|
|
2770
|
+
backupDir: opts.backupDir,
|
|
2771
|
+
allowSameVolumeForTest: opts.allowSameVolumeForTest,
|
|
2772
|
+
sameVolumeFn: opts.sameVolumeFn,
|
|
2773
|
+
now: opts.now,
|
|
2774
|
+
});
|
|
2775
|
+
if (guard.error) return { error: true, text: guard.text };
|
|
2776
|
+
}
|
|
2564
2777
|
const cfg = loadConfig();
|
|
2565
2778
|
const archU = parseFloat(opts.threshold !== undefined && opts.threshold !== null ? opts.threshold : (cfg.maintain_archived_utility || 0.35));
|
|
2566
2779
|
const archAge = parseInt(opts.age || cfg.maintain_archived_age || '180', 10) || 180;
|
|
@@ -2570,6 +2783,7 @@ function maintainCore(opts) {
|
|
|
2570
2783
|
const lines = ['## yotta-memory maintain(记忆自组织)'];
|
|
2571
2784
|
lines.push('- 模式: ' + mode);
|
|
2572
2785
|
lines.push('- 归档阈值: utility < ' + archU + ' 且年龄 > ' + archAge + ' 天;遗忘阈值: utility < ' + forgetU + ' 且年龄 > ' + forgetAge + ' 天');
|
|
2786
|
+
if (guard) lines.push('- 事务快照: ' + guard.snapshot.id);
|
|
2573
2787
|
lines.push('');
|
|
2574
2788
|
const toArchive = [], toForget = [], skipped = [];
|
|
2575
2789
|
for (const fp of collectEntryFiles(root)) {
|
|
@@ -2660,6 +2874,16 @@ function mergeCore(refA, refB, opts) {
|
|
|
2660
2874
|
}
|
|
2661
2875
|
if (ta.root !== tb.root) return { error: true, text: '暂不支持跨记忆库合并(' + ta.root + ' vs ' + tb.root + ')。' };
|
|
2662
2876
|
const root = ta.root;
|
|
2877
|
+
const guard = destructiveGuardCore({
|
|
2878
|
+
root: root,
|
|
2879
|
+
action: 'merge',
|
|
2880
|
+
snapshotDir: opts.snapshotDir,
|
|
2881
|
+
backupDir: opts.backupDir,
|
|
2882
|
+
allowSameVolumeForTest: opts.allowSameVolumeForTest,
|
|
2883
|
+
sameVolumeFn: opts.sameVolumeFn,
|
|
2884
|
+
now: opts.now,
|
|
2885
|
+
});
|
|
2886
|
+
if (guard.error) return { error: true, text: guard.text };
|
|
2663
2887
|
const ea = readEntry(ta.fp, root);
|
|
2664
2888
|
const eb = readEntry(tb.fp, root);
|
|
2665
2889
|
const keep = ea.confidence >= eb.confidence ? ea : eb;
|
|
@@ -2677,7 +2901,7 @@ function mergeCore(refA, refB, opts) {
|
|
|
2677
2901
|
fs.renameSync(dropFp, dest);
|
|
2678
2902
|
removeIndexEntry(root, drop.file);
|
|
2679
2903
|
appendAudit(root, 'audit', { ts: new Date().toISOString(), file: drop.file, action: 'merge', reason: '并入 ' + keep.file, utility: round3(utilityScore(keep)) });
|
|
2680
|
-
return { error: false, text: '已合并: ' + drop.file + ' -> ' + keep.file + '\n 保留: [' + keep.type + '] ' + keep.subject + ': ' + keep.statement + '\n 归档低 confidence: ' + drop.file };
|
|
2904
|
+
return { error: false, text: '已合并: ' + drop.file + ' -> ' + keep.file + '\n 保留: [' + keep.type + '] ' + keep.subject + ': ' + keep.statement + '\n 归档低 confidence: ' + drop.file + '\n 事务快照: ' + guard.snapshot.id };
|
|
2681
2905
|
}
|
|
2682
2906
|
// 心理日志蒸馏:统计摘要 / 主题画像 / 知识地图(启发式默认,可选 --model)
|
|
2683
2907
|
function distillCore(opts) {
|
|
@@ -3223,8 +3447,11 @@ function cmdDistill(opts) {
|
|
|
3223
3447
|
console.log(r.text);
|
|
3224
3448
|
if (r.error) process.exit(2);
|
|
3225
3449
|
}
|
|
3226
|
-
function cmdForget(fileRef) {
|
|
3227
|
-
const r = forgetCore(fileRef
|
|
3450
|
+
function cmdForget(fileRef, opts) {
|
|
3451
|
+
const r = forgetCore(fileRef, {
|
|
3452
|
+
selfAgent: currentAgent(),
|
|
3453
|
+
unsafe: !!(opts && opts.unsafe),
|
|
3454
|
+
});
|
|
3228
3455
|
console.log(r.text);
|
|
3229
3456
|
if (r.error) process.exit(2);
|
|
3230
3457
|
}
|
|
@@ -3291,6 +3518,12 @@ function cmdBackupDrill(id, opts) {
|
|
|
3291
3518
|
console.log(r.text);
|
|
3292
3519
|
if (r.error) process.exit(2);
|
|
3293
3520
|
}
|
|
3521
|
+
function cmdDoctor(opts) {
|
|
3522
|
+
const r = doctorCore(opts);
|
|
3523
|
+
if (opts.json) console.log(JSON.stringify(r, null, 2));
|
|
3524
|
+
else console.log(r.text);
|
|
3525
|
+
if (!r.ok) process.exit(2);
|
|
3526
|
+
}
|
|
3294
3527
|
function cmdReindex() {
|
|
3295
3528
|
const roots = memoryRoots();
|
|
3296
3529
|
if (!roots.length) { console.log('记忆库不存在。'); return; }
|
|
@@ -3812,12 +4045,14 @@ function contextCore(opts) {
|
|
|
3812
4045
|
if (!trace.length) lines.push('(无选择记录)');
|
|
3813
4046
|
for (const t of trace) lines.push(t);
|
|
3814
4047
|
}
|
|
3815
|
-
const
|
|
3816
|
-
if (
|
|
4048
|
+
const reliability = doctorCore({ root: root });
|
|
4049
|
+
if (reliability.level !== 'ok') {
|
|
3817
4050
|
lines.push('');
|
|
3818
4051
|
lines.push('## 可靠性提醒');
|
|
3819
4052
|
lines.push('');
|
|
3820
|
-
lines.push('- ' +
|
|
4053
|
+
for (const message of reliability.critical) lines.push('- [严重] ' + message);
|
|
4054
|
+
for (const message of reliability.warnings) lines.push('- [警告] ' + message);
|
|
4055
|
+
if (!reliability.ok) lines.push('- 破坏性写入已锁定:先运行 yotta-memory doctor 修复严重问题。');
|
|
3821
4056
|
}
|
|
3822
4057
|
return { error: false, exitCode: 0, text: lines.join('\n') };
|
|
3823
4058
|
}
|
|
@@ -3874,6 +4109,7 @@ function mcpTools() {
|
|
|
3874
4109
|
{ name: 'recall', description: '检索记忆。query 可选;type 可选;limit 可选(默认 20);explain 可选。只返回当前智能体可读记忆;embedding 插件只能由本机 config 配置,远端不可传命令', inputSchema: { type: 'object', properties: { query: { type: 'string' }, type: { type: 'string' }, limit: { type: 'number' }, embeddingTimeout: { type: 'number' }, explain: { type: 'boolean' } } } },
|
|
3875
4110
|
{ name: 'search', description: '检索记忆(同 recall)。query 可选;type 可选;limit 可选(默认 20);explain 可选;embedding 插件只能由本机 config 配置,远端不可传命令', inputSchema: { type: 'object', properties: { query: { type: 'string' }, type: { type: 'string' }, limit: { type: 'number' }, embeddingTimeout: { type: 'number' }, explain: { type: 'boolean' } } } },
|
|
3876
4111
|
{ name: 'context', description: '生成开工上下文包。focus 可选;limit 可选;budget 可选;explain 可选;embedding 插件只能由本机 config 配置,远端不可传命令', inputSchema: { type: 'object', properties: { focus: { type: 'string' }, limit: { type: 'number' }, budget: { type: 'number' }, explain: { type: 'boolean' }, embeddingTimeout: { type: 'number' } } } },
|
|
4112
|
+
{ name: 'doctor', description: '开工可靠性检查:检查记忆库根目录、密钥库、索引、身份登记与最近备份;只读,不会修改记忆', inputSchema: { type: 'object', properties: {} } },
|
|
3877
4113
|
{ name: 'forget', description: '删除一条记忆。file 为记忆文件路径(如 facts/2026-08-24-0001.md 或文件名)', inputSchema: { type: 'object', properties: { file: { type: 'string' } }, required: ['file'] } },
|
|
3878
4114
|
{ name: 'archive', description: '归档旧记忆。days 默认 180;threshold 默认 0.4', inputSchema: { type: 'object', properties: { days: { type: 'number' }, threshold: { type: 'number' } } } },
|
|
3879
4115
|
{ name: 'reindex', description: '重建索引(手动改 .md 后校正;扫描 facts/prefs/bounds/commits 四目录)', inputSchema: { type: 'object', properties: {} } },
|
|
@@ -3917,6 +4153,10 @@ function callTool(name, args, ctx) {
|
|
|
3917
4153
|
});
|
|
3918
4154
|
return { text: r.text, error: r.error };
|
|
3919
4155
|
}
|
|
4156
|
+
if (name === 'doctor') {
|
|
4157
|
+
const r = doctorCore({ selfAgent: agent });
|
|
4158
|
+
return { text: r.text, error: !r.ok };
|
|
4159
|
+
}
|
|
3920
4160
|
if (name === 'forget') {
|
|
3921
4161
|
const r = forgetCore(String(args.file || ''), { selfAgent: agent });
|
|
3922
4162
|
return { text: r.text, error: r.error };
|
|
@@ -4931,9 +5171,20 @@ function consolidateCore(opts) {
|
|
|
4931
5171
|
lines.push('dry-run 未执行任何变更。确认后加 --apply 执行(写入批次审计,可 consolidate --undo <batch> 回滚)。');
|
|
4932
5172
|
return { error: false, text: lines.join('\n') };
|
|
4933
5173
|
}
|
|
5174
|
+
const guard = destructiveGuardCore({
|
|
5175
|
+
root: root,
|
|
5176
|
+
action: 'consolidate --apply',
|
|
5177
|
+
snapshotDir: opts.snapshotDir,
|
|
5178
|
+
backupDir: opts.backupDir,
|
|
5179
|
+
allowSameVolumeForTest: opts.allowSameVolumeForTest,
|
|
5180
|
+
sameVolumeFn: opts.sameVolumeFn,
|
|
5181
|
+
now: opts.now,
|
|
5182
|
+
});
|
|
5183
|
+
if (guard.error) return { error: true, text: guard.text };
|
|
4934
5184
|
const batch = newBatchId();
|
|
4935
5185
|
appendAudit(root, 'audit', { action: 'manifest', ts: new Date().toISOString(), batch: batch, command: 'consolidate', mode: 'apply', root: root, active_before: collectEntryFiles(root).length, undone: false });
|
|
4936
5186
|
lines.push('');
|
|
5187
|
+
lines.push('- 事务快照: ' + guard.snapshot.id);
|
|
4937
5188
|
lines.push('- 批次: ' + batch + '(审计 .archive/audit-<日期>.jsonl;回滚: yotta-memory consolidate --undo ' + batch + ')');
|
|
4938
5189
|
let made = 0, moved = 0;
|
|
4939
5190
|
for (const g of groups) {
|
|
@@ -5094,6 +5345,7 @@ function usage() {
|
|
|
5094
5345
|
['forget', '删除一条记忆'],
|
|
5095
5346
|
['archive', '归档(--days/--threshold 盖棺分+年龄)'],
|
|
5096
5347
|
['backup', '备份记忆库(volumes / setup / status / ensure-daily / schedule / create / list / doctor / restore <id> --to <目录> / drill)'],
|
|
5348
|
+
['doctor', '开工可靠性检查(根目录/密钥库/索引/身份/最近备份;严重异常时锁定破坏性写入)'],
|
|
5097
5349
|
['maintain', '记忆自组织(归档/遗忘候选/去重/合并;默认 dry-run;--dedup 查重+置信度,--dedup --apply 自动合并高置信组)'],
|
|
5098
5350
|
['consolidate', '周期摘要压缩(默认 dry-run;--apply 执行;--undo <batch> 回滚;--batches 查批次;候选=超龄+闲置+低效用,immutable/BOUND 豁免)'],
|
|
5099
5351
|
['distill', '心理日志蒸馏(统计摘要/主题画像/知识地图;--model 可选外部模型)'],
|
|
@@ -5239,7 +5491,7 @@ async function main() {
|
|
|
5239
5491
|
case 'maintain': cmdMaintain(opts); break;
|
|
5240
5492
|
case 'distill': cmdDistill(opts); break;
|
|
5241
5493
|
case 'consolidate': if (opts.undo === true && rest.length) opts.undo = rest[0]; cmdConsolidate(opts); break;
|
|
5242
|
-
case 'forget': cmdForget(rest[0]); break;
|
|
5494
|
+
case 'forget': cmdForget(rest[0], opts); break;
|
|
5243
5495
|
case 'archive': cmdArchive(opts); break;
|
|
5244
5496
|
case 'backup': {
|
|
5245
5497
|
const sub = rest[0];
|
|
@@ -5257,6 +5509,7 @@ async function main() {
|
|
|
5257
5509
|
break;
|
|
5258
5510
|
}
|
|
5259
5511
|
case 'reindex': cmdReindex(); break;
|
|
5512
|
+
case 'doctor': cmdDoctor(opts); break;
|
|
5260
5513
|
case 'profile': cmdProfile(opts); break;
|
|
5261
5514
|
case 'context': cmdContext(opts); break;
|
|
5262
5515
|
case 'export': cmdExport(opts.out); break;
|
|
@@ -5431,6 +5684,8 @@ module.exports = {
|
|
|
5431
5684
|
backupStatusCore: backupStatusCore,
|
|
5432
5685
|
backupEnsureDailyCore: backupEnsureDailyCore,
|
|
5433
5686
|
backupHealthCore: backupHealthCore,
|
|
5687
|
+
doctorCore: doctorCore,
|
|
5688
|
+
destructiveGuardCore: destructiveGuardCore,
|
|
5434
5689
|
backupWindowsTaskXml: backupWindowsTaskXml,
|
|
5435
5690
|
backupSystemdServiceContent: backupSystemdServiceContent,
|
|
5436
5691
|
backupSystemdTimerContent: backupSystemdTimerContent,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yottameta/yotta-memory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Yuanyi (元忆) — boundary-aware, file-based memory for AI agents. File-based, zero-dependency, diff/rollback-able; FACT/PREF/BOUND/COMMIT types (public shared / private isolated), user-level + project-level storage; v0.12 reliability baseline (init guard, trash-based deletion, independent-volume backup/list/doctor/restore), v0.10 consolidation (periodic summaries with provenance, near-duplicate auto-merge, per-type decay, batch audit + rollback), v0.9 recall quality + context focus + optional local embedding plugin, plus v0.8 semantic search, feedback loop, self-organization and distillation.",
|
|
3
|
+
"version": "0.13.1",
|
|
4
|
+
"description": "Yuanyi (元忆) — boundary-aware, file-based memory for AI agents. File-based, zero-dependency, diff/rollback-able; FACT/PREF/BOUND/COMMIT types (public shared / private isolated), user-level + project-level storage; v0.12 reliability baseline (init guard, trash-based deletion, independent-volume backup/list/doctor/restore, start-of-work doctor, transactional snapshots before destructive writes), v0.10 consolidation (periodic summaries with provenance, near-duplicate auto-merge, per-type decay, batch audit + rollback), v0.9 recall quality + context focus + optional local embedding plugin, plus v0.8 semantic search, feedback loop, self-organization and distillation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent-skills",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"files": [
|
|
15
15
|
"SKILL.md",
|
|
16
|
+
"skill-manifest.json",
|
|
16
17
|
"LICENSE",
|
|
17
18
|
"README.md",
|
|
18
19
|
"USER_GUIDE.md",
|
package/references/faq.md
CHANGED
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
开工运行 `yotta-memory context`(身份 + 画像 + 近期记忆 + 边界 + 承诺),需要细节再 `recall <关键词>`。
|
|
31
31
|
|
|
32
32
|
## 10. 备份与迁移?
|
|
33
|
-
优先使用 `backup create --dir <独立盘目录>` 创建整库备份;`backup list` 查看,`backup doctor` 校验 SHA-256,`backup restore <id> --to <新目录>` 恢复到新目录。备份默认拒绝与记忆库同卷。`export --out 文件.json` 仍可用于跨工具迁移;公共 FACT 是明文文件也可直接 git 备份。
|
|
33
|
+
优先使用 `backup create --dir <独立盘目录>` 创建整库备份;`backup list` 查看,`backup doctor` 校验 SHA-256,`backup restore <id> --to <新目录>` 恢复到新目录。备份默认拒绝与记忆库同卷。`export --out 文件.json` 仍可用于跨工具迁移;公共 FACT 是明文文件也可直接 git 备份。v0.12.2 起,破坏性操作也会在写入前自动创建事务快照。
|
|
34
34
|
|
|
35
35
|
## 13. init 会不会覆盖已有记忆?
|
|
36
|
-
不会。v0.12.0 起 `init` 遇到已有记忆库默认拒绝;接入现有库用 `init --attach`。`--force`
|
|
36
|
+
不会。v0.12.0 起 `init` 遇到已有记忆库默认拒绝;接入现有库用 `init --attach`。`--force` 也不能覆盖已有记忆库;强制重建需要完整备份与显式确认保护,当前版本不提供覆盖初始化路径。
|
|
37
37
|
|
|
38
38
|
## 11. 记忆太多 / 越来越膨胀怎么办?
|
|
39
|
-
两步走:`yotta-memory maintain --apply` 归档单条低效用旧记忆;同主题积累了很多「又老又不常用」的旧条目时,用 `yotta-memory consolidate`(先预览)→ `consolidate --apply` 把它们归纳成 1
|
|
39
|
+
两步走:`yotta-memory maintain --apply` 归档单条低效用旧记忆;同主题积累了很多「又老又不常用」的旧条目时,用 `yotta-memory consolidate`(先预览)→ `consolidate --apply` 把它们归纳成 1 条带溯源的周期摘要并归档原文。v0.12.2 起这些写操作要求先通过 doctor,并会自动创建事务快照;没有独立备份目录时会拒绝执行。也可以先 `config set maintain_decay_halflife_COMMIT 90` 等让任务类承诺更快让位。
|
|
40
40
|
|
|
41
41
|
## 12. consolidate 和 distill 有什么区别?
|
|
42
42
|
`distill` 是全库当前快照的统计报告(不淘汰任何记忆,产物是报告);`consolidate` 是生命周期压缩——只针对超龄 + 长期闲置 + 低效用的旧记忆,生成可检索的周期摘要、把原文归档,让记忆库变小且主题不丢。两者互补:想「看看我记了什么」用 distill;想「给记忆库瘦身」用 consolidate。
|
|
@@ -53,3 +53,6 @@
|
|
|
53
53
|
- `yotta-memory-install` 是技能安装器。安装或更新技能用 `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name>`;用 `--dir` 安装时传入同一个技能目录。
|
|
54
54
|
- 更新 CLI:`npm i -g @yottameta/yotta-memory`。更新技能:重跑上面的 `--package ... yotta-memory-install` 命令,或全局安装后运行 `yotta-memory-install --agent <name>`。
|
|
55
55
|
- 为什么在 `npx -y @yottameta/yotta-memory` 后面加 `--agent <name>` 没有安装技能?因为 npm 默认运行的是包内同名的 `yotta-memory` 引擎 bin,而不是 `yotta-memory-install`;必须用 `--package` 显式指定安装器 bin。
|
|
56
|
+
|
|
57
|
+
## 16. 开工时怎么知道记忆库是否可靠?
|
|
58
|
+
运行 `yotta-memory doctor`。它只读检查记忆库根目录、加密库密钥文件、公共索引、`agents.json` 与最近备份;`--json` 可输出机器可读结果。严重异常时会锁定 `maintain --apply`、`consolidate --apply`、`merge`、`archive` 与 `--purge` 等破坏性写入,`context` 也会在开工提醒中显示“破坏性写入已锁定”。先按 doctor 提示修复,再继续写操作。
|
package/references/protocol.md
CHANGED
|
@@ -194,7 +194,7 @@ magic "YTMIDX1" (7B) | nonce(12B) | tag(16B) | ciphertext(JSON: {version, update
|
|
|
194
194
|
|
|
195
195
|
- **语义检索(recall)**:默认开启——精确(字段加权:subject×3 / tags×2 / statement×1)+ 同义词(内置 `SYNONYM_GROUPS`,可扩展)+ 拼音(全拼 `py:` / 首字母 `pyi:` token,内置 3755 常用字表 `references/pinyin-common.json`)+ 模糊(编辑距离 ≤ 2)+ 子串兜底;排序融合 0.65×语义 + 0.35×效用;`--explain` 输出命中理由与效用分项;`--semantic` 显式开启(默认开),旧索引(version<3)首次 recall 自动重建。
|
|
196
196
|
- **feedback(使用反馈闭环)**:`feedback <文件> --useful|--useless [--reason] [--undo]`——useful → weight×1.2(上限 3.0)+ confidence +0.05 + feedback_net +1;useless → weight×0.8(下限 0.2)+ confidence −0.05 + feedback_net −1;审计写 `.archive/feedback-<日期>.jsonl`(含 before/after);`--undo` 按最近一条回滚。
|
|
197
|
-
- **maintain(规则层自组织)**:统一效用分 `utility = (0.30×confidence + 0.25×usage + 0.20×recency + 0.15×type + 0.10×structure) × weight`(typeScore:BOUND 1.0 / COMMIT 0.9 / PREF 0.8 / FACT 0.6);归档条件 utility<0.35 且年龄>180 天,遗忘候选 utility<0.12 且年龄>365 天;默认 dry-run,`--apply` 归档(移入 `.archive/`),`--purge` 才真删;immutable / BOUND 豁免;`--dedup` 相似候选(subject 编辑距离 ≤3 且 token Jaccard ≥0.7),`--merge A,B` 手动合并;审计写 `.archive/audit-<日期>.jsonl`。
|
|
197
|
+
- **maintain(规则层自组织)**:统一效用分 `utility = (0.30×confidence + 0.25×usage + 0.20×recency + 0.15×type + 0.10×structure) × weight`(typeScore:BOUND 1.0 / COMMIT 0.9 / PREF 0.8 / FACT 0.6);归档条件 utility<0.35 且年龄>180 天,遗忘候选 utility<0.12 且年龄>365 天;默认 dry-run,`--apply` 归档(移入 `.archive/`),`--purge` 才真删;v0.12.2 起写操作先通过 doctor 并创建事务快照;immutable / BOUND 豁免;`--dedup` 相似候选(subject 编辑距离 ≤3 且 token Jaccard ≥0.7),`--merge A,B` 手动合并;审计写 `.archive/audit-<日期>.jsonl`。
|
|
198
198
|
- **distill(心理日志蒸馏)**:统计摘要(类型 / 年龄 / 热度 / 反馈)+ 主题画像(按 subject 聚类合并)+ 知识地图(type → tags);`--model <cmd>` 外部模型协议(stdin JSON → stdout 提炼文本);私密产物 `private/<owner>/distills/<日期>-<slug>.md`(加密库随 owner key 加密),公共 `facts/distills/`。
|
|
199
199
|
- **explain(效用解释)**:`explain <文件>` 输出效用分项明细与归档 / 遗忘状态判定。
|
|
200
200
|
|
|
@@ -250,4 +250,4 @@ magic "YTMIDX1" (7B) | nonce(12B) | tag(16B) | ciphertext(JSON: {version, update
|
|
|
250
250
|
## 6. 与其他系统互操作
|
|
251
251
|
|
|
252
252
|
- **git**:整个记忆库可纳入版本控制,回滚 / 审计 / 团队同步。
|
|
253
|
-
- **Agent Skills 标准**:本技能 SKILL.md 符合 agentskills.io 开放标准,支持 npx skills 安装。
|
|
253
|
+
- **Agent Skills 标准**:本技能 SKILL.md 符合 agentskills.io 开放标准,支持 npx skills 安装。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifestVersion": 1,
|
|
3
|
+
"slug": "yotta-memory",
|
|
4
|
+
"name": "元忆",
|
|
5
|
+
"package": "@yottameta/yotta-memory",
|
|
6
|
+
"version": "0.13.1",
|
|
7
|
+
"trust": "yottameta",
|
|
8
|
+
"install": {
|
|
9
|
+
"idempotent": true
|
|
10
|
+
},
|
|
11
|
+
"permissions": {
|
|
12
|
+
"filesystem": "user-skills-dir",
|
|
13
|
+
"network": "none",
|
|
14
|
+
"process": "child-process",
|
|
15
|
+
"note": "只调用本包内 Node 引擎读写本地记忆库;默认不联网。"
|
|
16
|
+
},
|
|
17
|
+
"auto_apply": {
|
|
18
|
+
"mode": "hook",
|
|
19
|
+
"note": "关键里程碑完成后写入长期记忆,并保留本地文件证据。"
|
|
20
|
+
},
|
|
21
|
+
"hooks": [
|
|
22
|
+
{
|
|
23
|
+
"event": "after_milestone",
|
|
24
|
+
"require_tool": "remember_commit",
|
|
25
|
+
"condition": "里程碑完成并进入下一步之前",
|
|
26
|
+
"on_fail": "block",
|
|
27
|
+
"fallback": "explicit-unverified",
|
|
28
|
+
"evidence": [
|
|
29
|
+
"file_path"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|