15 KiB
15 KiB
| id | name | status | progress |
|---|---|---|---|
| agent-access-reliability | Agent 访问可靠性 | in_progress | 96 |
设计决策:Agent Access Reliability v1
背景
炼境已经具备项目级 .codex/config.toml、canonical .lian-jing/context.json、Codex Skill、30 余个 MCP tools、工具链契约及 PR/CI 安全门禁,但“配置存在”仍不等于“当前 Agent 可用”。2026-09-23 实测项目配置正确,127.0.0.1:27190 却拒绝连接,当前 Codex 会话也未发现 lian-jing 工具;现有 verify_onboarding 只检查配置文本,无法区分服务未运行、握手失败、工具缺失或客户端需要重载。
同时,MCP 当前监听 0.0.0.0,健康检查仍使用 localhost,所有项目共享无连接作用域的工具目录并由调用方自由传 project_id。这对单人本机使用足够方便,但扩大了局域网暴露、跨项目误操作和 prompt injection 的影响面。
方案选择
| 方案 | 优点 | 代价 / 风险 | 结论 |
|---|---|---|---|
| 保持现状,仅在 Skill 提醒先启动炼境 | 零实现成本 | 配置假绿、MCP 不可用时诊断工具也不可用,人工启动继续成为瓶颈 | 否决 |
| 立即拆独立 Windows Service,并由插件内置完整 MCP | 可用性最高 | 安装、升级、日志、双实现漂移和权限模型成本过高 | 后置 |
| loopback 单一 MCP + 分层自检 + 可选 Windows 自启动 + 逻辑项目作用域 | 保留现有本体,优先解决真实高频故障;不复制凭证和业务逻辑 | 不能抵御同一 OS 用户主动伪造其他项目 URL,属于单用户威胁模型下的误操作隔离 | v1 选定 |
| 为每个项目写入持久 secret/token | 能形成更强隔离 | secret 易进入受版本控制的 .codex/config.toml,还需安全存储与轮换 |
v1 否决,威胁模型变化后再评估 |
系统边界变更
Codex / Claude 项目
├─ Skill:工作流与降级规则
├─ .lian-jing/context.json:唯一 project_id
└─ .codex/config.toml:指向 127.0.0.1 的项目作用域 MCP URL
│
▼
炼境单一 Tauri 进程
├─ loopback MCP listener
├─ runtime readiness:configured / reachable / handshake / tools_ready / reload_required
├─ scoped route:连接绑定 project_id,工具参数不得越界
├─ read-only 与 write/destructive 元数据
└─ 现有 SQLite / Gitea / 工具链 / 交付能力本体
- MCP、健康检查和 SSE callback 全部使用
127.0.0.1;v1 不对局域网开放 MCP。 - Gitea webhook 若未来确需入站访问,必须与 MCP listener 分离并单独认证;不得以 webhook 为由继续让 MCP 监听
0.0.0.0。 verify_onboarding保留静态接入检查,并新增运行态 readiness;不能把配置存在描述成当前客户端已加载。- 增加项目作用域路由,例如
/mcp/projects/:project_id。服务端从连接取得 scope;若 tool 参数携带不同project_id必须拒绝。旧/mcp仅作兼容迁移,不扩展新的项目写能力。 - v1 的项目作用域是单用户本机的误操作隔离,不宣称是多租户认证。不得把 token 写入受版本控制的项目文件。
- MCP
initialize返回精简 server instructions;核心工具增加 read-only / destructive / idempotent 元数据,并为状态类工具同时返回文本摘要与结构化结果。 - 炼境继续使用现有托盘常驻。Windows 登录自启动仅服务正式安装版:默认关闭、debug/target 构建拒绝启用,复用 single-instance;不拆独立服务。updater 保持独立模块。
- 插件 v1 继续保持 Skill-only,不复制 MCP 能力、项目身份或凭证;只同步 readiness 和降级指引。
受影响文件
- 修改:
src-tauri/src/mcp_server.rs— loopback 监听、项目作用域路由、initialize instructions 与握手测试。 - 修改:
src-tauri/src/commands/settings.rs— 统一127.0.0.1、运行态 readiness 与正式版自启动状态。 - 修改:
src-tauri/src/commands/onboarding.rs— 分层 onboarding/runtime 自检和作用域配置校验。 - 修改:
src-tauri/src/mcp_inject.rs— 项目作用域 URL 的兼容迁移与保留用户配置。 - 修改:
src-tauri/src/mcp_tools.rs— scope 校验、核心工具 metadata 与 structured result。 - 修改:
src-tauri/src/lib.rs、src/lib/commands.ts— readiness 与正式版自启动的 Tauri 投影。 - 修改:
src/components/settings/SettingsPage.tsx— Agent 连接状态与自启动 UI。 - 修改:
src-tauri/Cargo.toml、src-tauri/Cargo.lock— 官方 autostart plugin,仅 Rust 侧使用,不增加前端依赖或 pnpm lock 变更。 - 修改:
src-tauri/resources/onboarding/、plugins/lian-jing-codex/skills/lian-jing/SKILL.md— readiness/重载/降级说明。 - 修改:
docs/ai-context/project-context.yaml、docs/ai-context/agent-button-system.md— 外部契约与稳定边界。
验证路径
- 载体:Rust 纯函数与 HTTP MCP 握手测试;Tauri command 做运行态聚合;UI 仅做人眼终验。
- 示例:
[cargo test] mcp_bind_addr(27190) → 127.0.0.1:27190,绝不返回 0.0.0.0 或 ::。 - 示例:
[HTTP MCP] POST /mcp/projects/p1 initialize → server instructions 存在;tools/list 只返回允许集合;tools/call 携带 project_id=p2 → 明确拒绝且零副作用。 - 示例:
[Tauri command] verify_onboarding(p1) 在配置存在但端口未监听时 → configured=ready、reachable=unavailable,不得整体假报 ready。 - 示例:
[Tauri command] get_agent_runtime_status() → 返回 configured、listener、handshake、core_tools 与 reload_required 的可行动状态。 - human_verify:关闭窗口后托盘进程仍提供 MCP;打开新的 Codex 项目任务可发现核心只读工具。
风险点
- 自检调用自身形成死锁:运行态探针必须是 Tauri command/本地 HTTP 的独立路径,不能只通过 MCP
get_diagnostics暴露。 - 自检拖慢 UI 或任务启动:健康探针使用短超时并区分 unreachable/timeout/protocol_error;不得阻塞主线程。
- 旧客户端兼容:现有
/mcp和旧项目配置采用版本化迁移;迁移期只给出明确 legacy 状态,不静默覆盖手工同名 server。 - 作用域被误当成强认证:文档明确 v1 只防误路由;服务仍依赖 loopback 与本机用户边界。
- 工具 metadata 漂移:metadata 与 dispatch 使用同一工具定义来源或配套完整性测试,避免只改 schema 未改执行边界。
- 范围膨胀:本模块不做 production 部署、SSH、migration、公开插件或 staging;production 双证据继续由
project-capability-control-planeP3-B~E 承担。
任务卡
✅ A1. MCP loopback 与 Windows 地址一致性
- status: done
- complexity: S
- files: src-tauri/src/mcp_server.rs, src-tauri/src/commands/settings.rs
- acceptance: "[cargo test] MCP bind/health URL 构造 → listener=127.0.0.1:、health=http://127.0.0.1:/health、SSE callback 同源;源码中 MCP 健康路径不再出现 localhost 或 0.0.0.0。"
- done_note: "MCP listener、health 与 SSE callback 已统一到 127.0.0.1;定向测试 2/2 与 cargo test --lib 164/164 通过,git diff --check 通过。真实 HTTP/SSE 连接留待 A9 运行态验收。"
✅ A2. 分层 Agent runtime readiness 本体
- status: done
- complexity: M
- depends: A1. MCP loopback 与 Windows 地址一致性
- files: src-tauri/src/mcp_server.rs, src-tauri/src/commands/settings.rs, src-tauri/src/commands/onboarding.rs, src-tauri/src/lib.rs, src/lib/commands.ts
- acceptance: "[Tauri command] get_agent_runtime_status(project_id) 对 configured/reachable/handshake/tools_ready/reload_required 五层 fixture → 分别返回 ready|missing|unavailable|protocol_error 与可行动 detail;端口拒绝时不得把静态配置误报为整体 ready。"
- done_note: "新增独立 Tauri readiness command、五层状态与 TS 契约;端口拒绝、协议错误、核心工具缺失、需重载和全就绪 fixture 均通过。cargo test --lib 167/167、mise pnpm 9.15.0 typecheck 通过。MCP 不可用,M 级复盘待恢复后写入 append_project_note。"
✅ A3. Onboarding 自检接入运行态证据
- status: done
- complexity: M
- depends: A2. 分层 Agent runtime readiness 本体
- files: src-tauri/src/commands/onboarding.rs, src-tauri/src/mcp_tools.rs, docs/ai-context/project-context.yaml
- acceptance: "[cargo test] verify_onboarding(project_id) → 保留现有静态检查并附加 runtime readiness;配置正确但 MCP 不可达时 summary.pass=false 且检查项明确为 unavailable;握手成功且核心工具齐全时为 ready。"
- done_note: "verify_onboarding 报告已同时包含静态 checks 与五层 runtime;配置正确但 connection refused fixture 明确失败,当前 MCP 调用以内层连接事实避免 HTTP 自调用。cargo test --lib 168/168 通过;MCP 不可用,M 级复盘待恢复后写入。"
✅ A4. MCP instructions、工具风险元数据与核心结构化结果
- status: done
- complexity: M
- depends: A1. MCP loopback 与 Windows 地址一致性
- files: src-tauri/src/mcp_server.rs, src-tauri/src/mcp_tools.rs
- acceptance: "[HTTP MCP] initialize → instructions 前 512 字包含先读状态、项目边界和写操作确认;tools/list → 核心工具具有 readOnly/destructive/idempotent 元数据;get_delivery_status/get_toolchain_status/verify_onboarding → 同时返回结构化数据与可读摘要。"
- done_note: "initialize instructions 已声明先读状态、项目边界与写操作确认;全工具 annotations 与核心 structuredContent/摘要测试通过。cargo test --lib 170/170 通过;MCP 复盘待服务恢复后写入。"
✅ A5. 项目作用域 MCP 路由与越界拒绝
- status: done
- complexity: M
- depends: A4. MCP instructions、工具风险元数据与核心结构化结果
- files: src-tauri/src/mcp_server.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/mcp_inject.rs
- acceptance: "[HTTP MCP] /mcp/projects/p1 的 tools/call(get_delivery_status) → 服务端固定使用 p1;显式传 p2 或调用全局管理写工具 → 返回 scope_violation 且零副作用;旧 /mcp 仍可握手并报告 legacy/upgrade_hint。"
- done_note: "新增 scoped HTTP/SSE 路由、连接作用域工具目录、project_id 自动绑定与越界前置拒绝;旧 /mcp 初始化返回 legacy/upgrade_hint,新注入 URL 指向项目 scope。cargo test --lib 174/174 通过;真实 HTTP 留待 A9。"
✅ A6. Windows 登录自启动后端能力(正式安装版限定)
- status: done
- complexity: M
- depends: A2. 分层 Agent runtime readiness 本体
- files: src-tauri/Cargo.toml, src-tauri/Cargo.lock, src-tauri/src/lib.rs, src-tauri/src/commands/settings.rs
- acceptance: "[Tauri command] set_agent_autostart(enabled) → 默认 false,可启用、查询并撤销;重复调用幂等;不启动第二个炼境实例,不修改项目文件或 pnpm-lock.yaml。"
- decision_note: "用户于 2026-09-23 明确恢复 A6,但限定正式安装版:默认关闭,debug/target 构建拒绝启用,查询/启停幂等,复用 single-instance;开发前先退出正式版,不并行运行两个实例;updater 单独建设。"
- done_note: "接入官方 tauri-plugin-autostart,保留既有 single-instance;query/set command 通过纯状态机 fixture 证明重复调用幂等,debug 与 target/release 路径拒绝启用,未改 pnpm-lock。路径 fixture、cargo test --lib 179/179 与 cargo check --release 通过;
mise exec -- pnpm build:local已生成 NSIS/MSI,NSIS SHA-256=82F05ECB0A68E5B87FCD5D50906A64A4CD07B6E9169A07CB2F20D6130FE36C12。正式安装后的注册表效果留待安装包 human_verify。"
✅ A7. Agent 连接状态与自启动 UI 投影
- status: done
- complexity: M
- depends: A2. 分层 Agent runtime readiness 本体, A6. Windows 登录自启动后端能力
- files: src/lib/commands.ts, src/components/settings/SettingsPage.tsx, src/components/settings/SettingsPage.test.ts
- acceptance: "agent_verify: [pnpm typecheck] Agent runtime/autostart 类型与调用无错误;[Tauri command fixture] unavailable、reload_required、ready 分别渲染准确状态和唯一建议动作。human_verify: 设置页能区分未配置、炼境未运行、需重启 Codex、已就绪,并仅在正式安装版允许启停登录自启动。"
- done_note: "设置页新增项目级 Agent readiness 与正式版自启动投影;五类状态映射各自产生唯一行动。Vitest 5/5、mise pnpm typecheck 与 production frontend build 通过;视觉与正式安装版开关留待 human_verify。"
✅ A8. 项目作用域配置迁移与 Skill 降级指引
- status: done
- complexity: M
- depends: A3. Onboarding 自检接入运行态证据, A5. 项目作用域 MCP 路由与越界拒绝
- files: src-tauri/resources/onboarding/.agents/skills/lian-jing/SKILL.md, plugins/lian-jing-codex/skills/lian-jing/SKILL.md, src-tauri/src/commands/onboarding.rs, src-tauri/src/mcp_inject.rs
- acceptance: "[cargo test] 旧 unscoped Codex 配置重跑 onboarding → 报 legacy/upgrade_hint 且不覆盖手工同名 server;新配置指向当前 project_id 的 scoped URL;[Skill] MCP 缺失时按 configured→reachable→reload 顺序降级,不猜测状态或要求复制凭证。"
- done_note: "verify_onboarding 已区分 scoped ready、旧 /mcp legacy 与错误项目;炼境托管块可幂等迁移到 scoped URL,手工同名 server 仍拒绝覆盖。接入包与私有插件 Skill 均采用 configured→reachable→reload;相关回归及 Rust 全量 176/176 通过。"
✅ A9. 炼境与 Enterprise 消费者端到端验收
- status: done
- complexity: M
- depends: A7. Agent 连接状态与自启动 UI 投影, A8. 项目作用域配置迁移与 Skill 降级指引
- files: .blueprint/modules/agent-access-reliability.md, .blueprint/manifest.yaml, docs/ai-context/agent-button-system.md
- acceptance: "agent_verify: [HTTP MCP] 炼境与 Enterprise scoped endpoint 分别握手 → 只读取本项目状态,跨项目调用被拒绝;关闭窗口保留托盘后仍可调用,完全退出后 readiness=unavailable;新 Codex 任务重载后核心工具可见。human_verify: 用户在两个项目各打开一次新任务,均无需手工提供 project_id 或凭证即可读取自身状态。"
- done_note: "真实开发实例仅监听 127.0.0.1;炼境与 Enterprise scoped endpoint 均完成 initialize、tools/list、get_delivery_status,跨项目读取和 scoped 全局写工具返回 scope_violation;完全退出后 27190 unavailable。用户关闭窗口但保留托盘后,进程与 health 仍存活、26 个 scoped tools 可见且 get_delivery_status 可调用。新建的炼境任务 01a0ca89-8162-7010-8447-7468fce0a8a3 与 Enterprise 任务 01a0ca8a-1ef0-7d41-8eb7-afbf23185805 均产生原生 lian-jing/get_delivery_status MCP tool marker,分别绑定 dev-manager-tauri 与 d248554e-582d-4131-9e16-43ece91cd203,未使用 shell/手写 HTTP/本地文件兜底。"
执行顺序
A1 → A2 → A3;A1 → A4 → A5;稳定正式安装包链路 → A6 → A7;A3 + A5 → A8;A7 + A8 → A9。updater 不纳入 A6。
v1 完成后观测 2~4 周:若“服务未运行/客户端未加载”仍占 Agent 阻塞的主要比例,再新建立项评估插件 lifecycle hook、stdio bootstrap 或独立 daemon;不得在本模块内提前扩张。