Files
mnote/design/07-ai/process/7-41-page-ai-hermes-reasonix-user-profile-isolation-v2.md
T

426 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 7-41 Page AI Hermes / Reasonix user profile isolation v2
> 创建时间:2026-05-29
>
> 状态:`process`
>
> OwnerPage AI agent identity / Hermes profile policy / Reasonix memory policy
>
> 上位依据:
> - `design/07-ai/done/7-39-page-ai-agent-selector-context-authorization-settings-v1.md`
> - `design/07-ai/done/7-40-page-ai-context-envelope-and-run-receipt-v1.md`
> - `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
> - `design/07-ai/process/7-38-page-ai-sidebar-runtime-owner-split-v1.md`
>
> 参考依据:
> - Hermes 官方 profile 文档:profile 是独立 Hermes home,包含 `config.yaml`、`.env`、`SOUL.md`、memories、sessions、skills、cron、state database、gateway state。
> - Hermes WebUI:技能 toggle 直接写 profile `config.skills.disabled`。
> - Hermes VSCode:只管理会话和一次性上下文建议,不提供真正的 per-user skill disable。
> - PilotDeck:以 WorkSpace 为边界隔离文件、记忆和技能,并强调白盒记忆可追溯。
> - Reasonix 文档/本地实现:默认使用 `~/.reasonix/memory/global` 与 `~/.reasonix/memory/<project-hash>``REASONIX_MEMORY=off|false|0` 可关闭 memory 注入。
## 1. 背景
`7-39` 已把 Page AI UI 收口为 agent selector + contextRefs + SQLite per-user preference。`7-40` 进一步把 MNote skill library 定位为 agent 可按需读取的能力摘要,而不是每轮强行注入 prompt。
但当前 Hermes skill/profile 的真实边界仍不正确:
- Hermes 的 `skills.disabled` 是 profile 级配置,不是 MNote 用户级偏好。
- Hermes profile 自带 `SOUL.md`、memory、session、skills 和 state;多个 MNote 用户共用同一个可写 profile,会把个人偏好、记忆和技能配置混在一起。
- 当前 Page AI UI 已经能展示 Hermes skill toggle,但普通用户如果直接写共享 Hermes profile,就会影响其它用户。
- Reasonix 虽然不像 Hermes 那样有 SOUL 人格,但它也有 global/project memory。默认共享 `~/.reasonix` 时,不能假设完全无状态。
因此本稿把 Page AI agent identity 从“选择 Hermes / Reasonix”提升为“选择 agent + profile scope + memory policy”。
## 2. 产品决策
### 2.1 Hermes profile 分层
Hermes profile 分为两类:
| profile kind | owner | 普通用户能否使用 | 普通用户能否改 skill | memory/session |
| --- | --- | --- | --- | --- |
| `personal` | 单个 MNote 用户 | 能 | 能,仅限自己的 profile | 用户独立 |
| `shared` | 系统 / 管理员 | 能,若管理员公开 | 不能 | 默认不写入用户私有长期记忆 |
初始共享 Hermes profile 只包含 `lite`
要求:
- 每个 MNote 用户可以访问自己的 personal Hermes profile。
- 每个 MNote 用户可以访问被管理员公开的 shared Hermes profile。
- shared Hermes profile 的 skill/config 只有管理员能改。
- personal Hermes profile 的 skill/config 只有该 profile owner 或管理员能改。
- Page AI 发送请求时必须携带解析后的 `agentProfileRef`,不能只携带裸 `profile=lite`
### 2.2 MNote 内置技能开关
MNote 内置 skill / tool 属于 MNote 自己的能力面,普通用户应该可以按用户启停。该开关不写 Hermes profile,而是写 SQLite control-plane。
含义:
- `mnote_builtin_skill_enabled` 是 per-user 偏好和服务端执行策略,必须按当前 SQLite 用户隔离。
- `hide_builtin_skills=true` 只是 UI 展示偏好,用于折叠或隐藏 MNote 内置能力说明;它不等于禁用。
- MNote tool 是否可调用由服务端根据 `user_id + agentId + contextRefs + allowedRoots + mnote_builtin_skill_enabled` 决定。
- 普通用户可启停自己的 MNote 内置 skills;管理员可设置默认值或全局禁用策略。
- MNote 内置 skill 开关不修改 Hermes profile `config.yaml`,也不影响 shared Hermes profile。
### 2.3 Hermes profile skills
可以配置的只有 Hermes profile 自己的 skills。
规则:
- personal profile:用户可启停该 profile 下的 Hermes skills,写入该 personal profile 的 `config.yaml`
- shared profile:普通用户只读;管理员可启停 shared profile skills。
- skill toggle API 必须做服务端权限检查,不能只靠 UI 禁用按钮。
- MNote 不再把 `ai.agent.hermes.profile.<name>.skills.enabled` 当成普通用户对共享 profile 的安全开关;MNote 内置 skill 的 per-user 开关应使用独立 SQLite key。
### 2.4 Reasonix memory policy
Reasonix 默认关闭 memory 注入。
规则:
- 默认启动 Reasonix ACP 时设置 `REASONIX_MEMORY=off`
- 每个 MNote 用户可在设置中开启 Reasonix memory。
- 该设置保存到 SQLite `user_ui_preferences`,按用户隔离。
- 开启后,UI 需明确标注 Reasonix 将使用 global/project memory。
- 第一阶段不强制给 Reasonix 每用户独立 HOME;若后续需要更强隔离,再增加 `reasonix_home_mode=per_user`
## 3. 数据合同
### 3.1 Agent profile ref
Page AI payload 中新增或固定以下结构:
```json
{
"agentId": "hermes",
"agentProfileRef": {
"kind": "personal",
"profileId": "usr_123_default",
"ownerUserId": "usr_123",
"baseProfile": "default",
"displayName": "我的 Hermes"
}
}
```
shared profile
```json
{
"agentId": "hermes",
"agentProfileRef": {
"kind": "shared",
"profileId": "shared_lite",
"ownerUserId": null,
"baseProfile": "lite",
"displayName": "Lite"
}
}
```
服务端要求:
- 不接受浏览器直接提交任意 Hermes filesystem path。
- `profileId` 必须由 SQLite control-plane 解析到当前用户可访问的 profile。
- 对 personal profile,当前用户必须是 owner 或 admin。
- 对 shared profile,必须是公开 profile 或 admin。
- 所有 run / skill list / skill toggle / settings read 都通过 `agentProfileRef` 解析,不走裸 profile name。
### 3.2 SQLite control-plane
建议新增控制面表,避免只用偏好 key 表达权限关系:
```text
ai_agent_profiles
- id
- agent_id # hermes
- profile_kind # personal | shared
- owner_user_id # personal 必填,shared 为空
- base_profile_name # Hermes 原始 profile 名或模板名
- isolated_profile_name # MNote 管理后的真实 Hermes profile 名
- display_name
- status # active | disabled
- created_at
- updated_at
ai_agent_profile_grants
- profile_id
- user_id
- role # owner | user | admin
- can_run
- can_manage_skills
- can_manage_config
- created_at
- updated_at
```
第一阶段也可以在现有 `user_ui_preferences` 中保存默认选择:
- `ai.agent.hermes.default_profile_id`
- `ai.agent.hermes.hide_builtin_skills`
- `ai.agent.mnote_builtin.skills.enabled`
- `ai.agent.reasonix.memory_enabled`
但 profile 权限、owner、shared/personal 类型不应只存在于 UI preference。
MNote 内置 skill 的用户级开关可以第一阶段存在 `user_ui_preferences`,但服务端 tool policy 读取时必须视为执行策略,而不是纯 UI 状态。若后续需要审计、管理员默认值或组织策略,应升级为独立表:
```text
ai_user_skill_preferences
- user_id
- skill_id
- enabled
- updated_at
```
### 3.3 Hermes profile provisioning
创建 personal Hermes profile 时:
- 可从共享模板复制 `config.yaml``.env``SOUL.md` 和 skills。
- 不复制 memories、sessions、state database、gateway state。
- 生成的真实 profile name 必须包含 MNote 用户隔离标识,例如 `mnote-u-<userId>-default`
- provisioning 过程由服务端执行,并记录到 SQLite control-plane。
shared `lite`
- 初始由管理员登记为 `shared_lite`
- 普通用户只可 run / list readonly。
- 管理员可改 skill/config。
## 4. API 合同
### 4.1 Profile list
`GET /api/ai/agent-profiles?agentId=hermes`
返回当前用户可访问 profiles:
```json
{
"profiles": [
{
"profileId": "usr_123_default",
"kind": "personal",
"displayName": "我的 Hermes",
"canRun": true,
"canManageSkills": true
},
{
"profileId": "shared_lite",
"kind": "shared",
"displayName": "Lite",
"canRun": true,
"canManageSkills": false
}
]
}
```
### 4.2 Skill list
`GET /api/hermes/client/skills?profileId=...`
要求:
- personal profile 返回可 toggle 状态。
- shared profile 对普通用户返回 readonly 状态。
- MNote 内置 skills 返回值必须标记 `builtin=true``configurable=true``configScope=user_sqlite`
- Hermes profile skills 标记 `builtin=false``configurable=canManageSkills`
### 4.3 Skill toggle
`PUT /api/hermes/client/skills/toggle`
请求:
```json
{
"profileId": "usr_123_default",
"skillName": "writer",
"enabled": false
}
```
服务端必须:
- 解析 `profileId`
-`skillKind=mnote_builtin`,写当前用户 SQLite skill preference,不写 Hermes profile。
-`skillKind=hermes_profile`,校验 `canManageSkills=true`
- 拒绝普通用户修改 shared profile 的 Hermes profile skills。
- personal profile skill toggle 只写目标 Hermes profile 的 `config.yaml`
错误码建议:
- `ai_profile_not_found`
- `ai_profile_forbidden`
- `ai_profile_readonly`
- `ai_builtin_skill_preference_failed`
- `hermes_skill_toggle_failed`
### 4.4 Reasonix run
Reasonix ACP spawn / session create 需读取当前用户设置:
```json
{
"agentId": "reasonix",
"memoryPolicy": {
"enabled": false,
"source": "user_ui_preferences"
}
}
```
默认:
- `enabled=false`
- 子进程环境包含 `REASONIX_MEMORY=off`
开启:
- 不设置 `REASONIX_MEMORY=off`,或设置为 `on`
- UI 明确展示 memory 已开启
## 5. UI 设计
### 5.1 Agent selector
Hermes agent 下增加 profile 子选择:
```text
Hermes
我的 Hermes personal · 可配置
Lite shared · 只读
```
显示规则:
- personal profile 显示“可配置”。
- shared profile 显示“共享 / 只读”。
- 若普通用户选择 shared profile,技能开关显示为只读。
- 管理员选择 shared profile,技能开关可用,并显示“管理员正在修改共享 profile”。
### 5.2 Skills panel
三组仍保留:
- MNote 内置技能:可折叠,可隐藏/显示,也可由普通用户按自己账号启停。
- Hermes 技能:随当前 Hermes profile 变化;personal 可配置,shared 普通用户只读。
- Reasonix 技能:展示可用能力;memory 是单独设置,不混入 skill toggle。
Hermes profile 切换时:
- 必须重新加载 profile skills。
- 必须清空旧 profile skill cache。
- `hide_builtin_skills` 不随 Hermes profile 改变;它是用户 UI 偏好。
- MNote 内置 skill enable/disable 不随 Hermes profile 改变;它是当前 MNote 用户的 SQLite policy。
### 5.3 Settings
设置页拆分:
- Common:授权区域、contextRefs 默认值、内置技能显示/隐藏、内置技能启停。
- Hermes:默认 Hermes profile、personal profile 管理、shared profile 只读/管理员管理。
- Reasonix:默认关闭 memory;用户可开启。
- Chat-only:只聊天配置。
## 6. 非目标
- 不让普通用户直接修改 shared Hermes profile。
- 不把 shared Hermes profile 用作沉淀个人偏好的长期人格。
- 不在本阶段实现 Reasonix per-user HOME;只实现默认 memory off 与可选开启。
- 不新增第二套目录授权真相;文件访问仍由 SQLite directory grants / allowedRoots 控制。
- 不实现 PilotDeck 的完整 router、always-on 或 memory engine。
## 7. Checklist
### Batch A - 现状冻结与风险取证
- [ ] 复核当前 Page AI Hermes skill toggle 的真实写入路径,确认是否直接写 Hermes profile `config.yaml`
- [ ] 复核当前 UI preference 中 `hide_builtin`、MNote 内置 skill enabled、profile skill enabled、default profile 的存储键。
- [ ] 复核 Reasonix ACP spawn 环境,确认当前是否默认注入 memory。
- [ ] 形成 RED 证据:普通用户修改 shared profile skill 会影响其它用户,或当前缺少服务端权限边界。
- [ ] 验证:Rust/JS 只读审计记录在本文档或后续 checklist evidence 中。
### Batch B - SQLite profile policy 合同
- [ ] 新增或扩展 SQLite control-plane profile policy`ai_agent_profiles` / `ai_agent_profile_grants` 或等价结构。
- [ ] 初始化 shared Hermes profile:仅 `lite`,普通用户 `canRun=true``canManageSkills=false`
- [ ] 为每个用户 provision personal Hermes profile。
- [ ] 补 Rust 定点测试:personal owner、shared readonly、admin manage、跨用户不可管理。
- [ ] 验证:不同用户查询 profile list 只返回自己 personal + shared lite。
### Batch C - Hermes profile resolver
- [ ] 新增服务端 `agentProfileRef` resolver,禁止前端提交任意 Hermes path。
- [ ] `/api/hermes/client/runs``profileId` 解析真实 Hermes profile。
- [ ] `/api/hermes/client/skills``profileId` 解析真实 Hermes profile。
- [ ] 保留旧 `profile=` 参数只作为兼容入口,并映射到当前用户可访问 profile。
- [ ] 验证:旧路径兼容不允许越权访问 shared/admin profile。
### Batch D - Skill toggle 权限收口
- [ ] 修改 skill toggle API:只接受 `profileId + skillName + enabled`
- [ ] 区分 `mnote_builtin``hermes_profile` skill kind。
- [ ] MNote 内置 skill toggle 写当前用户 SQLite preference。
- [ ] 拒绝普通用户修改 shared profile 的 Hermes profile skills。
- [ ] personal profile skill toggle 只写该用户 isolated profile `config.yaml`
- [ ] shared profile skill toggle 仅 admin 可写。
- [ ] 验证:Rust API 测试覆盖 `ai_profile_readonly`、MNote 内置 skill per-user toggle、personal profile skill success。
### Batch E - MNote 内置 skill per-user policy
- [ ] 将 MNote 内置 skill enable/disable 保存为 SQLite per-user policy。
- [ ] UI 中 `hide_builtin_skills` 只影响展示,不影响 enable/disable。
- [ ] Skills panel 标记 `builtin=true``configurable=true``configScope=user_sqlite`
- [ ] 服务端 MNote tool policy 按 `user_id + agentId + contextRefs + allowedRoots + mnote_builtin_skill_enabled` 判断。
- [ ] 验证:用户 A 禁用某内置 skill 不影响用户 B;禁用后对应 MNote tool 被服务端拒绝;隐藏展示不影响 enable 状态。
### Batch F - Reasonix memory policy
- [ ] 新增 per-user 设置 `ai.agent.reasonix.memory_enabled`,默认 `false`
- [ ] Reasonix ACP spawn 默认设置 `REASONIX_MEMORY=off`
- [ ] 开启 memory 后不注入 `REASONIX_MEMORY=off`,并在 UI 显示 memory enabled。
- [ ] 补 JS/Rust 测试或 smoke,覆盖默认 off、用户开启、不同用户隔离 preference。
- [ ] 验证:普通消息由 Reasonix 自己决定是否使用工具;MNote 不再强行注入 memory/context 正文。
### Batch G - UI 收口
- [ ] Agent selector 中 Hermes profile 显示 personal/shared/readonly 状态。
- [ ] Skills panel 三组均可折叠。
- [ ] Hermes profile 切换必须刷新 skill catalog,避免旧 profile skill 残留。
- [ ] shared profile 的 Hermes profile skills 对普通用户展示只读开关或锁定状态。
- [ ] MNote 内置 skills 对普通用户展示可启停状态,并标明按当前 MNote 用户保存。
- [ ] 管理员对 shared profile 显示可管理状态,并提示影响所有用户。
- [ ] 验证:真实浏览器截图覆盖 MNote 内置 skill per-user 可配置、personal Hermes skill 可配置、shared Hermes skill 只读、admin shared 可配置。
### Batch H - 回归矩阵与文档收尾
- [ ] 更新 Page AI 设计说明,明确 MNote 内置 skill per-user policy、personal/shared Hermes profile 与 Reasonix memory policy。
- [ ] 更新 smokeagent 切换、MNote 内置 skill per-user toggle、Hermes profile 切换、shared readonly、personal skill toggle、Reasonix memory off/on。
- [ ] 运行 `node --check` 覆盖相关 browser runtime / smoke。
- [ ] 运行 Rust 定点测试覆盖 SQLite profile policy 与 Hermes skill toggle 权限。
- [ ] 运行真实浏览器验证并截图。
- [ ] 运行 `git diff --check`
- [ ] 涉及代码图后运行 `codegraph sync .`
- [ ] 完成后将本 checklist 移动到 `done/` 或标记为 `done`
## 8. 验收口径
完成后必须满足:
- 普通用户可以启停自己的 MNote 内置 skills,开关保存到 SQLite 并由服务端 tool policy 执行。
- 用户 A 的 MNote 内置 skill 开关不影响用户 B。
- 普通用户无法修改 shared `lite` 的 Hermes profile skills。
- 普通用户可以修改自己的 personal Hermes profile skills。
- MNote 内置 skill 的显示/隐藏与启停是两个不同状态:隐藏只影响 UI,启停影响服务端可调用性。
- Hermes profile 切换后 skill catalog 正确刷新。
- Reasonix 默认 memory off,开启 memory 是 per-user preference。
- Page AI run payload 不再用裸 Hermes profile name 表达身份,而是经服务端解析的 `agentProfileRef` / `profileId`
- 所有文件访问仍受 SQLite directory grants / allowedRoots 约束。