162 lines
7.3 KiB
Markdown
162 lines
7.3 KiB
Markdown
# MindSpace 解耦 P0 边界审计
|
|
|
|
日期: 2026-07-02
|
|
|
|
分支: `codex/mindspace-decouple-20260702`
|
|
|
|
目标:
|
|
|
|
- 找出当前代码中直接依赖 MindSpace 物理路径、公开 URL、agent sandbox policy、MindSpace DB 表的边界点。
|
|
- 为后续 P1 `MindSpaceService`、P2 conversation package、P3 独立进程拆分提供改造顺序。
|
|
- 本文只做审计和分层,不改变运行行为。
|
|
|
|
## 1. 审计结论
|
|
|
|
当前 MindSpace 已经具备资产、页面、发布、agent job 的基础模型,但边界仍混在 Portal 单体里:
|
|
|
|
- 物理 workspace 路径由 `user-publish.mjs`、`user-space.mjs`、`server.mjs`、`capabilities.mjs` 共同参与。
|
|
- 公开 URL 由 `user-publish.mjs`、`mindspace-assets.mjs`、`mindspace-publications.mjs`、`server.mjs` 等处生成或修正。
|
|
- Agent sandbox policy 仍以 `sandboxRoot` / `SANDBOX_ROOT` 为核心契约。
|
|
- DB 层已有 `h5_page_records.source_session_id`、`h5_page_records.source_message_id`、`h5_agent_jobs.session_id`,但 `h5_assets` 还缺 conversation provenance。
|
|
- 现有回归守卫重点保护 `public/*.html` materialize、Finish merge、用户消息内部前缀隐藏,这些路径后续改造时不能绕过。
|
|
|
|
架构判断:
|
|
|
|
- P1 先抽 `MindSpaceService` 和 storage adapter 边界,不拆进程。
|
|
- P2 先落 conversation package 元数据和 manifest,不立即替换所有文件路径。
|
|
- P3 以后再把 MindSpace Service 独立进程化。
|
|
- 103/105 只作为生产部署说明,不作为开发或架构依赖。
|
|
|
|
## 2. 物理路径边界
|
|
|
|
核心文件:
|
|
|
|
| 文件 | 当前职责 | 风险 | P1 处理方式 |
|
|
| --- | --- | --- | --- |
|
|
| `user-publish.mjs` | `PUBLISH_ROOT_DIR = MindSpace`、用户发布目录、公开 URL helper、skill 约束文本 | URL 和路径耦合在同一层 | 保留兼容 helper,但新代码走 `MindSpaceService` URL/storage 接口 |
|
|
| `user-space.mjs` | `MINDSPACE_STORAGE_ROOT`、workspace 分区、上传镜像、agent hints | workspace 和 asset storage 同时出现 | 把 storage root、workspace root 分离为 adapter 能力 |
|
|
| `server.mjs` | watcher、static route、shared upload、public serving | Portal 直接掌握所有路径 | P1 先集中成 MindSpace route/service facade |
|
|
| `mindspace-sandbox-mcp.mjs` | OS 层 `SANDBOX_ROOT` 文件操作 | Goose 以 raw fs path 作为契约 | P4 改成 `workspaceRef` / `packageId`,短期保留兼容 |
|
|
| `capabilities.mjs` | 注入 sandbox MCP env 和 args | policy 直接依赖 `sandboxRoot` | 新增 policy adapter,避免业务层自己拼 path |
|
|
|
|
主要路径形态:
|
|
|
|
```text
|
|
MindSpace/<userId>/public/*.html
|
|
MindSpace/<userId>/oa/*
|
|
data/mindspace/users/<userId>/*
|
|
SANDBOX_ROOT=<absolute workspace path>
|
|
MINDSPACE_STORAGE_ROOT=<absolute storage path>
|
|
```
|
|
|
|
## 3. URL 权威边界
|
|
|
|
核心文件:
|
|
|
|
| 文件 | 当前职责 | 风险 | P1 处理方式 |
|
|
| --- | --- | --- | --- |
|
|
| `user-publish.mjs` | `buildPublicUrl()`、`buildPublicZonePageUrl()` | 调用方可能把 helper 当长期权威 | 标记为 legacy compatibility,新增 canonical URL service |
|
|
| `mindspace-assets.mjs` | public temp image URL | asset public URL 与 storage key 绑定 | 改成 `MindSpaceService.createPublicAssetUrl()` |
|
|
| `mindspace-publications.mjs` | publication response、public URL、标准图片替换 | 发布 URL 权威分散 | publication canonical URL 统一收口 |
|
|
| `server.mjs` | `/MindSpace/*` route、redirect、shared upload public URL | Express route 同时做 URL 修复和文件服务 | route 仅调用 MindSpace service facade |
|
|
| `mindspace-chat-save.mjs` | 修正聊天中的 MindSpace 链接 | 后续仍需兼容旧 URL | 保留兼容,新增 canonicalize API |
|
|
|
|
短期规则:
|
|
|
|
- 不删除旧 URL helper。
|
|
- 新增代码不得直接拼 `/MindSpace/<userId>/...`。
|
|
- 所有新 public URL 生成必须经过 MindSpace service facade。
|
|
|
|
## 4. Agent Policy 边界
|
|
|
|
核心文件:
|
|
|
|
| 文件 | 当前职责 | 风险 | P1/P4 处理方式 |
|
|
| --- | --- | --- | --- |
|
|
| `capabilities.mjs` | 将 sandbox MCP 注入 Goose extension | policy 里暴露 raw path | 先增加 policy builder 边界,后续改 workspaceRef |
|
|
| `mindspace-agent-runner.mjs` | agent job claim 后创建 goosed 运行上下文 | job 与 workspace/path/publish layout 混合 | job runner 改为请求 MindSpace workspace capability |
|
|
| `user-space.mjs` | hints 中告诉 agent 目录和公开链接格式 | agent 直接学习物理路径 | hints 改为逻辑 workspace + 兼容说明 |
|
|
| `mindspace-sandbox-mcp.mjs` | read/write/edit/create_dir | 只能处理 filesystem backend | 后续新增 MindSpace MCP bridge |
|
|
|
|
长期 policy 目标:
|
|
|
|
```json
|
|
{
|
|
"mindspace": {
|
|
"workspaceRef": "mindspace://users/<userId>/conversations/<sessionId>",
|
|
"packageId": "cp_xxx",
|
|
"apiBaseUrl": "https://mindspace.example.com",
|
|
"accessToken": "scoped-short-lived-token"
|
|
}
|
|
}
|
|
```
|
|
|
|
## 5. DB 和 Provenance 边界
|
|
|
|
已有基础:
|
|
|
|
- `h5_assets`: asset 基础元数据,当前没有 `source_session_id`、`source_message_id`、`source_run_id`。
|
|
- `h5_asset_versions`: `storage_key` 是当前 asset 到物理存储的主要桥。
|
|
- `h5_page_records`: 已有 `source_session_id`、`source_message_id`、`source_asset_id`。
|
|
- `h5_page_versions`: 已有 `source_snapshot_json`,可承载页面生成时的上下文。
|
|
- `h5_publish_records`: 保存 `public_url`,是公开 URL 现有权威记录。
|
|
- `h5_publication_asset_refs`: publication 与 asset 引用关系。
|
|
- `h5_agent_jobs`: 已有 `session_id`、`result_page_id`、`result_asset_id`。
|
|
- `h5_agent_runs`: 已有 `agent_session_id`、`request_id`。
|
|
|
|
缺口:
|
|
|
|
- 没有 conversation package 表。
|
|
- 没有 package 与 asset/page/publication/job/message 的统一 artifact 关系表。
|
|
- Asset 层没有完整对话来源,导致“这次对话生成了哪些文件”只能间接推断。
|
|
|
|
建议新增:
|
|
|
|
- `h5_conversation_packages`
|
|
- `h5_conversation_artifacts`
|
|
|
|
过渡策略:
|
|
|
|
- 先通过 `h5_page_records.source_session_id` 和 `h5_agent_jobs.session_id` 回填页面/任务。
|
|
- 新增 asset 时尽量携带 `session_id`、`message_id`、`run_id` 到 artifact 表。
|
|
- 不急着修改旧 asset schema,优先用 junction table 降低迁移风险。
|
|
|
|
## 6. 回归守卫
|
|
|
|
后续改这些区域前必须跑:
|
|
|
|
```bash
|
|
npm run verify:mindspace-publish-guards
|
|
npm run verify:mindspace-publish-guards:full
|
|
```
|
|
|
|
重点保护:
|
|
|
|
- `mindspace-public-finish-sync.mjs`: `edit_file` 必须 materialize 到 `public/*.html`。
|
|
- `chat-finish-sync.mjs`: Finish / UpdateConversation 必须 merge,禁止盲覆盖。
|
|
- `conversation-display.mjs` 和 `src/utils/message.ts`: 用户消息不得展示 routing/skill 内部前缀。
|
|
- `src/hooks/useTKMindChat.ts`: `syncSessionMessages` merge + retry。
|
|
- `server.mjs`: session snapshot 缓存必须保守。
|
|
|
|
## 7. 第一阶段落点
|
|
|
|
P1 最小代码目标:
|
|
|
|
- 新增 `MindSpaceStorageAdapter` 契约和 local fs adapter 包装层。
|
|
- 新增 canonical public URL facade。
|
|
- 新增 conversation package manifest 纯函数。
|
|
- 先不替换现有 route,不改生产行为。
|
|
|
|
P2 最小产品目标:
|
|
|
|
- 为每个 chat session 懒创建 package。
|
|
- 新上传文件、生成文件、页面、发布记录写入 artifact 归属。
|
|
- H5 增加 package view。
|
|
- 历史数据 best-effort 回填。
|
|
|
|
本轮已经开始的安全基线:
|
|
|
|
- 当前工作必须在 `codex/mindspace-decouple-20260702`。
|
|
- `main` 本地 pre-commit 已阻止直接提交。
|
|
- 不允许把 103 作为本地开发依赖。
|