Skip to content

feat(cli): add supervised Fleet workspace - #8869

Closed
yiliang114 wants to merge 2 commits into
codex/feat-fleet-1a-contractsfrom
codex/feat-fleet-1b-runtime
Closed

feat(cli): add supervised Fleet workspace#8869
yiliang114 wants to merge 2 commits into
codex/feat-fleet-1a-contractsfrom
codex/feat-fleet-1b-runtime

Conversation

@yiliang114

@yiliang114 yiliang114 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR upgrades the Fleet preview from the Stage 1A in-process contract to a usable supervised-process MVP. With experimental.fleet: true, teammates run as independent Qwen Code OS processes, exchange prompts and peer messages through an authenticated supervisor, and project bounded semantic transcripts back into the leader UI.

Wide terminals show the leader and up to three teammates at the same time, while narrow terminals, dialogs, screen readers, and native scrollback keep the existing single-view tabs. The active tab remains the composer target.

The runtime also handles startup races, worker crashes, cooperative and forced shutdown, per-worker credential revocation, stale supervisor protocol replacement, subprocess identity, nested-team denial, and Fleet credential scrubbing from child environments.

This is a stacked PR based on #8859.

Why it's needed

Stage 1A exposes the Fleet workflow and contracts, but all teammates still share the leader process and only one detailed transcript is visible at a time. This change makes the preview independently executable and gives it the same-terminal multi-view behavior needed for useful /coordinate dogfooding.

Reviewer Test Plan

How to verify

  1. Enable experimental.fleet, keep experimental.agentTeam disabled, restart Qwen Code, and open a terminal at least 120 columns wide.
  2. Run /coordinate with two independent read-only investigation tasks. Confirm both teammates start with distinct OS PIDs and the leader plus both teammate transcripts are visible in one frame.
  3. Move focus with the existing Agent View navigation, send a follow-up to one teammate, and confirm only that session receives it.
  4. Let one teammate finish and stop another. Confirm the finished transcript remains inspectable, the stopped worker exits, and the leader plus any sibling continue running.
  5. Repeat in a narrow terminal and confirm the UI falls back to the existing tabs without clipped panes.
  6. Exit the leader and confirm no managed teammate remains active in the supervisor roster.

Evidence (Before & After)

Before: Fleet teammates run in the leader process and detailed Agent View content is shown one tab at a time.

After: Fleet teammates use supervised OS processes and a wide terminal renders the leader plus multiple bounded semantic transcript panes simultaneously.

Focused supervisor, runtime, grid, identity, messaging, lifecycle, and environment-scrubbing tests pass. Core typecheck and the targeted Core build pass.

The preview has now been run end to end. integration-tests/terminal-capture/fleet-dogfood.ts drives a real PTY leader, a real supervisor process and real teammate subprocesses through the production defaultSpawnWorker path over the real socket, with only the model backend stubbed. Leader + 1 and leader + 3 both pass all six gates: separate OS PIDs, handshake, status transitions, live transcript projection, teammate to leader reporting, a cross-process approval answered from the leader, a leader to teammate follow-up through the mailbox, and no surviving teammate process after the leader exits. Full logs, PID samples, TUI capture and idle-CPU numbers are in the dogfooding comment on this PR.

The earlier note that a run was blocked by an ACP bridge / workspace-link mismatch applied to a stacked worktree; a clean checkout with its own npm install does not hit it.

Tested on

OS Status
🍏 macOS ⚠️ focused automated tests pass; end-to-end run not repeated here
🪟 Windows ⚠️ not tested
🐧 Linux ✅ full end-to-end dogfood run, leader + 1 and leader + 3

Environment (optional)

macOS, Node.js 22.22.0, source-mode stacked worktree.

Risk & Scope

  • Main risk or tradeoff: the semantic transcript channel is deliberately bounded; oversized snapshots degrade to a minimal view rather than disconnecting the worker.
  • Measured: each teammate polls the supervisor 20x/sec. Idle steady-state cost is ~6% of one core for leader + 1 and ~13% for leader + 3. Acceptable for an experimental, default-off preview; socket fan-out should land before Fleet is ever on by default.
  • Not validated / out of scope: raw PTY panes, arbitrary shell/TUI hosting, external attach, persistence, crash reattachment, multi-client leases, and plan-required teammate approval. Ordinary cross-process tool-confirmation approval does work and is demonstrated in the dogfooding comment; only the plan-mode variant is rejected on the supervised runtime.
  • Breaking changes / migration notes: none. Fleet remains experimental and disabled by default. A live authenticated Stage 1A supervisor is shut down before the protocol v2 supervisor starts; newer supervisor protocols fail closed.

Linked Issues

Part of #8841.

中文说明

这个 PR 做了什么

这个 PR 将 Fleet Preview 从 Stage 1A 的进程内契约升级为可用的受监管子进程 MVP。开启 experimental.fleet: true 后,teammate 会作为独立的 Qwen Code 操作系统进程运行,通过带认证的 supervisor 交换 prompt 和 peer message,并将有界的语义化 transcript 投影回 leader UI。

宽终端会同时展示 leader 和最多三个 teammate;窄终端、对话框、读屏模式以及原生 scrollback 继续使用现有单视图 tabs。当前激活的 tab 仍然是 composer 的发送目标。

运行时同时处理启动竞态、worker 崩溃、协作式与强制关闭、每个 worker 的凭据撤销、旧 supervisor 协议替换、子进程身份、禁止嵌套团队,以及从子环境中清除 Fleet 凭据。

这是一个以 #8859 为 base 的 stacked PR。

为什么需要它

Stage 1A 已经暴露 Fleet workflow 和 contracts,但所有 teammate 仍共享 leader 进程,并且一次只能查看一个详细 transcript。这个改动让 preview 真正由独立进程执行,并提供 /coordinate dogfood 所需要的单终端多视图体验。

Reviewer 测试计划

如何验证

  1. 开启 experimental.fleet,保持 experimental.agentTeam 关闭,重启 Qwen Code,并使用至少 120 列的终端。
  2. 使用 /coordinate 启动两个独立的只读调查任务。确认两个 teammate 使用不同的操作系统 PID,并且同一帧中能同时看到 leader 和两个 teammate transcript。
  3. 使用现有 Agent View 导航切换焦点,只向其中一个 teammate 发送 follow-up,确认只有对应 session 收到消息。
  4. 让一个 teammate 完成并停止另一个。确认已完成 transcript 仍可查看,被停止的 worker 已退出,leader 和其他 sibling 继续运行。
  5. 在窄终端中重复,确认 UI 回退到现有 tabs,并且 pane 不会被裁坏。
  6. 退出 leader,确认 supervisor roster 中没有仍处于 active 状态的托管 teammate。

证据(Before & After)

Before:Fleet teammate 在 leader 进程内运行,详细 Agent View 内容一次只显示一个 tab。

After:Fleet teammate 使用受监管的独立操作系统进程,宽终端会同时渲染 leader 和多个有界语义 transcript pane。

Supervisor、runtime、grid、identity、messaging、lifecycle 和环境凭据清理的定向测试均通过。Core typecheck 和定向 Core build 通过。最终独立代码 gate 为 0 Critical、0 Major。

该预览现已完成端到端真实运行。 integration-tests/terminal-capture/fleet-dogfood.ts 通过真实 PTY 启动 leader,并经由生产路径 defaultSpawnWorker 拉起真实的 supervisor 与 teammate 子进程,走真实 socket 通信,仅 model 后端为桩实现。leader + 1 与 leader + 3 均通过全部六项校验:独立的操作系统 PID、handshake、状态迁移、transcript 实时投影、teammate 向 leader 汇报、在 leader 侧应答跨进程审批、经 mailbox 下发 leader 到 teammate 的后续消息,以及 leader 退出后无残留 teammate 进程。完整日志、PID 采样、TUI capture 与空闲 CPU 数据见本 PR 的 dogfooding 评论。

此前关于 ACP bridge / workspace-link 不匹配导致无法启动的说明仅适用于 stacked worktree;在独立 checkout 中自行 npm install 不会遇到该问题。

测试平台

OS 状态
🍏 macOS ⚠️ 定向自动化测试通过;未在此重复端到端运行
🪟 Windows ⚠️ 未测试
🐧 Linux ✅ 完整端到端 dogfood 运行(leader + 1 与 leader + 3)

环境(可选)

macOS、Node.js 22.22.0、源码模式 stacked worktree。

风险与范围

  • 主要风险或取舍:语义 transcript channel 被刻意限制大小;过大的 snapshot 会降级为最小视图,而不是断开 worker。
  • 实测:每个 teammate 以 20 次/秒轮询 supervisor。空闲稳态开销约为单核的 6%(leader + 1)与 13%(leader + 3)。对默认关闭的实验特性可以接受;在 Fleet 默认开启之前应先落地 socket fan-out。
  • 未验证 / 范围外:raw PTY panes、任意 shell/TUI hosting、external attach、持久化、崩溃后 reattach、多客户端 lease,以及 plan-required teammate approval。普通的跨进程工具确认审批是可用的,并已在 dogfooding 评论中给出证据;仅 plan 模式变体在 supervised runtime 上被拒绝。
  • Breaking change / 迁移说明:无。Fleet 仍是默认关闭的实验功能。协议 v2 supervisor 启动前会先关闭已认证且仍存活的 Stage 1A supervisor;更高版本 supervisor 会 fail closed。

关联 Issue

#8841 的一部分。

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Fleet Stage 1B E2E

Setup

  • Use a wide terminal (at least 120 columns × 24 rows).
  • Set experimental.fleet to true and ui.useTerminalBuffer to true.
  • Keep experimental.agentTeam disabled to prove Fleet selects the supervised runtime.

Scenario

  1. Start Qwen Code from the Stage 1B branch and ask it to coordinate two named teammates on two independent read-only repository questions.
  2. Confirm the leader and both named teammate transcripts are visible in the same terminal frame.
  3. Switch panes with the existing tab navigation and send a follow-up only to one teammate.
  4. Confirm only that teammate receives the follow-up and its pane updates live.
  5. Trigger a harmless approval-gated action in a teammate and answer it from that teammate view.
  6. Exit the leader normally.

Expected

  • Each teammate has an OS PID distinct from the leader and from the other teammate.
  • The grid shows leader + teammate transcripts simultaneously; a narrow terminal falls back to tabs.
  • Status, model text, tool activity, approvals, and final results update without parsing terminal bytes.
  • A teammate cannot create a nested team.
  • Worker shell children do not inherit Fleet routing or authentication variables.
  • Leader exit stops both worker processes and leaves no active worker in the supervisor roster.

Result (2026-08-10)

  • Focused supervisor/runtime/grid tests passed, including distinct worker lifecycle simulation, peer mailbox delivery, shutdown acknowledgement, early exit, and cleanup.
  • A real /coordinate source run was attempted at 160×50, but the CLI failed before startup because this stacked worktree reuses the main checkout's workspace dependency links and @qwen-code/acp-bridge/externalToolGuard was not exported there.
  • Targeted Core build passed. Targeted ACP bridge build remained blocked because its workspace link resolved incompatible Core goal-wire declarations from the main checkout.
  • No Fleet supervisor or teammate process was started by the failed run. Manual wide-terminal confirmation remains for reviewer dogfood in a clean checkout/install.

Teammates and the supervisor spawn detached from the leader's terminal,
so `stdio: 'ignore'` meant a subprocess that died during startup left no
trace at all: the leader could only report "exited before becoming
ready", with no exit code, no output and nothing to read afterwards.

- Capture supervisor and per-teammate output to log files under the
  agent-view store, falling back to the previous behaviour when the log
  cannot be opened so diagnostics never block a spawn.
- Carry the exit code and a bounded tail of the captured output into
  `lastError`, through the supervisor `state` event, into RemoteSession,
  and out to the composer's status label and the thrown start error.
- Emit a terminal `state` event on spawn failure; previously nothing did,
  so the leader waited out the full readiness timeout for a failure the
  supervisor already knew about.
- Add `QWEN_FLEET_DEBUG=1` lifecycle tracing across spawn, config load,
  auth and handshake. Leader-side breadcrumbs go to a log file rather
  than stderr, which Ink owns.
- Always write a teammate's fatal error to its log, flag or not.
- Show the Fleet grid with a single teammate; leader-beside-one-teammate
  is the smallest scenario worth debugging and it used to fall back to
  tabs.
- Add an end-to-end dogfood harness driving the real subprocess path
  with a stubbed model backend, plus troubleshooting docs.
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Dogfooding pass: Fleet now actually runs, and fails legibly

Pushed as 46bcc12. This addresses the "can't run it, can't debug it" gap. The draft has now been run end to end — the earlier note that no worker ever started is no longer true.

The core problem

Both the supervisor and every teammate spawned with stdio: 'ignore', with no log file and no debug flag. A teammate that died during startup — the likeliest first failure, on auth or config — produced exactly one line in the leader:

Agent "scout" exited before becoming ready (failed).

No exit code, no output, nothing to read afterwards. That is why the previous attempt at a real run couldn't be diagnosed.

What changed

Change Detail
Subprocess logs ~/.qwen/jobs/<id>/worker.log per teammate, ~/.qwen/daemon/supervisor.log. Truncated per run. Falls back to 'ignore' if the log can't be opened, so diagnostics never block a spawn.
Exit diagnostics Exit code + bounded output tail flow into lastError → supervisor state event → RemoteSession → the composer's status label and the thrown start error. First line is self-sufficient (code + log path) so the one-line UI stays useful.
Spawn-failure event failSession now emits a terminal state event. Previously nothing did, so the leader sat out the full 60s readiness timeout for a failure the supervisor already knew about.
QWEN_FLEET_DEBUG=1 Lifecycle breadcrumbs across spawn → spec → config → auth → mcp → handshake. Leader-side breadcrumbs go to ~/.qwen/daemon/fleet-debug.log, not stderr, which Ink owns.
Unconditional fatal write A teammate's fatal error is always written to its log, flag or not.
Single-teammate grid canShowFleetGrid required ≥2 teammates, so the smallest debug scenario fell back to tabs. Now ≥1.

Real run — leader + 1 real subprocess teammate

integration-tests/terminal-capture/fleet-dogfood.ts (added). Real PTY, real bundled CLI, real supervisor and teammate processes through the production defaultSpawnWorker path, real unix socket. Only the model backend is stubbed (the repo's existing fake-openai-server), so it needs no API key and is deterministic. No injected spawners anywhere.

$ npm run bundle
$ npx tsx integration-tests/terminal-capture/fleet-dogfood.ts 1
{
  "teammatesRanAsSeparateProcesses": true,
  "leaderSpawnedAll": true,
  "teammateReportedToLeader": true,
  "approvalAnsweredCrossProcess": true,
  "leaderFollowUpDelivered": true,
  "allTeammatesStoppedOnLeaderExit": true
}

Separate OS processes, from pgrep during the run:

supervisor:  182405 node dist/cli.js --internal-agent-view-supervisor
teammates:   182421 node dist/cli.js --internal-fleet-teammate
leader:      182384 (PTY)
after leader exit: (no teammate processes remain)

Startup and handshake, from worker.log (QWEN_FLEET_DEBUG=1):

[fleet:spawn]    log opened role=teammate sessionId=scout@dogfood
[fleet:teammate] entrypoint reached socket=/tmp/qwen-agent-view-0/supervisor-a82732ff4998.sock
[fleet:teammate] spec loaded sessionId=scout@dogfood team=dogfood readOnly=true
[fleet:teammate] settings parsed
[fleet:teammate] cli config loaded model=fake-model
[fleet:teammate] auth/app initialized
[fleet:teammate] config initialized, mcp ready
[fleet:teammate] session started status=running
[fleet:teammate] run loop exited stopped=true

Leader side (fleet-debug.log): dispatched, awaiting handshaketeammate ready, 1.9s apart.

Final frame — leader beside a single teammate, both transcripts live:

│ Leader  ✓ idle                                        ││ scout  ✓ idle
│  > Start the dogfood run.                             ││  > FLEET_DOGFOOD_TEAMMATE_TASK: read fleet-target.txt …
│  ✓ TeamCreate Create team "dogfood"                   ││  ✓ read_file fleet-target.txt
│  ✓ Agent inspect fleet-target.txt                     ││  ✓ send_message Send to leader: TEAMMATE_REPORT[scout]…
│    Teammate "scout" is now running concurrently.      ││    Message sent to "leader".
│                                                       ││  ◆ TEAMMATE_DONE[scout]
│  ◆ LEADER_SPAWNED_ALL (scout). Waiting for reports.   ││  > <team_message_9093f6280c7d25a6 from="leader">
│  ● scout reported back                                ││    LEADER_FOLLOWUP: acknowledge receipt, scout.
│  ✓ SendMessage Send to scout: LEADER_FOLLOWUP…        ││    </team_message_9093f6280c7d25a6>
│    Message sent to "scout".                           ││  ◆ TEAMMATE_FOLLOWUP_ACK[scout]

Every observable on the checklist: process start, handshake/auth, status transitions (● blocked✓ idle), leader assigning work, teammate receiving a real turn, live transcript projection, teammate→leader reporting (● scout reported back), leader→teammate follow-up with the nonce envelope and unverified-sender warning intact, clean shutdown.

Multi-teammate

… fleet-dogfood.ts 3 — same six gates green. Leader left, three stacked teammate panes right, each with independent state (one ✓ idle, two ● blocked on their own approvals).

One artifact to check with a human eye: at 3 panes with a full approval dialog, the third pane's title looked truncated in my text capture (prob⚠ Auto Mode couldn't classify…). distributeRows gives each pane ~13 rows and the dialog is ~12, so the header may be squeezed out. Could also be my ANSI stripping. Worth a look, not obviously blocking.

The 50ms poll loop — measured

Steady-state CPU, all sessions idle, sampled from /proc/<pid>/stat over 10s (ps -o pcpu was useless here — it averages over process lifetime, which startup dominates):

Teammates Each teammate Supervisor Total
1 2.8% 3.4% ~6.2% of one core
3 2.3 / 1.8 / 2.0% 7.1% ~13.2% of one core

Each teammate issues a workerControl RPC 20×/sec; the supervisor cost scales roughly linearly with teammate count (3.4% → 7.1% for 1 → 3).

Recommendation: keep it for the MVP, don't redesign it in this PR. It is not a correctness problem, the feature is experimental and off by default, and socket fan-out is already scoped for Stage 2. But ~13% of a core for three idle agents is real battery and fan cost, so this should not ship on-by-default with polling still in place. Suggest it becomes an explicit Stage 2 exit criterion rather than a nice-to-have.

Approval routing — reachable, and it works

The run disproves the assumption that this path is unreachable. A read-only teammate calling send_message raised a genuine approval inside the teammate process, which streamed over the socket and rendered in the leader's pane:

│ scout  ● blocked
│   Send to leader: TEAMMATE_REPORT[scout]: FLEET_TARGET_CONTENT_OK
│   Do you want to proceed?
│   › 1. Yes, allow once
│     2. Switch to Default Mode and allow once (recommended)
│     3. No, suggest changes (esc)

Answering it (↓ → Enter) routed the decision back through SupervisedRuntime.answer → supervisor → worker, and the tool then executed. Nothing was weakened to produce this — a read-only teammate reached it on the ordinary /coordinate path.

Two caveats on how it was reached: the teammate was in Auto mode and its classifier call failed against the stub backend, so it fell back to asking. With a working classifier send_message may well be auto-approved, so this is not guaranteed to appear in every real run. And the deliberately-blocked case is narrower than the PR body implies: planModeRequired is hard-rejected on the supervised runtime (TeamManager.ts:269), which is plan-mode approval — a different thing from tool-confirmation approval, which demonstrably works cross-process.

Verdict: approval routing should stay a Stage 1B acceptance criterion. It is implemented, reachable and now demonstrated. The remaining gap — plan-required teammates — should be named as its own deferred item rather than being read as "approvals don't work".

Also found

A stale supervisor survives its leader by design (10-min idle grace). If its store is deleted underneath it, the next launch dies with Agent View supervisor socket is already in use and the new supervisor exits. retireIncompatibleSupervisor only handles protocol-version mismatch, not a supervisor whose store has vanished. Reachable in practice by clearing ~/.qwen while a fleet is up. Not fixed here — flagging for Stage 2 recovery work.

Tests

packages/cli $ npx vitest run src/agent-view/ src/ui/components/agent-view/ \
    src/ui/layouts/DefaultAppLayout.test.tsx src/ui/contexts/AgentViewContext.test.tsx
  Test Files  14 passed (14)
       Tests  178 passed (178)

New coverage: fleet-debug.test.ts (10 tests — tail extraction, truncation, unopenable-log fallback, first-line sufficiency), two remote-session tests (diagnostic surfacing, mid-run crash), two supervisor-process tests (exit code + captured output recorded; no diagnostic on clean stop).

Two pre-existing default-case lint errors in fleet-teammate.ts / remote-session.ts blocked the pre-commit hook once those files were staged; fixed with exhaustiveness asserts rather than silent fallbacks.

How to test this locally

git fetch origin codex/feat-fleet-1b-runtime && git checkout codex/feat-fleet-1b-runtime
npm install && npm run bundle

# A. Scripted end-to-end run, no API key needed
npx tsx integration-tests/terminal-capture/fleet-dogfood.ts 1     # leader + 1
npx tsx integration-tests/terminal-capture/fleet-dogfood.ts 3     # leader + 3
# prints six pass/fail gates and an evidence dir (logs, PID samples, TUI capture, idle CPU)

# B. By hand with your own credentials
echo '{"experimental":{"fleet":true}}' > .qwen/settings.json
QWEN_FLEET_DEBUG=1 qwen                  # terminal ≥120 cols
# then: /coordinate investigate <something> and use one or two teammates

# Watch it work
tail -f ~/.qwen/daemon/fleet-debug.log ~/.qwen/jobs/*/worker.log
pgrep -af internal-fleet-teammate        # separate PIDs per teammate

# Prove the diagnostics: make a teammate die on startup
QWEN_FLEET_DEBUG=1 OPENAI_BASE_URL=http://127.0.0.1:1 qwen
# the leader now names the exit code and the log path instead of "did not become ready"

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Two dogfooding findings, deferred to Stage 2

Both surfaced while dogfooding this PR. Neither is a Stage 1B regression — the first is a property of the polling design this stage deliberately shipped with, the second is pre-existing supervisor lifecycle behaviour. Neither blocks #8869. Both are now tracked in #8842 with acceptance criteria, so they do not live only in review chat.

1. Worker poll loop idle CPU

Each teammate issues a workerControl RPC every 50 ms — 20 round trips/sec/teammate regardless of activity. Measured at 46bcc12, all sessions idle, from /proc/<pid>/stat over 10 s:

Teammates Per teammate Supervisor Total
1 2.8% 3.4% ~6.2% of one core
3 2.3 / 1.8 / 2.0% 7.1% ~13.2% of one core

Recorded in #8842 as the baseline to regress against. Socket/event fan-out was already in Stage 2 scope; it is now an explicit exit criterion rather than a nice-to-have, on the grounds that Fleet must not become default-on while polling remains.

2. Stale supervisor wedges later launches

A supervisor outlives its leader by 10 minutes. Delete its store while it is alive and the next launch cannot authenticate to it (the token lived in the deleted daemon/supervisor.json), so it spawns a replacement that dies on Agent View supervisor socket is already in use — the socket sits in a per-uid /tmp fallback outside the store. retireIncompatibleSupervisor only retires on protocol-version mismatch. A full reproduction is in #8842.

Scope call

Fixing either here would pull socket fan-out or supervisor recovery into Stage 1B, both of which are Stage 2 by design. Keeping #8869 to Stage 1B.

@qwen-code-review-bot

Copy link
Copy Markdown
Collaborator

I found two blockers in the supervised-process lifecycle and authorization model:

  1. kill can report success while the worker remains alive (packages/cli/src/agent-view/supervisor-process.ts:515-522).
    After an unexpected supervisor restart, persisted sessions and worker credentials survive, but this.workers does not. A subsequent kill therefore skips process termination when the handle is missing, marks the session as exited, clears its credential, and returns { killed: true }. The orphaned process can continue running tools even though Fleet reports it as stopped. Please either recover and verify process ownership before signaling it, or fail closed without changing the session state when no live handle is available.

  2. The newly added privileged routes reuse a global bearer stored in supervisor.json (packages/cli/src/agent-view/supervisor-runner.ts:356-436).
    The token is stored with 0600, but agent-launched commands run under the same UID and can read it. That bearer authorizes cross-session operations including dispatch, send, answer, kill, and shutdown, so one permitted shell/MCP command can control Fleet sessions from other workspaces or submit persistent approval outcomes. Environment scrubbing does not protect a predictable on-disk credential. These routes need workspace-bound, least-privilege capabilities—or another mechanism that does not expose the global control credential to ordinary child processes.

The overall subprocess and UI flow looks coherent, but these ownership boundaries should be fixed before merge.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Pausing this draft while we reset the direction around the merged #8804 baseline. The subprocess experiment is not covered by #8804, but the current stack also carries a broader Fleet platform and has unresolved lifecycle and authorization blockers, so I’m not advancing it toward merge. If dogfooding shows a concrete isolation gap, the next step should be a smaller vertical slice from main.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Follow-up: the remaining Qwen ↔ Herdr state-detection gap is now reproduced in herdrdev/herdr#2756. That stays in Herdr’s detection layer; it does not justify reviving the Qwen-side Reporter or Fleet stack.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Closing as superseded. Not because independent teammate processes were the wrong idea, but because this is not how they get built here.

The supervisor, worker entrypoint, credential scoping and lifecycle handling this PR implements already ship in the repository for other callers — serve/create-sub-session.ts spawns fresh top-level sub-sessions with promptId correlation, concurrency slots and abort; serve/channel-worker-*.ts is ~9,595 lines of fork, heartbeat, startup timeout, kill/stop grace, token and workspace env isolation and diagnostics; and acp-bridge/child-heap-policy.ts gives every child its own heap budget. A second, parallel implementation of that layer is the part that does not survive.

The topology does not survive either. This stack is keyed one-fleet-per-project with a single-leader lock, which makes a second Qwen session in the same repository a read-only roster viewer. That participant — a session someone already started, doing its own work — is the one the replacement design is about.

Replacement: #9399, tracked on #8724. It reuses the fleet plan's semantic/terminal channel split, its turn correlation, and its verification that the existing file lock model needs no upgrade. See #8718 for a correction to that plan's closing rationale.

The branch is not deleted, and nothing here is lost if the direction changes.

中文说明

作为已被取代关闭。不是因为"独立 teammate 进程"这个想法错了,而是因为在这个仓库里它不该这样建。

本 PR 实现的 supervisor、worker 入口、凭据隔离与生命周期处理,仓库里已经为其他调用方提供了:serve/create-sub-session.ts 能 spawn 全新的顶层 sub-session,带 promptId 关联、并发槽位与中止;serve/channel-worker-*.ts 约 9,595 行,涵盖 fork、心跳、启动超时、kill/stop grace、token 与 workspace 环境隔离及诊断;acp-bridge/child-heap-policy.ts 为每个子进程分配独立堆预算。不成立的正是"再平行实现一套"这件事。

拓扑同样不成立。本栈按"一个项目一个 fleet"加单 leader 锁,这会让同一仓库中第二个 Qwen 会话变成只读的 roster 观察者。而那个参与者——别人已经启动、正在做自己工作的会话——恰恰是替代设计要服务的对象。

替代方案:#9399,跟踪于 #8724。它沿用了 fleet 计划的语义/终端双通道划分、turn 关联,以及"现有文件锁模型无需升级"的验证。对该计划结案理由的更正见 #8718

分支不会删除;若方向变化,这里的工作不会丢失。

@yiliang114 yiliang114 closed this Aug 18, 2026
yiliang114 pushed a commit that referenced this pull request Aug 18, 2026
packages/cli/src/agent-view/ — supervisor-server, supervisor-client,
supervisor-store, supervisor-process, supervisor-runner, terminal-bridge,
protocol and current-cli-argv — has no consumer. All 87 exported symbols
are unreferenced outside the directory, no module imports it by path or by
name, no CLI flag or sentinel string reaches it, and no build config
mentions it. It only imports node builtins and core, so it is a leaf.

It is the bespoke supervisor and PTY half of the fleet architecture, whose
last open PR (#8869) is closed. The peer-session design that replaces it
does not build a terminal or supervisor layer natively, so nothing here
gets a consumer later.

Left in place because it is live and unrelated despite the similar name:
packages/cli/src/ui/components/agent-view/ (AgentTabBar, AgentChatView,
AgentComposer), imported by DefaultAppLayout.

Refs #9399
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants