fix(core,cli): isolate teammate leader turns from agent context - #7576
Conversation
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with a deterministic reproduction on Direction: aligned. Multi-agent context isolation is core to the teammate feature, and this follows the exact pattern established by #7194 ( Size: 36 production lines (TeamManager.ts +5/-1, useGeminiStream.ts +17/-14) and 89 test lines. Well under any threshold. Not applicable for escalation. Approach: the scope feels right — two wrapping points (producer callback registration, consumer effect drain), each independently tested. No unrelated changes, no drive-by refactors. The fix is the minimal application of an established pattern to the remaining uncovered path. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的 bug,有确定性复现( 方向:对齐。多智能体上下文隔离是 teammate 功能的核心,本 PR 完全遵循 #7194 建立的模式(在 producer 回调和 React consumer drain 处使用 规模:36 行生产代码(TeamManager.ts +5/-1,useGeminiStream.ts +17/-14),89 行测试代码。远低于任何阈值,无需升级。 方案:范围合理——两个包裹点(producer 回调注册、consumer effect drain),各自独立测试。无无关改动,无顺手重构。修复是将已建立的模式应用于剩余未覆盖路径的最小改动。 进入代码审查 🔍 — Qwen Code · qwen3.8-max-preview Reviewed at |
Code ReviewIndependent proposal: given the issue (teammate plan approval leaks AsyncLocalStorage context into the leader turn), I would wrap the Comparison with the diff: the PR does exactly this. No simpler path missed.
No critical blockers. No AGENTS.md violations. The change reuses the existing Real-Scenario TestingThis is a non-visual runtime fix (AsyncLocalStorage context isolation). The observable effect — correct model selection in leader turns — requires a multi-agent team with different models per agent, which is not feasible to drive headlessly. The deterministic tests provide the before/after evidence (see issue #7574's reproduction). tmux is not available on this CI runner. Headless smoke test confirming the CLI starts and responds correctly with the PR code: Unit tests (all pass): Build and typecheck: pass. 中文说明代码审查独立方案: 根据 issue(teammate 计划审批将 AsyncLocalStorage 上下文泄漏到 leader 轮次),我会在 TeamManager 注册时用 与 diff 对比: PR 完全这样做了。没有遗漏更简路径。
无关键阻塞。无 AGENTS.md 违规。改动复用现有 真实场景测试这是一个非视觉运行时修复(AsyncLocalStorage 上下文隔离)。可观测效果——leader 轮次的正确模型选择——需要具有不同模型的多智能体团队,无法在无头模式下驱动。确定性测试提供了 before/after 证据(见 issue #7574 的复现)。 此 CI runner 上 tmux 不可用。无头冒烟测试确认 CLI 使用 PR 代码正常启动和响应(见上方输出)。 单元测试全部通过(11 + 160 = 171 个测试)。Build 和 typecheck 通过。 — Qwen Code · qwen3.8-max-preview Reviewed at |
|
Confidence: 5/5 — clean across every stage; would merge without hesitation. This is a textbook application of an established pattern to a remaining uncovered path. The problem is real and deterministically reproduced (#7574), the fix is the same The only thing I couldn't do is a live dual-model TUI run (tmux unavailable on this runner, and the scenario requires a multi-agent team with distinct models). The deterministic tests cover both boundaries thoroughly, and the headless smoke test confirms the CLI starts and responds correctly. 中文说明置信度:5/5 — 每个阶段都干净;毫不犹豫地合并。 这是将已建立的模式应用于剩余未覆盖路径的教科书式修复。问题真实且有确定性复现(#7574),修复与 #7194 为后台通知引入的 唯一无法执行的是实时双模型 TUI 运行(此 runner 上 tmux 不可用,且场景需要具有不同模型的多智能体团队)。确定性测试充分覆盖了两个边界,无头冒烟测试确认 CLI 正常启动和响应。 — Qwen Code · qwen3.8-max-preview Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
— qwen3.7-max via Qwen Code /review
|
I ran an additional live tmux check with Qwen Code 0.20.1:
This is useful supplemental lifecycle coverage, but it does not directly verify the plan-approval AsyncLocalStorage fix in this PR. A live verification of that fix would still require a |
gwinthis
left a comment
There was a problem hiding this comment.
Review: APPROVE (C=0)
Summary
Focused context-isolation fix (+110/-15) that prevents teammate agent/model context from leaking into leader conversation turns. Two boundaries are isolated: the producer callback (TeamManager) and the consumer drain (React effect), because React batching can restore an outer teammate frame after the producer returns.
Findings
None blocking. Correct and minimal:
- Producer boundary (
TeamManager.setLeaderMessageCallback): wraps callback inrunOutsideAgentContext()— clears AsyncLocalStorage before the leader callback executes. - Consumer boundary (
useGeminiStreameffect): wraps the queue drain inrunOutsideAgentContext()— necessary because React can flush the effect after restoring a teammate frame. - Both boundaries are independently tested — core test verifies
getCurrentAgentId() === nullandgetRuntimeContentGenerator() === undefinedin the callback; CLI test verifies the runtime view is undefined whensendMessageStreamis called. - No API changes, no breaking changes. The
runOutsideAgentContextutility is the established mechanism for clearing agent context.
Architecture Insight
AsyncLocalStorage context leakage across callback boundaries: When a callback is registered in one async context (teammate) but invoked in another (leader), the invocation context can leak through AsyncLocalStorage. The fix pattern is: wrap the callback registration to clear the context at invocation time. When the consumer is a React effect, a second boundary is needed because React's batching can restore the outer frame between producer and consumer.
中文说明
评审:APPROVE (C=0)
概要
聚焦的上下文隔离修复(+110/-15):防止 teammate agent/model 上下文泄漏到 leader 对话轮次。隔离两条边界:生产者回调(TeamManager)和消费者 drain(React effect),因为 React batching 可在生产者返回后恢复外层 teammate frame。
架构洞察
AsyncLocalStorage 跨回调边界的上下文泄漏: 回调在一个异步上下文(teammate)中注册但在另一个(leader)中调用时,调用上下文可通过 AsyncLocalStorage 泄漏。修复模式:在回调注册时包裹上下文清除。当消费者是 React effect 时,需要第二条边界。
— qwen3.7-max via Qwen Code /review
doudouOUC
left a comment
There was a problem hiding this comment.
Review Summary
Clean, well-targeted fix for the AsyncLocalStorage context leak in teammate→leader message paths.
What it does
Two-layer isolation ensuring leader turns never inherit a teammate's agent frame:
- Producer (
TeamManager.setLeaderMessageCallback): wraps the registered callback inrunOutsideAgentContextat registration time, so invocations from within a teammate frame execute cleanly. - Consumer (
useGeminiStreamteammate queue drain): wraps the entire batch submission inrunOutsideAgentContext, guarding against React flushing the effect while still inside a teammate's ALS frame.
Why both layers are needed
Producer-only isolation is insufficient because React batching can restore an outer teammate frame after the producer callback returns. The consumer repeats the boundary independently — a sound defensive pattern already validated by the notification drain fix for #7156.
Verification
- Two focused regression tests independently cover each boundary with deterministic reproduction (no model calls needed).
- Ubuntu CI passes. The
runOutsideAgentContext→storage.exit()path is synchronous with negligible overhead.
Minor observations (non-blocking)
- The
as nevertype casts in tests are a pragmatic hack; a shared mock factory could unify these later. setLeaderMessageCallbackcreates a new closure wrapper on each call — acceptable given it's only called once per manager lifecycle.
LGTM — minimal, focused, and well-tested.
Maintainer Local Verification ReportVerified PR #7576 locally on macOS (darwin, Node.js v22.22.2) against commit 1. Focused Unit TestsCore — Includes the new test CLI — Includes the new test 2. Build / Typecheck / LintAll three checks passed with zero errors: 3. CI StatusAll 10 CI checks passed (Ubuntu test suite, lint, build, typecheck, triage, etc.). macOS and Windows test jobs were skipped per CI configuration. 4. Code Review SummaryThe change is minimal and well-scoped (+110/−15 across 4 files):
The approach is consistent with the existing fix in #7194 for background-agent notifications. 5. Notes
VerdictReady to merge. The fix correctly isolates both the producer and consumer boundaries, the tests are deterministic and focused, and all standard checks pass. 中文版本维护者本地验证报告在 macOS (darwin, Node.js v22.22.2) 上针对 commit 1. 聚焦单元测试Core — 包含新增测试 CLI — 包含新增测试 2. 构建 / 类型检查 / Lint三项检查均零错误通过。 3. CI 状态全部 10 项 CI 检查通过(Ubuntu 测试套件、lint、build、typecheck、triage 等)。macOS 和 Windows 测试任务按 CI 配置跳过。 4. 代码审查摘要改动精简且范围明确(+110/−15,4 个文件):
方案与 #7194 中对 background-agent 通知的已有修复一致。 5. 备注
结论可以合入。 修复正确隔离了 producer 和 consumer 两条边界,测试确定性且聚焦,所有标准检查通过。 |






What this PR does
This change ensures that teammate-to-leader messages cross a clean async-local boundary before entering the leader conversation. It also isolates the React consumer that submits queued teammate messages, because React can restore an outer teammate frame after the producer callback returns. Focused regressions cover both boundaries independently.
Why it's needed
A plan-required teammate can request approval while running inside its own agent and model context. The leader callback and the later React drain could inherit that context, causing the approval turn to use the teammate model and appear to be a nested rather than top-level session.
Reviewer Test Plan
How to verify
Run the focused plan-approval tests and confirm that a request emitted from a teammate frame reaches the leader callback with no agent ID or teammate runtime. Run the CLI stream-hook tests and confirm that a React flush triggered inside a teammate runtime submits the resulting leader turn without that runtime.
Evidence (Before & After)
Before: the deterministic reproduction observed
agentId='planner-agent', the teammate runtime/model, andisTopLevelSession=falsein the leader drain. After: the producer callback observes no agent frame, and the consumer submission observes no teammate runtime. This is a non-visual runtime fix, so screenshots are N/A.Tested on
Environment (optional)
Node.js 22.22.0. Verified with 11 focused core tests, 160 CLI stream-hook tests, the repository lint, full build, and full typecheck.
Risk & Scope
run_in_backgroundfallback behavior is not changed.Linked Issues
Fixes #7574
Related to #7156 and #7194.
中文说明
本 PR 的改动
本改动确保 teammate 发往 leader 的消息在进入 leader 对话前跨过一个干净的异步本地上下文边界。同时也隔离了提交 teammate 队列消息的 React 消费端,因为 producer 回调返回后,React 仍可能恢复外层 teammate frame。两条聚焦回归测试分别独立覆盖这两个边界。
为什么需要
要求审批计划的 teammate 会在自己的 agent 和模型上下文中发起审批请求。leader 回调以及后续 React drain 可能继承该上下文,导致审批轮次使用 teammate 模型,并被误判为嵌套会话而不是顶层会话。
Reviewer Test Plan
如何验证
运行聚焦的计划审批测试,确认从 teammate frame 发出的请求到达 leader 回调时既没有 agent ID,也没有 teammate runtime。运行 CLI stream hook 测试,确认在 teammate runtime 内触发的 React flush 所提交的 leader 轮次不再携带该 runtime。
证据(修复前后)
修复前:确定性复现中,leader drain 观察到
agentId='planner-agent'、teammate runtime/model,以及isTopLevelSession=false。修复后:producer 回调观察不到 agent frame,consumer 提交也观察不到 teammate runtime。该修复不涉及视觉变化,因此截图不适用。测试平台
环境(可选)
Node.js 22.22.0。已通过 11 个聚焦 Core 测试、160 个 CLI stream hook 测试、仓库 lint、完整 build 和完整 typecheck。
风险与范围
run_in_backgroundfallback 行为不在本次修改范围内。关联 Issue
Fixes #7574
关联 #7156 和 #7194。