135 lines
13 KiB
Markdown
135 lines
13 KiB
Markdown
---
|
||
id: agent-client-parity
|
||
name: 三端客户端对等
|
||
status: in_progress
|
||
progress: 0
|
||
---
|
||
|
||
## 概述
|
||
|
||
子项目在 Claude Code、Codex、DSH(DeepSeek Harness 桌面版)三端都能继续开发:项目状态只存在于 Git 与炼境,客户端是可替换的投影。炼境适配的是**客户端层**(规则文件、skill 位置、MCP 连接、认证、无头模式),不是模型层——在某客户端内切换 DeepSeek 等模型属 `codex-model-switch`,与本模块正交。
|
||
|
||
## 定位:agent 客户端兼容层(类比 Web 框架之于浏览器)
|
||
|
||
炼境对 agent 客户端的角色,如同 Web 框架对浏览器:内容按开放标准写一次(AGENTS.md、MCP、SKILL.md),各客户端私有机制的差异由炼境的适配层吸收,并持续实测"这些机制此刻是否仍有效"。
|
||
|
||
| Web 世界 | 炼境 |
|
||
| --- | --- |
|
||
| 浏览器 | agent 客户端(Claude Code、Codex、DSH) |
|
||
| Web 标准 | AGENTS.md、MCP、SKILL.md 格式 |
|
||
| 浏览器私有实现差异 | skill 目录、MCP 配置位置、helper 执行 shell、DSH `!!js` 等 |
|
||
| 框架兼容层 / polyfill | 炼境各客户端适配器(skill 双投影、统一 shim、各端注入) |
|
||
| Can I Use 兼容性表 | 三端对等矩阵(A–F) |
|
||
| 跨浏览器自动化测试 | 三端无头探针(P4) |
|
||
|
||
skill 只是五个接入面之一,兼容层覆盖全部五面。
|
||
|
||
类比不成立处(也是本模块的特殊难点):agent 客户端迭代远快于浏览器且机制常无文档,所以更依赖持续实测而非读规范;"浏览器"本身是智能的,部分兼容可用 skill 里的降级说明解决,但可靠性低于代码适配,只作兜底。
|
||
|
||
## 设计原则
|
||
|
||
- **标准优先,私有机制只进适配层**:项目内容只写标准形态,客户端差异不得渗入项目内容。
|
||
- **检测行为而非嗅探版本**:用无头实测判定机制是否有效,不凭版本号或文档推断(H1–H4 即此法)。
|
||
- **兼容性表必须可自动重跑**:客户端几乎每周更新,人工结论很快过期。
|
||
- **polyfill 可退役**:客户端原生支持标准后撤掉对应适配(例如 Claude 若开始读 `.agents/skills`,即撤 `.claude/skills` 投影)——对应 AGENTS.md「管家准入与退出」的退出规则。
|
||
- **渐进增强**:核心能力在最弱客户端也可用(落地文件兜底),高级能力只在支持的客户端启用(明文读取有界例外)。
|
||
- **五个接入面**逐一对齐:① 项目规则(AGENTS.md)② 工作流 skill ③ MCP 连接炼境 ④ 敏感取值 ⑤ 交付信号(Git + lefthook + Gitea,已与客户端无关)。
|
||
- **先路由、后认证**:客户端必须连项目专用地址 `/mcp/projects/<project_id>`;全局 `/mcp` 只作兼容,且暴露跨项目管理工具,不应作为子项目日常入口(最小权限)。
|
||
- **无头模式 = 各客户端的 agent 按钮**:`claude -p`、`codex exec`、`dsh headless`。对等性用同一任务、同一断言在三端无头实测,不靠文档推断。
|
||
- 不写静态 token 进任何配置文件;认证靠各客户端的动态机制或不需认证的落地路径。
|
||
|
||
## 基线证据(2026-09-30,enterprise-system,三端无头同一只读任务)
|
||
|
||
| 检查项 | Codex 0.155 | Claude Code 2.1.173 | DSH 桌面版 0.2.0-rc.2 |
|
||
| --- | --- | --- | --- |
|
||
| A 加载项目规则 | ✅ 摘录 AGENTS.md | ✅ CLAUDE.md 经 `@AGENTS.md` 引用 | ✅ 原生读取 AGENTS.md |
|
||
| B lian-jing skill | ✅ | ❌ 看不到(未投影到 `.claude/skills/`) | ✅ 读取 `.agents/skills`,与 Codex 共用 |
|
||
| C MCP 工具 | ✅ 32 个,含敏感读取 | ⚠️ 35 个全局工具,无开发变量工具 | ⚠️ 35 个全局工具,无开发变量工具 |
|
||
| D get_diagnostics | ✅ | ✅ | ✅ |
|
||
| E 列出开发变量 | ✅ | ❌ 工具不存在 | ❌ 工具不存在 |
|
||
| F 敏感读取 | ✅ | ❌ 工具不存在 | ❌ 工具不存在 |
|
||
| 耗时 | 56 s | 138 s | 25 s |
|
||
|
||
三端均未泄露凭据。连接现状:Codex 用项目 `.codex/config.toml`(项目地址 + helper);Claude 仅在 `~/.claude.json` user scope 注册全局 `/mcp`(SSE);DSH 在机器级 `~/.dsh/cordis.patch.yml` 注册全局 `/mcp`,配置无法按项目拆分,但支持 `!!js` 求值与每次调用的 `--patch` 叠加层。
|
||
|
||
## 实施前待验证假设(闸门)
|
||
|
||
- **H1 DSH**:`!!js` 能否在加载时读取当前目录的 `.lian-jing/context.json` 算出项目地址,并执行 helper 生成认证头。
|
||
- **H2 Claude**:Windows 上 `headersHelper` 的执行方式与调用写法;local scope 条目覆盖 user scope 全局注册。
|
||
- **H3 Claude skill**:项目 `.claude/skills/<name>/SKILL.md` 能被无头会话发现。
|
||
- **H4 Codex**:`http_headers_helper` 中 `%LOCALAPPDATA%` 能被 `cmd /C` 展开。
|
||
|
||
任一假设不成立:回到设计,不退回写静态 token 或本机绝对路径。
|
||
|
||
### 假设实测结果(2026-09-30,Workflow 并行实验 + 独立对抗复核)
|
||
|
||
- **H1 ✅ 成立(复核未推翻)**:DSH `--patch` 叠加层即可,不改 `~/.dsh`。`!!js` 在主进程加载时求值,`require` 不可用但 `process.getBuiltinModule('fs'|'path'|'crypto'|'child_process')` 可用;`process.cwd()` = 启动目录,向上查找 `.lian-jing/context.json` 算项目路由(子目录启动亦可);`execFileSync` 直接执行 helper(不经 shell)生成 Authorization;`disabled: !!js` 在项目外不挂载且不求值 config。helper 失败时安全降级(只写一行 stderr、读取 403、会话不崩)。**限制**:桌面 GUI 模式下 cwd 不随工作区切换,本方案目前只证实适用于在项目目录启动的 headless/CLI。
|
||
- **H3 ✅ 成立(复核未推翻)**:Claude 发现 `<项目>/.claude/skills/<name>/SKILL.md`(向上查找到 git 根为止,git 根以上不加载);**不读 `.agents/skills`**,所以必须投影一份到 `.claude/skills`。`-p` 模式下目录 trust 不影响 skill 加载。
|
||
- **H4 ✅ 成立(第二轮零凭据实测)**:首轮仅 cmd 层模拟、被复核判证据不足;第二轮用哑 helper + 本地请求记录器直接观测 Codex 0.155 自身行为:`"%LOCALAPPDATA%\...\hdr.cmd"` 被执行且输出成为请求头(记录器收到 `codex-mcp-client` 携带的标记头);`%NOT_A_REAL_VAR%` 不执行;父进程导出的自定义变量 `%LJPROBE_BASE%` 不执行(证实 env_clear 白名单);把 `LOCALAPPDATA` 指向替身目录后执行的是替身目录里的脚本(因果对照);cmd 独有的 `%LOCALAPPDATA:~0%` 子串语法也生效(展开由 cmd.exe 完成);helper 非零退出时连接不建立。
|
||
- **H2 ✅ 成立(第二轮零凭据实测)**:Claude Code 2.1.173 在 Windows 经 **cmd.exe** 执行 headersHelper——带引号反斜杠路径、正斜杠路径、`%TEMP%`、显式 `bash x.sh` 均执行且输出成为请求头;`$TEMP/...` 不执行,`%USERNAME%` 展开而 `$USERNAME` 保持字面。**local scope 覆盖 user scope**:init 事件中 `lian-jing` 工具数 35(全局)→ 加 local 条目后 32(项目路由)→ 删除后回到 35。经 `--mcp-config` 传入的 helper 在未信任目录也执行(文档所述 trust 限制针对项目 `.mcp.json` 来源)。`-p` 需设 `MCP_CONNECTION_NONBLOCKING=false` 才会等 MCP 连上再发 init。
|
||
- 第二轮两项的对抗复核 agent 未能运行(见下条),结论由主会话直接核对原始日志(记录器日志由独立进程写入)得出,未经第二个 agent 复现。
|
||
- **结论:Codex 与 Claude 都经 cmd.exe 执行 helper,可共用同一个 `%LOCALAPPDATA%\lian-jing\bin\` shim 写法**;DSH 经 `!!js` + `execFileSync` 直接调用同一 shim(在 JS 里用 `process.env.LOCALAPPDATA` 拼路径)。
|
||
|
||
### 实验附带发现
|
||
|
||
- **服务端缺陷**:`/mcp/projects/<任意字符串>` 都返回完整 32 个项目工具,不校验项目是否存在;应对未知 project_id 明确拒绝。
|
||
- **零凭据验证法**(后续实验与 P4 探针采用):认证链路用 `read_project_test_account(name="LJ_PROBE_NONEXISTENT")` 判定——认证通过返回"开发需求未登记",失败返回 403,凭据不进任何会话;客户端 helper 机制用"哑 helper(写运行标记 + 输出假请求头)+ 本地 HTTP 记录器"判定,全程不碰真 token。
|
||
- 模型自报的工具数量不可靠(同一路由报出 27~38 个),计数以 `curl tools/list` 真值为准(项目路由 32、全局 35)。
|
||
- 在 Claude 会话中嵌套无头 Claude 须先去掉父会话注入的 `CLAUDECODE`、`CLAUDE_CODE_*` 环境变量并 `< /dev/null`;在 Git Bash 中传 `/斜杠命令` 需设 `MSYS_NO_PATHCONV=1`,否则提示词被改写成 Windows 路径导致误判。
|
||
- **实验编排教训**:Workflow 子 agent 超过 180 秒无进展即判卡死并从头重试,而单次真实客户端无头会话常需 1~3 分钟,导致 H2/H4 共 4 次整轮重跑、无一产出结论(实验本身已完成并落盘日志)。需要启动真实客户端会话的实验应由主会话直接执行,或把长调用拆到后台任务、agent 只读结果。
|
||
|
||
## 任务拆解
|
||
|
||
### 🔵 P1 — 统一连接项目专用地址(Claude + DSH)
|
||
- status: in_progress
|
||
- complexity: M
|
||
- files: src-tauri/src/mcp_inject.rs, docs/ai-context/agent-button-system.md
|
||
- depends: H1, H2
|
||
- acceptance: "agent_verify: [三端无头探针] 在 enterprise 目录 Claude 与 DSH 的 C 项均看到开发变量工具(项目工具集),E 项成功;项目目录外的会话不获得他项目工具。"
|
||
- 注:吸收原 project-dev-variables V17(Claude 以 local scope + headersHelper 取值)。
|
||
|
||
### ✅ P2 — Claude skill 投影
|
||
- status: done
|
||
- complexity: S
|
||
- files: src-tauri/src/commands/onboarding.rs, src-tauri/resources/onboarding/
|
||
- depends: H3
|
||
- acceptance: "agent_verify: [cargo test onboarding] 接入包同时写 .claude/skills/lian-jing/SKILL.md,内容与 .agents/skills 单一来源一致;[Claude 无头探针] B 项看到 lian-jing。"
|
||
- verification: "2026-10-01:同一模板双投影到 .agents/skills 与 .claude/skills;verify_onboarding 新增「Claude 炼境技能已分发」(检查链 12 项);base-onboarding 升至 1.6.0,supported_clients 增 dsh;skill 降级步骤改为三端通用并提示「无开发变量工具即连的是全局地址」。新增测试 skill_is_projected_to_codex_and_claude_from_one_source;cargo test --locked 229 passed / 5 ignored。Enterprise 分发后 verify_onboarding 14/14,两份 skill 逐字节一致;Claude 无头会话 init 事件 skills 与 slash_commands 均含 lian-jing(基线探针时为不存在)。"
|
||
|
||
### 🔵 P3 — 稳定 helper 入口,项目配置去本机路径
|
||
- status: in_progress
|
||
- complexity: M
|
||
- files: src-tauri/src/mcp_inject.rs, src-tauri/src/lib.rs, src-tauri/src/commands/onboarding.rs
|
||
- depends: H4
|
||
- acceptance: "agent_verify: [cargo test] 炼境启动写/更新 %LOCALAPPDATA% 下 shim 指向当前 exe;inject 后受跟踪文件不含本机绝对路径或用户名;[Codex 无头探针] F 项经 shim 成功;换构建目录启动后无需重注入仍成功。"
|
||
- 注:即原 project-dev-variables V18;Claude headersHelper 与 DSH 共用同一 shim。
|
||
|
||
### 📋 P4 — 三端对等探针固化为炼境能力
|
||
- status: todo
|
||
- complexity: M
|
||
- files: scripts/ 或 src-tauri/src/commands/(实现时定), docs/ai-context/agent-button-system.md
|
||
- depends: P1, P2, P3
|
||
- acceptance: "agent_verify: [CLI] 一条命令对指定项目依次跑三端无头探针,输出 A–F × 三端矩阵与耗时,断言无凭据泄露;接入包升级后重跑结果可比对。"
|
||
|
||
### 📋 P5 — 测试账户落地到被 Git 忽略文件
|
||
- status: todo
|
||
- complexity: M×3
|
||
- 见 project-dev-variables V14–V16 与 `docs/tasks/2026-09-30-dev-variables-materialize.md`;落地不返回值、无需认证,是三端都可用的兜底路径。
|
||
|
||
### 📋 P6 — 技能能力包:通用 skill 单一来源、三端投影
|
||
- status: todo
|
||
- complexity: M
|
||
- files: src-tauri/src/commands/onboarding.rs, src-tauri/resources/(实现时定)
|
||
- depends: P2
|
||
- acceptance: "agent_verify: [cargo test] 通用 skill(architect、splitter、code-review 等)由一处模板按版本投影到各客户端目录(Claude user/project skills、Codex/DSH 的 .agents/skills);[三端无头探针] B 项三端看到同一组 skill 且内容一致。"
|
||
- 为什么:2026-09-30 探针显示同一批 skill 在三端各维护一份(Claude `~/.claude/skills`、Codex `source-command-*` 副本、DSH 手写 `.dsh/skills`),是实际的重复与漂移源。先把 skill 中的确定性步骤下沉为炼境 MCP 工具,让 skill 变薄,再做投影。
|
||
- 不做:炼境不执行推理类 skill(无模型、会丢失会话代码上下文、越过「不替 agent 思考」边界);不以运行时拉取 skill 正文作为主路径(炼境离线即失效、脱离 Git 分支)。
|
||
|
||
### 📋 P7 — MCP 服务器说明的三端支持验证
|
||
- status: todo
|
||
- complexity: S
|
||
- files: src-tauri/src/mcp_server.rs, docs/ai-context/agent-button-system.md
|
||
- depends: P1
|
||
- acceptance: "agent_verify: [三端无头探针] 在 initialize 下发唯一标记的 instructions,判定三端是否把它带入模型上下文。全部支持则把引导提示迁到服务器说明,本地 skill 只保留 MCP 不可达时的排障步骤;部分支持则如实记录、保留 skill 引导。"
|
||
- 为什么:Claude 已实证会把 lian-jing 的服务器说明带入上下文;若三端皆支持,引导提示可由炼境集中维护。
|