@yottameta/yotta-memory 0.12.0 → 0.13.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.
- package/CHANGELOG.md +29 -1
- package/README.md +26 -3
- package/README.zh-CN.md +25 -3
- package/SKILL.md +31 -4
- package/USER_GUIDE.md +17 -2
- package/bin/install.js +5 -5
- package/bin/yotta-memory.js +264 -12
- package/package.json +3 -2
- package/references/faq.md +13 -3
- package/references/protocol.md +2 -2
- package/skill-manifest.json +33 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,36 @@
|
|
|
1
|
+
## v0.13.0 (2026-09-13)
|
|
2
|
+
|
|
3
|
+
**P0-4.6 元忆 after_milestone 试点**:
|
|
4
|
+
|
|
5
|
+
- 新增 `skill-manifest.json`,声明 `after_milestone` / `remember_commit` / `fallback: explicit-unverified`。
|
|
6
|
+
- 里程碑记忆必须有真实 `file_path` 证据才允许标记 verified;缺证据或写入失败时输出 `explicit-unverified` + 一次纠偏。
|
|
7
|
+
- 元阁适配器回归覆盖成功与降级两条路径。
|
|
8
|
+
|
|
9
|
+
## v0.12.2 (2026-09-12)
|
|
10
|
+
|
|
11
|
+
**可靠性收口:开工 doctor + 破坏性操作前事务快照**
|
|
12
|
+
|
|
13
|
+
- 修正 `init --force` 的过期拒绝文案:明确已有记忆库的强制重建必须由完整备份与显式确认保护,当前版本不提供覆盖初始化路径。
|
|
14
|
+
- 新增 `yotta-memory doctor [--json]`:只读检查记忆库根目录、加密库密钥文件、公共索引、`agents.json` 与最近备份;严重异常返回非零退出码,并锁定破坏性写入。
|
|
15
|
+
- 新增 MCP `doctor` 工具;`context` 的开工可靠性提醒改为直接使用 doctor 结果,critical 时明确提示“破坏性写入已锁定”。
|
|
16
|
+
- `maintain --apply`(含 `--dedup --apply`)、`consolidate --apply`、`merge`、`archive` 与 `--purge` 在写入前自动创建新的整库事务快照;未配置独立备份目录、doctor critical 或快照失败时拒绝写入,原记忆保持不变。
|
|
17
|
+
- `--allow-same-volume` 仍只用于 `backup create` 的显式临时备份,不会绕过破坏性写入门;不存在 CLI 级跳过快照开关。
|
|
18
|
+
- 每次事务快照写入 `.archive/audit-<日期>.jsonl`(transaction / operation / snapshot 记录);`forget` 继续只进 `.trash/`,不重复做整库快照。
|
|
19
|
+
- 新增 `test/reliability-doctor.test.js` 与 `test/reliability-destructive-guard.test.js`;既有 consolidate / maintain 回归补齐临时快照目录。
|
|
20
|
+
|
|
21
|
+
## v0.12.1 (2026-09-12)
|
|
22
|
+
|
|
23
|
+
安装与更新文档修复(无功能变更):
|
|
24
|
+
|
|
25
|
+
- 明确区分两个 bin:`yotta-memory` 是引擎 CLI,`yotta-memory-install` 是技能安装器;`npx -y @yottameta/yotta-memory` 只临时运行引擎,不安装技能。
|
|
26
|
+
- README 中英版、USER_GUIDE、SKILL、FAQ 补全安装 / 更新命令:更新技能统一使用 `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name>`(或 `--dir <技能目录>`);全局安装后可运行 `yotta-memory-install`。
|
|
27
|
+
- 修正 `bin/install.js` 头部注释中的错误示例,并新增 `test/docs-install-update.test.js` 防止两个 bin 的文档再次漂移。
|
|
28
|
+
|
|
1
29
|
## v0.12.0 (2026-09-12)
|
|
2
30
|
|
|
3
31
|
**可靠性基线(误删事故整改)**:
|
|
4
32
|
|
|
5
|
-
- `init` 对已有记忆库默认拒绝覆盖;新增 `--attach` 接入现有库;`--force`
|
|
33
|
+
- `init` 对已有记忆库默认拒绝覆盖;新增 `--attach` 接入现有库;`--force` 也不能覆盖已有记忆库,防止初始化再次清空记忆。
|
|
6
34
|
- `forget` 不再物理删除,改为移动到 `.trash/<timestamp>/<原相对路径>`,并写 `audit-<date>.jsonl` 审计记录。
|
|
7
35
|
- 新增 `backup create / list / doctor / restore <id> --to <目录>`:备份 `facts/`、`private/`、`keys/`(排除 `keys/cache/` 授权缓存)、`agents.json`、`index.json`、`.archive/`,生成 SHA-256 清单;默认拒绝同卷备份;`restore` 默认只恢复到新目录,不覆盖正在使用的记忆库。
|
|
8
36
|
- 新增每日自动备份闭环:`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,10 @@
|
|
|
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
|
+
|
|
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.
|
|
29
|
+
|
|
26
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.
|
|
27
31
|
|
|
28
32
|
> 🆕 **v0.10.0**: compression & forgetting — `consolidate` turns old low-use memories on the same topic into one **provenance-carrying periodic summary** that stays searchable (originals archived; `consolidate --undo <batch>` restores everything); near-duplicate **auto-merge with confidence** (`maintain --dedup --apply`); **per-type decay curves** (FACT slow / PREF medium / COMMIT task-like fast / BOUND never decays); batch audit via `consolidate --batches`.
|
|
@@ -41,7 +45,8 @@ Most memory solutions treat "remembering" as a black box: data goes into a datab
|
|
|
41
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.
|
|
42
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).
|
|
43
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`.
|
|
44
|
-
- **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.
|
|
45
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).
|
|
46
51
|
|
|
47
52
|
### Memory types
|
|
@@ -176,6 +181,13 @@ bash install.sh --dir <path> # install to the given directory
|
|
|
176
181
|
bash install.sh --list # list agents -> default directories
|
|
177
182
|
```
|
|
178
183
|
|
|
184
|
+
### Two commands, two jobs
|
|
185
|
+
|
|
186
|
+
This package exposes two separate commands:
|
|
187
|
+
|
|
188
|
+
- `yotta-memory` — the engine CLI that reads and writes the memory store. `npx -y @yottameta/yotta-memory` temporarily runs only this CLI; it does **not** install the skill.
|
|
189
|
+
- `yotta-memory-install` — the skill installer. Install or update the skill with `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <agent-name>` (or `--dir <skills-dir>`).
|
|
190
|
+
|
|
179
191
|
> Method 1 uses the npm registry (npmmirror / npmjs) and does not depend on GitHub; Methods 2/3 use GitHub and may fail without a proxy in China.
|
|
180
192
|
## Example outputs
|
|
181
193
|
|
|
@@ -222,7 +234,17 @@ Two upgrade paths match the two install paths:
|
|
|
222
234
|
npm i -g @yottameta/yotta-memory
|
|
223
235
|
```
|
|
224
236
|
|
|
225
|
-
**Upgrade the skill**: rerun
|
|
237
|
+
**Upgrade the skill**: rerun the command that matches the way you installed it:
|
|
238
|
+
|
|
239
|
+
```text
|
|
240
|
+
npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <agent-name>
|
|
241
|
+
npx -y --package @yottameta/yotta-memory yotta-memory-install --dir <skills-dir>
|
|
242
|
+
bash install.sh --agent <agent-name>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
If you installed the CLI globally, `npm i -g @yottameta/yotta-memory` updates both the engine and the `yotta-memory-install` command; then run `yotta-memory-install --agent <agent-name>` to refresh the skill.
|
|
246
|
+
|
|
247
|
+
After updating, verify `yotta-memory --version` and the installed `yotta-memory/SKILL.md` frontmatter `version:`.
|
|
226
248
|
|
|
227
249
|
**v0.10.0 upgrade notes** — upgrading touches no data: no migration, no reindex, no re-init needed. v0.10.0 does not change the memory file format, the `facts/` / `private/<owner>/<type>/` layout, or the index version, so existing stores open as-is.
|
|
228
250
|
|
|
@@ -248,6 +270,7 @@ Optional post-upgrade self-check: `yotta-memory config get` (confirm `memory_hom
|
|
|
248
270
|
| `yotta-memory profile [--owner <id>]` | Generate a user profile (aggregates `private/<owner>` verbatim, zero inference, writes `profile.md`; cross-owner denied by default) |
|
|
249
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) |
|
|
250
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) |
|
|
251
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>/`) |
|
|
252
275
|
| `yotta-memory reindex` | Rebuild the index (after manually editing .md) |
|
|
253
276
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | Export / import |
|
|
@@ -329,7 +352,7 @@ Register the connection in the agent's MCP config (`url` + two headers):
|
|
|
329
352
|
}
|
|
330
353
|
```
|
|
331
354
|
|
|
332
|
-
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).
|
|
333
356
|
|
|
334
357
|
### Location persistence
|
|
335
358
|
|
package/README.zh-CN.md
CHANGED
|
@@ -23,6 +23,10 @@
|
|
|
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
|
+
|
|
28
|
+
> 🆕 **v0.12.1**:安装与更新文档明确区分引擎 CLI(`yotta-memory`)和技能安装器(`yotta-memory-install`),并补齐可直接复制的升级命令。
|
|
29
|
+
|
|
26
30
|
> 🆕 **v0.12.0**:可靠性基线——`init` 对已有记忆库默认拒绝覆盖(用 `--attach` 接入);`forget` 先移入 `.trash/` 并留审计;`backup create / list / doctor / restore` 支持独立盘备份、SHA-256 清单校验和只恢复到新目录。
|
|
27
31
|
|
|
28
32
|
> 🆕 **v0.10.0**:压缩遗忘——`consolidate` 周期摘要压缩(同主题旧记忆 → 带溯源摘要 + 原文归档,`--undo <batch>` 一键回滚);近重复自动合并(`maintain --dedup` 置信度分档,`--apply` 批量执行);分类型衰减(FACT 慢 / PREF 中 / COMMIT 任务类快 / BOUND 不衰减);`consolidate --batches` 批次审计可查。
|
|
@@ -45,7 +49,7 @@
|
|
|
45
49
|
- **越用越懂(v0.6.0)**:`profile` 聚合用户画像(引擎零推断,只归组原文)+ `context` 一键生成开工上下文包(身份 + 画像 + 近期记忆 + 边界 + 承诺);SKILL「记忆守则」注入规则层(类型红线 / 触发信号 / 了解用户 / 底线 / 宿主隔离),只注入规则与机制、不注入人格数据,出厂零数据。
|
|
46
50
|
- **自我学习 / 自我进化 / 自我提升(v0.8.0)**:`recall` 语义检索(同义词 / 拼音全拼+首字母 / 字段加权 / 模糊匹配,零依赖)+ 效用分融合排序;`feedback` 显式使用反馈闭环(useful / useless 调整 weight / confidence / feedback_net,越用越懂);`maintain` 规则层自组织(统一效用分 + 年龄自动归档 / 遗忘候选 / 去重,默认 dry-run,immutable / BOUND 豁免);`distill` 心理日志蒸馏(统计摘要 / 主题画像 / 知识地图,可选 `--model` 外部模型增强)。
|
|
47
51
|
- **压缩遗忘(v0.10.0)——记忆越用越精简**:`consolidate` 周期摘要压缩——把超龄 + 长期闲置 + 低效用的同主题旧记忆归纳成 **1 条带溯源的摘要**(每条原文路径都写在正文里)留在活跃区,原文整体进 `.archive/`,`--undo <batch>` 一键回滚;`maintain --dedup` 给近重复打分(≥0.85 高置信自动合并 / 0.65–0.85 建议手动),`--apply` 批量合并同归属重复组;时效分量改**分类型衰减**(FACT 慢 / PREF 中 / COMMIT 任务类快 / BOUND 永不衰减)——持久事实不被时间抹掉,过期承诺快速让位;每一步写批次审计(`--batches` 可查)。
|
|
48
|
-
- **可靠性基线(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` 检查根目录 / 密钥库 / 索引 / 身份 / 最近备份,破坏性操作在写入前自动创建事务快照。
|
|
49
53
|
- **私密区加密(v0.7.0)**:私密区文件 AES-256-GCM 信封加密(口令派生主密钥 + 恢复钥匙 + 每 owner 加密索引);`yotta-memory view` 用户查看平台(口令解锁看全部 AI 记忆);`migrate` 明文→密文迁移;`--no-encrypt` 可降级。跨 AI 私密从「纪律层隔离」升级为「机制层不可解」。
|
|
50
54
|
- **便携记忆盘**:记忆库本身就是记忆引擎——装在硬盘或主机上随盘走,局域网内其它主机上的智能体可远程读写;本地零进程与局域网常驻两种模式可并存,插上硬盘即恢复全部记忆。
|
|
51
55
|
|
|
@@ -216,6 +220,13 @@ bash install.sh --dir <path> # 装到指定目录
|
|
|
216
220
|
bash install.sh --list # 列出智能体 -> 默认目录
|
|
217
221
|
```
|
|
218
222
|
|
|
223
|
+
### 两个命令,各管一件事
|
|
224
|
+
|
|
225
|
+
这个包提供两个不同的命令:
|
|
226
|
+
|
|
227
|
+
- `yotta-memory`:读写记忆库的引擎 CLI。`npx -y @yottameta/yotta-memory` 只是临时运行这个 CLI,**不会**安装技能。
|
|
228
|
+
- `yotta-memory-install`:技能安装器。安装或更新技能必须使用 `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <智能体名称>`(或 `--dir <智能体技能目录>`)。
|
|
229
|
+
|
|
219
230
|
> 方式一走 npm 源(npmmirror / npmjs),不依赖 GitHub;方式二 / 三走 GitHub,国内无代理可能失败。
|
|
220
231
|
## 命令输出样例
|
|
221
232
|
|
|
@@ -262,7 +273,17 @@ bash install.sh --list # 列出智能体 -> 默认目录
|
|
|
262
273
|
npm i -g @yottameta/yotta-memory
|
|
263
274
|
```
|
|
264
275
|
|
|
265
|
-
|
|
276
|
+
**升级技能**:按你当初的安装方式重跑对应命令,覆盖旧版技能文件夹:
|
|
277
|
+
|
|
278
|
+
```text
|
|
279
|
+
npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <智能体名称>
|
|
280
|
+
npx -y --package @yottameta/yotta-memory yotta-memory-install --dir <智能体技能目录>
|
|
281
|
+
bash install.sh --agent <智能体名称>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
如果你全局安装了 CLI,`npm i -g @yottameta/yotta-memory` 会同时升级引擎和 `yotta-memory-install` 命令;之后运行 `yotta-memory-install --agent <智能体名称>` 即可刷新技能。
|
|
285
|
+
|
|
286
|
+
升级后请核对 `yotta-memory --version` 和已安装的 `yotta-memory/SKILL.md` frontmatter 中的 `version:`。
|
|
266
287
|
|
|
267
288
|
**v0.10.0 升级提示**——升级不碰数据:无需迁移 / reindex / 重新 init。v0.10.0 没有改记忆文件格式、`facts/` 与 `private/<owner>/<type>/` 布局,也没有改索引版本,旧库打开即用。
|
|
268
289
|
|
|
@@ -290,6 +311,7 @@ npm i -g @yottameta/yotta-memory
|
|
|
290
311
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(聚合 `private/<owner>/` 原文,零推断,写 `profile.md`;跨 owner 默认拒绝)|
|
|
291
312
|
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <命令>]` | 生成开工上下文包(身份 + 多智能体铁律 + 画像 + 任务相关记忆 + 近期记忆 + 边界 + 承诺;--budget 字符预算;--focus 任务聚焦;--explain 输出 included/dropped 选择解释)|
|
|
292
313
|
| `yotta-memory forget <文件>` | 删除一条记忆(按类型目录路径或文件名)|
|
|
314
|
+
| `yotta-memory doctor [--json]` | 开工可靠性检查(根目录 / 密钥库 / 索引 / 身份 / 最近备份;严重异常时锁定破坏性写入)|
|
|
293
315
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(分类型衰减效用分 + 年龄;immutable / BOUND 豁免;私密入 `.archive/private/<owner>/<type>/`)|
|
|
294
316
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
295
317
|
| `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
|
|
@@ -371,7 +393,7 @@ yotta-memory recall --type FACT --limit 10
|
|
|
371
393
|
}
|
|
372
394
|
```
|
|
373
395
|
|
|
374
|
-
连接后可通过 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 私密隔离)。
|
|
375
397
|
|
|
376
398
|
### 位置持久化
|
|
377
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.0
|
|
5
5
|
license: MIT
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -20,6 +20,15 @@ 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` + 一次纠偏。
|
|
25
|
+
|
|
26
|
+
## 安装 / 更新(两个 bin)
|
|
27
|
+
|
|
28
|
+
- `yotta-memory`:引擎 CLI,负责读写记忆库。`npx -y @yottameta/yotta-memory` 只是临时运行引擎,不会安装技能。
|
|
29
|
+
- `yotta-memory-install`:技能安装器。安装或更新技能使用 `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name>`(或 `--dir <技能目录>`)。
|
|
30
|
+
|
|
31
|
+
AI 更新流程:先运行 `yotta-memory --version` 记录当前引擎版本;升级 CLI 用 `npm i -g @yottameta/yotta-memory`;升级技能用 `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name>`(或原安装时使用的 `--dir` / `install.sh`);完成后回读 `yotta-memory --version` 与已安装 `SKILL.md` 的 `version:`,两者不一致时不得声称更新成功。
|
|
23
32
|
|
|
24
33
|
## 何时使用(触发)
|
|
25
34
|
|
|
@@ -35,7 +44,7 @@ license: MIT
|
|
|
35
44
|
3. **收工归档**:写会话小结(COMMIT / 笔记);旧记录定期 `yotta-memory maintain --apply`(单条低效用归档)+ 记忆多了周期 `yotta-memory consolidate --apply`(同主题压缩成带溯源摘要,`--undo` 可回滚)。
|
|
36
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` 等文件,否则会绕过权限边界、读到别的智能体私密内容。
|
|
37
46
|
|
|
38
|
-
## 可靠性基线(v0.12.0)
|
|
47
|
+
## 可靠性基线(v0.12.0 / v0.12.2)
|
|
39
48
|
|
|
40
49
|
**目的**:防止初始化覆盖、删除不可逆、备份缺失再次造成记忆库丢失。
|
|
41
50
|
|
|
@@ -43,7 +52,7 @@ license: MIT
|
|
|
43
52
|
|
|
44
53
|
- 目标已是记忆库时,`yotta-memory init` 默认拒绝覆盖。
|
|
45
54
|
- 接入已有库:`yotta-memory init --attach`。
|
|
46
|
-
- `--force`
|
|
55
|
+
- `--force` 不能覆盖已有记忆库;强制重建需要完整备份与显式确认保护,当前版本不提供覆盖初始化路径。
|
|
47
56
|
- 非 owner 智能体不得初始化、重建或清空记忆库。
|
|
48
57
|
|
|
49
58
|
### 2. forget 回收区
|
|
@@ -104,6 +113,23 @@ yotta-memory backup drill [<备份ID>]
|
|
|
104
113
|
- 恢复目标非空时拒绝覆盖;恢复后先校验,再决定是否替换正式记忆库。
|
|
105
114
|
- 备份是可靠性基线的一部分;没有备份时,不得执行永久删除或覆盖初始化。
|
|
106
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
|
+
|
|
107
133
|
|
|
108
134
|
## 记忆守则(Memory Doctrine,v0.6.0)
|
|
109
135
|
|
|
@@ -238,6 +264,7 @@ yotta-memory backup drill [<备份ID>]
|
|
|
238
264
|
| `yotta-memory forget <文件>` | 移入 `.trash/<时间>/` 回收区并写审计(v0.12.0;不再物理删除)|
|
|
239
265
|
| `yotta-memory backup volumes / setup --dir <目录> / status / ensure-daily / schedule enable|disable|status` | 每日自动备份(v0.12.0;只展示实际枚举的异卷、用户确认一次位置后默认每日执行,Windows Task Scheduler / systemd timer / launchd 调度,`serve` 补跑)|
|
|
240
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;根目录 / 密钥库 / 索引 / 身份 / 最近备份;严重异常时锁定破坏性写入)|
|
|
241
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`)|
|
|
242
269
|
| `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
|
|
243
270
|
| `yotta-memory export [--out f.json]` / `import <f.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
|
|
|
@@ -58,7 +59,13 @@
|
|
|
58
59
|
|
|
59
60
|
> CLI 与技能分工不同:CLI 让命令行能读写记忆;技能让 AI 知道「开工 recall 恢复上下文、重要信息 remember 落盘、收工归档」。只装 CLI 不装技能,AI 不会自动使用这套工作流。
|
|
60
61
|
|
|
61
|
-
**更新:**
|
|
62
|
+
**更新:**
|
|
63
|
+
|
|
64
|
+
- 升级 CLI:`npm i -g @yottameta/yotta-memory`(不带版本号即最新版)。
|
|
65
|
+
- 升级技能:`npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name>`;如果你用 `--dir` 安装,把同一个目录再传一次。
|
|
66
|
+
- 全局安装时:`npm i -g @yottameta/yotta-memory` 会同时升级引擎和安装器,之后可运行 `yotta-memory-install --agent <name>`。
|
|
67
|
+
|
|
68
|
+
> `npx -y @yottameta/yotta-memory` 只是临时运行引擎 CLI,不会安装技能;技能安装器必须使用上面的 `--package ... yotta-memory-install` 形式,或全局安装后的 `yotta-memory-install` 命令。
|
|
62
69
|
|
|
63
70
|
**卸载:** `npm rm -g @yottameta/yotta-memory`,并从智能体的 skills 目录删除整个 `yotta-memory` 文件夹。
|
|
64
71
|
|
|
@@ -222,6 +229,13 @@ statement: 本周完成发布
|
|
|
222
229
|
- `yotta-memory backup status` 查看目录、计划、上次成功、最近失败与调度状态;`backup drill` 恢复到隔离副本,校验 manifest / 索引并解密一条测试私密。
|
|
223
230
|
- 备份目录与记忆库同卷时默认拒绝;这是一条防线,不替代独立盘备份。
|
|
224
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
|
+
|
|
225
239
|
## 4. 便携记忆盘 · 记忆引擎主机篇
|
|
226
240
|
|
|
227
241
|
场景:记忆放在一台主机上(Linux / Windows 均可),本机直接 CLI 读写;局域网内其它主机上的 AI 智能体经 MCP 远程接入。引擎主机只需装 CLI,不需要装任何 AI 智能体。
|
|
@@ -359,6 +373,7 @@ yotta-memory remember FACT 主题 内容 # 智能体落盘
|
|
|
359
373
|
| `yotta-memory profile [--owner <id>]` | 生成用户画像(零推断,写 `profile.md`)|
|
|
360
374
|
| `yotta-memory context [--limit N] [--owner <id>] [--budget N] [--focus <关键词>] [--explain] [--embedding <命令>]` | 开工上下文包(身份+铁律+画像+任务相关记忆+近期记忆+边界+承诺;--focus 任务聚焦;--explain 输出 included/dropped 选择解释)|
|
|
361
375
|
| `yotta-memory forget <文件>` | 删除一条记忆 |
|
|
376
|
+
| `yotta-memory doctor [--json]` | 开工可靠性检查(v0.12.2:根目录 / 密钥库 / 索引 / 身份 / 最近备份;严重异常时锁定破坏性写入)|
|
|
362
377
|
| `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆(分类型衰减效用分 + 年龄;immutable / BOUND 豁免;私密入 `.archive/private/<owner>/<type>/`)|
|
|
363
378
|
| `yotta-memory reindex` | 重建索引 |
|
|
364
379
|
| `yotta-memory export [--out 文件.json]` / `import <文件.json>` | 导出 / 导入 |
|
package/bin/install.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* yotta-memory 跨平台安装器
|
|
4
4
|
* 用法:
|
|
5
|
-
* npx -y @yottameta/yotta-memory --agent <name> # 按智能体默认用户级目录安装(推荐)
|
|
6
|
-
* npx -y @yottameta/yotta-memory --dir PATH # 装到指定目录(用户改了目录的智能体)
|
|
7
|
-
* npx -y @yottameta/yotta-memory -g # 安装到全部已知智能体用户级目录
|
|
8
|
-
* npx -y @yottameta/yotta-memory # 安装到检测到的项目级目录
|
|
9
|
-
* npx -y @yottameta/yotta-memory --list # 列出智能体 -> 默认目录
|
|
5
|
+
* npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name> # 按智能体默认用户级目录安装(推荐)
|
|
6
|
+
* npx -y --package @yottameta/yotta-memory yotta-memory-install --dir PATH # 装到指定目录(用户改了目录的智能体)
|
|
7
|
+
* npx -y --package @yottameta/yotta-memory yotta-memory-install -g # 安装到全部已知智能体用户级目录
|
|
8
|
+
* npx -y --package @yottameta/yotta-memory yotta-memory-install # 安装到检测到的项目级目录
|
|
9
|
+
* npx -y --package @yottameta/yotta-memory yotta-memory-install --list # 列出智能体 -> 默认目录
|
|
10
10
|
*/
|
|
11
11
|
'use strict';
|
|
12
12
|
const fs = require('fs');
|
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.0';
|
|
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) {
|
|
@@ -3291,6 +3515,12 @@ function cmdBackupDrill(id, opts) {
|
|
|
3291
3515
|
console.log(r.text);
|
|
3292
3516
|
if (r.error) process.exit(2);
|
|
3293
3517
|
}
|
|
3518
|
+
function cmdDoctor(opts) {
|
|
3519
|
+
const r = doctorCore(opts);
|
|
3520
|
+
if (opts.json) console.log(JSON.stringify(r, null, 2));
|
|
3521
|
+
else console.log(r.text);
|
|
3522
|
+
if (!r.ok) process.exit(2);
|
|
3523
|
+
}
|
|
3294
3524
|
function cmdReindex() {
|
|
3295
3525
|
const roots = memoryRoots();
|
|
3296
3526
|
if (!roots.length) { console.log('记忆库不存在。'); return; }
|
|
@@ -3812,12 +4042,14 @@ function contextCore(opts) {
|
|
|
3812
4042
|
if (!trace.length) lines.push('(无选择记录)');
|
|
3813
4043
|
for (const t of trace) lines.push(t);
|
|
3814
4044
|
}
|
|
3815
|
-
const
|
|
3816
|
-
if (
|
|
4045
|
+
const reliability = doctorCore({ root: root });
|
|
4046
|
+
if (reliability.level !== 'ok') {
|
|
3817
4047
|
lines.push('');
|
|
3818
4048
|
lines.push('## 可靠性提醒');
|
|
3819
4049
|
lines.push('');
|
|
3820
|
-
lines.push('- ' +
|
|
4050
|
+
for (const message of reliability.critical) lines.push('- [严重] ' + message);
|
|
4051
|
+
for (const message of reliability.warnings) lines.push('- [警告] ' + message);
|
|
4052
|
+
if (!reliability.ok) lines.push('- 破坏性写入已锁定:先运行 yotta-memory doctor 修复严重问题。');
|
|
3821
4053
|
}
|
|
3822
4054
|
return { error: false, exitCode: 0, text: lines.join('\n') };
|
|
3823
4055
|
}
|
|
@@ -3874,6 +4106,7 @@ function mcpTools() {
|
|
|
3874
4106
|
{ 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
4107
|
{ 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
4108
|
{ 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' } } } },
|
|
4109
|
+
{ name: 'doctor', description: '开工可靠性检查:检查记忆库根目录、密钥库、索引、身份登记与最近备份;只读,不会修改记忆', inputSchema: { type: 'object', properties: {} } },
|
|
3877
4110
|
{ name: 'forget', description: '删除一条记忆。file 为记忆文件路径(如 facts/2026-08-24-0001.md 或文件名)', inputSchema: { type: 'object', properties: { file: { type: 'string' } }, required: ['file'] } },
|
|
3878
4111
|
{ name: 'archive', description: '归档旧记忆。days 默认 180;threshold 默认 0.4', inputSchema: { type: 'object', properties: { days: { type: 'number' }, threshold: { type: 'number' } } } },
|
|
3879
4112
|
{ name: 'reindex', description: '重建索引(手动改 .md 后校正;扫描 facts/prefs/bounds/commits 四目录)', inputSchema: { type: 'object', properties: {} } },
|
|
@@ -3917,6 +4150,10 @@ function callTool(name, args, ctx) {
|
|
|
3917
4150
|
});
|
|
3918
4151
|
return { text: r.text, error: r.error };
|
|
3919
4152
|
}
|
|
4153
|
+
if (name === 'doctor') {
|
|
4154
|
+
const r = doctorCore({ selfAgent: agent });
|
|
4155
|
+
return { text: r.text, error: !r.ok };
|
|
4156
|
+
}
|
|
3920
4157
|
if (name === 'forget') {
|
|
3921
4158
|
const r = forgetCore(String(args.file || ''), { selfAgent: agent });
|
|
3922
4159
|
return { text: r.text, error: r.error };
|
|
@@ -4931,9 +5168,20 @@ function consolidateCore(opts) {
|
|
|
4931
5168
|
lines.push('dry-run 未执行任何变更。确认后加 --apply 执行(写入批次审计,可 consolidate --undo <batch> 回滚)。');
|
|
4932
5169
|
return { error: false, text: lines.join('\n') };
|
|
4933
5170
|
}
|
|
5171
|
+
const guard = destructiveGuardCore({
|
|
5172
|
+
root: root,
|
|
5173
|
+
action: 'consolidate --apply',
|
|
5174
|
+
snapshotDir: opts.snapshotDir,
|
|
5175
|
+
backupDir: opts.backupDir,
|
|
5176
|
+
allowSameVolumeForTest: opts.allowSameVolumeForTest,
|
|
5177
|
+
sameVolumeFn: opts.sameVolumeFn,
|
|
5178
|
+
now: opts.now,
|
|
5179
|
+
});
|
|
5180
|
+
if (guard.error) return { error: true, text: guard.text };
|
|
4934
5181
|
const batch = newBatchId();
|
|
4935
5182
|
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
5183
|
lines.push('');
|
|
5184
|
+
lines.push('- 事务快照: ' + guard.snapshot.id);
|
|
4937
5185
|
lines.push('- 批次: ' + batch + '(审计 .archive/audit-<日期>.jsonl;回滚: yotta-memory consolidate --undo ' + batch + ')');
|
|
4938
5186
|
let made = 0, moved = 0;
|
|
4939
5187
|
for (const g of groups) {
|
|
@@ -5094,6 +5342,7 @@ function usage() {
|
|
|
5094
5342
|
['forget', '删除一条记忆'],
|
|
5095
5343
|
['archive', '归档(--days/--threshold 盖棺分+年龄)'],
|
|
5096
5344
|
['backup', '备份记忆库(volumes / setup / status / ensure-daily / schedule / create / list / doctor / restore <id> --to <目录> / drill)'],
|
|
5345
|
+
['doctor', '开工可靠性检查(根目录/密钥库/索引/身份/最近备份;严重异常时锁定破坏性写入)'],
|
|
5097
5346
|
['maintain', '记忆自组织(归档/遗忘候选/去重/合并;默认 dry-run;--dedup 查重+置信度,--dedup --apply 自动合并高置信组)'],
|
|
5098
5347
|
['consolidate', '周期摘要压缩(默认 dry-run;--apply 执行;--undo <batch> 回滚;--batches 查批次;候选=超龄+闲置+低效用,immutable/BOUND 豁免)'],
|
|
5099
5348
|
['distill', '心理日志蒸馏(统计摘要/主题画像/知识地图;--model 可选外部模型)'],
|
|
@@ -5257,6 +5506,7 @@ async function main() {
|
|
|
5257
5506
|
break;
|
|
5258
5507
|
}
|
|
5259
5508
|
case 'reindex': cmdReindex(); break;
|
|
5509
|
+
case 'doctor': cmdDoctor(opts); break;
|
|
5260
5510
|
case 'profile': cmdProfile(opts); break;
|
|
5261
5511
|
case 'context': cmdContext(opts); break;
|
|
5262
5512
|
case 'export': cmdExport(opts.out); break;
|
|
@@ -5431,6 +5681,8 @@ module.exports = {
|
|
|
5431
5681
|
backupStatusCore: backupStatusCore,
|
|
5432
5682
|
backupEnsureDailyCore: backupEnsureDailyCore,
|
|
5433
5683
|
backupHealthCore: backupHealthCore,
|
|
5684
|
+
doctorCore: doctorCore,
|
|
5685
|
+
destructiveGuardCore: destructiveGuardCore,
|
|
5434
5686
|
backupWindowsTaskXml: backupWindowsTaskXml,
|
|
5435
5687
|
backupSystemdServiceContent: backupSystemdServiceContent,
|
|
5436
5688
|
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.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, 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。
|
|
@@ -46,3 +46,13 @@
|
|
|
46
46
|
|
|
47
47
|
## 14. 半衰期 / 归档阈值怎么调?
|
|
48
48
|
`yotta-memory config set maintain_decay_halflife_FACT 1000`(FACT 慢衰减,单位天)、`maintain_decay_halflife_PREF` / `maintain_decay_halflife_COMMIT` 同理;BOUND 固定不衰减。归档阈值:`config set maintain_archived_utility 0.3`、`maintain_archived_age 180`;consolidate 参数 `consolidate_min_age` / `consolidate_min_idle` / `consolidate_max_utility` / `consolidate_min_group` / `consolidate_period`。改完 `config get` 可复查;单次运行也可用 CLI 参数(如 `consolidate --min-age 90`)临时覆盖。
|
|
49
|
+
|
|
50
|
+
## 15. 两个命令有什么区别?怎么更新?
|
|
51
|
+
|
|
52
|
+
- `yotta-memory` 是引擎 CLI,负责读写记忆库;`npx -y @yottameta/yotta-memory` 只是临时运行引擎,**不会安装技能**。
|
|
53
|
+
- `yotta-memory-install` 是技能安装器。安装或更新技能用 `npx -y --package @yottameta/yotta-memory yotta-memory-install --agent <name>`;用 `--dir` 安装时传入同一个技能目录。
|
|
54
|
+
- 更新 CLI:`npm i -g @yottameta/yotta-memory`。更新技能:重跑上面的 `--package ... yotta-memory-install` 命令,或全局安装后运行 `yotta-memory-install --agent <name>`。
|
|
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.0",
|
|
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
|
+
}
|