@yottameta/yotta-memory 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,8 @@
15
15
  <a href="https://github.com/YottaMeta/yotta-memory"><img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen" /></a>
16
16
  </p>
17
17
 
18
+ > 📖 面向用户的操作手册见 [USER_GUIDE.md](USER_GUIDE.md)。
19
+
18
20
  ## 核心价值
19
21
 
20
22
  多数记忆方案把「记住」做成了黑盒:数据进了数据库或云端,用户既看不到内容、也难以审计,更控制不了「哪个智能体能看到什么」。元忆换了一条路:
@@ -22,6 +24,7 @@
22
24
  - **记忆就是文件**:每条记忆是一个带 YAML frontmatter 的 Markdown 文件,放在用户自己的目录里。用任何编辑器都能查看、修改、删除,用 git 就能做版本管理与回滚。
23
25
  - **隔离由机制保证**:FACT 进公共区共享,PREF / BOUND / COMMIT 进私密区、按 agent 隔离。读取按 scope/owner 分区过滤,越界内容由 CLI 拦截、永不返回(默认静默跳过;显式跨读无授权才报错拒绝)——不依赖智能体的「自觉」。
24
26
  - **零依赖、即装即用**:无守护进程、无数据库、无向量库,只需要 Node.js。安装即用,数据留在本机。
27
+ - **便携记忆盘**:记忆库本身就是记忆引擎——装在硬盘或主机上随盘走,局域网内其它主机上的智能体可远程读写;本地零进程模式与局域网常驻模式可并存,插上硬盘即恢复全部记忆。
25
28
 
26
29
  ## 核心优势
27
30
 
@@ -34,6 +37,9 @@
34
37
  | **双级存储** | 用户级 `~/.yottamemory/`(跨项目)+ 项目级 `.yottamemory/`(随项目共享 / 交接)|
35
38
  | **检索与生命周期** | 索引 + TF 打分(中文分词友好);质量分 / 盖棺分 / 自动归档,记忆库不会无限膨胀 |
36
39
  | **生态分发** | GitHub + npm 双源同步发布;npx skills / npm / install.sh 三种方式,覆盖 17+ 类智能体目录 |
40
+ | **便携记忆盘(随盘走)** | 记忆库即引擎:装在硬盘 / 主机上,插上即恢复全部记忆;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体 |
41
+ | **局域网共享与自启** | 每智能体独立 token(Bearer + X-Agent-Id)鉴权、可吊销;`lan enable` 注册开机自启(默认登录自启,可选开机即启);MCP 工具集与 CLI 一致(8 个工具),管理动作不远程暴露 |
42
+ | **本地 / 局域网双模式** | 本地 `serve --stdio` 零进程、按需拉起(无常驻);局域网 streamable HTTP 常驻——两种模式可并存、按需选用 |
37
43
 
38
44
  ## 功能详解
39
45
 
@@ -62,9 +68,20 @@
62
68
  - **默认静默、显式跨读才报错**:默认 recall 遇其它 agent 私密静默跳过(不泄露「存在 N 条私密不可见」);仅当显式跨智能体读取(`--all` / `--owner <其它>`)且无授权命中时才报错 / 警告。
63
69
  - **隔离定位**:scope: private 保证的是 AI 之间的语义隔离(正常 recall 不会被搜到、不会主动去读),不是文件系统级机密保护——数据主权在用户,作为所有者有权看到任何记忆文件。
64
70
 
71
+ ### 智能体身份(唯一 ID + 自我档案)
72
+
73
+ 每个智能体有一个**全局唯一的 agent ID**:它是私密记忆(PREF / BOUND / COMMIT)的归属键,也是远端接入的身份声明(`X-Agent-Id`)。
74
+
75
+ - **登记(必须唯一)**:`yotta-memory iam <id>` 写入记忆库根目录 `agents.json`,**强制唯一性**——ID 已被其它主机 / 来源(含远端 token 登记)占用时拒绝,确认是同一智能体才 `--force`。
76
+ - **确认身份**:`yotta-memory whoami`(远端 MCP 工具 `agent_info`)读「当次声明身份」(本机 `YOTTA_AGENT_ID` / CLI `--agent`;远端 `X-Agent-Id`),不猜不默认。
77
+ - **自我档案(强制落盘)**:`iam` 自动写一条 PREF `subject=自我接入档案`(owner=自己),statement 为 `; ` 分隔的 key:value:`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`。开工先 `recall "自我接入档案"` 找回身份与接入信息。
78
+ - **本机免 token**:本机 CLI / stdio 直连不经网络、不校验 token;身份经该智能体 MCP 配置的 `env.YOTTA_AGENT_ID` 声明。本机多个智能体各自声明唯一 ID,互不撞。
79
+ - **私密记忆必须有 owner**:写 PREF / BOUND / COMMIT 时未声明身份会被拒绝(公共 FACT 不受影响),从机制上防止「抄别人的 ID」。
80
+
65
81
  ### 检索:索引 + 中文分词打分
66
82
 
67
83
  - `remember` 时自动构建 `index.json` 索引;recall 用 TF 打分排序,中文按 bigram 分词,不用额外模型。
84
+ - `index.json` 的 `tokens` 字段是中文分词的词频表(TF 打分用),**不是**访问凭证;鉴权令牌在记忆目录下 `.server/tokens.json`。
68
85
  - 支持关键词、`--type` 过滤、`--limit` 截断、项目级优先。
69
86
  - 命中展示会累加 `access_count` / `last_accessed`,为生命周期管理提供依据。
70
87
 
@@ -80,6 +97,14 @@
80
97
 
81
98
  把技能装进智能体后,SKILL.md 会自动教会智能体一套工作流:**开工 `recall` 恢复上下文 → 重要信息 `remember` 落盘 → 收工归档**。也可在对话中直接说「记住 XXX」「上次说到哪了」「记一笔」触发。
82
99
 
100
+ ### 便携记忆盘(记忆库即引擎)
101
+
102
+ - **记忆库即引擎**:`serve` 把记忆目录挂成 MCP 服务,目录随盘走;引擎主机只需装 CLI 当存放点,无需装任何 AI 智能体。
103
+ - **双模式可并存**:本地 `serve --stdio` 零进程(客户端按需拉起);局域网 streamable HTTP(默认 `0.0.0.0:8787`)+ 每智能体 token 鉴权。
104
+ - **开机自启**:`lan enable` 注册 Windows 计划任务(默认登录自启,`--onstart` 开机即启需管理员);`lan disable` 移除,`lan status` 查询。
105
+ - **安全边界**:管理动作(init / config / token / lan / serve)不进 MCP,token 不远程暴露;远程智能体只能读写记忆,不能改配置、不能管 token。
106
+ - 完整操作步骤见上文「局域网多机共享」章节与 [USER_GUIDE.md](USER_GUIDE.md)。
107
+
83
108
  ## 与其他方案对比
84
109
 
85
110
  > 以下按方案类型对比,不涉及具体产品名称。判断标准:数据主权、部署成本、权限边界、可审计性、跨智能体能力。
@@ -125,8 +150,22 @@ bash install.sh --dir /path/to/skills
125
150
  ```
126
151
  也可把整个 `yotta-memory` 文件夹复制到目标智能体的 skills 目录(常见位置见 install.sh --list)。
127
152
 
153
+ ## 升级
154
+
155
+ 两种升级方式对应两种安装方式:
156
+
157
+ **升级 CLI**(重新执行一次安装命令即可,不带版本号默认装最新):
158
+ ```bash
159
+ npm i -g @yottameta/yotta-memory
160
+ ```
161
+
162
+ **升级技能**:重新执行你当初的安装命令即可(`npx skills add YottaMeta/yotta-memory` 或 `yotta-memory-install -g`),覆盖旧版本技能文件夹。
163
+
164
+ > 升级只影响命令与技能文件,**不会动你已存的记忆**(记忆是独立于安装的文件,保留在原目录)。
165
+
128
166
  ## CLI 用法
129
167
 
168
+
130
169
  | 命令 | 作用 |
131
170
  |---|---|
132
171
  | `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库(默认用户级 `~/.yottamemory/`;--dir 显式指定位置)|
@@ -137,7 +176,9 @@ bash install.sh --dir /path/to/skills
137
176
  | `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
138
177
  | `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
139
178
  | `yotta-memory config set memory_home <目录>` / `config get` | 持久记住 / 查看记忆库位置(`~/.yottamemory/config.json`)|
140
- | `yotta-memory token new --agent <id>` / `token list` / `token revoke --agent <id>` | 为智能体生成 / 列出 / 吊销访问 token(登记于记忆库 `.server/tokens.json`)|
179
+ | `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
180
+ | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
181
+ | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 为智能体生成 / 列出 / 吊销访问 token(登记于记忆库 `.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
141
182
  | `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
142
183
  | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows 计划任务,默认 ONLOGON 登录自启;--onstart 开机即启需管理员)|
143
184
 
@@ -153,7 +194,7 @@ yotta-memory recall --type FACT --limit 10
153
194
 
154
195
  环境变量:
155
196
  - `YOTTA_MEMORY_HOME`:覆盖用户级记忆库目录(默认 `~/.yottamemory/`)。
156
- - `YOTTA_AGENT_ID` / `AGENT_ID`:当前 agent 标识,参与读取分区判定。
197
+ - `YOTTA_AGENT_ID` / `AGENT_ID`:当前 agent 标识(本机声明身份用,参与读取分区判定;私密记忆必须有 owner,未声明会被拒绝)。
157
198
 
158
199
  ## 智能体接入后怎么用
159
200
 
@@ -172,10 +213,11 @@ yotta-memory recall --type FACT --limit 10
172
213
  1. 初始化或接入记忆库(见 CLI 用法)。
173
214
  2. 为需要访问的每个智能体生成独立 token:
174
215
  ```bash
175
- yotta-memory token new --agent <智能体ID> # 打印一次,如 ytm_...
216
+ yotta-memory token new --agent <智能体ID> # 打印一次,如 ytm_...(同 ID 已被其它来源占用需加 --force)
176
217
  yotta-memory token list # 查看已登记智能体
177
218
  yotta-memory token revoke --agent <智能体ID> # 吊销
178
219
  ```
220
+ > 新生成的 token 即时生效,无需重启服务。
179
221
  3. 启动服务(默认监听 0.0.0.0:8787,Bearer token + X-Agent-Id 鉴权)——临时运行或注册开机自启二选一:
180
222
  ```bash
181
223
  yotta-memory serve # 临时前台运行
@@ -204,7 +246,7 @@ yotta-memory recall --type FACT --limit 10
204
246
  }
205
247
  ```
206
248
 
207
- 连接后可通过 MCP tools(remember / recall / search / forget / archive / reindex / export / import)读写记忆;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露。`X-Agent-Id` 必须与 token 登记的智能体一致;读取分区规则与 CLI 相同(FACT 公共可读,PREF / BOUND / COMMIT 私密隔离)。
249
+ 连接后可通过 MCP tools(remember / recall / search / forget / archive / reindex / export / import / agent_info)读写记忆与确认身份;管理动作(init / config / token / lan / serve)不进 MCP,token 管理不远程暴露。`X-Agent-Id` 必须与 token 登记的智能体一致;读取分区规则与 CLI 相同(FACT 公共可读,PREF / BOUND / COMMIT 私密隔离)。
208
250
 
209
251
  ### 位置持久化
210
252
 
package/SKILL.md CHANGED
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: yotta-memory
3
3
  description: "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚:让任何 AI 智能体活过会话,开工 recall 恢复上下文、重要信息 remember 落盘、收工归档。类型体系 FACT(公共共享)/ PREF / BOUND / COMMIT(私密隔离)。触发:记住、别忘了、记一笔、记忆、remember、recall、跨会话、上次说到、续测、交接、归档、记忆盘、共享记忆、局域网记忆"
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  license: MIT
6
6
  ---
7
7
 
8
8
  # yotta-memory(元忆)— 有权限边界的文件式智能体记忆
9
9
 
10
10
  > 一句话:元忆 —— 有权限边界的文件式智能体记忆(不注入、可 diff、能回滚;FACT 共享、PREF / BOUND / COMMIT 私密隔离)。
11
- > 版本:0.4.0 | 最后更新:2026-08-25
11
+ > 版本:0.4.2 | 最后更新:2026-08-25
12
12
 
13
13
  ## 这是什么
14
14
 
@@ -26,11 +26,42 @@ license: MIT
26
26
 
27
27
  ## 核心流程
28
28
 
29
- 1. **开工定向**:运行 `yotta-memory recall`(可带关键词)恢复上下文;项目级记忆优先,其次用户级。
29
+ 1. **开工定向**:先按「开工第一步:确认记忆位置 + 智能体身份」检测记忆库与身份,再运行 `yotta-memory recall`(可带关键词)恢复上下文;项目级记忆优先,其次用户级。
30
30
  2. **进行中落盘**:重要信息立即 `yotta-memory remember <type> <subject> <statement>`,不攒到收工。
31
31
  3. **收工归档**:写会话小结(COMMIT / 笔记),旧记录定期 `yotta-memory archive`。
32
32
  4. **多智能体纪律**:FACT 写入公共区,PREF / BOUND / COMMIT 只写本智能体私密区;不读取其他智能体私密区。
33
33
 
34
+
35
+ ### 开工第一步:确认记忆位置 + 智能体身份(每次会话先做,必做)
36
+
37
+ **A. 确认记忆库位置**(AI 不会自动知道记忆库在哪,先检测,避免「recall 读空库 / 错库」):
38
+
39
+ 1. 运行 `yotta-memory config get`。
40
+ - 输出 `memory_home: <目录>`(已显式设置)→ 直接用该位置。
41
+ - 输出 `memory_home: (未设置,默认 ~/.yottamemory)` → 🔒 征得同意后引导设置:问用户用默认还是指定目录(项目级 `<repo>/.yottamemory`、记忆盘等),确认后 AI 执行 `yotta-memory config set memory_home <目录>`,回读 `config get` 验证。
42
+ 2. **已有记忆**:目标目录已存在 `facts/` 等子目录或 `index.json` → 直接 recall;全新目录 → 按「便携记忆盘模式 §0.3」初始化。
43
+
44
+ **B. 确认本智能体唯一身份(强制,写私密记忆前必做)**:
45
+
46
+ 1. 运行 `yotta-memory whoami`(远端经 MCP 用 `agent_info`)确认「我是谁」。
47
+ - 已显示身份 + 已登记 + 有自我档案 → 用它,进入第 3 步。
48
+ - 显示「未声明身份」或「未登记」→ 进入第 2 步。
49
+ 2. **登记唯一 ID(AI 自己定义,须用户确认)**:
50
+ - AI 提议一个**全局唯一** ID(建议 `<主机名>-<角色>` 或带随机后缀,如 `win-zhiwei` / `kali-dashu`;禁止用 `dashu` / `codex` 这类易撞名)。
51
+ - 🔒 征得用户同意后执行 `yotta-memory iam <id>`:引擎**强制唯一性**(ID 已被其它主机/来源占用 → 拒绝并提示换 ID;确认是同一智能体才 `--force`),并**自动写自我档案**到本智能体私密区。
52
+ - 回读:`yotta-memory whoami` 显示「已登记 + 自我档案」。
53
+ 3. **自我档案校验**:`yotta-memory recall "自我接入档案"`(本智能体)能读回字段才算就绪:
54
+ `agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`。
55
+ 4. **本机多智能体**:本机多个 AI 智能体共用引擎时,**每个都必须**在它自己的 MCP 配置里声明唯一 `YOTTA_AGENT_ID`(如 `env: { YOTTA_AGENT_ID: "<该智能体唯一ID>" }`),各自 `whoami` 各回各的、互不撞;本机走 stdio 免 token。
56
+
57
+ **C. 身份红线(强制)**:
58
+
59
+ - 智能体 ID **必须全局唯一**;**禁止**「从记忆里读到别人的 ID 就当自己的」(如看到「Kali 智能体 ID 为 dashu」就把自己当 dashu)。
60
+ - 不确定自己的 ID → 先 `whoami` / `agent_info`,再向用户确认;**禁止猜**。
61
+ - 私密记忆(PREF / BOUND / COMMIT)**必须有 owner**:未声明身份写私密会被引擎拒绝(公共 FACT 不受影响)。
62
+
63
+ > 已用 `YOTTA_MEMORY_HOME` 临时覆盖时不必改 config;本步骤是常规 CLI 直连用户级位置的引导。
64
+
34
65
  ## CLI 速查
35
66
 
36
67
  | 命令 | 作用 |
@@ -43,13 +74,17 @@ license: MIT
43
74
  | `yotta-memory reindex` | 重建索引(手动改 .md 后校正)|
44
75
  | `yotta-memory export [--out f.json]` / `import <f.json>` | 导出 / 导入 |
45
76
  | `yotta-memory config set memory_home <目录>` / `config get` | 持久记住 / 查看记忆库位置(`~/.yottamemory/config.json`)|
46
- | `yotta-memory token new --agent <id>` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`)|
77
+ | `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
78
+ | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
79
+ | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 每智能体访问 token:生成 / 列出 / 吊销(登记 `<记忆库>/.server/tokens.json`;同 ID 已被其它来源占用需 `--force` 覆盖,防不同智能体合流)|
47
80
  | `yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio]` | 启动 MCP 记忆引擎(streamable HTTP 局域网 / --stdio 本地零进程模式;Bearer token + X-Agent-Id 鉴权)|
48
81
  | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理(Windows 计划任务,默认 ONLOGON 登录自启;--onstart 开机即启需管理员)|
49
82
 
50
83
  ## 存储格式(摘要)
51
84
 
52
- `<YYYY-MM-DD>-<NNNN>.md`,frontmatter 含 `type / subject / statement / confidence / created / updated / tags / immutable / scope / owner / access_count / last_accessed`;正文为记忆内容。顶层另有 `index.json`(反向索引 + TF 打分)。
85
+ `<YYYY-MM-DD>-<NNNN>.md`,frontmatter 含 `type / subject / statement / confidence / created / updated / tags / immutable / scope / owner / access_count / last_accessed`;正文为记忆内容。顶层另有 `index.json`(反向索引 + TF 打分,其 `tokens` 字段是分词词频,非访问令牌)与 `agents.json`(智能体身份登记表,唯一性)。
86
+
87
+ 自我档案(本智能体身份,强制落盘):PREF,`subject=自我接入档案`,`owner=<本智能体ID>`,statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http)/ engine_url(仅远端)/ token(仅远端;本机不存 token)`。
53
88
 
54
89
  ## 便携记忆盘模式(局域网多机共享)
55
90
 
package/USER_GUIDE.md ADDED
@@ -0,0 +1,286 @@
1
+ # 元忆(yotta-memory)用户使用手册
2
+
3
+ > 面向最终用户的操作手册:从安装、初始化,到便携记忆盘(局域网多机共享)的部署与接入。AI 智能体的自动引导流程见 `SKILL.md`。
4
+
5
+ ## 目录
6
+
7
+ 1. 这是什么
8
+ 2. 安装
9
+ 3. 本机单机使用
10
+ 4. 便携记忆盘 · 记忆引擎主机篇(Linux / Windows)
11
+ 5. 智能体接入篇(本机 / 局域网其它主机)
12
+ 6. CLI 命令速查
13
+ 7. 故障排查
14
+ 8. 安全与边界
15
+
16
+ ## 1. 这是什么
17
+
18
+ 元忆(yotta-memory)是一个文件式智能体记忆工具:每条记忆是一个 Markdown 文件,放在你自己的目录里,可以用任何编辑器查看、修改,用 git 做版本管理。FACT 记忆共享给所有智能体,PREF / BOUND / COMMIT 记忆按智能体隔离。
19
+
20
+ - 零依赖:只需要 Node.js,没有数据库、没有常驻服务(除非你开启便携记忆盘模式)。
21
+ - 记忆即文件:数据主权在你手里。
22
+
23
+ **典型使用场景:**
24
+
25
+ - **跨会话续接**:AI 开工 `recall` 恢复上次上下文,不丢记忆。
26
+ - **多智能体共享**:FACT 进公共区共享,PREF / BOUND / COMMIT 各自私密隔离。
27
+ - **交接与团队协作**:项目级 `.yottamemory` 随仓库走,交接即恢复。
28
+ - **便携记忆盘**:记忆装在固定主机上,本机与局域网其它主机共享同一份记忆(见第 4 / 5 篇)。
29
+
30
+ ## 2. 安装(CLI + 技能)
31
+
32
+ 使用元忆需要两部分:**CLI**(命令行工具,负责读写记忆)与**技能**(SKILL.md,负责教 AI 智能体怎么用)。先装 CLI,再把技能装进要用的智能体。
33
+
34
+ **CLI:任选一种方式**
35
+
36
+ | 方式 | 命令 | 适用 |
37
+ |---|---|---|
38
+ | npm 全局(推荐) | `npm i -g @yottameta/yotta-memory` | 长期使用 |
39
+ | npx 临时 | `npx -y @yottameta/yotta-memory` | 临时试用 |
40
+ | install.sh | `bash <(curl -s https://raw.githubusercontent.com/YottaMeta/yotta-memory/main/install.sh)` | 离线 / 无 npm |
41
+
42
+ 安装后验证:`yotta-memory --version` 能输出版本号即成功。
43
+
44
+ **技能(SKILL.md):装进要用的 AI 智能体**
45
+
46
+ | 方式 | 命令 | 适用 |
47
+ |---|---|---|
48
+ | npx skills(生态标准入口) | `npx skills add YottaMeta/yotta-memory` | 自动检测已装智能体 |
49
+ | 安装器(npm 全局时) | `yotta-memory-install -g` / `yotta-memory-install --agent codex` | 装进所有 / 指定智能体 |
50
+ | 手动 | 把整个 `yotta-memory` 文件夹复制到智能体的 skills 目录 | 离线 |
51
+
52
+ > CLI 与技能分工不同:CLI 让命令行能读写记忆;技能让 AI 知道「开工 recall 恢复上下文、重要信息 remember 落盘、收工归档」。只装 CLI 不装技能,AI 不会自动使用这套工作流。
53
+
54
+ **更新:** `npm i -g @yottameta/yotta-memory` 升级 CLI(不带版本号即最新版),再把技能文件夹重新装进智能体(重复执行上面的技能安装命令即可)。
55
+
56
+ **卸载:** `npm rm -g @yottameta/yotta-memory`,并从智能体的 skills 目录删除整个 `yotta-memory` 文件夹。
57
+
58
+ ## 3. 本机单机使用
59
+
60
+ ```bash
61
+ yotta-memory init # 初始化记忆库(默认 ~/.yottamemory)
62
+ yotta-memory remember FACT 项目 本周完成发布 # 记一条事实
63
+ yotta-memory recall 项目 # 检索记忆
64
+ yotta-memory config get # 查看记忆库位置
65
+ ```
66
+
67
+ - 想换记忆位置:`yotta-memory config set memory_home <目录>`,之后所有命令自动用新位置。
68
+ - 项目级记忆:在项目目录里 `yotta-memory init --project`,该项目的智能体优先读项目级记忆。
69
+
70
+ ### 记忆库位置:本机智能体如何找到记忆
71
+
72
+ 位置解析优先级:`YOTTA_MEMORY_HOME`(环境变量,临时覆盖)> `config.json#memory_home`(`config set memory_home` 持久记住)> 默认 `~/.yottamemory`。
73
+
74
+ - 本机智能体装好技能后,AI 开工执行 `yotta-memory config get` 即可看到当前生效位置,`recall` 会自动读对位置。
75
+ - 记忆库换位置后,执行一次 `yotta-memory config set memory_home <新目录>` 即可,无需改技能。
76
+
77
+ ### 手动添加 / 编辑记忆
78
+
79
+ 记忆就是文件:直接在 `facts/` `prefs/` `bounds/` `commits/` 目录下新建或修改 `.md` 文件即可,frontmatter 至少包含 `type` / `subject` / `statement`:
80
+
81
+ > 说明:`index.json` 是引擎自动维护的检索索引,其 `tokens` 字段是中文分词的词频表(供 TF 打分),**不是**访问令牌。手动改过 `.md` 后可用 `yotta-memory reindex` 校正索引,无需手动编辑 `index.json`。
82
+
83
+ ```markdown
84
+ ---
85
+ type: FACT
86
+ subject: 项目
87
+ statement: 本周完成发布
88
+ ---
89
+ 本周完成发布
90
+ ```
91
+
92
+ 手动改过文件后运行 `yotta-memory reindex` 重建索引,`recall` 才能按索引检索到。整个记忆目录可以用 git 做版本管理与回滚。
93
+
94
+ ## 4. 便携记忆盘 · 记忆引擎主机篇
95
+
96
+ 场景:记忆放在一台主机上(Linux / Windows 均可),本机直接 CLI 读写;局域网内其它主机上的 AI 智能体经 MCP 远程接入。引擎主机只需装 CLI,不需要装任何 AI 智能体。
97
+
98
+ **本机与局域网可同时用同一个记忆库**:引擎 `serve` 运行期间,本机智能体照常用 CLI(或 stdio MCP)读写(不经网络、不需要 token),局域网其它主机的智能体同时经 MCP + token 接入——两条通道并存、互不干扰。
99
+
100
+ **第 1 步:安装 CLI**(见第 2 节)。
101
+
102
+ **第 2 步:指定记忆位置并初始化(或接入现有记忆库)**
103
+
104
+ ```bash
105
+ yotta-memory config set memory_home /srv/yotta-memory # 改成你的实际目录
106
+ yotta-memory init --dir /srv/yotta-memory # 新库:初始化(自动建 facts/prefs/bounds/commits/.archive)
107
+ ```
108
+
109
+ 如果目标目录**已经是记忆库**(里面有 `facts/` 等子目录或 `index.json`,例如从旧机复制或 git 克隆来的),直接 `config set memory_home <目录>` 接入即可,**不要重复 init**。
110
+
111
+ **第 3 步:注册开机自启(可选,推荐)**
112
+
113
+ ```bash
114
+ # Windows:内置命令(计划任务)
115
+ yotta-memory lan enable # 登录后自动启动(默认)
116
+ yotta-memory lan enable --onstart # 开机即启(需管理员)
117
+ yotta-memory lan status # 查看状态
118
+ yotta-memory lan disable # 取消
119
+
120
+ # Linux:用 systemd 开机自启(lan 命令当前仅支持 Windows)
121
+ # 先查 yotta-memory 实际路径:which yotta-memory(npm 全局默认 /usr/local/bin/yotta-memory)
122
+ sudo tee /etc/systemd/system/yotta-memory.service > /dev/null <<'EOF'
123
+ [Unit]
124
+ Description=yotta-memory memory engine
125
+ After=network.target
126
+
127
+ [Service]
128
+ ExecStart=/usr/local/bin/yotta-memory serve --host 0.0.0.0 --port 8787
129
+ Restart=on-failure
130
+
131
+ [Install]
132
+ WantedBy=multi-user.target
133
+ EOF
134
+ sudo systemctl daemon-reload
135
+ sudo systemctl enable --now yotta-memory
136
+ systemctl status yotta-memory # 查看状态
137
+ ```
138
+
139
+ **第 4 步:为局域网其它主机的智能体生成访问 token**
140
+
141
+ ```bash
142
+ yotta-memory token new --agent 我的智能体ID
143
+ ```
144
+
145
+ 命令会打印一次 `ytm_...`,请妥善保管(它就是访问凭证)。多个智能体就重复执行、用不同 ID。**本机上的 AI 智能体不需要 token**(见第 5 篇)。
146
+
147
+ > token 丢失或要更换:重新执行 `token new --agent <id> --force` 会生成新 token 并覆盖旧的,旧 token 立即失效;不加 `--force` 时若该 ID 已被其它来源占用会被拒绝(每个 AI 的 ID 必须唯一);`token list` 可查看已登记哪些智能体。
148
+
149
+ **第 5 步:把连接信息交给远程智能体的用户**
150
+
151
+ - 引擎地址:`http://<本机IP>:8787/mcp`
152
+ - 该智能体的 token:`ytm_...`
153
+ - 智能体 ID(对应 X-Agent-Id 请求头)
154
+
155
+ 查本机 IP:Linux 运行 `hostname -I`(或 `ip a`);Windows 运行 `ipconfig` 找「IPv4 地址」。防火墙:Linux 若启用了 ufw,执行 `sudo ufw allow 8787/tcp`;Windows 首次监听时允许放行。否则局域网其它主机连不进来。
156
+
157
+ **第 6 步:备份与迁移**
158
+
159
+ - 备份 = 复制整个记忆目录(`facts/` `prefs/` `bounds/` `commits/` `.archive/` + `index.json` + `agents.json` + `.server/`),复制到哪、哪就是记忆库;迁移同理,整个目录拷走即可。
160
+ - `export` / `import` 是把记忆导出成单个 JSON 或从 JSON 导入,适合跨工具交换或归档,不是日常备份的必需步骤。
161
+
162
+ ## 5. 智能体接入篇(本机 / 局域网其它主机)
163
+
164
+ 接入智能体分两类:引擎主机本机的智能体、局域网其它主机上的智能体。本机不需要 token、不依赖远程服务;其它主机经 MCP + token 接入。
165
+
166
+ ### 5.1 本机 AI 智能体(引擎主机本机)
167
+
168
+ **方式一(推荐,最简单):让智能体直接调用 CLI。**
169
+
170
+ ```bash
171
+ yotta-memory recall <关键词> # 智能体开工恢复上下文
172
+ yotta-memory remember FACT 主题 内容 # 智能体落盘
173
+ ```
174
+
175
+ 智能体装上技能后(`SKILL.md`)会自动学会这套工作流,无需任何 MCP 配置。
176
+
177
+ **方式二:stdio MCP(零常驻进程,智能体按需拉起 CLI)。** 在智能体 MCP 配置里加:
178
+
179
+ ```json
180
+ {
181
+ "mcpServers": {
182
+ "yotta-memory": {
183
+ "command": "yotta-memory",
184
+ "args": ["serve", "--stdio"],
185
+ "env": { "YOTTA_AGENT_ID": "<该智能体唯一ID>" }
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
191
+ 本机接入不需要 token,也不需要启动 HTTP 服务;但**必须在配置里声明唯一的 `YOTTA_AGENT_ID`**(见下),否则写私密记忆会被拒。
192
+
193
+ **本机智能体装好技能后如何获取记忆存放位置?** 按优先级:`YOTTA_MEMORY_HOME` 环境变量 > `config set memory_home` 持久化的 `~/.yottamemory/config.json` > 默认 `~/.yottamemory`。AI 开工执行 `yotta-memory config get` 查看当前生效位置;记忆库移动后执行一次 `config set memory_home <新目录>` 即可。
194
+
195
+ **给本机智能体设置唯一身份(强制)**:私密记忆(PREF / BOUND / COMMIT)按 owner 隔离,owner 取当前智能体的 agent ID。流程如下:
196
+
197
+ 1. 开工先 `yotta-memory whoami` 确认「我是谁」。
198
+ 2. 未登记 → 向用户确认一个**全局唯一** ID(建议 `<主机名>-<角色>`,别用 `dashu` / `codex` 这类易撞名),执行 `yotta-memory iam <id>`:引擎**强制唯一性**(被其它主机 / 来源占用会拒绝),并自动落一条「自我接入档案」PREF(owner=自己)。
199
+ 3. 本机多个 AI 智能体共用引擎时,**每个都要在它自己的 MCP 配置里声明唯一 `YOTTA_AGENT_ID`**(CLI 直连则每次带 `--agent <id>`),各自 `whoami` 各回各的、互不撞。
200
+ 4. **禁止**从记忆里读到别人的 ID 就当自己的(比如看到「Kali 智能体 ID 为 dashu」就把自己当 dashu);不确定先 `whoami` 再问用户,**禁止猜**。
201
+ 5. **不设则 owner 为空**:写私密记忆会被引擎拒绝(公共 FACT 不受影响),避免私密隔离退化。
202
+
203
+ ### 5.2 局域网其它主机的 AI 智能体
204
+
205
+ **第 1 步:向记忆引擎主机获取**:引擎 IP、端口(默认 8787)、本智能体的 token、智能体 ID。
206
+
207
+ **第 2 步:配置 MCP**(可以让 AI 按 `SKILL.md` 引导自动完成;也可以手动在你的智能体 MCP 配置里加这段):
208
+
209
+ ```json
210
+ {
211
+ "mcpServers": {
212
+ "yotta-memory": {
213
+ "url": "http://<引擎主机IP>:8787/mcp",
214
+ "headers": {
215
+ "Authorization": "Bearer <TOKEN>",
216
+ "X-Agent-Id": "<本智能体ID>"
217
+ }
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ **第 3 步:验证**:让 AI 调一次 `recall` / `search`,能读到记忆即连接成功。
224
+
225
+ **第 4 步:复用**:连接成功后就一直复用;token 失效(被吊销)时回到第 1 步重新获取。
226
+
227
+ **身份确认与自我档案(强制)**:先调一次 MCP 工具 `agent_info` 确认「我是谁」(读 X-Agent-Id 声明 + 登记状态)。随后用 `remember` 写一条 `subject=自我接入档案` 的 PREF(owner=自己),body 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode: http / engine_url / token`,把接入信息存进自己的永久记忆;下次会话 `recall "自我接入档案"` 直接找回。
228
+
229
+ ## 6. CLI 命令速查
230
+
231
+ | 命令 | 作用 |
232
+ |---|---|
233
+ | `yotta-memory init [--project] [--dir <目录>]` | 初始化记忆库 |
234
+ | `yotta-memory remember <类型> <主题> <内容> [--owner <id>]` | 写入记忆 |
235
+ | `yotta-memory recall [关键词] [--type T] [--limit N]` | 检索记忆 |
236
+ | `yotta-memory forget <文件>` | 删除一条记忆 |
237
+ | `yotta-memory archive [--days 180] [--threshold 0.4]` | 归档旧记忆 |
238
+ | `yotta-memory reindex` | 重建索引 |
239
+ | `yotta-memory export [--out 文件.json]` / `import <文件.json>` | 导出 / 导入 |
240
+ | `yotta-memory config set memory_home <目录>` / `config get` | 记忆库位置 |
241
+ | `yotta-memory whoami` | 查看当前智能体身份与登记状态(读 `YOTTA_AGENT_ID` / `X-Agent-Id`,不猜不默认)|
242
+ | `yotta-memory iam <id> [--force]` | 登记本智能体唯一身份并自动落自我档案(`agents.json`,ID 必须唯一)|
243
+ | `yotta-memory token new --agent <id> [--force]` / `token list` / `token revoke --agent <id>` | 访问 token(同 ID 已被其它来源占用需 `--force` 覆盖)|
244
+ | `yotta-memory serve [--port 8787] [--stdio] [--no-auth]` | 启动记忆引擎(--no-auth 关闭鉴权,仅限可信内网)|
245
+ | `yotta-memory lan enable [--onstart] / disable / status` | 开机自启管理 |
246
+
247
+ 类型:`FACT`(事实,共享)/ `PREF`(偏好)/ `BOUND`(边界)/ `COMMIT`(承诺),后三类按智能体隔离。
248
+
249
+ ## 7. 故障排查
250
+
251
+ **远程连不上时,按顺序检查:**
252
+
253
+ 1. 引擎在运行吗?Windows:`yotta-memory lan status`;Linux(systemd 自启时):`systemctl status yotta-memory`,或 `ps aux | grep yotta-memory`;也可直接 `yotta-memory --version`。
254
+ 2. IP 对吗?引擎主机执行 `hostname -I`(Linux)或 `ipconfig`(Windows)确认。
255
+ 3. 端口对吗?默认 8787,两端要一致。
256
+ 4. 防火墙放行了吗?Linux:`sudo ufw status`,未放行则 `sudo ufw allow 8787/tcp`;Windows:允许首次监听的入站请求。
257
+ 5. token 有效吗?记忆盘主机执行 `yotta-memory token list` 看该智能体是否登记;无效就 `token new --agent <id>` 重新生成。
258
+ 6. 网络通吗?任意一台主机执行 `curl http://<引擎IP>:8787/mcp`:
259
+ - 返回 401:服务在运行,是鉴权问题(token / 请求头不对)。
260
+ - 连接被拒 / 超时:服务没启动,或防火墙拦截。
261
+
262
+ **`lan enable` 失败提示 Access denied:** 需要管理员权限,请用管理员终端重新执行;`--onstart` 模式必须管理员。
263
+
264
+ **启动引擎报端口被占用(EADDRINUSE):** 用 `--port <其它端口>` 换端口启动,两端保持一致;或先查占用:Linux `ss -tlnp | grep 8787`,Windows `netstat -ano | findstr 8787`。
265
+
266
+ **recall 没有结果:** 关键词太细或确实没有这条记忆;先 `recall`(不带关键词)看库里的全部记忆核对。
267
+
268
+ **systemd 自启的引擎看日志:** `journalctl -u yotta-memory -n 50`。
269
+
270
+ **记错位置了?** `yotta-memory config get` 查看当前生效位置;`config set memory_home <正确目录>` 改正。
271
+
272
+ ## 8. 安全与边界
273
+
274
+ - token 等同密码:只给需要接入的智能体,别外传。
275
+ - 元忆不做加密:PREF / BOUND / COMMIT 私密记忆的隔离是「权限边界」机制(谁该读由 scope/owner 决定),属于纪律层保护,不是机密数据保护层。
276
+ - 管理动作(init / config / token / lan / serve)不通过 MCP 暴露,远程只能读写记忆,不能改配置、不能管 token。
277
+ - `--no-auth` 会关闭鉴权,仅限可信内网使用。
278
+ - 数据主权在用户:所有数据都是本地明文文件,随时可看、可改、可删。
279
+
280
+ **确实需要读取其它智能体的私密记忆时(三种授权方式,满足任一即可):**
281
+
282
+ 1. 显式授权 `grants.json`:在记忆库根目录写 `{"<你的agentID>": ["<对方agentID>"]}`;
283
+ 2. identity=user:以 `--agent user` / `--owner user` / 环境变量 `YOTTA_AGENT_ID=user` 读取;
284
+ 3. 显式放行 `--unsafe`:用户明确同意时使用。
285
+
286
+ **协作纪律**:FACT 写入公共区共享;PREF / BOUND / COMMIT 只写自己的私密区;不主动读取其它智能体的私密记忆。
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // yotta-memory(元忆): 有权限边界的文件式智能体记忆 CLI(零依赖)
3
3
  // v0.4.0 新增:lan(Windows 计划任务开机自启 serve)/ init --dir(显式指定位置)/ serve --stdio(本地零进程模式)/ MCP 工具补 reindex/export/import
4
+ // v0.4.2 新增:iam/whoami 智能体身份自注册(agents.json 唯一性)+ 自我档案 PREF + 私密记忆须声明 owner
4
5
  'use strict';
5
6
 
6
7
  const fs = require('fs');
@@ -10,7 +11,7 @@ const crypto = require('crypto');
10
11
  const http = require('http');
11
12
  const child_process = require('child_process');
12
13
 
13
- const VERSION = '0.4.0';
14
+ const VERSION = '0.4.2';
14
15
  const TYPES = ['FACT', 'PREF', 'BOUND', 'COMMIT'];
15
16
  const TYPE_DIRS = { FACT: 'facts', PREF: 'prefs', BOUND: 'bounds', COMMIT: 'commits' };
16
17
  const ARCHIVE_DIR = '.archive';
@@ -20,6 +21,7 @@ const FIELD_ORDER = ['type', 'subject', 'statement', 'confidence', 'created', 'u
20
21
  const CONFIG_FILE = 'config.json';
21
22
  const SERVER_SUBDIR = '.server';
22
23
  const TOKENS_FILE = 'tokens.json';
24
+ const AGENTS_FILE = 'agents.json';
23
25
 
24
26
  // ---- 全局配置(记忆库位置持久化,固定 ~/.yottamemory/config.json)----
25
27
  function configPath() {
@@ -311,6 +313,9 @@ function rememberCore(type, subject, statement, opts) {
311
313
  if (!subj) return { error: true, text: 'subject 不能为空' };
312
314
  const owner = opts.owner || currentAgent();
313
315
  const scope = opts.scope || defaultScope(t);
316
+ if (scope === 'private' && !owner) {
317
+ return { error: true, text: '私密记忆必须声明归属智能体:请设环境变量 YOTTA_AGENT_ID(或 AGENT_ID)、传 --owner <id>,或先 yotta-memory whoami / iam 登记唯一身份。公共记忆(FACT)不受影响。' };
318
+ }
314
319
  if (fs.existsSync(dir)) {
315
320
  for (const f of fs.readdirSync(dir)) {
316
321
  const fp = path.join(dir, f);
@@ -586,13 +591,18 @@ function saveTokens(root, data) {
586
591
  fs.writeFileSync(tokensPath(root), JSON.stringify(data, null, 2), 'utf8');
587
592
  try { fs.chmodSync(tokensPath(root), 0o600); } catch (e) {}
588
593
  }
589
- function cmdTokenNew(agentId) {
594
+ function cmdTokenNew(agentId, opts) {
590
595
  const root = userRoot();
591
596
  ensureInit(root);
592
597
  if (!agentId) { console.error('请指定 --agent <id>'); process.exit(2); }
593
598
  const data = loadTokens(root);
594
599
  data.version = 1;
595
600
  data.tokens = data.tokens || {};
601
+ const conflict = identityConflict(root, agentId);
602
+ if (conflict && !opts.force) {
603
+ console.error("错误: 智能体 ID '" + agentId + "' " + conflict + '。每个 AI 智能体的 ID 必须唯一;确认是同一智能体后加 --force 覆盖。');
604
+ process.exit(2);
605
+ }
596
606
  const token = 'ytm_' + crypto.randomBytes(16).toString('hex');
597
607
  data.tokens[agentId] = { token: token, created: today() };
598
608
  saveTokens(root, data);
@@ -616,6 +626,104 @@ function cmdTokenRevoke(agentId) {
616
626
  else { console.log('未找到已登记智能体: ' + agentId); }
617
627
  }
618
628
 
629
+
630
+ // ---- 智能体身份登记(agents.json)+ 自我档案 ----
631
+ function agentsPath(root) { return path.join(root, AGENTS_FILE); }
632
+ function loadAgents(root) {
633
+ try { return JSON.parse(fs.readFileSync(agentsPath(root), 'utf8')) || {}; } catch (e) { return {}; }
634
+ }
635
+ function saveAgents(root, data) {
636
+ fs.mkdirSync(root, { recursive: true });
637
+ fs.writeFileSync(agentsPath(root), JSON.stringify(data, null, 2), 'utf8');
638
+ }
639
+ const SELF_PROFILE_SUBJECT = '自我接入档案';
640
+ function findSelfProfile(root, agentId) {
641
+ const dir = path.join(root, TYPE_DIRS.PREF);
642
+ if (!fs.existsSync(dir)) return '';
643
+ for (const f of fs.readdirSync(dir)) {
644
+ const fp = path.join(dir, f);
645
+ if (!fs.statSync(fp).isFile()) continue;
646
+ const meta = parseFrontmatter(fs.readFileSync(fp, 'utf8')).meta;
647
+ if (meta.subject === SELF_PROFILE_SUBJECT && (meta.owner || '') === agentId) return f;
648
+ }
649
+ return '';
650
+ }
651
+ function selfProfileBody(agentId, root, extra) {
652
+ const lines = [];
653
+ lines.push('agent_id: ' + agentId);
654
+ lines.push('host: ' + os.hostname());
655
+ lines.push('memory_home: ' + root);
656
+ lines.push('mcp_mode: ' + (extra.mcpMode || 'stdio'));
657
+ if (extra.engineUrl) lines.push('engine_url: ' + extra.engineUrl);
658
+ if (extra.token) lines.push('token: ' + extra.token);
659
+ return lines.join('; ');
660
+ }
661
+ function writeSelfProfile(root, agentId, extra) {
662
+ const dir = path.join(root, TYPE_DIRS.PREF);
663
+ const seq = nextSeq(dir);
664
+ const file = path.join(dir, today() + '-' + seq + '.md');
665
+ const body = selfProfileBody(agentId, root, extra || {});
666
+ const rec = {
667
+ type: 'PREF', subject: SELF_PROFILE_SUBJECT, statement: body,
668
+ confidence: 1.0, created: today(), updated: today(),
669
+ tags: ['自我档案'], immutable: false,
670
+ scope: 'private', owner: agentId, access_count: 0, last_accessed: '',
671
+ };
672
+ fs.writeFileSync(file, frontmatterToText(rec, body), 'utf8');
673
+ upsertIndexEntry(root, readEntry(file, root));
674
+ return file;
675
+ }
676
+ function identityConflict(root, agentId) {
677
+ const agents = loadAgents(root).agents || {};
678
+ const tokens = loadTokens(root).tokens || {};
679
+ const host = os.hostname();
680
+ const a = agents[agentId];
681
+ if (a && a.host && a.host !== host) return '已被智能体身份登记占用(host=' + a.host + ')';
682
+ if (tokens[agentId]) return '已被远端 token 登记占用(.server/tokens.json)';
683
+ return '';
684
+ }
685
+ function cmdIam(agentId, opts) {
686
+ const root = userRoot();
687
+ ensureInit(root);
688
+ if (!agentId) { console.error('请指定 <id>:yotta-memory iam <id> [--force]'); process.exit(2); }
689
+ const conflict = identityConflict(root, agentId);
690
+ if (conflict && !opts.force) {
691
+ console.error("错误: ID '" + agentId + "' " + conflict + '。每个 AI 智能体的 ID 必须唯一。请换一个唯一 ID,或确认是同一智能体后加 --force 覆盖。');
692
+ process.exit(2);
693
+ }
694
+ const data = loadAgents(root);
695
+ data.version = 1;
696
+ data.agents = data.agents || {};
697
+ const host = os.hostname();
698
+ const existed = data.agents[agentId];
699
+ data.agents[agentId] = { host: host, created: (existed && existed.created) || today() };
700
+ saveAgents(root, data);
701
+ const file = writeSelfProfile(root, agentId, { mcpMode: 'stdio' });
702
+ console.log('已登记智能体身份: ' + agentId + '(host=' + host + ',' + (conflict ? '--force 覆盖' : '新建') + ')');
703
+ console.log('已写入自我档案: ' + file);
704
+ console.log('本机免 token:以后用 whoami 确认身份;远端接入需 token new --agent ' + agentId);
705
+ }
706
+ function cmdWhoami() {
707
+ const root = userRoot();
708
+ const id = currentAgent();
709
+ if (!id) {
710
+ console.log('当前未声明智能体身份(YOTTA_AGENT_ID / AGENT_ID 未设置,也未传 --agent)。');
711
+ console.log('本机:请在该智能体的 MCP 配置 env 设 YOTTA_AGENT_ID=<唯一ID>,然后 yotta-memory iam <id> 登记;或 CLI 每次带 --agent <id> / --owner <id>。');
712
+ console.log('远端:X-Agent-Id 请求头 + token(token new --agent <id>)。');
713
+ return;
714
+ }
715
+ const agents = loadAgents(root).agents || {};
716
+ const tokens = loadTokens(root).tokens || {};
717
+ let reg = '未登记';
718
+ if (agents[id]) reg = 'agents.json 已登记(host=' + agents[id].host + ')';
719
+ else if (tokens[id]) reg = 'tokens.json 已登记(远端)';
720
+ const profile = findSelfProfile(root, id);
721
+ console.log('当前智能体身份: ' + id);
722
+ console.log('登记状态: ' + reg);
723
+ console.log('自我档案: ' + (profile ? profile : '未写入(请执行 yotta-memory iam ' + id + ')'));
724
+ if (!agents[id] && !tokens[id]) console.log('提示: 请先 yotta-memory iam ' + id + ' 登记唯一身份并落自我档案,再写私密记忆。');
725
+ }
726
+
619
727
  // ---- config 命令 ----
620
728
  function cmdConfigSet(key, value) {
621
729
  if (key !== 'memory_home') { console.error('未知配置项: ' + key + '(可用: memory_home)'); process.exit(2); }
@@ -643,6 +751,7 @@ function mcpTools() {
643
751
  { name: 'reindex', description: '重建索引(手动改 .md 后校正;扫描 facts/prefs/bounds/commits 四目录)', inputSchema: { type: 'object', properties: {} } },
644
752
  { name: 'export', description: '导出全部记忆到引擎主机上的 JSON 文件。out 可选(默认 <记忆库>/yottamemory-export-<日期>.json)', inputSchema: { type: 'object', properties: { out: { type: 'string' } } } },
645
753
  { name: 'import', description: '从引擎主机上的 JSON 文件导入记忆。src 为文件路径(可绝对路径,或相对记忆库目录)', inputSchema: { type: 'object', properties: { src: { type: 'string' } }, required: ['src'] } },
754
+ { name: 'agent_info', description: '查看当前智能体身份与登记状态(远端读经 token 校验的 X-Agent-Id;本机读 YOTTA_AGENT_ID)。开工先确认「我是谁」,禁止从记忆里抄别人的 ID', inputSchema: { type: 'object', properties: {} } },
646
755
  ];
647
756
  }
648
757
  function callTool(name, args, ctx) {
@@ -678,6 +787,18 @@ function callTool(name, args, ctx) {
678
787
  const r = importCore(userRoot(), String(args.src || ''));
679
788
  return { text: r.text, error: r.error };
680
789
  }
790
+ if (name === 'agent_info') {
791
+ const root = userRoot();
792
+ const id = agent || '';
793
+ if (!id) return { text: '当前未声明智能体身份(无 X-Agent-Id / YOTTA_AGENT_ID)。本机请在 MCP 配置 env 设 YOTTA_AGENT_ID=<唯一ID> 并 iam 登记。', error: false };
794
+ const agents = loadAgents(root).agents || {};
795
+ const tokens = loadTokens(root).tokens || {};
796
+ let reg = '未登记';
797
+ if (agents[id]) reg = 'agents.json 已登记(host=' + agents[id].host + ')';
798
+ else if (tokens[id]) reg = 'tokens.json 已登记(远端)';
799
+ const profile = findSelfProfile(root, id);
800
+ return { text: '当前智能体身份: ' + id + '\n登记状态: ' + reg + '\n自我档案: ' + (profile || '未写入(本地引擎主机执行 yotta-memory iam ' + id + ')'), error: false };
801
+ }
681
802
  return { text: '未知工具: ' + name, error: true };
682
803
  } catch (e) {
683
804
  return { text: '错误: ' + (e && e.message ? e.message : String(e)), error: true };
@@ -862,7 +983,9 @@ function usage() {
862
983
  ' yotta-memory reindex 重建索引',
863
984
  ' yotta-memory export [--out 文件.json] 导出全部记忆',
864
985
  ' yotta-memory import <文件.json> 导入记忆',
865
- ' yotta-memory token new --agent <id> 生成/重置某智能体访问 token(登记 .server/tokens.json,打印一次)',
986
+ ' yotta-memory whoami 查看当前智能体身份与登记状态(读 YOTTA_AGENT_ID / X-Agent-Id,不猜不默认)',
987
+ ' yotta-memory iam <id> [--force] 登记本智能体唯一身份并自动落自我档案(agents.json;ID 必须唯一)',
988
+ ' yotta-memory token new --agent <id> [--force] 生成/重置某智能体访问 token(同 ID 已被其它来源占用需 --force 覆盖)',
866
989
  ' yotta-memory token list 列出已登记智能体',
867
990
  ' yotta-memory token revoke --agent <id> 吊销某智能体 token',
868
991
  ' yotta-memory serve [--host 0.0.0.0] [--port 8787] [--no-auth] [--stdio] 启动 MCP 记忆引擎(streamable HTTP;--stdio 本地零进程模式)',
@@ -870,7 +993,7 @@ function usage() {
870
993
  ' yotta-memory --version 版本',
871
994
  '',
872
995
  '类型: FACT(公共共享) / PREF(偏好) / BOUND(边界) / COMMIT(承诺)',
873
- '环境变量: YOTTA_MEMORY_HOME 临时覆盖用户级位置; YOTTA_AGENT_ID/AGENT_ID 当前 agent 标识(读取分区)',
996
+ '环境变量: YOTTA_MEMORY_HOME 临时覆盖用户级位置; YOTTA_AGENT_ID/AGENT_ID 当前 agent 标识(本机声明身份用;私密记忆必须有 owner)',
874
997
  '远端接入: MCP url http://<主机IP>:8787/mcp;请求头 Authorization: Bearer <token> + X-Agent-Id: <id>',
875
998
  '',
876
999
  ].join('\n'));
@@ -892,6 +1015,7 @@ function main() {
892
1015
  else if (a === '--no-auth') opts.noAuth = true;
893
1016
  else if (a === '--stdio') opts.stdio = true;
894
1017
  else if (a === '--onstart') opts.onstart = true;
1018
+ else if (a === '--force') opts.force = true;
895
1019
  else if (valueOpts.has(a)) {
896
1020
  const v = args[++i];
897
1021
  if (a === '--type') opts.type = v;
@@ -914,6 +1038,8 @@ function main() {
914
1038
  }
915
1039
  switch (first) {
916
1040
  case 'init': cmdInit(opts); break;
1041
+ case 'whoami': cmdWhoami(); break;
1042
+ case 'iam': cmdIam(positional[0], opts); break;
917
1043
  case 'config': {
918
1044
  const sub = positional[0];
919
1045
  if (sub === 'set') cmdConfigSet(positional[1], positional[2]);
@@ -930,7 +1056,7 @@ function main() {
930
1056
  case 'import': cmdImport(positional[0]); break;
931
1057
  case 'token': {
932
1058
  const sub = positional[0];
933
- if (sub === 'new') cmdTokenNew(opts.agent);
1059
+ if (sub === 'new') cmdTokenNew(opts.agent, opts);
934
1060
  else if (sub === 'list') cmdTokenList();
935
1061
  else if (sub === 'revoke') cmdTokenRevoke(opts.agent);
936
1062
  else { console.error('token 子命令: new --agent <id> / list / revoke --agent <id>'); process.exit(2); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottameta/yotta-memory",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "元忆 —— 有权限边界的文件式智能体记忆。文件式、零依赖、可 diff/回滚;FACT/PREF/BOUND/COMMIT 四分类(公共共享/私密隔离),用户级+项目级双级存储。",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -15,6 +15,7 @@
15
15
  "SKILL.md",
16
16
  "LICENSE",
17
17
  "README.md",
18
+ "USER_GUIDE.md",
18
19
  "install.sh",
19
20
  "references",
20
21
  "scripts",
@@ -22,10 +22,20 @@
22
22
  ├── bounds/ # BOUND 边界(私密)
23
23
  ├── commits/ # COMMIT 承诺(私密)
24
24
  ├── .archive/ # 归档区(archive 命令移入)
25
- ├── index.json # 反向索引 + TF 打分
25
+ ├── index.json # 反向索引 + TF 打分(见下方说明)
26
+ ├── agents.json # 智能体身份登记表(iam 写入,唯一性强制)
26
27
  └── README.md # 记忆库说明
27
28
  ```
28
29
 
30
+ > **`index.json` 说明**:条目内的 `tokens` 字段是中文分词的「词频表」,供 recall 的 TF 打分使用,**不是**访问令牌。真正的访问令牌由 `token` 命令生成,存放在 `<root>/.server/tokens.json`,仅在局域网 `serve` 模式下用于请求鉴权。
31
+
32
+ ## 2.5 智能体身份(谁在写 / 谁在读)
33
+
34
+ - **agent ID 必须全局唯一**:`iam <id>` 写入 `agents.json`(记忆库根目录),唯一性强制——ID 已被其它主机 / 来源(含远端 token 登记)占用则拒绝,确认是同一智能体才 `--force`。
35
+ - **当次身份声明**:`whoami` / MCP `agent_info` 读「当次声明身份」——本机 `YOTTA_AGENT_ID`(stdio 由 MCP 配置 `env` 注入,CLI 用 `--agent`/环境变量);远端 `X-Agent-Id` 请求头(经 token 绑定校验)。不猜不默认。
36
+ - **自我档案**:`iam` 自动写一条 PREF `subject=自我接入档案`(owner=自己),statement 为 `; ` 分隔的 key:value——`agent_id / host / memory_home / mcp_mode(stdio|http) / engine_url(仅远端) / token(仅远端;本机不存 token)`。
37
+ - **私密记忆必须有 owner**:PREF / BOUND / COMMIT 写入时未声明身份(owner 空)直接拒绝(公共 FACT 不受影响),从机制上防止「抄别人的 ID」。
38
+
29
39
  ## 3. 文件格式
30
40
 
31
41
  每个记忆一条独立 `.md` 文件,文件名 `<YYYY-MM-DD>-<NNNN>.md`(NNNN 为当日序号):