feat(agent): unify MCP tools and adopt Responses delivery
Move project and host queries into native MCP with host-owned OAuth/context, while retaining memory, skills, actions and publication plugins. Add nearby-model queries and explicit chat/task/learning model defaults. Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped session ownership, pending input edits and investigation communication. Disable local-provider discovery: Server /health allowed native vLLM to probe /v1/models on port 8000. A native before/after regression prevents recurrence. Validation: 170 focused Bun tests; service/plugin typechecks; native discovery, provider-discovery and input-inbox smoke checks; no paid model inference.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# 用户隔离与会话存储
|
||||
|
||||
Agent 支持个人会话和项目会话。会话始终属于登录用户,项目归属可选;同一项目中的其他用户不会因此获得聊天记录或计算文件的访问权。
|
||||
|
||||
## 身份与范围
|
||||
|
||||
| 范围 | 请求凭据 | Server 验证 | 工作空间 |
|
||||
| --- | --- | --- | --- |
|
||||
| 个人会话 | Bearer | `/api/v1/access-context` 验证用户 | 用户目录中的 `personal/` |
|
||||
| 项目会话 | Bearer、`X-Project-Id` | `/api/v1/agent-auth-context` 验证用户与项目权限 | 用户目录中的项目键目录 |
|
||||
|
||||
无项目头不等于匿名请求。生产环境始终验证 Bearer,验证失败不回退为本地用户。开发环境的无凭据请求使用同一个本地身份,不能用于多用户部署。
|
||||
|
||||
个人会话不取得项目业务凭据,调用依赖这些凭据的 TJWater API 会返回 `PROJECT_CONTEXT_REQUIRED`。任务执行和任务协作仍要求项目权限。个人记忆范围的键包含用户身份,避免所有个人会话共用一个记忆桶。
|
||||
|
||||
前端面板的“对话归属”支持个人会话和当前项目。切换会开启新对话,保留旧会话及其原归属,历史缓存按认证来源和范围区分。当前没有把已有会话搬入或移出项目的接口。
|
||||
|
||||
## 文件结构
|
||||
|
||||
根目录由 `CONVERSATION_WORKSPACE_ROOT` 管理,默认是 `data/session-workspaces/`:
|
||||
|
||||
```text
|
||||
actor-<用户键>/
|
||||
├── personal/
|
||||
│ └── session-<会话 ID 的 SHA-256>/
|
||||
└── project-<项目键>/
|
||||
└── session-<会话 ID 的 SHA-256>/
|
||||
├── chat/
|
||||
│ ├── owner.json
|
||||
│ ├── messages.json
|
||||
│ ├── transcript.json
|
||||
│ └── learning.json
|
||||
└── workspace/
|
||||
├── files/
|
||||
├── tmp/
|
||||
└── .cache/
|
||||
```
|
||||
|
||||
个人和项目会话具有相同的内部结构。用户和项目键来自经过验证的身份,不从工具参数或浏览器路径直接取值。会话 ID 使用完整 SHA-256 形成目录名,原 ID 保存在 `chat/owner.json` 中。
|
||||
|
||||
`messages.json` 保留前端聊天状态,包括消息、工具展示和执行计划;`transcript.json` 是经过持久化策略处理的问答摘要,供历史搜索和学习使用,最多保留配置的轮数。摘要不是完整原生执行轨迹。
|
||||
|
||||
`learning.json` 保存该会话的学习检查、复盘进度。它与聊天记录共用归属校验和串行写入保护,位于宿主管理的 `chat/`,不向模型开放。
|
||||
|
||||
`data/session-metadata/` 是归属、标题、父会话、用途和任务身份的索引。它为会话 ID 冲突检查、列表及所有会话接口提供同一归属来源。新会话创建也会初始化对应的聊天目录。
|
||||
|
||||
历史索引在服务进程内按用户和范围缓存,创建、更新和删除同步维护缓存。同一数据根目录由一个 Agent 进程写入;修改底层索引文件后需要重启,当前不支持多个 Agent 进程并发写入同一目录。
|
||||
|
||||
## 隔离边界
|
||||
|
||||
| 对象 | 当前边界 |
|
||||
| --- | --- |
|
||||
| 会话接口 | 先校验用户、个人或项目范围,再读取状态;无权访问统一返回未找到 |
|
||||
| 聊天目录 | 校验归属文件和规范路径;拒绝目录、记录文件中的符号链接 |
|
||||
| 聊天搜索 | 只扫描当前用户与范围的摘要目录 |
|
||||
| 原生文件工具 | 只允许当前原生会话的 `workspace/`;会话哈希不匹配时拒绝读取 |
|
||||
| Shell | 在 `workspace/files/` 运行,Landlock/seccomp 限制文件、网络和进程访问 |
|
||||
| 记忆 | 用户记忆按用户隔离;项目记忆在项目内共享;个人工作空间记忆按用户隔离 |
|
||||
| Skill | 按部署实例共享,不能当作用户私人文件夹 |
|
||||
| MCP | 连接配置与上游账号按实例管理,工具权限按会话应用;上游数据授权由 MCP 服务负责 |
|
||||
| OpenCode 数据库 | 仍是实例共享的原生数据库,访问依赖服务密码和可信 Agent 路由 |
|
||||
|
||||
`chat/` 是计算目录的同级目录,目录权限为 `0700`,记录文件为 `0600`。模型和 Shell 不能直接读写聊天记录;需要查询历史时使用受范围约束的 `session_search`。
|
||||
|
||||
这是产品用户隔离和会话沙箱,不是为每个用户启动一个 Linux 账号、容器或独立 OpenCode 实例。服务宿主账号和管理员仍可管理全部持久化数据。原生数据库默认位置为 `~/.local/share/opencode/opencode.db`,工作空间不会取代它,部署仍需保留该数据卷。见 [OpenCode 存储说明](https://opencode.ai/v2/docs/troubleshooting/#service-files)。
|
||||
|
||||
## 旧记录与保留
|
||||
|
||||
启动时依据会话索引迁移旧 UI 状态和学习进度,并依据摘要的用户、范围与会话身份迁移旧摘要。旧文件经逐字节备份核对后归入 `PERSISTENCE_BACKUP_DIR/legacy-chat/actor-*/<范围>/session-<sha256>/chat/`,文件名包含内容哈希;源文件只在备份核对成功后移除。重复迁移不会覆盖已存在的新记录。`legacy-chat/migration.json` 保存版本、来源目录时间戳与迁移结果,来源未变化时跳过重复导入。缺少归属索引的 UI 状态和学习进度保留在旧目录,不猜测归属。历史匿名记录保留原本的本地用户范围,不自动划归某个登录用户。
|
||||
|
||||
旧计算目录在该会话继续执行前迁入对应工作空间,使用原生 Session API 更新目录绑定。文件保留,目标冲突停止迁移,绑定更新失败可重试。聊天目录的迁移不需要模型推理。
|
||||
|
||||
迁移核对完成后,已迁入工作空间的旧记录备份可以清理,保留 `legacy-chat/migration.json` 作为完成记录。当前本机的 139 份迁移备份及三个空迁移目录已清理,现行聊天和学习记录仍在对应的 `chat/` 中。使用工作空间存储时,初始化不再创建旧迁移目录。
|
||||
|
||||
归档会话保留记录。删除会话先写入索引删除状态和 `chat/deleted.json`,会话立即从列表、读取和搜索中隐藏;随后停止聊天与学习任务,移除该原生会话、消息、摘要、学习进度、计算目录及其旧记录和迁移备份。保留最小归属和删除标记,拒绝迟到写入、会话 ID 再使用以及旧数据重新导入。清理未完成时返回 `503 SESSION_CLEANUP_PENDING`,重复同一用户、范围的删除请求可继续清理。
|
||||
|
||||
审计日志、已经形成的用户或项目记忆、共享 Skills、Server 业务报告及部署级离线备份有各自的保留职责,不随会话删除。工作空间仍没有自动过期清理,不依据目录为空就删除历史目录。
|
||||
|
||||
标题生成、学习检查和复盘在成功、失败及取消后释放自己的原生辅助会话和计算目录;清理失败会记录日志。它们不作为用户聊天长期保留。
|
||||
|
||||
持久化默认 `data/` 可同时保存 Agent 索引、工作空间和记忆;Skill 目录与 OpenCode 数据库仍需各自的数据卷。自定义工作空间根目录也需要单独挂载。
|
||||
Reference in New Issue
Block a user