Repository navigation
Feat/agent access - #310
Conversation
- 新模块 agent-access:持久连接凭证(agent_connections,token 仅存 sha256)、 幂等提交存储(agent_submissions,(connectionId, clientSubmissionId) 唯一, 重放返回 duplicate)、调试事件(agent_access_events,每连接留 200 条) - 对外 MCP 端点 POST /v1/mcp/agents:Bearer 连接 token 自鉴权(auth 豁免全局 bearer)、会话重启自愈、工具按连接能力过滤;复用 channel-mcp 的 ExchangeTransport(加 export 共享,不复制) - 九工具面:list_rooms/search/fetch/read_file/get_rules/get_status/ register_file/submit/submit_status;状态语义 ok/empty/no_permission/ source_lost/parse_failed/stale/duplicate/pending_review;搜索命中带 conversation_id+message_id 与 everroom:// URI(与 fetch 解析方案一致) - 产物入库走 files 管线(sourceKind=agent-generated,CAS 去重); 候选 pending_review 暂存,待第 6 章三路治理接入 - 连接页 REST /v1/agent-access/*:连接 CRUD/自检/示例查询/安装代写生成 (agentskills 六字段 SKILL.md + 各工具配置片段) - preSerialization 脱敏对连接签发/轮换/安装三端点豁免(对齐 runtime-config/secrets 先例;否则 token 被改写为 [REDACTED]) - 迁移 0081 手写(drizzle-kit generate 因 0048 快照损坏不可用,仓库惯例) - 测试 13 用例:单元/交换层 7 + 真引导服务器 HTTP 层 6;typecheck 过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- GatewaySupervisor 新增 portProvider:固定端口设置(dataDir/ agent-access-settings.json,损坏回退默认)使外部工具的 MCP 配置在 网关重启后持续有效;页面提供应用与一键重启 - AgentAccessGatewayBridge(Diary 桥模式)+ 14 个 agent-access:* IPC 通道 + preload agentAccess API + shared/agent-access.ts 共享 DTO; 处理器提前到 gateway start 前注册(否则冷启动 IPC 排 servicesReady 队列超时,连接列表须手动触发才出现) - AgentConnectionsPage(导航「执行」组):连接列表/新建(Room 多选+能力 勾选+回写位置)/暂停恢复/换 token(一次性展示+复制走主进程 system-clipboard)/自检(发现/搜索/精读/回写)/示例查询/调试面板 (提交+调用记录);加载 15s 超时兜底防无限转圈 - AgentAccessInstaller 安装落盘:Claude Code/Cursor 自动合并 mcpServers.everroom(先备份、损坏 JSON 不写、只动自己的键)、Codex 技能落盘+TOML 手动片段(token 已替换)、无机制宿主仅片段;安装时 无 token 自动换发;卸载干净移除 - UI 复用项目资产:PageHeader/secondary-button/全局表单配方(对齐 AgentSchedulesPage 与 source-connect-form),危险色统一 #b5493a - 测试 11 用例(安装器 6/设置存储 3/导航 2);typecheck 与 electron-vite build 过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- docs/notes/everroom-agent-access-research.md:四路调研合成(网关现状/ 领域模型/MCP+Skills 规范生态/竞品),PRD 第 7 章逐条映射 - docs/design/agent-access-mcp-skills.zh-CN.md:大任务门禁文档——目标/ 架构红线自查/接口设计(表/工具面/状态语义/安装代写)/测试计划/ 分阶段与真机验证清单 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
审查总结
本 PR 新增外部 Agent 访问能力(持久连接 token + /v1/mcp/agents MCP 端点 + 桌面连接页/安装代写)。整体设计扎实:token 只存 sha256、明文仅创建/轮换返回一次、REST 走全局 bearer、MCP 端点以连接 token 自鉴权、读路径按 roomIds 过滤且有较完整的测试覆盖(鉴权豁免、暂停即时失效、幂等重放都测到了)。但写路径存在一处授权范围绕过,建议修复后再合入。
阻塞问题
1. everroom_submit 的 result_text 条目可绕过 Room 范围校验,向未授权 Room 写入(授权绕过)
apps/gateway/src/modules/agent-access/service.ts:359-362 中,processItem 的范围校验只对 artifact_file 生效:
const roomId = item.roomId ?? connection.scopes.writebackRoomId ?? null;
if (item.kind === "artifact_file" && roomId && !this.roomAllowed(connection, roomId)) {
return { ...base, status: "rejected", error: "room_out_of_scope" };
}而 result_text 的 schema 明确允许调用方携带任意 roomId(apps/gateway/src/modules/agent-access/tools-write.ts:46-53、types.ts:103)。复现路径:持有一个仅授权 roomIds: ["room-a"]、writebackRoomId: null 的连接 token 的外部 Agent,提交 { kind: "result_text", title: "x", content: "# x", roomId: "room-b" } —— 文件会以 roomId=room-b、pipelines.room: true 入库(见 create-server.ts 中 agentAccessImportArtifact),并返回 everroom://open/file/... 深链,即写入了连接未被授权的 Room。
同文件 artifact_file 分支与 everroom_register_file(tools-write.ts:130-135)都做了同样的范围校验,可确认这是遗漏而非设计意图。影响:对外暴露的 MCP 写端点的授权被扩大(例如被提示注入的外部 Agent 可向用户未授权的 Room 写内容)。建议去掉 item.kind === "artifact_file" && 条件对两类条目统一校验,并补一条「result_text 携带越界 roomId 应 rejected」的测试(现有 apps/gateway/tests/agent-access.test.ts:351-394 未覆盖该场景)。
建议修复(非阻塞)
2. writebackRoomId 未校验在授权 Room 范围内
apps/gateway/src/modules/agent-access/routes.ts:38-50(parseScopes)创建/更新时不校验writebackRoomId ∈ roomIds;- 桌面表单
apps/desktop/src/renderer/src/components/pages/AgentConnectionCreateForm.tsx:152-158在「仅选定 Room」模式下,回写下拉仍列出全部 Room。
修复问题 1 后,越界的 writebackRoomId 会导致所有回写条目被拒(与 UI 预期不一致);建议网关侧校验,表单只列选定 Room。
3. 已吊销/暂停的连接仍可换取安装代写内容
apps/gateway/src/modules/agent-access/service.ts:165-169 的 verifyTokenMatch 不检查 status,routes.ts:270-287 对 revoked 连接仍会返回含明文 token 的安装计划。该端点虽有全局 bearer 保护,影响有限,但建议非 active 连接直接 403/404,与 MCP 端点行为一致。
4. 含 token 的配置文件权限与备份残留
apps/desktop/src/main/agent-access/installer.ts:92 新建 ~/.claude.json、~/.cursor/mcp.json(内含 Bearer token)使用默认 0644 权限,多用户机器上其他本地用户可读,建议新建文件用 0600。另外 ${path}.everroom-backup-${Date.now()} 备份会无限累积(重新安装后备份里也含 token),可考虑只保留最近一份。
小问题 / 备注
apps/gateway/src/modules/agent-access/tools-read.ts:527:非"all"范围的记忆检索只取前 10 个 Room(roomIds.slice(0, 10)),超出部分静默漏检;建议在工具描述中注明或改为分页/上限提示。apps/gateway/src/modules/agent-access/routes.ts:218-223:每次自检都以selftest-${Date.now()}新建一条 submission(citation)永久落库,调试数据会随自检持续膨胀;可考虑复用固定 submissionId(利用幂等重放)。apps/gateway/src/modules/agent-access/mcp-routes.ts:5-7:body schema 只接受单个 JSON-RPC 对象,批量数组会得到普通 400(非 JSON-RPC 错误形状);发送 batch 的旧版 MCP 客户端会不兼容。apps/gateway/src/modules/agent-access/mcp-routes.ts:49-56:DELETE /v1/mcp/agents对无效 token 也返回 204,与 POST 的 401 不一致(且closeSession内的verifyBearer会顺带刷新lastSeenAt)。apps/gateway/src/modules/agent-access/install.ts:27-29:连接名未转义直接写入 SKILL.md 的 YAML frontmatter(name 仅限长度、未限字符),含换行/---的名称会破坏 frontmatter;建议过滤控制字符。apps/gateway/drizzle/meta/_journal.json丢失文件末尾换行(不影响解析),建议恢复以减少 diff 噪音。
值得肯定
- 凭证管理规范:sha256 落库 + 唯一索引、前缀展示、明文一次性返回;
- 读路径每个 fetch 目标都做
roomInScope校验,搜索 roomIds 下推到 SQL 条件; - 测试真引导整服务器覆盖鉴权豁免与迁移(0081),桌面 installer 对损坏 JSON 的保守处理也有测试。
此 PR 修改了高风险文件,需要人工批准,不会自动合并。
apps/gateway/drizzle/0081_agent_access.sqlapps/gateway/drizzle/meta/_journal.json
No description provided.