From 2ec4ce138ad254e8a61244d8c65ddb08a2055a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E5=A5=87?= Date: Tue, 19 May 2026 00:26:09 +0800 Subject: [PATCH] docs(daemon): align roadmap with web-first client strategy --- .../qwen-code-daemon-design/01-overview.md | 33 ++-- .../02-architectural-decisions.md | 58 +++---- .../qwen-code-daemon-design/03-http-api.md | 4 +- .../04-deployment-and-client.md | 43 +++-- .../qwen-code-daemon-design/06-roadmap.md | 147 ++++++++++-------- .../qwen-code-daemon-design/README.md | 21 ++- 6 files changed, 163 insertions(+), 143 deletions(-) diff --git a/docs/comparison/qwen-code-daemon-design/01-overview.md b/docs/comparison/qwen-code-daemon-design/01-overview.md index 7f0d99b..b51b13e 100644 --- a/docs/comparison/qwen-code-daemon-design/01-overview.md +++ b/docs/comparison/qwen-code-daemon-design/01-overview.md @@ -22,9 +22,10 @@ - **Observability 直接**:`htop` / `ps` 列表 1 OS process = 1 workspace - **心智简单**:daemon ↔ session 两层(无中间抽象) -**两种部署**: -- **Mode B** `qwen serve` — 当前主线:headless HTTP front,所有 client 通过 HTTP/SSE 接入同一 daemon runtime。 -- **Mode A** `qwen --serve` — 2026-05-15 后暂停推进;作为 parking lot 保留,待 Mode B event/control/client contract 稳定后再评估。 +**两条长期链路**: +- **Native local TUI** `qwen` — 继续走现有直连 / streamJson / Ink 路径;这是本地终端体验的长期主线,不是临时 fallback。原因:本机 TUI 直连比“本机 TUI → localhost HTTP server → runtime”少一层 httpServer,性能更直接、维护面更小、故障概率更低。 +- **Mode B** `qwen serve` — headless HTTP front,当前优先服务 **remote web chat + web terminal** 等浏览器 / BFF / remote runtime 场景。remote web chat + web terminal technical POC 可与 P0 must-haves / state CRUD 并行启动,先验证 daemon HTTP/SSE typed event contract 的可行性;production-ready 仍等 P0/P1 收口。其他 channel、IDE 插件继续使用现有 `--acp` 模式,daemon 迁移后置。 +- **Mode A** `qwen --serve` — 2026-05-15 后暂停推进;作为 parking lot 保留。 **主线 scope**:daemon building block + 协议表面锁定(Stage 2 后)。多 tenant / 跨 daemon process 路由 / SaaS 部署属 **External Reference Architecture**(外部商业平台实施)。 @@ -36,7 +37,7 @@ |---|---|---| | **Daemon process** | `qwen serve` HTTP front 进程;启动时绑定 cwd = 单 workspace;持 Express 5 server + EventBus + 1 个内嵌 `qwen --acp` child | `packages/cli/src/serve/server.ts` | | **Session** | ACP `Session` 实例(`QwenAgent.sessions: Map` 内一条);持 transcript / FileReadCache / PermissionManager | `packages/cli/src/acp-integration/session/Session.ts` | -| **Daemon client / adapter** | TUI / channel / web / IDE / JSONL 等 client 通过 `DaemonClient` / `DaemonSessionClient` + typed event reducer 接入,不直接订阅内存 EventBus | 详 [§02 §8](./02-architectural-decisions.md#8-server--client--runtime-boundary2026-05-18) | +| **Daemon client / adapter** | web chat / web terminal 等 daemon-native client 通过 `DaemonClient` / `DaemonSessionClient` + typed event reducer 接入,不直接订阅内存 EventBus;TUI / channel / IDE 可共享 client/render 基础库,但不默认改传输链路 | 详 [§02 §8](./02-architectural-decisions.md#8-server--client--runtime-boundary2026-05-18) | | **Runtime worker** | 真正执行 tool / shell / MCP / skills / LSP / file operations 的 runtime;当前是 `qwen --acp` child,未来可替换 sandbox runner | 详 [§02 §8](./02-architectural-decisions.md#runtime-worker--sandbox-runner-boundary) | **核心约束**: @@ -78,13 +79,13 @@ qwen serve (1 Daemon process, 绑定 cwd = /work/repo-a) **2026-05-18 架构边界补充**: ```text -client adapters / output sinks +client/render adapters / output sinks → daemon client/protocol layer → qwen serve HTTP/SSE control plane → runtime worker / sandbox runner ``` -TUI / web terminal / channel / IDE / JSONL / stream-json 都应成为 daemon-native consumers:消费 typed events + shared reducer,再投影到各自 UI 或输出格式。PTY proxy 只保留为兼容 / demo / debug fallback。remote-control 是 control overlay,不再拥有独立 runtime / event protocol。详 [§02 §8](./02-architectural-decisions.md#8-server--client--runtime-boundary2026-05-18)。 +web chat / web terminal 是 daemon 第一阶段 daemon-native consumers:消费 typed events + shared reducer,再投影到 DOM chat 或 terminal-like UI。Native local TUI 继续走直连链路,但应逐步把 source adapter 与 terminal renderer 分离,让 **direct streamJson** 和 **daemon ACP-over-HTTP events** 都能复用同一套 view model / Ink→ANSI 渲染核心。PTY proxy 只保留为兼容 / demo / debug fallback。remote-control 是 control overlay,不再拥有独立 runtime / event protocol。详 [§02 §8](./02-architectural-decisions.md#8-server--client--runtime-boundary2026-05-18)。 External Reference Architecture 提供 orchestrator 层(详 [§06 §五 External Reference Architecture](./06-roadmap.md))。 @@ -94,12 +95,13 @@ External Reference Architecture 提供 orchestrator 层(详 [§06 §五 Extern | 模式 | 启动命令 | TUI | 适用场景 | |---|---|---|---| -| **Mode B: Headless + HttpServer** | `qwen serve [--port N]` | ❌ | 当前主线:服务器 / 容器 / 远端机器 / K8s pod / 所有 client 的统一 runtime | +| **Native local TUI** | `qwen` | ✅ | 本地终端长期主线:直连 runtime,保留最佳性能和最低复杂度 | +| **Mode B: Headless + HttpServer** | `qwen serve [--port N]` | ❌ | daemon server 主线:服务器 / 容器 / 远端机器 / K8s pod / web chat + web terminal | | **Mode A: CLI + HttpServer** | `qwen --serve [--port N]` | ✅ 本地 | 暂停推进;作为 parking lot 保留 | -**当前 client 边界**:TUI / channels / web / IDE 直接对接 `qwen serve` HTTP server;`GET /session/:id/events` 是 daemon 内部 EventBus 的 SSE projection。外部 client 不直接 import / subscribe 内存 EventBus。 +**当前 client 边界**:web chat / web terminal 作为第一阶段 daemon consumers,直接或通过 BFF 对接 `qwen serve` HTTP/SSE;`GET /session/:id/events` 是 daemon 内部 EventBus 的 SSE projection。外部 client 不直接 import / subscribe 内存 EventBus。本地 TUI、channel、IDE 插件暂不默认改链路。 -**Mode B 远端 client 是 "thin shell"**(Stage 1)——只能渲染 wire 流,daemon-side state dialogs(`/memory` / `/mcp` / `/agents` 等)不可用。Stage 1.5c daemon-side state CRUD 补齐后,TUI / channels / web / IDE 才能成为完整 client。详 [§04](./04-deployment-and-client.md)。 +**Mode B 远端 client 是 "thin shell"**(Stage 1)——只能渲染 wire 流,daemon-side state dialogs(`/memory` / `/mcp` / `/agents` 等)不可用。Stage 1.5c daemon-side state CRUD 补齐后,web chat / web terminal 才能成为完整 browser client;channel / IDE 是否迁移另行评估。详 [§04](./04-deployment-and-client.md)。 ### 三·一 Deployment forms(来自 chiga0 [#3803 comment 4476174099](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4476174099),2026-05-18) @@ -107,8 +109,8 @@ Mode B 之下进一步区分**3 种 deployment form**,钉死 daemon host 与 w | Form | Daemon host | Workspace host | Client host | 推荐度 | |---|---|---|---|---| -| **Local single-machine** | local 机器 | local 机器 | local TUI/IDE/channel | ✅ 主装机体验;daemon 可在 loopback 自动起 | -| **Cloud/devbox remote-runtime** | remote host/pod | **同** remote host/volume | local TUI/IDE/web | ✅ 推荐 cloud+client 拆分;Codespaces/Coder 风格——workspace 与 runtime **必须 colocate** | +| **Local single-machine** | local 机器 | local 机器 | local native TUI / local IDE / optional local web | ✅ 主装机体验;native TUI 直连长期保留;local daemon 仅服务 web/remote-control/实验 client | +| **Cloud/devbox remote-runtime** | remote host/pod | **同** remote host/volume | browser web chat / web terminal | ✅ 推荐 cloud+client 拆分;Codespaces/Coder 风格——workspace 与 runtime **必须 colocate** | | **Local workspace + remote daemon** | remote host/pod | local 机器 | local client | ❌ **不推荐** —— daemon 看不到 local files / tools / MCP / skills,除非有显式 sync / mount / orchestrator | **核心不变式 — daemon host = runtime host**: @@ -146,9 +148,9 @@ Mode B 之下进一步区分**3 种 deployment form**,钉死 daemon host 与 w | **Stage 1.5a must-haves** **(P0)** | chiga0 10 must-haves 剩 9 项 — Mode B 生产 blocker(loadSession HTTP / pair tokens / sessionScope override)| ~2 周(9 PRs 可并行)| | **Stage 1.5c** **(P0)** | daemon-side state CRUD 8 routes — Mode B 远端 client 摆脱 thin shell | ~3-5d | | **Stage 1.5-prereq** **(P1)** | chiga0 6 architecture findings — lift `AcpChannel` / `EventBus` / `PermissionMediator` 到 `@qwen-code/acp-bridge` | ~1-2 周 | -| **Stage 1.5-client adapters** **(P1 behind flag)** | TUI / channels / web/debug / IDE 接入 Mode B daemon;默认切换必须等 P0/P1 | ~2-3 周 | +| **Stage 1.5-client/render extraction** **(P1 / POC 并行)** | remote web chat / web terminal 先做 technical POC 接入 Mode B daemon;抽取 client 包与 terminal render core,供 native TUI 与 web terminal 共享渲染能力;channel / IDE 继续 `--acp`,迁移后置评估 | ~2-3 周,可与 P0 并行试点 | | **Stage 1.5b** Mode A **(P2 推迟)** | Mode A `qwen --serve` flag — [Issue #4156](https://github.com/QwenLM/qwen-code/issues/4156);A1 [PR#4160](https://github.com/QwenLM/qwen-code/pull/4160) ✅ MERGED;**推迟到 1.5c 后**(Mode A 价值依赖 1.5c)| ~5-6d | -| **Stage 1.5-remote-control** **(P2 后置)** | [PR#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) 后续作为 daemon facade | 待 primary clients 收敛 | +| **Stage 1.5-remote-control** **(P2 后置)** | [PR#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) 后续作为 daemon facade | 待 web chat / web terminal contract 稳定 | | **Stage 2** | 协议补齐(WebSocket / mDNS / OpenAPI / Prometheus / `/ext` + Reverse RPC 5 类 Client Capability)| ~3-4 周(拆 2a-2d)| | **Stage 2e** | 可选 native in-process(去 `qwen --acp` child)| ~1-2 周 | @@ -191,9 +193,8 @@ Mode B 之下进一步区分**3 种 deployment form**,钉死 daemon host 与 w > - ✅ Wave 4 **PR 15** [PR#4236](https://github.com/QwenLM/qwen-code/pull/4236) mutation gating helper + `--require-auth` — **MERGED 2026-05-17 12:10** (doudouOUC, 3h06m, 2-3 轮 review 无 Critical 全 nit suggestion;最终 +620/-24;wenshao 真起 `qwen serve` **端到端 verify 4-cell behavior matrix 各 1 次**(review quality 新基线);`createMutationGate` clean handoff for Wave 4 routes;`CONDITIONAL_SERVE_FEATURES` registry primitive;**解锁 Wave 4 PR 16-21 (6 个 PR)**) > - ✅ PR 3 follow-up [PR#4225](https://github.com/QwenLM/qwen-code/pull/4225) DaemonSessionClient hardening — **MERGED 2026-05-17 07:05** (chiga0, 3h11m open→merge, +323/-18 测试 141→205 行;多模型 /review pipeline:DeepSeek-v4-pro + claude-opus-4-7 + wenshao 4 轮 review;**chiga0 最终让步把 eager guard 改回 lazy**+ 加 `cursor monotonicity via Math.max`(同 PR#4217 reducer hardening pattern)+ abort-signal propagation + SSE `event.id` validation + test 钉 lazy-guard 防 regression) > - ⚠️ [PR#4226](https://github.com/QwenLM/qwen-code/pull/4226) typed event schema 竞品 OPEN (doudouOUC, 2026-05-17 03:58, +1398/-18, 测试/生产 1.8x);与 PR#4217 重叠,开 4 分钟后 PR#4217 即 MERGED;**待 close 或拆 SessionState reducer 部分作 Wave 5 PR 25 提前** -> - ✅ **Bonus** client adapter Stage 0 全 MERGED 2026-05-18:[PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) channel bridge (chiga0, 02:21, +2012) + [PR#4199](https://github.com/QwenLM/qwen-code/pull/4199) IDE connection (chiga0, 02:38, +1676) + [PR#4202](https://github.com/QwenLM/qwen-code/pull/4202) TUI adapter (chiga0, 03:22, +1970);Wave 5 PR 26 (`flag-gated daemon client adapters`) **提前交付** —— chiga0 按 **3 adapter × 3 stage = 9 PR** 拆分推进 -> - ⚠️ **Bonus** Stage 1 wire-up 调整:[PR#4260](https://github.com/QwenLM/qwen-code/pull/4260) TUI harness + [PR#4263](https://github.com/QwenLM/qwen-code/pull/4263) IDE Smoke Test **CLOSED 2026-05-18 06:59**(chiga0 主动;Stage 1 价值被 Stage 2 真接入 production UI 覆盖更彻底,wenshao 抓 command-layer 测试缺口后 chiga0 不再修而是放弃 Stage 1 直接走 Stage 2);仅 [PR#4261](https://github.com/QwenLM/qwen-code/pull/4261) channel `--daemon-url` 仍 draft OPEN(channel 没 Stage 2 PR) -> - 🔧 **Bonus** Stage 2 experimental flag-gated 2 PR:[PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui` (+664/-16) 真接入 Ink TUI / [PR#4267](https://github.com/QwenLM/qwen-code/pull/4267) `qwen-code.experimentalDaemonIde` config 真注入 webview (+230/-5) —— OPEN draft (chiga0, 2026-05-18 05:06 起;channel stage 2 待开) +> - ✅ **Bonus** client adapter Stage 0 全 MERGED 2026-05-18:[PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) channel bridge (chiga0, 02:21, +2012) + [PR#4199](https://github.com/QwenLM/qwen-code/pull/4199) IDE connection (chiga0, 02:38, +1676) + [PR#4202](https://github.com/QwenLM/qwen-code/pull/4202) TUI adapter (chiga0, 03:22, +1970);2026-05-19 后定位调整为 **default-off spike / future migration 参考**,不代表 native TUI/channel/IDE 默认迁 daemon +> - ⚠️ **Bonus** Stage 1/2 wire-up 调整:[PR#4260](https://github.com/QwenLM/qwen-code/pull/4260) TUI harness + [PR#4263](https://github.com/QwenLM/qwen-code/pull/4263) IDE Smoke Test **CLOSED 2026-05-18 06:59**;[PR#4261](https://github.com/QwenLM/qwen-code/pull/4261) channel `--daemon-url`、[PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui`、[PR#4267](https://github.com/QwenLM/qwen-code/pull/4267) `qwen-code.experimentalDaemonIde` 与最新主线不再一致,应收口为 draft/closed 或只吸收可复用代码到 render-core / web-terminal 方向 > - ✅ **外部** `/demo` debug page [PR#4132](https://github.com/QwenLM/qwen-code/pull/4132) — **MERGED 2026-05-18 08:31** (jifeng, **远古 4 天 24 reviews 终于合**, 最终 +858/-3);单 HTML browser-based debug UI 是 **Mode B POST+SSE 最薄 client 验证面**;XSS in `logEvent` `tag` 修:识别 `update.sessionUpdate.kind` / `event.type` 从 daemon SSE 上行是 daemon-controlled (而非 user-controlled) → 威胁模型扩到 daemon-emitted poisoned events 后 escape;rebase 过 PR#4247/4250/4251 冲突 > - ✅ Wave 5 **PR 22b/2 design slice** [PR#4304](https://github.com/QwenLM/qwen-code/pull/4304) `BridgeOptions` lift + `DaemonStatusProvider` seam — **MERGED 2026-05-19 01:27** (doudouOUC, **1h57m** open→merge, merge commit `68e3ec988a`,+852/-371 11 files;wenshao 1 轮 CHANGES_REQUESTED → 4 轮 inline 修 → qwen-latest /review LGTM → MERGED;**3-stage 改 4-stage**:原 PR 22b/2 implementation lift 拆为 22b/2 design slice (冻 contract) + 22b/3 mechanical bulk lift (~3000 LOC, 1-2h IDE-driven `git mv`);6 design decision baked in JSDoc:positional 参 / 全 envelope 返 / 无 abort/timeout / 单接口含 2 方法 / 可选 + idle fallback / acpChannelLive 由 bridge 传参;2 文件移:`BridgeOptions` interface (~150 LOC) → `acp-bridge/src/bridgeOptions.ts` + `buildDaemonPreflightCells` + `safeCheck` (~210 LOC byte-identical) → `cli/src/serve/daemonStatusProvider.ts`;新 factory `createDaemonStatusProvider()`;**Mode A in-process consumer 可省 `statusProvider?` 不崩** —— 对齐 PR 12/13 "idle status is queryable";701/701 vitest pass;**Wave 5 PR 22 现 3/4 MERGED,仅剩 22b/3 mechanical bulk lift**) > - 🔧 Follow-up [PR#4305](https://github.com/QwenLM/qwen-code/pull/4305) #4291 post-merge fix(7 threads)— **OPEN + CHANGES_REQUESTED** 2026-05-18 23:50 (doudouOUC, +454/-116 5 files;qwen-latest review on **MERGED #4291** 后 7 项 hardening:① late-poll observer 解构原始字段不再持 entry/BrandedSecret/cancelController(**memory + secret retention**)② 抽 `callerIsDeviceFlowInitiator(view, callerClientId)` 共享 helper 3 处 inline copy 换掉(**DRY+回归预防**)③ timer fire `DeviceFlowPollTimeoutError` 单实例复用 abort.reason+reject(polish)④ `err.name` 走 `sanitizeForStderr`(**log injection 姊妹堵**与 `oauthError`)⑤ timeout 路径 audit hint 用 `result.hint` 与 SSE 一致 ⑥ inline `QWEN_SERVE_DEBUG` → 现有 `isServeDebugMode()` helper + `?? ''` 死代码删(DRY+dead code)⑦ late-rejection observer `name + length` 模式对称 provider catch 不再 slice 原 message(**secret leak**);新测试 hard-negative-assert secret 不入 stderr + hostile `Error.name` sanitization;OAuth 史诗续:**+8032 LOC line if PR 21+#4291+#4305 全合**) diff --git a/docs/comparison/qwen-code-daemon-design/02-architectural-decisions.md b/docs/comparison/qwen-code-daemon-design/02-architectural-decisions.md index 5661640..5ab1f29 100644 --- a/docs/comparison/qwen-code-daemon-design/02-architectural-decisions.md +++ b/docs/comparison/qwen-code-daemon-design/02-architectural-decisions.md @@ -18,7 +18,7 @@ | **Session** | daemon 内嵌 `qwen --acp` child 的 `QwenAgent.sessions: Map` 内一条 | | **Daemon server** | HTTP/SSE API + auth + session lifecycle + EventBus projection + control-plane routes;当前在 `packages/cli/src/serve` | | **Daemon client SDK** | `DaemonClient` / `DaemonSessionClient` + typed event schema + reducer + reconnect / heartbeat / capability negotiation | -| **Client adapter** | TUI / channel / web / IDE / JSONL / stream-json / dual-output 等渲染或输出层;只消费 daemon client SDK / protocol surface | +| **Client adapter** | remote web chat / web terminal / future channel / future IDE / JSONL / stream-json / dual-output 等渲染或输出层;只消费 daemon client SDK / protocol surface。native local TUI 保持直连 runtime,但可复用 source-independent render core | | **Runtime worker** | 真正执行 model/tool/shell/MCP/skills/LSP/file operations 的 runtime;当前是 `qwen serve → qwen --acp child`,未来可替换为 sandbox runner | | **Control overlay** | remote-control / channel / web/mobile ingress;只作为 daemon facade,不拥有独立 runtime protocol | @@ -175,7 +175,7 @@ OpenCode default 是 1 daemon 多 workspace(`Map` ALS in- ### 重复 spawn 代价 -同 user 同 workspace N session 当前会有 N 套 MCP children。多 workspace 同 user = M daemon × N session × MCP sets。单 MCP ~50-200MB,若未来 TUI / channels / IDE 大量并发 session,MCP pool/proxy 会变成 Stage 2/2e 的资源优化项。 +同 user 同 workspace N session 当前会有 N 套 MCP children。多 workspace 同 user = M daemon × N session × MCP sets。单 MCP ~50-200MB,若 remote web chat / web terminal 或未来 channel / IDE daemon 化带来大量并发 session,MCP pool/proxy 会变成 Stage 2/2e 的资源优化项。 --- @@ -274,22 +274,23 @@ ACP 协议本身就是"client → agent → 同步 response"语义,不允许 ### 决策 -**2026-05-15 后,roadmap 先只推进 Mode B。** Mode A 仍作为设计记录保留,但不再是 Stage 1.5 主线。 +**2026-05-19 后,roadmap 先只推进 Mode B 的 remote web chat / web terminal POC + daemon server 能力。** Mode A 仍作为设计记录保留,但不再是 Stage 1.5 主线。native TUI、channel、IDE 默认链路暂不迁移。 | 模式 | 启动命令 | TUI | 适用场景 | |---|---|---|---| -| **Mode B: Headless + HttpServer** | `qwen serve [--port N]` | ❌ | 当前主线:服务器 / 容器 / 远端机器 / K8s pod / 所有 client 的统一 runtime | +| **Mode B: Headless + HttpServer** | `qwen serve [--port N]` | ❌ | 当前主线:服务器 / 容器 / 远端机器 / K8s pod / remote web chat + web terminal | | **Mode A: CLI + HttpServer** | `qwen --serve [--port N]` | ✅ 本地 | 暂停推进;待 Mode B contract 稳定后再评估 | -当前 client 直接边界是 HTTP server:TUI / channels / web / IDE 通过 `DaemonSessionClient` 调用 `POST /session/:id/*`,通过 `GET /session/:id/events` 消费 SSE。EventBus 是 daemon 内部 fan-out primitive;`EventBus lift` 指抽出 typed event contract / reducer / server-side primitive,不是让外部 client 直接 subscribe 内存对象。 +当前 daemon-native client 的直接边界是 HTTP server:remote web chat / web terminal 通过 `DaemonSessionClient` 调用 `POST /session/:id/*`,通过 `GET /session/:id/events` 消费 SSE。EventBus 是 daemon 内部 fan-out primitive;`EventBus lift` 指抽出 typed event contract / reducer / server-side primitive,不是让外部 client 直接 subscribe 内存对象。native TUI、channel、IDE 当前默认链路不变。 ### 依据 1. **PR#3889 已实现 Mode B**(Stage 1 ✅ MERGED 2026-05-13)。 2. **PR#4113 已把 Mode B 边界收紧到 1 daemon = 1 workspace**(✅ MERGED 2026-05-15)。 -3. **TUI / channels / web / IDE 是 primary clients**,应先统一到 daemon HTTP/SSE + typed event contract。 -4. **remote-control 后置**,等 primary clients 收敛后再作为 daemon facade 复用同一 contract。 -5. **Mode A 暂停**,避免在 event/control/client contract 稳定前引入第二条 in-process 暴露路径。 +3. **remote web chat / web terminal 是 primary daemon clients**,应优先做 technical POC,先统一到 daemon HTTP/SSE + typed event contract。 +4. **native TUI / channel / IDE 不作为当前默认迁移目标**:TUI 复用 render core 但继续直连,channel / IDE 继续 `--acp` 优先。 +5. **remote-control 后置**,等 web contract 稳定后再作为 daemon facade 复用同一 contract。 +6. **Mode A 暂停**,避免在 event/control/client contract 稳定前引入第二条 in-process 暴露路径。 ### TUI 在多 session daemon 下的语义 @@ -297,7 +298,7 @@ ACP 协议本身就是"client → agent → 同步 response"语义,不允许 - **本地单用户 `qwen` TUI = 传统单进程 in-process direct call**,**永远不走网络**(local default 永久路径,最高优先级 UX)。详下方 🌟 设计原则。 - **Mode B TUI adapter**([PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui`)= **opt-in advanced**,仅 multi-client 协作场景。**永远 behind flag,不进入 default migration**。 -- **channels / web / IDE / remote TUI** 通过 `DaemonSessionClient` 接入——它们本来就是跨进程 client,daemon 是它们唯一可行路径。 +- **remote web chat / web terminal** 通过 `DaemonSessionClient` 优先接入,用来验证 daemon contract;channel / IDE 继续 `--acp`,daemon 迁移后置。 - **daemon-side control-plane parity** 落地后,跨进程 remote clients 才能覆盖 memory / MCP / skills / tools / agents / auth / provider / context 等状态。 - **Mode A** 保留为 parking lot;如未来要支持 "本地多 client 协作"(IDE + TUI 同 session live collaboration),**Mode A in-process TUI + 内嵌 HTTP server 可能比 Mode B + TUI adapter 更合适**——in-process TUI 保留零网络体验,HTTP server 仅服务其他 client。待 chiga0 auto-daemon UX 工程债重新评估时一并 revisit。 @@ -318,7 +319,7 @@ ACP 协议本身就是"client → agent → 同步 response"语义,不允许 **因此**: - ❌ "Local-Local 用户 TUI 通过 loopback `qwen serve`" **不能成为现有用户默认迁移目标**(与 §04 §六 deployment shapes table 早期措辞冲突,已修正) - ❌ chiga0 #3803 comment 4476174099 的 "auto-daemon UX" 草图(`qwen → discover daemon → if absent auto-start → attach TUI`)**仅适用于用户主动选 multi-client 协作场景**,不适合作 local default -- ✅ Wave 5 PR 26 `flag-gated daemon client adapters` **scope 收紧**:仅 channel / web / IDE default 切换,**TUI default 不切换** +- ✅ Wave 5 PR 26 **scope 收紧**:remote web chat / web terminal POC + render-core extraction 优先;channel / IDE default migration 后置;**TUI default 不切换** --- @@ -332,7 +333,8 @@ Mode B 终态架构按三层逻辑边界收敛: ```text client adapters / output sinks - TUI / channel / web chat / web terminal / IDE / JSONL / stream-json + web chat / web terminal / future channel / future IDE / JSONL / stream-json + native TUI shares render core but keeps direct transport ↓ depend on daemon client/protocol layer DaemonClient / DaemonSessionClient / typed event schema / reducer @@ -364,10 +366,12 @@ runtime worker / sandbox runner typed event schema, reducers, reconnect, heartbeat, capability negotiation @qwen-code/daemon-adapters-* - tui adapter - channel adapter - ide/web adapter - jsonl / stream-json / dual-output sinks + web chat adapter + web terminal adapter + optional channel / IDE adapters after daemon stabilization + +@qwen-code/client-render + source adapters / shared view model / terminal render core / output sinks ``` **硬约束**: @@ -375,27 +379,27 @@ runtime worker / sandbox runner | 约束 | 含义 | |---|---| | Server 不依赖 adapters | `serve` / daemon server 不能 import TUI / IDE / channel renderer | -| Adapters 只依赖 client/protocol | TUI / channel / web / IDE 不可直接拿 `HttpAcpBridge` / EventBus 内部对象 | +| Adapters 只依赖 client/protocol | remote web chat / web terminal / future channel / future IDE 不可直接拿 `HttpAcpBridge` / EventBus 内部对象 | | Reducer / typed events 在 client/protocol 层 | 不能散落在 `packages/cli/src/serve` 或某个 adapter 私有实现里 | -| Output sinks 也是 adapter | JSONL / stream-json / dual-output 走同一 typed event stream,不再各自驱动 runtime | +| Render core source-independent | native streamJson 与 daemon events 可投影到同一 view model,但 native TUI 不因此改走 daemon transport | +| Output sinks 也是 adapter | JSONL / stream-json / dual-output 可复用 typed event semantics,但不改变现有 CLI 输出兼容 | ### Daemon-native renderer 是目标形态 -TUI adapter、web terminal、web chat、IDE panel、channel cards 都应消费同一 typed daemon event contract: +Web terminal、web chat 先消费同一 typed daemon event contract;future IDE panel / channel cards 若迁移也复用同一 contract: ```text daemon SSE event → shared reducer / view model → renderer-specific projection - ├─ Ink TUI ├─ DOM chat ├─ DOM terminal-like view - ├─ IDE panel - ├─ channel message/cards + ├─ future IDE panel + ├─ future channel message/cards └─ JSONL / stream-json / dual-output sink ``` -**PTY proxy 不是主线架构**。它可作为兼容 / demo / debug fallback,但不能成为 web terminal 或 TUI 迁移目标,因为它代理 terminal bytes,会绕开 typed event / reducer convergence,并重新耦合 runtime process lifecycle。 +**PTY proxy 不是主线架构**。它可作为兼容 / demo / debug fallback,但不能成为 web terminal 目标架构,因为它代理 terminal bytes,会绕开 typed event / reducer convergence,并重新耦合 runtime process lifecycle。native TUI 继续直连,不需要 daemon-native renderer 作为默认路径。 ### Runtime worker / sandbox runner boundary @@ -441,11 +445,11 @@ remote-control 后续不得重新拥有 runtime、event log 或 worker server。 这条架构决策不要求当前 PR 立即重构实现,但会改变后续 PR 的验收标准: 1. `DaemonSessionClient` 继续作为 client adapter 的唯一 session 级入口。 -2. typed event schema + reducer 必须逐步成为 TUI / web / IDE / channel / JSONL / stream-json 的共享消费面。 -3. TUI / web terminal wire-up 应做 daemon-native renderer,而不是 PTY proxy 默认路径。 +2. typed event schema + reducer 必须先成为 web chat / web terminal / JSONL / stream-json 的共享消费面;channel / IDE 后续若迁移也复用该 contract。 +3. web terminal wire-up 应做 daemon-native renderer,而不是 PTY proxy 默认路径;native TUI 只共享 render core,不改默认 transport。 4. channel / remote-control 只能做 daemon facade / ingress overlay,不可 fork runtime/event protocol。 5. sandbox / runtime worker 抽象应在 Wave 5/Stage 2 后续设计中预留 failure isolation 和 runtime locality diagnostics。 -6. 所有 client PR 必须 behind flag / default off,且声明验证的 deployment shape 和 locality 假设(详 [§04](./04-deployment-and-client.md#deployment-shape-matrixclientruntime-lens2026-05-18))。 +6. 所有 daemon client PR 必须 behind flag / explicit entrypoint / default off,且声明验证的 deployment shape 和 locality 假设(详 [§04](./04-deployment-and-client.md#deployment-shape-matrixclientruntime-lens2026-05-18))。 --- @@ -459,8 +463,8 @@ remote-control 后续不得重新拥有 runtime、event log 或 worker server。 | 4 | FileReadCache 共享 | **per-session 严格私有**(同 daemon N session 各自实例不共享;跨 daemon 自然独立)+ PR#3774 prior-read 守卫 + PR#3810 5 路径 invalidation | PR#3717 / PR#3774 / PR#3810 | | 5 | Permission flow | 复用 PR#3723 + daemon 第 4 mode + SSE permission_request | PR#3723 evaluatePermissionFlow() | | 6 | 多 client 并发 | **同 session prompt 串行(FIFO)+ 事件 fan-out + 任何 client 可应答 permission** | PR#3889 commit `ca996ecb5`(FIFO + no-poison)+ ACP 协议语义 + EventBus subscriber set | -| 7 | 部署模式 | **Mode B mainline;Mode A parking lot** | 2026-05-15 决策:优先 TUI / channels / web / IDE 接入 Mode B;remote-control 后置 | -| 8 | Server / client / runtime boundary | **server control plane / daemon client-protocol / adapters / runtime worker 分层**;daemon-native renderer 为目标;remote-control 只是 control overlay | 2026-05-18 deployment/package/runtime comments;§04 deployment shape matrix;§06 deployment + package contract | +| 7 | 部署模式 | **Mode B mainline;Mode A parking lot** | 2026-05-19 决策:优先 remote web chat / web terminal POC;native TUI 直连;channel / IDE 迁移后置;remote-control 后置 | +| 8 | Server / client / runtime boundary | **server control plane / daemon client-protocol / shared render core / adapters / runtime worker 分层**;web terminal daemon-native;remote-control 只是 control overlay | 2026-05-18/19 deployment/package/runtime comments;§04 deployment shape matrix;§06 deployment + package contract | --- diff --git a/docs/comparison/qwen-code-daemon-design/03-http-api.md b/docs/comparison/qwen-code-daemon-design/03-http-api.md index b89211a..0533ed6 100644 --- a/docs/comparison/qwen-code-daemon-design/03-http-api.md +++ b/docs/comparison/qwen-code-daemon-design/03-http-api.md @@ -42,7 +42,7 @@ POST /permission/:requestId vote on permission request | PR | Route / 能力 | 说明 | |---|---|---| | **PR#4191** ✅ | capability registry refactor | hard-coded `STAGE1_FEATURES` → plug-in registry,未来 routes additive 注册不需改 capability 数组 | -| **PR#4205** ✅ | `DaemonSessionClient` skeleton | SDK 侧统一接口(HTTP/SSE 之上 `subscribe()` / `prompt()` / `cancel()`),TUI / IDE / channels 共享 reducer | +| **PR#4205** ✅ | `DaemonSessionClient` skeleton | SDK 侧统一接口(HTTP/SSE 之上 `subscribe()` / `prompt()` / `cancel()`),remote web chat / web terminal 先共享 reducer;channel / IDE future migration 复用 | | **PR#4209** ✅ | typed `SessionEvent` / `ControlEvent` schema 草案 | wire envelope `{id, v, type, data, originatorClientId?}` + zod schema lockstep | | **PR#4217** ✅ | typed event schema **v1** | `narrowDaemonEvent()` discriminated union;未知 type → `kind: 'unknown'` 向前兼容;reducer skeleton | | **PR#4214** ✅ | 三套来源 lockstep 立 invariant | 生产 `SERVE_CAPABILITY_REGISTRY` ↔ unit `EXPECTED_STAGE1_FEATURES` ↔ integration `caps.features` `toEqual` | @@ -469,7 +469,7 @@ type DaemonErrorKind = | **Wave 2.5**(reliability)| heartbeat / Last-Event-ID replay / slow_client_warning / session metadata + close-delete | ✅ 3/3 MERGED | | **Wave 3**(read-only control plane)| status routes / preflight + env diagnostics / MCP guardrails + budget push | ✅ 3/3 MERGED | | **Wave 4**(auth-gated mutation)| mutation gate / memory&agents CRUD / approval+tools+init / FS boundary / file r/w / OAuth device-flow | ✅ 7/7 MERGED | -| **Wave 5**(architecture extraction)| bridge primitives / MCP shared pool / PermissionMediator / output sinks / flag-gated adapters | 🟢 22a + 22b/1 + 22b/2 design ✅ / 22b/3 mechanical 待开 | +| **Wave 5**(architecture extraction)| bridge primitives / MCP shared pool / PermissionMediator / output sinks / web client + render-core extraction | 🟢 22a + 22b/1 + 22b/2 design ✅ / 22b/3 mechanical 待开 | | **Wave 6**(release hardening + v0.16)| docs / metrics / changelog / RC / GA | 🚧 待启动 | **业务逻辑 100% 同源**——daemon 复用 ACP zod schema 与 `Session.handleXxx`,HTTP 仅是传输层桥接。Wave 5 PR 22 系列剥离桥接 primitives 后,未来 Stage 2 native in-process(直接 import `QwenAgent`,去 `qwen --acp` child)只是另一种 transport,wire 协议不变。 diff --git a/docs/comparison/qwen-code-daemon-design/04-deployment-and-client.md b/docs/comparison/qwen-code-daemon-design/04-deployment-and-client.md index 7fb471a..2450714 100644 --- a/docs/comparison/qwen-code-daemon-design/04-deployment-and-client.md +++ b/docs/comparison/qwen-code-daemon-design/04-deployment-and-client.md @@ -4,11 +4,11 @@ ## TL;DR -**2026-05-15 更新**:先忽略 Mode A(`qwen --serve`),以 **Mode B `qwen serve`** 作为唯一主线。TUI / channels / web / IDE 都应作为 daemon HTTP/SSE client 接入;EventBus 是 daemon 内部 fan-out primitive,client 共享的是 typed event contract + reducer + `DaemonSessionClient`,不是直接订阅内存 EventBus。 +**2026-05-19 更新**:先忽略 Mode A(`qwen --serve`)。**Mode B `qwen serve`** 是 daemon server 主线,但不再要求本地 native TUI 默认迁到 daemon:现有 `qwen` TUI 直连链路长期保留。daemon 第一阶段优先服务 **remote web chat + web terminal**;这条技术 POC 可与 P0 must-haves / state CRUD 并行启动,用真实 browser/BFF 体验验证 HTTP/SSE typed event contract。channel / IDE 插件继续使用现有 `--acp` 模式,daemon 迁移优先级后置。 **多 client 协调**(P1 拓扑):subscriber 协议 + liveness(15s SSE heartbeat + TCP RST 即时剔除)+ active typer 提示 + takeover + first-responder permission。 -**Deployment shapes(Mode B)**:runtime 维度仍是 Local-Local / Local-Remote(**不推荐**)/ Remote-Remote(**推荐**,workspace 与 daemon 同机);client 维度进一步拆成 local-local、web chat/terminal-remote、local IDE-local daemon、local TUI-remote、channel+daemon、remote-control overlay。Web terminal 的目标是 **daemon-native renderer**(typed event + reducer),PTY proxy 只作为兼容 / demo / debug fallback。Client Capability 反向 RPC(5 类:editor / clipboard / browser / notification / file_picker)让 daemon 调起 client 本地资源——属 External Reference Architecture 范畴。 +**Deployment shapes(Mode B)**:runtime 维度仍是 Local-Local / Local-Remote(**不推荐**)/ Remote-Remote(**推荐**,workspace 与 daemon 同机);client 维度进一步拆成 native local TUI、local/remote web chat + web terminal、future channel/IDE、remote-control overlay。Web terminal 的目标是 **daemon-native renderer**(typed event + reducer),PTY proxy 只作为兼容 / demo / debug fallback。Client Capability 反向 RPC(5 类:editor / clipboard / browser / notification / file_picker)让 daemon 调起 client 本地资源——属 External Reference Architecture 范畴。 --- @@ -16,7 +16,7 @@ | 模式 | 启动命令 | TUI | 适用场景 | |---|---|---|---| -| **Mode B: Headless + HttpServer** | `qwen serve [--port N]` | ❌ | 当前主线:服务器 / 容器 / 远端机器 / 所有 client 的统一 runtime | +| **Mode B: Headless + HttpServer** | `qwen serve [--port N]` | ❌ | 当前主线:服务器 / 容器 / 远端机器 / remote web chat + web terminal | | **Mode A: CLI + HttpServer** | `qwen --serve [--port N]` | ✅ 本地 | 暂停推进;parking lot | 当前主线只有 Mode B。详 [§02 §7](./02-architectural-decisions.md#7-部署模式--mode-b-mainline--mode-a-parking-lot)。 @@ -39,7 +39,7 @@ > > **走 daemon 的场景仅限**:远端 client / 跨进程多 client 协作(IDE + TUI 同 session live collaboration)/ channel bot / web BFF。本地单用户 TUI **永远不被强制套上 HTTP 包袱**。 > -> [PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui` 是 **opt-in advanced 路径**(用户主动选 multi-client 协作时),**永远 behind flag,不进入 default migration**。Wave 5 PR 26 `flag-gated daemon client adapters` 默认覆盖 channel / web / IDE,**不含 TUI default 切换**。Mode A `qwen --serve`(TUI super-client + 内嵌 HTTP server)可能比 Mode B + TUI adapter 更适合 "本地多 client 协作" —— 待 chiga0 `auto-daemon UX` 工程债重新评估时一并 revisit。 +> [PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui` 是 **opt-in advanced 路径**(用户主动选 multi-client 协作时),**永远 behind flag,不进入 default migration**。当前阶段不推进 channel / IDE 默认迁 daemon;remote web chat + web terminal POC 先行,用它验证 daemon contract。Mode A `qwen --serve`(TUI super-client + 内嵌 HTTP server)可能比 Mode B + TUI adapter 更适合 "本地多 client 协作" —— 待 chiga0 `auto-daemon UX` 工程债重新评估时一并 revisit。 ### Client 接入顺序 @@ -112,9 +112,9 @@ QWEN_DAEMON_WORKSPACE=/repo | `session_died` | Notify user + stop stream | | **Unknown events** | **Ignore or forward as debug, NOT fatal** | -### 5 Blockers before channel/web default migration +### 5 Blockers before web/BFF production use and future channel migration -[PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) 明确:channel / web client 默认切换 daemon 前必须先 ship 以下 5 项(全部来自 [Issue #4175](https://github.com/QwenLM/qwen-code/issues/4175) Wave 2-3): +[PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) 明确了 channel/web BFF 安全边界。按 2026-05-19 最新结论,remote web chat / web terminal 是第一阶段重点;channel 默认仍走 `--acp`,daemon bridge 仅作 future behind-flag 评估。web/BFF 生产使用,以及未来 channel daemon 化,都至少依赖以下 5 项(全部来自 [Issue #4175](https://github.com/QwenLM/qwen-code/issues/4175) Wave 2-3): | # | Blocker | 对应 PR | 状态(2026-05-18)| |---|---|---|---| @@ -138,7 +138,7 @@ QWEN_DAEMON_WORKSPACE=/repo ## 二、TUI / client 边界 -Stage 1 的 Mode B client 只能覆盖 conversation 主链路。要让 TUI / channels / web / IDE 成为完整 client,需要先补 P0 的 production must-haves 与 daemon-side control-plane parity,再补 P1 typed event contract / bridge primitives;client adapters 只能先 behind flag。 +Stage 1 的 Mode B client 只能覆盖 conversation 主链路。要让 remote web chat / web terminal 成为完整 client,需要先补 P0 的 production must-haves 与 daemon-side control-plane parity,再补 P1 typed event contract / bridge primitives;web POC 可以先 explicit entrypoint / behind flag 并行验证,channel / IDE 迁移后置。 ### TUI 形态 4 种 @@ -153,7 +153,7 @@ Stage 1 的 Mode B client 只能覆盖 conversation 主链路。要让 TUI / cha > > 本地单用户 TUI **不需要** loopback HTTP / port / token / discovery / lifecycle / control-plane parity wire 化,**就不应该被强加这些代价**。`qwen` 默认是 daemon 项目里**最高优先级的用户体验**,任何 default migration 设计都不可破坏这个 UX。 > -> 走 daemon convergence 的 client(channel / web / IDE / remote TUI)是**主动选择跨进程协作**的场景;本地单用户 TUI **不在此列**。 +> 当前走 daemon convergence 的主场景是 **remote web chat / web terminal**。channel / IDE / remote TUI 是 future behind-flag 评估;本地单用户 TUI **不在此列**。 ### TUI 与 wire 的边界 — 9 项 dialogs 真实成本 @@ -204,7 +204,7 @@ Mode A `qwen --serve` 设计暂时 hold。本节保留原问题作为 future eva | TUI 是否同进程 co-host daemon | HOLD | | TUI 是否绑定一个 session 还是 attach 任意 session | HOLD | | TUI local dialogs 是否 wire 化 | 先通过 Mode B control-plane parity 解决 | -| TUI 是否可作为纯 daemon client | **优先 behind flag 试点**:Stage 1.5c TUI adapter;默认切换等 P0/P1 | +| TUI 是否可作为纯 daemon client | 仅保留 opt-in advanced / future experiment;本地 native TUI 默认永远直连,不进入默认切换 | --- @@ -306,17 +306,16 @@ T=25 Alice 笔记本醒来重连 → daemon 通知 "Bob 接管了" | Shape | Client / adapter | Daemon + runtime | Workspace | 主要要求 | 推荐阶段 | |---|---|---|---|---|---| -| **1. Local - Local (本地单用户 TUI)** 🌟 | **本地 `qwen` TUI = in-process direct call**(永远不走网络)| 不存在 daemon | 本地 | **零网络栈代价**;本地 TUI 不参与 daemon convergence(详 [§04 §一 设计原则](#mode-b-拓扑核心特征) + [§02 §7](./02-architectural-decisions.md#7-部署模式--mode-b-mainline--mode-a-parking-lot))| **永久 default UX,最高优先级** | -| **1b. Local - Local (multi-client 协作)** | IDE / local web / channel adapter(**TUI 不在此列**)| 本机 loopback `qwen serve` | 本机 | auto-daemon discovery / loopback auth / lifecycle / port+token+logs;control-plane parity | 跨进程多 client 协作场景;TUI 默认 in-process 不切换 | -| **2. Web chat / web terminal - Remote** | browser UI | remote devbox / pod | remote volume | gateway/auth/CORS;SSE reconnect;runtime diagnostics;web terminal 走 daemon-native renderer,PTY proxy 仅 fallback | cloud/devbox P1 | -| **3. Local IDE - Local daemon** | IDE extension | 本机 loopback daemon | IDE 当前 workspace | IDE 启动/发现 daemon;workspace mismatch preflight;editor context 显式传入;path identity 共享 | P1 early dogfood | -| **4. Local TUI - Remote** | 本地 terminal renderer | remote daemon/runtime | remote workspace | TUI 明示 remote label/path/auth;local cwd 非 runtime cwd;path mapping / client capability reverse RPC;latency coalescing | P1 after TUI adapter quality | -| **5. Channel + daemon** | IM/channel backend adapter | local 或 remote daemon | daemon-bound workspace | user/group/thread → session routing;identity mapping;permission cards;dedupe;长任务通知 | P1 behind flag | -| **6. Remote-control overlay** | web/mobile/channel control surface | local daemon + bridge 或 remote daemon + gateway | local workstation 或 remote devbox | pairing/revoke;outbound bridge;audit;不 fork runtime/protocol | P2 after client contract stabilizes | +| **1. Native local TUI** | local terminal `qwen` | 无需 daemon | 本机 | 保留 direct runtime / streamJson / Ink;可复用 shared render core | 现有用户默认 | +| **2. Local web chat / web terminal** | browser UI + local BFF | 本机 loopback `qwen serve` | 本机 | BFF 持 token;loopback auth;shared render core;可做 local web POC | optional / POC | +| **3. Web chat / web terminal - Remote** | browser UI | remote devbox / pod | remote volume | gateway/auth/CORS;SSE reconnect;runtime diagnostics;web terminal 走 daemon-native renderer,PTY proxy 仅 fallback | cloud/devbox POC + P1 | +| **4. Channel + daemon** | IM/channel backend adapter | local 或 remote daemon | daemon-bound workspace | 默认仍 `--acp`;daemon bridge 后续评估;需 user/thread routing、permission cards、dedupe | future behind flag | +| **5. IDE plugin** | IDE extension | 默认 `--acp` child;daemon 后续评估 | IDE workspace | 保留当前成熟路径;daemon 化需 editor context、path identity、session/control parity | future behind flag | +| **6. Remote-control overlay** | web/mobile/channel control surface | local daemon + bridge 或 remote daemon + gateway | local workstation 或 remote devbox | pairing/revoke;outbound bridge;audit;不 fork runtime/protocol | P2 after web contract stabilizes | #### Web terminal:daemon-native renderer,不是 PTY proxy 主线 -Web chat 和 web terminal 都应消费 daemon typed events / reducer。差异只在 UI 呈现:chat UI 用 DOM chat renderer,web terminal 用 terminal-like renderer。TUI adapter 已经是在做 Ink/terminal 版的 **daemon-native renderer**;web terminal 应复用同一 contract。 +Web chat 和 web terminal 都应消费 daemon typed events / reducer。差异只在 UI 呈现:chat UI 用 DOM chat renderer,web terminal 用 terminal-like renderer。Web terminal 应复用 native TUI 的 terminal view model / Ink→ANSI 渲染核心,但不要求 native TUI 改走 daemon。 PTY proxy 仍可作为兼容 / demo / debug fallback,但不能成为目标架构:它代理 terminal bytes,会重新耦合进程生命周期,并绕开 typed event / reducer convergence。 @@ -375,7 +374,7 @@ Laptop Remote workstation | 选项 | 部署 | TUI 体验(Stage 1)| TUI 体验(Stage 1.5c + 1.5-prereq 后)| |---|---|---|---| | **A. SSH + 单进程** | SSH 进远端机器跑 `qwen` | ✅ 完整本地 TUI | ✅ 完整 | -| **B. Mode B + 远端 TUI client** | 远端 `qwen serve` headless,本地用 TUI adapter attach | ⚠️ thin shell | ✅ **接近完整**(除 `/ide` 等场景)| +| **B. Mode B + web terminal** | 远端 `qwen serve` headless,浏览器 terminal-like renderer | ⚠️ thin shell | ✅ 接近完整(依赖 shared render core + daemon control-plane parity)| | **C. Mode A** | `qwen --serve` | ⏸ HOLD | 待重新评估 | --- @@ -489,7 +488,7 @@ Laptop Remote workstation ## 七、与 PR#3929-3931 (remote-control stack) 的关系 -2026-05-15 决策后,remote-control 优先级后置。它仍然可以作为 mobile/browser/channel control overlay 存在,但不应继续拥有 parallel runtime / event log / worker server。正确方向是等 TUI / channels / web / IDE 先收敛到 Mode B daemon contract 后,remote-control 再复用同一 `DaemonSessionClient` + HTTP/SSE typed event contract。 +2026-05-15 决策后,remote-control 优先级后置。它仍然可以作为 mobile/browser/channel control overlay 存在,但不应继续拥有 parallel runtime / event log / worker server。2026-05-19 修订后,正确方向是等 **remote web chat / web terminal** 先稳定 Mode B daemon contract 后,remote-control 再复用同一 `DaemonSessionClient` + HTTP/SSE typed event contract;native TUI / channel / IDE 不再作为当前阶段默认迁移前置。 **重要澄清(2026-05-18)**:remote-control 不要求 daemon 必须在远端。它至少有两种有效部署形态: @@ -504,15 +503,15 @@ PR#3929-3931 当前仍是 draft / changes requested。简表: | 维度 | Mode B daemon mainline | PR#3929-3931 stack | |---|---|---| -| 入口 | `qwen serve` + client adapters | `qwen remote-control` worker / `qwen --remote-control` attach | +| 入口 | `qwen serve` + web chat / web terminal clients | `qwen remote-control` worker / `qwen --remote-control` attach | | 传输 | HTTP + SSE(Stage 2 可选 WebSocket facade)| HTTP + WebSocket + stream-json + dual-output JSONL | | 协议复用 | 100% 复用 ACP zod schema | 复用 dual-output + stream-json 包装 | | session 模型 | 1 daemon = 1 workspace × N session multiplexed | Worker server spawn / attach 当前 TUI | | 多 client 共 session | ✅ live collaboration + first-responder | ⚠️ mobile/browser attach 同 session 但首要场景单 mobile + 当前 TUI 双视图 | -| Mobile / browser UI | 先由 web/debug + IDE/TUI adapters 定 contract | ✅ 自带最小化 mobile/browser UI(PR#3930 +2564 行)| +| Mobile / browser UI | 先由 web chat / web terminal + `/demo` 定 contract | ✅ 自带最小化 mobile/browser UI(PR#3930 +2564 行)| | Pairing token + LAN URL | ❌ 仅 bearer token | ✅ 一次性 pairing token + LAN URL 报告 | | Capability 反向 RPC(5 类)| ✅ editor / clipboard / browser / notification / file_picker | ❌ 无反向 RPC——permission approve/deny 通过 stream-json 直接路由 | -| 2026-05-15 处理 | 主线;优先 TUI / channels / web / IDE | 后置;未来应改为 daemon facade | +| 2026-05-19 处理 | 主线;优先 remote web chat / web terminal;native TUI 直连保留,channel/IDE 继续 `--acp` 优先 | 后置;未来应改为 daemon facade | --- diff --git a/docs/comparison/qwen-code-daemon-design/06-roadmap.md b/docs/comparison/qwen-code-daemon-design/06-roadmap.md index cc72055..87e10f4 100644 --- a/docs/comparison/qwen-code-daemon-design/06-roadmap.md +++ b/docs/comparison/qwen-code-daemon-design/06-roadmap.md @@ -4,7 +4,7 @@ ## TL;DR -**2026-05-15 roadmap reset**:先忽略 Mode A(`qwen --serve`),以 **Mode B:`qwen serve` headless daemon** 作为唯一主线。Stage 1 ✅ MERGED 2026-05-13(PR#3889)→ Stage 1.5a ✅ MERGED 2026-05-15(PR#4113,1 daemon = 1 workspace)→ Stage 1.5 继续做 Mode B event/control/client convergence。执行优先级对齐 upstream 最新 roadmap:**P0 = 9 个 production must-haves + daemon-side state CRUD**;client adapters 先 behind flag,默认切换必须等 P0/P1 基础完成;remote-control / Mode A 后置。Stage 2 做协议和生态补齐。**Stage 2 后协议表面锁定**——后续不扩展 wire 协议;平台层(orchestrator / 多租户 / sandbox / SaaS)由 External Reference Architecture 实施。 +**2026-05-19 roadmap reset**:先忽略 Mode A(`qwen --serve`),以 **Mode B:`qwen serve` headless daemon** 作为 daemon server 主线。Stage 1 ✅ MERGED 2026-05-13(PR#3889)→ Stage 1.5a ✅ MERGED 2026-05-15(PR#4113,1 daemon = 1 workspace)→ Stage 1.5 继续做 Mode B event/control/client convergence。执行优先级调整为:**P0 = production must-haves + daemon-side state CRUD**;**P1 / POC 并行 = remote web chat + web terminal technical POC + shared client/render contract**;channel / IDE daemon migration 后置,native TUI 默认永远直连。remote-control / Mode A 后置。Stage 2 做协议和生态补齐。**Stage 2 后协议表面锁定**——后续不扩展 wire 协议;平台层(orchestrator / 多租户 / sandbox / SaaS)由 External Reference Architecture 实施。 **与竞品定位**:qwen-code daemon = building block + Unix-style 可组合;OpenCode 走端到端 SaaS;Anthropic Managed Agents 是托管服务。详 §五 / §六。 @@ -20,7 +20,7 @@ qwen-code 主线 1.5a must-haves/P0 ███ identity + lifecycle + reliability must-haves(~1-2w) Stage 1.5c/P0 ███ daemon-side control-plane parity / state CRUD(~1-2w) 1.5-prereq/P1 ███ typed event contract + shared DaemonSessionClient(~1w) - client adapters/P1 █████ primary clients behind flag: TUI / channels / web / IDE(~2-3w) + web POC/P1 █████ remote web chat + web terminal 技术 POC + shared render/client core(~1-2w,并行 P0) remote-control/P2 ▒▒▒ remote-control revisit(后置,复用 daemon facade) Stage 1.5b/P2 ▒▒▒ Mode A revisit(后置) Stage 2 ████████ 2a-2d(~3-4w) @@ -114,7 +114,7 @@ capability registry → DaemonSessionClient → typed events | **2.5** Reliability + session lifecycle closure | client heartbeat + SSE replay sizing + slow-client warnings + session metadata/close-delete lifecycle | PR 9-11 — ✅ **3/3 MERGED**:PR 9 [#4235](https://github.com/QwenLM/qwen-code/pull/4235) + PR 10 [#4237](https://github.com/QwenLM/qwen-code/pull/4237) + PR 11 [#4240](https://github.com/QwenLM/qwen-code/pull/4240) 2026-05-17 | 1.5a #4 reliability + #2 lifecycle closure | | **3** Read-only control plane + diagnostics | read-only status routes + preflight/env diagnostics + MCP guardrails (measurement, not full pool) | PR 12-14 — ✅ **3/3 + PR 14b 全 MERGED**:PR 12 [#4241](https://github.com/QwenLM/qwen-code/pull/4241) + PR 13 [#4251](https://github.com/QwenLM/qwen-code/pull/4251) 2026-05-17 + PR 14 [#4247](https://github.com/QwenLM/qwen-code/pull/4247) 2026-05-18 + follow-up PR 14b [#4271](https://github.com/QwenLM/qwen-code/pull/4271) 2026-05-18 17:06 | 1.5c read-only + chiga0 diagnostics | | **4** Auth-gated mutation/control routes | **mutation gating helper** + memory/agents CRUD + approval/tools/init/MCP-restart + FileSystemService 边界 + safe file read + file write/edit + auth device-flow | PR 15-21 — ✅ **7/7 完整 MERGED** 🎉:PR 15/16/17/18/19/20/21 全合 (#4236/#4249/#4282/#4250/#4269/#4280/#4255) | 1.5c CRUD + 文件 routes | -| **5** Architecture extraction + output sinks + full multi-client security | bridge primitives extraction + real MCP shared pool (config-hash keyed) + pairing revocation + full PermissionMediator + 独立 output sinks + flag-gated client adapters | PR 22-26 — ✅ PR 22a [#4295](https://github.com/QwenLM/qwen-code/pull/4295) MERGED + ✅ PR 22b/1 [#4298](https://github.com/QwenLM/qwen-code/pull/4298) MERGED + ✅ PR 22b/2 [#4304](https://github.com/QwenLM/qwen-code/pull/4304) MERGED design slice + ⏳ PR 22b/3 待开 mechanical bulk lift(**PR 22 拆 4 阶段 3/4 MERGED**);PR 23/24/25/26 ⏳ | 1.5-prereq full + 1.5a #3 full + adapter migration | +| **5** Architecture extraction + output sinks + full multi-client security | bridge primitives extraction + real MCP shared pool (config-hash keyed) + pairing revocation + full PermissionMediator + 独立 output sinks + remote web chat / web terminal POC + shared render/client core | PR 22-26 — ✅ PR 22a [#4295](https://github.com/QwenLM/qwen-code/pull/4295) MERGED + ✅ PR 22b/1 [#4298](https://github.com/QwenLM/qwen-code/pull/4298) MERGED + ✅ PR 22b/2 [#4304](https://github.com/QwenLM/qwen-code/pull/4304) MERGED design slice + ⏳ PR 22b/3 待开 mechanical bulk lift(**PR 22 拆 4 阶段 3/4 MERGED**);PR 23/24/25/26 ⏳ | 1.5-prereq full + 1.5a #3 full + web client/render POC | | **6** Release hardening + v0.16 | alpha release docs + npm alpha publish + **production token defaults** (`~/.qwen/serve/instances/--/token`) + deployment refs + v0.16 release | PR 27-31 | Stage 2 + release | ### Wave 1 — Protocol foundation(无依赖,可立即开始) @@ -123,7 +123,7 @@ capability registry → DaemonSessionClient → typed events |---|---|:---:| | **PR 1** `test/perf: daemon baseline harness` | RSS curve + same-workspace attach latency + prompt p50/p99 + MCP child count + SSE replay/backpressure basics — **measure before optimize** | ✅ **MERGED 2026-05-16 16:41** [PR#4205](https://github.com/QwenLM/qwen-code/pull/4205) (doudouOUC, +1343 LOC 全在 `integration-tests/`,0 production code;4 Critical 修后 wenshao 本地 70.7s 跑过 20 iteration;首份 `baseline-stage-1.json` macOS arm64:RSS 223.5 MB / `growthPerSessionMB ≈ 0` / attach 1-3 ms / MCP 4 children constant under default single-scope) | | **PR 2** `feat(serve): capability registry + protocol versions` | 替换 hard-coded `STAGE1_FEATURES` 为 additive registry(新 `capabilities.ts` +52 LOC,每 feature `{ since: 'v1' }` descriptor,留 `deprecated` / `requires` 扩展位)+ `/capabilities.protocolVersions: { current, supported }`;`STAGE1_FEATURES` 保留为 `@deprecated` alias;SDK 加 `DaemonProtocolVersions` type;测试验证 backward compat(accepts old v1 envelopes without `protocolVersions`);关闭 chiga0 finding 5 FIXME | ✅ **MERGED 2026-05-16 10:07** [PR#4191](https://github.com/QwenLM/qwen-code/pull/4191) (doudouOUC, `[codex]` 前缀, +170/-39, 84+43 tests passing) | -| **PR 3** `feat(sdk): DaemonSessionClient skeleton` | SDK helper over `DaemonClient`:create/attach/prompt/events/cancel/model;tracks `Last-Event-ID` replay state;给 TUI/channels/web/IDE adapters 共用(依赖 PR 2)| ✅ **MERGED 2026-05-16 17:01** [PR#4201](https://github.com/QwenLM/qwen-code/pull/4201) (chiga0;前身 [PR#4195](https://github.com/QwenLM/qwen-code/pull/4195) CLOSED;v2 经 review 补 AbortSignal 转发 / event-without-id guard / error path 三轴 45 cases;wenshao 本地 verify 53 tests + typecheck + build clean);✅ follow-up [PR#4225](https://github.com/QwenLM/qwen-code/pull/4225) **MERGED 2026-05-17 07:05** (chiga0, 3h11m open→merge, +323/-18 测试 141→205;**多模型 /review pipeline**:DeepSeek-v4-pro + claude-opus-4-7 + wenshao 4 轮 review;初稿提的 eager guard 设计被 wenshao 否决—abandoned generator 永久占 slot 风险——**chiga0 让步改回 lazy**+ 加 `cursor monotonicity via Math.max`(同 PR#4217 reducer hardening pattern 跨 PR 复用)+ abort-signal propagation + SSE `event.id` validation + test 钉 lazy-guard 行为防 regression) | +| **PR 3** `feat(sdk): DaemonSessionClient skeleton` | SDK helper over `DaemonClient`:create/attach/prompt/events/cancel/model;tracks `Last-Event-ID` replay state;给 remote web chat / web terminal / future daemon clients 共用(依赖 PR 2)| ✅ **MERGED 2026-05-16 17:01** [PR#4201](https://github.com/QwenLM/qwen-code/pull/4201) (chiga0;前身 [PR#4195](https://github.com/QwenLM/qwen-code/pull/4195) CLOSED;v2 经 review 补 AbortSignal 转发 / event-without-id guard / error path 三轴 45 cases;wenshao 本地 verify 53 tests + typecheck + build clean);✅ follow-up [PR#4225](https://github.com/QwenLM/qwen-code/pull/4225) **MERGED 2026-05-17 07:05** (chiga0, 3h11m open→merge, +323/-18 测试 141→205;**多模型 /review pipeline**:DeepSeek-v4-pro + claude-opus-4-7 + wenshao 4 轮 review;初稿提的 eager guard 设计被 wenshao 否决—abandoned generator 永久占 slot 风险——**chiga0 让步改回 lazy**+ 加 `cursor monotonicity via Math.max`(同 PR#4217 reducer hardening pattern 跨 PR 复用)+ abort-signal propagation + SSE `event.id` validation + test 钉 lazy-guard 行为防 regression) | | **PR 4** `feat(protocol): typed daemon event schema v1` | SDK-layer discriminated union + reducer skeleton;保留 raw `DaemonEvent { data: unknown }` 兼容(依赖 PR 2, 3)| ✅ **MERGED 2026-05-17 04:31** [PR#4217](https://github.com/QwenLM/qwen-code/pull/4217) (chiga0, squashed `ab88f6f1`;**5 轮 review / 2 轮自我修复**:① 4 Critical 修—bracket notation 22 处 / `client_evicted` reducer 不设 `alive: false` / `client_evicted`+`session_update` 零测试 / `isPermissionRequestData` 不检查 required `toolCall`;② 5 项 reducer hardening invariants—`lastEventId` 单调推进抗回退 / `pendingPermissions` cap 64/session + dropped 诊断计数 / `permission_resolved` 未匹配 requestId 诊断计数 + cancelled outcome / `toolCall` 校验收紧 + finite number / reducer 存储时复制 data 抗引用共享;wenshao 本地 20/20 SDK schema + DaemonSessionClient tests passing);⚠️ 同 PR 平行的 [PR#4226](https://github.com/QwenLM/qwen-code/pull/4226) (doudouOUC, 2026-05-17 03:58, +1398/-18, 1.8x 测试比;开 4 分钟后 PR#4217 即 MERGED) 重复,**待 close 或拆 SessionState reducer 部分作 Wave 5 PR 25 提前** | #### Bonus: 提前到 Wave 1 阶段的 client adapter spikes @@ -138,7 +138,7 @@ capability registry → DaemonSessionClient → typed events #### Stage 1 / 2 adapter wire-up + experimental flag-gated (chiga0, 2026-05-18) -> chiga0 把 adapter migration 工作进一步拆 **3 adapter × 3 stage = 9 PR**。Stage 0 spike 是 protocol primitive(已 ✅ 3/3),Stage 1 是 developer harness(reviewer 手验 + 不替换 production path),Stage 2 是 experimental flag-gated(真接入 production 渲染链,behind `--experimental-daemon-tui` / `qwen-code.experimentalDaemonIde` flag)。 +> chiga0 已合入的 3 个 adapter Stage 0 PR(channel / IDE / TUI)现在只作为 **default-off spike / future migration 参考**。2026-05-19 之后不继续把 3 adapter × 3 stage 当作主线推进;主线改为 remote web chat / web terminal POC + shared render/client core。 | Stage | PR | 内容 | 状态 | |---|---|---|---| @@ -204,7 +204,7 @@ chiga0 设计哲学(每 PR 反复出现):**existing default path remains u | **PR 23** `feat(mcp): shared MCP transport/process pool` | 真共享 pool,keyed by canonical workspace + server **config hash** + auth/env/runtime inputs;lifecycle/refcount tests(依赖 PR 22, 14)| ⏳ | | **PR 24** `feat(security): client pairing revocation + PermissionMediator` | pair tokens + revocation API + audit log + 4 policy strategies(first-responder / designated / consensus / local-only)(依赖 PR 8, 22)| ⏳ | | **PR 25** `refactor(output): daemon-compatible output sinks` | JSONL / stream-json / dual-output behavior 移到 protocol/output sink 边界后;让 CLI + daemon client 共享 event semantics 而不是 duplicate terminal-specific logic(依赖 PR 4, 22)—— PR#4226 doudouOUC 平行 reducer 实现可拆这里提前 | ⏳ | -| **PR 26** `feat(adapters): flag-gated daemon client adapters` | 开始 TUI / channels / web-debug / IDE adapter migration behind flag,用 `DaemonSessionClient`;如 ownership/review size 需要可按 adapter 拆分;3 bonus spike [PR#4202](https://github.com/QwenLM/qwen-code/pull/4202) / [PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) / [PR#4199](https://github.com/QwenLM/qwen-code/pull/4199) 已铺路(依赖 PR 3, 4, 25)| ⏳ | +| **PR 26** `feat(web): daemon web clients + render-core extraction` | remote web chat / web terminal 接入 `DaemonSessionClient`;抽取 source adapter / view model / terminal render core,让 native TUI direct streamJson 与 web terminal daemon events 复用渲染核心。PR#4202 / #4203 / #4199 仅作 default-off spike 参考;不要求 TUI/channel/IDE 默认迁移(依赖 PR 3, 4, 25)| ⏳ | #### PR 22b/1 ([#4298](https://github.com/QwenLM/qwen-code/pull/4298)) 工程明细 @@ -311,14 +311,14 @@ chiga0 设计哲学(每 PR 反复出现):**existing default path remains u | 何时 npm alpha publish? | Wave 1 + 足够的 Wave 2 后 + release docs ready;不阻塞所有 control-plane routes | | Loopback 默认 token? | v0.15 alpha 保持现状 + 加 `--require-auth`(PR 15);v0.16 改 token-by-default + SDK auto-discovery + explicit opt-out | | Token instance path? | `~/.qwen/serve/instances/--/token`(多 daemon 共存)+ PID metadata(替代 port-only 路径)| -| 如何 align PR#3929-3931 remote-control? | 等 primary clients 稳定后改为 daemon facade / control overlay;同时承认 **local daemon + remote-control relay** 和 **remote daemon + gateway** 两种形态,避免 parallel runtime/protocol fork | +| 如何 align PR#3929-3931 remote-control? | 等 remote web chat / web terminal contract 稳定后改为 daemon facade / control overlay;同时承认 **local daemon + remote-control relay** 和 **remote daemon + gateway** 两种形态,避免 parallel runtime/protocol fork | | Worktree 交互? | `boundWorkspace` 仍是 boot-time daemon workspace;file routes 默认 bound-workspace safety;worktree-specific 行为必须显式,不是隐式 `process.chdir()` | --- ### 三·二 Deployment + package contract (chiga0 #3803 comment 2026-05-18) -来源:chiga0 [Issue #3803 comment 4476174099](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4476174099) + [comment 4476318030](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4476318030) (GPT-5 Codex 协助生成),**在 Wave 4 收尾、Wave 5 adapter migration 真要默认切换前,提议钉死 deployment / package / renderer / remote-control 契约**: +来源:chiga0 [Issue #3803 comment 4476174099](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4476174099) + [comment 4476318030](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4476318030) (GPT-5 Codex 协助生成),2026-05-19 修订为:**在 Wave 5 web/client-render POC 开始前,钉死 deployment / package / renderer / remote-control 契约**。旧的“所有 client 默认迁 daemon”口径不再作为主线。 #### 1. Deployment forms 3 类(详 [§01 §三·一](./01-overview.md#三一-deployment-forms来自-chiga0-3803-comment-4476174099-2026-05-18)) @@ -326,16 +326,15 @@ local-local(主装)/ remote-remote daemon 与 workspace colocate(cloud 推 #### 1.5 Deployment shape matrix(详 [§04 §四](./04-deployment-and-client.md#deployment-shape-matrixclientruntime-lens2026-05-18)) -3-form 表只回答 daemon/workspace locality;client migration 还需要按 UI/adapter 形态拆分验证: +3-form 表只回答 daemon/workspace locality;client/render 工作还需要按 UI/adapter 形态拆分验证: | Shape | Runtime locality | 当前 roadmap 含义 | |---|---|---| -| **Local - Local (本地单用户 TUI)** 🌟 | 不存在 daemon——`qwen` = in-process direct call | **永久 default UX,最高优先级**;本地 TUI **不参与** daemon convergence(不要为不需要的东西付费)| -| **Local - Local (multi-client 协作)** | 本机 daemon/runtime/workspace | IDE / local web / channel adapter 走 loopback `qwen serve` 时;**TUI 默认 in-process 不切换** | -| Web chat / web terminal - Remote | remote daemon/runtime/workspace | web terminal 目标是 **daemon-native renderer**(typed events + reducer),PTY proxy 仅 fallback | -| Local IDE - Local daemon | 本机 daemon/runtime/workspace | IDE dogfood 优先形态;path identity 共享,workspace mismatch 要 fail loud | -| Local TUI - Remote | remote daemon/runtime/workspace | TUI 必须明示 remote label/path/auth;local cwd 不可当 runtime cwd | -| Channel + daemon | daemon 可 local 或 remote | channel adapter 需要 session routing / identity mapping / permission cards;local daemon 可服务个人 workstation remote-control | +| Native local TUI | local runtime/workspace,无需 daemon | `qwen` 直连长期保留;可复用 shared render core,但不默认走 localhost HTTP | +| Local web chat / web terminal | 本机 daemon/runtime/workspace + local BFF/browser | optional local web POC;BFF 持 token;复用 web contract | +| Web chat / web terminal - Remote | remote daemon/runtime/workspace | remote web POC 第一优先;web terminal 目标是 **daemon-native renderer**(typed events + reducer),PTY proxy 仅 fallback | +| Local IDE | 默认 `--acp` child | IDE daemon dogfood 后置;path identity / workspace mismatch / editor context 需 future validation | +| Channel + daemon | daemon 可 local 或 remote | channel 默认仍 `--acp`;daemon bridge 仅 future behind-flag;local daemon 可服务个人 workstation remote-control | | Remote-control overlay | local daemon + relay 或 remote daemon + gateway | P2 后置,但不是新 runtime protocol;复用 `DaemonSessionClient` + HTTP/SSE | **Review rule**:每个 client PR 必须说明它验证哪种 shape、daemon/workspace/client locality 假设、是否 behind flag/default off,以及对 path mapping、runtime diagnostics、client capability reverse RPC 的依赖。 @@ -352,38 +351,46 @@ local-local(主装)/ remote-remote daemon 与 workspace colocate(cloud 推 DaemonClient / DaemonSessionClient / typed event schema / reducer reconnect / heartbeat / capability negotiation +@qwen-code/client-render + source adapters / shared view model / terminal render core / output sink helpers + @qwen-code/daemon-adapters-* - tui adapter / channel adapter / ide-web adapter / jsonl + stream-json + dual-output sinks + web chat adapter / web terminal adapter + optional channel / IDE adapters after daemon stabilization ``` **3 条依赖方向规则**: - ❌ server code 不可依赖 TUI / IDE / channel adapter -- ✅ adapter 只可依赖 SDK / protocol / reducer surface -- ✅ shared event reducer + typed protocol helper **必须住 client/protocol 层**,不能在 `packages/cli/src/serve/` +- ✅ daemon adapter 只可依赖 SDK / protocol / reducer / render surface +- ✅ shared event reducer + typed protocol helper **必须住 client/protocol 层**,terminal render core 必须 source-independent,不能在 `packages/cli/src/serve/` 否则"每个 client integration 长出略不同的 bridge → Mode B convergence goal 侵蚀"。 -#### 3. Local auto-daemon UX(仅 multi-client 协作场景,**不作 local TUI default**) +#### 3. Native TUI direct path + local web daemon lifecycle -> ⚠️ **2026-05-18 设计原则**(详 [§02 §7](./02-architectural-decisions.md#7-部署模式--mode-b-mainline--mode-a-parking-lot)):本地单用户 TUI **永远 in-process direct call,永远不走网络**。auto-daemon UX 草图**仅适用用户主动选 multi-client 协作场景**(IDE + TUI 同 session live collaboration),**不作 local TUI default 路径**。 -> -> 反驳 chiga0 [#3803 comment 4476174099](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4476174099) 的 "Local auto-daemon UX 作 local default" 草图,已发 [#3803 reply 4483031818](https://github.com/QwenLM/qwen-code/issues/3803#issuecomment-4483031818) 立 zero-cost abstraction 原则——**不要为不需要的东西付费**。 +local TUI 默认 **不走 daemon**。现有 `qwen` 直连 runtime / streamJson / Ink 链路长期保留,原因是本地 TUI 经 localhost httpServer 会引入额外进程、端口、token、lifecycle、日志和故障面,同时性能与可维护性都不如直连。 -对于**用户主动选 multi-client 协作场景**(少数 advanced 用户),auto-daemon UX 设计草图: +需要抽取的是共用的 terminal/render core,而不是强制共用 transport: ```text -qwen --experimental-daemon-tui # 或某个 future opt-in flag - → discover daemon for this workspace - → if absent, auto-start local `qwen serve` on loopback - → attach TUI to a session - → apply lifecycle policy on TUI exit: keep daemon / idle timeout / explicit shutdown +native qwen + → direct runtime / streamJson + → native source adapter + → shared view model / terminal state + → shared Ink→ANSI renderer + → local terminal + +local web terminal + → local BFF starts/discovers loopback `qwen serve` + → daemon HTTP/SSE events + → daemon source adapter + → same view model / terminal state + → browser terminal transport ``` -**收益**(仅当用户主动选时):IDE/web/channel attach 同一 session;重连 SSE `Last-Event-ID` + heartbeat + load/resume;event fan-out + permission flow 跨 client 统一。 +**收益**:native TUI 保持低延迟/低复杂度;web terminal 仍能得到 daemon-native 远端能力;两边共享 renderer/state,避免维护两套相似 TUI。 -**代价/风险**:extra local process/port/token/discovery file-lock/health check/logs;TUI 渲染必须 coalesce daemon chunks 到 Ink UI 而非 raw event spam;每个 mutate runtime state 的 TUI dialog 必须有 daemon control-plane route;daemon crash 影响 workspace 所有 session(1 daemon = 1 workspace blast radius 仍 acceptable)。 - -—— Mode A([#4156](https://github.com/QwenLM/qwen-code/issues/4156) `qwen --serve` TUI super-client + 内嵌 HTTP server)可能比 Mode B + TUI adapter 更合适——**保零网络 TUI 体验 + 仅对其他 client 开 HTTP**。待 Mode A revisit 时与本设计草图一并评估。Wave 6 release hardening 前不入主线。 +**local web daemon lifecycle** 仍需要 discovery / loopback auth / lifecycle policy / port collision / workspace binding,但这是 local web/BFF 的问题,不是 native TUI 的默认路径问题。Wave 6 release hardening 前补 local web/BFF lifecycle;native TUI 不需要 auto-daemon gate。 #### 4. Sandbox runner model(未来分层) @@ -412,20 +419,18 @@ daemon 保 **control plane / session-event coordinator**,sandbox/runtime worke - skill scripts/resources 在 runtime env 求值 - **`client capability reverse RPC` 严格 scoped 到 editor/clipboard/browser/notification/file_picker**,**不能 silently 成为 MCP/skill 执行 fallback** -#### 5. Client default migration gate(3-condition checklist) +#### 5. Web readiness + future migration gate -> ⚠️ **TUI default 永远不切换**([§02 §7 设计原则](./02-architectural-decisions.md#7-部署模式--mode-b-mainline--mode-a-parking-lot))。本 gate **仅适用 channel / web / IDE** 三类跨进程 client。 +> ⚠️ **native TUI default 永远不切换**([§02 §7 设计原则](./02-architectural-decisions.md#7-部署模式--mode-b-mainline--mode-a-parking-lot))。当前 gate 优先服务 remote web chat / web terminal;channel / IDE daemon migration 后置。 -channel / web / IDE default 切换到 daemon-backed 必须等: +web chat / web terminal 进入 production-ready 需要: -1. **control-plane parity** —— 跨进程 client 所需 mutating route 在 daemon API 全 wire 化(详 [§04 §二 9 项 dialog](./04-deployment-and-client.md)) +1. **control-plane parity** —— web chat / web terminal 需要的 mutating dialogs 在 daemon API 都有对应 route([§04 §二 9 项 dialog](./04-deployment-and-client.md) 全 wire 化) 2. **reducer / adapter quality** —— typed event 消费器稳定 + 无 raw event spam -3. **auto-daemon lifecycle** —— 上面第 3 点的 discovery / loopback auth / lifecycle policy / port collision / workspace binding 都齐(**仅适用 multi-client 协作场景**,不作 local TUI default 路径) -4. **shape-specific readiness** —— remote TUI / IDE / channel / web terminal 各自 locality、path mapping、auth/gateway、diagnostics 要有明确验证记录 - -**当前 PR#4266 / PR#4267 draft 不冲突这条 gate** —— PR#4266 (TUI experimental) 是 **opt-in advanced 永远 behind flag,不进入 default migration**(见 [PR#4266 reply 4483046985](https://github.com/QwenLM/qwen-code/pull/4266#issuecomment-4483046985));PR#4267 (IDE experimental) 是合规 behind-flag experiment for default migration。 +3. **web/BFF lifecycle** —— discovery / loopback auth / lifecycle policy / port collision / workspace binding 都齐 +4. **shape-specific readiness** —— local web / remote web terminal / channel+daemon future / IDE future 各自 locality、path mapping、auth/gateway、diagnostics 要有明确验证记录 -**Wave 5 PR 26 scope 显式收紧**:仅 channel / web / IDE default 切换,**TUI default 不切换**(见 [#4175 reply 4483033542](https://github.com/QwenLM/qwen-code/issues/4175#issuecomment-4483033542))。 +**当前 PR#4266 / PR#4267 draft 不再是主线 gate 的前置** —— 它们可关闭或保留为 future behind-flag experiment,不应阻塞 web-first roadmap。 #### 6. 文档更新建议(chiga0 list) @@ -463,7 +468,7 @@ channel / web / IDE default 切换到 daemon-backed 必须等: | **P0** | **1.5a must-haves** | chiga0 10 must-haves 剩 9 项(生产 blocker)| ~2 周(9 PRs 可并行)| | **P0** | **1.5c** | daemon-side state CRUD 8 routes(Mode B 远端 client 摆脱 thin shell)| ~3-5d | | **P1** | **1.5-prereq** | chiga0 6 architecture refactor findings(lift `AcpChannel` / `EventBus` / `PermissionMediator` 到 `@qwen-code/acp-bridge`)| ~1-2 周 | -| **P1** | **client adapters** | TUI / channels / web/debug / IDE 通过 `DaemonSessionClient` behind flag 接入 Mode B;默认切换必须等 P0/P1 | ~2-3 周,可与 P0/P1 试点并行 | +| **P1 / POC 并行** | **client/render** | remote web chat / web terminal 通过 `DaemonSessionClient` 接入 Mode B;先做 technical POC 验证 BFF/browser + HTTP/SSE typed events + reducer/render core 可行性;channel / IDE daemon migration 后置 | ~2-3 周,可与 P0 must-haves / state CRUD 并行 | | **P2** | **remote-control revisit** | [#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) 后续作为 daemon facade | 后置 | | **P2** | **1.5b Mode A** | Mode A `qwen --serve` flag — [Issue #4156](https://github.com/QwenLM/qwen-code/issues/4156) doudouOUC 3-phase plan;A1 [PR#4160](https://github.com/QwenLM/qwen-code/pull/4160) ✅ MERGED;剩余 ~5-6d **推迟到 P0 完成后** | ~5-6d | | **合计**(按 P0 → P1 → P2 顺序)| | | **~4 周 + Mode A 1 周** | @@ -488,23 +493,28 @@ Week 2 Week 3 ├─ 1.5-prereq merge → Stage 2 协议层准备就绪 -├─ TUI / channels / web/debug / IDE behind-flag adapters 试点 +├─ remote web chat / web terminal 技术 POC + shared render/client core 试点 └─ 1.5b Mode A 启动(Issue #4156 A0/A2/A3 + Phase B/C) Week 4 └─ 1.5b Mode A merge + 部署生态(Dockerfile / systemd / k8s manifest) ``` -### Mode B client convergence 补充 +### Mode B remote web convergence 补充 -client 统一接入不是让外部 client 直接 import daemon 内存里的 `EventBus`,而是稳定 HTTP/SSE 边界上的 typed event contract、shared reducer 和 `DaemonSessionClient`: +remote web chat / web terminal 先接入 daemon,不是让外部 client 直接 +import daemon 内存里的 `EventBus`,而是稳定 HTTP/SSE 边界上的 typed +event contract、shared reducer 和 `DaemonSessionClient`。native local TUI +继续直连本机 runtime;channel / IDE 继续走现有 ACP,daemon 化迁移后置。 ```text qwen serve -> HTTP/SSE API -> internal EventBus fan-out -> typed event contract + DaemonSessionClient - -> TUI / channels / web / IDE adapters + -> remote web chat / web terminal adapters + -> shared render/client core + -> future channel / IDE daemon adapters if later approved -> JSONL / stream-json / dual-output sinks -> remote-control later as daemon facade ``` @@ -517,7 +527,7 @@ Stage 1.5 不是一次性 rewrite,而是 **逐步测试、逐步迁移**。每 |---|---| | 单 PR 可合入 | 每个 PR 自带完整测试,合入后 main 仍可发布 | | 向后兼容 | 不移除现有 route / event 字段 / CLI 行为;新增字段必须 additive + optional | -| 默认不切换 | TUI / channels / IDE 先 behind flag 或 adapter 双栈;默认仍走现有路径,直到验证完成 | +| 默认不切换 | native TUI / channels / IDE 保持现有默认路径;remote web chat / web terminal 使用显式入口做技术 POC,直到 daemon web contract 验证完成 | | serve 不破坏 | `qwen serve` Stage 1 routes 和 SDK 行为保持可用;新能力通过 capabilities feature tag 暴露 | | 渐进迁移 | P0 must-haves / state CRUD / typed contract 可并行;client 先 behind flag 试点,再扩大默认面 | | 可回滚 | 每个 client adapter 都能独立关闭,不影响其他 client 和 daemon | @@ -556,7 +566,10 @@ Mode A 价值是 "本地 TUI super-client + 远端 client 同时接入同 daemon } ``` -要让 TUI / channels / web / IDE 真正统一接入,不是让它们直接 import daemon 里的 `EventBus` 对象,而是把它背后的 **event contract** 稳定下来。Mode B 下外部 client 的直接边界仍然是 HTTP server: +要让 remote web chat / web terminal 可靠接入,并为后续 channel / IDE +daemon 化保留余地,不是让它们直接 import daemon 里的 `EventBus` 对象, +而是把它背后的 **event contract** 稳定下来。Mode B 下 daemon client +的直接边界仍然是 HTTP server: ```text client @@ -571,14 +584,14 @@ client | 工作项 | 目标 | |---|---| | typed `SessionEvent` / `ControlEvent` | 把 `data: unknown` 收敛为 discriminated union | -| shared `DaemonSessionClient` | SDK / TUI / channels / IDE 共用 HTTP/SSE client | +| shared `DaemonSessionClient` | SDK / remote web chat / web terminal / future daemon clients 共用 HTTP/SSE client | | `AcpChannel` / transport primitive | 把 child stdio、in-memory channel、daemon HTTP transport 的 bridge 边界拆清 | | `PermissionMediator` | 统一 daemon first-responder、channels、stream-json / non-interactive 的 permission 策略 | | Event reducer | 从 daemon events 构建 client view-model,避免每个 client 自己拼状态 | | output sinks | JSONL / stream-json / dual-output 变成同一 event stream 的 sink | | capability negotiation | `/capabilities` 增加 `protocol_versions` / feature registry,client 可按能力降级 | -这一步是所有 client 默认切换的前置;可以与 P0 must-haves / state CRUD 并行启动,不需要等 Mode A。 +这一步是 remote web chat / web terminal 进入 production-ready 的前置;可以与 P0 must-haves / state CRUD 并行启动,不需要等 Mode A。channel / IDE 后续若评估 daemon migration,也复用同一 contract。 **兼容性要求**: @@ -587,29 +600,33 @@ client - 新 capability 字段必须 optional,旧 daemon / 新 client、旧 client / 新 daemon 都能工作。 - Event reducer 必须只消费已有事件,不能要求 daemon 立即新增全量 state event。 -### Client adapters — Primary clients(优先 TUI / channels / web / IDE) +### Client/render — Primary daemon clients(优先 remote web chat / web terminal) -目标是让现有 client 不再各自拥有一条 parallel runtime,而是接到同一个 Mode B daemon: +当前目标是先让 remote web chat / web terminal 验证 daemon 技术方案,native +local TUI、channel、IDE 默认链路暂不迁移: | Client | 当前状态 | Mode B 适配方向 | 接入顺序 | |---|---|---|---| -| **TUI** | 走内部 Ink / `useGeminiStream` 路径 | 新增 attach-to-daemon render target;用 shared reducer 渲染 daemon `SessionEvent`;本地 TUI 不再拥有 runtime | 第一波 behind flag | -| **channels** | `packages/channels/base/AcpBridge.ts` 自己 spawn `qwen --acp` | 新增 daemon transport;保留 channel routing,但 prompt/event/cancel/model 走 `DaemonSessionClient` | 第一波 behind flag | +| **remote web chat** | 仍缺 production web client | 显式入口 / BFF 消费 daemon HTTP/SSE;验证 auth、session identity、permission、state CRUD、streaming UX | 第一优先 POC | +| **web terminal** | 仍缺 production web terminal | 复用 shared render core / Ink→ANSI 投影,在浏览器 terminal 中消费 daemon events;不走 PTY proxy 作为主线 | 第一优先 POC | +| **native local TUI** | 走内部 Ink / `useGeminiStream` 路径 | 保持直接链路;只抽 shared render core,避免为本机 TUI 增加 localhost HTTP/SSE 损耗 | 不默认迁移 | +| **channels** | `packages/channels/base/AcpBridge.ts` 自己 spawn `qwen --acp` | 继续 ACP;daemon transport 只保留为 future behind-flag reference | 后置 | +| **IDE** | VSCode companion 直接 spawn `qwen --acp` | 继续 ACP;daemon transport 只保留为 future behind-flag reference | 后置 | | **web/debug** | [PR#4132](https://github.com/QwenLM/qwen-code/pull/4132) `/demo` ✅ **MERGED 2026-05-18 08:31** (jifeng, 4 天 24 reviews) | 作为最薄 POST+SSE client 验证面,已 ship;XSS 修把威胁模型扩到 daemon-emitted poisoned events | 已 ship | -| **IDE** | VSCode companion 直接 spawn `qwen --acp` | 新增 daemon transport behind flag;先覆盖 session create/prompt/events/cancel/model,再补 file/context/control routes | 第二波 behind flag | | JSONL / stream-json / dual-output | CLI 内部 adapter | 变成 daemon event sinks;不再驱动 runtime,只消费 typed events | 与 contract 并行 | | remote-control | [#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) draft stack | **后置**;等上述 clients 收敛后作为 daemon facade 复用同一 contract | P2 deferred | -适配可以先 behind flag 开始;默认切换需要等 P0 的 1.5a must-haves / 1.5c state CRUD,以及 P1 的 1.5-prereq contract / bridge primitive 完成。 +web 适配应先以 explicit entrypoint / behind flag 做 technical POC,优先验证 remote web chat + web terminal 这条主场景是否能跑通。POC 可以和 P0 的 1.5a must-haves / 1.5c state CRUD 并行推进,用真实 web 体验反向暴露缺口;production-ready 才需要等 P0/P1 contract、bridge primitive、state CRUD 和 release hardening 收口。native TUI/channel/IDE 默认链路不受该 gate 影响,channel / IDE daemon migration 后置。 **每个 client 的合入策略**: -| Client | 第一个可合入 PR | 默认切换条件 | +| Client | 第一个可合入 PR | 默认/生产条件 | |---|---|---| -| TUI | `qwen tui --daemon-url` / env flag attach 原型;只读渲染 + prompt/cancel/model | control-plane parity 覆盖常用 dialogs;TUI regression 通过 | -| channels | 新 `DaemonChannelTransport` behind config flag;保留 `AcpBridge` 默认 | IM routing、permission、reconnect、sessionScope 验证通过 | -| web/debug | `/demo` 或独立 web client 只依赖 HTTP/SSE;不扩大 daemon CORS 默认面 | allow-origin / auth / reconnect 策略明确 | -| IDE | daemon transport behind flag;默认仍 direct ACP child | file/context/control routes 补齐;workspace mismatch 和 resume 测试通过 | +| web chat | `/demo` → BFF web chat;不扩大 daemon CORS 默认面 | allow-origin / auth / reconnect / sessionScope 策略明确 | +| web terminal | daemon source adapter + shared render-core prototype | terminal regression / ANSI frame / reconnect / control-plane parity 通过 | +| native TUI | source adapter / render-core extraction;不改默认 transport | 现有 TUI regression 通过;无 localhost daemon requirement | +| channels | 保留 `AcpBridge` 默认;daemon bridge 仅 future config flag | IM routing、permission、reconnect、sessionScope 验证通过后再评估 | +| IDE | 默认仍 direct ACP child;daemon transport 仅 future config flag | file/context/control routes 补齐;workspace mismatch 和 resume 测试通过后再评估 | | output sinks | JSONL / stream-json adapter 从 typed events 生成旧格式 | 快照和现有 CLI output tests 通过 | ### 1.5c — daemon-side state CRUD / control-plane parity @@ -651,7 +668,7 @@ Stage 1 的主链路已经可用:prompt / events / cancel / model / permission ### 1.5a must-haves — identity + lifecycle + reliability -来源仍是 chiga0 PR#3889 downstream-consumer review:Stage 1 适合原型和本地小团队,但 TUI/channel/web/IDE 正式默认接入前,需要补多 client 的硬约束。 +来源仍是 chiga0 PR#3889 downstream-consumer review:Stage 1 适合原型和本地小团队,但 remote web chat / web terminal 进入 production 前,需要补多 client / browser / network 的硬约束。channel / IDE daemon 化若后续重启,也复用同一套约束。 | 类别 | 必需项 | |---|---| @@ -673,8 +690,8 @@ Stage 1 的主链路已经可用:prompt / events / cancel / model / permission remote-control 仍然有价值,但优先级后置。原因: - 当前 [#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) 是 draft / changes requested,且包含 parallel worker/WebSocket/runtime 路线。 -- 今天的 Mode B 决策要求所有 client 先收敛到 daemon HTTP/SSE API,并消费同一套 EventBus-backed typed event contract。 -- TUI / channels / web / IDE 是基础 client 面,先完成它们能反过来定义 remote-control 应复用的 contract。 +- 今天的 Mode B 决策把 daemon 技术验证优先级收敛到 remote web chat / web terminal,并要求它们消费同一套 EventBus-backed typed event contract。 +- native TUI、channels、IDE 默认路径先保持不变;remote-control 应等 remote web contract 稳定后复用 daemon facade,而不是另起 parallel runtime/protocol。 后续 remote-control 应该降级为: @@ -849,7 +866,7 @@ Anthropic Managed Agents 的内部模型很可能是 per-session container/proce - **自托管 vs 云托管**:Qwen 走 building block 路线;Anthropic 提供托管服务 - **IM 多渠道生态**:Qwen 通过 `packages/channels/` + IM 路由原生支持 Telegram / 微信 / 钉钉 / Slack;Anthropic 只暴露 API - **Java SDK 直连**:Qwen 唯一有 Java acp-sdk,daemon 后跨语言更顺 -- **Background tasks 4 kinds 跨 client 可见**:Qwen kind framework(PR#3836)能在 daemon 模式下让所有 client 看到所有后台任务(agent/shell/monitor/dream) +- **Background tasks 4 kinds 跨 daemon-capable client 可见**:Qwen kind framework(PR#3836)能在 daemon 模式下让 remote web chat / web terminal 以及未来 daemon clients 看到所有后台任务(agent/shell/monitor/dream) --- diff --git a/docs/comparison/qwen-code-daemon-design/README.md b/docs/comparison/qwen-code-daemon-design/README.md index eec387a..cf0f5a3 100644 --- a/docs/comparison/qwen-code-daemon-design/README.md +++ b/docs/comparison/qwen-code-daemon-design/README.md @@ -4,7 +4,7 @@ ## 一、TL;DR -> **2026-05-15 决策更新**:先忽略 Mode A(`qwen --serve`)。后续 roadmap 以 **Mode B:`qwen serve` headless daemon 作为底层 runtime** 为主线;Mode A 暂停在 [Issue #4156](https://github.com/QwenLM/qwen-code/issues/4156) 里作为 parking lot,已合并的 [PR#4160](https://github.com/QwenLM/qwen-code/pull/4160) 仅作为可复用 in-memory channel primitive 记录。 +> **2026-05-19 决策更新**:先忽略 Mode A(`qwen --serve`)。后续 roadmap 以 **Mode B:`qwen serve` headless daemon 作为 remote web chat / web terminal 的 runtime** 为主线;本地 native TUI `qwen` 直连链路长期保留,不默认迁到 daemon。channel / IDE 插件继续使用现有 `--acp` 模式,daemon 迁移优先级后置。remote web chat + web terminal 的 **technical POC 优先级上调**:可与 P0 must-haves / state CRUD 并行启动,先验证技术可行性;production-ready 仍依赖 P0/P1 contract 收口。Mode A 暂停在 [Issue #4156](https://github.com/QwenLM/qwen-code/issues/4156) 里作为 parking lot,已合并的 [PR#4160](https://github.com/QwenLM/qwen-code/pull/4160) 仅作为可复用 in-memory channel primitive 记录。 ``` ACP NDJSON 协议 → HTTP+SSE daemon @@ -24,7 +24,7 @@ ACP NDJSON 协议 → HTTP+SSE daemon | 模式 | 命令 | TUI | 适用场景 | |---|---|:---:|---| -| **Mode B** | `qwen serve [--port N]` | ❌ | **当前主线**:服务器 / 容器 / 远端机器 / K8s pod / 所有 client 的统一 runtime | +| **Mode B** | `qwen serve [--port N]` | ❌ | **当前主线**:服务器 / 容器 / 远端机器 / K8s pod / remote web chat + web terminal | | **Mode A** | `qwen --serve [--port N]` | ✅ 本地渲染 | **暂停推进**:待 Mode B HTTP/SSE event contract / control-plane / client identity 稳定后再评估 | **当前状态**: @@ -71,12 +71,11 @@ ACP NDJSON 协议 → HTTP+SSE daemon - ✅ [PR#4255](https://github.com/QwenLM/qwen-code/pull/4255) Wave 4 PR 21 OAuth device-flow route **MERGED 2026-05-18 14:05** (doudouOUC, **20h39m daemon 项目史上最长 lifetime** 破 PR#4249 15h20m;**135 reviews 史上最多**;最终 **+6172/-51 daemon 项目史上最大** 破 PR#4249 +5318;OAuth 2.0 RFC 8628 brokered through daemon;4 route + 5 typed event + build-time grep 防 browser-spawn regression + BrandedSecret 4-way redaction + 3 pre-PR agent / 12 P0+P1 fold-in;临门一脚被 1 个 TS 类型 inference 卡住 12:51 → 13:01 doudouOUC 修 → 13:59 gpt-5.5 /review LGTM → 14:05 MERGED) - ✅ PR 3 follow-up [PR#4225](https://github.com/QwenLM/qwen-code/pull/4225) DaemonSessionClient hardening **MERGED 2026-05-17 07:05** (chiga0, 多模型 /review 4 轮;chiga0 让步把 eager guard 改回 lazy + cursor monotonicity + abort propagation + event.id validation) - ⚠️ [PR#4226](https://github.com/QwenLM/qwen-code/pull/4226) typed event schema 竞品 OPEN (doudouOUC) — 与 PR#4217 重叠,待 close 或拆 reducer 作 Wave 5 PR 25 提前 - - ✅ Bonus Stage 0 spike 全 MERGED 2026-05-18: [PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) channel (02:21) + [PR#4199](https://github.com/QwenLM/qwen-code/pull/4199) IDE (02:38) + [PR#4202](https://github.com/QwenLM/qwen-code/pull/4202) TUI (03:22) (chiga0; Wave 5 PR 26 提前) - - ⚠️ Bonus Stage 1 wire-up 调整: [PR#4260](https://github.com/QwenLM/qwen-code/pull/4260) TUI + [PR#4263](https://github.com/QwenLM/qwen-code/pull/4263) IDE **CLOSED 06:59** (Stage 2 直接覆盖); [PR#4261](https://github.com/QwenLM/qwen-code/pull/4261) channel `--daemon-url` 仍 draft (channel 无 Stage 2) - - 🔧 Bonus Stage 2 experimental flag-gated 2 PR draft: [PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui` / [PR#4267](https://github.com/QwenLM/qwen-code/pull/4267) `qwen-code.experimentalDaemonIde` (chiga0, 2026-05-18 05:06 起; channel stage 2 待开) + - ✅ Bonus Stage 0 spike 全 MERGED 2026-05-18: [PR#4203](https://github.com/QwenLM/qwen-code/pull/4203) channel (02:21) + [PR#4199](https://github.com/QwenLM/qwen-code/pull/4199) IDE (02:38) + [PR#4202](https://github.com/QwenLM/qwen-code/pull/4202) TUI (03:22) (chiga0);2026-05-19 后定位调整为 **default-off spike / future migration 参考**,不代表 native TUI/channel/IDE 默认迁 daemon + - ⚠️ Bonus Stage 1/2 wire-up 调整: [PR#4260](https://github.com/QwenLM/qwen-code/pull/4260) TUI + [PR#4263](https://github.com/QwenLM/qwen-code/pull/4263) IDE **CLOSED 06:59**;[PR#4261](https://github.com/QwenLM/qwen-code/pull/4261) channel `--daemon-url`、[PR#4266](https://github.com/QwenLM/qwen-code/pull/4266) `--experimental-daemon-tui`、[PR#4267](https://github.com/QwenLM/qwen-code/pull/4267) `qwen-code.experimentalDaemonIde` 与最新主线不再一致,应收口为 draft/closed 或仅保留可复用代码到 render-core / web-terminal 方向 - ✅ [PR#4132](https://github.com/QwenLM/qwen-code/pull/4132) `/demo` debug page (jifeng) **MERGED 2026-05-18 08:31** —— 4 天 24 reviews 终于合,daemon 项目 OPEN 最长 PR;XSS 修 (daemon-emitted poisoned events 威胁模型) + rebase 过 #4247/4250/4251 冲突 -- 🧭 [PR#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) remote-control stack 仍 OPEN draft / changes requested;**优先级后置**,等 TUI / channels / web / IDE 先完成 Mode B client 适配后,再重定向为 daemon HTTP/SSE facade -- ⏳ Stage 1.5 剩余主线:P0 production must-haves + daemon-side state CRUD,P1 typed event contract / bridge primitives + client adapters behind flag,P2 remote-control / Mode A revisit(详 [§06 Roadmap](./06-roadmap.md)) +- 🧭 [PR#3929](https://github.com/QwenLM/qwen-code/pull/3929) / [#3930](https://github.com/QwenLM/qwen-code/pull/3930) / [#3931](https://github.com/QwenLM/qwen-code/pull/3931) remote-control stack 仍 OPEN draft / changes requested;**优先级后置**,等 remote web chat / web terminal daemon contract 稳定后,再重定向为 daemon HTTP/SSE facade +- ⏳ Stage 1.5 剩余主线:P0 production must-haves + daemon-side state CRUD,P1 typed event contract / bridge primitives + remote web chat/web terminal POC + shared render core,P2 remote-control / Mode A / channel-IDE daemon migration revisit(详 [§06 Roadmap](./06-roadmap.md)) ## 二、6 章总览 @@ -85,7 +84,7 @@ ACP NDJSON 协议 → HTTP+SSE daemon | **01** | [Overview](./01-overview.md) | TL;DR + 2 层术语 + 架构图 + Mode B 主线 + 资源经济性 + Stage 进展 + 阅读指南 | | **02** | [Architectural Decisions](./02-architectural-decisions.md) | 8 决策:session 共享 P1/P2 / 1 daemon = 1 workspace × N session / MCP 生命周期 / FileReadCache / Permission flow / 多 client 并发 / Mode B mainline / server-client-runtime boundary | | **03** | [HTTP API & Protocol](./03-http-api.md) | Route table(`/workspace/*` 单 workspace 路由)+ ACP wire 4 层兼容性矩阵 + SSE + Last-Event-ID + 双向 RPC 异步化 + Capability negotiation | -| **04** | [Deployment & Client](./04-deployment-and-client.md) | Mode B client convergence + deployment shape matrix + TUI / channels / web / IDE 适配边界 + daemon-native renderer + remote-control overlay | +| **04** | [Deployment & Client](./04-deployment-and-client.md) | Mode B web-first client convergence + deployment shape matrix + native TUI 直连边界 + web terminal daemon-native renderer + remote-control overlay | | **05** | [Security & Permission](./05-permission-auth.md) | Bearer + Host allowlist + 0.0.0.0 拒绝 / PR#3723 4-mode evaluatePermissionFlow / first-responder vote + per-session 隔离 / Multi-tenant = 1 daemon 1 tenant OS 进程级隔离 | | **06** | [Roadmap & Ecosystem](./06-roadmap.md) | Timeline + Stage 1 audit + Stage 1.5 + chiga0 10 must-haves + 6 architecture findings + Stage 2 + External Reference Architecture + vs OpenCode + vs Anthropic | @@ -127,7 +126,7 @@ ACP NDJSON 协议 → HTTP+SSE daemon ### Stage 进展(at 2026-05-15) -**合入原则**:Stage 拆分必须逐步迁移。每个 PR 都要可单独合入、向后兼容、默认不破坏现有 TUI / channels / IDE / CLI 行为;新 daemon 能力通过 capability tag 暴露,client adapter 先 behind flag / 双栈测试,再单独 PR 切默认。 +**合入原则**:Stage 拆分必须逐步迁移。每个 PR 都要可单独合入、向后兼容、默认不破坏现有 TUI / channels / IDE / CLI 行为;新 daemon 能力通过 capability tag 暴露。remote web chat / web terminal 先 dogfood daemon;channel / IDE daemon 化只作为 future behind-flag 评估;native local TUI 不进入默认切换计划。 | Stage | 状态 | 范围 | |---|:---:|---| @@ -136,7 +135,7 @@ ACP NDJSON 协议 → HTTP+SSE daemon | **Stage 1.5a must-haves** | ⏳ **P0** | chiga0 10 must-haves 剩 9 项 — Mode B 生产 blocker(~2 周,9 PRs 可并行)| | **Stage 1.5c** | ⏳ **P0** | daemon-side state CRUD 8 routes — Mode B 远端 client 摆脱 thin shell(~3-5d)| | Stage 1.5-prereq | ⏳ **P1** | chiga0 6 architecture findings — `AcpChannel` / `EventBus` / `PermissionMediator` lift(~1-2 周)| -| Stage 1.5-client adapters | 🔧 **P1 behind flag** | TUI / channels / web/debug / IDE 作为 daemon HTTP/SSE clients 试点;默认切换必须等 P0/P1 | +| Stage 1.5-client/render | 🔧 **P1 / POC 并行** | remote web chat / web terminal 先做 technical POC,验证 `qwen serve` + BFF/browser + HTTP/SSE typed events + shared reducer/render core 可行性;native TUI 复用渲染能力但保持直连;channel / IDE 迁移后置评估 | | **Stage 1.5b** Mode A | ⏳ **P2 推迟** | Mode A `qwen --serve` flag — [Issue #4156](https://github.com/QwenLM/qwen-code/issues/4156);A1 [PR#4160](https://github.com/QwenLM/qwen-code/pull/4160) ✅;剩余推迟到 1.5c 后 | | Stage 1.5-remote-control | ⏳ **P2 后置** | [#3929](https://github.com/QwenLM/qwen-code/pull/3929)/[#3930](https://github.com/QwenLM/qwen-code/pull/3930)/[#3931](https://github.com/QwenLM/qwen-code/pull/3931) 后续作为 daemon facade | | Stage 2a-2d | ⏳ 待开 | 协议补齐(WebSocket / mDNS / OpenAPI / Prometheus / `/ext` + Reverse RPC)| @@ -163,7 +162,7 @@ ACP NDJSON 协议 → HTTP+SSE daemon | [Issue #3803](https://github.com/QwenLM/qwen-code/issues/3803) | OPEN | daemon proposal / Stage 1.5 tracker;最新 comment 将 P0/P1/P2 重排为 Mode B must-haves + state CRUD 优先,Mode A 后置 | | [Issue #4156](https://github.com/QwenLM/qwen-code/issues/4156) | OPEN | Mode A 设计 issue,但最新结论是 **Mode A hold,核心推进 Mode B** | | [PR#4132](https://github.com/QwenLM/qwen-code/pull/4132) | OPEN / changes requested | `/demo` debug page 可继续作为 Mode B POST+SSE client 验证面 | -| [PR#3929](https://github.com/QwenLM/qwen-code/pull/3929) | OPEN draft | remote-control foundation 后置;应等 TUI / channels / web / IDE 适配完成后改为 daemon HTTP/SSE client facade | +| [PR#3929](https://github.com/QwenLM/qwen-code/pull/3929) | OPEN draft | remote-control foundation 后置;应等 remote web chat / web terminal contract 稳定后改为 daemon HTTP/SSE client facade | | [PR#3930](https://github.com/QwenLM/qwen-code/pull/3930) | OPEN draft / changes requested | worker/WebSocket 层若保留,应成为 daemon transport facade,而不是替代 HTTP/SSE + EventBus-backed event contract | | [PR#3931](https://github.com/QwenLM/qwen-code/pull/3931) | OPEN draft / changes requested | remote-control TUI attach 后置;TUI 自身的 Mode B client adapter 更优先 |