From 9b6c6f7ea30ec46d334ff4fb1c06f411c8ee5498 Mon Sep 17 00:00:00 2001 From: sandyzzx Date: Wed, 7 Oct 2026 12:09:27 +0800 Subject: [PATCH 1/5] docs: add agent rules and documentation router --- .gitignore | 10 ++---- AGENTS.md | 31 ++++++++++++++++ docs/README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++ src/interfaces.ts | 9 +++-- 4 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 AGENTS.md create mode 100644 docs/README.md diff --git a/.gitignore b/.gitignore index 3bdaa40..b60b6e7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,5 @@ plugins/codex-zcode-bridge/.mcp.local.json .env .env.* !.env.example -# Current shared-core contracts are versioned; other local research stays local. -!docs/ -docs/* -!docs/SHARED_CORE.md -!docs/ARCHITECTURE.md -!docs/INTERFACES.md -!docs/ZCODE_RUNTIME.md -!docs/PHASE7_LIVE_PROGRESS.md +# Documentation is versioned by default. Scratch material belongs in docs/_draft/. +docs/_draft/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d15c406 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,31 @@ +# AGENTS.md + +本仓库把 Codex 的任务派发给本机 ZCode 执行。任何 agent 动手前先读这份文件。 + +## 阅读顺序 + +1. `docs/README.md` —— 文档路由、状态等级、语言约定。 +2. `docs/PROJECT_STATE.md` —— 当前状态快照:稳定、实验、不支持、调查中。 +3. 按任务类型读权威文档:架构改动读 `docs/ARCHITECTURE.md`,接口改动读 `docs/INTERFACES.md`,宿主接入读 `docs/SHARED_CORE.md`,运行配置读 `docs/ZCODE_RUNTIME.md`。 +4. 需要知道"为什么这样定"时读 `docs/decisions/`。 +5. `docs/archive/` 默认不读,只有调查历史决策时才读。 + +## 文档等级 + +每份文档顶部标注四种状态之一:`AUTHORITATIVE` 当前事实或合同、`DECISION` 已批准决策、`RESEARCH` 研究结论、`ARCHIVED` 历史材料。研究结论和归档材料不能当作当前实现使用。 + +## 硬规则 + +- 改变 `docs/ARCHITECTURE.md`、`docs/INTERFACES.md`、`docs/SHARED_CORE.md`、`docs/ZCODE_RUNTIME.md` 描述的行为前,先有 `DECISION` 记录。实现不能反向改写合同。 +- 公共 MCP 工具名与 schema、任务状态语义、隐私边界、新增运行时依赖,都属于合同变更。 +- 研究材料放 `docs/research/`,草稿放 `docs/_draft/`(已忽略)。不要把未定稿留在 `docs/` 顶层。 +- 语言:`README.md` 英文,技术文档中文,`docs/README.md` 双语索引。 +- 核心权威文档控制在 10 份以内。单份超过约 15 KB,或同一主题被三个以上独立读者分读,才拆成多份。 +- 写结论时区分"已核实"和"未运行"。不要把 NOT RUN 写成通过,也不要把尝试过的方法写成可行方法。 +- 引用 ZCode 内部结构时标明观测版本,用 Observed 措辞,不要写成官方合同。 + +## 提交与验证 + +- 代码改动跑 `npm run typecheck`、`npm test`、`npm run build`、`npm run validate:plugin`,并保证 `git diff --check` 干净。 +- `plugins/codex-zcode-bridge/dist/bridge.mjs` 与 `worker/worker-main.mjs` 是提交物,必须与源码同步;CI 会校验它们没有落后。 +- 不默认提交、推送或发布。发布走 release-please,合并到 `master` 之后由它生成版本 PR。 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..9a69fcf --- /dev/null +++ b/docs/README.md @@ -0,0 +1,91 @@ +# 文档索引 / Documentation + +英文使用者从 [README.md](../README.md) 开始。技术文档为中文。本文是文档路由:先看状态等级,再按读者路径选文档。 + +English readers start at [README.md](../README.md). This index is bilingual; the technical documents themselves are written in Chinese. + +## 状态等级 / Status levels + +每份文档顶部标注四种状态之一。这是本仓库最重要的一条约定:没有状态标注的文档不能被当成事实依据。 + +| 状态 | 含义 | 使用方式 | +|---|---|---| +| `AUTHORITATIVE` | 当前事实或合同 | 可以据此开发和判断 | +| `DECISION` | 已批准的架构决策 | 引用它回答"为什么不那样做" | +| `RESEARCH` | 研究结论 | 只作参考,不代表当前实现 | +| `ARCHIVED` | 历史材料 | 默认不读,只用于追溯 | + +现状说明:下面的现有文档在本次整理前没有状态标注,状态由本索引统一指定。文档下次被修改时补上顶部标注。 + +## For Master Agents + +按顺序读,不要通读全仓库文档。标 `待建` 的条目属于下一批整理,本批尚未提交: + +1. `docs/PROJECT_STATE.md` —— 当前状态快照(稳定 / 实验 / 不支持 / 调查中)`待建` +2. `docs/ARCHITECTURE.md` —— 系统现在怎么工作 +3. `docs/INTERFACES.md` —— 合同与兼容边界 +4. `docs/decisions/README.md` —— 已批准决策索引 `待建` +5. 只在需要证据时读 `docs/research/` `待建` + +不要默认读 `docs/archive/`。 + +## For Coding Agents + +1. 本任务涉及的合同文档(`INTERFACES.md` / `ZCODE_RUNTIME.md` / `SHARED_CORE.md`) +2. 对应组件的架构(`ARCHITECTURE.md`) +3. `AGENTS.md` 里的硬规则 + +实现不能自行改写合同。需要改合同先提 `DECISION`。 + +## 索引 / Index + +### 权威文档 AUTHORITATIVE + +| 文档 | 语言 | 最后更新 | 最后核对 | +|---|---|---|---| +| [README.md](../README.md) | en | 2026-10-05 | 未记录 | +| [README.zh-CN.md](../README.zh-CN.md) | zh | 2026-10-03 | 未记录 | +| [ARCHITECTURE.md](ARCHITECTURE.md) | zh | 2026-10-03 | 未记录 | +| [INTERFACES.md](INTERFACES.md) | zh | 2026-10-06 | 未记录 | +| [SHARED_CORE.md](SHARED_CORE.md) | zh | 2026-10-06 | 未记录 | +| [ZCODE_RUNTIME.md](ZCODE_RUNTIME.md) | zh | 2026-10-03 | 未记录 | +| [plugins/codex-zcode-bridge/README.md](../plugins/codex-zcode-bridge/README.md) | zh | 2026-10-03 | 未记录 | +| [plugins/codex-zcode-bridge/SECURITY.md](../plugins/codex-zcode-bridge/SECURITY.md) | zh + en | 2026-10-03 | 未记录 | +| [plugins/codex-zcode-bridge/skills/zcode-bridge/SKILL.md](../plugins/codex-zcode-bridge/skills/zcode-bridge/SKILL.md) | zh | 2026-10-06 | 未记录 | + +`最后核对` 表示上一次有人把文档内容与代码逐条对照的日期。这一列目前全部为空,说明此前没有这个习惯;新建和修改文档时必须填写,否则该文档只能算"最后更新",不能算"已验证"。 + +### 报告与历史 REPORT / ARCHIVED + +| 文档 | 状态 | 说明 | +|---|---|---| +| [TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md](../TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md) | REPORT | Task Feedback v0.1 的交付报告,一次性材料 | +| [PHASE7_LIVE_PROGRESS.md](PHASE7_LIVE_PROGRESS.md) | ARCHIVED | Phase 7 兼容说明;有效内容待提炼进 `INTERFACES.md` 后移入 `archive/` | + +### 自动生成 Generated + +| 文档 | 说明 | +|---|---| +| [CHANGELOG.md](../CHANGELOG.md) | 由 release-please 维护,不要手改 | + +## 新文档放哪里 + +| 内容 | 位置 | +|---|---| +| 当前事实、合同 | `docs/` 顶层,数量控制在 10 份以内 | +| 已批准决策 | `docs/decisions/ADR-NNN-.md` | +| 研究结论与证据 | `docs/research/-/` | +| 一次性交付报告 | `docs/reports/` | +| 已被取代的材料 | `docs/archive//` | +| 未定稿草稿 | `docs/_draft/`(已 gitignore,不进仓库) | + +## 仓库外的材料 + +以下内容有意不放进仓库,追溯时按下表位置查找: + +| 位置 | 内容 | +|---|---| +| `C:\Users\Sandy\.codex\archived_docs\codex-zcode-bridge\` | 全库审计报告、生命周期可观测性方案、ccteam 对比、整改报告、验收证据 JSON | +| `\.tasks\notes\` | 会话期笔记;属于桥接运行数据根,不是版本化文档 | + +桥接运行数据根 `.tasks/` 只放任务记录,不要在那里存放研究用的外部仓库克隆。 diff --git a/src/interfaces.ts b/src/interfaces.ts index 49934da..4a06167 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,6 +1,9 @@ -// Frozen contract projection of docs/INTERFACES.md (V0.1, FROZEN). -// The documents are authoritative; keep this file mechanically in sync. -// Do not change tool names, required fields, status names, or result semantics. +// Contract projection of docs/INTERFACES.md, which is authoritative; keep this +// file in sync with it. The V0.1 "frozen" wording is historical: later additive +// features are documented in INTERFACES.md, not here. +// Public tool names, required fields, status names and result semantics are +// compatibility commitments. Change them only through a decision under +// docs/decisions/, as required by AGENTS.md. // Additive optional fields (observation, usage/timing/model, scan cursor) are // backward compatible: old clients ignore them, old records read as absent. From 807bf9051bc7b7173a0cbcad3f5ebf72ed1f4da6 Mon Sep 17 00:00:00 2001 From: sandyzzx Date: Wed, 7 Oct 2026 12:14:23 +0800 Subject: [PATCH 2/5] docs: bring local documents into the repository --- docs/PROJECT_STATE.md | 47 ++++ docs/README.md | 43 ++- .../appserver-capability-matrix-2026-09-27.md | 84 ++++++ .../mcp-sdk-v2-migration-2026-09-27.md | 15 ++ docs/archive/mvp-v0.3-2026-09-27.md | 57 ++++ docs/decisions/README.md | 26 ++ .../reliability-repair-plan-v2-2026-10-03.md | 251 ++++++++++++++++++ .../decisions/roadmap-decisions-2026-09-27.md | 209 +++++++++++++++ .../desktop-task-refresh-2026-09-28.md | 208 +++++++++++++++ .../research/phase1-codex-zcode-2026-09-26.md | 80 ++++++ .../start-plan-headless-2026-09-27.md | 42 +++ 11 files changed, 1054 insertions(+), 8 deletions(-) create mode 100644 docs/PROJECT_STATE.md create mode 100644 docs/archive/appserver-capability-matrix-2026-09-27.md create mode 100644 docs/archive/mcp-sdk-v2-migration-2026-09-27.md create mode 100644 docs/archive/mvp-v0.3-2026-09-27.md create mode 100644 docs/decisions/README.md create mode 100644 docs/decisions/reliability-repair-plan-v2-2026-10-03.md create mode 100644 docs/decisions/roadmap-decisions-2026-09-27.md create mode 100644 docs/research/desktop-task-refresh-2026-09-28.md create mode 100644 docs/research/phase1-codex-zcode-2026-09-26.md create mode 100644 docs/research/start-plan-headless-2026-09-27.md diff --git a/docs/PROJECT_STATE.md b/docs/PROJECT_STATE.md new file mode 100644 index 0000000..02e73c2 --- /dev/null +++ b/docs/PROJECT_STATE.md @@ -0,0 +1,47 @@ +# Project State + +> Status: AUTHORITATIVE +> Last updated: 2026-10-07 +> Last verified: 2026-10-07,核对来源见文末。 + +当前状态快照,回答"现在什么能用、什么不能用"。路线图和优先级不在这里。 + +## 版本与发布 + +- 当前发布:`1.2.2`(`package.json`、`plugins/codex-zcode-bridge/plugin.json`)。 +- 发布方式:release-please 监听 `master`,合并后自动开版本 PR;合并版本 PR 才产生 tag 与 GitHub Release。 +- CI:`.github/workflows/ci.yml`,ubuntu 与 windows 两个作业,跑 typecheck、build、test、validate:plugin,并校验生成的 bundle 已随源码提交。 + +## 稳定 + +- TaskManager 生命周期:`queued` / `running` / `completed` / `failed` / `cancelled` / `waiting_for_master`,含 attempt 独占、续跑归档、清理验证。 +- MCP 默认工具 13 个,清单见 `INTERFACES.md`。 +- `zcode_events` 的增量事件、cursor 与 raw/summary 视图。 +- `zcode_feedback` 的 `TaskFeedbackSnapshotV01`。 +- 工作区隔离:执行目录由调用宿主准备,Bridge 不创建也不删除 worktree。 +- 模型选择:可按任务覆盖 provider/model,不持久化为工作区默认。 +- 并发:同一 data root 内默认最多 8 个 worker,可用 `ZCODE_BRIDGE_MAX_CONCURRENT_WORKERS` 在 1–8 之间调整。 + +## 实验 + +- `zcode_progress_probe`:默认关闭,只有直接调用 `createBridgeServer({ enableExperiments: true })` 才注册。 +- Desktop 索引同步:best effort,事务内校验 schema 与 Bridge owner;尚未写入真实 Desktop 数据库。 + +## 不支持 + +- Start Plan 的 headless 认证:app-server 需要桌面渲染器提供的验证码会话,Bridge 不伪造、不绕过。当前 headless 开发路径使用 Coding Plan。见 `research/start-plan-headless-2026-09-27.md`。 + +## 调查中 / NOT RUN + +以下边界没有实跑验证,不能当成已知可用: + +- `session/read` 原生当前状态查询:未接入。 +- 真实 ZCode app-server RPC 探针与跨版本兼容:只在记录过的 0.16.9 路径上观测过。 +- 跨 Host 并发 attach/control:研究阶段结论为 NO-GO,除非上游提供 ownership/control 协议。 +- 真实 GUI 关闭时序、UI 响应与取消时延。 +- 真实 Desktop 数据库写入与刷新行为。 +- PID 重用,以及自行脱离进程组的后代进程。 + +## 核对来源 + +`package.json`、`src/host/stdio.ts`、`src/mcp/server.ts`、`docs/ARCHITECTURE.md`、`docs/INTERFACES.md`、`docs/PHASE7_LIVE_PROGRESS.md`、GitHub Actions 运行记录。文中标注"未运行"的条目没有被上述来源证实,保持未验证状态。 diff --git a/docs/README.md b/docs/README.md index 9a69fcf..4cd7579 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,13 +19,13 @@ English readers start at [README.md](../README.md). This index is bilingual; the ## For Master Agents -按顺序读,不要通读全仓库文档。标 `待建` 的条目属于下一批整理,本批尚未提交: +按顺序读,不要通读全仓库文档: -1. `docs/PROJECT_STATE.md` —— 当前状态快照(稳定 / 实验 / 不支持 / 调查中)`待建` +1. `docs/PROJECT_STATE.md` —— 当前状态快照(稳定 / 实验 / 不支持 / 调查中) 2. `docs/ARCHITECTURE.md` —— 系统现在怎么工作 3. `docs/INTERFACES.md` —— 合同与兼容边界 -4. `docs/decisions/README.md` —— 已批准决策索引 `待建` -5. 只在需要证据时读 `docs/research/` `待建` +4. `docs/decisions/README.md` —— 已批准决策索引 +5. 只在需要证据时读 `docs/research/` 不要默认读 `docs/archive/`。 @@ -45,6 +45,7 @@ English readers start at [README.md](../README.md). This index is bilingual; the |---|---|---|---| | [README.md](../README.md) | en | 2026-10-05 | 未记录 | | [README.zh-CN.md](../README.zh-CN.md) | zh | 2026-10-03 | 未记录 | +| [PROJECT_STATE.md](PROJECT_STATE.md) | zh | 2026-10-07 | 2026-10-07 | | [ARCHITECTURE.md](ARCHITECTURE.md) | zh | 2026-10-03 | 未记录 | | [INTERFACES.md](INTERFACES.md) | zh | 2026-10-06 | 未记录 | | [SHARED_CORE.md](SHARED_CORE.md) | zh | 2026-10-06 | 未记录 | @@ -55,12 +56,38 @@ English readers start at [README.md](../README.md). This index is bilingual; the `最后核对` 表示上一次有人把文档内容与代码逐条对照的日期。这一列目前全部为空,说明此前没有这个习惯;新建和修改文档时必须填写,否则该文档只能算"最后更新",不能算"已验证"。 -### 报告与历史 REPORT / ARCHIVED +### 决策 DECISION -| 文档 | 状态 | 说明 | +| 文档 | 日期 | 说明 | |---|---|---| -| [TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md](../TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md) | REPORT | Task Feedback v0.1 的交付报告,一次性材料 | -| [PHASE7_LIVE_PROGRESS.md](PHASE7_LIVE_PROGRESS.md) | ARCHIVED | Phase 7 兼容说明;有效内容待提炼进 `INTERFACES.md` 后移入 `archive/` | +| [decisions/README.md](decisions/README.md) | 2026-10-07 | 决策索引与待补 ADR 清单 | +| [decisions/roadmap-decisions-2026-09-27.md](decisions/roadmap-decisions-2026-09-27.md) | 2026-09-27 | 路线图与决策讨论 | +| [decisions/reliability-repair-plan-v2-2026-10-03.md](decisions/reliability-repair-plan-v2-2026-10-03.md) | 2026-10-03 | 可靠性修复计划,含未完成项 | + +### 研究 RESEARCH + +| 文档 | 日期 | 说明 | +|---|---|---| +| [research/phase1-codex-zcode-2026-09-26.md](research/phase1-codex-zcode-2026-09-26.md) | 2026-09-26 | Phase 1 参考项目调研与 V0.1 架构建议 | +| [research/desktop-task-refresh-2026-09-28.md](research/desktop-task-refresh-2026-09-28.md) | 2026-09-28 | ZCode Desktop 任务列表刷新机制 | +| [research/start-plan-headless-2026-09-27.md](research/start-plan-headless-2026-09-27.md) | 2026-09-27 | Start Plan headless 认证阻塞与 Coding Plan 回归 | + +`docs/research/` 里还会随 2026-10-05 的 Native CLI / app-server 研究合并而增加内容;那批文档目前还在独立 PR 中。 + +### 归档 ARCHIVED + +| 文档 | 日期 | 说明 | +|---|---|---| +| [archive/appserver-capability-matrix-2026-09-27.md](archive/appserver-capability-matrix-2026-09-27.md) | 2026-09-27 | app-server 能力普查,已被 2026-10-05 的能力探针取代 | +| [archive/mvp-v0.3-2026-09-27.md](archive/mvp-v0.3-2026-09-27.md) | 2026-09-27 | MVP 0.3 版本说明 | +| [archive/mcp-sdk-v2-migration-2026-09-27.md](archive/mcp-sdk-v2-migration-2026-09-27.md) | 2026-09-27 | MCP SDK v2 迁移记录 | + +### 报告 REPORT + +| 文档 | 说明 | +|---|---| +| [TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md](../TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md) | Task Feedback v0.1 的交付报告,一次性材料 | +| [PHASE7_LIVE_PROGRESS.md](PHASE7_LIVE_PROGRESS.md) | Phase 7 兼容说明;有效内容待提炼进 `ARCHITECTURE.md` / `INTERFACES.md` 后移入 `archive/` | ### 自动生成 Generated diff --git a/docs/archive/appserver-capability-matrix-2026-09-27.md b/docs/archive/appserver-capability-matrix-2026-09-27.md new file mode 100644 index 0000000..4a629ef --- /dev/null +++ b/docs/archive/appserver-capability-matrix-2026-09-27.md @@ -0,0 +1,84 @@ +# ZCode app-server 能力普查 + +> Status: ARCHIVED +> Date: 2026-09-27 +> Superseded by: 2026-10-05 的 app-server 能力探针与 Native CLI 对比(`ZCODE_APPSERVER_EVENT_CAPABILITY_PROBE.md`、`docs/research/`)。 +> 历史材料,不指导当前开发。保留用于追溯当时的能力边界与证据等级。 + +调查日期:2026-09-27 +本机版本:ZCode Desktop `3.14.3.7762` / CLI `0.16.9` +Runtime 入口:`\resources\glm\zcode.cjs` +Runtime SHA-256:`B1DF2EF3E5BD76C4AF3ECB296BC003A10D3F13191A26610BD0BA940FEADAD529` + +本调查只读检查本机 CLI bundle 和 app-server dispatcher,并复用此前通过的双轮真实 E2E 作为实际调用证据。**没有启动额外模型调用、创建额外 session,或写入 ZCode 数据库。** 未知请求参数仍待后续隔离探测。 + +## 先回答:app-server 是谁提供的? + +`app-server` 是 **ZCode 自带的 Agent runtime / CLI 能力**。本机实际启动的是随 ZCode 安装的 `zcode.cjs app-server --stdio`。ZCode 官方 CLI 源码也将 `app-server` 作为原生 CLI 子命令,并把打包态 app-server 描述为 Desktop host 使用的内部协议子进程:[官方 CLI `run.ts`](https://github.com/zai-org/ZCode/blob/main/apps/zcode-cli/packages/cli/src/run.ts)。 + +本项目实现的是 **Bridge → ZCode 的客户端接入层**:`ZCodeAppServerAdapter` 启动 ZCode 进程、通过 stdio 收发本机协议请求、订阅事件,并把事件映射到 Bridge 的持久化事件流。Codex 侧 MCP server 则是 **Codex → Bridge 的接口**。因此它既不是我们编写了 ZCode Agent,也不是通过 ZCode 的 MCP 工具调用 Agent。 + +官方仓库包含 app-server 命令实现,但当前查到的官方产品文档没有承诺该 stdio 协议是稳定的第三方 SDK/API。官方源码注释将打包态 app-server 称为 Desktop host 的内部协议子进程;本机 app-server 协议、方法和 payload 应视为版本相关接口,升级后需要重新核验。以上“目前未见稳定兼容承诺”是基于公开文档范围的结论,不代表证明 ZCode 永远不会发布正式 API。 + +## 证据等级 + +| 等级 | 含义 | +|---|---| +| **官方资料** | ZCode 官方文档或官方仓库源码明确记载。证明产品/源码具有该能力,不自动证明第三方兼容承诺。 | +| **本机分发实现** | 在本机 `0.16.9` bundle 的方法注册表或 app-server dispatcher 中发现。证明该构建包含相应分发路径;尚未通过请求验证的参数和效果仍属未知。 | +| **本机实际验证** | 本机已向 app-server 发送请求或收到事件,并检查了结果。 | +| **Bridge E2E** | Bridge 经实际 MCP 工具完成了端到端验证。 | + +## 能力矩阵 + +| 能力 | 本机发现/入口 | 当前证据 | 当前结论 | +|---|---|---|---| +| 启动 Agent runtime | `zcode.cjs app-server --stdio` | **官方资料 + Bridge E2E** | ZCode 原生子命令;Bridge 已能启动和关闭该进程。stdio 上使用 `{id, method, params}` 一类 NDJSON 消息,当前观察到的封装没有 JSON-RPC 2.0 的 `jsonrpc` 字段。 | +| Runtime 初始化 | `session/requestRuntimePreferences` 反向请求 | **本机实际验证 + Bridge E2E** | 创建 session 前需要响应;本机接受 `nativeSearchEnhancementsEnabled`、`memoryEnabled`、`askUserQuestionAutoResolutionEnabled` 三个布尔值。当前 Bridge 统一回复 `false`。 | +| 创建/恢复 session | `session/create`、`session/resume` | **本机实际验证 + Bridge E2E** | Bridge 创建 session、订阅事件、发送任务,并在续作中恢复原 session;双轮 E2E 的 session ID 相同。workspace 字段为 `{workspacePath, workspaceKey}`;创建还传 `mode: "yolo"`、`persistence: "immediate"`。这里只验证了这些参数组合。 | +| 读取所选模型 | 创建/恢复返回的 session snapshot:`settings.model.current` | **本机实际验证 + Bridge E2E** | E2E 观测到模型元数据并将其写入事件。它表示 session 报告的配置模型,不证明每次内部重试或路由请求的最终 provider。 | +| 设置模型 | `session/setModel` | **Bridge 已实现;模型目录及 snapshot 运行时核验** | Bridge 支持任务级覆盖用户默认;将请求与 runtime model catalog 比较,并核对设置后的 snapshot。是否真实发出模型请求需按任务 E2E 验证。 | +| 设置思考等级 | `session/setThoughtLevel` | **本机分发实现** | 方法名及 dispatcher 分支存在;参数、支持等级、默认继承与实际生效情况未验证。 | +| 设置权限模式 | `session/setMode` / create `mode` | **Bridge 已实现;调用语义只核对到响应/snapshot** | 新建与续作 session 采用用户配置的 `plan`、`build`、`edit` 或 `yolo`;逐工具门控效果、权限反向 RPC 和 ask 回传尚未验证。 | +| Session 生命周期 | `session/list`、`session/read`、`session/messages`、`session/events`、`session/close`、`session/fork` | **本机分发实现** | 本机构建中有对应分发路径;未验证数据范围、恢复语义或对 Desktop 历史的影响。 | +| 发送/停止/取消 | `session/send`、`session/stop`、`session/cancelBackgroundTask` | **本机分发实现;部分 E2E** | Bridge 已验证发送初始任务和续作 prompt;取消目前通过终止 worker 进程树实现。运行中发送是否等同安全 steering、stop 与 cancelBackgroundTask 的精确行为尚未验证。 | +| 实时引导 | bundle 中有 `turn.steer.*` 事件名;V4 统一入口 `v4/command` | **本机分发实现** | 未发现 `session/steer` 方法名。发送/引导的具体 payload、`requestedDelivery: "guide"` 是否适用于本机构建、确认事件和取消语义均未验证。 | +| 模型文本与工具事件 | `session/subscribe`;事件 `session/event`;含 `turn.started`、`model.streaming`、`tool.updated`、`turn.completed`、`turn.failed` | **本机实际验证 + Bridge E2E** | 双轮 E2E 收到了可见文本、模型工具调用、工具状态、turn 开始/结束和 runtime 状态事件。Bridge 不存隐藏推理或原始工具参数。 | +| Usage | `turn.completed` payload;`session/usage`、`v4/usage/stats`、`v4/conversation/usage` | **部分 Bridge E2E;其余本机分发实现** | E2E 两个 turn 均出现 usage 事件;本阶段没有独立验证 token 字段是否完整、session 汇总口径或统计 API 的参数。 | +| Goal / compact | `session/goal`、`session/compact`;另有 `v4/command`、`v4/commands/query` | **官方产品文档 + 本机分发实现;Bridge 未调用** | ZCode 自带自动上下文压缩;社区 ACP 额外提供 turn 后 threshold→`session/compact`,但 Bridge 尚无稳定 context occupancy 信号,暂不重复实现。[官方命令文档](https://zcode.z.ai/en/docs/commands) [模型上下文说明](https://zcode.z.ai/en/docs/configuration) | +| Plans、文件变更与回退预览 | `v4/conversation/plans`、`v4/conversation/fileChanges`、`v4/conversation/fileRewindPreview` | **本机分发实现** | 存在 V4 gateway 调用路径。可能提供比 Agent 自报 `files_changed` 更有用的证据;返回结构、revision 语义及能否对应当前 session 尚未验证。 | +| Subagents | `session/subagents` | **本机分发实现** | 有相应分发路径;本阶段未调用,也未确认主 session 是否能创建、控制或读取其结果。官方 Subagent 文档只能证明产品能力,不证明 app-server 请求契约。 | +| Hooks/权限请求 | `interaction/requestPermission` 字符串;`workspace/hooks/trustGrant`;工具权限相关事件字符串 | **部分本机分发实现,Bridge 未验证** | 存在相关协议痕迹,但没有验证 app-server 创建的 session 是否触发 ZCode Plugin Hooks,也没有验证 Master 决策往返。Bridge 当前只响应 `session/requestRuntimePreferences`;其他携带 `id + method` 的未知反向请求会以 `-32601` 拒绝。 | +| MCP/插件/工作流配置 | `mcp/list`、`plugins/*`、`skills/*`、`workflows/*` 等 dispatcher 路径 | **本机分发实现** | ZCode runtime 包含这些内部管理路径;Bridge 没有把它们作为对外功能,本阶段也未改变本机配置。 | +| Task 注册 / Desktop 历史索引 | `/v2/tasks-index.sqlite` 的 `tasks` 表 | **本机 schema/status 只读检查;Bridge best-effort 实现** | 登记由 app-server 创建的 session,并同步 running/completed/error;取消时清空活动状态。直接 SQLite 写入不会向 Desktop 进程推事件,需刷新列表;索引失败不影响 ZCode 执行。 | +| Automation | bundle 方法名表含 `automation/create|list|update|delete` 字符串;在本次检查的 app-server `dispatchRequest` 中未找到对应分支 | **本机分发未确认** | 这些字符串不能作为 app-server 可调用方法的证据;可能属于 Desktop 内部其他通信层。官方 UI 有 Automation,但没有据此推断 Bridge 可调用的协议 API。[官方 Automation 文档](https://zcode.z.ai/en/docs/automations) | + +## 本机事件观察 + +| 事件类别 | 本机/Bridge 已见内容 | 是否通过本次 Bridge E2E | +|---|---|---| +| Session 建立 | `session_ready`,含 session ID、配置模型(有报告时)和工作区路径 | 是 | +| Turn 生命周期 | `turn.started`、`turn.completed`;bundle 还包含 `turn.failed` 等路径 | started/completed 是;failed 未触发 | +| 模型流 | `model.streaming`;文本增量和 tool-call kind 被分别处理,推理 kind 被过滤 | 可见文本和模型工具调用是 | +| 工具生命周期 | `tool.updated`;含工具名、调用 ID(有报告时)和状态 | 是 | +| Runtime 状态 | `state.updated` → `runtime_state` | 是 | +| 用量 | `turn.completed` 中存在 usage | 是;本报告不记录 token 总量 | +| Steering / 权限 | bundle 中存在 steer/permission 相关标记 | 否 | + +## 接入边界与后续 + +1. 继续把 `app-server` 当成本机 ZCode runtime 的版本化适配层,不把目前观察到的字段当作稳定公共 API。 +2. Phase 10 在临时工作区中分别探测 `session/setModel`、`session/setThoughtLevel` 和 `session/setMode`:每次只变更一个字段,核对响应、后续 snapshot、事件和一次无害任务的实际效果;探测前先处理 provider/model revision 兼容问题。 +3. task-index 直写已实现为 best-effort;不要据此调用 `automation/*`、批准权限请求或发送 steering。这些需要各自的可恢复性和安全策略。 +4. Hook/审批路线需要先验证 ZCode Plugin Hook 是否被 app-server session 调用,以及 Hook 子进程能否取得 task/session 策略上下文。`PreToolUse`/`PermissionRequest` 的存在不自动建立 Codex 的审批回调。 +5. 每次升级 ZCode 后重新生成方法/事件差异,并至少重跑 session create/resume、事件流和取消路径的回归验证。 + +## 参考资料 + +- [ZCode 官方 CLI `run.ts`](https://github.com/zai-org/ZCode/blob/main/apps/zcode-cli/packages/cli/src/run.ts):确认 `app-server` 是 ZCode CLI 子命令;源码将打包态协议进程描述为 Desktop host 内部进程。 +- [ZCode Agent Framework](https://zcode.z.ai/en/docs/agent-framework):官方产品层 Agent、任务、模型和权限模式说明。 +- [ZCode Hooks](https://zcode.z.ai/en/docs/hooks):Hook 子进程协议、工具前/后事件和决策返回格式。 +- [ZCode Commands](https://zcode.z.ai/en/docs/commands):官方 `/goal`、`/compact` 命令说明。 +- [ZCode Automations](https://zcode.z.ai/en/docs/automations):官方自动化 UI 与本机运行限制。 +- [本机 Phase 7 协议记录](../PHASE7_LIVE_PROGRESS.md) 和 [双轮 E2E 决策记录](../decisions/roadmap-decisions-2026-09-27.md)。 +- 社区协议逆向:[ZCode app-server V4 协议笔记](https://github.com/csuftt/zcode-jetbrains-plugin/blob/master/docs/zcode-appserver-protocol.md)。该资料不是官方兼容承诺。 diff --git a/docs/archive/mcp-sdk-v2-migration-2026-09-27.md b/docs/archive/mcp-sdk-v2-migration-2026-09-27.md new file mode 100644 index 0000000..778faa8 --- /dev/null +++ b/docs/archive/mcp-sdk-v2-migration-2026-09-27.md @@ -0,0 +1,15 @@ +# MCP TypeScript SDK v2 迁移记录 + +> Status: ARCHIVED +> Date: 2026-09-27 +> 迁移当时的一次性记录。当前依赖以 `package.json` 为准。 + +本分支使用官方模块化 MCP TypeScript SDK v2.1.0。服务端从 `@modelcontextprotocol/server` 导入 `McpServer` 和 `CallToolResult`,并从 `@modelcontextprotocol/server/stdio` 使用 `serveStdio`。stdio 帧格式和连接生命周期由 SDK 工厂 API 管理。 + +SDK 工具 schema 使用 Zod 4(`zod/v4`)。SDK v2 不支持 Zod 3;直接依赖使用 Zod `^4.2.0`,这是 SDK 所用 Standard Schema JSON Schema 转换的最低版本。测试客户端和可选的真实集成客户端使用 `@modelcontextprotocol/client` v2.1.0。 + +现有六个 MCP 工具的名称、参数形状、结果字段和 TaskManager 行为均未改变。`zcode_events` 仍是 Phase 7 增量增加的工具。`docs/ARCHITECTURE.md` 和 `docs/INTERFACES.md` 的契约边界保持冻结。 + +## 验证范围 + +本次迁移通过了 TypeScript 类型检查和构建。作为本次依赖/API 迁移的一部分,没有运行完整测试套件。 diff --git a/docs/archive/mvp-v0.3-2026-09-27.md b/docs/archive/mvp-v0.3-2026-09-27.md new file mode 100644 index 0000000..3865828 --- /dev/null +++ b/docs/archive/mvp-v0.3-2026-09-27.md @@ -0,0 +1,57 @@ +# MVP 0.3:模型选择与隔离工作区 + +> Status: ARCHIVED +> Date: 2026-09-27 +> 历史版本说明。当前工具输入以 `INTERFACES.md` 与 `src/mcp/schemas.ts` 为准。 + +日期:2026-09-27 + +本文记录在 V0.1 冻结接口之上的增量版本。V0.1 文件仍作为历史契约保留;MVP 0.3 的实际工具输入以当前 MCP schema 为准。 + +## 目标 + +Codex 能把有边界的开发任务交给本机 ZCode,选择本次任务使用的模型,在 Codex 中查看进度和结果,并审查隔离工作区中的改动。Codex 决定是否把审查通过的改动应用到用户工作区。 + +## V0.1 上的增量 + +TaskPackage 增加可选字段: + + model?: { + provider_id: string; + model_id: string; + reasoning_level?: string; + }; + +- 省略 model:保留 ZCode session 默认模型。 +- 设置 model:先检查新 session 快照。如果所选 provider/model 已经是当前模型,就保留 ZCode 当前有效选项;否则调用本机 ZCode session/setModel 并核对返回快照中的 providerId / modelId。ZCode 明确要求档位的模型需额外提供 reasoning_level,并映射为原生 options.reasoningLevel。 +- persistAsWorkspaceLastUsed 设置为 false:模型覆盖只作用于这个 session,不修改项目默认模型。 +- 选择结果通过 model_selected 事件持久化;session 快照另记录 runtime 报告的模型信息。 +- 目前不提供模型目录查询。Codex 可使用用户指定的 provider/model 标识;需要 reasoning_level 的模型应提供该字段。运行时不接受或无法确认选择时,任务失败,不静默回退。 + +`workspace` 始终是 Codex 项目根目录,也是 ZCode Desktop 项目归属。Codex agent 根据自己的任务指示决定是否需要 worktree;如果创建,则通过可选的 `worktree_path` 传入。Bridge 只校验两个路径,直接在 `worktree_path` 或(未提供时)`workspace` 启动 ZCode,不创建 Git snapshot,不创建、选择或删除 worktree。项目路径、执行路径和模式写入 workspace.json 并发出 workspace_ready 事件。续作复用原 session 和执行路径。 + +路径必须是已存在目录。Git worktree 提供文件目录隔离,但不是 OS 沙箱,也不能阻止命令访问仓库外路径。若直接执行于项目目录,ZCode 改动会直接出现在该目录。 + +## Codex 插件闭环 + +本仓库的 plugins/codex-zcode-bridge 提供 Master 工作流 Skill 和本地 stdio MCP 配置。Codex 按此顺序工作: + +1. 将用户已授权的开发目标、验收条件和适用测试整理为 TaskPackage。 +2. 用户指定模型时传入 model;否则省略并使用 ZCode 默认值。 +3. 调用 zcode_task 时始终传 Codex 项目根目录为 workspace;按指示准备 worktree 时再传 worktree_path。读取 zcode_events,记录项目路径和实际执行路径。 +4. 等待任务终态,再调用 zcode_result;从事件中确认实际模型和执行证据。 +5. 检查实际执行目录的 diff,独立运行验收。若使用 worktree,仅将审查通过且仍符合用户授权范围的改动接收到项目目录。 +6. 如有失败项,使用 zcode_continue 在同 session / 同执行目录续作;不符合要求时取消或停止。 + +模型报告、AgentReport 和 completed 都不代表代码审查通过。 + +## 版本与验证 + +- MCP server/package 版本:0.3.0。 +- app-server 模型设置路径已对照本机安装包确认,并通过 fake app-server 回归测试;真实 E2E 已用本机 `deepseek-flash` 完成任务并核对模型事件。此次请求的 provider/model 与 session 默认选择相同,所以沿用该 session 的有效 reasoning 选项;对其他模型的 reasoning_level 值尚未逐模型实测。 +- Bridge 不负责 workspace 快照和 worktree 生命周期;Codex 选择并准备 worktree。 +- app-server 是 ZCode 原生运行时入口,但仍属于随 ZCode 版本变化的本机协议;升级后须重跑模型选择、worktree、事件和续作验证。 + +## MVP 不包含 + +ZCode companion 插件、Hooks 审批、权限模式选择、模型目录 UI、ZCode Desktop 历史索引、自动化、并行 worker、自动合并和 OS 级沙箱。这些都不阻止上述单任务闭环。 diff --git a/docs/decisions/README.md b/docs/decisions/README.md new file mode 100644 index 0000000..34d78b3 --- /dev/null +++ b/docs/decisions/README.md @@ -0,0 +1,26 @@ +# 决策记录 / Decisions + +> Status: AUTHORITATIVE(仅指本索引) +> Last updated: 2026-10-07 + +本目录保存已批准的架构决策和带日期的决策记录。决策回答"为什么这样定",当前实现仍以 `ARCHITECTURE.md` / `INTERFACES.md` 为准。 + +## 现有记录 + +| 文档 | 状态 | 说明 | +|---|---|---| +| [roadmap-decisions-2026-09-27.md](roadmap-decisions-2026-09-27.md) | DECISION | 2026-09-27 的路线图与决策讨论。仍然成立的结论需要提炼进 `ARCHITECTURE.md` / `INTERFACES.md`;本文本身不是当前事实来源。 | +| [reliability-repair-plan-v2-2026-10-03.md](reliability-repair-plan-v2-2026-10-03.md) | DECISION | Bridge 可靠性修复计划。A/B 主要改动已实现;C/D 与宿主启动核验仍有未完成项。 | + +## 待补的 ADR + +以下主题目前只存在于权威文档的正文叙述里,没有独立决策记录。补写 ADR 时应以现有权威文档为准,搬运已有结论,不新增决策: + +- 生产执行路径使用 ZCode app-server,历史 CLI 路径只作 legacy 模块保留。 +- Manager 独占任务生命周期,worker 通过 attempt claim 进入执行。 +- 执行目录(worktree)由调用宿主准备,Bridge 不创建也不删除。 +- 本地 metadata、rollout、日志与 Desktop 索引只作观察面,不作控制面。 + +## 命名 + +新决策使用 `ADR-NNN-.md`,正文至少包含 Status、Date、Context、Decision、Rationale、Consequences。 diff --git a/docs/decisions/reliability-repair-plan-v2-2026-10-03.md b/docs/decisions/reliability-repair-plan-v2-2026-10-03.md new file mode 100644 index 0000000..ed6e1c9 --- /dev/null +++ b/docs/decisions/reliability-repair-plan-v2-2026-10-03.md @@ -0,0 +1,251 @@ +# Bridge 可靠性修复计划 V2 + +> Status: DECISION +> Date: 2026-10-03 +> 计划与决策记录,其中仍有未完成项(见文首状态行)。已实现的部分以 `ARCHITECTURE.md` 为准。 + +日期:2026-10-03。状态:A/B 主要改动已推送,其完整验收仍待补齐;新增收尾修复、C/D 和宿主启动核验尚未实施。下方第 10–17 节是本轮实施规格,与旧条目冲突时以新增规格为准。本文件不替代描述当前实现的 ARCHITECTURE.md,目前为被 Git 忽略的本地规划文件。 + +## 1. 目标与当前证据 + +解决:请求积压导致 300 秒工具超时、提交结果未知后重复派发、PID 与实际执行状态不一致、结果丢失后无法核对 ZCode 执行记录。 + +本会话观察:任务库有 188 条任务,无 queued/running worker,多达 5 个 Bridge MCP 服务共享 data root。现有每秒恢复 tick 进入同一进程内 promise 队列,再竞争跨进程 manager 锁。一次 recovery 经 recoverTasks、running 列表、损坏检查、queued 列表重复执行四遍全库读取;对应只读读取测量约 988–1053 ms。代码与测量支持恢复积压为主要原因;未读取存活进程内部队列长度,不将准确积压数量写成已证实事实。 + +CAD2BIM RM09_004 与 LumeCAE TASK_089 提交未获回执,当前任务目录不存在。不能仅据此证明仍在服务队列中的请求永远不会执行。TASK_088 的 1800000 ms 任务截止已被 task.json、events 和 outcome 证实,与之后的 300 秒工具调用超时分开处理。 + +最新源码交付:取消修复 bf23b8b 与 A/B 主要修复 34134b6 已推送;dsh f7997db 已推送并 pin 34134b6,两仓库对应提交 Windows/Ubuntu CI 均通过。这是对应提交的历史验证,不代表本轮新增规格通过;磁盘安装与存活服务的加载版本需单独核实。 + +## 2. 固定边界 + +- 公共 TaskStatus 的 queued/running/completed/failed/cancelled/waiting_for_master 保留。unknown 等作为附加执行观测字段,不把等待权限误映射为 waiting_for_master(后者是任务报告需要 Master 决策)。 +- task_id 为一次逻辑任务的稳定 ID;attempt 只由显式续作增加。工具超时不得自动增加 attempt 或更换 ID。 +- 不引入新 daemon、通用 observer 框架或第二套任务控制权。manager 管理接受/调度/所有权,worker 持有 ZCode stdio,adapter 解释原生协议,TaskStore 发布证据。 +- 共享能力进入 codex-zcode-bridge core;dsh 仅更新 pin、tarball、bundle 和宿主组合测试。 +- 本地 ZCode 文件只读、按需、字段白名单。Desktop task index 包含 Bridge 写入的镜像,不是独立执行权威。不修改真实任务库、Desktop 索引、rollout 或 CAD2BIM/LumeCAE 工作区来制造通过结果。 +- 活 owner 的 manager 锁不按超时强制删除。RPC、进程终止等待、大量历史读取不长期持有全局调度锁。 + +## 3. 统一状态与证据模型 + +为现有 zcode_status 增加可选 observation 字段;zcode_events 可附同样摘要,doctor 提供服务健康。旧客户端仍可只读 status。 + +| 字段 | 含义 | +|---|---| +| execution_state | not_started / starting / active / waiting_permission / waiting_input / cancelling / finished / unknown | +| result_state | pending / available / missing / invalid;执行已结束不等于报告有效 | +| worker_health | responding / unresponsive / exited / unknown;不以 PID 存在代替 responding | +| source | worker_event / native_snapshot / native_events / persisted_result / local_record / process_check | +| observed_at、stale、reason | 证据时间、是否过旧、已知或未知的理由;不能用状态文件更新时刻代替最后执行证据时间 | +| session_id、turn_id、attempt、last_event_seq | 当前 attempt 的关联身份;缺字段必须降低判断能力,不猜测 | +| recommended_action | wait / reply_interaction / fetch_result / reconcile / manual_review;未知不建议重派 | + +这不是对 ZCode 返回字段的假设,是 Bridge 对验证后证据的投影。execution_state=active 仅表示已证实活动 turn/工具,不保证模型持续产出。 + +判定规则: + +1. 同 attempt 的合法 result 优先确定任务结果;cleanup_unverified 仍保留执行目录和 slot,不能因结果存在直接释放。 +2. 新鲜 heartbeat 证明 worker 事件循环响应;模型等待、工具执行和权限等待分别由 runtime 证据解释。 +3. PID 存活但 heartbeat 过期:worker_health=unresponsive,execution_state=unknown;触发有界核对,不直接判失败或新建任务。 +4. worker 退出而 ZCode 仍可能运行:保留目录占用,核对 session/进程;不自动续作或重新发送 prompt。 +5. 已核验同 session、同 turn 的结束证据但缺 Bridge 报告:execution_state=finished、result_state=missing,进入结果恢复。只有通过既有报告校验且清理有证明,才能发布相应终态。 +6. session 快照 idle、日志静默、mtime 未变都不能单独证明当前 turn 完成或任务死亡。来源冲突时保留各来源和 reason,返回 unknown。 +7. 旧 attempt/旧 turn 的结束事件、快照、heartbeat 不得更新当前 attempt。未取得当前 turn 身份时不能依据泛化 session 状态补造当前任务结果。 + +## 4. 阶段 A:解除队列积压 + +主要文件:src/manager/task-manager.ts、src/store/process-lock.ts、src/store/task-store.ts、src/mcp/server.ts、src/host/stdio.ts。 + +- 同一服务 recovery single-flight:一次 recovery 正在等待/执行时,后续 tick 合并,最多一项 pending,不继续追加 promise。 +- 同 data root 使用一个共享 recovery 调度时隙/协调 owner;多个 MCP 服务不能各自每秒全库扫描。记录 next_due、owner token 与有界健康期限;移交通过短事务和 fencing 校验,不能删除活 manager 锁,也不能改变 worker 的永久 attempt 执行 claim。 +- 一次 recovery 建立同一份 task/status 快照,复用 running/queued/占用计算;先消除四遍扫描,再引入可重建的非终态索引。索引失效时回退重建,不能据缓存遗漏释放执行目录。 +- zcode_status/result/events 读取原子持久化快照,检查同 attempt,不等待全库 recovery。若需纠正状态,安排一次后台核对;返回当前证据与 stale/reconcile_pending。 +- 调度写操作保留串行化和跨进程锁,但队列有界。建议初始上限 32 项、未开始执行的排队预算 5 秒;超限返回 BRIDGE_BUSY,超预算返回 REQUEST_QUEUE_TIMEOUT。数值是拟定起点,需要阶段 A 压测后确认。 +- 请求排队超时后先标记失效,执行前再次检查;不得稍后建立任务目录或派 worker。计时使用进程单调时钟,不能把 MCP 客户端 300 秒超时当成服务器自动取消证明。 +- 取消先在短事务中记录意图、保留目录占用;进程/RPC 清理在锁外进行,提交时重新核对 attempt 与清理证据,避免清理等待堵住所有查询。 + +验收:188 条终态任务、5 个 MCP 服务运行恢复 tick 的隔离 fixture 中,恢复队列长度有固定上界;没有因积压导致的 300 秒等待;status 正常负载目标 1 秒内,故意争锁时 5 秒内得到响应或明确 busy;取消不受历史恢复积压阻塞。性能阈值用固定 fixture/并发数报告,不能只报一次手工 stopwatch。 + +## 5. 阶段 B:提交回执与幂等 + +主要文件:src/manager/task-manager.ts、src/store/task-store.ts、src/interfaces.ts、src/mcp/server.ts;两宿主 instructions/skill 同步行为说明。 + +- 验证后计算规范化任务 fingerprint;稳定排序对象键,保留数组顺序,包含 workspace/worktree、objective、requirements、路径限制、model、timeout 等执行语义字段。原始 prompt/敏感配置不写入调度诊断日志。 +- 同 task_id + 相同 fingerprint 返回首次持久化的 receipt,不 spawn 新 worker、不增加 attempt。receipt 已是 queued/running 的接受凭据,若任务如今已终态,仍回放原 receipt,并通过可选 replayed/current_status 与 status/result 告知当前情况,避免扩大原 receipt.status 类型。 +- 同 task_id + 不同 fingerprint 返回 TASK_ID_CONFLICT。旧任务若没有 fingerprint,可从原 task.json 做确定性比较;不能将“无法比较”视为相同。 +- receipt 与接受事实在同一锁保护的可恢复事务中持久化,再提交调度。崩溃恢复明确区分未接受/已接受未派发/已派发回执丢失;跨进程同时重试只能接受一次。 +- 可取消的排队请求超时前尚未接受:保证无副作用;已接受但客户端断联:任务保留,查询/相同 ID 重试返回事实。 +- Codex 遇超时使用原 ID 查询/重试;禁止因超时把 _001 改成 _002。查询也失败时标记 submission_unknown,停止追加派发,保留原工作区。 +- zcode_continue 属于新的执行意图,不能仅靠 task_id 去重:新增可选 operation_id,持久化 operation_id -> accepted attempt/feedback fingerprint。重试同操作不再增加 attempt;旧调用仍可用,但 instructions 要求可重试操作提供该 ID。 +- 相同 payload 的重复 task_id 从错误改为回放 receipt 是明确的兼容行为变更,需更新契约说明和宿主测试;不同 payload 继续明确拒绝,不允许覆盖原任务。 + +验收:提交后丢失回执、多个进程同时重试、首次持久化各阶段崩溃、提交终态任务、旧格式任务、同 ID 不同 payload、continue 回执丢失。每个逻辑任务/续作操作最多一个被 claim 的 attempt,超时队列请求不得延迟派发。 + +## 6. 阶段 C:worker 身份、心跳与证据发布 + +主要文件:src/worker/run-task.ts、src/manager/spawn-worker.ts、src/adapters/zcode-app-server-adapter.ts、src/store/task-store.ts、src/interfaces.ts。 + +- 每 attempt 的 observation.json 原子更新。包含 attempt、worker PID、启动身份、ownership token、heartbeat_seq/at、session_id、turn_id、最后 native seq、执行状态、最后观测来源;不包含模型推理、原始工具输出或凭据。 +- heartbeat 与模型输出独立,建议 3 秒发布一次、15 秒无进展视为过旧并触发核对;这些是拟定观测阈值,不是任务失败/自动重派期限。权限等待及长工具执行照常发布心跳。 +- OS 支持时记录进程启动身份并核对 PID 重用;无法可靠取得时显式 process_identity_unverified,不把 PID 对应任意存活进程作为强证明。 +- 写入使用 attempt/owner fencing,旧 worker 不能更新新 attempt。心跳不增加 events 全文,也不进入全局恢复锁。 +- session/turn 身份在获得时立即发布,状态快照和事件一致。没有 session ID 的 starting 是合法状态;超时需报告启动卡在哪一阶段。 +- 心跳异常只能触发诊断,不单独释放路径、清理存活 runtime 或自动重跑已执行 attempt。保留现有 pre-start 受 claim 保护的一次重拉例外。 + +验收:模型静默、长工具、等待权限、worker 事件循环卡住、worker 退出 runtime 仍活、PID 重用、旧 attempt 写入、Bridge 重启且 worker 存活、worker 正常结束时心跳停止。不能通过更改真实任务状态来验证。 + +## 7. 阶段 D:ZCode 原生核对与结果恢复 + +主要文件:src/adapters/zcode-app-server-adapter.ts、src/worker/run-task.ts、必要的窄接口与能力记录。 + +先验证当前安装 runtime。session/read、session/events、session/subagents 的方法存在或历史文档,不等于当前字段和恢复语义已验证。 + +验证矩阵:同一个活 app-server 内读当前 session;长工具/权限等待中读取;缺 seq/turnId 的兼容行为;Bridge 重启而 worker 仍持有 stdio;worker/app-server 退出后的持久化 session 读取;另一 app-server 读同 session 是否是实时状态或只是历史重建。不得用 session/resume 充当无副作用查询,也不能发送 prompt 来探测。 + +执行结构: + +- 活 stdio 由 worker 持有,优先由 worker 在同一连接上发有界核对并发布 observation;manager 读取发布结果。manager 不能假设可另建连接访问同一个运行中进程。 +- 正常靠推送事件;事件缺口/状态冲突时才核对。建议单次 RPC 预算 3 秒、同 session 一次 in-flight、失败退避 5–30 秒。不能每个 status 调用新建 app-server 或触发远程 RPC。 +- 全局 manager 锁外执行原生查询;结果回来后按 attempt/session/turn/token 核验并短事务提交。过期查询结果只留诊断,不改当前状态。 +- worker 不响应时,独立 read-only session 查询是否可行必须由验证矩阵决定。若不支持跨进程实时查询,返回 unknown,不实现基于新进程 snapshot 的虚假“仍在运行”。 +- 持久化 events/snapshot 的结束证据只对已经建立身份映射的 turn 有效。恢复完整报告需复用现有 parseAgentReport/normalize 和 cleanup 约束;无法补取报告时保留 result_state=missing 与人工审查指引,不合成 completed 报告。 +- 权限/input pending 由已验证的原生字段及现有 interaction 记录交叉核对;不自动回答,不从 session/messages 暴露完整对话。 + +门槛:提供安装版本/能力、脱敏请求响应样本、字段白名单、身份匹配规则、支持与不支持场景和超时行为。不能取得可靠证据的分支保持 unknown,而不是使用版本号推断支持。 + +## 8. 阶段 E:诊断与受控恢复上线 + +- 本地有界按 attempt 保存 app-server stderr,避免当前只在内存捕获。文件权限与保留上限明确;公开 MCP 只返回脱敏摘要/诊断位置,不默认返回原始内容。 +- 结构化诊断记录 request_id/task_id/attempt/操作、queue_wait_ms、queue_depth、recovery_duration_ms、lock_wait_ms、heartbeat_age、probe_result、退出 code/signal、取消/清理阶段、receipt_replayed。诊断不阻塞 worker,不把 heartbeat 全部写成公开事件。 +- doctor 增加 core/source commit、安装 bundle fingerprint、服务 PID/身份、data root、最近恢复耗时、pending recovery 和队列健康。区分“服务可响应”“运行记录新鲜”“已验证 runtime 能力”。 +- 本地日志/agent metadata 只在原生查询不够时按指定 session/attempt 点查。不得扫描全部 rollout 填入恢复队列;mtime、Desktop index、日志文字都不能单独确定终态。session/subagents 作为后续独立能力,不阻塞核心可靠性修复。 +- core 本地通过后,更新 dsh pin/tarball/bundle;在 Windows/Ubuntu CI 核验共享取消、并发接受、队列有界和恢复。明确区分本地 commit、push、CI、已安装缓存和存活进程加载版本。 +- 上线前备份任务库并只读列出活 worker/runtime 与未获回执的任务 ID。受控停止/重启 MCP 服务,保留 detached worker;启动新版本后逐 ID 核对 receipt/attempt/session,先恢复原任务状态再放开新派发。 +- 如果旧服务仍有未接受的超时提交积压,停止旧 MCP 服务使其内存队列失效,再由同 ID 接受事实核对决定重试。不能仅替换磁盘 bundle 就认为旧进程队列消失。 +- RM09_004、TASK_089 按原 ID 核对;TASK_088 保留既有 timeout 事实,代码接受依据与执行报告状态分开。需要续作时使用显式新 attempt/operation_id,不覆写旧超时证据。 + +## 9. 分阶段交付与停止条件 + +| 阶段 | 独立交付 | 接收门槛 | +|---|---|---| +| A | recovery 合并、共享节流、快读与队列失效 | 5 服务/188 任务隔离复现不再积压;旧 attempt/路径占用规则仍成立 | +| B | 可恢复回执、task/continue 幂等、instructions | 丢回执/并发/崩溃矩阵只有一次接受/执行 | +| C | attempt 身份与独立 heartbeat、状态投影 | 静默/等待/卡死可区分;旧 worker 无法写新 attempt | +| D | 当前 runtime 协议验证、同连接原生核对、结果恢复 | 脱敏证据支持每个采用分支;不支持分支清楚返回 unknown | +| E | 有界诊断、dsh 更新、CI 与安装/服务版本恢复 | 本地/CI/安装版本各自验证;原任务记录无非预期改写 | + +A/B 先解决重复派发风险,C/D 解决执行状态不明确,E 交付现场可诊断能力。A/B/C 的假 runtime 回归可独立开展,D 的真实协议证据不足不阻塞已验证修复,也不允许猜测完成。 + +每阶段:设计审查 → 最小实现 → 聚焦回归 → 适用完整回归 → 差异审查 → 本地独立提交。重叠文件串行集成,不自行派发到用户的两个项目会话。本文件没有授权向其他会话发消息或重新执行它们的任务。 + +停止条件:无法确定 attempt/turn 身份、runtime 只返回历史快照却被要求判实时、清理未验证、真实数据记录出现非预期变化、隐私白名单测试失败。停止相关恢复/新调度,保留证据;不能通过清空任务库、删除活 owner 锁、把 unknown 改成 failed 或提高 300 秒客户端等待来掩盖问题。 + +原计划 C/D、真实 native 查询验证与新增上线流程:NOT RUN。A/B 实施与完整验收状态见第 10、15 节。 + +## 10. 本轮证据、范围与实施顺序 + +共享基准:core 34134b6;dsh f7997db,依赖 pin 34134b6。新增工作不直接编辑插件缓存,不修改真实任务记录或交付物来制造通过结果。 + +mate90-vs-mate80-01 attempt 2:seq 232 为 turn_completed,随后 seq 233 报找不到 app-server PID 15748,最终 outcome=null、文件/测试数组为空。源码先 client.close()、后 parseAgentReport,确认清理异常可以丢掉已收到的报告并误分类为 zcode_nonzero_exit。 + +attempt 1:execution.claim/started.json 均指向 worker 38720,约 4 分 40 秒后被写为 worker_lost。没有判定瞬间的启动身份、心跳和查询记录,假阴性原因仍不确定,不能据此断言 Windows process.kill(pid, 0) 不可靠。当前源码/已安装 dsh 已有逐任务 recovery try/catch,不重复实现。 + +dsh Desktop 由 DeepSeek Harness.exe 执行 Bridge,worker 使用 process.execPath,环境白名单未传 Electron Node 模式;这是待复现的宿主启动风险,不认定为本次 respawn 根因。 + +实施顺序:R1 收尾/报告保全 → H dsh 启动核验 → C 身份/心跳/保守判定 → D0 原生能力实验 → D1 核对/恢复 → AB 补强 → E 两宿主交付/上线。D0 可提前独立验证;不支持的协议分支明确 unknown,不阻塞其他已验证阶段。 + +## 11. R1:收尾竞态与报告保全(P0,共享核心) + +主要文件:process-spawn.ts、zcode-app-server-adapter.ts、run-task.ts、normalize.ts、task-store.ts。 + +- 收到当前 session/turn 的结束响应后,先解析并原子保存私有 outcome-checkpoint.json,再清理 runtime。保存 attempt/owner/session/turn/seq、结束类型、可见响应/解析报告与截断标志;沿用输出限额,不保存隐藏推理、原始工具结果或凭据。报告被截断且无法校验时保持 invalid_agent_report。 +- 区分 turn 结果、报告校验、runtime 清理、Desktop 镜像同步。Desktop 同步失败记录诊断;Bridge 收尾异常不能泛化为 zcode_nonzero_exit,后者留给明确的执行失败。 +- client.close() 同连接 single-flight、幂等记录结果,覆盖 EOF、child close、取消、关闭超时、taskkill 与自然退出竞态。 +- taskkill 非零后有界核对同身份进程/已知树退出证据,不按 128 或中英文文案直接认定清理完成。根 PID 消失不证明后代全部退出;无法核验时保持 cleanup_failed。先明确既有进程树保证,再决定窄范围 Windows 追踪;Job Object 不是默认第一版前提。 +- 有效报告且清理 verified:按报告发布 completed/waiting_for_master。清理未验证:保留 failed + cleanup_failed、workspace/slot,并用现有 report_candidate 保留完整报告;checkpoint 保留可恢复结果。既有失败投影的空 files_changed/tests 不再意味着报告丢失。 +- manager/worker 使用同一份清理证据;清理未验证不能清空 runtime 身份。取消与结束并发重新核对结果、意图和身份,不吞掉已确认结果,也不无条件 completed。 + +验收:正常退出、taskkill/自然退出竞态、重复 close、取消/结束并发、后代仍活、查询失败、checkpoint 后 worker 崩溃、Desktop 同步失败、无效报告。Windows 真实进程 fixture 与两平台假 runtime 回归;成功报告不丢,未验证清理不释放占用。 + +## 12. H:dsh Desktop worker 启动核验与宿主适配 + +- 用隔离 fixture 复现 Electron execPath 启动,记录 shell/实际 worker PID、process.pid、argv、Node/Electron 版本、退出码、execution claim;不反复启动业务任务验证。 +- 普通 Node 正常复用执行器;Electron 宿主显式选择经验证的 Node 执行器或可用的 Electron Node 模式。执行器用可信绝对路径/argv,不在工作区搜索 executable。 +- 验证需要后才扩展 core 公共 host worker launcher 配置;模式由 dsh 提供,不扩大任意环境继承。可用时测试 ELECTRON_RUN_AS_NODE;安装 fuse 不允许时使用发现/配置的 Node,无法启动明确拒绝,不能把 GUI 当 worker。 +- 仅无永久 execution claim 的 attempt 可走现有一次 pre-start 替代启动。claim 已取得但 started.json 未写不能被当作从未执行;旧格式缺身份须保守处理。 + +验收:普通 Node、真实可用 Electron 模式或明确不支持、环境过滤、缺执行器、启动即退出、claim/started 间崩溃。此风险与本次误判的因果关系仍待证据。 + +## 13. C 的实施规格:所有权、独立心跳与三态判定 + +主要文件:spawn-worker.ts、run-task.ts、process-spawn.ts、task-store.ts、task-manager.ts、interfaces.ts、mcp/schemas.ts。 + +- 每 attempt 永久 execution claim 扩展为版本化身份:attempt、owner token、worker PID、启动身份、claimed_at。旧 claim 不重写;启动 shell 与实际执行 owner 分开记录。worker_started 表示 spawn 接受,worker_running 表示 owner 开始。 +- observation/checkpoint/最终结果写入在任务 state.lock 内检查 attempt + owner token。旧 worker/过期 probe/旧 turn 不得覆盖新状态;manager 判 worker_lost 前在同一短事务重读结果、claim、心跳与取消意图。 +- 私有 observation.json:schema_version、attempt/owner、worker/runtime 启动身份、heartbeat_seq/at、session/turn/native seq、执行阶段、最后核对证据。公开只投影白名单摘要,不暴露 owner token。 +- heartbeat 独立每 3 秒发布,15 秒未刷新标 stale 并触发核对,不判失败/重派;长工具/静默/权限等待仍刷新。finally 停止 timer,禁止把终态改回 running。进程内期限用单调时钟;跨进程序号/时间处理时钟跳变。 +- 进程探测返回 alive/exited/unknown,附观察时间、方法、启动身份、脱敏错误。OS 查询失败是 unknown,不是 exited。Windows 原生查询用于身份和退出核对,具体方法先验证并有界执行;不在全局锁内同步调用 PowerShell/tasklist。Linux 同样核验启动身份;缺身份明确 unverified。 + +| 证据 | 状态与行动 | +|---|---| +| 当前 owner 心跳新鲜 | running;worker responding;执行状态采用已验证 runtime 证据;保留占用 | +| 同身份 PID 活但心跳旧 | running;unresponsive + execution unknown;有界 probe;保留占用 | +| 查询异常、PID 重用、来源冲突 | running;unknown + reason;不重派,不释放占用 | +| worker 退出、runtime 活或未知 | running;recovery_required;核对 runtime/session,保留占用 | +| worker/runtime 均确认退出且无可恢复结果 | failed/worker_lost;保存判定证据;清理 verified 后释放占用 | +| 当前 checkpoint/合法结果存在 | 优先校验并恢复;清理未验证仍占用 | + +旧任务无 heartbeat 走 legacy 证据路径,证据不足 unknown;mtime、文件存在、session idle 不作终态权威。zcode_status 增加可选 observation:execution_state、worker_health、result_state、cleanup_state、observed_at/stale/reason、sources、attempt/session/turn、last_event_seq、reconcile_pending、recommended_action。心跳证明事件循环响应,不证明模型产出;waiting_permission/input 对照同 attempt 的 pending interaction;unknown 不建议换 ID 重派。 + +验收:静默、长工具、审批等待、事件循环卡死、worker 退出/runtime 活、PID 重用、查询失败、时钟跳变、旧 attempt/owner 写入、多 manager reconcile、Bridge 重启/worker 活、旧格式兼容、claim 后 started 前崩溃。 + +## 14. D 的实施规格:先验证能力,再同连接核对与恢复 + +### D0:真实协议能力门槛 + +session/read、session/events、session/subagents 是候选方法,不假定字段/恢复语义。记录当前 runtime/Node/Desktop 版本与路径、脱敏请求/响应、白名单、实时/历史属性、session/turn/seq 语义、错误与超时。session/subagents 仅记录能力,不阻塞本轮核心完成。 + +必须验证:同一活 stdio 读当前 session;工具执行/权限等待/静默时读;缺 turnId/seq;Bridge 重启但 worker 仍持连接;worker/runtime 已退出后历史恢复;另一 app-server 读同 session 的实时性;worker 不响应时可否跨进程只读。不能用 resume/send prompt 作为 probe。生成实验状态只能用隔离 fixture/测试 session;会调用模型的实验与 NOT RUN 项单独报告,不操作业务 session。 + +### D1:核对与恢复 + +- worker 持有原 stdio,adapter 提供窄只读 probe 并发布白名单 observation。正常靠事件;缺口/冲突/结果缺失才查。manager 发同 attempt/owner 的持久化 probe request;worker 不响应且 D0 无替代入口则 unknown,不为每个 status 启动新 app-server。 +- 单 probe 初始预算 3 秒,同 session 一项 in-flight,失败退避 5→10→20→30 秒。超时移除 pending RPC并拒收迟到响应;probe 不阻塞 heartbeat/结束/取消。全局锁外查询,回写核对 attempt/owner/session/turn/source seq。 +- 优先恢复 R1 checkpoint;无 checkpoint 仅采用 D0 已验证的当前 turn 结束证据与完整可见响应。复用 parseAgentReport/normalize 并校验清理;不通过文件存在/关键字补造 completed。 +- 只有结束证据没有完整报告:finished + result missing + manual_review。报告无效/截断:invalid_agent_report 与候选证据。来源冲突 unknown,不重复发送 prompt。 +- 同 checkpoint/turn 恢复幂等,不增加 attempt;取消/恢复并发按当前意图与已确认结束事实裁决,旧快照不能覆盖新取消或新 attempt。 +- 今后当前非终态任务可做 checkpoint 崩溃恢复。历史已失败任务不自动覆写;需独立可审查恢复操作,先归档旧结果/状态,记录来源,不改交付物。 + +验收:订阅缺口、异 session/turn、重复结束、RPC 超时/晚返回、不支持方法、只读副作用检查、取消并发、checkpoint 各写入窗口崩溃、完整恢复/报告缺失/无效/截断、审批等待、worker 不响应、仅历史快照。每个采用分支必须有 D0 证据。 + +## 15. A/B 未完成项补强与验收 + +- status/result/events 加 attempt 前后校验,避免 continuation 跨文件混读;快读不等 recovery/RPC,带新鲜度/核对状态。 +- recovery 复用一次扫描快照,消除重复历史读取;索引可重建且失败保守,不能遗漏占用。验证已有逐任务隔离与 pump 故障安全性。 +- 取消拆为短意图事务→锁外清理→身份/结果复核提交。写请求的 5 秒未开始预算涵盖本地排队加跨进程争锁总时间,用单调时钟;过期未接受请求不能晚派发。 +- task/status/workspace/submission receipt 各写入崩溃窗口可恢复;规范化处理 undefined/默认值/路径,避免假冲突/假等同。 +- continue operation_id 的 intent/attempt/receipt 可恢复,重点覆盖 nextAttempt 的 continue.json 已写、status 尚在旧 attempt 的窗口;同操作不能在后续终态后再次执行。 +- 两宿主 instructions 补充原 ID 重试、未知停止追加、续作 operation_id;仅 README 说明不算完成。 + +验收:188 条历史任务/5 个共享 data root 服务/运行队列混合 fixture。正常 status 目标 1 秒内,争锁写请求总 5 秒内响应或明确超时,队列容量 32,过期请求无副作用;并发接受及全部崩溃窗口最多执行一次。必须报告实际性能数据,不能以功能测试绿代替压测。 + +## 16. E:诊断、两宿主交付与上线 + +- 有界私有诊断:worker/runtime 身份、claim、心跳、查询方法/结论/耗时、清理阶段/OS exit、checkpoint/恢复来源、request/queue/lock/recovery 耗时。公开只给摘要,不按 heartbeat 刷 events,不暴露推理/凭据/原始工具输出。 +- doctor 区分 core pin/source、磁盘 bundle fingerprint、活服务加载身份、data root、Node/Electron 执行器、恢复健康与经验证能力;不能将安装文件更新等同活进程升级。 +- core 各阶段聚焦/适用全量验证后独立提交;dsh core:update 刷新 pin/tarball/lock,再重建 bundles,追加宿主 C/D/退出竞态/Electron 组合验证。Windows/Ubuntu CI 检查制品可重现。 +- 安装/重启为独立步骤:备份任务库,列活 worker/runtime/未知提交和旧 manager。活业务未收敛时不盲目升级/重启 worker;受控重启 manager 保留 detached worker,按原 ID 验证。 +- mate90-vs-mate80-01 保留历史失败与交付物验收,不继续执行刷绿。恢复报告先给可审查候选,不默认覆写历史失败;不向其他会话派任务或重跑它们的业务任务。 + +## 17. 新增交付门槛与状态 + +| 阶段 | 完成门槛 | +|---|---| +| R1 | 收尾竞态不吞报告;清理未验证不释放占用 | +| H | dsh Desktop/普通 Node 实际 owner 正确启动;不支持场景明确拒绝 | +| C | 心跳/身份/三态/fencing/投影矩阵通过,无静默自动重派 | +| D0 | 每个采用的原生方法/字段有当前 runtime 证据 | +| D1 | probe/恢复有界幂等;旧 turn/owner 不能改当前状态 | +| AB 补强 | 多服务压测及接受/续作崩溃矩阵通过 | +| E | core/dsh 本地与 CI 齐全;安装/服务版本另行确认 | + +本轮仅确定方案;新增 R1/H/C/D0/D1/AB补强/E 的实现与验收均 NOT RUN。身份/能力/清理证据不足时停止相关终态发布或恢复分支,保留 unknown 和占用;独立已验证阶段可继续交付。不自动修改缓存、真实历史任务或重启活服务。 diff --git a/docs/decisions/roadmap-decisions-2026-09-27.md b/docs/decisions/roadmap-decisions-2026-09-27.md new file mode 100644 index 0000000..a989dc4 --- /dev/null +++ b/docs/decisions/roadmap-decisions-2026-09-27.md @@ -0,0 +1,209 @@ +# Bridge 路线图与决策记录 + +> Status: DECISION +> Date: 2026-09-27 +> 当时的路线图与决策记录。其中仍然成立的结论应提炼进 `ARCHITECTURE.md` / `INTERFACES.md`;本文本身不作为当前事实来源。 + +日期:2026-09-27 +分支:`phase7-live-progress` + +本文汇总近期关于 Codex → ZCode 委派的讨论,覆盖安全性、运行可见性、ZCode Desktop 中的会话可见性、未确认事项和建议实施顺序。本文不修改冻结的 V0.1 接口。 + +## 当前基线 + +- MCP 使用官方模块化 TypeScript SDK v2.1.0 和 Zod 4。迁移后已通过类型检查和构建;迁移时未运行完整测试套件。 +- Phase 7 在原有五个 V0.1 工具之外增加 `zcode_events`。运行时提供相应信息时,事件会保存所选模型、可见的助手文本、工具生命周期摘要、用量和任务生命周期。 +- Bridge 当前只有一个全局 worker 槽位,并直接使用指定工作区。ZCode app-server 协议是私有且随版本变化的;本机观察结果针对 ZCode CLI 0.16.9。 +- `zcode_continue` 会尝试恢复保存的 ZCode session。CLI `--resume` 已单独验证;Phase 7 文档编写时,app-server 流式路径还未完成端到端验证。 +- 第一次真实 Phase 8 验证发现接线缺陷:`run-task.ts` 默认使用 `ZCodeAppServerAdapter`,但 `worker-main.ts` 显式注入了旧 CLI adapter,导致 app-server 进度事件被绕过。现已改为由 `run-task.ts` 创建 adapter 并安装事件持久化回调;完整 E2E 已通过,见文末记录。 +- `src/prompts/task-prompt.ts` 已根据类型化 `TaskPackage` 生成有界 worker 提示。插件 Skill 已说明委派、轮询事件、独立检查 diff 和验收结果。 +- `allowed_paths` 和 `forbidden_paths` 是提示约束。Direct 模式不会在操作系统层面强制执行这些路径限制。ZCode 任务目前使用 `yolo` 模式。 +- 开源审查阶段曾加入 unrestricted-execution opt-in;后续产品决策移除此 Bridge 专用开关,任务默认以 ZCode app-server `yolo` 模式启动。子进程环境白名单、常见凭据路径快照排除和本地任务数据权限加固仍保留,但都不把 `yolo` 模式或 Git worktree 变成 OS 沙箱。 + +## 设计讨论与决策 + +### Agent 协作契约 + +让以下三种职责保持清晰: + +1. **Master 指令**由 Codex 插件 Skill 承载:决定哪些已获用户授权的实现工作适合委派;架构和验收决定仍由 Codex 负责;ZCode 工作期间不允许并发修改同一工作区;结果必须独立审查。 +2. **任务契约**继续使用现有严格的 `TaskPackage`:目标、要求、允许/禁止路径、验收标准、测试命令和可选上下文。不要为同一契约再造模板或 schema。任何改变冻结 MCP 契约的新字段,都需要单独批准架构更新。 +3. **Worker 指令**继续由 prompt builder 生成简洁且稳定的策略:检查相关代码、遵守任务边界、不擅自重设计全局架构、运行适用检查、如实报告,并把超出权限的决定交给 Master。 + +这些指令可以改善行为,但不构成硬性的权限边界。真正的隔离需要沙箱、worktree 或运行时提供的权限门控。 + +### ZCode 原生控制能力与 Bridge 支持情况 + +用户提供的分析方向基本正确:模型、思考等级、执行模式、Hooks 和自动化属于 ZCode Agent/runtime 层;MCP 是 Bridge 的控制协议,本身并不提供这些控制能力。ZCode 当前官方文档确认产品界面提供模型选择、按模型区分的思考等级、四种执行模式、Hooks 和定时自动化。这只能证明产品层能力,**不能**证明本机私有 app-server 接受相同配置,也不能证明 Bridge 已映射这些选项。 + +| 能力 | 官方产品文档 | Bridge / 本机 runtime 状态 | 建议 | +|---|---|---|---| +| 覆盖模型 | ZCode Agent 和自动化可选模型;自动化模型留空时使用项目默认值。 | 尚未验证本机 app-server 请求。CLI 帮助和现有 runtime 证据不能证明主任务支持模型参数。 | 先探测创建 app-server session 的请求和持久化行为。省略时应使用 ZCode/项目默认模型;不要在 Skill 中写死模型。 | +| 思考等级 | ZCode 文档说明等级和默认值依模型而异;自动化可留空以继承项目默认值。 | Bridge 尚未验证。Subagent 的 `thoughtLevel` 仅有子 Agent 文档依据,不能假定它能配置主 session。 | 在模型选择探测后再验证;依据所选模型校验等级,省略时保留默认值。 | +| 权限/执行模式 | 产品文档列出 Ask before changes、Edit automatically、Plan 和 Full access。 | 本机 CLI 接受过 `--mode yolo`;各工具的具体权限语义及 app-server 配置能力尚未单独验证。Bridge 当前固定使用 `yolo`。 | 只有在确认 app-server 支持并验证文件/命令门控生效后,才加入类型化且保守的模式映射。 | +| Hooks | 官方文档说明 `PreToolUse` 可 allow/ask/deny,`PermissionRequest` 可处理权限请求,另有工具后置和 Stop hooks。 | Bridge 尚无 hook 决策通道或 runtime E2E。官方页面当前指出项目级 Hooks 会被忽略;还需核实本机版本支持的配置来源。 | 只有确认 app-server session 会触发 Hooks,且 Codex 能收到并返回持久、可关联的决定后,才将 Hooks 用于权限执行。否则不要宣称 Master 审批已强制生效。 | +| 工具/MCP 白名单 | ZCode 文档说明自定义 subagent 可配置工具和 MCP server。 | 这不能证明主任务 session 支持穷尽式白名单。 | 确认适用范围后再暴露。它与仅作为提示约束的 `allowed_paths` 是两种不同能力。 | +| 自动化 | 官方文档确认可配置计划、项目、权限、模型、思考等级,以及立即运行、暂停、编辑、删除和绑定当前 session。它受产品配额和本机可用性限制。 | Bridge 尚无自动化工具;私有 API、任务索引以及事件/结果映射均未验证。 | 等单次任务、权限、session 所属关系和 Desktop 可见性验证完成后再做。若存在受支持 API,优先调用 ZCode 原生调度,不另造 Bridge cron。 | +| Session 连续性 | 官方 Agent 文档描述多轮连续交互;本机 CLI `--resume` 已在 `docs/ZCODE_RUNTIME.md` 中独立验证。 | Bridge app-server 续作已通过下文双轮真实 E2E,session ID 相同。 | ZCode/runtime 升级后将此项保留为回归验收。 | + +统一的 `execution_config`(模型、思考等级、权限和工具)是合理的未来接口,但冻结的 V0.1 MCP schema 不包含它。在能力探测完成前继续冻结契约。之后再提出版本化的增量 schema,明确每个字段省略时的含义、支持值校验、续作继承规则,以及产品默认值和 Bridge 覆盖值之间的区别。不要根据仅适用于 subagent 的文档为主任务加入 `max_turns`。 + +### Master 决策与 runtime 权限 + +现有 `needs_master_decision` 和 `waiting_for_master` 表示报告完成后的升级请求,不会暂停正在运行的 ZCode 工具调用。当前 app-server adapter 会响应已观察到的 `session/requestRuntimePreferences` 请求,并拒绝其他入站 server 请求;它尚未把待处理的工具审批转发给 Codex。 + +在设计 `zcode_decisions` / `zcode_respond_decision` 前,先探测本机 runtime 的 `interaction/requestPermission` 行为与响应契约。如果实现,待处理决定需要可持久化的身份、超时和取消语义,以及不会冒充现有终态的状态模型。 + +### Session 连续性与运行中引导 + +续作路径会保存 session ID、调用 `session/resume`,并在恢复的 session 中发送反馈。此路径已纳入真实 E2E。 + +当前 ZCode 版本不应继续寻找 `session/steer` RPC:社区协议笔记称该接口在 app-server 0.16+ 已移除。本机 bundle 含有 `v4/command`;社区资料将 `sendText` 配合 `requestedDelivery: "guide"` 描述为在安全 turn 边界注入文本的 V4 路径。Bridge 尚未实现或验证该命令。它的 payload 和回退行为仍属于 runtime 专属假设,需实测。 + +### 文件变更与测试证据 + +当前事件流概述工具生命周期,但不提供已验证的逐文件 diff。`TaskResult.files_changed` 和测试报告只是 ZCode 的声明,Codex 必须检查实际工作区 diff 并独立运行验收。后续可根据 Git 或工作区快照生成精简的文件/测试摘要,并明确标记缺失证据。 + +### 上下文与成本预算 + +保留有界事件存储。为 Master 轮询提供精简摘要,只有在需要时再读取详情。分别统计返回给 Codex 的字符/字节数和 ZCode 实际提供的 token 用量;没有实际 usage 数据时不得根据字符数估算 token 或费用。 + +### ZCode Desktop 可见性 + +将 session 持久化与 Desktop 任务列表索引视作不同问题。社区资料称直接创建的 app-server session 可能已持久化,却没有登记到 `~/.zcode/v2/tasks-index.sqlite`。社区 `zcode-acp-server` 项目记录了同步 task-index 行的做法,让 ACP 创建的 session 出现在 ZCode App 历史中。建议先固定依赖版本做原型,或复用其公开说明中的逻辑,不要从零猜测私有 SQLite schema。该行为不是 ZCode 官方兼容承诺。 + +索引可见不代表 ZCode Desktop 在技术上只读。Codex 控制任务期间,在单一控制方行为经过设计和验证前,不要同时在 Desktop session 输入命令。共享持久化也不能证明并发控制安全。 + +### 工作区隔离与并行工作 + +在开启并行任务前,重要仓库应先支持 worktree/clone 隔离。当前 V0.1 Bridge 只有一个 worker 槽位,直接使用工作区,而且没有跨进程 manager 锁。安全并行需要工作区隔离、任务依赖图、冲突处理和可恢复的所有权记录,应放在单 worker runtime 验证之后。 + +## 建议实施顺序(本轮讨论稿) + +Phase 8A 的双轮真实 E2E 已完成。以下顺序吸收了 Hook 分析与 app-server 控制面分析,作为待讨论方案,不是新的冻结架构或接口承诺。 + +| 顺序 | 阶段 | 范围 | 完成证据 | +|---|---|---|---| +| 1 | Phase 9 — app-server 能力普查 | 静态检查本机 3.14.3.7762 / CLI 0.16.9 的 command、request、event 和 app-server dispatcher;覆盖 session、配置、steering、task 注册、usage、subagent、compact/goal、Automation。区分官方产品功能、本机协议出现、Bridge 端到端验证三种证据。 | [中文能力矩阵](../archive/appserver-capability-matrix-2026-09-27.md) 已记录入口、主要方法、事件、证据等级和版本边界。静态普查完成;未知参数和行为留待隔离探测。**本阶段完成。**(该矩阵已被 2026-10-05 的能力探针取代,见 `docs/README.md`。) | +| 2 | Phase 10 — 模型与思考等级映射 | 先单独探测模型及思考等级配置;省略时继承 ZCode 默认值。权限模式只做协议探测和隔离环境验证,暂不向日常任务开放高权限选项。 | 每项参数分别验证设置请求、session 快照、实际模型元数据及续作继承;不支持项有明确回退。 | +| 3 | Phase 11 — ZCode companion 插件 / Hook 原型 | 保留 Codex 插件作为 MCP 与 Master Skill 入口;另做最小 ZCode 插件实验,验证 app-server 创建的 session 是否触发 Hooks。先记录事件,再在临时仓库测试确定性 deny;不把 Hook 当作 OS 沙箱。 | Hook 输入字段、工具名、路径/命令信息、触发时序、超时和返回结果均有本机证据;绕过路径(特别是 Bash)被明确列出。 | +| 4 | Phase 12 — 工作区隔离 | 以 Git worktree/clone 承载可写任务,定义创建、回收、检查、应用和丢弃;把 `allowed_paths` 与 `forbidden_paths` 纳入规范化路径判定。 | 未显式 apply 前,主工作区保持不变;清理与崩溃恢复可验证。 | +| 5 | Phase 13 — Master 审批往返与策略执行 | 在 Hook 能同步阻止工具且可关联 task/session/tool call 的前提下,设计持久 decision inbox 和 MCP 查询/响应工具。区分 `PreToolUse` 的 ask/deny 与 `PermissionRequest` 的用户确认流程;为等待状态、超时、取消、Bridge 重启定义语义。 | Codex 能看到具体工具请求并批准/拒绝;决定回到同一 Hook 调用;超时、取消或 Bridge 不可用时 fail-closed,不会静默放行。 | +| 6 | Phase 14 — ZCode Desktop 可见性 | 验证 app-server session 与 task index 的关系,研究官方/社区支持路径;先只读调查,之后才在隔离数据环境做索引原型。 | 临时任务在 Desktop 历史中可见且完成后可打开;不与 Codex 并发控制同一 session。 | +| 7 | Phase 15 — 运行中控制与 Agent 原生能力 | 根据 capability matrix 选择实现 `sendText`/steering、`/goal`、`/compact`、subagents 等。逐项验证请求、事件、状态和续作,不因 Desktop 有该功能就推断 app-server 也暴露。 | 每项能力都有受支持的调用路径、确认事件、取消/恢复语义和失败回退。 | +| 8 | Phase 16 — ZCode 原生 Automation | 先确认是否存在可调用且受支持的 API;若只有 UI/私有存储路径,暂缓 Bridge 自动化工具。优先调用 ZCode 原生 scheduler,不另建 cron 系统。 | 创建、立即运行、暂停、恢复、删除及运行历史均可关联到项目/session/task,并有持久化证据。 | +| 9 | Phase 17 — 并行委派 | 工作区隔离、decision 状态和跨进程协调稳定后,再增加 worker 槽位及任务依赖。 | 并行任务能在重启后恢复,不会双重调度或写冲突。 | + +### 并行 worker 原型(2026-09-28) + +在 `codex/parallel-multi-project` 分支实现可选的单进程多 worker 调度:`ZCODE_BRIDGE_MAX_CONCURRENT_WORKERS` 接受 1–8;当前代码默认 8,不同或隔离执行路径可并行,同一或嵌套执行路径互斥。该保证仅限单个 Bridge 进程;跨进程锁尚未实现,因此共享数据目录的多个 Bridge 进程不应并行运行。排队仍按创建时间排序,遇到被占用路径时会跳过该项以使用空闲槽位。 + +2026-09-28 的真实 E2E 使用 Coding Plan `GLM-5.3-Flash` 完成了四次调用:两个独立项目同时运行,以及同一项目的两个真实 Git worktree 同时运行。每对任务均观察到两个 worker PID 同时处于 running、不同 session ID、模型选择正确、各自在指定目录生成唯一文件;四个任务均发出 `desktop_task_registered`,验证了并发 task-index 登记没有报错。临时任务、工作区和测试仓库已清理。该测试没有确认 Desktop UI 是否刷新显示,也没有并发切换不同 provider/model。 + +当前原型没有实现跨 Bridge 进程的全局调度锁,因此并发保证仅适用于单个 MCP server 进程。多 worker 会启动多个独立 app-server;account provider 配置同步的并发竞争及多 provider/model 并行尚未验证。默认保持 1;在跨进程协调与 provider 切换验证完成前,不要提高默认值或让多个 Bridge 进程共享数据目录。 + +### 两份分析的关键判断 + +- **同意控制路径的区分**:ZCode 的 MCP 是把外部工具接入 ZCode Agent;本项目的 Codex MCP 是 Codex 到 Bridge 的 API;Bridge 到 ZCode 使用本机 app-server/runtime。模型 Provider API 只提供模型推理,不能替代 Agent 的文件、终端、权限、MCP 和 session 行为。[ZCode MCP 文档](https://zcode.z.ai/en/docs/mcp-services) 将 MCP 描述为向 Agent 接入外部能力;[Agent Framework 文档](https://zcode.z.ai/en/docs/agent-framework) 描述的是 ZCode 产品中的 Agent 能力,并未因此承诺公开稳定的第三方 app-server SDK。 +- **Phase 7 的进度流已实测**:最近的双轮 E2E 已经证实 Bridge 可从本机 app-server 接收模型可见文本、工具生命周期、模型元数据和 turn 事件,并在同一 session 续作。这只证明已测试的接口,不证明 model override、permission mode、task indexing、steering 或 Automation 都对第三方稳定开放。 +- **Hook 不是“每个事件都会暂停”**:官方定义 Hook 为本机子进程协议;`PreToolUse` 可对工具调用 allow/ask/deny,`PermissionRequest` 在权限结果需要用户确认时触发,`PostToolUse` 在工具成功后运行,`Stop` 可让模型继续有限轮次。只有特定事件有门控作用。[Hooks 官方说明](https://zcode.z.ai/en/docs/hooks) +- **`ask` 不等于 Codex 已拿到审批**:官方 Hook 的 allow/deny/ask 和 `PermissionRequest` 输出是 ZCode Hook 协议。如何将挂起决定交给 Codex、等待期间是否保持工具调用、Hook 超时行为,必须在本机实测。Bridge 需要独立的 decision identity、MCP 往返、超时、取消、崩溃恢复和非终态状态模型;当前 `waiting_for_master` 是终态报告,不能直接复用为暂停中的审批状态。 +- **Hook 增强的是工具门控,不等于 OS 沙箱**:即使 `Edit` / `Write` 的 Hook 能拦截越界路径,Bash/终端仍可能通过命令或脚本修改文件。需要确定命令策略并使用 worktree/clone 限制影响面;不要声称只加 Hook 就“根本改不了”。 +- **双插件架构值得验证,但不必先拆仓库**:Codex 侧插件继续负责 MCP 配置和 Master Skill;ZCode 侧 companion 插件可以打包 Hooks、Skills、Commands、Subagents 和 MCP 配置。[ZCode 插件文档](https://zcode.z.ai/en/docs/plugin) 确认了这些组件类型。先做本地原型,验证 app-server session 能否加载、Hook 能否获得 task/session 上下文;验证后再决定是否以同一仓库的两个插件包发布。 +- **产品功能不等于控制 API**:官方确认 `/goal` 与 `/compact` 是 ZCode 命令能力,[Goal 文档](https://zcode.z.ai/en/docs/goal) 描述了 goal 循环。但 command palette、Desktop Automation 或 Subagent 配置存在,不代表 app-server 有等价第三方请求。Automation 也应优先复用 ZCode 原生计划能力;[官方自动化文档](https://zcode.z.ai/en/docs/automations) 描述了 UI 功能及其本机运行限制,但没有证明公开调度 API。 + +因此,Phase 9 的静态能力普查已完成。接下来建议推进 **Phase 10:模型与思考等级映射探测**,并在隔离工作区逐项验证请求、响应和效果。权限模式暂不向日常任务开放;先等 Hook 和工作区隔离策略明确。V0.1 接口继续冻结;任何 `execution_config`、`policy` 或 decision 工具都应在证据齐备后另提版本化方案。 + +## MVP 实施期间补充的参考结论(2026-09-27) + +用户提供的社区项目梳理有助于减少协议摸索,但它描述的是不同项目、不同提交和运行版本的观测,不能直接当成本机 ZCode 3.14.3.7762 / CLI 0.16.9 的兼容承诺。 + +| 参考项目 | 本项目采用的参考点 | 本项目不据此推断 | +|---|---|---| +| [william0wang/zcode-acp](https://github.com/william0wang/zcode-acp) | 将真实 `zcode app-server --stdio` 作为运行时;关注 backend client、事件转换、交互处理和版本兼容记录的分层。其 README 明确标记 CLI 0.16+ 对 steer/rewind 的变化。 | ACP 是 Codex 的必要入口;其列出的 mode、thought、权限、task-index、quota 或 remote 功能在本机都可用。该项目仍在开发,需针对固定 commit 和本机 runtime 复核。 | +| [csuftt/zcode-jetbrains-plugin](https://github.com/csuftt/zcode-jetbrains-plugin) | 作为 app-server 消息和更完整 UI 活动映射的二级研究资料。 | 逆向协议文档等同官方规范;其 UI、V4 API 或权限流程是 MVP 必需项。 | +| [KyoMio/zcode-executor](https://github.com/KyoMio/zcode-executor) | 关注其隔离 worktree 与 Git diff 验收和权限门控的工程实践。 | 社区测试中出现的 `session/requestRuntimePreferences` 或 `session/create` 参数可直接复制到当前 Bridge。 | +| [jpalmae/zcode-acp](https://github.com/jpalmae/zcode-acp) | 参考 transport / protocol / adapter 分层,避免后续把 stdio、JSON-RPC 和任务语义混成一层。 | 需要引入 ACP 或照搬 Rust 结构。 | +| [ZhouXiaolin/zcode-provider](https://github.com/ZhouXiaolin/zcode-provider) | 将来研究模型发现和 `session/setModel` 工作流时作为补充线索。 | 本 MVP 需要新增模型目录 API;当前 MVP 支持明确 provider/model ID,并在运行时核验实际选择。 | + +`zcode-acp` 的公开 README 将自己定位为面向 ACP host 的适配器,并称其启动官方 ZCode app-server、转换事件和交互请求;其 README 还列出当前支持范围及 0.16+ 的 steer/rewind 边界。这足以把它列为首要**研究参考**,但不是本项目依赖或协议规范。本文根据其公开仓库首页作范围筛选;在复用任一具体实现前,还需固定 commit、核对许可证和代码,并在本机版本验证行为。 + +对当前 MVP 的直接影响只有两项:继续使用真实 app-server 而非直接调用模型 API;在已有功能扩展时逐步分离 protocol client、任务 adapter 和 Codex MCP 层。当前实现的模型选择、事件流、续作和 Git worktree 已分别由本机 runtime 检查、回归测试或真实 E2E 验证;尚未验证的思考等级、权限交互、steering、task-index 和桌面历史可见性仍留在后续研究列表。 + +## 参考资料与兼容性说明 + +- [ZCode ACP server](https://github.com/william0wang/zcode-acp):社区 ACP adapter、task-index 同步、事件转换、权限和 runtime 兼容性记录。Apache-2.0,仍在积极开发。 +- [ZCode ACP 协议说明](https://github.com/william0wang/zcode-acp/blob/main/docs/PROTOCOL.md):记录 `session/steer` 在 0.16+ 移除的社区观察。 +- [ZCode app-server V4 协议说明](https://github.com/csuftt/zcode-jetbrains-plugin/blob/master/docs/zcode-appserver-protocol.md):社区对 V4 conversation 和 command 方法的逆向研究。 +- [Agent Client Protocol Codex adapter](https://github.com/agentclientprotocol/codex-acp):可参考事件、权限和 session 设计,但后端仅适用于 Codex。 +- [polyagent-mcp](https://github.com/JaimeJunr/polyagent-mcp) 与 [cc-plugin-codex](https://github.com/hex1n/cc-plugin-codex):可参考 Master 路由、类型化任务控制、输出预算和状态门控流程。 +- [ZCode Agent](https://zcode.z.ai/en/docs/agents) 与 [安全确认](https://zcode.z.ai/en/docs/safety-confirm):官方产品层的模型、思考等级和执行模式控制。 +- [ZCode Hooks](https://zcode.z.ai/en/docs/hooks):官方 Hook 事件、决策输出及配置范围注意事项。 +- [ZCode Automations](https://zcode.z.ai/en/docs/automations):官方调度控制、session 绑定、可用性限制和运行历史。 +- [ZCode Subagents](https://zcode.z.ai/en/docs/subagents):说明 subagent 的 `thoughtLevel`、工具白名单和 `maxTurns`;没有单独证据时,不要将其视为主 session 控制项。 + +以上 app-server 和 task-index 细节均为社区观察或本机特定版本证据,不是稳定的官方 API 承诺。升级 ZCode 后需要重新检查。 + +## 回归初心后的 MVP 优先级 + +目标收敛为:Codex 能把用户已授权的开发任务交给 ZCode,选择本次任务的模型,在 Codex 查看执行过程,审查改动并决定是否应用。 + +### MVP 必须具备 + +| 能力 | 当前状态 | 验收边界 | +|---|---|---| +| Codex 插件作为 Master 指令与 MCP 入口 | 插件 0.3.0 已安装并启用;MCP stdio 工具清单和插件缓存配置已验证 | Codex 能按 Skill 调用 Bridge,而不依赖 ZCode 插件。新线程加载新 Skill/MCP 工具。 | +| 单任务派发、排队、取消、续作、持久化 | 已实现 | Bridge 重启后能恢复状态;续作恢复原 session 和 worktree。 | +| Codex 中的状态、模型可见输出、工具活动和结果 | Phase 7 已有,真实双轮 E2E 通过 | 任务期间可用 zcode_events 增量读取,终态由 zcode_result 提供。 | +| 按任务选择模型 | 已接入任务级 provider_id/model_id、用户默认 provider/model 与优先级;runtime snapshot 校验实际模型 | 任务级覆盖用户默认;两者都未配置时继承 ZCode 默认。默认值及跨模型切换仍需本机实际模型请求验证。 | +| 执行模式可配置 | 已实现新建 session 的 `plan`/`build`/`edit`/`yolo` 配置,续作时调用 `session/setMode`;默认仍为 `yolo` | Bridge 报告 runtime 配置值;逐项权限交互、模式权限语义和 ask 回传尚未 E2E 验证。 | +| ZCode Desktop 会话可发现 | 已实现 best-effort tasks-index 登记和运行/完成/失败状态同步;本机 schema/status 已只读检查 | Bridge 数据库写入不会向 Desktop 进程发实时事件;需刷新列表。已取消状态清除任务索引活动状态,因为 schema 没有 `cancelled` 值。 | +| 隔离可审查的工作区 | 已实现 Git worktree 和 dirty snapshot | 源工作区保持不变;改动留在任务 worktree,Codex 审查后才应用。不是 OS 沙箱。 | +| Codex 独立审查与验收 | 插件 Skill 已说明;真实 MVP E2E 独立检查唯一改动文件、源目录、禁改文件和测试 | Agent 自报不能替代实际 diff、文件和测试检查。 | + +### MVP 暂不需要 + +ZCode Companion 插件/Hook、Master 审批往返、运行中 steering、goal/subagents、Automation、并行 worker、自动合并、模型目录 UI 和 OS 级沙箱。模式配置已提供,但权限审批往返不是它的等价替代。 + +### 推荐顺序 + +1. **已完成**:app-server 指定模型检查、dirty Git 工作区快照和隔离执行的真实 E2E。 +2. **已完成**:个人 marketplace 中 Codex 插件 0.3.0 安装并启用;配置包含本机 MCP 入口和有效 provider 配置路径。 +3. **已完成**:独立 diff/文件/测试验收和中文文档状态更新。 +4. 真实使用验证用户默认模型、任务级覆盖、非默认 `ZCODE_HOME` 和每种执行模式;不默认提交真实任务或更改本机 Desktop 数据库。 +5. 根据真实使用反馈再决定 Hooks 审批往返、运行中 steering、goal 与并行。 + +V0.1 冻结文件保留为历史契约。模型选择和 worktree 是版本化的增量,记录于 docs/MVP_V0.3.md;不要把它们误标成 V0.1 的原始能力。 + +## 真实 E2E 记录 + +**状态:通过。** 2026-09-27 使用本机 ZCode app-server,并通过插件 MCP 配置运行。前两次诊断发现 `worker-main.ts` 绕过了进度事件持久化回调;改为由 `run-task.ts` 创建 adapter 后,完整两轮 E2E 通过。 + +- 首轮和续作均为 `completed`;续作 attempt 为 2,返回了与首轮相同的 session ID(具体值已从公开文档移除)。 +- ZCode 报告所选模型为 `deepseek-flash`。事件中观察到可见的 `model_output`、`model_tool_call`、`tool_status`、`session_ready`、`turn_started`、`turn_completed` 和 `runtime_state`。两轮合计 74 个事件、16 个可见文本事件、35 个工具生命周期事件和 2 个 turn usage 事件。运行时提供了 usage;本文不据此估算 token 或费用。 +- 独立运行的 Python `unittest` 首轮通过 6 项,续作后通过 18 项。工作区仅包含 `README.txt`、`calculator.py`、`test_calculator.py`;README 内容未变。 +- 任务到达终态后,临时工作区和 Bridge 数据目录均已删除。一次性 E2E 驱动也已从仓库移除。本次没有验证 ZCode Desktop 历史索引、Hook 权限往返或模型/思考等级/权限覆盖。 + +这完成了最重要的 Phase 8 运行证明:实际进度经 MCP 事件工具可见,续作恢复了同一 session。上文讨论的官方产品控制能力仍需单独探测,没有因本次 E2E 而宣称已由 Bridge 实现。 + +### MVP 0.3 模型与 worktree E2E(2026-09-27) + +**状态:通过。** 使用安装的 ZCode app-server,通过 stdio MCP Bridge 启动一个临时 Git 项目。显式提交模型 `deepseek-flash`(provider/model ID 见任务输入);session 快照报告目标模型,任务在 Bridge 生成的 worktree 中完成。 + +- ZCode 创建唯一目标文件 `calculator.py`;源工作区未改变,预置的 `test_calculator.py` 字节内容未改变,worktree 实际变化只有 `calculator.py`。 +- ZCode 报告的三项测试由 Codex 在隔离 worktree 中独立重跑,3/3 通过。终态为 `completed`,任务报告与实际 Git 文件状态一致。 +- 临时任务仓库、task worktree 和 Bridge data root 均由 E2E 驱动清理;没有提交或修改真实项目文件。 +- 本次指定模型恰好是新 session 已选中的当前模型,所以 runtime 核验并保留其 reasoning 选项;`session/setModel` 对不同 provider/model 的分支由 fake app-server 测试覆盖,跨模型真实切换及各模型可用 reasoning_level 尚未完成验证。 +- 首次运行要求 Bridge 配置通过 MCP 环境显式传入 provider JSON 路径;缺失时 resolver 正确拒绝了本机的已知无效 stub。E2E 临时仓库另固定 `core.autocrlf=false`,确保隔离保护按字节比较。最终 E2E 通过。 + +### 社区桥接参考补充(2026-09-27) + +用户提供的 [tizerluo/zcode-open-bridge](https://github.com/tizerluo/zcode-open-bridge) 分析经仓库 README、ACP bridge 源码、0.16.1 升级记录和 MIT LICENSE 复核。它与本项目同样驱动 app-server,但不是协议规范,也没有复制其代码。 + +- **采用能力探测思路,不以版本号推断能力。**本 Bridge 使用 app-server 实际返回的 session model catalog,并校验 `session/setModel` 后的 snapshot;未覆盖的协议方法仍应按实际请求结果报告不支持。当前无需在启动时额外发送会改变 session 的“探测”请求。 +- **不把只读审查工具黑名单用于开发任务。**参考项目将 `--disallowed-tools` 用于 review:禁用写入和命令执行以保障只读;本项目委派目标就是开发,照搬会移除必要能力。以后若新增审查任务,可单独设计只读 profile,并验证 Node REPL 等替代执行通道。 +- **provider 环境自愈无需复制。**Bridge 以白名单构造 ZCode 子进程环境,不继承父进程的 `ZCODE_BASE_URL`、模型或凭证覆盖;账号 provider 从已选择的 ZCode 配置与 app-server 注册流程解析。应继续保留 hosted account provider 路径,不改成把 API key 注入进程环境。 +- **0.16.x 协议事实作为兼容性线索。**反向 RPC 必须应答、事件订阅和方法删除等变化需要固定到本机 runtime 验证;`session/send` 是否等同可靠 steering 不因社区报告而视为本机已支持。 +- **Auto compact 暂不实现。**ZCode 官方文档称 Agent 默认会在上下文窗口耗尽前自动压缩,且没有用户可配置开关;社区 ACP 的额外 threshold 是在成功 turn 后按 `contextUsed` 调用 `session/compact`。本 Bridge 当前没有经过验证的稳定上下文占用阈值事件,也没有证明手动 compact 相比 ZCode 内建机制的收益。避免重复压缩、丢失任务约束或增加未经验证的协议调用;若以后多轮续作出现上下文问题,再先确认 usage 投影字段及触发语义。 + +参考:[zcode-open-bridge README](https://github.com/tizerluo/zcode-open-bridge)、[0.16.1 升级记录](https://github.com/tizerluo/zcode-open-bridge/blob/main/docs/upgrade-0.16.1-spec.md)、[ZCode auto compaction 说明](https://zcode.z.ai/en/docs/configuration)、[社区 ACP threshold 配置](https://github.com/william0wang/zcode-acp)。这些资料有利于选研究方向,不是本机 0.16.9 的 API 承诺。 diff --git a/docs/research/desktop-task-refresh-2026-09-28.md b/docs/research/desktop-task-refresh-2026-09-28.md new file mode 100644 index 0000000..9061a70 --- /dev/null +++ b/docs/research/desktop-task-refresh-2026-09-28.md @@ -0,0 +1,208 @@ +# ZCode Desktop 任务列表刷新机制调查 + +> Status: RESEARCH +> Date: 2026-09-28 +> 研究结论,不代表当前实现。观测版本 ZCode Desktop 3.14.3.7762 / CLI 0.16.9。 + +调查日期:2026-09-28 +调查分支:`phase7-live-progress`(本文档为 Phase 6/7 前置研究交付物) + +## 0. 证据等级与来源 + +| 等级 | 含义 | +|---|---| +| **VERIFIED_SOURCE** | 官方源码 `zai-org/ZCode`(克隆于 `D:\zcode-official-src`,`package.json` version **3.14.3**)中直接读到,含文件与符号名。 | +| **VERIFIED_RUNTIME** | 在本机(ZCode Desktop 3.14.3.7762 / CLI 0.16.9)只读检查实际数据文件或进程行为证实。 | +| **INFERRED** | 由 VERIFIED 事实按代码逻辑推导,未单独运行验证。 | +| **UNVERIFIED** | 有待验证的开放问题。 | + +版本对齐说明:官方仓库 `package.json` version `3.14.3` 与本机安装的 Desktop `3.14.3.7762` 主版本一致(VERIFIED_RUNTIME),因此上游源码与本机分发实现高度对应;升级后需按第 7 节清单重新核验。 + +--- + +## 1. 官方架构(VERIFIED_SOURCE) + +Desktop 是 Electron 应用,任务列表逻辑分三层: + +- **Renderer**(`packages/ui`、`packages/desktop/src/renderer`):React + 自研 zustand/module store(无 react-query/rtk-query)。 +- **Host**(`packages/desktop/src/host`,Electron utilityProcess):持有全部服务实现;Renderer 通过 Electron MessagePort RPC 调用(`InternalChannels.ServicePort = "zcode:service-port"`,`packages/shared/src/channels.ts`;`packages/rpc/src/proxy-channel.ts` 将 `onDynamic*` 方法自动识别为事件流)。 +- **Agent runtime**:Host 按 workspace 以 stdio 子进程方式拉起 `zcode.cjs app-server --stdio`(`packages/services/src/zcode-agent/zcodeAgentProcessManager.ts:369`)。**仅 stdio,无外部可连接端口**;本 Bridge 的 app-server 与 Desktop 的 app-server 是两个独立进程。 + +两个关键服务: + +- `IZCodeTaskService`(channel `"zcode-task"`,接口 `packages/services/src/session/zcodeTaskService.ts:199`,实现 `packages/services/src/zcode-agent/zcodeTaskServiceAdapter.ts`)——task 索引的读写面。 +- `IWindowControllerService`(channel `"window-controller"`,实现 `packages/desktop/src/host/windowHostControllerService.ts`)——为 Renderer 聚合任务列表投影并推送帧。 + +## 2. 任务列表调用链(VERIFIED_SOURCE) + +Renderer 侧有两条并行数据面(均在 `packages/ui/src/WorkspaceSidebar.tsx` 挂载): + +### 数据面 1:v4 Controller 投影(Timeline / Pinned / Archived 区块) + +```text +WorkspaceTimelineTasksSection.tsx / WorkspacePinnedTasksSection.tsx / WorkspaceArchivedTasksFlatSection.tsx + ↓ useGlobalTaskList() (packages/ui/src/hooks/useGlobalTaskList.ts) + ↓ windowControllerTaskListRegistry (packages/ui/src/v4/windowControllerTaskListRegistry.ts, useSyncExternalStore) + ↓ IWindowControllerService.listTaskList / subscribeControllerV4 / onDynamicControllerFrame + ↓ [MessagePort RPC] + ↓ createWindowHostControllerRuntime (packages/desktop/src/host/windowHostControllerService.ts:137) + listTaskList → refreshSource → readSourceTaskIndex(:345) + → zcodeTaskService.listTasks / listPinnedTasks / listArchivedTasks ← 每次都直读 SQLite + → windowHostControllerProjection (packages/desktop/src/host/windowHostControllerProjection.ts:196) + topics "controller/tasks-index"、"controller/workspaces";delta: task.upserted / task.removed +``` + +注意:Renderer 的 `list()` 只有在 version key(`taskListVersionSignature` / `workspaceSourceGenerationSignature` / `manualRefreshSerial`,`windowControllerTaskListRegistry.ts:56-65`)变化时才重新 RPC;纯 activity 帧复用缓存。 + +### 数据面 2:workspace 行列表(默认任务列表主视图) + +```text +WorkspaceSidebar.tsx:630 + ↓ useWorkspaceTaskLists() (packages/ui/src/hooks/useWorkspaceTaskLists.ts:195) + ├─ useWorkspaceSessionsIndexItems() (packages/ui/src/v4/useWorkspaceSessionsIndexItems.ts) + │ → agentService.subscribeSessionsIndexV4(topic "sessions-index/") + │ → SessionsIndexStore → ZCodeTaskMeta[](活动/detail 层) + ├─ fetchTaskListMembershipSets (packages/ui/src/lib/taskListMembershipSets.ts:211) + │ → zcodeTaskService.listTasks / listPinnedTasks / listArchivedTasks ← 直读 SQLite + ├─ buildTaskListResult (packages/ui/src/v4/buildTaskListResultFromSessions.ts:170) + │ “以 tasks-index 行为左表做字段级 join……session-only 冷摘要不会进入持久列表”(:77-78) + ↓ taskQueryCacheStore (zustand) → TaskList.tsx 行渲染 + 订阅:zcodeTaskService.onDynamicWorkspaceEvent → 事件 "workspace_task_list_changed" +``` + +### Host 侧事件源头(`workspace_task_list_changed`) + +- 发射器:`emitWorkspaceTaskListChanged`(`packages/services/src/zcode-agent/zcodeTaskIndexSyncer.ts:460-499`)。 +- 触发点只有两类(VERIFIED_SOURCE,全仓库枚举): + 1. **Host 自身写入**:adapter 的 createTask/deleteTask/renameTask/pin/archive/终态迁移等(`zcodeTaskServiceAdapter.ts:1912,2128,2862,2974,…`); + 2. **`ZCodeTaskIndexSyncer` 摄取本 workspace 自属 app-server 的 `sessions-index/` V4 topic**(`zcodeAgentService.ts:5494` 订阅;`zcodeTaskIndexSyncer.ts:1437`)。 +- 跨窗口转发(`taskRealtimeBridge.ts`)只转发应用内产生的事件。 + +## 3. 持久化(VERIFIED_RUNTIME + VERIFIED_SOURCE) + +### tasks-index.sqlite(Desktop 任务列表唯一真相源) + +- 路径:`getAppConfigDir() = getZCodeDataRootDir()/v2`(`packages/services/src/paths.ts:53`)→ 本机 Desktop 实际为 **`D:\Program Files\.zcode\v2\tasks-index.sqlite`**(VERIFIED_RUNTIME:活动 WAL,159 行)。 +- schema:`packages/services/src/session/tasksDatabase/schema-v1.ts` + 迁移 `0001`–`0003`(`migrations.ts`),`tasks` 表主键 `(workspace_key, task_id)`,列含 `workspace_path/workspace_identity/task_id/title/task_status/provider/mode/model/created_at/updated_at/unread_at/pinned/archived/deleted/title_overridden/meta_json/searchable_text/cron_automation_id/off_peak_task_id`。本机实际 schema 与源码完全一致(VERIFIED_RUNTIME,PRAGMA 只读比对)。 +- **workspace_key**:`resolveWorkspaceKey = workspaceIdentity?.trim() || workspacePath`(`packages/shared/src/task-realtime-core.ts:78-83`)→ 本地为项目根路径原文。本机行证实:Bridge 注册的任务以 `D:\codex-zcode-bridge` 为 key 分组(VERIFIED_RUNTIME)。 +- **provider 过滤**:Desktop 所有列表查询按 `provider = "glm"` 过滤(`ZCODE_AGENT_PROVIDER = "glm"`,`packages/shared/src/zcode-agent-policy.ts:5`;adapter 各 list 方法传 `GLM_PROVIDER`)。Bridge 已写 `provider: "glm"`,匹配。 +- 读写实现:`TaskIndexRepo`(`taskIndexRepo.ts`)持有单条长连 `node:sqlite DatabaseSync`,**无结果缓存**,每次 `listTasks/listPinnedTasks/listArchivedTasks/queryTaskList` 都实时执行 SQL(VERIFIED_SOURCE)。 + +### CLI session store(所有 app-server 进程共享) + +- 路径硬编码 `os.homedir()/.zcode/cli/db/db.sqlite`(`apps/zcode-cli/packages/adapters/src/storage/session-store/paths.ts:6-8`),**不受 ZCODE_HOME/ZCODE_DATA_BASE_DIR 影响** → Desktop 与 Bridge 的 app-server 进程写同一个库(本机:`C:\Users\Sandy\.zcode\cli\db\db.sqlite`)。 +- `session` 表含 `id/project_id/workspace_id/directory/title/task_type/taskTypes…`;行落库时机:首条输入 / 外部活动 / 启动轮 / 冷恢复(`apps/zcode-cli/packages/core/src/runtime/methods/events.ts:583-600`)。 + +### “sessions-index” 不是文件 + +全仓库不存在 `sessions-index.sqlite`。它是 **V4 协议 topic** `sessions-index/`(`packages/shared/src/zcode-protocol-v4/sessions-index.ts:76-78`)+ 每个 app-server 进程内的内存投影(`SessionsIndexPublisher`,`apps/zcode-cli/packages/bootstrap/src/zcode-protocol-v4/sessions-index-publisher.ts`),冷种子读共享 session store。 + +## 4. 刷新机制(核心问题的答案) + +### 4.1 不存在的机制(均 VERIFIED_SOURCE,全仓库枚举为负) + +- ❌ **无文件监听**:`fs.watch` 仅用于 renderer 的文件树/工作流目录/PPTX 监视;没有任何代码 watch `~/.zcode` 数据目录或 tasks-index.sqlite;无 chokidar。 +- ❌ **无 tasks 表轮询**:唯一 20s 轮询器是 cron 调度 utilityProcess(`packages/desktop/src/scheduler/index.ts:33`),只读 `automations/automation_runs/off_peak_tasks`,不读 `tasks`,也不驱动列表。 +- ❌ **无窗口 focus/visibilitychange 刷新**。 +- ❌ **无跨进程推送**:sessions-index 只在单进程内 fanout(`v4-gateway.ts` `flushIndex`);`ensureIndexPublisher` 缓存 publisher,进程存活期间**不重读共享 store**(`v4-gateway.ts:1206-1209`)。 + +### 4.2 存在的刷新触发器(Renderer 侧实测路径,VERIFIED_SOURCE) + +| 触发器 | 路径 | 对外部写入行有效? | +|---|---|---| +| 挂载/查询变化/workspace 切换 | `useGlobalTaskList`、`useWorkspaceTaskLists` 重新 load | ✅(listTasks 实时读 SQLite) | +| `workspace_task_list_changed` 事件(reason ∈ task_created / task_archived / task_unarchived / task_pinned / task_unpinned / task_meta_changed / task_deleted;**显式排除 task_status_changed、task_model_changed**,`packages/ui/src/lib/taskListRefreshPolicy.ts:35-48`) | `useWorkspaceTaskLists.ts:676-713` bump membershipVersion + markStale → refresh | ✅(但事件只能由 Host 自身写入产生) | +| **sessions-index 内容变化**(本 workspace 任意原生 session 的 prompt 开始/标题更新/turn 完成/终态,ModelStreaming 被跳过,`v4-gateway.ts:1080-1089`) | `useWorkspaceTaskLists.ts:622-676` diff 变化 workspace → `markTaskQueryCacheScopesStale` → refresh → **listTasks 实时重读 SQLite** | ✅ **这是活动 workspace 上外部写入行的自然可见通道**(INFERRED:链路各环节均 VERIFIED_SOURCE,端到端未单独实测) | +| Host 侧 workspace 事件 | `windowHostControllerService.ts:318-335` → refreshSource(force) → 投影帧 | ✅ | +| 手动 `refresh()` / 删除归档后回调 | `useGlobalTaskList.ts:183-191` | ✅ | + +### 4.3 官方外部触发器(second-instance,VERIFIED_SOURCE) + +`packages/desktop/src/main/desktopSecondInstanceDeepLink.ts` + `desktopOAuthDeepLink.ts:188`: + +- 运行中的 Desktop 收到第二实例启动时(Electron `requestSingleInstanceLock`,`main/index.ts:1886-1899`): + - **argv 形式**:`ZCode.exe --open-workspace "<绝对路径>"` → `handleOpenWorkspacePath` → 校验目录存在 → `webContents.send(PlatformChannels.OpenWorkspacePath, path)` → Renderer 打开/切换该 workspace(**无确认对话框**); + - **deep link 形式**:`zcode://workspace/open?path=<编码路径>` → `handleDeepLink`(带信任确认对话框)→ 同上。 +- 打开/切换 workspace 必然重挂载任务列表 → 两条数据面重新 `listTasks` → SQLite 中的外部行立即可见。 +- 若 Desktop 未运行,同一命令会正常启动它(第二实例语义不成立)——调用方需自行判断(INFERRED)。 + +### 4.4 结论(对第三阶段选项的裁决) + +**对“外部进程直写 tasks-index.sqlite”这一事件源,当前版本是 F(无任何官方监听/轮询/推送)**;但 Desktop 的自然活动(同 workspace 任意原生 session 的 turn 边界、workspace 切换/重开)会周期性触发对 SQLite 的实时重读,外部行随之出现(4.2 表第 3 行)。**存在一个官方、无重启、非 UI 自动化的确定性触发器:`--open-workspace` second-instance argv(4.3)。** + +## 5. 外部 session 与 Desktop session 的差异(Phase 4/5) + +Desktop 原生路径(`createTask`,VERIFIED_SOURCE): + +```text +renderer/automation 发起 + → zcodeTaskServiceAdapter.createTask (:1775) + → v4 createSession / zcodeAgentService.createSession(本 workspace 的 app-server) + → syncTaskIndexMeta (:1893) → taskIndexRepo.initializeGroupedTaskAtTop (:1899) + → emitWorkspaceTaskListChanged(…, "task_created") (:1912) + → Renderer 立即刷新(数据面 2 事件 + 数据面 1 refreshSource) +``` + +Bridge 外部路径:session 经**自己的** app-server 进程创建: + +1. session 行落共享 `~/.zcode/cli/db/db.sqlite`(首条输入即落;legacy `session/create` 传 `persistence:"immediate"` 更早;V4 `persistence:"deferred"` 首条 prompt 才落)。 +2. Desktop 的 app-server **存活期间不重读该 store** → 不会出现在其 sessions-index → `ZCodeTaskIndexSyncer` 不会写入/发事件 → 列表不更新。 +3. 只有当 Desktop 侧该 workspace 的 runtime **重启/重建**(进程回收、`restartWorkspaceProcess`、workspace 重开、Desktop 重启)时,sessions-index 冷种子 `getStoredSessionSummaries`(`v4-bridge.ts:1352-1428`:按 `directory = workspacePath`、`taskTypes ∈ {interactive, fork, workflow_parent}`、未归档、limit 200)才会看到 Bridge session → `seedMissingRowsFromInitialSnapshot`(`zcodeTaskIndexSyncer.ts:1015-1020`)补写 tasks-index → 发 `task_created` → 列表刷新。 +4. 因此 **V4 conversation 创建本身不会让运行中的 Desktop 发现外部任务**;V4 与 legacy 最终写同一 store,差别只在落库时机。这是“Bridge 建的 session 不自动出现”的根本原因。 + +## 6. 推荐集成(按侵入度排序) + +| # | 机制 | 侵入度 | 证据 | 采纳 | +|---|---|---|---|---| +| 1 | **保持现状:按官方 schema 直写 tasks-index**(workspace_key=项目根、provider=`glm`、title/task_status/meta_json 与 Desktop 写法一致) | 低(已实现并已在生产 DB 验证) | VERIFIED_RUNTIME(DB 中已有 PROMPT_DECISION_E2E_* 等行) | ✅ 保留 | +| 2 | **官方确定性触发器:`--open-workspace` second-instance**(Desktop 在运行时转发给运行实例打开/切换 workspace → 立即重读列表;未运行时等同启动 Desktop,需自行守卫) | 低(官方入口,非 UI 自动化、无重启、无注入) | VERIFIED_SOURCE;runtime 冒烟见第 8 节 | ✅ 新增 `ZCodeDesktopIntegration`/`DesktopTaskRefresh` 组件封装 | +| 3 | **依赖 Desktop 自身 seed**(runtime 重启/重开 workspace 时从共享 db.sqlite 自动收养 Bridge session) | 零(纯官方行为,无需 Bridge 动作) | VERIFIED_SOURCE | ✅ 作为 #1/#2 的自然兜底;两者行身份一致(task_id=sess_*),`seedTaskMetaIfMissing` 不会重复建行 | +| 4 | 注入 Electron / 补丁 Desktop / 重启 / 模拟按键 | 高 | — | ❌ 禁止 | + +架构落点(遵守冻结契约):SQLite 写入继续留在 `ZCodeAdapter` → `task-index-sync.ts`(MCP 面五工具不变);新增触发逻辑独立成 `ZCodeDesktopIntegration`(或 `DesktopTaskRefresh`),通过 `zcode_events` 暴露 `desktop_refresh_triggered` 事件,不新增 ZCode 私有协议到 MCP API。 + +约束提示:#1 属于“直写 ZCode SQLite”类别,按 Decision Gate 在此显式报告——它是本仓库既有、已交付的能力(commit 5aeb2cb),非本次新增;本次新增的只有 #2 官方触发器。多控制器安全:Desktop 仅作 Viewer;点击列表中的 Bridge 任务会在 Desktop 侧 resume 该 session,需用户自行避免双写(第 7 节风险)。 + +## 7. 兼容性风险 + +- app-server 协议与 tasks-index schema 均为**版本相关私有接口**(官方源码将打包 app-server 定位为 Desktop host 内部协议)。本结论绑定 3.14.3;升级后必须重验:schema 列集(`requireTaskTable` 已守护)、provider 过滤值、`resolveWorkspaceKey` 语义、sessions-index topic/seed 行为、`--open-workspace` argv 与 deep link 格式。 +- `task_status_changed` 被排除在列表刷新原因之外(`taskListRefreshPolicy.ts`):Bridge 后续的纯状态更新不会触发刷新——由 #2 触发器或用户自然活动覆盖。 +- sessions-index 冷种子按 `directory` 精确匹配 workspacePath 且 limit 200:依赖 #3 时,Bridge session 的 cwd 必须与 Desktop 打开的目录逐字一致(大小写/分隔符)。 +- 点击列表行会让 Desktop resume 该 session(Viewer 变 Controller 风险):本文档不建立多控制器安全,仍按“Session Ownership”约束禁止从 Desktop 发 prompt。 +- `--open-workspace` 依赖 Electron 单实例锁语义;Desktop 未运行时该命令会冷启动 Desktop(INFERRED,未实测冷启动分支)。 + +## 8. 冒烟记录:TASK_DESKTOP_REFRESH_SMOKE(2026-09-28) + +### 8.1 执行方式 + +- 通过生产入口驱动真实 Bridge 流程:`node plugins/codex-zcode-bridge/server/bridge.mjs`(stdio MCP,`ZCODE_BRIDGE_PLUGIN_MODE=1`,data root `C:\Users\Sandy\.codex\codex-zcode-bridge`),由驱动脚本以官方 MCP SDK v2 Client 调用 `zcode_task` → `zcode_events`(长轮询)→ `zcode_status` → `zcode_result`,与 Codex 调用方式一致。 +- 执行目录(`worktree_path`)为一次性隔离仓库 `C:\Users\Sandy\AppData\Local\Temp\zcode-desktop-refresh-smoke\repo`(git init + 1 commit);用户源码仓库零写入(clone/读操作除外)。objective 为只读任务(git log + 禁止改文件)。 +- 排障记录:首次尝试误用 dist 构建 + `ZCODE_BRIDGE_PLUGIN_MODE=1` 组合,`spawn-worker.ts` 的 `workerEntryPath()` 在 plugin 模式下解析到 `dist/src/worker/worker-main.mjs`(不存在)→ worker 秒退 → `worker_lost`。该组合无效,插件模式必须配 bundled `server/bridge.mjs`。 + +### 8.2 结果 + +| 记录项 | 值 | +|---|---| +| Bridge task ID | `TASK_DESKTOP_REFRESH_SMOKE` | +| ZCode session ID | `sess_bc486102-1b74-4db9-85d4-ad571a850582` | +| 模型 | `account:bigmodel-individual-coding-plan/GLM-5.3`(session snapshot 报告) | +| workspace(分组键) | `D:\codex-zcode-bridge`(= `workspace_key`,项目根) | +| 执行目录 | `C:\Users\Sandy\AppData\Local\Temp\zcode-desktop-refresh-smoke\repo`(= `workspace_path`) | +| 注册事件 | `desktop_task_registered`(events seq 8),task-index 写入成功 | +| task-index 记录 | `tasks` 表新行:provider `glm`、mode `yolo`、task_status `completed`、archived/deleted 0(VERIFIED_RUNTIME,只读查询) | +| 共享 session store | `C:\Users\Sandy\.zcode\cli\db\db.sqlite` `session` 行:`directory`=执行目录、`task_type=interactive`(第 6 节 #3 兜底前提成立) | +| 任务结果 | completed;报告确认未改动任何文件;`needs_master_decision=false` | + +**附注(副作用披露)**:tasks-index 中另有一行 `sess_a283caef-…`(同名、16:54:10 创建、completed)——源于排障时手工执行 `node dist/src/worker/worker-main.js TASK_DESKTOP_REFRESH_SMOKE`:TS 版 run-task **不拒绝已终态任务**,把失败任务重跑了一次(同样只读、同样在隔离目录完成)。这是一个值得记录的 Bridge 健壮性缺口:终态任务缺少重入守卫。 + +### 8.3 运行中的 Desktop 观察结果(VERIFIED_RUNTIME) + +- Desktop 全程保持运行(未重启)。Host 日志 `D:\Program Files\.zcode\v2\logs\2026-09-28.log`: + - 行插入(00:55:25 本地时间)之后、外部触发之前,`window-controller.listTaskList OK` 被自然调用 **47 次**(00:55:25–00:58:10,同 workspace 原生 session 活动驱动)——每次调用按源码语义实时重读 tasks-index(`readSourceTaskIndex` → `listTasks` 等直读 SQLite)。 + - 外部触发后 2 秒内再次出现成簇 `listTaskList` 调用。 +- 官方触发器验证:`ZCode.exe --open-workspace "D:\codex-zcode-bridge"` → 运行实例日志 `[deep-link] 工作区打开请求路由成功 {"windowId":1,"path":"D:\\codex-zcode-bridge"}`,随后 listTaskList 重读;第二实例自行退出(单实例锁)。无对话框、无重启、无 UI 自动化。 +- **UI 呈现(INFERRED→待用户确认)**:源码保证每次 listTaskList 读出的行进入侧栏投影(数据面 1 快照帧 + 数据面 2 查询缓存);两条 `TASK_DESKTOP_REFRESH_SMOKE` 任务应已出现在 `codex-zcode-bridge` 工作区任务列表中。请用户目视确认作为最终闭环。 + +## 9. 运行记录 + +见第 8 节。核心结论一句话回答:**在 3.14.3 架构下,外部进程没有任何受支持的“推送刷新”通道;最干净的机制是——按官方 schema 直写 tasks-index(现状),运行中的 Desktop 会在同 workspace 原生 session 的每个 turn 边界自然重读该库(活动工作区近似实时);需要确定性即时可见时,使用官方 second-instance 入口 `ZCode.exe --open-workspace ` 让运行中的 Desktop 重新打开该工作区。** diff --git a/docs/research/phase1-codex-zcode-2026-09-26.md b/docs/research/phase1-codex-zcode-2026-09-26.md new file mode 100644 index 0000000..0d9f347 --- /dev/null +++ b/docs/research/phase1-codex-zcode-2026-09-26.md @@ -0,0 +1,80 @@ +# Phase 1 调研:Codex → ZCode Bridge + +> Status: RESEARCH +> Date: 2026-09-26 +> 研究结论,不代表当前实现。文档索引见 [docs/README.md](../README.md)。 + +调研日期:2026-09-26(Asia/Shanghai) + +## 范围与证据 + +检查了以下项目当时的 `main` 分支: + +- [hex1n/cc-plugin-codex](https://github.com/hex1n/cc-plugin-codex) +- [alexeygrigorev/codex-zcode](https://github.com/alexeygrigorev/codex-zcode) +- [zai-org/ZCode](https://github.com/zai-org/ZCode),包括 CLI 参数解析、prompt 结果格式和 provider runtime 初始化。 + +第一个项目检查了 MCP server、service、进程、任务生命周期、隔离工作区和任务 prompt 源码。第二个项目检查了 `ABOUT.md` 和 `codex-rs/ext/zcode/src/lib.rs`。本机 ZCode 检查及其限制记录在 [ZCode Runtime 验证](../ZCODE_RUNTIME.md)。 + +第一次调研时,工作目录里没有项目文件,也没有 `.git`。之后项目已初始化 Git,并连接到 `https://github.com/Sandyzzx/codex-zcode-bridge.git`。本地分支和当前提交状态以 `git status` 为准。 + +## 参考 A:可复用的设计模式 + +`cc-plugin-codex` 使用类型化的 stdio MCP,并通过简洁的协议层校验工具参数、再委托 service 函数处理。协议 handler 不负责任务执行逻辑。此分层适用于本项目:MCP schema/handler 调用 task manager,task manager 再调用 workspace provider 和 coding-agent adapter。 + +它的任务生命周期状态保存在进程内存之外,记录子进程身份和日志,并在重启后恢复过期的 `starting`/`running` 任务。Windows 超时和取消通过 `taskkill /T /F` 终止进程树。这些做法可用于 Bridge 的重启恢复和取消;V0.1 每个任务使用一个 JSON 记录即可。 + +它的写入流程使用独立 clone 和显式 apply 步骤。相比本项目 V0.1 直接工作区模式,隔离更强,因此适合作为未来选项,不必现在照搬。主要经验是将工作区创建和清理放在 `WorkspaceProvider` 边界之后。 + +它的 prompt 会明确任务范围与能力边界,结果工具会返回持久化的结构化任务状态。完成状态只是报告,不证明任务正确。Codex 仍需检查 diff、重跑相关检查、对照验收标准,并决定续作或通过。 + +## 参考 B:可复用的 adapter 模式 + +`codex-zcode` 是 Codex CLI fork,不是独立 MCP 任务管理器。不过它的 ZCode 集成是一个职责清晰的子进程 adapter:解析 `zcode.cjs` runtime,通过 Node 调用 `--prompt`、`--json`、`--mode` 和 `--cwd`,必要时传入 `--resume`,捕获 stdout/stderr,设置硬超时,解析 JSON,并要求存在 `sessionId` 才将响应视为有效结果。 + +Rust adapter 通过简短 Node loader 传递临时 prompt 文件路径,而不是把可能很长的 prompt 直接放进 OS 命令行。它显式设置子进程工作目录、不使用 shell,并在 future 被丢弃时终止子进程。这些是适用于 Windows 的 adapter 实践。Bridge 仍应负责持久化任务状态和规范化结果;ZCode 进程应封装在 `CodingAgentAdapter` 后。 + +该参考项目的 Linux 说明记录了 provider 配置查找路径不匹配,并建议复制打包配置。ZCode 官方仓库展示了更合适的 Bridge 接入点:`prepareCliProviderRuntimeEnv` 同时接受 `ZCODE_BUILTIN_PROVIDER_CONFIG_FILE` 和 `ZCODE_PERSONAL_PROVIDER_CONFIG_FILE` 后,会跳过基于 entrypoint 的路径查找。官方 README 列出了 builtin 配置环境变量,runtime 路径实现要求 builtin 和 personal 配置成对提供。Bridge 应先验证两个文件,再仅在子进程环境中设置变量;不得复制或修改 Desktop 安装内容。 + +官方 CLI 源码还确认 Agent CLI 会解析 `--prompt`、`--json`、`--cwd`、`--mode` 和 `--resume`。本机观察到的 `--json` prompt 结果包含 `sessionId`、`traceId`、`turnId`、`response`、`usage`、`eventCount` 和 `projection`。该结构只在本机 CLI 0.16.9 上验证过,不是跨版本稳定契约。当前 CLI 参数解析器没有 `--max-turns`,因此 Bridge 不得向本机版本传此参数。本机运行证据和未确认事项见 [ZCode Runtime 验证](../ZCODE_RUNTIME.md)。 + +## V0.1 架构建议 + +采用以下边界: + +1. **stdio MCP server** — 通过严格输入 schema 暴露任务、状态、结果、续作和取消工具;协议层保持精简。 +2. **Task manager 和文件存储** — 校验 task ID 和规范路径,将原始任务包、状态时间戳、进程元数据、stdout/stderr 和规范化结果保存在 `.tasks//`。状态和结果文件需原子写入,避免重启留下半截 JSON。 +3. **WorkspaceProvider** — 首先实现 `DirectWorkspaceProvider`,将规范目录传给 adapter。工作区准备过程独立封装,以便未来增加 worktree provider 时不改变 MCP 工具契约。 +4. **CodingAgentAdapter / ZCode adapter** — 发现并验证 Node 和 `zcode.cjs`,构造参数数组(禁止拼接 shell 命令字符串),启动和监控子进程,解析输出并规范化结果。启动/配置错误应与模型/任务失败区分。 +5. **Prompt builder** — 将结构化任务包和 Master 反馈渲染为下属 Agent prompt。明确任务边界,但 Direct 模式下的 `allowed_paths` 和 `forbidden_paths` 只是 Agent 指令及运行后检查,不是 OS 强制的沙箱。 + +数据流:Codex 调用 `zcode_task` → 校验并持久化任务包 → 选择工作区 → 构造 prompt → 启动 ZCode → 更新持久状态和日志 → 规范化结果 → Codex 调用 status/result 并独立检查工作区 diff 和测试 → Codex 接受结果,或提供具体反馈调用 `zcode_continue`。`zcode_cancel` 请求终止,只有确认进程已停止后才记录终态。 + +建议状态转换:`queued → running → completed | failed | cancelled | waiting_for_master`。Server 重启后根据已记录的 PID 恢复仍存活的任务;子进程已退出但没有结果时标记失败。只有实际验证 session 复用后才优先使用 `--resume `;否则启动新调用,并在 prompt 中携带任务、结果和反馈摘要。只有返回的 session ID 确认一致时,才能声称复用了原 session。 + +故障处理应保留原始 stdout/stderr、退出码、开始/结束时间、超时/取消原因、解析错误和规范化结果。进程成功退出但 JSON 缺失或错误,仍是 Bridge 任务失败,不是 coding task 完成。日志/结果需要有界,并明确清理策略。 + +## 安全边界与已知风险 + +Direct 工作区模式会根据 ZCode 自己的权限模式和工具,让 ZCode 在选定工作区中写入。Prompt 无法从技术上阻止它修改允许列表之外的文件,运行后检测也无法撤销写入。V0.1 应规范化工作区、拒绝无效/越界路径、记录路径约束,并向 Codex 报告越界改动;更强隔离需要单独实现 workspace/sandbox。 + +不得通过 `exec` 或 shell 命令字符串调用 `zcode.cjs`。不要在任务 prompt 或持久化日志中写入密钥。MCP 取消必须覆盖完整 Windows 进程树。ZCode 自己报告的 `completed` 只是下属证据;最终 PASS 只能由 Codex 判定。 + +## Phase 1 结论 + +- 可以采用职责分层和任务生命周期设计,无需完整照搬任一参考项目。 +- ZCode Desktop 3.14.3 / CLI 0.16.9 的 headless 执行、JSON 输出、`--cwd` 文件放置和 `--resume` session 续作已通过本机真实 smoke test。Resume 返回相同 session ID,并修改同一隔离工作区。 +- 在干净子进程环境中,原 provider 路径查找失败可以稳定复现。只在子进程环境中设置成对 provider 配置变量即可修复,不必修改 ZCode 安装。Personal 配置必须解析为有效且已存在的配置;自动生成的小 stub 无法使用。 +- 多种环境下都曾出现短暂的 `Bundled 与 Active ZCode Built-in Release 均不可用`,之后停止出现。原因未知。Bridge 应保留诊断;只有区分该暂时性故障与配置/model 错误后,才采用有界重试。 +- 后续工作:在架构中明确 Bridge 的 provider 配置发现和预检行为,再实现 adapter。在本机应从有效的 ZCode 数据目录(或 `ZCODE_DATA_BASE_DIR`)发现 personal 配置,不要假定它位于 Windows 用户主目录。 + +## 资料来源 + +- [cc-plugin-codex README](https://github.com/hex1n/cc-plugin-codex) +- [cc-plugin-codex MCP server](https://github.com/hex1n/cc-plugin-codex/blob/main/mcp/server.mjs) +- [codex-zcode ABOUT.md](https://github.com/alexeygrigorev/codex-zcode/blob/main/ABOUT.md) +- [codex-zcode ZCode adapter 源码](https://github.com/alexeygrigorev/codex-zcode/blob/main/codex-rs/ext/zcode/src/lib.rs) +- [ZCode CLI 参数解析器](https://github.com/zai-org/ZCode/blob/main/apps/zcode-cli/packages/cli/src/arguments.ts) +- [ZCode CLI provider runtime 环境](https://github.com/zai-org/ZCode/blob/main/apps/zcode-cli/packages/cli/src/provider-runtime-env.ts) +- [ZCode provider runtime 路径变量](https://github.com/zai-org/ZCode/blob/main/packages/provider-node/src/runtime-paths.ts) +- [ZCode headless prompt 结果格式](https://github.com/zai-org/ZCode/blob/main/apps/zcode-cli/packages/cli/src/prompt-command.ts) diff --git a/docs/research/start-plan-headless-2026-09-27.md b/docs/research/start-plan-headless-2026-09-27.md new file mode 100644 index 0000000..d315c13 --- /dev/null +++ b/docs/research/start-plan-headless-2026-09-27.md @@ -0,0 +1,42 @@ +# 已知问题:Start Plan 在 headless app-server 中无法完成认证 + +> Status: RESEARCH +> Date: 2026-09-27 +> 已知问题的观测记录,结论仍成立:headless 路径不伪造验证码。当前可用路径为 Coding Plan。 + +记录日期:2026-09-27 + +## 现象 + +Codex → ZCode Bridge 可同步账户 provider 并在 app-server 会话中选择 `account:bigmodel-start-plan/GLM-5.3-Flash`;但请求开始时,ZCode 要求宿主处理 `interaction/requestProviderRuntimeHeaders`。当前 Bridge 没有 ZCode 桌面渲染器提供的 Start Plan 验证码会话,因此请求在模型生成前失败。 + +## 本机证据 + +- 活动运行配置来自 `D:\Program Files\.zcode\v2`。该路径的 `coding-plan-cache.json`(2026-09-17)将 BigModel Coding Plan 标记为可用、Start Plan 标记为不可用。 +- `C:\Users\Sandy\.zcode\v2` 的缓存(2026-09-06)将两个套餐都标记为可用,但该缓存较旧,不能证明当前账户状态。 +- 使用 C 盘快照仅验证了 app-server 能列出并选择 Start Plan 模型;运行时随后因缺少桌面验证码会话失败,没有产生模型输出。 +- 活动 D 盘快照下,BigModel Coding Plan 的 GLM-5.3-Flash 已通过 Bridge TaskManager 的隔离 worktree 真实运行。 + +## 处理结论 + +- 当前 headless 自动开发使用 `builtin:bigmodel-coding-plan` + `GLM-5.3-Flash`。 +- 不尝试伪造或绕过验证码。Start Plan 需要 ZCode 提供官方 headless 认证接口,或允许宿主复用桌面验证会话。 +- 若账户状态变化,先刷新并确认活动数据根下的套餐缓存;不能以旧的 C 盘缓存覆盖活动 D 盘状态。 + +## 后续 + +跟踪 ZCode app-server 是否提供官方 Start Plan runtime-header/验证码宿主接口;接口可用后再实现并做真实模型调用验证。 + +## Coding Plan 回归验证(2026-09-27) + +为验证当前可用路径,已通过 Bridge TaskManager 派发隔离真实任务,使用 BigModel Coding Plan 的 `GLM-5.3-Flash`(runtime 返回 provider `account:bigmodel-individual-coding-plan`,reasoning `max`)。 + +- 任务:`TASK_ACCOUNT_PROVIDER_REGRESSION_20260927` +- ZCode session:`sess_7f867a52-7fe0-4b91-9695-dc5bfe8eb0fe` +- 隔离分支:`codex-zcode/TASK_ACCOUNT_PROVIDER_REGRESSION_20260927` +- 隔离工作树:`D:\\codex-zcode-bridge\\.tasks\\coding-plan-regression-data\\.tasks\\workspaces\\TASK_ACCOUNT_PROVIDER_REGRESSION_20260927` +- 任务产物:工作树 `.tasks/e2e-tests/account-provider.test.mjs`,只使用合成 fixture,不读真实 ZCode 凭据;9 项测试全部通过。 +- 复核:Master 在该工作树独立重跑 `node --experimental-strip-types --test .tasks/e2e-tests/account-provider.test.mjs`,9/9 通过;主仓库仍干净,测试产物留在被忽略的 E2E 工作树中。 +- 首轮审查发现 provider ID 与 runtime 实际值不一致,已续作修正为 `account:bigmodel-individual-coding-plan`,并保留 `GLM-5.3-Flash` 大小写;再次验证通过。 + +这证明 Coding Plan 路径可启动模型并完成真实开发任务;不代表 Start Plan 验证码问题已解决。 From 790700fda6a70d6c0a4b315aede6d4e591fdf394 Mon Sep 17 00:00:00 2001 From: sandyzzx Date: Wed, 7 Oct 2026 12:18:05 +0800 Subject: [PATCH 3/5] docs: record architecture decision records --- docs/README.md | 6 +++- ...-appserver-as-production-execution-path.md | 29 ++++++++++++++++ .../ADR-002-manager-owns-task-lifecycle.md | 31 +++++++++++++++++ ...03-execution-directory-prepared-by-host.md | 29 ++++++++++++++++ .../ADR-004-observation-is-not-control.md | 33 +++++++++++++++++++ docs/decisions/README.md | 14 +++++--- 6 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 docs/decisions/ADR-001-appserver-as-production-execution-path.md create mode 100644 docs/decisions/ADR-002-manager-owns-task-lifecycle.md create mode 100644 docs/decisions/ADR-003-execution-directory-prepared-by-host.md create mode 100644 docs/decisions/ADR-004-observation-is-not-control.md diff --git a/docs/README.md b/docs/README.md index 4cd7579..687b79a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -60,7 +60,11 @@ English readers start at [README.md](../README.md). This index is bilingual; the | 文档 | 日期 | 说明 | |---|---|---| -| [decisions/README.md](decisions/README.md) | 2026-10-07 | 决策索引与待补 ADR 清单 | +| [decisions/README.md](decisions/README.md) | 2026-10-07 | 决策索引 | +| [ADR-001](decisions/ADR-001-appserver-as-production-execution-path.md) | 2026-10-07 | Accepted:生产执行路径使用 app-server | +| [ADR-002](decisions/ADR-002-manager-owns-task-lifecycle.md) | 2026-10-07 | Accepted:Manager 独占生命周期,worker 通过 attempt claim 入场 | +| [ADR-003](decisions/ADR-003-execution-directory-prepared-by-host.md) | 2026-10-07 | Accepted:执行目录由调用宿主准备 | +| [ADR-004](decisions/ADR-004-observation-is-not-control.md) | 2026-10-07 | Proposed:本地材料只作观察面,待 Master 决策 | | [decisions/roadmap-decisions-2026-09-27.md](decisions/roadmap-decisions-2026-09-27.md) | 2026-09-27 | 路线图与决策讨论 | | [decisions/reliability-repair-plan-v2-2026-10-03.md](decisions/reliability-repair-plan-v2-2026-10-03.md) | 2026-10-03 | 可靠性修复计划,含未完成项 | diff --git a/docs/decisions/ADR-001-appserver-as-production-execution-path.md b/docs/decisions/ADR-001-appserver-as-production-execution-path.md new file mode 100644 index 0000000..437df56 --- /dev/null +++ b/docs/decisions/ADR-001-appserver-as-production-execution-path.md @@ -0,0 +1,29 @@ +# ADR-001 — 生产执行路径使用 ZCode app-server + +Status: Accepted +Date: 2026-10-07(记录日期,决策早于此) + +## Context + +Bridge 早期通过历史 CLI `--prompt --json` 与 ZCode 交互。当前生产路径改为 `node zcode.cjs app-server --stdio`。两套路径的协议、事件模型和取消语义不同。 + +## Decision + +- 生产任务使用 `ZCodeAppServerAdapter`。 +- 历史 CLI adapter、envelope、loader 与 `GitWorktreeProvider` 保留为 legacy 模块及其测试,不从共享公共入口导出,也不参与生产任务路径。 +- 不把 legacy CLI 的重试、快照排除或 worktree 创建语义套用到当前生产路径。 + +## Rationale + +app-server 是 ZCode 桌面宿主实际使用的运行通道,能提供 session 事件、交互(权限/输入)、模型选择与 usage 等能力;CLI 路径没有等价信息,无法支撑任务生命周期与可观测性需求。 + +## Consequences + +- app-server 协议是私有且随安装版本变化,Bridge 必须按版本观测能力,不能依赖跨版本稳定契约。 +- 任何"当前 ZCode 支持什么"的结论都要标注观测版本,未实跑的部分保持 NOT RUN。 +- legacy 模块仍然需要维护与测试,但不承担生产职责。 + +## Evidence + +- `docs/ARCHITECTURE.md`:"生产执行使用 ZCodeAppServerAdapter。历史 CLI adapter、envelope、loader 和 GitWorktreeProvider 保留为 legacy 模块及其测试,不从共享公共入口导出,也不参与生产任务路径。" +- `docs/ZCODE_RUNTIME.md`:"当前生产路径是 `node zcode.cjs app-server --stdio`,不是历史 CLI `--prompt --json`。" diff --git a/docs/decisions/ADR-002-manager-owns-task-lifecycle.md b/docs/decisions/ADR-002-manager-owns-task-lifecycle.md new file mode 100644 index 0000000..c1fd300 --- /dev/null +++ b/docs/decisions/ADR-002-manager-owns-task-lifecycle.md @@ -0,0 +1,31 @@ +# ADR-002 — Manager 独占任务生命周期,worker 通过 attempt claim 入场 + +Status: Accepted +Date: 2026-10-07(记录日期,决策早于此) + +## Context + +同一个 data root 可能被多个 Bridge MCP 进程共享。任务以 detached worker 执行,进程身份基于 PID,调度与执行不在同一进程内。 + +## Decision + +- Manager 用进程内 promise 队列加同一 data root 下的 `.tasks/.manager.lock` 串行化调度;活 owner 不因时间超限被驱逐,死 owner 由串行 reclaim guard 回收。 +- worker 在进入 adapter 前,必须在 attempt 目录写入永久 `execution.claim` 抢占执行权;重复、旧 attempt 与终态入场被拒绝。 +- `state.lock` 保护状态更新与结果提交;worker 提交还要校验当前 attempt 与非终态。 +- 未启动的 worker 可重拉一次;抢占过的 attempt 不重复执行;已开始的 worker 不自动重跑。 + +## Rationale + +跨进程并发下,"谁有权执行这一次 attempt"必须由持久化声明决定,而不是由内存状态或时间推断。否则会出现重复执行、旧 attempt 覆盖新结果、以及丢失的结果被重放。 + +## Consequences + +- 进程身份依赖 PID;操作系统重用 PID、以及自行脱离进程组的后代进程属于未充分验证的边界。 +- worker 丢失但记录的 ZCode PID 仍存活时保留占用,要求 `zcode_cancel` 验证清理。 +- 清理未验证的终态任务保留目录与 slot,续跑被拒绝,再次 cancel 成功后才释放。 +- 不同 data root 不共享调度锁,调用宿主必须避免向重叠目录提交冲突任务。 + +## Evidence + +- `docs/ARCHITECTURE.md`:Manager 锁、attempt claim、`state.lock`、重拉与不重跑规则、清理占用与 PID 边界均在该文档"当前架构"一节中描述。 +- `docs/INTERFACES.md`:任务状态与终态语义、续跑只接受 `completed` / `failed` / `waiting_for_master` 且清理必须已验证。 diff --git a/docs/decisions/ADR-003-execution-directory-prepared-by-host.md b/docs/decisions/ADR-003-execution-directory-prepared-by-host.md new file mode 100644 index 0000000..250826f --- /dev/null +++ b/docs/decisions/ADR-003-execution-directory-prepared-by-host.md @@ -0,0 +1,29 @@ +# ADR-003 — 执行目录由调用宿主准备,Bridge 不创建也不删除 + +Status: Accepted +Date: 2026-10-07(记录日期,决策早于此) + +## Context + +任务需要隔离的执行目录才能安全修改代码。是否隔离、隔离多深、以及何时清理,属于调用宿主的工程判断;Bridge 无法知道宿主的工作区所有权、代码托管方式与保留策略。 + +## Decision + +- `workspace` 是绝对项目路径,同时作为 ZCode Desktop 的项目身份。 +- `worktree_path` 是可选的、由调用宿主准备的实际执行目录。 +- Bridge 不创建、不选择、不删除 worktree。 + +## Rationale + +把破坏性文件系统操作留给拥有上下文的调用方。Bridge 只负责在给定目录内执行任务,避免在不知道宿主意图的情况下创建或删除工作树。 + +## Consequences + +- 隔离性由调用宿主保证;Bridge 不能承诺任务之间互不影响。 +- 同一可变目录上的任务被串行化;不同项目根可并发。 +- 任务记录里的执行目录可能被宿主之后移除,历史记录不因此失效。 + +## Evidence + +- `docs/INTERFACES.md`:"`workspace` 是绝对项目路径;`worktree_path` 是宿主已准备的执行目录。Bridge 不创建或删除 worktree。" +- `docs/ARCHITECTURE.md`:说明历史 `GitWorktreeProvider` 属于 legacy,不在生产路径上。 diff --git a/docs/decisions/ADR-004-observation-is-not-control.md b/docs/decisions/ADR-004-observation-is-not-control.md new file mode 100644 index 0000000..cecbb8a --- /dev/null +++ b/docs/decisions/ADR-004-observation-is-not-control.md @@ -0,0 +1,33 @@ +# ADR-004 — 本地材料只作观察面,不作控制面 + +Status: Proposed(需要 Master 决策) +Date: 2026-10-07 + +## Context + +ZCode 在本机留下多种材料:app-server 事件、CLI 的 metadata/output/model-io/rollout 文件、日志,以及 Desktop 的 `tasks-index.sqlite`。其中一部分结构已经在研究阶段被观测过,但都属于未公开、随版本变化的内部结构。 + +在 bridge 之外的历史讨论中已经出现过方向性结论:控制面走 app-server RPC,本地文件只用于观察,且 `task-index` 的写入是需要单独治理的既有例外。这一结论目前没有在仓库文档里形成决策记录,因此本条标为 Proposed。 + +## Proposed decision + +- 控制面只使用 app-server 及其支持的 RPC。 +- 本地 metadata、rollout、日志、Desktop 索引作为观察来源,读取只读、失败可降级,不得成为任务状态的事实来源。 +- 观察来源的优先级低于 app-server 事件;本地读取必须在隐私边界内进行,不做原始 reasoning 或工具参数的转发。 +- Desktop 索引同步保持 best effort 的旁路写入,明确不是权威数据;它是当前唯一的本地写入例外。 + +## Rationale + +本地结构没有稳定性承诺。把它们当作控制面会让 Bridge 在 ZCode 升级后静默失效,也会把未验证的推断变成任务状态。 + +## Consequences + +- 恢复与状态判定必须能用原生通道解释;本地材料只能提供线索,不能单独定论。 +- 观察能力受安装版本影响,需要按版本标注并保留 NOT RUN 记录。 +- 如果将来确实需要写本地结构,必须单独走决策,并说明兼容与回滚。 + +## Evidence + +- `docs/ARCHITECTURE.md`:"Desktop 索引同步是 best effort,事务内检查 schema 与 Bridge owner、更新有限状态字段,保留用户标题与额外 metadata。回归使用临时 SQLite。真实 Desktop schema/刷新/并发行为受安装版本影响,本轮未写入真实数据库。" +- `docs/INTERFACES.md`:"公开进度 usage 仅保留数值 token/cost 字段,隐藏推理、未知 metadata 和原始 RPC error 不转发。" +- `docs/ZCODE_RUNTIME.md`:运行配置与发现逻辑只读官方 provider 配置,不复制、不改写、不把凭据写入任务 metadata。 diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 34d78b3..79bc1b2 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -14,12 +14,16 @@ ## 待补的 ADR -以下主题目前只存在于权威文档的正文叙述里,没有独立决策记录。补写 ADR 时应以现有权威文档为准,搬运已有结论,不新增决策: +已补写: -- 生产执行路径使用 ZCode app-server,历史 CLI 路径只作 legacy 模块保留。 -- Manager 独占任务生命周期,worker 通过 attempt claim 进入执行。 -- 执行目录(worktree)由调用宿主准备,Bridge 不创建也不删除。 -- 本地 metadata、rollout、日志与 Desktop 索引只作观察面,不作控制面。 +| ADR | 状态 | 主题 | +|---|---|---| +| [ADR-001](ADR-001-appserver-as-production-execution-path.md) | Accepted | 生产执行路径使用 ZCode app-server | +| [ADR-002](ADR-002-manager-owns-task-lifecycle.md) | Accepted | Manager 独占任务生命周期,worker 通过 attempt claim 入场 | +| [ADR-003](ADR-003-execution-directory-prepared-by-host.md) | Accepted | 执行目录由调用宿主准备,Bridge 不创建也不删除 | +| [ADR-004](ADR-004-observation-is-not-control.md) | Proposed | 本地材料只作观察面,不作控制面 | + +ADR-001 到 ADR-003 只搬运权威文档里已经写明的结论。ADR-004 是唯一需要 Master 决策的一条:仓库文档里没有明文记录"观察面不等于控制面",此前只有设计讨论,因此它保持 Proposed,不当作已批准决策使用。 ## 命名 From 37ed3e7267dcefec5ff6a75cfcfe0d799eee3bab Mon Sep 17 00:00:00 2001 From: sandyzzx Date: Wed, 7 Oct 2026 12:19:30 +0800 Subject: [PATCH 4/5] docs: point the index at the incoming research dossier --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 687b79a..e98a89b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -76,7 +76,7 @@ English readers start at [README.md](../README.md). This index is bilingual; the | [research/desktop-task-refresh-2026-09-28.md](research/desktop-task-refresh-2026-09-28.md) | 2026-09-28 | ZCode Desktop 任务列表刷新机制 | | [research/start-plan-headless-2026-09-27.md](research/start-plan-headless-2026-09-27.md) | 2026-09-27 | Start Plan headless 认证阻塞与 Coding Plan 回归 | -`docs/research/` 里还会随 2026-10-05 的 Native CLI / app-server 研究合并而增加内容;那批文档目前还在独立 PR 中。 +2026-10-05 的 Native CLI / app-server 研究会落在 `docs/research/native-cli-vs-appserver-2026-10-05/`。那批文档目前在独立 PR 中,合并后本索引再补上逐篇条目。 ### 归档 ARCHIVED From a6778a14a29d6e98f995a32eb2064b1ea4736acc Mon Sep 17 00:00:00 2001 From: sandyzzx Date: Wed, 7 Oct 2026 12:24:19 +0800 Subject: [PATCH 5/5] docs: fold the phase 7 notes into the authoritative documents --- docs/ARCHITECTURE.md | 2 ++ docs/INTERFACES.md | 2 +- docs/PROJECT_STATE.md | 3 ++- docs/README.md | 2 +- docs/archive/appserver-capability-matrix-2026-09-27.md | 2 +- .../phase7-live-progress.md} | 8 +++++++- package.json | 1 - src/adapters/zcode-app-server-adapter.ts | 6 +++--- 8 files changed, 17 insertions(+), 9 deletions(-) rename docs/{PHASE7_LIVE_PROGRESS.md => archive/phase7-live-progress.md} (80%) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c376ef4..4d74b87 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -10,6 +10,8 @@ Manager 用进程内 promise 队列以及同一 data root 下的 `.tasks/.manage 未启动 worker 可重拉一次;抢占过的 attempt 不会重复执行。已开始的 worker 不自动重跑。worker 丢失但记录的 ZCode PID 仍存活时保留占用,要求 `zcode_cancel` 验证清理。清理失败的终态任务也保留目录与 slot;续跑被拒绝,再次 cancel 成功后才释放。进程身份依赖 PID;操作系统重用 PID 和自行脱离进程组的后代属于未充分验证的边界。 +worker 每 3 秒原子写入一次绑定 attempt 与 PID 的私有 heartbeat,字段含 session、turn、Bridge event 序号和已观测到的 ZCode event 序号。管理器遇到一次负向 PID 探测时,若 heartbeat 不超过 15 秒则暂缓失联判定。ZCode turn 完成后,worker 在清理进程前写入 outcome checkpoint,并在清理成功后更新验证标志;worker 在提交最终 result 之前退出时,管理器可据 checkpoint 恢复报告,清理未验证则保留 `cleanup_failed` 与 workspace 占用。 + 续跑先复制旧结果到 attempt 归档、准备 continue spec,再提交新 attempt 的 queued 状态。旧 root result 在提交前可恢复,在提交后因 attempt 不匹配不会被视为新结果。即使准备过程崩溃,原终态仍可读取。 审批记录按 attempt 隔离,公开 request ID 也带 attempt。相同 ID 的方法与规范化参数(包括 session 和输入)必须一致,否则拒绝。超时、取消和结束均 abort 待处理 interaction,释放 worker 轮询。 diff --git a/docs/INTERFACES.md b/docs/INTERFACES.md index b970cec..8dc75bb 100644 --- a/docs/INTERFACES.md +++ b/docs/INTERFACES.md @@ -10,7 +10,7 @@ TaskPackage 的五个数组必须存在,可为空。workspace 是绝对项目 任务 objective、requirements、路径、验收、测试命令及续跑 feedback 不截断;完整 prompt 超过 60,000 字符会返回 TASK_INVALID。参考 context 与旧结果摘要仍有明确的截断标记,不能把安全约束只放在参考 context。 -zcode_events 使用单调 seq cursor、limit 1–200、wait_ms 0–25,000、raw/summary view。summary 合并可见输出时会注明压缩。运行时事件先核对 session、单调 runtime seq 和可用 turn ID;存在历史事件的 session 必须观察新 turn.started 后才接受结束事件。缺少某些身份字段的旧协议仍有兼容路径,真实跨版本行为未全部验证。 +zcode_events 使用单调 seq cursor、limit 1–200、wait_ms 0–25,000、raw/summary view。summary 合并可见输出时会注明压缩。公开事件只包含可见文本、工具名称与状态,以及有限的生命周期 metadata;隐藏 reasoning 和未知 usage metadata 不进入公开事件。订阅前记录 `snapshot.runtime.eventSeq`。live 订阅连续 10 秒没有新事件时,每 5 秒按已观测序号向 `session/events` 补拉一次,重放仍经过同样的 session、单调 runtime seq 和 turn ID 过滤;runtime 明确拒绝该方法时降级为纯 live 订阅,连续 3 次失败后同样降级,两种情况都会发出可见的 `session_event_replay_unavailable` 事件。协议变化不能只靠字符串方法名推断支持。`session/read` 未接入,原生当前状态查询仍为 NOT RUN。运行时事件先核对 session、单调 runtime seq 和可用 turn ID;存在历史事件的 session 必须观察新 turn.started 后才接受结束事件。缺少某些身份字段的旧协议仍有兼容路径,真实跨版本行为未全部验证。 `zcode_feedback` 是独立的 `TaskFeedbackSnapshotV01` v0.1 聚合层,含 `schema_version: "0.1"`,并返回结构化 snapshot 与简洁文本。它不替代 `zcode_events`,也不改变 `TaskResult`、状态接口或已持久化记录。字段来源分为 Bridge task state(task、attempt、status、起止时间和墙钟 duration)、runtime-observed(只接受当前 attempt 规范化 `model_selected`、`model_tool_call`、`tool_status` 事件中的 allowlisted 字段)和 Agent report(仅当前 attempt 出现 `report_ready`、结果 attempt/status 匹配且结果为 `completed` 或 `waiting_for_master` 时,标记 `source: "agent_report"`)。模型不从请求或 catalog 回退;runtime model 缺失时为 `null`。`phase` 与 `progress` 固定为 `null`。activity 使用 Bridge 持久化事件时生成的 observation timestamp,语义固定为 `last_observed`;不表达工具仍在运行。交互状态固定为 `not_observed`,表示 snapshot 未确立可靠的当前交互状态,不表示历史中没有交互或没有待处理请求;原始交互事件仍由 `zcode_events` 提供。该 snapshot 不包含 runtime turn outcome 或 Bridge failure reason 字段,因此文本只显示可由 Bridge status 支持的完成/失败状态,不推断 turn 成功、目标完成或主机验收。Agent 报告中的 summary、文件、tests 与 issues 均不是 Bridge 独立验证结果。 diff --git a/docs/PROJECT_STATE.md b/docs/PROJECT_STATE.md index 02e73c2..7434fa9 100644 --- a/docs/PROJECT_STATE.md +++ b/docs/PROJECT_STATE.md @@ -37,6 +37,7 @@ - `session/read` 原生当前状态查询:未接入。 - 真实 ZCode app-server RPC 探针与跨版本兼容:只在记录过的 0.16.9 路径上观测过。 +- 真实 ZCode 权限审批往返:现有回归使用假运行时,未经真实交互验证。 - 跨 Host 并发 attach/control:研究阶段结论为 NO-GO,除非上游提供 ownership/control 协议。 - 真实 GUI 关闭时序、UI 响应与取消时延。 - 真实 Desktop 数据库写入与刷新行为。 @@ -44,4 +45,4 @@ ## 核对来源 -`package.json`、`src/host/stdio.ts`、`src/mcp/server.ts`、`docs/ARCHITECTURE.md`、`docs/INTERFACES.md`、`docs/PHASE7_LIVE_PROGRESS.md`、GitHub Actions 运行记录。文中标注"未运行"的条目没有被上述来源证实,保持未验证状态。 +`package.json`、`src/host/stdio.ts`、`src/mcp/server.ts`、`src/worker/run-task.ts`、`src/adapters/zcode-app-server-adapter.ts`、`docs/ARCHITECTURE.md`、`docs/INTERFACES.md`、GitHub Actions 运行记录。文中标注"未运行"的条目没有被上述来源证实,保持未验证状态。 diff --git a/docs/README.md b/docs/README.md index e98a89b..bff88b8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -85,13 +85,13 @@ English readers start at [README.md](../README.md). This index is bilingual; the | [archive/appserver-capability-matrix-2026-09-27.md](archive/appserver-capability-matrix-2026-09-27.md) | 2026-09-27 | app-server 能力普查,已被 2026-10-05 的能力探针取代 | | [archive/mvp-v0.3-2026-09-27.md](archive/mvp-v0.3-2026-09-27.md) | 2026-09-27 | MVP 0.3 版本说明 | | [archive/mcp-sdk-v2-migration-2026-09-27.md](archive/mcp-sdk-v2-migration-2026-09-27.md) | 2026-09-27 | MCP SDK v2 迁移记录 | +| [archive/phase7-live-progress.md](archive/phase7-live-progress.md) | 2026-10-03 | Phase 7 兼容说明;有效内容已提炼进 `ARCHITECTURE.md` / `INTERFACES.md` | ### 报告 REPORT | 文档 | 说明 | |---|---| | [TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md](../TASK_FEEDBACK_V01_IMPLEMENTATION_REPORT.md) | Task Feedback v0.1 的交付报告,一次性材料 | -| [PHASE7_LIVE_PROGRESS.md](PHASE7_LIVE_PROGRESS.md) | Phase 7 兼容说明;有效内容待提炼进 `ARCHITECTURE.md` / `INTERFACES.md` 后移入 `archive/` | ### 自动生成 Generated diff --git a/docs/archive/appserver-capability-matrix-2026-09-27.md b/docs/archive/appserver-capability-matrix-2026-09-27.md index 4a629ef..b5dc0fd 100644 --- a/docs/archive/appserver-capability-matrix-2026-09-27.md +++ b/docs/archive/appserver-capability-matrix-2026-09-27.md @@ -80,5 +80,5 @@ Runtime SHA-256:`B1DF2EF3E5BD76C4AF3ECB296BC003A10D3F13191A26610BD0BA940FEADAD - [ZCode Hooks](https://zcode.z.ai/en/docs/hooks):Hook 子进程协议、工具前/后事件和决策返回格式。 - [ZCode Commands](https://zcode.z.ai/en/docs/commands):官方 `/goal`、`/compact` 命令说明。 - [ZCode Automations](https://zcode.z.ai/en/docs/automations):官方自动化 UI 与本机运行限制。 -- [本机 Phase 7 协议记录](../PHASE7_LIVE_PROGRESS.md) 和 [双轮 E2E 决策记录](../decisions/roadmap-decisions-2026-09-27.md)。 +- [本机 Phase 7 协议记录](phase7-live-progress.md) 和 [双轮 E2E 决策记录](../decisions/roadmap-decisions-2026-09-27.md)。 - 社区协议逆向:[ZCode app-server V4 协议笔记](https://github.com/csuftt/zcode-jetbrains-plugin/blob/master/docs/zcode-appserver-protocol.md)。该资料不是官方兼容承诺。 diff --git a/docs/PHASE7_LIVE_PROGRESS.md b/docs/archive/phase7-live-progress.md similarity index 80% rename from docs/PHASE7_LIVE_PROGRESS.md rename to docs/archive/phase7-live-progress.md index 7653981..6ab9813 100644 --- a/docs/PHASE7_LIVE_PROGRESS.md +++ b/docs/archive/phase7-live-progress.md @@ -1,6 +1,12 @@ # 进度集成兼容说明 -此文件为历史 Phase 7 源码引用提供当前说明,不重建历史冻结契约。详细架构与事件接口见 [ARCHITECTURE.md](ARCHITECTURE.md) 和 [INTERFACES.md](INTERFACES.md)。 +> Status: ARCHIVED +> Date: 2026-10-03 +> 有效内容已提炼:worker liveness 与结果恢复进 `../ARCHITECTURE.md`;事件内容、补拉与降级、`session/read` NOT RUN 进 `../INTERFACES.md`。 +> 本文中"本轮没有对真实 app-server 执行 RPC 探针"等表述是 2026-10-03 的状态;2026-10-05 的研究另做了探针,见 `../research/native-cli-vs-appserver-2026-10-05/`。 +> 历史材料,不指导当前开发。 + +此文件为历史 Phase 7 源码引用提供当前说明,不重建历史冻结契约。详细架构与事件接口见 [ARCHITECTURE.md](../ARCHITECTURE.md) 和 [INTERFACES.md](../INTERFACES.md)。 生产 worker 通过 app-server 订阅 session/event,仅转发可见文本、工具名称/状态和有限生命周期 metadata。隐藏 reasoning 和未知 usage metadata 不进入公开事件。审批必须保留必要的输入以供主代理判断,属于私有任务证据。 diff --git a/package.json b/package.json index 8f147d1..8018c72 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "docs/ARCHITECTURE.md", "docs/INTERFACES.md", "docs/ZCODE_RUNTIME.md", - "docs/PHASE7_LIVE_PROGRESS.md", "LICENSE", "NOTICE" ], diff --git a/src/adapters/zcode-app-server-adapter.ts b/src/adapters/zcode-app-server-adapter.ts index 49ee8c8..d41d760 100644 --- a/src/adapters/zcode-app-server-adapter.ts +++ b/src/adapters/zcode-app-server-adapter.ts @@ -1,6 +1,6 @@ -// Streaming ZCode Protocol adapter for Phase 7. The wire protocol is versioned -// with the installed ZCode runtime; docs/PHASE7_LIVE_PROGRESS.md records the -// local 0.16.9 observations and compatibility boundary. +// Streaming ZCode Protocol adapter. The wire protocol is versioned with the +// installed ZCode runtime; docs/INTERFACES.md records the event cursor and +// replay boundary, and docs/ZCODE_RUNTIME.md the runtime configuration edge. import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; import { homedir } from "node:os"; import type {