Skip to content

feat(core): Workflow P3 — agent({schema, agentType, model, isolation:'worktree'}) (#4721)#5034

Merged
wenshao merged 4 commits into
mainfrom
lazzy/workflow-p3-schema-agenttype-isolation
Jun 13, 2026
Merged

feat(core): Workflow P3 — agent({schema, agentType, model, isolation:'worktree'}) (#4721)#5034
wenshao merged 4 commits into
mainfrom
lazzy/workflow-p3-schema-agenttype-isolation

Conversation

@LaZzyMan

@LaZzyMan LaZzyMan commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Implements phase P3 of the Dynamic Workflows port, building on the merged P1 (#4732) and P2 (#4947). P3 adds the four per-call agent() options that complete the dispatch contract qwen-code's workflow tool matches against upstream Claude Code 2.1.168:

  • agent({agentType: 'X'}) resolves against the declarative-agents registry (shipped via feat(core): declarative agent frontmatter v1 — permissionMode bridge + maxTurns wiring + color allowlist (CC 2.1.168 parity) #4842 + feat(core): port declarative-agent mcpServers + hooks (CC 2.1.168 parity follow-up) #4996) using config.getSubagentManager().findSubagentByName(name). Unresolved names throw the upstream-verbatim "agent({agentType}): agent type 'X' not found". Resolved configs flow through SubagentManager.createAgentHeadless so per-agent mcpServers / hooks get their own lifecycle and the cleanup callback runs in finally (no MCP stdio leaks).
  • agent({model: 'qwen3-max'}) is threaded into SubagentConfig.model so buildRuntimeContentGeneratorView sees the override and routes a different provider correctly. modelConfigOverrides alone would only swap the model name within the existing provider's runtime view — a subtle gotcha caught during the contract scout.
  • agent({isolation: 'worktree'}) provisions a fresh git worktree under <projectRoot>/.qwen/worktrees/agent-<7hex> via GitWorktreeService.createUserWorktree (mirroring AgentTool 1849-1963), then rebinds targetDir / cwd / getFileService / getWorkspaceContext on a prototype-chained Config override so the subagent's Edit / Write / Read / Glob / Grep / Ls / Shell tools anchor inside the worktree. On completion: hasWorktreeChanges + hasUnmergedWorktreeCommits decide whether to auto-remove (clean) or preserve and append [worktree preserved at <path> on branch <branch>] to the result (dirty). Parent-dirty trees are refused with a clear error to avoid silently running the subagent against a stale HEAD (matching AgentTool's UX).
  • agent({isolation: 'remote'}) throws "agent({isolation:'remote'}) is not available in this build" verbatim (upstream 2.1.168 parity — the binary ships the option but gates the feature off).
  • agent({schema: S}) injects a per-call SyntheticOutputTool (existing tools/syntheticOutput.ts, AJV-backed) into a fresh per-subagent ToolRegistry built via rebuildToolRegistryOnOverride, then attaches an AgentEventEmitter listener that watches TOOL_CALL / TOOL_RESULT events for structured_output invocations. A successful call's args are captured as the dispatch return value (the workflow agent now returns object, not string, in schema mode); after two failed attempts the third failure aborts the dispatch and throws the upstream-verbatim "subagent completed without calling StructuredOutput (after 2 in-conversation nudges)".

No changes to agent-core.ts — the entire 2-nudge counter lives in the dispatch layer via event listening, so the shared subagent loop used by AgentTool / AgentInteractive / AgentTeam is unaffected.

Architecture notes:

  • The fast path (no agentType / model / isolation / schema) is preserved byte-for-byte from P1/P2: direct AgentHeadless.create with the hardcoded workflow subagent prompt and the disallowed-tool floor. Zero added overhead for the common case.
  • The workflow disallowed-tool floor [SendMessage, ExitPlanMode] is Array.from(new Set([floor, agentType.disallowedTools])) unioned BEFORE convertToRuntimeConfig runs, so the manager's transformToToolNames normalizes display names / MCP patterns for the merged set — no duplicated normalization in workflow code, no path where a permissive agentType can re-enable a workflow-forbidden tool.
  • The sandbox's agent() wrapper now revives per-call object returns through JSON.parse(JSON.stringify(value)) inside the vm runInContext block. This closes the same T1 / T8 / T14 host-prototype-escape vector that PR feat(core): Workflow P2 — parallel() + pipeline() concurrent fan-out (#4721) #4947 closed for parallel() / pipeline() — schema-mode returns a host-realm object, and handing it to the script verbatim would reopen the escape via result.constructor.constructor("return process")(). Two new sandbox security tests regress this (constructor-chain probe + non-JSON-serializable collapse).
  • WorkflowAgentResult widens from string to string | object. The widening is transparent for the no-schema path (still string); only schema mode produces objects.

Why it's needed

The P1/P2 stubs surfaced clear "scheduled for P3" error messages but the workflow tool was unusable for the upstream /deep-research-style scripts that rely on agent({schema}) for typed outputs, agent({agentType}) for picking the right subagent kind, agent({model}) for cost-sensitive routing, and agent({isolation: 'worktree'}) for parallel file-mutating subagents. P3 unlocks all four — and since #4842 (frontmatter v1) + #4996 (mcpServers + hooks) merged the declarative-agents registry with CC 2.1.168 parity, the SubagentManager.createAgentHeadless API was already wired with full model / MCP / hooks lifecycle support. P3 just routes through it rather than reimplementing.

Reviewer Test Plan

How to verify

Unit + integration (workflow suite, 159 tests; adjacent regression, 217 tests):

cd packages/core
npx vitest run \
  src/agents/runtime/workflow-sandbox.test.ts \
  src/agents/runtime/workflow-orchestrator.test.ts \
  src/tools/workflow/workflow.test.ts \
  src/utils/concurrencyLimiter.test.ts
# → 4 files, all green (159/159)

npx vitest run \
  src/subagents/ \
  src/config/config.workflow* \
  src/tools/syntheticOutput* \
  src/tools/agent/agent-override.test.ts
# → 10 files, all green (217/217)

New tests covering P3 specifically:

  • workflow-sandbox.test.ts:
    • 4 passthrough tests (agent({schema/agentType/model/isolation:'worktree'/'remote'}) reach dispatch verbatim, sandbox no longer rejects them)
    • 1 invalid-isolation-mode rejection ({isolation:'not-a-real-mode'} still throws at sandbox level since no dispatch can interpret it)
    • 2 security regressions (object return constructor-chain probe + non-JSON-serializable collapse to null)
  • workflow-orchestrator.test.ts (new WorkflowOrchestrator P3 describe block):
    • agentType resolves SubagentConfig and routes through createAgentHeadless
    • agentType not found throws upstream-aligned error
    • opts.model is threaded into SubagentConfig.model for provider routing
    • isolation:'remote' throws upstream-aligned 'not available' error
    • floor disallowedTools always unioned (agentType cannot re-enable them)
    • schema-mode: structured_output success → returns validated args
    • schema-mode: 3 failed structured_output calls → upstream-aligned terminal error
    • schema-mode: subagent never calls structured_output → same terminal error
    • schema-mode attaches an event emitter to the subagent
  • workflow.test.ts:
    • 1 end-to-end integration through WorkflowTool for schema mode (sandbox → orchestrator → dispatch → object capture → safeStringifyResult)

tsc --noEmit and eslint are clean on all touched files.

Real-LLM E2E — 7/7 passing against qwen3-coder-plus via DashScope

A standalone Node harness drives WorkflowOrchestrator + createProductionDispatch against the real qwen3-coder-plus model. Each scenario constructs a full Config via loadCliConfig + refreshAuth('openai') (no mocking of the dispatch path), then runs a workflow script and asserts the outcome against the upstream-aligned contract.

config initialized. selected model: qwen3-coder-plus
isWorkflowsEnabled(): true
▶ S1 schema happy path ............... ✅ → {"primary_color":"blue","confidence":0.9}
▶ S2 agentType not found ............. ✅ upstream-aligned msg
▶ S3 isolation:'remote' unavailable .. ✅ upstream-aligned msg
▶ S4 agentType=Explore resolves ...... ✅ Explore answered: "GREEN"
▶ S5 P1 regression — no opts ......... ✅ → "YELLOW"
▶ S6 parallel + schema ............... ✅ grass=green snow=white
▶ S7 isolation:'worktree' lifecycle .. ✅ subagent → PURPLE; worktree provisioned + lifecycle ran

=== 7/7 scenarios passed ===

Scenario coverage:

# Scenario What it exercises Verdict
S1 agent({schema}) happy path full schema-mode chain: ephemeral SyntheticOutputTool injection → subagent calls structured_output with valid args → event listener captures args → returned to script as object → safeStringifyResult outputs {"primary_color":"blue","confidence":0.9}
S2 agent({agentType:'NoSuchAgent_42xyz'}) upstream-verbatim error agent({agentType}): agent type 'NoSuchAgent_42xyz' not found
S3 agent({isolation:'remote'}) upstream-verbatim error agent({isolation:'remote'}) is not available in this build
S4 agent({agentType:'Explore'}) built-in Explore resolves via SubagentManager.findSubagentByName('Explore') → routes to its (fast) model + read-only tool surface → returns the expected answer
S5 agent("...") (no opts) fast-path regression — P1/P2 byte-for-byte behavior unchanged, direct AgentHeadless.create without going through SubagentManager.createAgentHeadless
S6 parallel([() => agent(...,{schema:S}), () => agent(...,{schema:S})]) concurrent schema-mode subagents under the per-run window; per-call SchemaModeState isolation (no shared counter); per-element vm-realm revival on the returned array
S7 agent({isolation:'worktree'}) provisionWorkflowWorktreecreateUserWorktreecreateWorktreeConfigOverride (Object.create + 6 cwd rebinds) → subagent runs in the worktree → cleanupWorkflowWorktree post-spawn

Pre-existing infrastructure note found during S7: when a clean subagent run leaves the worktree functionally untouched, cleanupWorkflowWorktree still preserves it because GitWorktreeService.hasWorktreeChanges sees the .qwen-session ownership marker as ?? .qwen-session despite writeWorktreeSessionMarker writing the marker name to <gitDir>/info/exclude. This is the same cleanupWorktreeIsolation path AgentTool uses (agent.ts:1607-1698), so the quirk affects both — it is not introduced by P3 and not a blocker. Filed for follow-up: the fix is to use --git-common-dir (vs the per-worktree --git-dir) so the exclude rule lands on the shared info/exclude git actually consults. P3 correctly proceeds with the preserve path when the worktree appears dirty for any reason — only the cleanup-detection heuristic is off here, not the P3 lifecycle.

Harness command:

DSK=$(jq -r .env.DASHSCOPE_API_KEY ~/.qwen/settings.json)
QWEN_CODE_ENABLE_WORKFLOWS=1 \
  OPENAI_API_KEY=$DSK \
  OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 \
  node harness.mjs

The harness source is local (not committed to this PR) — happy to add it under integration-tests/ if the maintainer prefers a reproducible E2E artifact alongside the workflow code.

Evidence (Before & After)

N/A — non-user-visible backend change (workflow execution primitives). The verifiable evidence is the unit + integration test count above.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️ CI-only
🐧 Linux ⚠️ CI-only

Environment

Node v22.21.1, npm 10.x.

Risk & Scope

  • Main risk or tradeoff: the vm-realm boundary widened to accept object returns in schema mode. Closed by per-call JSON.parse(JSON.stringify(value)) revival inside the vm runInContext block (same mechanism PR feat(core): Workflow P2 — parallel() + pipeline() concurrent fan-out (#4721) #4947 used for parallel / pipeline array results) + two security regression tests verifying the constructor-chain escape stays in the vm realm. Worktree isolation introduces a sub-process git invocation per agent({isolation:'worktree'}) call (200-500ms baseline upstream est, no caching), bounded by the 16-concurrency window + 1000-agent cap.
  • Not validated / out of scope: P4 (/workflows UI + extractAndStripMeta + phase-tree progress), P5 (budget global), P6 (resume via JSONL journal), P7 (Ultracode session mode + keyword trigger) remain follow-ups per Feature Request: Port Dynamic Workflows / Ultracode from Claude Code 2.1.160 #4721. Real-LLM E2E follow-up noted above.
  • Breaking changes / migration notes: none. Strictly additive — fast path (no P3 opts) is byte-for-byte preserved. WorkflowAgentResult widens from string to string | object but the no-schema path still returns string. isWorkflowsEnabled() gate (off by default) unchanged.

Linked Issues

Related #4721 (parent design — multi-phase, not closed by this PR)
Related #4732 (P1 merged) #4947 (P2 merged)
Related #4842 #4996 (declarative agents — SubagentManager.createAgentHeadless API used here)

中文说明

这个 PR 做了什么

实现 Dynamic Workflows 移植的 P3 阶段,基于已合并的 P1(#4732)和 P2(#4947)。P3 把 agent() 的 4 个 per-call 选项全部接通,让 workflow tool 的 dispatch contract 跟 Claude Code 2.1.168 对齐:

  • agent({agentType: 'X'})SubagentManager.findSubagentByName(name) 解析 declarative-agents registry(feat(core): declarative agent frontmatter v1 — permissionMode bridge + maxTurns wiring + color allowlist (CC 2.1.168 parity) #4842 + feat(core): port declarative-agent mcpServers + hooks (CC 2.1.168 parity follow-up) #4996 已 ship)。找不到时抛 upstream 字面值 "agent({agentType}): agent type 'X' not found"。命中的 config 经 SubagentManager.createAgentHeadless 走完整 lifecycle —— per-agent mcpServers / hooks 各自隔离,cleanup callback 在 finally 跑,绝不漏 MCP stdio。
  • agent({model: 'qwen3-max'}) 注入 SubagentConfig.model,让 buildRuntimeContentGeneratorView 看到 override 并正确路由 provider。只设 modelConfigOverrides 是 contract scout 时发现的坑 —— 那只会在当前 provider 的 runtime view 里换 model 名。
  • agent({isolation: 'worktree'})<projectRoot>/.qwen/worktrees/agent-<7hex>GitWorktreeService.createUserWorktree 拉新 worktree(镜像 AgentTool 1849-1963),然后用 prototype-chain Config override 重绑 targetDir / cwd / getFileService / getWorkspaceContext,subagent 的 Edit/Write/Read/Glob/Grep/Ls/Shell 锚定到 worktree 内部。结束时 hasWorktreeChanges + hasUnmergedWorktreeCommits 决定 auto-remove(无改动)还是保留 + 追加 [worktree preserved at <path> on branch <branch>] 到结果(脏的)。父 worktree 有未提交改动会拒绝,避免 subagent 看到 stale HEAD(跟 AgentTool 行为一致)。
  • agent({isolation: 'remote'}) 抛字面值 "agent({isolation:'remote'}) is not available in this build"(upstream 2.1.168 二进制里选项存在但关闭)。
  • agent({schema: S})rebuildToolRegistryOnOverride 注入 per-call SyntheticOutputTool(已存在的 tools/syntheticOutput.ts,AJV 校验)到一个新的 per-subagent ToolRegistry,然后挂 AgentEventEmitter 监听 TOOL_CALL / TOOL_RESULT。成功调用的 args 作为 dispatch 返回值(schema 模式下 agent() 返回 object 不是 string);两次失败后第三次失败 abort dispatch,抛 upstream 字面值 "subagent completed without calling StructuredOutput (after 2 in-conversation nudges)"

agent-core.ts 零改动 —— 2-nudge counter 全在 dispatch 层走 event listening,共享的 subagent loop(AgentTool / AgentInteractive / AgentTeam 都用)不动一行。

架构说明:

  • 快路径(无 agentType / model / isolation / schema)跟 P1/P2 一模一样:直接 AgentHeadless.create + 写死的 workflow subagent prompt + disallow floor。零额外开销。
  • 工作流 disallow floor [SendMessage, ExitPlanMode]Array.from(new Set([floor, agentType.disallowedTools]))convertToRuntimeConfig 前 union,所有 entry 一起经 transformToToolNames 做 display name / MCP pattern 规范化 —— workflow 代码不重复规范化,permissive agentType 也没办法把工作流禁掉的工具放回来。
  • sandbox 的 agent() wrapper 现在在 vm runInContext 块里 per-call 用 JSON.parse(JSON.stringify(value)) 复活对象返回。关掉跟 PR feat(core): Workflow P2 — parallel() + pipeline() concurrent fan-out (#4721) #4947parallel() / pipeline() 关掉的同一个 T1/T8/T14 宿主 prototype 逃逸 —— schema 模式返回宿主对象,直接喂给脚本会经 result.constructor.constructor("return process")() 走宿主 Object.prototype 链。两个新的 sandbox 安全测试做了回归(constructor-chain probe + 非 JSON-serializable 降级到 null)。
  • WorkflowAgentResultstring 扩到 string | object。无 schema 路径仍然 string,只有 schema 模式产 object,完全向后兼容。

为什么需要

P1/P2 stub 抛过 "scheduled for P3" 错误,但 workflow tool 对 /deep-research 那种依赖 agent({schema}) typed output、agent({agentType}) 选 subagent 种类、agent({model}) 成本敏感路由、agent({isolation: 'worktree'}) 并行修改文件的脚本来说不可用。P3 全部解锁 —— 而 #4842(frontmatter v1)+ #4996(mcpServers + hooks)合并后 declarative-agents registry 跟 CC 2.1.168 对齐了,SubagentManager.createAgentHeadless API 已经把完整的 model / MCP / hooks lifecycle 接通了。P3 复用它,不重造。

测试验证

单元 + 集成(workflow 套件 159 个测试,相邻回归 217 个测试,合计 376 个测试全绿)。具体命令同英文版。

P2 那种真模型 E2E 跟进:P2 旁路了 createProductionDispatch(P2 改的是 orchestrator 层)。P3 改的全部都是 createProductionDispatch 内部,P2 的 harness 形状不适用。两种可行方案 ——(a)跑完整 CLI bundle,(b)单独 Node harness 复刻 ~200 LOC 的 CLI Config init —— 在本分支尝试过都没办法不污染 package-lock.jsonpackages/vscode-ide-companion/NOTICES.txt

我后续在本分支单独 commit 补真模型 E2E(按 P2 的 S1..S6 场景形状)。上面的单元测试已经穷举了 schema 模式所有 event-driven 路径(TOOL_CALL 捕获 / TOOL_RESULT success-failure 归属 / 2-nudge 边界 / abort 传播)—— 剩下的风险是模型行为形状("qwen3-max 在 schema 模式 system prompt 下会不会真去调 structured_output?"),这跟本 PR 的代码正确性是正交的两件事。

风险与范围

  • 主要风险或权衡: vm 域边界扩展到接受 schema 模式的对象返回。经 vm runInContext 块内 per-call JSON.parse(JSON.stringify(value)) 复活闭合(跟 PR feat(core): Workflow P2 — parallel() + pipeline() concurrent fan-out (#4721) #4947 给 parallel/pipeline 数组结果用的同一机制)+ 两个安全回归测试验证 constructor-chain 逃逸停在 vm 域内。worktree 隔离每次 agent({isolation:'worktree'}) 调用引入一次子进程 git invocation(upstream est 200-500ms,无缓存),由 16 并发窗口 + 1000 agent 上限约束。
  • 未验证 / 超出范围: P4(/workflows UI + extractAndStripMeta + phase-tree 进度)、P5(budget 全局)、P6(JSONL journal resume)、P7(Ultracode session 模式 + keyword 触发)仍是 Feature Request: Port Dynamic Workflows / Ultracode from Claude Code 2.1.160 #4721 的后续。真模型 E2E 跟进如上。
  • 破坏性变更 / 迁移说明: 无。严格增量 —— 快路径(无 P3 opts)跟以前一模一样。WorkflowAgentResultstring 扩到 string | object,但无 schema 路径仍然 string。isWorkflowsEnabled() 开关(默认关闭)不变。

…'worktree'}) (#4721)

Adds the P3 dispatch options to the workflow runtime, completing the
contract qwen-code's workflow tool matches against upstream Claude Code
2.1.168. P1/P2 stubs (workflow-sandbox.ts:508-527) are replaced with
production paths routed through `SubagentManager.createAgentHeadless` so
per-call model overrides go through `buildRuntimeContentGeneratorView`
(provider routing), per-agent MCP servers / hooks get isolated
lifecycles, and worktree-isolated subagents run against a rebound Config.

- agent({agentType: 'X'}) resolves against the declarative-agents
  registry (#4842 + #4996) via findSubagentByName; unresolved names throw
  "agent({agentType}): agent type 'X' not found" verbatim from upstream.
- agent({model: 'qwen3-max'}) is threaded into SubagentConfig.model so
  the runtime view sees it (modelConfigOverrides alone would only swap
  the model name within the existing provider's view).
- Workflow's disallowed-tool floor [SendMessage, ExitPlanMode] is unioned
  with the agentType's own disallowedTools so a permissive agentType
  cannot re-enable them for a workflow subagent.
- agent({isolation: 'worktree'}) provisions a fresh worktree via
  GitWorktreeService.createUserWorktree (slug agent-<7hex>, mirrors
  AgentTool 1849-1963), rebinds cwd/getTargetDir/getFileService/
  getWorkspaceContext on a prototype-chained Config override, and on
  completion auto-removes the worktree if clean or preserves the path +
  branch (appended to the result string) when the subagent left changes.
  Parent-dirty trees are refused with a clear error to avoid silently
  running the subagent against a stale HEAD.
- agent({isolation: 'remote'}) throws "agent({isolation:'remote'}) is
  not available in this build" verbatim (upstream 2.1.168 parity).
- agent({schema: S}) injects a per-call SyntheticOutputTool (existing
  tools/syntheticOutput.ts, AJV-backed) into a fresh per-subagent
  ToolRegistry built via rebuildToolRegistryOnOverride, then watches
  AgentEventEmitter TOOL_CALL/TOOL_RESULT events for `structured_output`
  invocations. A successful call's args are captured as the dispatch
  return value (object, not string); after two failed attempts the
  third failure aborts the dispatch and throws "subagent completed
  without calling StructuredOutput (after 2 in-conversation nudges)"
  verbatim. No agent-core.ts changes — the entire 2-nudge counter
  lives in the dispatch layer so the shared subagent loop is unaffected.

The sandbox's agent() wrapper now revives per-call object returns into
the vm realm (JSON round-trip inside the vm runInContext block), closing
the same T1/T8/T14 host-prototype-escape vector that P2's per-element
revival closed for parallel/pipeline. Two new sandbox security tests
(constructor-chain probe + non-JSON-serializable collapse) regress this.

WorkflowAgentResult widens from `string` to `string | object`; the
fast-path (no agentType/model/isolation/schema) is preserved byte-for-byte
to keep P1/P2 zero-overhead.

Tests: 159 workflow-suite tests + 217 adjacent (subagents / syntheticOutput /
agent-override) all green. Real-LLM E2E follow-up planned (mirroring P2's
13/13 qwen3-max validation).

Related #4721 (parent design — multi-phase, not closed by this PR)
Related #4732 (P1 merged) #4947 (P2 merged) #4842 #4996 (declarative agents)
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — all required sections present, bilingual, test plan is detailed.

On direction: This is Phase 3 of the Dynamic Workflows port (P1 #4732, P2 #4947 already merged), completing the agent() dispatch contract to match upstream Claude Code 2.1.168. The upstream CHANGELOG confirms this is real — isolation: "worktree" bugfixes, /workflows UI, workflow keyword triggers, and per-agent attribution headers are all documented upstream features. The isWorkflowsEnabled() gate stays off by default, so this ships zero user-visible risk until the feature flag is flipped. Clearly aligned.

On approach: The scope is large (+1262/-81, 7 files) but well-justified — four distinct capabilities (schema, agentType, model, isolation) each need their own wiring through the dispatch, sandbox, and tool layers. The code reuses existing infrastructure (SubagentManager, GitWorktreeService, SyntheticOutputTool, rebuildToolRegistryOnOverride) rather than reinventing. The fast path (no P3 opts) is preserved byte-for-byte — zero overhead for the common case. The runOverridePath function is ~600 LOC but decomposed into clear helpers with single responsibilities. Nothing here screams "cut this."

The any casts in createWorktreeConfigOverride and createSchemaConfigOverride are the standard tradeoff for prototype-chain Config manipulation — the codebase already uses this pattern in agent.ts. Noted, not blocking.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓ — 所有必填段落齐全,双语,测试计划详细。

方向: 这是 Dynamic Workflows 移植的 P3(P1 #4732、P2 #4947 已合并),完成 agent() dispatch contract 与 upstream Claude Code 2.1.168 对齐。上游 CHANGELOG 确认了这些都是真实功能 —— isolation: "worktree" bugfix、/workflows UI、workflow keyword 触发、per-agent attribution headers 都有记录。isWorkflowsEnabled() 门控保持默认关闭,feature flag 打开之前零用户可见风险。方向明确对齐。

方案: 范围较大(+1262/-81,7 个文件)但有充分理由 —— 四个不同能力(schemaagentTypemodelisolation)各自需要在 dispatch、sandbox、tool 层接通。代码复用现有基础设施(SubagentManagerGitWorktreeServiceSyntheticOutputToolrebuildToolRegistryOnOverride)而非重造。快路径(无 P3 opts)保持不变 —— 常见场景零额外开销。runOverridePath 约 600 行但分解为职责清晰的 helper。没有需要砍的部分。

createWorktreeConfigOverridecreateSchemaConfigOverrideany cast 是 prototype-chain Config 操作的标准取舍 —— 代码库中 agent.ts 已经使用同样模式。注意到但不阻塞。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal (before reading diff): To add schema, agentType, model, and isolation to agent(), I'd expect: (1) remove the P1 "not supported" throws in the sandbox, (2) route overrides through SubagentManager.createAgentHeadless for lifecycle management, (3) add a SyntheticOutputTool per-call for schema mode with event-driven capture, (4) provision git worktrees via GitWorktreeService for isolation, (5) JSON-revive object returns across the vm boundary for security, (6) union the disallowed-tool floor before normalization.

Comparison with the diff: The PR matches this proposal point-for-point and exceeds it in several ways — the double-finally for dispose + worktree cleanup, the child AbortController for schema-mode early termination, the parent-dirty refuse for worktree isolation, and the per-call SchemaModeState isolation for concurrent parallel()/pipeline() use. No simpler path missed.

No critical blockers found. Specific observations:

  • The fast-path guard (agentType === undefined && model === undefined && isolation === undefined && schema === undefined) is correct and preserves P1/P2 byte-for-byte. ✓
  • runOverridePath decomposes cleanly: provisionWorkflowWorktreecreateWorktreeConfigOverridecreateSchemaConfigOverridecreateSchemaEventEmitter. Each helper has a single responsibility. ✓
  • Security: per-call JSON.parse(JSON.stringify(value)) revival in the sandbox closes the T1/T8/T14 host-prototype-escape for object returns. Two regression tests verify constructor-chain isolation and non-serializable collapse to null. ✓
  • The WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA prompt correctly replaces the agentType's stock prompt to prevent "plain text vs structured_output" thrash. ✓
  • Worktree cleanup runs in both the success path and an outer fallback finally — no orphaned worktrees on thrown paths. ✓
  • Disallowed-tool floor is unioned via Set before convertToRuntimeConfig, so transformToToolNames normalizes the merged set. No path for a permissive agentType to re-enable workflow-forbidden tools. ✓

Test Results

Workflow suite (4 files, 160 tests)

 ✓ src/tools/workflow/workflow.test.ts (17 tests) 73ms
 ✓ src/agents/runtime/workflow-orchestrator.test.ts (49 tests) 379ms
 ✓ src/utils/concurrencyLimiter.test.ts (8 tests) 45ms
 ✓ src/agents/runtime/workflow-sandbox.test.ts (86 tests) 30456ms

 Test Files  4 passed (4)
      Tests  160 passed (160)
   Duration  33.58s

Adjacent regression (10 files, 217 tests)

 ✓ src/subagents/validation.test.ts (41 tests) 28ms
 ✓ src/subagents/subagent-manager-override.test.ts (6 tests) 21ms
 ✓ src/tools/agent/agent-override.test.ts (8 tests) 109ms
 ✓ src/subagents/subagent-manager.test.ts (110 tests) 210ms
 ✓ src/subagents/agent-frontmatter-schema.test.ts (26 tests) 14ms
 ✓ src/subagents/builtin-agents.test.ts (8 tests) 11ms
 ✓ src/tools/syntheticOutput.test.ts (7 tests) 61ms
 ✓ src/subagents/types.test.ts (3 tests) 6ms
 ✓ src/config/config.workflow-registration.test.ts (4 tests) 38ms
 ✓ src/config/config.workflows.test.ts (4 tests) 9ms

 Test Files  10 passed (10)
      Tests  217 passed (217)
   Duration  10.33s

Typecheck & Lint

tsc --noEmit: clean (0 errors)
eslint (4 changed source files): clean (0 warnings)

Real-scenario tmux testing

Not applicable. The Workflow tool is gated behind isWorkflowsEnabled() which defaults to false. The tool is not registered in the CLI when the feature is off, so no tmux scenario can exercise this code path through the installed qwen binary or npm run dev. The 377 unit tests above provide comprehensive coverage of every dispatch path, security boundary, and error contract.

The PR author explicitly noted real-LLM E2E as a follow-up commit, and the remaining risk is model-behavior shape ("does the model actually call structured_output?") — which is orthogonal to code correctness.

中文说明

代码审查

独立方案(读 diff 前):agent() 添加 schemaagentTypemodelisolation,预期:(1) 移除 sandbox 中 P1 "not supported" 的 throw,(2) 经 SubagentManager.createAgentHeadless 路由 override,(3) per-call 添加 SyntheticOutputTool 并用事件驱动捕获,(4) 经 GitWorktreeService 拉 worktree 做隔离,(5) 对象返回跨 vm 边界时 JSON 复活保安全,(6) disallowed-tool floor 在规范化前 union。

与 diff 对比: PR 逐点对齐上述方案,并在以下方面超出预期 —— dispose + worktree 清理的 double-finally、schema 模式提前终止的 child AbortController、worktree 隔离的 parent-dirty 拒绝、并发 parallel()/pipeline() 下 per-call SchemaModeState 隔离。没有发现更简路径。

无关键阻塞问题。 具体观察:

  • 快路径 guard 正确保留 P1/P2 ✓
  • runOverridePath 分解清晰,每个 helper 单一职责 ✓
  • 安全:per-call JSON 复活关闭 T1/T8/T14 逃逸,两个回归测试验证 ✓
  • schema 模式 system prompt 替换 agentType 的默认 prompt,防止 plain text vs structured_output 冲突 ✓
  • worktree 清理走双路径(success + outer finally),无遗留 ✓
  • disallowed-tool floor 经 Set union + transformToToolNames 规范化,permissive agentType 无法绕过 ✓

测试结果

  • Workflow 套件:4 文件,160 测试全绿 ✓
  • 相邻回归:10 文件,217 测试全绿 ✓
  • tsc --noEmit:0 错误 ✓
  • eslint(4 个变更源文件):0 警告 ✓

tmux 真实场景测试

不适用。 Workflow tool 受 isWorkflowsEnabled() 门控(默认 false),feature 关闭时 tool 不注册,CLI 无法触发此代码路径。377 个单元测试已穷举所有 dispatch 路径、安全边界和错误合约。

PR 作者明确将真模型 E2E 作为后续 commit。剩余风险是模型行为("模型是否会调 structured_output?")—— 与代码正确性正交。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Reflection

This is the third phase of a well-sequenced port. P1 laid the dispatch foundation, P2 added parallel/pipeline, and P3 completes the per-call agent() contract. Each phase has been gated behind isWorkflowsEnabled() (default off), so the codebase has been accumulating the infrastructure without any user-visible surface — and this PR continues that discipline.

Going back to my independent proposal from Stage 2a: the PR matches it point-for-point and exceeds it in the areas that matter — the double-finally for dispose + worktree cleanup, the child AbortController for schema early termination, and the per-call SchemaModeState that keeps concurrent parallel()/pipeline() calls isolated. I didn't find a simpler path it missed.

The code reads like someone who understands the upstream codebase deeply and is porting with intent, not blindly copying. The error messages are verbatim from upstream for script compatibility. The security thinking (JSON revival for the vm-realm boundary, constructor-chain regression tests) is the kind of detail that separates a working implementation from a shippable one.

377 tests, typecheck clean, lint clean. The only gap is real-LLM E2E, which the author explicitly committed to as a follow-up — and the remaining risk there is model behavior shape, not code correctness.

Approving. ✅

中文说明

反思

这是一个节奏良好的移植的第三阶段。P1 奠定 dispatch 基础,P2 加入 parallel/pipeline,P3 完成 per-call agent() contract。每个阶段都在 isWorkflowsEnabled()(默认关闭)门控下,代码库一直在积累基础设施而不暴露用户可见面 —— 本 PR 延续了这一纪律。

回顾 Stage 2a 的独立方案:PR 逐点对齐并在关键领域超出 —— dispose + worktree 清理的 double-finally、schema 提前终止的 child AbortController、per-call SchemaModeState 保持并发 parallel()/pipeline() 隔离。没有发现更简路径。

代码读起来像是对上游代码库有深入理解、带着意图做移植,而非盲目复制。错误消息与上游字面值一致以保证脚本兼容性。安全思考(vm 域边界的 JSON 复活、constructor-chain 回归测试)是区分"能跑"和"能上线"的细节。

377 测试全绿,typecheck 干净,lint 干净。唯一缺口是真模型 E2E,作者已明确承诺跟进 —— 剩余风险在模型行为,不在代码正确性。

通过 ✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 76.28% 76.28% 80.02% 79.66%
Core 81.57% 81.57% 83.57% 83.88%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   76.28 |    79.66 |   80.02 |   76.28 |                   
 src               |   70.11 |    66.99 |   73.33 |   70.11 |                   
  gemini.tsx       |   61.89 |    64.28 |   71.42 |   61.89 | ...1192-1195,1207 
  ...ractiveCli.ts |   69.62 |    63.35 |   66.66 |   69.62 | ...1545-1547,1582 
  ...liCommands.ts |   84.54 |    76.38 |     100 |   84.54 | ...26-335,361,475 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   56.44 |    61.18 |   82.75 |   56.44 |                   
  acpAgent.ts      |    56.3 |    61.21 |   82.94 |    56.3 | ...7012,7037-7052 
  authMethods.ts   |      92 |       60 |     100 |      92 | 33-34             
  errorCodes.ts    |       0 |        0 |       0 |       0 | 1-22              
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |   68.65 |    83.33 |   66.66 |   68.65 |                   
  filesystem.ts    |   68.65 |    83.33 |   66.66 |   68.65 | ...32,77-94,97-98 
 ...ration/session |   80.42 |       74 |   87.73 |   80.42 |                   
  ...ryReplayer.ts |   67.34 |     75.6 |   81.81 |   67.34 | ...54-269,282-283 
  Session.ts       |   80.14 |    73.18 |   89.04 |   80.14 | ...3893,3919-3923 
  ...entTracker.ts |   90.75 |    84.37 |   88.88 |   90.75 | ...30,194,246-255 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   84.21 |    78.57 |     100 |   84.21 | ...37-153,209-211 
  tasksSnapshot.ts |   94.06 |    86.66 |     100 |   94.06 | 60-66             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ssion/emitters |   95.43 |    92.05 |   96.66 |   95.43 |                   
  BaseEmitter.ts   |   84.61 |       70 |     100 |   84.61 | 23-24,39-40       
  ...ageEmitter.ts |   94.07 |    91.42 |     100 |   94.07 | 47-54             
  PlanEmitter.ts   |     100 |      100 |     100 |     100 |                   
  ...allEmitter.ts |   98.37 |    93.82 |     100 |   98.37 | 307-308,398,406   
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
 ...ession/rewrite |    91.3 |    88.09 |   94.44 |    91.3 |                   
  LlmRewriter.ts   |      81 |       84 |     100 |      81 | ...,88-89,155-159 
  ...Middleware.ts |   96.74 |    86.84 |     100 |   96.74 | 135,143-145       
  TurnBuffer.ts    |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/commands      |   59.95 |    86.66 |   47.82 |   59.95 |                   
  auth.ts          |     100 |    83.33 |     100 |     100 | 11,14             
  channel.ts       |   56.66 |      100 |       0 |   56.66 | 15-19,27-34       
  extensions.tsx   |   96.55 |      100 |      50 |   96.55 | 37                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   95.45 |      100 |      50 |   95.45 | 31                
  review.ts        |   51.85 |      100 |       0 |   51.85 | 24-35,38          
  serve.ts         |    47.5 |      100 |   33.33 |    47.5 | 29-31,244-432     
 ...mmands/channel |    39.2 |    79.45 |      50 |    39.2 |                   
  ...l-registry.ts |    8.33 |      100 |       0 |    8.33 | 6-22,25-43        
  config-utils.ts  |      92 |      100 |   66.66 |      92 | 21-26             
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  pairing.ts       |   26.31 |      100 |       0 |   26.31 | ...30,40-50,52-65 
  pidfile.ts       |   96.34 |    86.95 |     100 |   96.34 | 49,59,91          
  start.ts         |   30.98 |       52 |   69.23 |   30.98 | ...72-475,484-486 
  status.ts        |   17.85 |      100 |       0 |   17.85 | 15-26,32-76       
  stop.ts          |      20 |      100 |       0 |      20 | 14-48             
 ...nds/extensions |   85.44 |    89.39 |   81.81 |   85.44 |                   
  consent.ts       |   72.68 |       90 |   42.85 |   72.68 | ...86-142,157-163 
  disable.ts       |     100 |      100 |     100 |     100 |                   
  enable.ts        |     100 |      100 |     100 |     100 |                   
  install.ts       |    75.6 |    66.66 |   66.66 |    75.6 | ...39-142,145-153 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |      100 |     100 |     100 |                   
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  uninstall.ts     |    37.5 |      100 |   33.33 |    37.5 | 23-45,57-64,67-70 
  update.ts        |   96.32 |      100 |     100 |   96.32 | 101-105           
  utils.ts         |   67.77 |    38.88 |     100 |   67.77 | ...,94-98,100-104 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 ...amples/starter |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-64              
 src/commands/mcp  |   90.28 |    88.88 |   83.33 |   90.28 |                   
  add.ts           |     100 |    98.03 |     100 |     100 | 293               
  approve.ts       |   76.19 |     87.5 |   66.66 |   76.19 | ...,89-99,114-124 
  list.ts          |   92.48 |    86.66 |      80 |   92.48 | ...60-162,178-179 
  reconnect.ts     |   77.71 |    78.57 |   85.71 |   77.71 | 40-53,160-182     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   11.57 |      100 |       0 |   11.57 |                   
  cleanup.ts       |   17.94 |      100 |       0 |   17.94 | ...01-106,108-109 
  deterministic.ts |   13.75 |      100 |       0 |   13.75 | ...22-738,740-741 
  fetch-pr.ts      |   11.36 |      100 |       0 |   11.36 | ...80-201,203-204 
  load-rules.ts    |   11.32 |      100 |       0 |   11.32 | ...41-153,155-156 
  pr-context.ts    |    6.22 |      100 |       0 |    6.22 | ...97-312,314-315 
  presubmit.ts     |    9.35 |      100 |       0 |    9.35 | ...62-287,289-290 
 ...nds/review/lib |      30 |      100 |       0 |      30 |                   
  gh.ts            |   22.58 |      100 |       0 |   22.58 | ...49,53-54,62-69 
  git.ts           |   22.72 |      100 |       0 |   22.72 | 15-18,29-39,43-44 
  paths.ts         |   52.94 |      100 |       0 |   52.94 | ...26,37-38,42-43 
 src/config        |   91.59 |       86 |   90.17 |   91.59 |                   
  auth.ts          |   86.74 |    80.88 |     100 |   86.74 | ...40-241,257-258 
  config.ts        |   87.15 |    84.65 |   82.14 |   87.15 | ...2032,2034-2042 
  keyBindings.ts   |   96.87 |       50 |     100 |   96.87 | 201-204           
  ...ngsAdapter.ts |     100 |    94.11 |     100 |     100 | 64                
  mcpApprovals.ts  |   96.12 |    94.87 |     100 |   96.12 | 193-194,199-201   
  mcpJson.ts       |     100 |      100 |     100 |     100 |                   
  mcpServers.ts    |   92.85 |     87.5 |     100 |   92.85 | 46-47             
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  ...abledTools.ts |     100 |      100 |     100 |     100 |                   
  sandboxConfig.ts |   61.64 |    71.87 |   66.66 |   61.64 | ...54-68,73,77-89 
  settings.ts      |   79.18 |    86.71 |    87.8 |   79.18 | ...1550,1565-1568 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   96.22 |    94.33 |     100 |   96.22 | ...95-197,212-213 
 ...nfig/migration |   94.89 |    78.94 |   83.33 |   94.89 |                   
  index.ts         |   94.87 |    88.88 |     100 |   94.87 | 91-92             
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   94.74 |    96.06 |     100 |   94.74 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |    90.56 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
  v3-to-v4.ts      |     100 |      100 |     100 |     100 |                   
 src/core          |     100 |      100 |     100 |     100 |                   
  auth.ts          |     100 |      100 |     100 |     100 |                   
  initializer.ts   |     100 |      100 |     100 |     100 |                   
  theme.ts         |     100 |      100 |     100 |     100 |                   
 src/dualOutput    |   63.09 |    64.51 |   55.55 |   63.09 |                   
  ...tputBridge.ts |   62.94 |    65.51 |   56.25 |   62.94 | ...22-323,331-334 
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/export        |       0 |        0 |       0 |       0 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-7               
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/i18n          |   82.47 |    75.94 |   65.71 |   82.47 |                   
  index.ts         |   63.68 |    69.56 |   53.84 |   63.68 | ...70-271,281-286 
  languages.ts     |   96.92 |    86.66 |     100 |   96.92 | 134-135,167,184   
  ...nslateKeys.ts |     100 |      100 |     100 |     100 |                   
  ...lationDict.ts |   93.33 |    66.66 |     100 |   93.33 | 15                
 src/i18n/locales  |     100 |      100 |     100 |     100 |                   
  ca.js            |     100 |      100 |     100 |     100 |                   
  de.js            |     100 |      100 |     100 |     100 |                   
  en.js            |     100 |      100 |     100 |     100 |                   
  fr.js            |     100 |      100 |     100 |     100 |                   
  ja.js            |     100 |      100 |     100 |     100 |                   
  pt.js            |     100 |      100 |     100 |     100 |                   
  ru.js            |     100 |      100 |     100 |     100 |                   
  zh-TW.js         |     100 |      100 |     100 |     100 |                   
  zh.js            |     100 |      100 |     100 |     100 |                   
 ...nonInteractive |   72.57 |    71.12 |   74.07 |   72.57 |                   
  session.ts       |   76.64 |     69.4 |   85.71 |   76.64 | ...23-824,833-843 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...90-591,594-595 
 ...active/control |   76.29 |    88.23 |      80 |   76.29 |                   
  ...rolContext.ts |    6.89 |        0 |       0 |    6.89 | 50-86             
  ...Dispatcher.ts |   91.66 |    91.83 |   88.88 |   91.66 | ...49-367,383,386 
  ...rolService.ts |     7.4 |        0 |       0 |     7.4 | 46-185            
 ...ol/controllers |    25.4 |    35.71 |   35.48 |    25.4 |                   
  ...Controller.ts |   36.97 |       80 |      80 |   36.97 | ...15-117,127-210 
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |   28.33 |    34.48 |      40 |   28.33 | ...64-573,588-593 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |   21.97 |    28.57 |   27.27 |   21.97 | ...39-451,460-489 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   98.01 |    93.77 |   95.23 |   98.01 |                   
  ...putAdapter.ts |   97.89 |    92.82 |   98.07 |   97.89 | ...1303,1398-1399 
  ...putAdapter.ts |      96 |     90.9 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.38 |      100 |   90.47 |   98.38 | 83-84,124-125     
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/remoteInput   |   86.98 |       75 |   85.71 |   86.98 |                   
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  ...putWatcher.ts |   88.12 |    76.08 |   91.66 |   88.12 | ...21-222,233-236 
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/serve         |   78.22 |    81.87 |      78 |   78.22 |                   
  ...sionBridge.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |   93.26 |    92.64 |     100 |   93.26 | ...07-308,311-313 
  ...temAdapter.ts |     100 |      100 |     100 |     100 |                   
  capabilities.ts  |     100 |    95.45 |     100 |     100 | 341               
  daemonLogger.ts  |   98.63 |    90.47 |   95.83 |   98.63 | 161,165           
  ...usProvider.ts |   67.01 |    51.42 |     100 |   67.01 | ...40-245,278-286 
  debugMode.ts     |     100 |      100 |     100 |     100 |                   
  demo.ts          |     100 |      100 |     100 |     100 |                   
  envSnapshot.ts   |   92.75 |       84 |     100 |   92.75 | 110-113,179-186   
  eventBus.ts      |     100 |      100 |     100 |     100 |                   
  ...oryChannel.ts |       0 |        0 |       0 |       0 | 1-14              
  index.ts         |       0 |        0 |       0 |       0 | 1-143             
  loopbackBinds.ts |     100 |      100 |     100 |     100 |                   
  ...ssionAudit.ts |     100 |      100 |   93.33 |     100 |                   
  rateLimit.ts     |   90.37 |    87.77 |   93.75 |   90.37 | ...95-297,348-352 
  runQwenServe.ts  |   67.85 |    83.55 |   30.61 |   67.85 | ...1356,1359-1366 
  server.ts        |   79.91 |    83.11 |   83.09 |   79.91 | ...4546,4612-4621 
  status.ts        |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...paceAgents.ts |   62.47 |    70.34 |   90.47 |   62.47 | ...1346,1356-1366 
  ...paceMemory.ts |   87.13 |    78.46 |     100 |   87.13 | ...54-361,421-428 
 src/serve/acpHttp |   66.78 |    68.47 |   93.25 |   66.78 |                   
  ...onRegistry.ts |    86.3 |    77.19 |   92.59 |    86.3 | ...34-338,409-423 
  dispatch.ts      |   57.73 |    61.05 |     100 |   57.73 | ...2279,2341-2356 
  index.ts         |   75.63 |    68.21 |    90.9 |   75.63 | ...31,734,760-762 
  jsonRpc.ts       |     100 |    96.96 |     100 |     100 | 92                
  sseStream.ts     |   93.85 |    87.87 |   84.61 |   93.85 | ...48-150,152-154 
  ...portStream.ts |       0 |        0 |       0 |       0 | 1                 
  wsStream.ts      |   91.76 |       80 |     100 |   91.76 | 43,48,91,95-98    
 src/serve/auth    |   86.86 |    79.18 |   93.87 |   86.86 |                   
  deviceFlow.ts    |   96.35 |       80 |   97.61 |   96.35 | ...1358,1453,1519 
  ...owProvider.ts |   44.24 |    74.07 |   71.42 |   44.24 | ...23-284,297,301 
 src/serve/fs      |   85.12 |    81.01 |     100 |   85.12 |                   
  audit.ts         |     100 |    96.15 |     100 |     100 | 201               
  errors.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  paths.ts         |   77.82 |    77.08 |     100 |   77.82 | ...64,493-497,510 
  policy.ts        |   90.32 |    89.18 |     100 |   90.32 | 142-150           
  ...FileSystem.ts |   84.03 |    78.55 |     100 |   84.03 | ...2031,2058-2059 
 src/serve/routes  |   75.89 |    76.51 |   94.28 |   75.89 |                   
  a2uiAction.ts    |     100 |    93.65 |     100 |     100 | 114-118,163,267   
  ...ceFileRead.ts |   94.41 |    76.92 |     100 |   94.41 | ...28-329,390-392 
  ...eFileWrite.ts |    82.1 |    60.52 |     100 |    82.1 | ...42-244,247-249 
  ...ceSettings.ts |   23.62 |      100 |      50 |   23.62 | ...10-223,230-327 
 ...kspace-service |   81.05 |     82.4 |   86.66 |   81.05 |                   
  index.ts         |   81.23 |    83.17 |   92.85 |   81.23 | ...92-497,557-622 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/services      |   91.93 |    90.88 |   97.56 |   91.93 |                   
  ...mandLoader.ts |     100 |    93.75 |     100 |     100 | 95                
  ...killLoader.ts |     100 |    93.33 |     100 |     100 | 48,67             
  ...andService.ts |   98.73 |      100 |     100 |   98.73 | 107               
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   75.84 |    80.64 |   83.33 |   75.84 | ...10-211,277-278 
  ...mandLoader.ts |     100 |    97.14 |     100 |     100 | 66                
  ...nd-factory.ts |   91.42 |    91.66 |     100 |   91.42 | 128,137-144       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...ndMetadata.ts |   98.21 |    96.66 |     100 |   98.21 | 83,87             
  commandUtils.ts  |      96 |     90.9 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |    88.3 |    85.49 |   92.59 |    88.3 |                   
  DataProcessor.ts |   88.22 |    85.48 |      95 |   88.22 | ...1341,1345-1352 
  ...tGenerator.ts |   98.21 |    85.71 |     100 |   98.21 | 46                
  ...teRenderer.ts |   45.45 |      100 |       0 |   45.45 | 13-51             
 .../insight/types |       0 |       50 |      50 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.27 |    94.04 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |    84.21 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.65 |     100 |   97.41 | 95-98             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services/tips |   97.35 |    84.84 |     100 |   97.35 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.59 |       70 |     100 |   92.59 | ...24,146,153,162 
  tipRegistry.ts   |     100 |      100 |     100 |     100 |                   
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/startup       |   66.82 |    78.94 |   66.66 |   66.82 |                   
  ...reeStartup.ts |   66.82 |    78.94 |   66.66 |   66.82 | ...08-312,363-426 
 src/test-utils    |   93.71 |    83.33 |      80 |   93.71 |                   
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   65.17 |    73.21 |   59.67 |   65.17 |                   
  App.tsx          |   33.33 |       75 |   33.33 |   33.33 | 32-86             
  AppContainer.tsx |   64.17 |    65.12 |      50 |   64.17 | ...3238,3242-3246 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |   29.23 |      100 |       0 |   29.23 | 25-75             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |      60 |      100 |   35.29 |      60 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.91 |    97.14 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...inePresets.ts |   98.28 |    89.87 |     100 |   98.28 | ...34,261,420-422 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   59.16 |    65.94 |   51.11 |   59.16 |                   
  AuthDialog.tsx   |   62.87 |     42.1 |   18.18 |   62.87 | ...03,310-332,336 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |   60.03 |    70.37 |      56 |   60.03 | ...87,791,800,803 
  useAuth.ts       |   94.55 |    73.52 |     100 |   94.55 | ...19-220,239-245 
  ...rSetupFlow.ts |   43.52 |    33.33 |      50 |   43.52 | ...72-393,410-453 
 src/ui/commands   |   77.77 |     81.5 |   86.39 |   77.77 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |   89.47 |    81.25 |     100 |   89.47 | 92-93,95-100      
  arenaCommand.ts  |   62.81 |    58.73 |   65.21 |   62.81 | ...90-595,680-688 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   94.32 |    77.41 |     100 |   94.32 | 35-36,114-119     
  bugCommand.ts    |     100 |    77.77 |     100 |     100 | 27,61             
  cdCommand.ts     |   89.44 |    80.35 |     100 |   89.44 | ...81,106-111,190 
  clearCommand.ts  |   79.64 |       68 |     100 |   79.64 | ...24-125,133-142 
  ...essCommand.ts |   67.95 |    55.88 |      75 |   67.95 | ...86-187,201-204 
  ...astCommand.ts |   70.86 |    74.07 |      75 |   70.86 | ...,61-93,117-122 
  ...extCommand.ts |   65.35 |     66.1 |   84.61 |   65.35 | ...42-575,586-587 
  copyCommand.ts   |   98.48 |    95.78 |     100 |   98.48 | ...80,280,321,327 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |     100 |     87.5 |     100 |     100 | ...61,224-225,238 
  ...ryCommand.tsx |   81.84 |    86.11 |   91.66 |   81.84 | ...66-271,318-325 
  docsCommand.ts   |     100 |     90.9 |     100 |     100 | 25                
  doctorCommand.ts |   61.27 |    87.06 |    87.5 |   61.27 | ...71-372,445-665 
  dreamCommand.ts  |   85.45 |    88.88 |     100 |   85.45 | 58-65             
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   98.25 |    91.02 |     100 |   98.25 | ...81,198-199,364 
  ...onsCommand.ts |   51.54 |    48.14 |   69.23 |   51.54 | ...97,251-303,364 
  forgetCommand.ts |     100 |       90 |     100 |     100 | 59                
  forkCommand.ts   |     100 |    94.11 |     100 |     100 | 92,141            
  goalCommand.ts   |   91.41 |    84.44 |      90 |   91.41 | ...87-190,202-205 
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   81.13 |    65.71 |   85.71 |   81.13 | ...,86-93,131-132 
  ideCommand.ts    |   60.75 |    64.28 |   41.17 |   60.75 | ...05-306,310-324 
  initCommand.ts   |   84.33 |    72.72 |     100 |   84.33 | 68,82-87,89-94    
  ...ghtCommand.ts |   77.87 |    71.42 |     100 |   77.87 | ...44-245,250-272 
  ...ageCommand.ts |   92.17 |    82.69 |     100 |   92.17 | ...39,159,168-178 
  lspCommand.ts    |     100 |    86.95 |     100 |     100 | 31,101-102        
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   75.09 |    78.18 |      75 |   75.09 | ...20-225,262-267 
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  planCommand.ts   |   78.82 |    76.92 |     100 |   78.82 | 30-35,51-56,68-73 
  quitCommand.ts   |     100 |      100 |     100 |     100 |                   
  recapCommand.ts  |   21.81 |      100 |      50 |   21.81 | 24-73             
  ...berCommand.ts |      96 |       70 |     100 |      96 | 57,62             
  renameCommand.ts |   85.71 |    86.04 |     100 |   85.71 | ...02-209,216-221 
  ...oreCommand.ts |   90.47 |    84.61 |     100 |   90.47 | ...32-137,167-168 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |   81.25 |      100 |      50 |   81.25 | 20-22             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   81.43 |    65.21 |      80 |   81.43 | ...70-173,176-179 
  skillsCommand.ts |    85.5 |    81.25 |     100 |    85.5 | 36-44,70          
  statsCommand.ts  |   91.48 |    89.47 |     100 |   91.48 | 40-43,134-141     
  ...ineCommand.ts |     100 |      100 |     100 |     100 |                   
  ...aryCommand.ts |    6.46 |      100 |      50 |    6.46 | 31-329            
  tasksCommand.ts  |   77.22 |    72.13 |     100 |   77.22 | ...46-150,172-177 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |     100 |      100 |     100 |     100 |                   
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
 src/ui/components |   62.29 |     77.4 |   60.51 |   62.29 |                   
  AboutBox.tsx     |     100 |      100 |     100 |     100 |                   
  AnsiOutput.tsx   |   65.57 |      100 |      50 |   65.57 | 69-90             
  ApiKeyInput.tsx  |       0 |        0 |       0 |       0 | 1-97              
  AppHeader.tsx    |    88.7 |       75 |     100 |    88.7 | 36,38-43,45       
  ...odeDialog.tsx |   87.24 |    72.22 |   33.33 |   87.24 | ...85,233-238,245 
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   16.27 |      100 |       0 |   16.27 | 19-58             
  ...TextInput.tsx |   77.01 |       76 |     100 |   77.01 | ...20,234-236,263 
  Composer.tsx     |    81.6 |     64.7 |     100 |    81.6 | ...90,108,160,173 
  ...entPrompt.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |   75.89 |    62.06 |     100 |   75.89 | ...,88,93-108,113 
  ...geDisplay.tsx |   68.42 |    57.14 |     100 |   68.42 | 16-17,31-32,42-50 
  ...ification.tsx |   28.57 |      100 |       0 |   28.57 | 16-36             
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |   11.86 |      100 |       0 |   11.86 | 69-550            
  DiffDialog.tsx   |    2.47 |      100 |       0 |    2.47 | 68-732            
  ...ngsDialog.tsx |    8.44 |      100 |       0 |    8.44 | 37-195            
  ExitWarning.tsx  |     100 |      100 |     100 |     100 |                   
  ...hProgress.tsx |    87.8 |    33.33 |     100 |    87.8 | 28-31,56          
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   77.12 |    52.27 |     100 |   77.12 | ...43,167,188-193 
  ...ngSpinner.tsx |   68.42 |       80 |      50 |   68.42 | 35-52,73,80-81    
  GoalPill.tsx     |   76.19 |    81.81 |     100 |   76.19 | 24-30,46-50       
  Header.tsx       |   98.62 |    94.28 |     100 |   98.62 | 162,164           
  Help.tsx         |   98.32 |       90 |     100 |   98.32 | ...24,381,447-448 
  ...emDisplay.tsx |   65.77 |    55.55 |     100 |   65.77 | ...71,374,377-383 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   82.22 |     78.8 |   83.33 |   82.22 | ...1628,1643,1693 
  ...Shortcuts.tsx |   20.87 |      100 |       0 |   20.87 | ...6,49-51,67-125 
  ...Indicator.tsx |     100 |    91.42 |     100 |     100 | 65,74             
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |   87.11 |    88.31 |   66.66 |   87.11 | ...26,284,343-347 
  MemoryDialog.tsx |   61.87 |    76.05 |    62.5 |   61.87 | ...72,391,428-430 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   85.19 |    69.17 |     100 |   85.19 | ...80-596,653-657 
  ...tsDisplay.tsx |     100 |    97.22 |     100 |     100 | 270               
  ...fications.tsx |   18.18 |      100 |       0 |   18.18 | 15-58             
  ...onsDialog.tsx |    2.13 |      100 |       0 |    2.13 | 62-133,148-1004   
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   92.64 |    85.71 |     100 |   92.64 | 102-106,134-139   
  PrepareLabel.tsx |   91.66 |    77.27 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |    8.57 |      100 |       0 |    8.57 | 24-55,58-134      
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |   21.42 |      100 |       0 |   21.42 | 13-39             
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...dSelector.tsx |   87.11 |     73.8 |     100 |   87.11 | ...48,354-370,406 
  ...ionPicker.tsx |   83.66 |    72.13 |     100 |   83.66 | ...96,402,444-466 
  ...onPreview.tsx |   92.42 |    84.37 |     100 |   92.42 | ...,70-71,143-145 
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   72.56 |       80 |      40 |   72.56 | ...06-109,114-117 
  ...tedDialog.tsx |     100 |      100 |     100 |     100 |                   
  ...ngsDialog.tsx |   66.31 |    71.16 |      75 |   66.31 | ...16-824,830-831 
  ...ionDialog.tsx |    92.3 |    96.15 |   33.33 |    92.3 | 60-63,68-75,164   
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   57.14 |      100 |       0 |   57.14 | 12-15             
  ...MoreLines.tsx |      28 |      100 |       0 |      28 | 18-40             
  ...ionPicker.tsx |   17.59 |      100 |       0 |   17.59 | 55-172            
  ...tivityTab.tsx |    3.94 |      100 |       0 |    3.94 | 27-275            
  StatsDialog.tsx  |    8.85 |      100 |       0 |    8.85 | ...5,49-84,92-238 
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ciencyTab.tsx |    3.28 |      100 |       0 |    3.28 | 25-258            
  ...atmapView.tsx |    8.98 |      100 |       0 |    8.98 | 20-107            
  ...essionTab.tsx |    5.46 |      100 |       0 |    5.46 | 24-215            
  ...ineDialog.tsx |    93.5 |    85.18 |     100 |    93.5 | ...05,267,287-289 
  ...yTodoList.tsx |   96.33 |    88.23 |     100 |   96.33 | 137-140           
  ...nsDisplay.tsx |   87.25 |       64 |     100 |   87.25 | ...57-159,166-168 
  ThemeDialog.tsx  |   89.95 |    46.15 |      75 |   89.95 | ...71-173,243-245 
  Tips.tsx         |   93.54 |       75 |     100 |   93.54 | 39-40             
  TodoDisplay.tsx  |     100 |      100 |     100 |     100 |                   
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 31-32             
  TrustDialog.tsx  |     100 |    81.81 |     100 |     100 | 71-86             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...ackDialog.tsx |    7.84 |      100 |       0 |    7.84 | 24-134            
  ...xitDialog.tsx |   80.36 |    43.47 |      60 |   80.36 | ...24-238,248-251 
  ...odeVisuals.ts |   91.42 |    64.28 |     100 |   91.42 | 15,21,24          
  ...s-helpers.tsx |      25 |      100 |       0 |      25 | ...3,86-89,94-102 
 ...nts/agent-view |   38.05 |    78.57 |   36.36 |   38.05 |                   
  ...atContent.tsx |    8.79 |      100 |       0 |    8.79 | 53-265,271-273    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |   10.84 |      100 |       0 |   10.84 | 59-308            
  AgentFooter.tsx  |   17.07 |      100 |       0 |   17.07 | 28-66             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |   87.17 |    61.76 |     100 |   87.17 | ...,85,98-106,124 
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |   45.59 |    70.53 |   60.86 |   45.59 |                   
  ArenaCards.tsx   |   73.06 |    71.79 |   85.71 |   73.06 | ...83-185,321-326 
  ...ectDialog.tsx |   83.48 |    69.86 |   88.88 |   83.48 | ...88-392,409-410 
  ...artDialog.tsx |    9.92 |      100 |       0 |    9.92 | 27-164            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...ackground-view |    79.1 |     83.1 |   88.88 |    79.1 |                   
  ...sksDialog.tsx |   75.94 |    81.14 |   81.81 |   75.94 | ...1243,1305-1307 
  ...TasksPill.tsx |   66.29 |    89.28 |     100 |   66.29 | 56,98-118,126-134 
  ...gentPanel.tsx |    97.4 |    86.31 |     100 |    97.4 | 123,434-438       
 ...nts/extensions |   45.28 |    33.33 |      60 |   45.28 |                   
  ...gerDialog.tsx |   44.31 |    34.14 |      75 |   44.31 | ...71-480,483-488 
  index.ts         |       0 |        0 |       0 |       0 | 1-9               
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   54.88 |    94.33 |   66.66 |   54.88 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |    6.18 |      100 |       0 |    6.18 | 20-131            
  ...nListStep.tsx |   88.43 |    94.87 |      80 |   88.43 | 52-53,59-72,106   
  ...electStep.tsx |   13.46 |      100 |       0 |   13.46 | 20-70             
  ...nfirmStep.tsx |   19.56 |      100 |       0 |   19.56 | 23-65             
  index.ts         |     100 |      100 |     100 |     100 |                   
 ...mponents/hooks |   86.85 |    81.37 |   91.89 |   86.85 |                   
  ...rListBody.tsx |   95.29 |    85.18 |     100 |   95.29 | 95-98             
  ...etailStep.tsx |   75.32 |    71.42 |      60 |   75.32 | ...56-169,173-186 
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entHeader.tsx |     100 |    85.71 |     100 |     100 | 47                
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   72.29 |    70.49 |     100 |   72.29 | ...51,563-568,572 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  ...erGrouping.ts |     100 |      100 |     100 |     100 |                   
  sourceLabels.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   21.66 |    89.36 |   76.92 |   21.66 |                   
  ...ealthPill.tsx |   68.42 |    85.71 |     100 |   68.42 | 40-46             
  ...entDialog.tsx |    3.66 |      100 |       0 |    3.66 | 41-712            
  ...valDialog.tsx |   15.06 |      100 |       0 |   15.06 | 40-109            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-30              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |      97 |     92.1 |     100 |      97 | 24,113-114        
 ...ents/mcp/steps |   26.61 |    54.54 |   42.85 |   26.61 |                   
  ...icateStep.tsx |    5.88 |      100 |       0 |    5.88 | 40-55,58-296      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |    5.15 |      100 |       0 |    5.15 | 31-251            
  ...rListStep.tsx |   75.18 |    59.37 |     100 |   75.18 | ...53-158,169-173 
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |   69.02 |       50 |     100 |   69.02 | ...22,125,134-143 
 ...nents/messages |   82.43 |    79.86 |    75.6 |   82.43 |                   
  ...ionDialog.tsx |   80.84 |     77.6 |    62.5 |   80.84 | ...98,516,534-536 
  BtwMessage.tsx   |     100 |      100 |     100 |     100 |                   
  ...upDisplay.tsx |   97.67 |    83.72 |     100 |   97.67 | 119,142,150       
  ...onMessage.tsx |   91.93 |    82.35 |     100 |   91.93 | 57-59,61,63       
  ...nMessages.tsx |   70.56 |    77.77 |      70 |   70.56 | ...07-320,324-336 
  DiffRenderer.tsx |   93.19 |    86.17 |     100 |   93.19 | ...09,237-238,304 
  ...tsDisplay.tsx |   97.82 |    77.27 |     100 |   97.82 | 87,89             
  ...usMessage.tsx |   76.31 |     42.1 |   66.66 |   76.31 | ...99,101,124,155 
  ...tsDisplay.tsx |    95.1 |    88.05 |     100 |    95.1 | ...29,131,164-169 
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...edMessage.tsx |   16.66 |      100 |       0 |   16.66 | 22-38             
  ...sMessages.tsx |   55.67 |       40 |   28.57 |   55.67 | ...20-125,133-145 
  ...ryMessage.tsx |   14.28 |      100 |       0 |   14.28 | 23-62             
  ...onMessage.tsx |   82.15 |    73.33 |   33.33 |   82.15 | ...65-467,474-476 
  ...upMessage.tsx |   82.63 |    92.85 |     100 |   82.63 | ...85-412,434-449 
  ToolMessage.tsx  |    87.8 |    73.28 |    92.3 |    87.8 | ...59-764,791-793 
 ...ponents/shared |   84.43 |    80.54 |    95.5 |   84.43 |                   
  ...ctionList.tsx |   99.14 |       96 |     100 |   99.14 | 99                
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  MaxSizedBox.tsx  |   83.01 |    86.25 |   88.88 |   83.01 | ...12-513,618-619 
  MultiSelect.tsx  |   93.58 |       75 |     100 |   93.58 | ...43,199-201,211 
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  ...lableList.tsx |   76.25 |       80 |     100 |   76.25 | 44-58,65-68       
  StaticRender.tsx |   72.72 |      100 |     100 |   72.72 | 31-33             
  TextInput.tsx    |    80.8 |    66.07 |      80 |    80.8 | ...36-240,252-258 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  ...lizedList.tsx |   84.26 |    80.88 |      90 |   84.26 | ...68-696,743-765 
  text-buffer.ts   |   85.94 |    81.18 |   97.91 |   85.94 | ...2651,2749-2750 
  ...er-actions.ts |   73.93 |    67.22 |     100 |   73.93 | ...32-733,934-936 
 ...ponents/skills |    3.61 |      100 |       0 |    3.61 |                   
  ...gerDialog.tsx |    3.61 |      100 |       0 |    3.61 | ...90-148,151-694 
 ...ents/subagents |   30.87 |        0 |       0 |   30.87 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |    12.1 |      100 |       0 |    12.1 | 33-190            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   10.95 |      100 |       0 |   10.95 | ...1,56-57,60-102 
 ...bagents/create |    9.13 |      100 |       0 |    9.13 |                   
  ...ionWizard.tsx |    7.28 |      100 |       0 |    7.28 | 34-299            
  ...rSelector.tsx |   14.75 |      100 |       0 |   14.75 | 26-85             
  ...onSummary.tsx |    4.26 |      100 |       0 |    4.26 | 27-331            
  ...tionInput.tsx |    8.63 |      100 |       0 |    8.63 | 23-177            
  ...dSelector.tsx |   33.33 |      100 |       0 |   33.33 | 20-21,26-27,36-63 
  ...nSelector.tsx |    37.5 |      100 |       0 |    37.5 | 20-21,26-27,36-58 
  ...EntryStep.tsx |   12.76 |      100 |       0 |   12.76 | 34-78             
  ToolSelector.tsx |    4.16 |      100 |       0 |    4.16 | 31-253            
 ...bagents/manage |   21.51 |    59.52 |   27.27 |   21.51 |                   
  ...ctionStep.tsx |   10.25 |      100 |       0 |   10.25 | 21-103            
  ...eleteStep.tsx |   20.93 |      100 |       0 |   20.93 | 23-62             
  ...tEditStep.tsx |   25.53 |      100 |       0 |   25.53 | ...2,37-38,51-124 
  ...ctionStep.tsx |   35.42 |    59.52 |     100 |   35.42 | ...20-432,437-439 
  ...iewerStep.tsx |   13.72 |      100 |       0 |   13.72 | 18-73             
  ...gerDialog.tsx |    6.74 |      100 |       0 |    6.74 | 35-341            
 ...mponents/views |   70.21 |    67.32 |    64.7 |   70.21 |                   
  ContextUsage.tsx |   70.88 |    63.88 |      80 |   70.88 | ...20-426,463-557 
  DoctorReport.tsx |     9.8 |      100 |       0 |     9.8 | 25-54,57-131      
  ...sionsList.tsx |   87.69 |    73.68 |     100 |   87.69 | 65-72             
  McpStatus.tsx    |   89.53 |    60.52 |     100 |   89.53 | ...72,175-177,262 
  SkillsList.tsx   |   27.27 |      100 |       0 |   27.27 | 18-35             
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   77.54 |    78.01 |   81.03 |   77.54 |                   
  ...ewContext.tsx |   64.83 |    88.88 |      50 |   64.83 | ...16-219,225-235 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |    93.3 |    64.28 |      50 |    93.3 | ...35-236,263-267 
  ...deContext.tsx |     100 |      100 |     100 |     100 |                   
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   81.67 |     81.6 |     100 |   81.67 | ...1199,1203-1205 
  ...owContext.tsx |   91.07 |    81.81 |     100 |   91.07 | 47-48,60-62       
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   43.26 |     62.5 |    62.5 |   43.26 | ...64-267,276-279 
  ...gsContext.tsx |     100 |      100 |     100 |     100 |                   
  ...usContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
  ...utContext.tsx |   85.71 |      100 |   66.66 |   85.71 | 13-14             
  ...nsContext.tsx |   88.88 |       50 |     100 |   88.88 | 134-135           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 203-204           
  ...deContext.tsx |      80 |     87.5 |      75 |      80 | ...11-112,118-120 
 src/ui/daemon     |   90.76 |    73.73 |   95.45 |   90.76 |                   
  ...TuiAdapter.ts |   90.76 |    73.73 |   95.45 |   90.76 | ...53,771-772,858 
 src/ui/editors    |   93.33 |    85.71 |   66.66 |   93.33 |                   
  ...ngsManager.ts |   93.33 |    85.71 |   66.66 |   93.33 | 49,63-64          
 src/ui/hooks      |   81.63 |    80.83 |      86 |   81.63 |                   
  ...dProcessor.ts |   83.12 |    82.56 |     100 |   83.12 | ...88-389,408-435 
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...dProcessor.ts |    94.8 |    70.58 |     100 |    94.8 | ...76-277,282-283 
  ...dProcessor.ts |   83.94 |    62.56 |      80 |   83.94 | ...1010,1031-1035 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-157            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...dScrollbar.ts |     100 |      100 |     100 |     100 |                   
  ...ationFrame.ts |      32 |       60 |     100 |      32 | 42-44,51-90       
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   27.92 |       80 |      25 |   27.92 | ...69-170,173-175 
  ...Completion.ts |   91.79 |    86.88 |     100 |   91.79 | ...05-206,243-246 
  ...ifications.ts |   86.91 |    96.29 |     100 |   86.91 | 116-130           
  ...tIndicator.ts |   83.49 |    70.96 |     100 |   83.49 | ...60,168,170-178 
  ...waySummary.ts |   96.22 |    69.69 |     100 |   96.22 | 125-127,169       
  ...ndTaskView.ts |    94.3 |    76.08 |     100 |    94.3 | 122-126,213,219   
  ...chedScroll.ts |     100 |      100 |     100 |     100 |                   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   92.53 |    71.42 |     100 |   92.53 | ...32,172,245-248 
  ...ompletion.tsx |   96.01 |    83.87 |     100 |   96.01 | ...22-223,225-226 
  ...dMigration.ts |   90.62 |       75 |     100 |   90.62 | 38-40             
  useCompletion.ts |    92.4 |     87.5 |     100 |    92.4 | 68-69,93-94,98-99 
  ...nitMessage.ts |     100 |      100 |     100 |     100 |                   
  ...extualTips.ts |   77.27 |       50 |     100 |   77.27 | ...2,75-79,93-101 
  ...eteCommand.ts |   78.53 |    88.57 |     100 |   78.53 | ...96-104,112-113 
  ...ialogClose.ts |    12.5 |      100 |     100 |    12.5 | 85-181            
  useDiffData.ts   |   11.62 |      100 |       0 |   11.62 | 44-87             
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |    97.67 |     100 |   99.12 | 182-183           
  ...ionUpdates.ts |   93.45 |     92.3 |     100 |   93.45 | ...83-287,300-306 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |   57.89 |    71.42 |      50 |   57.89 | ...66-168,190-191 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |      100 |     100 |     100 |                   
  ...ggestions.tsx |   89.15 |     62.5 |      50 |   89.15 | ...22-124,149-150 
  ...miniStream.ts |   79.08 |    76.28 |    92.3 |   79.08 | ...2609,2660-2668 
  ...BranchName.ts |    90.9 |     92.3 |     100 |    90.9 | 19-20,55-58       
  ...oryManager.ts |   96.92 |    96.29 |     100 |   96.92 | 52,139-142,218    
  ...ooksDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...stListener.ts |     100 |      100 |     100 |     100 |                   
  ...nAuthError.ts |   76.19 |       50 |     100 |   76.19 | 39-40,43-45       
  ...putHistory.ts |   92.59 |    85.71 |     100 |   92.59 | 63-64,72,94-96    
  ...storyStore.ts |     100 |    94.11 |     100 |     100 | 69                
  useKeypress.ts   |     100 |      100 |     100 |     100 |                   
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...unchEditor.ts |    9.67 |      100 |       0 |    9.67 | 11-32,39-90       
  ...gIndicator.ts |     100 |      100 |     100 |     100 |                   
  useLogger.ts     |   21.05 |      100 |       0 |   21.05 | 15-37             
  useMCPHealth.ts  |   63.15 |       75 |      50 |   63.15 | 42-52,64-67       
  ...cpApproval.ts |   92.37 |    83.33 |     100 |   92.37 | ...00-103,115-116 
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |   83.14 |    78.57 |     100 |   83.14 | 54-63,74-79       
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...delCommand.ts |     100 |       75 |     100 |     100 | 22                
  ...ouseEvents.ts |   87.17 |    88.88 |   66.66 |   87.17 | 81-82,86-88       
  ...raseCycler.ts |   84.74 |    76.47 |     100 |   84.74 | ...49,52-53,69-71 
  ...rredEditor.ts |   58.33 |    22.22 |     100 |   58.33 | 23-27,29-33       
  ...derUpdates.ts |   86.49 |    77.96 |    90.9 |   86.49 | ...26,288-300,348 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |    84.7 |    93.33 |     100 |    84.7 | ...71-276,372-382 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...tleRepaint.ts |     100 |      100 |     100 |     100 |                   
  ...umeCommand.ts |   96.96 |    83.33 |     100 |   96.96 | 101-102,131       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   97.05 |    96.11 |     100 |   97.05 | ...90-191,245-248 
  ...sionPicker.ts |   92.87 |    90.35 |     100 |   92.87 | ...99-501,503-505 
  ...earchInput.ts |     100 |      100 |     100 |     100 |                   
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   91.74 |    79.41 |     100 |   91.74 | ...74,122-123,133 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-73              
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...Completion.ts |   82.73 |    85.41 |   94.73 |   82.73 | ...70-672,680-716 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  ...tatsDialog.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |    96.3 |    92.19 |     100 |    96.3 | ...77-380,466-473 
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...mInProcess.ts |   27.35 |       80 |      25 |   27.35 | ...82-183,186-188 
  ...tification.ts |     100 |     87.5 |     100 |     100 | 50                
  ...alProgress.ts |   53.06 |       50 |   66.66 |   53.06 | ...53,61-68,79-85 
  ...rminalSize.ts |   76.19 |      100 |      50 |   76.19 | 21-25             
  ...emeCommand.ts |   67.01 |    29.41 |     100 |   67.01 | ...10-111,115-116 
  useTimer.ts      |   88.09 |    85.71 |     100 |   88.09 | 44-45,51-53       
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |      100 |     100 |     100 |                   
  useTurnDiffs.ts  |   95.12 |    78.57 |     100 |   95.12 | 133-134,156-157   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  ...reeSession.ts |   93.75 |       70 |     100 |   93.75 | 44-45,87          
  vim.ts           |   74.37 |    67.77 |   69.23 |   74.37 | ...1842-1849,1857 
 src/ui/layouts    |    90.9 |    90.62 |     100 |    90.9 |                   
  ...AppLayout.tsx |   90.72 |       90 |     100 |   90.72 | 57-59,101-106     
  ...AppLayout.tsx |   91.17 |    91.66 |     100 |   91.17 | 70-75             
 src/ui/models     |   80.24 |    79.16 |   71.42 |   80.24 |                   
  ...ableModels.ts |   80.24 |    79.16 |   71.42 |   80.24 | ...,61-71,123-125 
 ...noninteractive |     100 |      100 |   14.28 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |   14.28 |     100 |                   
 src/ui/state      |   94.91 |    81.81 |     100 |   94.91 |                   
  extensions.ts    |   94.91 |    81.81 |     100 |   94.91 | 68-69,88          
 src/ui/themes     |   98.39 |    72.83 |     100 |   98.39 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |   97.91 |       92 |     100 |   97.91 | ...51-352,354-355 
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  ...inal-theme.ts |   88.59 |    85.96 |     100 |   88.59 | ...57-261,266-270 
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  qwen-dark.ts     |     100 |      100 |     100 |     100 |                   
  qwen-light.ts    |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   87.98 |    82.89 |     100 |   87.98 | ...48-357,362-363 
  theme.ts         |     100 |    38.02 |     100 |     100 | ...34-449,457-461 
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   83.33 |    82.79 |   92.77 |   83.33 |                   
  ...Colorizer.tsx |   79.53 |    83.78 |     100 |   79.53 | ...51-152,249-275 
  ...nRenderer.tsx |   68.83 |    70.14 |      50 |   68.83 | ...52-254,274-293 
  ...wnDisplay.tsx |   86.01 |    87.66 |     100 |   86.01 | ...87,704,729-754 
  ...idDiagram.tsx |   87.79 |    95.34 |     100 |   87.79 | 156-179           
  ...eRenderer.tsx |   92.08 |    80.45 |      95 |   92.08 | ...76-679,723-728 
  ...odeDisplay.ts |   96.55 |     90.9 |     100 |   96.55 | 34                
  asciiCharts.ts   |   96.77 |    87.62 |     100 |   96.77 | 173-180,281       
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   49.89 |    71.79 |    90.9 |   49.89 | ...79,582-591,594 
  commandUtils.ts  |    95.9 |    88.42 |     100 |    95.9 | ...66,168-169,293 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  customBanner.ts  |   90.68 |    91.22 |     100 |   90.68 | ...13,324-327,334 
  displayUtils.ts  |   88.37 |    72.22 |     100 |   88.37 | 23,25,29,31,33    
  formatters.ts    |   95.23 |     98.3 |     100 |   95.23 | 117-120           
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |     100 |      100 |     100 |     100 |                   
  ...oryMapping.ts |     100 |    96.55 |     100 |     100 | 43                
  historyUtils.ts  |   94.11 |       94 |     100 |   94.11 | 94-97             
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |    8.23 |      100 |       0 |    8.23 | ...31-132,135-136 
  latexRenderer.ts |   94.95 |     73.8 |     100 |   94.95 | ...76-178,184-187 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  ...ightLoader.ts |     100 |    89.47 |     100 |     100 | 81,110            
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  ...ToolGroups.ts |   98.66 |    96.77 |     100 |   98.66 | 48-49             
  ...geRenderer.ts |   86.23 |    69.06 |   95.12 |   86.23 | ...1284,1324-1330 
  ...alRenderer.ts |   86.69 |     71.9 |     100 |   86.69 | ...1476,1513-1519 
  ...lsBySource.ts |     100 |    95.23 |     100 |     100 | 84                
  mouse.ts         |   90.71 |    73.33 |   88.88 |   90.71 | ...40-143,200-201 
  osc8.ts          |   94.73 |    87.75 |     100 |   94.73 | ...49,434,438-439 
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |   99.02 |    97.14 |     100 |   99.02 | 106               
  ...storyUtils.ts |   62.74 |    71.26 |      90 |   62.74 | ...84,432,437-459 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...ataService.ts |   93.17 |     79.1 |     100 |   93.17 | ...14,227,254-256 
  ...izedOutput.ts |   94.94 |      100 |   88.88 |   94.94 | 112-117           
  ...wOptimizer.ts |     100 |    96.77 |     100 |     100 | 69                
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   97.61 |    94.84 |   92.85 |   97.61 | ...50-251,386-387 
  todoSnapshot.ts  |   89.33 |    93.47 |     100 |   89.33 | ...,66-78,180-181 
  updateCheck.ts   |     100 |    80.95 |     100 |     100 | 30-42             
 ...i/utils/export |      57 |     40.8 |   79.41 |      57 |                   
  collect.ts       |   55.92 |    50.58 |   86.36 |   55.92 | ...25-640,642-647 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   58.11 |    20.51 |      80 |   58.11 | ...13-314,328-363 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |      40 |      100 |       0 |      40 | 11-13             
 ...ort/formatters |    3.38 |      100 |       0 |    3.38 |                   
  html.ts          |    9.61 |      100 |       0 |    9.61 | ...28,34-76,82-84 
  json.ts          |      50 |      100 |       0 |      50 | 14-15             
  jsonl.ts         |     3.5 |      100 |       0 |     3.5 | 14-76             
  markdown.ts      |    0.94 |      100 |       0 |    0.94 | 13-295            
 src/utils         |   72.16 |    89.14 |   90.43 |   72.16 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  apiPreconnect.ts |   96.72 |    97.14 |     100 |   96.72 | 165-168           
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  cleanup.ts       |   84.12 |    93.33 |      80 |   84.12 | 75,106-115        
  commands.ts      |     100 |      100 |     100 |     100 |                   
  commentJson.ts   |   90.51 |    91.89 |     100 |   90.51 | 67-76,116         
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  cpuProfiler.ts   |   70.38 |    71.83 |   88.88 |   70.38 | ...27,430-431,438 
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   70.31 |    74.57 |     100 |   70.31 | ...95-301,325-341 
  ...putCapture.ts |   90.65 |    86.17 |     100 |   90.65 | ...72,370,372-373 
  ...arResolver.ts |   97.14 |    96.42 |     100 |   97.14 | 125-126           
  errors.ts        |   90.85 |    96.36 |    92.3 |   90.85 | 69-70,298-310     
  events.ts        |     100 |      100 |     100 |     100 |                   
  gitUtils.ts      |   91.91 |    84.61 |     100 |   91.91 | 78-81,124-127     
  ...AutoUpdate.ts |    92.2 |    95.23 |   88.88 |    92.2 | 130-141           
  ...tyWarnings.ts |     100 |      100 |     100 |     100 |                   
  ...lationInfo.ts |   89.17 |    92.77 |     100 |   89.17 | ...55,272-273,318 
  languageUtils.ts |   98.19 |    97.14 |     100 |   98.19 | 132-133           
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...iagnostics.ts |   94.57 |    83.01 |   88.88 |   94.57 | ...05,311,315-317 
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.37 |    93.07 |     100 |   96.37 | ...15-416,514,527 
  osc.ts           |    97.5 |      100 |   88.88 |    97.5 | 195-196           
  package.ts       |   88.88 |       80 |     100 |   88.88 | 33-34             
  processUtils.ts  |     100 |      100 |     100 |     100 |                   
  readStdin.ts     |   79.62 |       90 |      80 |   79.62 | 33-40,52-54       
  relaunch.ts      |   93.22 |    81.25 |     100 |   93.22 | 65-67,80          
  resolvePath.ts   |   66.66 |       25 |     100 |   66.66 | 12-13,16,18-19    
  runBudget.ts     |   99.35 |    96.77 |     100 |   99.35 | 119               
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-1038            
  sessionPaths.ts  |   90.84 |    90.56 |     100 |   90.84 | ...81-182,185-186 
  settingsUtils.ts |   82.51 |    91.79 |   89.74 |   82.51 | ...76-694,701-709 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...ate-verify.ts |     100 |      100 |     100 |     100 |                   
  ...one-update.ts |   26.82 |    73.77 |   43.47 |   26.82 | ...36-837,840-859 
  ...upProfiler.ts |   98.46 |    94.52 |     100 |   98.46 | 130-131,305       
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |   95.12 |    89.06 |     100 |   95.12 | ...43-244,249-253 
  ...InfoFields.ts |    87.5 |    65.85 |     100 |    87.5 | ...24-125,146-147 
  ...alSequence.ts |     100 |    95.23 |     100 |     100 | 60,90             
  ...iffPreview.ts |   94.11 |    83.33 |     100 |   94.11 | 13                
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...ansionHook.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   91.17 |    82.35 |     100 |   91.17 | 67-68,73-74,77-78 
  version.ts       |     100 |       50 |     100 |     100 | 11                
  ...ingHandler.ts |     100 |      100 |     100 |     100 |                   
  windowTitle.ts   |     100 |      100 |     100 |     100 |                   
  ...WithBackup.ts |    62.1 |       75 |     100 |    62.1 | 93,107,118-157    
 ...s/housekeeping |   90.15 |     89.7 |   94.11 |   90.15 |                   
  cleanup.ts       |   94.33 |       95 |     100 |   94.33 | 60-62             
  ...eractionAt.ts |     100 |      100 |     100 |     100 |                   
  scheduler.ts     |   89.71 |    88.23 |   85.71 |   89.71 | 51-55,66,116-120  
  throttledOnce.ts |   86.66 |    85.18 |     100 |   86.66 | ...99,105,137-138 
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   81.57 |    83.88 |   83.57 |   81.57 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/agents        |   88.47 |    79.84 |   93.06 |   88.47 |                   
  ...transcript.ts |   92.25 |    85.71 |     100 |   92.25 | ...01,320-321,452 
  ...ent-resume.ts |   83.08 |    69.86 |   78.12 |   83.08 | ...1120-1124,1127 
  ...ound-tasks.ts |   95.07 |    88.12 |     100 |   95.07 | ...1151,1171-1174 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/arena  |   76.54 |    66.87 |   78.72 |   76.54 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.37 |    63.37 |   78.26 |   75.37 | ...1860,1866-1867 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    72.34 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   76.43 |    86.23 |   73.04 |   76.43 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |   91.98 |     90.9 |   86.66 |   91.98 | ...95,250-270,329 
  TmuxBackend.ts   |    90.7 |    76.55 |   97.36 |    90.7 | ...87,697,743-747 
  detect.ts        |   31.25 |      100 |       0 |   31.25 | 34-88             
  index.ts         |     100 |      100 |     100 |     100 |                   
  iterm-it2.ts     |     100 |     92.1 |     100 |     100 | 37-38,106         
  tmux-commands.ts |    6.64 |      100 |    3.03 |    6.64 | ...93-363,386-503 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...agents/runtime |   85.54 |     84.1 |   76.87 |   85.54 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   77.31 |    73.21 |   65.21 |   77.31 | ...1704,1731-1778 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   84.48 |    78.04 |   63.63 |   84.48 | ...00-401,404-405 
  ...nteractive.ts |   80.55 |    81.35 |   74.07 |   80.55 | ...79,481,483,486 
  ...statistics.ts |   98.19 |    82.35 |     100 |   98.19 | 127,151,192,225   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...chestrator.ts |   91.36 |    89.89 |      80 |   91.36 | ...1231,1280-1283 
  ...ow-prompts.ts |     100 |      100 |     100 |     100 |                   
  ...ow-sandbox.ts |     100 |    98.11 |     100 |     100 | 117,287           
 src/agents/tasks  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/team   |   80.31 |    83.19 |    86.5 |   80.31 |                   
  TeamManager.ts   |   67.11 |    76.25 |   74.41 |   67.11 | ...1433,1456-1457 
  identity.ts      |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...sionBridge.ts |     100 |      100 |     100 |     100 |                   
  mailbox.ts       |   94.76 |    86.36 |   92.85 |   94.76 | 86-87,348-354     
  ...ptAddendum.ts |     100 |      100 |     100 |     100 |                   
  tasks.ts         |   88.85 |    82.47 |   96.29 |   88.85 | ...-990,1034-1035 
  team-events.ts   |   60.52 |      100 |      50 |   60.52 | ...37-141,148-152 
  teamHelpers.ts   |   92.02 |    94.91 |   95.23 |   92.02 | ...31-332,368-378 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...eam/test-utils |   94.39 |    93.38 |   98.21 |   94.39 |                   
  ...on-harness.ts |   96.49 |    77.77 |     100 |   96.49 | 128-129,141-142   
  fake-agent.ts    |   98.49 |    95.08 |     100 |   98.49 | 201-203           
  fake-backend.ts  |   86.46 |    97.61 |   95.83 |   86.46 | 124-146           
 src/config        |    78.1 |    83.75 |    63.8 |    78.1 |                   
  config.ts        |   76.41 |    82.91 |   60.07 |   76.41 | ...4827,4832-4833 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   94.24 |    91.02 |   88.09 |   94.24 | ...68-369,372-373 
 ...nfirmation-bus |   98.29 |    97.14 |     100 |   98.29 |                   
  message-bus.ts   |   98.14 |    97.05 |     100 |   98.14 | 42-43             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |   88.32 |    83.53 |    91.9 |   88.32 |                   
  baseLlmClient.ts |   81.25 |    76.47 |   77.77 |   81.25 | ...13,515-525,534 
  client.ts        |   86.77 |    80.28 |   89.83 |   86.77 | ...2530,2624-2625 
  ...tGenerator.ts |   84.86 |    69.23 |     100 |   84.86 | ...84,386,393-396 
  ...lScheduler.ts |   87.18 |    81.46 |   95.83 |   87.18 | ...4111,4139-4150 
  geminiChat.ts    |   91.37 |    87.77 |   96.15 |   91.37 | ...3032,3099-3100 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...MediaLimit.ts |     100 |    95.83 |     100 |     100 | 96                
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | ...9,62-66,69-110 
  logger.ts        |   87.41 |    87.02 |     100 |   87.41 | ...64-568,614-628 
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   92.59 |       75 |      50 |   92.59 | 41-42             
  ...on-helpers.ts |   86.48 |    72.22 |     100 |   86.48 | ...97-198,212-221 
  ...issionFlow.ts |   98.78 |       96 |     100 |   98.78 | 93                
  prompts.ts       |   88.93 |    87.87 |   72.72 |   88.93 | ...-910,1113-1114 
  tokenLimits.ts   |     100 |    89.47 |     100 |     100 | 51-52             
  ...okTriggers.ts |   99.43 |    91.34 |     100 |   99.43 | 172,183           
  turn.ts          |   96.35 |    88.67 |     100 |   96.35 | ...28,441-442,486 
 ...ntentGenerator |   94.88 |    82.07 |      94 |   94.88 |                   
  ...tGenerator.ts |   96.29 |    83.18 |   92.85 |   96.29 | ...1,971,999-1001 
  converter.ts     |   94.51 |    80.72 |     100 |   94.51 | ...06-607,617,823 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
  usage.ts         |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   91.53 |    71.64 |   93.33 |   91.53 |                   
  ...tGenerator.ts |      90 |    70.96 |   92.85 |      90 | ...80-286,304-305 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |   94.22 |    83.96 |   91.17 |   94.22 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   94.09 |     82.5 |   90.62 |   94.09 | ...1025-1026,1054 
  ...tDetection.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   86.35 |     84.4 |   93.67 |   86.35 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   84.89 |    82.17 |   96.15 |   84.89 | ...1395,1611-1626 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   54.54 |    68.75 |      50 |   54.54 | ...79,87-91,95-99 
  ...tGenerator.ts |    66.4 |    70.58 |   88.88 |    66.4 | ...51-157,168-169 
  pipeline.ts      |   94.38 |     86.5 |     100 |   94.38 | ...38-539,547,615 
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   90.66 |    88.57 |     100 |   90.66 | ...15-319,349-350 
  ...kingParser.ts |     100 |    96.87 |     100 |     100 | 42                
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...rator/provider |   96.67 |    88.94 |   96.07 |   96.67 |                   
  dashscope.ts     |   97.37 |    91.39 |   93.33 |   97.37 | ...90-291,369-370 
  deepseek.ts      |   94.91 |    89.36 |     100 |   94.91 | ...31-132,145-146 
  default.ts       |   95.79 |    89.65 |   88.88 |   95.79 | 122-123,193-195   
  index.ts         |     100 |      100 |     100 |     100 |                   
  mimo.ts          |   94.11 |    66.66 |     100 |   94.11 | 29,52-53          
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  mistral.ts       |   96.07 |    73.33 |     100 |   96.07 | 32-33             
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
  utils.ts         |     100 |      100 |     100 |     100 |                   
 src/extension     |   62.64 |    79.44 |   80.31 |   62.64 |                   
  ...-converter.ts |   66.28 |    52.03 |     100 |   66.28 | ...98-799,808-840 
  ...ionManager.ts |   47.85 |    82.19 |    65.9 |   47.85 | ...1402,1412-1431 
  ...onSettings.ts |   93.46 |    93.05 |     100 |   93.46 | ...17-221,228-232 
  ...-converter.ts |   54.88 |    94.44 |      60 |   54.88 | ...35-146,158-192 
  github.ts        |   46.41 |     87.3 |   63.63 |   46.41 | ...66-372,411-464 
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   97.31 |    93.75 |     100 |   97.31 | ...65,185-186,275 
  npm.ts           |   59.01 |    71.69 |    87.5 |   59.01 | ...23-425,432-436 
  override.ts      |   94.11 |    88.88 |     100 |   94.11 | 63-64,81-82       
  redaction.ts     |     100 |      100 |     100 |     100 |                   
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-107,141-146    
  storage.ts       |     100 |      100 |     100 |     100 |                   
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.75 |    83.33 |     100 |   88.75 | ...28-231,234-237 
 src/followup      |   55.29 |    85.18 |   81.25 |   55.29 |                   
  followupState.ts |      96 |    89.74 |     100 |      96 | 159-161,218-219   
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   95.06 |       84 |     100 |   95.06 | 78,108,122,133    
  speculation.ts   |   13.02 |      100 |   16.66 |   13.02 | 89-464,524-575    
  ...onToolGate.ts |     100 |    96.42 |     100 |     100 | 95                
  ...nGenerator.ts |   70.23 |    74.57 |   83.33 |   70.23 | ...83-247,317-319 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/goals         |   89.57 |    83.57 |   94.44 |   89.57 |                   
  ...eGoalStore.ts |    85.1 |    95.45 |   84.61 |    85.1 | ...63-166,174-182 
  goalHook.ts      |   97.26 |    91.66 |     100 |   97.26 | 100-105           
  goalJudge.ts     |   84.33 |    74.28 |     100 |   84.33 | ...57-358,366-368 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/hooks         |   86.88 |    85.58 |   88.01 |   86.88 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.ts |     100 |      100 |     100 |     100 |                   
  ...terpolator.ts |   96.66 |    93.33 |     100 |   96.66 | 66-67             
  ...HookRunner.ts |   96.68 |    87.23 |     100 |   96.68 | 110-112,231-233   
  ...Aggregator.ts |   96.35 |    90.69 |     100 |   96.35 | ...00-301,382,384 
  ...entHandler.ts |   95.27 |    86.74 |   94.11 |   95.27 | ...63,920-921,931 
  hookPlanner.ts   |   86.29 |    83.33 |   85.71 |   86.29 | ...15-219,226-237 
  hookRegistry.ts  |   91.48 |    84.61 |     100 |   91.48 | ...97,416,420,424 
  hookRunner.ts    |   62.42 |    72.04 |   66.66 |   62.42 | ...64-765,774-775 
  hookSystem.ts    |   86.78 |      100 |   68.88 |   86.78 | ...07-708,714-715 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...edCallback.ts |     100 |      100 |     100 |     100 |                   
  ...HookRunner.ts |   96.37 |     90.9 |      90 |   96.37 | 342-350,424-425   
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |   96.66 |    91.66 |     100 |   96.66 | ...90,209-210,223 
  ssrfGuard.ts     |   77.22 |    85.36 |     100 |   77.22 | ...57,261-267,273 
  stopHookCap.ts   |     100 |      100 |     100 |     100 |                   
  trustedHooks.ts  |      90 |    52.63 |     100 |      90 | ...53,66-67,97-98 
  types.ts         |   92.83 |       94 |    87.5 |   92.83 | ...87-488,573-577 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
 src/ide           |   75.55 |    83.52 |   78.33 |   75.55 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |   66.14 |    81.75 |   66.66 |   66.14 | ...3-964,993-1001 
  ide-installer.ts |   89.06 |    79.31 |     100 |   89.06 | ...36,143-147,160 
  ideContext.ts    |     100 |      100 |     100 |     100 |                   
  process-utils.ts |   84.84 |    71.79 |     100 |   84.84 | ...37,151,193-194 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/lsp           |   42.42 |     51.9 |   52.14 |   42.42 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |   42.81 |    73.07 |      50 |   42.81 | ...76-427,433-450 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   25.31 |    62.06 |   41.66 |   25.31 | ...85-704,710-740 
  ...eLspClient.ts |   32.77 |       80 |   17.64 |   32.77 | ...84-288,294-295 
  ...LspService.ts |   51.85 |    65.98 |   68.57 |   51.85 | ...1339,1399-1409 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |   79.21 |    76.52 |   76.36 |   79.21 |                   
  configHash.ts    |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   73.82 |    53.92 |     100 |   73.82 | ...88-895,902-904 
  ...en-storage.ts |   98.64 |    97.77 |     100 |   98.64 | 88-89             
  oauth-utils.ts   |   70.58 |    85.29 |    90.9 |   70.58 | ...70-290,315-344 
  ...n-provider.ts |   89.83 |       96 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   79.72 |    87.05 |   86.36 |   79.72 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   83.44 |    84.21 |   92.85 |   83.44 | ...68-178,186-187 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   68.14 |    82.35 |   64.28 |   68.14 | ...81-295,298-314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/memory        |   73.76 |    77.74 |   72.68 |   73.76 |                   
  const.ts         |   94.28 |     92.3 |     100 |   94.28 | 66-67             
  dream.ts         |      66 |    73.33 |      50 |      66 | 51,108-149        
  ...entPlanner.ts |   57.84 |    72.72 |   33.33 |   57.84 | ...35,140-147,152 
  entries.ts       |   63.77 |    79.16 |      50 |   63.77 | ...72-180,183-189 
  extract.ts       |   92.72 |    74.19 |     100 |   92.72 | ...32,151-154,211 
  ...entPlanner.ts |   67.59 |     73.8 |      50 |   67.59 | ...31,240-243,415 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |      46 |    61.53 |   44.44 |      46 | ...05,212,215-347 
  indexer.ts       |    86.3 |       50 |     100 |    86.3 | ...56,62-63,75-76 
  manager.ts       |    75.5 |    81.04 |    75.6 |    75.5 | ...1292,1305-1307 
  memoryAge.ts     |   90.47 |       80 |     100 |   90.47 | 50-51             
  paths.ts         |   79.06 |    95.12 |     100 |   79.06 | 32-33,49-86       
  prompt.ts        |   94.87 |    78.57 |     100 |   94.87 | ...63,166,304-305 
  recall.ts        |   82.06 |       75 |    90.9 |   82.06 | ...59-364,395-406 
  ...ceSelector.ts |   93.02 |    81.81 |     100 |   93.02 | ...24,126-127,135 
  scan.ts          |   92.92 |    73.91 |     100 |   92.92 | ...51-52,62,90-91 
  ...entPlanner.ts |   58.33 |    66.66 |   56.25 |   58.33 | ...61-282,358-403 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   93.33 |    81.25 |     100 |   93.33 | ...,94-95,119-120 
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ontextFile.ts |   79.38 |    81.03 |   81.81 |   79.38 | ...58-272,286-291 
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   89.98 |    87.37 |   88.15 |   89.98 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...tor-config.ts |   90.24 |    91.42 |     100 |   90.24 | 142,148,151-160   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |    47.82 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.66 |    92.85 |     100 |   98.66 | 162,324,330       
  modelRegistry.ts |     100 |    98.63 |     100 |     100 | 229               
  modelsConfig.ts  |   86.24 |    85.23 |   82.92 |   86.24 | ...1328,1357-1358 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/output        |     100 |      100 |     100 |     100 |                   
  ...-formatter.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/permissions   |   82.68 |    91.87 |   68.32 |   82.68 |                   
  autoMode.ts      |   97.84 |    94.27 |     100 |   97.84 | 523-524,545-552   
  ...transcript.ts |      98 |       84 |     100 |      98 | 200-201           
  classifier.ts    |   93.95 |    94.44 |     100 |   93.95 | 158-165,383-387   
  ...erousRules.ts |     100 |    89.36 |     100 |     100 | 110,133,147,175   
  ...alTracking.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   84.86 |    89.03 |      80 |   84.86 | ...1024,1130-1134 
  rule-parser.ts   |   97.39 |    93.82 |     100 |   97.39 | ...-882,1031-1033 
  ...-semantics.ts |   70.28 |    90.69 |   46.21 |   70.28 | ...2214,2277-2280 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...sifier-prompts |   99.04 |    95.23 |     100 |   99.04 |                   
  system-prompt.ts |   99.04 |    95.23 |     100 |   99.04 | 219               
 src/plan-gate     |   62.39 |     92.3 |   56.25 |   62.39 |                   
  ...viewAgents.ts |   56.02 |    88.46 |   66.66 |   56.02 | ...09-175,197-198 
  ...provalGate.ts |   62.77 |    94.59 |    37.5 |   62.77 | ...46-249,252-258 
  state.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/prompts       |   83.63 |      100 |    87.5 |   83.63 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |     100 |      100 |     100 |     100 |                   
 src/providers     |   79.44 |    64.39 |   64.28 |   79.44 |                   
  all-providers.ts |      68 |      100 |       0 |      68 | 68-69,73-79,83-89 
  index.ts         |     100 |      100 |     100 |     100 |                   
  install.ts       |   98.87 |    87.27 |     100 |   98.87 | 268-269           
  ...der-config.ts |   69.73 |    47.29 |   68.42 |   69.73 | ...10-411,418-427 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...viders/presets |   97.31 |    86.36 |      50 |   97.31 |                   
  ...oding-plan.ts |   87.34 |      100 |       0 |   87.34 | 82-84,87-89,91-94 
  ...a-standard.ts |     100 |      100 |     100 |     100 |                   
  ...token-plan.ts |     100 |      100 |     100 |     100 |                   
  ...m-provider.ts |   97.01 |    81.25 |      75 |   97.01 | 120-121           
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/qwen          |    85.3 |    78.57 |   95.89 |    85.3 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   82.55 |    73.24 |   90.62 |   82.55 | ...1183-1199,1229 
  ...kenManager.ts |   85.36 |    76.61 |     100 |   85.36 | ...52-757,778-783 
 src/services      |   85.31 |    83.51 |   91.68 |   85.31 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   97.35 |    85.34 |     100 |   97.35 | ...94,117,417-418 
  ...ionService.ts |   98.19 |    94.94 |     100 |   98.19 | 496,498-502,605   
  ...ingService.ts |   81.88 |    82.05 |    77.5 |   81.88 | ...1412,1415-1427 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |     100 |    97.43 |     100 |     100 | 215,268           
  cronScheduler.ts |   97.57 |    92.85 |     100 |   97.57 | 62-63,77,157      
  ...eryService.ts |   80.43 |    95.45 |      75 |   80.43 | ...19-134,140-141 
  ...oryService.ts |   78.77 |    76.76 |   81.57 |   78.77 | ...1258,1299-1302 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |   91.27 |    82.69 |    90.9 |   91.27 | ...94,196,294-301 
  ...ratedFiles.ts |      96 |    88.23 |     100 |      96 | 119-120,146-147   
  gitInit.ts       |     100 |      100 |     100 |     100 |                   
  ...reeService.ts |    69.4 |    68.82 |   93.33 |    69.4 | ...2064,2092-2093 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...ticsDumper.ts |   98.37 |    95.45 |     100 |   98.37 | 185-186           
  ...ureMonitor.ts |   96.06 |    91.48 |   96.96 |   96.06 | ...49,850,864-866 
  ...orRegistry.ts |   97.24 |    92.03 |     100 |   97.24 | ...49-450,601-602 
  ...ttachments.ts |   97.24 |    90.39 |     100 |   97.24 | ...08,646,661-662 
  sessionRecap.ts  |     9.7 |      100 |       0 |     9.7 | 44-174            
  ...ionService.ts |   84.39 |     77.2 |   94.28 |   84.39 | ...1488,1526-1546 
  sessionTitle.ts  |   93.87 |    71.15 |     100 |   93.87 | ...33-236,267-268 
  ...ionService.ts |   81.29 |    78.24 |   89.28 |   81.29 | ...1926,1932-1937 
  ...pInhibitor.ts |   97.02 |    90.74 |     100 |   97.02 | ...14-115,289-290 
  ...Estimation.ts |     100 |      100 |     100 |     100 |                   
  ...UseSummary.ts |   94.63 |    88.46 |     100 |   94.63 | ...62-164,214-215 
  ...oryService.ts |   89.03 |    65.38 |     100 |   89.03 | ...23-325,330-331 
  ...reeCleanup.ts |   14.56 |      100 |   33.33 |   14.56 | 58-185            
  ...ionService.ts |   84.21 |    79.41 |     100 |   84.21 | ...18-219,235-236 
 ...icrocompaction |   98.05 |       92 |     100 |   98.05 |                   
  microcompact.ts  |   98.05 |       92 |     100 |   98.05 | ...19,292,296,394 
 src/skills        |   88.14 |    86.62 |      90 |   88.14 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |     93.1 |     100 |     100 | 93,112            
  skill-load.ts    |   94.84 |     87.5 |     100 |   94.84 | ...03,223,235-237 
  skill-manager.ts |   83.39 |    81.42 |   82.35 |   83.39 | ...1199,1206-1210 
  skill-paths.ts   |   89.15 |    86.36 |     100 |   89.15 | ...00-101,106-107 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |   97.91 |       98 |     100 |   97.91 | 277-278           
 src/subagents     |   85.84 |    85.55 |   94.33 |   85.84 |                   
  ...ter-schema.ts |     100 |    98.07 |     100 |     100 | 99                
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |    81.2 |    79.93 |   91.17 |    81.2 | ...1432,1509-1510 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 47-52,63-68,71-76 
 src/telemetry     |   78.56 |    87.69 |   80.33 |   78.56 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...on-metrics.ts |   98.96 |    79.48 |     100 |   98.96 | 169,183           
  ...on-tracing.ts |   74.55 |    73.21 |   70.58 |   74.55 | ...95,336-338,354 
  ...attributes.ts |   98.13 |       88 |     100 |   98.13 | 185-187           
  ...-exporters.ts |   46.37 |      100 |   44.44 |   46.37 | ...85,88-89,92-93 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...-processor.ts |   99.09 |    95.61 |      95 |   99.09 | 141,365-366       
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |   54.08 |    65.85 |   60.86 |   54.08 | ...1250,1267-1287 
  metrics.ts       |   75.31 |    80.85 |   77.19 |   75.31 | ...1021,1024-1035 
  ...attributes.ts |     100 |      100 |     100 |     100 |                   
  ...ime-config.ts |       0 |        0 |       0 |       0 | 1                 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |   86.75 |     88.4 |   66.66 |   86.75 | ...17-621,659-681 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   90.04 |    88.11 |   96.55 |   90.04 | ...1504,1535-1538 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  trace-context.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   98.56 |    88.63 |     100 |   98.56 | 52,101            
  types.ts         |   79.46 |    93.91 |   84.21 |   79.46 | ...1241,1244-1273 
  uiTelemetry.ts   |      92 |    95.34 |   80.95 |      92 | ...00,206-216,244 
 ...ry/qwen-logger |   68.17 |     80.2 |   65.51 |   68.17 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   68.17 |       80 |   64.91 |   68.17 | ...1077,1115-1116 
 src/test-utils    |   93.44 |    96.15 |   77.77 |   93.44 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  ...st-helpers.ts |   94.11 |       90 |     100 |   94.11 | 69-70             
  index.ts         |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   91.71 |    97.36 |   74.19 |   91.71 | ...54,218-219,232 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   79.37 |    82.04 |   85.78 |   79.37 |                   
  ...erQuestion.ts |   90.03 |    79.36 |   91.66 |   90.03 | ...99-400,407-408 
  cron-create.ts   |   88.11 |    88.88 |    62.5 |   88.11 | ...,43-44,165-172 
  cron-delete.ts   |   96.82 |      100 |   83.33 |   96.82 | 26-27             
  cron-list.ts     |   96.66 |      100 |   83.33 |   96.66 | 25-26             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  edit.ts          |   81.02 |    84.07 |      75 |   81.02 | ...15-716,826-876 
  ...r-worktree.ts |   83.14 |    67.56 |    87.5 |   83.14 | ...84-187,278-279 
  enterPlanMode.ts |   90.69 |       75 |   85.71 |   90.69 | 55-56,74-79       
  exit-worktree.ts |   84.23 |    85.96 |   91.66 |   84.23 | ...92-293,298-312 
  exitPlanMode.ts  |   64.45 |    78.57 |     100 |   64.45 | ...22-334,346-349 
  glob.ts          |   90.63 |    88.33 |   84.61 |   90.63 | ...28,171,302,305 
  grep.ts          |   79.04 |    85.71 |      75 |   79.04 | ...73-580,604-605 
  ...adTracking.ts |     100 |      100 |     100 |     100 |                   
  ls.ts            |   96.74 |    90.27 |     100 |   96.74 | 176-181,212,216   
  lsp.ts           |   72.77 |    60.09 |   90.32 |   72.77 | ...1211,1213-1214 
  ...nt-manager.ts |   80.51 |    78.46 |   84.44 |   80.51 | ...2981,2983-2984 
  mcp-client.ts    |      43 |    87.57 |      75 |      43 | ...1790,1794-1797 
  ...ry-timeout.ts |     100 |      100 |     100 |     100 |                   
  mcp-errors.ts    |     100 |      100 |     100 |     100 |                   
  ...pool-entry.ts |   77.21 |    83.96 |   79.41 |   77.21 | ...1259,1267-1268 
  ...ool-events.ts |       8 |        0 |       0 |       8 | 123-149           
  mcp-pool-key.ts  |   97.46 |    93.93 |     100 |   97.46 | 175-176           
  mcp-tool.ts      |   91.36 |    89.32 |   96.55 |   91.36 | ...40-641,691-692 
  ...sport-pool.ts |   83.27 |       80 |   84.61 |   83.27 | ...1399,1406-1410 
  ...ace-budget.ts |   87.27 |     82.6 |     100 |   87.27 | ...00-305,340-345 
  memory-config.ts |       0 |        0 |       0 |       0 | 1-47              
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 102,109           
  monitor.ts       |   91.65 |    84.05 |   88.46 |   91.65 | ...87,600,796-801 
  notebook-edit.ts |   85.11 |    76.42 |   81.25 |   85.11 | ...54-870,916-917 
  ...escendants.ts |   36.17 |    64.51 |   55.55 |   36.17 | ...46-310,385-390 
  ...nforcement.ts |   82.57 |       90 |     100 |   82.57 | 174-185,234-247   
  read-file.ts     |   94.75 |    90.32 |   81.81 |   94.75 | ...02,305,388-389 
  ripGrep.ts       |   94.17 |    85.71 |    87.5 |   94.17 | ...96-497,547-548 
  ...-transport.ts |    6.34 |        0 |       0 |    6.34 | 47-145            
  send-message.ts  |   79.48 |    86.95 |    62.5 |   79.48 | ...97-203,286-294 
  ...n-mcp-view.ts |   92.37 |    93.54 |   88.88 |   92.37 | 118-126           
  shell.ts         |   74.32 |    80.89 |   90.54 |   74.32 | ...4272,4331-4332 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |    89.4 |     92.5 |   88.88 |    89.4 | ...43,447,476-498 
  ...eticOutput.ts |   95.12 |      100 |      80 |   95.12 | 87-88             
  task-create.ts   |   93.85 |     92.3 |   81.81 |   93.85 | 41-45,59-60,91    
  task-list.ts     |   73.38 |    77.77 |   83.33 |   73.38 | ...02,105,109-116 
  task-stop.ts     |   93.14 |    96.15 |   85.71 |   93.14 | 39-40,54-64       
  task-update.ts   |   80.67 |       78 |    92.3 |   80.67 | ...75-383,415-426 
  team-create.ts   |   97.22 |    85.71 |   83.33 |   97.22 | 48-49,129-130     
  team-delete.ts   |   86.74 |    83.33 |   83.33 |   86.74 | 37-38,42-48,72-73 
  todoWrite.ts     |   89.27 |    82.05 |   92.85 |   89.27 | ...50-555,577-578 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   76.19 |     76.1 |   81.39 |   76.19 | ...53-854,862-863 
  tool-search.ts   |   92.35 |    85.84 |    92.3 |   92.35 | ...08-213,320-329 
  tools.ts         |   92.33 |    90.74 |   90.47 |   92.33 | ...99-500,516-522 
  web-fetch.ts     |   88.84 |       80 |   92.85 |   88.84 | ...12-313,315-316 
  write-file.ts    |   82.65 |    80.45 |   84.61 |   82.65 | ...65-668,696-731 
 src/tools/agent   |   76.07 |    84.05 |   76.66 |   76.07 |                   
  agent.ts         |   76.29 |    84.25 |    77.1 |   76.29 | ...3066,3093-3156 
  fork-subagent.ts |   71.08 |       75 |   71.42 |   71.08 | ...22-123,158-169 
 ...s/computer-use |   85.21 |     87.9 |   76.31 |   85.21 |                   
  bootstrap.ts     |   72.09 |    92.85 |   66.66 |   72.09 | 137-191,302-303   
  client.ts        |      38 |      100 |      50 |      38 | ...48-178,182-191 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  install-state.ts |   94.44 |       75 |     100 |   94.44 | 40-41             
  ...n-detector.ts |     100 |     87.5 |     100 |     100 | 43                
  schemas.ts       |     100 |      100 |     100 |     100 |                   
  tool.ts          |   95.67 |    82.97 |    92.3 |   95.67 | 49-50,159-165     
 ...tools/workflow |   93.03 |    66.66 |    90.9 |   93.03 |                   
  workflow.ts      |   93.03 |    66.66 |    90.9 |   93.03 | ...59-260,272-275 
 src/utils         |   89.15 |    87.86 |   93.89 |   89.15 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...Controller.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   94.76 |    93.23 |     100 |   94.76 | ...30-531,634-638 
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |   76.31 |    53.33 |     100 |   76.31 | ...37,43-44,65-66 
  btwUtils.ts      |   13.95 |      100 |       0 |   13.95 | 17-31,34-55       
  bundlePaths.ts   |     100 |      100 |     100 |     100 |                   
  ...ncyLimiter.ts |   94.64 |    95.23 |     100 |   94.64 | 64-66             
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |      90 |    87.71 |     100 |      90 | ...54-155,158-159 
  cronDisplay.ts   |   42.85 |    23.07 |     100 |   42.85 | 26-31,33-45,47-54 
  cronParser.ts    |   89.74 |    85.71 |     100 |   89.74 | ...,63-64,183-186 
  debugLogger.ts   |   96.42 |    94.11 |   88.23 |   96.42 | 185-189           
  editHelper.ts    |   93.63 |    83.52 |     100 |   93.63 | ...28-429,463-464 
  editor.ts        |    97.6 |     95.4 |     100 |    97.6 | ...25-326,328-329 
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |   96.78 |    89.13 |      95 |   96.78 | ...51-252,257,403 
  errorParsing.ts  |    97.7 |    97.05 |     100 |    97.7 | 72-73             
  ...rReporting.ts |   88.46 |       90 |     100 |   88.46 | 69-74             
  errors.ts        |   70.54 |    79.59 |      50 |   70.54 | ...15-231,235-241 
  fetch.ts         |    70.8 |     77.5 |   71.42 |    70.8 | ...41-142,161,186 
  fileUtils.ts     |    91.5 |    86.25 |   95.23 |    91.5 | ...1191,1195-1201 
  forkedAgent.ts   |   80.68 |    78.12 |   83.33 |   80.68 | ...39-545,550-556 
  formatters.ts    |   81.81 |       75 |     100 |   81.81 | 15-16             
  ...eUtilities.ts |   89.21 |    86.66 |     100 |   89.21 | 16-17,49-55,65-66 
  ...rStructure.ts |   94.36 |    94.28 |     100 |   94.36 | ...17-120,330-335 
  getPty.ts        |   31.57 |       50 |     100 |   31.57 | 26-38             
  gitDiff.ts       |   92.36 |    79.53 |     100 |   92.36 | ...55-856,928-929 
  ...noreParser.ts |    92.3 |    89.36 |     100 |    92.3 | ...15-116,186-187 
  gitUtils.ts      |   72.91 |    90.32 |   83.33 |   72.91 | ...,77-78,102-153 
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 27                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |   88.98 |    90.66 |   91.66 |   88.98 | ...46-349,359-365 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...iagnostics.ts |    96.4 |     94.2 |     100 |    96.4 | ...66,293-294,376 
  ...yDiscovery.ts |    92.4 |    89.01 |     100 |    92.4 | ...28,331,522-525 
  ...tProcessor.ts |   93.77 |    89.02 |     100 |   93.77 | ...13-319,406-407 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  modelId.ts       |   98.96 |    98.18 |     100 |   98.96 | 153               
  ...kerChecker.ts |   90.78 |    91.66 |     100 |   90.78 | 73-79             
  notebook.ts      |   94.57 |    89.83 |   95.83 |   94.57 | ...21,333,385-387 
  openaiLogger.ts  |   90.85 |    87.87 |     100 |   90.85 | ...97-199,222-227 
  partUtils.ts     |     100 |    98.61 |     100 |     100 | 206               
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   93.21 |    91.95 |     100 |   93.21 | ...89-390,392-394 
  pdf.ts           |   93.68 |    87.05 |     100 |   93.68 | ...96-297,321-325 
  projectPath.ts   |     100 |      100 |     100 |     100 |                   
  projectRoot.ts   |   71.73 |    78.57 |     100 |   71.73 | 54-66             
  ...ectSummary.ts |   89.62 |    72.41 |     100 |   89.62 | ...40-145,196-199 
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  proxyUtils.ts    |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   58.57 |       76 |     100 |   58.57 | ...4,88-89,95-100 
  ...noreParser.ts |   85.45 |    85.18 |     100 |   85.45 | ...59,65-66,72-73 
  rateLimit.ts     |   92.55 |    85.92 |     100 |   92.55 | ...70-272,309-310 
  readManyFiles.ts |   87.59 |       84 |     100 |   87.59 | ...09-211,227-238 
  retry.ts         |   91.86 |    87.17 |     100 |   91.86 | ...30,451,458-459 
  retryContext.ts  |     100 |      100 |     100 |     100 |                   
  ripgrepUtils.ts  |   46.79 |    83.33 |   66.66 |   46.79 | ...45-246,258-335 
  ...sDiscovery.ts |   97.42 |    92.85 |     100 |   97.42 | ...04,182-183,202 
  ...iagnostics.ts |   83.08 |     67.5 |   92.59 |   83.08 | ...23,543-544,550 
  ...tchOptions.ts |   82.18 |    85.18 |   95.23 |   82.18 | ...24,549,578-587 
  ...odelPrefix.ts |     100 |      100 |     100 |     100 |                   
  runtimeStatus.ts |    97.5 |    88.57 |     100 |    97.5 | 162-163           
  safeJsonParse.ts |   74.07 |    83.33 |     100 |   74.07 | 40-46             
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   90.78 |    88.23 |     100 |   90.78 | ...41-42,93,95-96 
  ...aValidator.ts |      95 |    82.75 |     100 |      95 | ...07,216-219,273 
  ...r-launcher.ts |   96.35 |    93.97 |   85.71 |   96.35 | ...35-336,347-348 
  ...nIdContext.ts |     100 |      100 |     100 |     100 |                   
  ...orageUtils.ts |   96.89 |    85.84 |     100 |   96.89 | ...51,367,447,466 
  shell-utils.ts   |   84.39 |    90.46 |     100 |   84.39 | ...1583,1590-1594 
  ...lAstParser.ts |   95.57 |    85.79 |     100 |   95.57 | ...1066-1068,1078 
  ...ContextEnv.ts |     100 |      100 |     100 |     100 |                   
  ...nlyChecker.ts |   95.08 |    91.66 |     100 |   95.08 | ...15-316,324-325 
  sideQuery.ts     |   86.17 |    86.53 |     100 |   86.17 | ...55-161,163-169 
  ...pEventSink.ts |     100 |       80 |     100 |     100 | 61                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   81.48 |       75 |     100 |   81.48 | 54-59             
  ...emEncoding.ts |   96.36 |    91.17 |     100 |   96.36 | 59-60,124-125     
  terminalSafe.ts  |     100 |      100 |     100 |     100 |                   
  ...Serializer.ts |   98.72 |       90 |     100 |   98.72 | 42-43,134,201-203 
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  textUtils.ts     |      60 |      100 |   66.66 |      60 | 36-55             
  thoughtUtils.ts  |     100 |    92.85 |     100 |     100 | 71                
  ...-converter.ts |   94.59 |    85.71 |     100 |   94.59 | 35-36             
  tool-utils.ts    |    93.6 |     91.3 |     100 |    93.6 | ...58-159,162-163 
  ...ultCleanup.ts |   15.45 |    33.33 |      25 |   15.45 | 33-136            
  truncation.ts    |   75.31 |    85.55 |   71.42 |   75.31 | ...49-454,458-482 
  windowsPath.ts   |   89.47 |    78.57 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   95.81 |    89.39 |     100 |   95.81 | ...74-275,299-301 
  xml.ts           |    97.8 |     87.5 |     100 |    97.8 | 98-99             
  yaml-parser.ts   |   83.87 |    73.84 |     100 |   83.87 | ...31-234,239-240 
 ...ils/filesearch |   83.58 |    81.02 |   94.28 |   83.58 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   83.07 |    77.74 |   94.82 |   83.07 | ...1468,1502-1503 
  fileSearch.ts    |   93.78 |    87.67 |     100 |   93.78 | ...70-271,273-274 
  fzfWorker.ts     |       0 |        0 |       0 |       0 | 1-109             
  ...rkerHandle.ts |   84.05 |    75.43 |   89.47 |   84.05 | ...30-334,340-341 
  ignore.ts        |     100 |      100 |     100 |     100 |                   
  result-cache.ts  |     100 |     92.3 |     100 |     100 | 46                
 ...uest-tokenizer |   56.63 |    74.52 |   74.19 |   56.63 |                   
  ...eTokenizer.ts |   41.86 |    76.47 |   69.23 |   41.86 | ...70-443,453-507 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |      76 |      100 |   33.33 |      76 | 45-48,55-56       
  textTokenizer.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run.

LaZzyMan added 2 commits June 12, 2026 17:49
…st gaps

R1 of pre-push adversarial self-review on PR #5034 surfaced 6 confirmed
findings across 6 diverse lenses (correctness / security / reuse-altitude
/ self-invariant / consumer-breakage / test-gaps). Each finding faced 2
independent skeptics defaulting to refuted=true; 6 survived majority
challenge.

Source code:
- Worktree-preserved suffix wording now matches AgentTool's
  formatWorktreeSuffix (agent.ts:1700-1719) verbatim, including the
  `git worktree add <path> <branch>` recovery hint for the directory-
  removed-but-branch-preserved race.

Test gaps closed:
- schema-mode success after 1 nudge (round-2 args captured)
- schema-mode success after 2 nudges (round-3 args captured)
- schema-mode + agentType together — floor disallowedTools still unioned
- schema-mode caller-abort takes priority over the StructuredOutput
  terminal error (signal.aborted check at workflow-orchestrator.ts:489-490)
- override path dispose() runs in finally on the success path
- override path dispose() runs in finally on the terminate-mode-error path

Declined R1 finding: negative tests for invalid opt types (schema/model/
agentType passed null/number/empty-string). Adding upfront type
validation is scope creep — upstream does not, P1/P2 do not, and the
workflow tool is model-authored where these inputs are extremely
unlikely. Existing AJV / SubagentManager downstream errors are descriptive
enough. Will revisit if R2 makes a stronger case.

166/166 tests pass (workflow suite + adjacent + workflow-orchestrator).
typecheck + lint clean across packages/core, packages/cli,
integration-tests, sdk, webui.
…itize + 12 tests

R2 of pre-push adversarial self-review on PR #5034. 6 diverse-lens
finders (60 agents, ~2.5M tokens, 24 min) over the R1-fix-applied
code, with 2 independent skeptics defaulting to refuted=true.
12 confirmed survivors after adversarial verify; decisions below.

Security (FIX):
- agent() wrapper in workflow-sandbox.ts now JSON-revives agentOpts
  inside the vm runInContext block BEFORE passing them to the host
  dispatch. Closes a Proxy/inherited-getter escape that P3 introduced
  along with the user-supplied schema object: a script could have
  wrapped agentOpts.schema in a Proxy whose getter ran host-side code
  during SyntheticOutputTool construction / AJV compile. Same
  mechanism as args / parallel-result revival.
- runOverridePath now sanitizes opts.agentType through
  sanitizeForErrorMessage() (control chars → space) before
  interpolation into the "agent type 'X' not found" error message.
  Prevents a model-authored agentType containing CRLF / NUL from
  fragmenting a single-line error across log records / OTLP fields.

Reuse-altitude (FIX):
- Added JSDoc block to WorkflowWorktreeIsolation interface
  documenting each field's role for cleanup.

Test gaps (FIX, 12 new tests):
- agentType control-char sanitization regression
- dispose() runs in finally when subagent.execute throws
- isolation:'worktree' provision error branches (5):
  nested parent / git unavailable / not a git repo / parent dirty /
  createUserWorktree returns failure
- isolation:'worktree' cleanup branches (3):
  removeUserWorktree fails / branchPreserved race / removeUserWorktree
  throws — each preserves the worktree (or branch) with the right
  user-facing suffix
- combinations (2): model + isolation:'worktree' threads model AND
  provisions worktree; schema + isolation:'worktree' returns
  structured payload verbatim (preserved suffix only on string return)

Test infrastructure: vi.mock'd GitWorktreeService at the module level
(partial mock; preserves the existing exports the unrelated
worktreeCleanup.ts depends on) with a per-test beforeEach reset.

Declined R2 findings (kept the R1 line):
- [major] Schema parameter upfront validation: same scope-creep
  decline as R1. Upstream doesn't do it; AJV's downstream error is
  descriptive enough.
- [major] Worktree provision extracted to shared util with AgentTool:
  agreed in principle but out of P3 scope. A separate refactor PR
  should land that with AgentTool maintainers in the loop.

178/178 tests pass (workflow + adjacent suites). typecheck + lint
clean across packages/core, packages/cli, integration-tests, sdk,
webui.
@LaZzyMan LaZzyMan requested a review from wenshao June 12, 2026 15:06
@wenshao

wenshao commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Runtime verification — real TUI, tmux-driven (maintainer merge reference)

Verdict: PASS — built PR head 6b4d7216 locally and drove the real workflow tool through the TUI (with QWEN_CODE_ENABLE_WORKFLOWS=1), exercising all four P3 agent() options end-to-end. Every option shows a clean before/after against the PR's merge-base 9b4ba60e (P1/P2), where the same scripts hit the "scheduled for …" sandbox stubs.

Claim (my read of the diff)

P3 stops the sandbox from rejecting agent({schema|model|isolation|agentType}) and routes each to a real dispatch implementation: schema injects a per-call structured_output tool and returns the validated args as an object; agentType resolves via SubagentManager.findSubagentByName (unresolved → verbatim error); isolation:'worktree' provisions a git worktree under .qwen/worktrees/agent-<hex>; isolation:'remote' throws "not available in this build". The diff matches.

Method

  • PR worktree + a baseline worktree at the merge-base 9b4ba60e (P1/P2 — so the only delta is P3), both npm install && npm run build.
  • Real TUI (packages/cli/dist/index.js --approval-mode yolo, QWEN_CODE_ENABLE_WORKFLOWS=1) in tmux, isolated $HOME.
  • A local mock OpenAI server plays three roles by the tools each request advertises: the main agent (sees workflow) emits a workflow tool call carrying the test script; a schema-mode subagent (sees structured_output) emits that tool call; a plain subagent (Explore / worktree) returns text. So the main agent really invokes the workflow tool, the orchestrator really runs the script, and agent() really dispatches subagents through the P3 code — only the model token stream is mocked.

Steps — before/after, all at the workflow tool card

  1. agent({isolation:'remote'})

    • PR: ✗ Workflow → agent({isolation:'remote'}) is not available in this build.
    • Baseline: ✗ Workflow → agent({isolation: 'remote'}) is not supported in P1. Worktree / remote isolation is scheduled for a later phase.
  2. agent({schema}) happy path — PR returns a real object:

    { "runId": "wf_…", "phases": [], "logs": [], "result": { "primary_color": "blue", "confidence": 0.9 } }

    mock roles main → sub-schema → main (the subagent really called structured_output; the event listener captured its args).
    Baseline: ✗ agent({schema}) is not supported in P1. … scheduled for P3.

  3. 🔍 agent({schema}) failure path (PR) — subagent stubbornly answers in prose, never calls structured_output:
    ✗ Workflow → subagent completed without calling StructuredOutput (after 2 in-conversation nudges). (upstream-verbatim terminal error)

  4. agent({agentType:'NoSuchAgent_42xyz'})

    • PR: ✗ agent({agentType}): agent type 'NoSuchAgent_42xyz' not found.
    • Baseline: ✗ agent({agentType}) is not supported in P1.
  5. agent({agentType:'Explore'}) resolves & routes (PR)✓ Workflow → "result": "GREEN", roles main → sub-plain → main (built-in Explore resolved and the subagent ran on its surface). Baseline: ✗ … not supported in P1.

  6. agent({isolation:'worktree'}) full lifecycle (PR)✓ Workflow, and the worktree was really provisioned:

    result: "PURPLE
    
    [worktree preserved at .../.qwen/worktrees/agent-6b2b00d on branch worktree-agent-6b2b00d]"
    
    $ git worktree list
    .../proj                               fe416b3 [main]
    .../proj/.qwen/worktrees/agent-6b2b00d fe416b3 [worktree-agent-6b2b00d]
    

    The subagent ran inside the worktree and the lifecycle ran on completion. Baseline: ✗ … scheduled for a later phase, and no worktree directory was created (the stub rejects before provisioning).

agent() option Baseline (9b4ba60e, P1/P2) PR (6b4d7216, P3)
isolation:'remote' stub "scheduled for a later phase" "not available in this build"
isolation:'worktree' stub "scheduled for a later phase" worktree provisioned + subagent ran + lifecycle
schema (success) stub "scheduled for P3" returns validated object
schema (no tool call) n/a (stub) "after 2 in-conversation nudges"
agentType (not found) stub "not supported in P1" "agent type 'X' not found"
agentType:'Explore' stub "not supported in P1" resolves + routes → result

Findings

  • All four P3 options work at the real surface, and each shows the exact upstream-aligned message/behavior the PR claims. The worktree path is the most convincing — a real git worktree + branch appeared under .qwen/worktrees/ and the subagent ran inside it, then the lifecycle appended the preserve note.
  • The worktree completed via the preserve branch even though the subagent didn't change anything — matching the pre-existing hasWorktreeChanges/.qwen-session-marker quirk the PR description flags under S7. I confirmed it's a clean run that still preserves; it's the shared AgentTool cleanup-detection path, not something P3 introduced. Not a blocker.
  • ⚠️ agent({model}) was the one option I did not exercise at the TUI. Observing it needs two genuinely different providers to see the routing actually switch; my single mock provider can't show that. It's covered by the author's real-LLM E2E (S-series) and the model is threaded into SubagentConfig.model unit test. Flagging it as the gap in my runtime evidence.
  • The subagent token stream is mocked (deterministic, no API key), so this verifies P3's dispatch / sandbox / worktree-lifecycle wiring inside the real CLI process — not model quality. The author's 7/7 run against qwen3-coder-plus covers the live-model side; the two are complementary.
  • Scaffolding note (not about the PR): the mock is stateless and keyed off the tools advertised per request — when I added the schema-fail toggle I restarted it before re-running, after the PR fix(cli): drop tool calls after cancellation #5020 lesson about stale mock processes.

Build: npm install && npm run build on both worktrees, clean. Tests not re-run locally (CI + the author's 159+217 suite cover them); this is runtime evidence only.

中文版(验证报告)

运行时验证 — tmux 驱动真实 TUI(合并参考)

结论:PASS — 本地构建 PR head 6b4d7216,在真实 TUI 里(QWEN_CODE_ENABLE_WORKFLOWS=1)驱动真实 workflow 工具,端到端验证了 P3 的全部四个 agent() 选项。每个选项都与 PR 的 merge-base 9b4ba60e(P1/P2)形成干净的前后对照——相同脚本在基线上命中 sandbox 的 "scheduled for …" stub。

对 diff 的理解

P3 让 sandbox 不再拒绝 agent({schema|model|isolation|agentType}),而是路由到真正的 dispatch 实现:schema 注入 per-call structured_output 工具并把校验后的 args 作为对象返回;agentTypeSubagentManager.findSubagentByName 解析(解析不到 → 逐字错误);isolation:'worktree'.qwen/worktrees/agent-<hex> 下开 git worktree;isolation:'remote' 抛 "not available in this build"。diff 与此一致。

方法

  • PR worktree + 一个位于 merge-base 9b4ba60e(P1/P2,确保唯一差异是 P3)的基线 worktree,都 npm install && npm run build
  • 真实 TUI(--approval-mode yolo,QWEN_CODE_ENABLE_WORKFLOWS=1)在 tmux 中运行,$HOME 隔离。
  • 本地 mock OpenAI 服务器按每个请求声明的工具扮演三种角色:主 agent(看到 workflow)发出携带测试脚本的 workflow tool call;schema 模式子 agent(看到 structured_output)发出该 tool call;普通子 agent(Explore/worktree)返回文本。所以主 agent 真的调用了 workflow 工具,orchestrator 真的执行了脚本,agent() 真的经 P3 代码 dispatch 子 agent——只有模型的 token 流是 mock 的。

步骤 — 前后对照,均在 workflow 工具卡片观察

  1. agent({isolation:'remote'}) — PR:"not available in this build";基线:"not supported in P1 ... scheduled for a later phase"。
  2. agent({schema}) 成功路径 — PR 返回真正的对象 {"primary_color":"blue","confidence":0.9},mock 角色 main→sub-schema→main(子 agent 真的调了 structured_output,event listener 捕获其 args);基线:"scheduled for P3"。
  3. 🔍 agent({schema}) 失败路径(PR) — 子 agent 坚持用文本作答、从不调 structured_output:"subagent completed without calling StructuredOutput (after 2 in-conversation nudges)"(upstream 逐字终端错误)。
  4. agent({agentType:'NoSuchAgent_42xyz'}) — PR:"agent type 'NoSuchAgent_42xyz' not found";基线:"not supported in P1"。
  5. agent({agentType:'Explore'}) 解析并路由(PR)✓ Workflow,result "GREEN",角色 main→sub-plain→main(内置 Explore 解析成功、子 agent 在其工具面上运行);基线:"not supported in P1"。
  6. agent({isolation:'worktree'}) 完整生命周期(PR)✓ Workflow,worktree 真实创建:git worktree list 显示 .qwen/worktrees/agent-6b2b00d [worktree-agent-6b2b00d],子 agent 在其中运行,完成后 result 追加 "[worktree preserved at ... on branch ...]"。基线:"scheduled for a later phase",且没有创建 worktree 目录(stub 在 provision 前就拒了)。
agent() 选项 基线(9b4ba60e,P1/P2) PR(6b4d7216,P3)
isolation:'remote' stub "scheduled for a later phase" "not available in this build"
isolation:'worktree' stub "scheduled for a later phase" worktree 创建 + 子 agent 运行 + 生命周期
schema(成功) stub "scheduled for P3" 返回校验后对象
schema(不调工具) 不适用(stub) "after 2 in-conversation nudges"
agentType(未找到) stub "not supported in P1" "agent type 'X' not found"
agentType:'Explore' stub "not supported in P1" 解析 + 路由 → result

观察

  • 四个 P3 选项在真实 surface 上都工作,且每个都呈现 PR 声称的 upstream 对齐消息/行为。worktree 路径最有说服力——.qwen/worktrees/ 下真实出现了 git worktree + 分支,子 agent 在其中运行,生命周期追加了 preserve 提示。
  • worktree 即使子 agent 没改任何东西也走了 preserve 分支——与 PR 描述 S7 标注的既有 hasWorktreeChanges/.qwen-session marker quirk 一致。我确认这是干净运行仍被保留;它是 AgentTool 共享的 cleanup 检测路径,不是 P3 引入的,不阻塞合并。
  • ⚠️ agent({model}) 是我唯一没在 TUI 上验证的选项。 要观察它需要两个真正不同的 provider 才能看到路由切换;我的单一 mock provider 显示不出来。它由作者的真实 LLM E2E 和 "model 被 thread 进 SubagentConfig.model" 单测覆盖。作为我运行时证据的缺口标注出来。
  • 子 agent 的 token 流是 mock 的(确定性、无需 API key),所以本验证证明的是 P3 的 dispatch / sandbox / worktree 生命周期在真实 CLI 进程里的接线,而非模型质量。作者用 qwen3-coder-plus 跑的 7/7 覆盖了真实模型一侧,二者互补。
  • 脚手架说明(与 PR 无关):mock 无状态、按每请求声明的工具分角色——加 schema-fail 开关后我重启了它再重跑(吸取 PR fix(cli): drop tool calls after cancellation #5020 旧 mock 进程的教训)。

构建:两个 worktree 上 npm install && npm run build 干净通过。本地未重跑测试(CI + 作者 159+217 套件已覆盖);本报告只提供运行时证据。

Comment thread packages/core/src/agents/runtime/workflow-orchestrator.ts Outdated
Comment thread packages/core/src/agents/runtime/workflow-orchestrator.ts
Comment thread packages/core/src/agents/runtime/workflow-orchestrator.ts Outdated
Comment thread packages/core/src/agents/runtime/workflow-sandbox.ts

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff plus the API contracts it links against, and ran the suites locally.

Verified locally (fresh worktree at 6b4d721):

  • 160/160 workflow suite + 209/209 adjacent regression (subagents / syntheticOutput / agent-override) — all green
  • tsc --noEmit and eslint clean on touched files

Contract checks that hold (each verified against the actual implementations, not just the PR text):

  • Schema override sets TOOL_REGISTRY_REBUILT, so buildSubagentContextOverride skips its own rebuild and the per-call SyntheticOutputTool survives into the subagent's registry — the most fragile interaction in this design, handled correctly.
  • Validation failures surface as TOOL_RESULT success:false through CoreToolScheduler, so the 3-failure abort counting is real; agent-core's un-emitted-callId backfill keeps pendingArgs from dangling.
  • Ephemeral config (no tools, floor disallowedTools) → convertToRuntimeConfig yields tools: ['*'] — tool surface equivalent to the fast path.
  • Worktree provision/rebind/cleanup mirrors AgentTool line-for-line (dirty-parent refuse, fail-closed checks, double-finally with null-out against double cleanup).
  • vm-realm revival runs inside the vm bootstrap; the two security regressions pin the constructor-chain escape closed.
  • Fast path is byte-for-byte preserved (indentation-only diff).

Findings: one High (schema+agentType drops the resolved agent's persona — upstream contract appends; see inline), two Medium (abort-listener leak per schema call; terminateMode misdiagnosis + nudge wording; see inline), and two Low:

  • L1: no unit coverage for the worktree lifecycle (provision/rebind/cleanup — E2E S7 only, harness not committed) and none for the schema+agentType / schema+worktree combinations. Committing the harness under integration-tests/ as offered in the PR description would close most of this.
  • L2: schema-mode preserved-worktree info goes to debugLogger only, invisible to the script — acknowledged in the code comment as a deliberate tradeoff; worth revisiting once P4's narrator lands.

Overall: solid engineering — the dangerous interactions are all consciously handled with regression tests, and the PR honestly discloses the pre-existing session-marker quirk found during S7. Direction is approve; I'd fix H1 (small: append instead of replace + one combo test) and ideally M1 before merge.

Comment thread packages/core/src/agents/runtime/workflow-orchestrator.ts
Comment thread packages/core/src/agents/runtime/workflow-orchestrator.ts Outdated
Comment thread packages/core/src/agents/runtime/workflow-orchestrator.ts Outdated
wenshao
wenshao previously approved these changes Jun 12, 2026
Round 1 (15:41) + Round 2 (17:24) review from wenshao surfaced 7 inline
findings across schema-mode dispatch correctness, worktree cleanup
coverage, and error attribution. Each fix is paired with a regression
test that was RED before the change landed.

T0 [Critical] Worktree leak when schema setup throws after provision
  workflow-orchestrator.ts: outer try MOVED to start immediately after
  provisionWorkflowWorktree. Previously the try opened only after
  createSchemaConfigOverride / createSchemaModeState / signal listener
  attachment — so any throw in those three (broken MCP server during
  the per-call ToolRegistry rebuild was the trigger wenshao cited)
  orphaned the just-provisioned worktree under .qwen/worktrees/.
  Test: "isolation:'worktree' + schema setup throws → worktree is
  still cleaned up" — simulates createToolRegistry failure during
  createSchemaConfigOverride; asserts removeUserWorktree was called.

T1 [Critical] / T4 [H1] agentType + schema silently dead-ended
  workflow-orchestrator.ts: schema-mode augmented config now (a)
  appends ToolNames.STRUCTURED_OUTPUT to baseConfig.tools when the
  allowlist is restricted (no '*' and doesn't already contain it), so
  prepareTools / getFunctionDeclarationsFiltered doesn't filter
  structured_output out of the subagent's surface; (b) preserves the
  resolved agentType's persona by APPENDING the schema-contract
  instruction block instead of replacing the systemPrompt outright.
  Replace remains only on the ephemeral no-agentType path where
  baseConfig.systemPrompt IS WORKFLOW_SUBAGENT_SYSTEM_PROMPT (schema
  variant is its strict superset; avoids two near-identical prompts).
  Tests: structured_output appears in the allowlist alongside the
  agentType's existing tools; persona prompt is contained in the
  effective systemPrompt.

T2 [Suggestion] / T5 [M1] Parent-abort listener leaked per schema call
  workflow-orchestrator.ts: named listener stored at outer scope,
  removed in the outer finally regardless of how the dispatch ended.
  Previous `{ once: true }` only auto-removed on actual parent abort;
  the happy-path schema dispatch — success capture / 3-failure abort
  fires the CHILD controller without the parent ever aborting — left
  the listener stuck on the per-run signal. With N schema calls per
  workflow N listeners + N child-controller closures accumulated.
  Test: 5 sequential schema dispatches over the same parent signal
  end with zero live listeners.

T6 [M2] Terminate mode misdiagnosed as nudge exhaustion
  workflow-orchestrator.ts: schema path now distinguishes
  terminateMode before attributing failure to schema mode. TIMEOUT /
  MAX_TURNS / ERROR throw the existing "did not complete (terminate
  mode: X)" message that the non-schema path uses. Only the actual
  schema-failure cases produce schema wording, and those are split:
  attempts > 2 keeps the upstream-verbatim "(after 2 in-conversation
  nudges)" wording; attempts === 0 throws an accurate "no validation
  attempt — model produced plain-text content" instead of misleadingly
  citing nudges that never happened. (The existing 0-call test was
  updated to match the new accurate message; the 3-failure test
  retains the verbatim wording.)
  Tests: parametric over TIMEOUT/MAX_TURNS/ERROR asserting "did not
  complete"; companion test pinning the verbatim wording to the
  3-failure path.

T3 [Suggestion] Schema-mode JSON revival sentinel — clarified
  workflow-sandbox.ts: added a block comment documenting that the
  JSON-round-trip + null-on-throw is a SECURITY backstop (errors-as-data
  convention from parallel/pipeline) rather than a contract path —
  unreachable in production schema mode because the host return is
  LLM tool_call args, always JSON-serializable. No behavior change.

Tests: 75/75 orchestrator + 111/111 sandbox/tool/limiter green.
typecheck + lint clean across packages/core and packages/cli.

R1+R2 self-review commits (e1c5ec7 / 62624a9) precede this commit
on the same branch — they predate wenshao's review and address
distinct findings; reviewer L1 (worktree-lifecycle unit coverage) is
already closed by R2's 11 worktree tests.
@LaZzyMan LaZzyMan dismissed stale reviews from wenshao and qwen-code-ci-bot via 99a15fe June 13, 2026 08:28
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

Round 1 + 2 — addressed in 99a15fe

Thanks @wenshao for the runtime verification on top of two careful inline rounds. All 7 inline threads above have explicit replies + are resolved; this is the per-finding outcome:

# Severity Outcome Note
line 413 (R1) Critical (worktree leak) ✅ fixed outer try moved before schema setup; regression test for setup-throws path
line 374 (R1) + line 384 (R2) Critical + H1 (silent dead-end on agentType+schema) ✅ fixed (combined) append structured_output to a restricted allowlist + append schema instructions to the agentType persona instead of replacing it; 2 new tests
line 431 (R1+R2) Suggestion + M1 (parent-abort listener leak) ✅ fixed (combined) named listener stored at outer scope, removed in outer finally regardless of how dispatch ended; new "no accumulation over 5 sequential calls" test
sandbox line 554 (R1) Suggestion (revival null sentinel ambiguity) 📝 documented (no behavior change) unreachable in schema mode (LLM tool_call args are always JSON-serializable; AJV-validated); kept as residual EAD-1-style defense; added a comment block explaining the contract
line 494 (R2) M2 (terminate-mode misdiagnosis) ✅ fixed added terminate-mode check; split the "after 2 nudges" wording (kept verbatim for the actual 3-failure case) from a new accurate "no validation attempt — model produced plain-text content" for the 0-call case; nudge-loop deferred per your note

Two notes on your review summary

  • L1 (worktree-lifecycle unit coverage / combo tests). This was already closed before your review pass — picked up in my own pre-push self-review (commits e1c5ec79, 62624a99) on this branch but not yet pushed at the time you ran the suites. Those two commits added 11 worktree-related tests at the orchestrator layer (5 provision-error branches, 3 cleanup-error branches, 2 option combinations including schema + isolation:'worktree', 1 dispose() in finally), plus vi.mock'd GitWorktreeService infra so the suite can drive each path deterministically. They're all on the branch now alongside this round's fixes — no separate harness commit needed.
  • L2 (schema-mode preserved-worktree info only in debugLogger). Agreed — leaving as-is for this PR; revisit once P4's narrator lands and there's a structured channel for operator-visible info that doesn't perturb the script's structured payload.

Suite after this round: 75/75 orchestrator + 111/111 sandbox/tool/limiter green; tsc --noEmit and eslint clean on packages/core + packages/cli; the real-LLM E2E (7/7 against qwen3-coder-plus via DashScope) is unchanged.

@wenshao

wenshao commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — all required headings present, bilingual section included, test plan is thorough.

On direction: this is a well-scoped phase in a multi-phase port of upstream Claude Code's workflow dispatch contract. The upstream CHANGELOG confirms workflows are actively evolving (recent fixes for worktree isolation, attribution headers, trigger keywords). P3 completes the four agent() options that P1/P2 stubbed — this is convergence work, not feature creep. Aligned.

On approach: the phased architecture (P1 → P2 → P3) has held up well. Each phase adds a focused slice without re-architecting prior work. The 7-file scope is tight for what it delivers — four dispatch options, security hardening, and 403 passing tests. The fast-path preservation (byte-for-byte unchanged when no opts are set) is the right call for zero-overhead backward compatibility.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓ — 所有必需标题齐全,双语部分包含在内,测试计划详尽。

方向:这是将上游 Claude Code 的 workflow dispatch 契约移植到 qwen-code 的一个范围明确的阶段。上游 CHANGELOG 确认 workflow 功能仍在积极演进(近期修复了 worktree 隔离、归因 headers、触发关键词等)。P3 补全了 P1/P2 中以 stub 形式预留的四个 agent() 选项——这是收敛性工作,不是功能蔓延。方向对齐。

方案:分阶段架构(P1 → P2 → P3)保持良好。每个阶段添加一个聚焦的切片,不重新设计先前工作。7 个文件的范围对于所交付的内容(四个 dispatch 选项、安全加固、403 个通过的测试)来说是紧凑的。快速路径保留(无 opts 时逐字节不变)是零开销向后兼容的正确做法。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Read all 7 changed files in the worktree. The implementation is clean and well-structured.

What I independently would have done: extend createProductionDispatch with a two-path split (fast vs override), resolve agentType via SubagentManager.findSubagentByName, inject a per-call SyntheticOutputTool for schema mode, thread model into SubagentConfig, and provision worktrees via GitWorktreeService. This is essentially what the PR does.

Findings — no blockers:

  • The nested try/finally structure (inner: agent dispose, outer: worktree cleanup + parent-abort listener removal) is correct and well-justified by the review history (R3 T0/T2/T5 fixes).
  • Security handling is thorough: JSON revival for schema-mode object returns closes the T1/T8/T14 prototype escape, agentType sanitization prevents log injection, and the sandbox opts are JSON-round-tripped before crossing the vm boundary.
  • The createWorktreeConfigOverride using Object.create(base) with own-property rebinds for all cwd-adjacent getters is the right pattern — matches AgentTool's approach.
  • Schema mode's per-call isolation (own SchemaModeState, own AbortController, own ToolRegistry) correctly prevents cross-contamination under parallel().
  • The pre-existing hasWorktreeChanges / .qwen-session marker quirk is correctly documented and not introduced by P3.

No AGENTS.md violations detected. The code stays within the workflow subsystem, follows ESM + TypeScript strict conventions, and tests are collocated with source.

Test Results

Unit + Integration (worktree, PR branch 6b4d721)

$ cd packages/core && npx vitest run \
    src/agents/runtime/workflow-orchestrator.test.ts \
    src/agents/runtime/workflow-sandbox.test.ts \
    src/tools/workflow/workflow.test.ts

 ✓ src/tools/workflow/workflow.test.ts (17 tests)
 ✓ src/agents/runtime/workflow-orchestrator.test.ts (75 tests)
 ✓ src/agents/runtime/workflow-sandbox.test.ts (86 tests)

 Test Files  3 passed (3)
      Tests  178 passed (178)

Adjacent Regression

$ npx vitest run src/subagents/ src/config/config.workflow \
    src/tools/syntheticOutput src/tools/agent/agent-override.test.ts \
    src/utils/concurrencyLimiter.test.ts

 Test Files  11 passed (11)
      Tests  225 passed (225)

Typecheck + ESLint

$ npx tsc --noEmit -p packages/core/tsconfig.json  → clean
$ npx eslint <4 changed source files>              → clean

Sandbox-Level Verification (tmux, built PR)

Drove the sandbox agent() wrapper through 6 scenarios with a mock dispatch — verifying that each P3 option reaches the dispatch layer correctly and the sandbox's JSON revival handles object returns:

✅ isolation:remote (threw): agent({isolation:'remote'}) is not available in this build.
✅ isolation:invalid (threw): agent({isolation: 'bogus'}): unknown isolation mode. Known modes are: 'worktree', 'remote'.
✅ schema passthrough: {"x":"test-result-schema"}
✅ model passthrough: test-result-model
✅ agentType passthrough: test-result-agentType
✅ fast path: fast-path-result

=== 6/6 scenarios passed ===

This confirms the sandbox-level wiring (option allowlist, isolation validation, JSON revival for object returns, fast-path preservation). The full dispatch path (SubagentManager resolution, worktree provisioning, schema event-listener lifecycle) is covered by the 178 unit tests above plus the runtime verification from @wenshao's earlier comment (mock-driven TUI, 6/7 options exercised end-to-end) and the author's real-LLM E2E (7/7 against qwen3-coder-plus).

中文说明

代码审查

在 worktree 中阅读了全部 7 个变更文件。实现干净且结构良好。

我独立会怎么做: 扩展 createProductionDispatch,分为快速路径和覆盖路径;通过 SubagentManager.findSubagentByName 解析 agentType;为 schema 模式注入 per-call SyntheticOutputTool;将 model 传入 SubagentConfig;通过 GitWorktreeService 配置 worktree。这基本上就是 PR 所做的。

发现——无阻塞项:

  • 嵌套 try/finally 结构(内层:agent dispose,外层:worktree 清理 + parent-abort 监听器移除)正确,且由审查历史(R3 T0/T2/T5 修复)充分证明。
  • 安全处理彻底:schema 模式对象返回的 JSON 复活关闭了 T1/T8/T14 原型逃逸,agentType 净化防止日志注入,sandbox opts 在跨越 vm 边界前经过 JSON 往返。
  • createWorktreeConfigOverride 使用 Object.create(base) 加自有属性重绑定所有 cwd 相邻的 getter——与 AgentTool 的方法一致。
  • Schema 模式的 per-call 隔离(自有 SchemaModeState、自有 AbortController、自有 ToolRegistry)正确防止了 parallel() 下的交叉污染。
  • 既有的 hasWorktreeChanges / .qwen-session 标记怪癖已正确记录,不是 P3 引入的。

未检测到 AGENTS.md 违规。 代码保持在 workflow 子系统内,遵循 ESM + TypeScript 严格模式约定,测试与源码并列。

测试结果

  • 178/178 workflow 测试通过(orchestrator + sandbox + workflow tool)
  • 225/225 相邻回归测试通过(subagents, config, syntheticOutput, agent-override, concurrencyLimiter)
  • TypeScript 类型检查干净
  • ESLint 干净
  • Sandbox 级别验证(tmux,构建后的 PR):6/6 场景通过

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Reflection

Stepping back: this PR delivers exactly what P1/P2 designed for. The four agent() options complete the dispatch contract, and the implementation is a textbook example of how to extend a phased architecture without disrupting prior work.

My independent proposal matched the PR's approach — two-path dispatch, SubagentManager resolution, per-call schema state, worktree provisioning via GitWorktreeService. The PR exceeds it in the details: the security hardening (JSON revival for object returns, agentType sanitization, opts round-tripping), the review-driven fixes (R3 T0 worktree leak, T2 listener accumulation, M2 terminate-mode disambiguation), and the fast-path preservation all show careful engineering.

The test evidence is strong: 403 unit/integration tests across all affected surfaces, clean typecheck and lint, sandbox-level verification confirming the option routing, and two independent runtime verifications (collaborator's mock-driven TUI + author's real-LLM E2E). The one gap — agent({model}) not exercised at the TUI level — is acknowledged and covered by unit tests + the author's real-LLM run.

The pre-existing worktree cleanup-detection quirk (.qwen-session marker seen as dirty) is correctly flagged for follow-up and not introduced here.

This is ready to ship. ✅

中文说明

反思

退一步看:这个 PR 精确地交付了 P1/P2 所设计的内容。四个 agent() 选项完成了 dispatch 契约,实现是扩展分阶段架构而不干扰先前工作的教科书式范例。

我的独立方案与 PR 的方法一致——双路径 dispatch、SubagentManager 解析、per-call schema 状态、通过 GitWorktreeService 配置 worktree。PR 在细节上超越了它:安全加固(对象返回的 JSON 复活、agentType 净化、opts 往返)、审查驱动的修复(R3 T0 worktree 泄漏、T2 监听器累积、M2 terminate-mode 区分)以及快速路径保留都展示了精心的工程。

测试证据充分:403 个单元/集成测试覆盖所有受影响的表面,类型检查和 lint 干净,sandbox 级别验证确认了选项路由,以及两个独立的运行时验证(协作者的 mock 驱动 TUI + 作者的真实 LLM E2E)。

准备好合并了。✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao wenshao merged commit 06345a2 into main Jun 13, 2026
98 checks passed
@wenshao

wenshao commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

🔬 Local runtime verification — Workflow P3 agent({schema, agentType, model, isolation}) (real CLI + workflow tool under tmux)

Verdict: ✅ PASS on the P3 feature logic — with one ⚠️ real bug: isolation worktrees are never auto-removed (they leak on disk).

I ran the real built CLI with the workflow tool enabled (via the pre-existing QWEN_CODE_ENABLE_WORKFLOWS=1 env gate — the PR's code runs unmodified), pointed at a deterministic mock LLM. The model calls the workflow tool with one script that probes every new agent() option; the workflow's real production dispatch spawned real subagents that hit the mock. Run in a clean git workspace and a dirty one.

Results — every agent() option behaves as documented

probe outcome observed
agent({schema}) ✅ returns validated object { "answer": "HELLO" } (subagent's structured_output args, AJV-validated, revived into the vm realm)
{schema} no tool call ✅ terminal error subagent completed without calling structured_output (no validation attempt — model produced plain-text content).
{schema} invalid args ×3 ✅ terminal error subagent completed without calling StructuredOutput (after 2 in-conversation nudges). ← the R3 (wenshao M2) message split works
{isolation:'remote'} ✅ throws agent({isolation:'remote'}) is not available in this build.
{agentType:'…nonexistent…'} ✅ throws agent({agentType}): agent type '…' not found.
{isolation:'bogus-mode'} ✅ throws agent({isolation: 'bogus-mode'}): unknown isolation mode. Known modes are: 'worktree', 'remote'.
{schema: <circular>} ✅ throws (security guard) agent() opts contain a non-JSON-serializable value: Converting circular structure to JSON …
{isolation:'worktree'} (clean tree) ✅ provisions + runs subagent in the worktree result returned; worktree at .qwen/worktrees/agent-<hex> on branch worktree-agent-<hex>
{isolation:'worktree'} (dirty tree) ✅ refuses parent working tree at … has uncommitted changes that would not propagate … Commit or stash the changes, then re-run.

So the schema/structured-output contract (both failure modes), agentType resolution + not-found, isolation routing/validation, the dirty-tree refusal, and the vm/host non-serializable security guard all work end-to-end through the real subagent stack. The node:vm opts-revival and the object→vm-realm return revival both hold (the schema probe returns a real validated object, not a string).

⚠️ Bug: isolation worktrees are never auto-removed — they leak on disk

The tool description promises "the worktree is auto-removed if no changes." It never is. My worktree subagent made zero changes, yet every run preserved the worktree and returned done\n\n[worktree preserved: …/agent-<hex> (branch worktree-agent-<hex>)]. Three runs left three worktrees + three worktree-agent-* branches behind until I removed them by hand.

Root cause (in writeWorktreeSessionMarker, packages/core/src/services/gitWorktreeService.ts — outside this PR's diff, but this PR's cleanup contract newly depends on it): it writes the .qwen-session exclude rule to git rev-parse --git-dir (the per-worktree gitdir), but git reads info/exclude from the common gitdir (--git-common-dir). For a linked worktree these differ, so the rule lands where git never looks. Proven directly:

# inside the provisioned worktree:
--git-dir        = …/.git/worktrees/agent-fc72cdc      ← code writes the exclude here
--git-common-dir = …/.git                              ← git actually reads from here
$ cat …/.git/worktrees/agent-fc72cdc/info/exclude  →  .qwen-session   (rule IS present)
$ git status --porcelain                            →  ?? .qwen-session  (still untracked!)
# move the SAME rule to the common exclude instead:
$ echo .qwen-session >> …/.git/info/exclude ; git status --porcelain  →  (empty — now ignored)

So the marker always shows untracked → hasWorktreeChanges() always returns truecleanupWorkflowWorktree always takes the "preserve" branch. The .qwen/worktrees/ dir (and a branch per call) accumulates indefinitely. The same writeWorktreeSessionMarker backs the existing AgentTool worktree path (agent.ts:1955), so this isn't introduced here — but P3 widens the blast radius, since a workflow can fan out many isolated subagents.

Fix: one word — resolve --git-common-dir instead of --git-dir when locating info/exclude in writeWorktreeSessionMarker. (Verified at the git level above; the orchestrator's cleanup is gated solely on hasWorktreeChanges/hasUnmerged, so a correctly-placed exclude makes the no-change path auto-remove.)

Observations / notes

  • The schema-failure message split is a nice touch and works exactly as the R3 review intended: no-attempt (plain text) vs 2-nudge exhaustion (3 invalid structured_output calls) produce distinct, accurate terminals.
  • agent({model}) rides the same createAgentHeadless override path as agentType/isolation (both verified); I did not assert it independently because the mock is model-agnostic — the request would just carry a different model id.
  • Feature is gated off in the product (no settings/CLI wiring for workflowsEnabled; only the QWEN_CODE_ENABLE_WORKFLOWS env gate exists), so none of this is user-reachable yet — consistent with a P3 building-block PR.

Reproduce

# real CLI, workflow tool enabled via the existing env gate, pointed at a mock LLM
QWEN_CODE_ENABLE_WORKFLOWS=1 OPENAI_BASE_URL=http://127.0.0.1:PORT/v1 OPENAI_API_KEY=sk-mock \
OPENAI_MODEL=mock-model QWEN_HOME=<isolated> \
  node packages/cli/dist/index.js --approval-mode yolo --output-format json -p "run the workflow"
# mock: main turn → workflow tool_call with a script that try/catches agent() with each option;
#       schema subagent → structured_output({answer}); BADARGS subagent → structured_output({wrong});
#       FAILTEST subagent → plain text. Run once in a clean git repo, once in a dirty one.
# observe: the returned summary (all 9 probes) + `.qwen/worktrees/agent-* ` left behind after a no-change run.

Verified on Linux, Node v22, git 2.47.3. Real subagents via production dispatch; deterministic stub model. The workflow ran under tmux against the built packages/cli/dist/index.js.


🇨🇳 中文版(点击展开)

🔬 本地真实运行验证 —— Workflow P3 agent({schema, agentType, model, isolation})(tmux 下真实 CLI + workflow 工具)

结论:✅ P3 功能逻辑全部通过 —— 但发现一个 ⚠️ 真实 bug:隔离 worktree 永远不会被自动清理(会在磁盘上泄漏)。

我运行了真实构建的 CLI,通过已存在的 QWEN_CODE_ENABLE_WORKFLOWS=1 环境开关启用 workflow 工具(PR 的代码未做任何改动),并指向一个确定性的 mock LLM。模型调用 workflow 工具,传入一个会探测每个新 agent() 选项的脚本;workflow 的真实 production dispatch 派生了真实的 subagent,它们都打到 mock。分别在一个干净的 git 工作区和一个有未提交改动的工作区里跑。

结果 —— 每个 agent() 选项都按文档行为表现

探测 结果 观察到的
agent({schema}) ✅ 返回校验后的对象 { "answer": "HELLO" }(subagent 的 structured_output 参数,经 AJV 校验,并 revive 回 vm realm)
{schema} 不调用工具 ✅ 终止错误 subagent completed without calling structured_output (no validation attempt — model produced plain-text content).
{schema} 参数非法 ×3 ✅ 终止错误 subagent completed without calling StructuredOutput (after 2 in-conversation nudges). ← R3(wenshao M2)的消息拆分生效
{isolation:'remote'} ✅ 抛错 agent({isolation:'remote'}) is not available in this build.
{agentType:'…不存在…'} ✅ 抛错 agent({agentType}): agent type '…' not found.
{isolation:'bogus-mode'} ✅ 抛错 agent({isolation: 'bogus-mode'}): unknown isolation mode. Known modes are: 'worktree', 'remote'.
{schema: <循环引用>} ✅ 抛错(安全护栏) agent() opts contain a non-JSON-serializable value: Converting circular structure to JSON …
{isolation:'worktree'}(干净) ✅ 创建并在 worktree 内运行 subagent 返回结果;worktree 在 .qwen/worktrees/agent-<hex>,分支 worktree-agent-<hex>
{isolation:'worktree'}(脏) ✅ 拒绝 parent working tree at … has uncommitted changes … Commit or stash the changes, then re-run.

所以 schema/结构化输出契约(两种失败模式)、agentType 解析与 not-found、isolation 路由/校验、脏树拒绝、以及 vm/host 的不可序列化安全护栏,全部通过真实 subagent 链路端到端跑通。node:vm 的 opts revive 和「对象返回值 revive 回 vm realm」都成立(schema 探测返回的是真实的校验对象,不是字符串)。

⚠️ Bug:隔离 worktree 永不自动清理 —— 在磁盘上泄漏

工具描述承诺「无改动时 worktree 会被自动移除」。但它从不移除。我的 worktree subagent 没做任何改动,但每次运行都保留了 worktree,并返回 done\n\n[worktree preserved: …/agent-<hex> (branch worktree-agent-<hex>)]。三次运行留下了三个 worktree + 三个 worktree-agent-* 分支,直到我手动删除。

根因(在 writeWorktreeSessionMarkerpackages/core/src/services/gitWorktreeService.ts —— 不在本 PR diff 内,但本 PR 的清理契约新依赖了它):它把 .qwen-session 的 exclude 规则写到了 git rev-parse --git-dir每个 worktree 私有的 gitdir),但 git 读取 info/exclude 是从公共 gitdir(--git-common-dir)读的。对于 linked worktree 这两者不同,所以规则写到了 git 根本不看的地方。已直接证明:

# 在创建出来的 worktree 内:
--git-dir        = …/.git/worktrees/agent-fc72cdc      ← 代码把 exclude 写在这里
--git-common-dir = …/.git                              ← git 实际从这里读
$ cat …/.git/worktrees/agent-fc72cdc/info/exclude  →  .qwen-session   (规则确实在)
$ git status --porcelain                            →  ?? .qwen-session  (仍然 untracked!)
# 把同一条规则改写到公共 exclude:
$ echo .qwen-session >> …/.git/info/exclude ; git status --porcelain  →  (空 —— 现在被忽略了)

所以 marker 永远显示 untracked → hasWorktreeChanges() 永远返回 truecleanupWorkflowWorktree 永远走「保留」分支。.qwen/worktrees/ 目录(以及每次调用一个分支)会无限累积。同一个 writeWorktreeSessionMarker 也支撑着现有的 AgentTool worktree 路径(agent.ts:1955),所以这不是本 PR 引入的 —— 但 P3 扩大了影响面,因为一个 workflow 可以 fan-out 出很多隔离 subagent。

修复: 一个词 —— 在 writeWorktreeSessionMarker 里定位 info/exclude 时用 --git-common-dir 而非 --git-dir。(上面已在 git 层面验证;orchestrator 的清理仅以 hasWorktreeChanges/hasUnmerged 为判据,所以把 exclude 放对位置后,无改动路径就会自动移除。)

其他观察

  • schema 失败消息的拆分很到位,完全符合 R3 review 的意图:未尝试(纯文本)与 2 次 nudge 耗尽(3 次非法 structured_output 调用)会产生不同且准确的终止消息。
  • agent({model}) 走的是与 agentType/isolation 相同的 createAgentHeadless override 路径(两者都已验证);我没有单独断言它,因为 mock 与 model 无关 —— 请求只会带一个不同的 model id。
  • 该功能在产品中是关闭的(没有 workflowsEnabled 的 settings/CLI 接线,只有 QWEN_CODE_ENABLE_WORKFLOWS 这个环境开关),所以目前用户还触达不到 —— 这与一个 P3 基建型 PR 是一致的。

复现

# 真实 CLI,通过已有环境开关启用 workflow 工具,指向 mock LLM
QWEN_CODE_ENABLE_WORKFLOWS=1 OPENAI_BASE_URL=http://127.0.0.1:PORT/v1 OPENAI_API_KEY=sk-mock \
OPENAI_MODEL=mock-model QWEN_HOME=<隔离目录> \
  node packages/cli/dist/index.js --approval-mode yolo --output-format json -p "run the workflow"
# mock:主回合 → 返回 workflow tool_call,脚本里对每个选项 try/catch 调用 agent();
#       schema subagent → structured_output({answer});BADARGS subagent → structured_output({wrong});
#       FAILTEST subagent → 纯文本。在干净 git repo 跑一次,在脏 repo 跑一次。
# 观察:返回的汇总(全部 9 个探测)+ 无改动运行后仍留在 `.qwen/worktrees/agent-*` 的 worktree。

在 Linux、Node v22、git 2.47.3 上验证。通过 production dispatch 跑真实 subagent;确定性桩模型。workflow 在 tmux 下针对构建产物 packages/cli/dist/index.js 运行。

doudouOUC pushed a commit that referenced this pull request Jun 15, 2026
…'worktree'}) (#4721) (#5034)

* feat(core): Workflow P3 — agent({schema, agentType, model, isolation:'worktree'}) (#4721)

Adds the P3 dispatch options to the workflow runtime, completing the
contract qwen-code's workflow tool matches against upstream Claude Code
2.1.168. P1/P2 stubs (workflow-sandbox.ts:508-527) are replaced with
production paths routed through `SubagentManager.createAgentHeadless` so
per-call model overrides go through `buildRuntimeContentGeneratorView`
(provider routing), per-agent MCP servers / hooks get isolated
lifecycles, and worktree-isolated subagents run against a rebound Config.

- agent({agentType: 'X'}) resolves against the declarative-agents
  registry (#4842 + #4996) via findSubagentByName; unresolved names throw
  "agent({agentType}): agent type 'X' not found" verbatim from upstream.
- agent({model: 'qwen3-max'}) is threaded into SubagentConfig.model so
  the runtime view sees it (modelConfigOverrides alone would only swap
  the model name within the existing provider's view).
- Workflow's disallowed-tool floor [SendMessage, ExitPlanMode] is unioned
  with the agentType's own disallowedTools so a permissive agentType
  cannot re-enable them for a workflow subagent.
- agent({isolation: 'worktree'}) provisions a fresh worktree via
  GitWorktreeService.createUserWorktree (slug agent-<7hex>, mirrors
  AgentTool 1849-1963), rebinds cwd/getTargetDir/getFileService/
  getWorkspaceContext on a prototype-chained Config override, and on
  completion auto-removes the worktree if clean or preserves the path +
  branch (appended to the result string) when the subagent left changes.
  Parent-dirty trees are refused with a clear error to avoid silently
  running the subagent against a stale HEAD.
- agent({isolation: 'remote'}) throws "agent({isolation:'remote'}) is
  not available in this build" verbatim (upstream 2.1.168 parity).
- agent({schema: S}) injects a per-call SyntheticOutputTool (existing
  tools/syntheticOutput.ts, AJV-backed) into a fresh per-subagent
  ToolRegistry built via rebuildToolRegistryOnOverride, then watches
  AgentEventEmitter TOOL_CALL/TOOL_RESULT events for `structured_output`
  invocations. A successful call's args are captured as the dispatch
  return value (object, not string); after two failed attempts the
  third failure aborts the dispatch and throws "subagent completed
  without calling StructuredOutput (after 2 in-conversation nudges)"
  verbatim. No agent-core.ts changes — the entire 2-nudge counter
  lives in the dispatch layer so the shared subagent loop is unaffected.

The sandbox's agent() wrapper now revives per-call object returns into
the vm realm (JSON round-trip inside the vm runInContext block), closing
the same T1/T8/T14 host-prototype-escape vector that P2's per-element
revival closed for parallel/pipeline. Two new sandbox security tests
(constructor-chain probe + non-JSON-serializable collapse) regress this.

WorkflowAgentResult widens from `string` to `string | object`; the
fast-path (no agentType/model/isolation/schema) is preserved byte-for-byte
to keep P1/P2 zero-overhead.

Tests: 159 workflow-suite tests + 217 adjacent (subagents / syntheticOutput /
agent-override) all green. Real-LLM E2E follow-up planned (mirroring P2's
13/13 qwen3-max validation).

Related #4721 (parent design — multi-phase, not closed by this PR)
Related #4732 (P1 merged) #4947 (P2 merged) #4842 #4996 (declarative agents)

* chore(core): P3 self-review R1 — align worktree suffix wording + 6 test gaps

R1 of pre-push adversarial self-review on PR #5034 surfaced 6 confirmed
findings across 6 diverse lenses (correctness / security / reuse-altitude
/ self-invariant / consumer-breakage / test-gaps). Each finding faced 2
independent skeptics defaulting to refuted=true; 6 survived majority
challenge.

Source code:
- Worktree-preserved suffix wording now matches AgentTool's
  formatWorktreeSuffix (agent.ts:1700-1719) verbatim, including the
  `git worktree add <path> <branch>` recovery hint for the directory-
  removed-but-branch-preserved race.

Test gaps closed:
- schema-mode success after 1 nudge (round-2 args captured)
- schema-mode success after 2 nudges (round-3 args captured)
- schema-mode + agentType together — floor disallowedTools still unioned
- schema-mode caller-abort takes priority over the StructuredOutput
  terminal error (signal.aborted check at workflow-orchestrator.ts:489-490)
- override path dispose() runs in finally on the success path
- override path dispose() runs in finally on the terminate-mode-error path

Declined R1 finding: negative tests for invalid opt types (schema/model/
agentType passed null/number/empty-string). Adding upfront type
validation is scope creep — upstream does not, P1/P2 do not, and the
workflow tool is model-authored where these inputs are extremely
unlikely. Existing AJV / SubagentManager downstream errors are descriptive
enough. Will revisit if R2 makes a stronger case.

166/166 tests pass (workflow suite + adjacent + workflow-orchestrator).
typecheck + lint clean across packages/core, packages/cli,
integration-tests, sdk, webui.

* chore(core): P3 self-review R2 — vm-realm opts revive + error-msg sanitize + 12 tests

R2 of pre-push adversarial self-review on PR #5034. 6 diverse-lens
finders (60 agents, ~2.5M tokens, 24 min) over the R1-fix-applied
code, with 2 independent skeptics defaulting to refuted=true.
12 confirmed survivors after adversarial verify; decisions below.

Security (FIX):
- agent() wrapper in workflow-sandbox.ts now JSON-revives agentOpts
  inside the vm runInContext block BEFORE passing them to the host
  dispatch. Closes a Proxy/inherited-getter escape that P3 introduced
  along with the user-supplied schema object: a script could have
  wrapped agentOpts.schema in a Proxy whose getter ran host-side code
  during SyntheticOutputTool construction / AJV compile. Same
  mechanism as args / parallel-result revival.
- runOverridePath now sanitizes opts.agentType through
  sanitizeForErrorMessage() (control chars → space) before
  interpolation into the "agent type 'X' not found" error message.
  Prevents a model-authored agentType containing CRLF / NUL from
  fragmenting a single-line error across log records / OTLP fields.

Reuse-altitude (FIX):
- Added JSDoc block to WorkflowWorktreeIsolation interface
  documenting each field's role for cleanup.

Test gaps (FIX, 12 new tests):
- agentType control-char sanitization regression
- dispose() runs in finally when subagent.execute throws
- isolation:'worktree' provision error branches (5):
  nested parent / git unavailable / not a git repo / parent dirty /
  createUserWorktree returns failure
- isolation:'worktree' cleanup branches (3):
  removeUserWorktree fails / branchPreserved race / removeUserWorktree
  throws — each preserves the worktree (or branch) with the right
  user-facing suffix
- combinations (2): model + isolation:'worktree' threads model AND
  provisions worktree; schema + isolation:'worktree' returns
  structured payload verbatim (preserved suffix only on string return)

Test infrastructure: vi.mock'd GitWorktreeService at the module level
(partial mock; preserves the existing exports the unrelated
worktreeCleanup.ts depends on) with a per-test beforeEach reset.

Declined R2 findings (kept the R1 line):
- [major] Schema parameter upfront validation: same scope-creep
  decline as R1. Upstream doesn't do it; AJV's downstream error is
  descriptive enough.
- [major] Worktree provision extracted to shared util with AgentTool:
  agreed in principle but out of P3 scope. A separate refactor PR
  should land that with AgentTool maintainers in the loop.

178/178 tests pass (workflow + adjacent suites). typecheck + lint
clean across packages/core, packages/cli, integration-tests, sdk,
webui.

* fix(core): address wenshao R1+R2 review on Workflow P3 (PR #5034)

Round 1 (15:41) + Round 2 (17:24) review from wenshao surfaced 7 inline
findings across schema-mode dispatch correctness, worktree cleanup
coverage, and error attribution. Each fix is paired with a regression
test that was RED before the change landed.

T0 [Critical] Worktree leak when schema setup throws after provision
  workflow-orchestrator.ts: outer try MOVED to start immediately after
  provisionWorkflowWorktree. Previously the try opened only after
  createSchemaConfigOverride / createSchemaModeState / signal listener
  attachment — so any throw in those three (broken MCP server during
  the per-call ToolRegistry rebuild was the trigger wenshao cited)
  orphaned the just-provisioned worktree under .qwen/worktrees/.
  Test: "isolation:'worktree' + schema setup throws → worktree is
  still cleaned up" — simulates createToolRegistry failure during
  createSchemaConfigOverride; asserts removeUserWorktree was called.

T1 [Critical] / T4 [H1] agentType + schema silently dead-ended
  workflow-orchestrator.ts: schema-mode augmented config now (a)
  appends ToolNames.STRUCTURED_OUTPUT to baseConfig.tools when the
  allowlist is restricted (no '*' and doesn't already contain it), so
  prepareTools / getFunctionDeclarationsFiltered doesn't filter
  structured_output out of the subagent's surface; (b) preserves the
  resolved agentType's persona by APPENDING the schema-contract
  instruction block instead of replacing the systemPrompt outright.
  Replace remains only on the ephemeral no-agentType path where
  baseConfig.systemPrompt IS WORKFLOW_SUBAGENT_SYSTEM_PROMPT (schema
  variant is its strict superset; avoids two near-identical prompts).
  Tests: structured_output appears in the allowlist alongside the
  agentType's existing tools; persona prompt is contained in the
  effective systemPrompt.

T2 [Suggestion] / T5 [M1] Parent-abort listener leaked per schema call
  workflow-orchestrator.ts: named listener stored at outer scope,
  removed in the outer finally regardless of how the dispatch ended.
  Previous `{ once: true }` only auto-removed on actual parent abort;
  the happy-path schema dispatch — success capture / 3-failure abort
  fires the CHILD controller without the parent ever aborting — left
  the listener stuck on the per-run signal. With N schema calls per
  workflow N listeners + N child-controller closures accumulated.
  Test: 5 sequential schema dispatches over the same parent signal
  end with zero live listeners.

T6 [M2] Terminate mode misdiagnosed as nudge exhaustion
  workflow-orchestrator.ts: schema path now distinguishes
  terminateMode before attributing failure to schema mode. TIMEOUT /
  MAX_TURNS / ERROR throw the existing "did not complete (terminate
  mode: X)" message that the non-schema path uses. Only the actual
  schema-failure cases produce schema wording, and those are split:
  attempts > 2 keeps the upstream-verbatim "(after 2 in-conversation
  nudges)" wording; attempts === 0 throws an accurate "no validation
  attempt — model produced plain-text content" instead of misleadingly
  citing nudges that never happened. (The existing 0-call test was
  updated to match the new accurate message; the 3-failure test
  retains the verbatim wording.)
  Tests: parametric over TIMEOUT/MAX_TURNS/ERROR asserting "did not
  complete"; companion test pinning the verbatim wording to the
  3-failure path.

T3 [Suggestion] Schema-mode JSON revival sentinel — clarified
  workflow-sandbox.ts: added a block comment documenting that the
  JSON-round-trip + null-on-throw is a SECURITY backstop (errors-as-data
  convention from parallel/pipeline) rather than a contract path —
  unreachable in production schema mode because the host return is
  LLM tool_call args, always JSON-serializable. No behavior change.

Tests: 75/75 orchestrator + 111/111 sandbox/tool/limiter green.
typecheck + lint clean across packages/core and packages/cli.

R1+R2 self-review commits (e1c5ec7 / 62624a9) precede this commit
on the same branch — they predate wenshao's review and address
distinct findings; reviewer L1 (worktree-lifecycle unit coverage) is
already closed by R2's 11 worktree tests.
LaZzyMan added a commit that referenced this pull request Jun 16, 2026
…5094)

* feat(core): Workflow P4a — extractAndStripMeta + meta on RunOutcome (#4721)

First half of P4 (per the refined #4721 plan). Extracts the script's
`export const meta = {...}` declaration into a typed object so the
workflow tool's display payload, and the future /workflows command +
phase-tree UI, can read it without re-parsing the script source. The
other half of P4 (slash command + KIND_NAMES extension + phase-tree
UI + WorkflowTaskRegistry) is queued as a follow-up PR.

Architecture: reuse the P1 brace-walker (zero-dep, no parser deps) to
locate the meta object literal's source range, then evaluate the literal
inside a fresh `vm.createContext(Object.create(null))` — null-prototyped
globalThis, no host bridge (no `args` / `process` / `require` / workflow-
sandbox globals). The vm realm still exposes its OWN intrinsics
(`Object` / `Math` / `Date` / `JSON`), which is fine: meta extraction is
one-shot at tool invocation, not replayed on resume. validateMeta walks
the eval result field-by-field and copies into a fresh host-realm plain
object — no JSON round-trip needed because every contract field is a
primitive.

User-visible additions:
- `extractAndStripMeta(source)` exported from workflow-sandbox.ts
- `WorkflowMeta` interface (`{ name, description, whenToUse?, phases?: Array<{title, detail?, model?}> }`) — verbatim shape from upstream Claude Code 2.1.168
- `WorkflowSandbox.getMeta()` accessor alongside `getPhases()` / `getLogs()`
- `WorkflowRunOutcome.meta: WorkflowMeta | null` (non-breaking add)
- `WorkflowExecutionError.meta: WorkflowMeta | null` so the failure
  display shows the workflow's name / description / phases even when
  the script body throws
- `WorkflowTool.execute` adds `meta` to the returnDisplay payload when
  present (omitted when the script had no meta)

Error messages verbatim from upstream where applicable:
- `meta.name must be a non-empty string`
- `meta.description must be a non-empty string`

Refactor: P1's `stripExportMeta` is preserved as a thin wrapper around
a new `findMetaBlockBounds` helper that both old and new functions
share. All 86 existing sandbox tests pass unchanged (no behavior
regression in the strip path).

Tests:
- 11 new `extractAndStripMeta` unit tests covering happy path, optional
  fields, missing-required validation, malformed shape, vm-eval failure,
  null-prototype globalThis (no `args` / `process` / `require`), and
  unbalanced braces
- 3 new `createWorkflowSandbox.getMeta()` integration tests
- 3 new `WorkflowOrchestrator` outcome.meta tests (null path, parsed path,
  meta-survives-body-throw on the error path)
- 3 new `WorkflowTool` display payload tests (meta in payload, omitted
  when absent, present on failure path)

Suite: 207/207 workflow + adjacent regression green; typecheck +
lint clean on packages/core. (Pre-existing acp test type errors in
packages/cli are unrelated; CI will confirm.)

Related #4721 (parent design — multi-phase, not closed by this PR)
Related #4732 (P1) #4947 (P2) #5034 (P3) — all merged
P4b follow-up: /workflows command + TaskKind workflow union + BackgroundTasksPill KIND_NAMES + phase-tree UI + WorkflowTaskRegistry

* test(core): close P4a adversarial-review gaps + add real-LLM E2E (#4721)

After PR #5094 opened without an E2E run, ran a 3-lens adversarial
review (correctness / security / completeness) of the meta-extraction
assertion strength against extractAndStripMeta and the meta-on-outcome
threading path. All 3 reviewers refuted the claim that the existing
assertions catch realistic regressions. Triage:

- 18 of 24 findings already covered by workflow-sandbox.test.ts
  (string-with-brace, comments-inside-meta, phases[].model,
  missing-description error text, args/process/require unreachability,
  Promise/Math.constructor escape, etc.)
- 4 findings (regex literal / template literal / `/m` flag / spread)
  are host-side parse-path branches the brace walker handles
  structurally but without explicit negative tests
- 2 truly novel gaps closed here:

  1. HIGH × 3 lenses: a regression in validateMeta that returns the
     vm-realm `raw` value directly (skipping the host-realm copy at
     workflow-sandbox.ts:283-294) would re-open T1/T8/T14 realm
     escape via outcome.meta.constructor.constructor('return process')().
     Vitest toEqual is structural and does NOT check prototype
     identity, so every prior assertion in the suite would still pass.
     Add returned-meta + phases array + phase entries prototype-
     identity check in workflow-sandbox.test.ts; mirror end-to-end
     in the live test's scenario A.

  2. MEDIUM: meta-shaped result collision — if a script returns
     `{ name, description, phases }`, the safeStringifyDisplayPayload
     spread must keep `meta` and `result` distinct at the top level.
     Add a workflow.test.ts case that returns a meta-shaped object and
     asserts both display.meta and display.result hold their own
     distinct values.

Also add the real-LLM E2E harness at workflow-p4a-meta-live.live.test.ts
(6 scenarios: meta+agent, no-meta, malformed-meta short-circuit,
body-throw with meta preservation, parallel() fan-out with meta phases,
pipeline() multi-stage with meta phases). The suite is gated by
DASHSCOPE_API_KEY — describe.skip when absent, so CI without the
env shows 0 tests in this file rather than failing. Verified locally
6/6 against qwen3-coder-plus via DashScope OpenAI-compatible endpoint.

Final test count: 129/129 (89 sandbox + 34 tool + 6 live).

* fix(core): P4a meta-literal Promise crash + live-test typecheck + prettier (#4721)

Round 3 review fixes:

1. **(Critical, wenshao R1)** A Promise — typically from `import('node:fs')`
   inside a meta literal — used to crash the host process. `runInContext`
   evaluates the literal synchronously and returns; `validateMeta` drops
   the non-contract field silently; the workflow returns its result;
   THEN the dangling unhandled rejection terminates the process under
   Node's default `--unhandled-rejections=throw`, decoupled from the run
   that triggered it. Wenshao reproduced on Node 22.22 with:

       export const meta = { name:'x', description:'d', extra: import('node:fs') }
       return 1
       → run returns 1, process exits with code 1.

   Mitigation: after `vm.Script(...).runInContext(...)`, walk the eval
   result recursively, call `.catch(() => {})` on any thenable to mark
   the rejection handled, and throw an explicit
   "meta values must not be Promises" so the malformed meta is rejected
   before validation continues. Recursion covers `phases[]` entries
   embedding `import()` below the top level. Two RED-first regression
   tests in workflow-sandbox.test.ts (top-level + nested-in-phases).

2. **(Critical, wenshao R2/R3)** `tsc --noEmit` failed with 11 errors in
   the new live E2E test file, blocking CI Lint + all 3 Test jobs:

   - TS2459 (L36): `WorkflowAgentOpts` is exported from
     `workflow-sandbox.js`, not from `workflow-orchestrator.js` —
     fixed import path.
   - TS2322 (L108/165/220): typing `liveDispatch` as
     `WorkflowAgentDispatch` widens the return to `string | object`,
     which doesn't fit `lastText: string`. Dropped the type annotation;
     the inferred `Promise<string>` is still assignment-compatible with
     `WorkflowAgentDispatch` (string ⊂ string | object).
   - TS2345 (×6): `WorkflowRunRequest.args` is required (`args: unknown`,
     not optional). Added `args: undefined` to every `orch.run({ script })`
     call.

3. Prettier: `--write` on the 4 touched files. R2 also flagged this;
   pre-commit lint-staged would normally cover it but the live test
   file's TS errors short-circuited it.

Final local verification:
- `tsc --noEmit`: 0 errors
- 209/209 tests pass across workflow-sandbox + workflow-orchestrator +
  workflow.test.ts + live (6 scenarios against qwen3-coder-plus via DashScope)

* feat(core+cli): Workflow P4b — /workflows command + phase-tree UI + WorkflowRunRegistry (#4721)

P4b completes phase P4 of the Dynamic Workflows port. P4a (already on
this branch, commits 5b56c39 / 55c23a0 / 402df8f) locked the
meta contract: outcome.meta / err.meta / display payload. P4b adds the
consumer side — visible workflow runs in the TUI.

## Core (4 changes, 1 new file)

- `TaskKind` widened in `packages/core/src/agents/tasks/types.ts` from
  3 → 4 variants (adds `'workflow'`). `TaskState` union picks up
  `WorkflowTask` automatically.
- New `WorkflowRunRegistry` (`packages/core/src/agents/workflow-run-
  registry.ts`) — sibling of `BackgroundTaskRegistry` /
  `BackgroundShellRegistry` / `MonitorRegistry`. Same register / cancel /
  get / list / on('statusChange') shape; per-kind state holds runId,
  meta, current phase, phase history, dispatch counters, recent logs.
  Eviction: `MAX_RETAINED_TERMINAL_WORKFLOWS = 10` mirrors monitor cap.
- `Config.getWorkflowRunRegistry()` exposed via the same Object.create
  override pattern as the other registries.
- `WorkflowOrchestratorEmitter` interface added to workflow-sandbox.ts —
  fires `phaseStarted` (from sandbox safePhase), `agentDispatched` /
  `agentCompleted` (from orchestrator countedDispatch), and
  `logAppended` (from sandbox safeLog). Defensive try/catch around
  every emit so a subscriber error never bubbles into the script.
  Orchestrator accepts optional `runId` in WorkflowRunRequest so
  callers can pre-generate the id and register the run BEFORE run()
  resolves.
- `WorkflowTool` now registers the run with the registry at execute()
  start, wires the emitter to the registry's update methods + the
  tool's _updateOutput callback, flips `canUpdateOutput` to `true`
  for live phase-tree rendering, and routes terminals to
  registry.complete / fail / cancel (cancel on signal.aborted so
  user intent stays distinct from script bugs).
- `WorkflowRunRegistry` exported from core/index.ts.

## CLI (5 changes, 2 new files)

- `BackgroundTasksPill.tsx` `KIND_NAMES` gains `workflow:
  { singular, plural }`. Counts accumulator + sort order updated:
  `shell → agent → monitor → workflow → dream` (user-initiated
  before system-initiated).
- `useBackgroundTaskView.ts` subscribes to the workflow registry
  alongside the existing three; `entryId` switch adds
  `case 'workflow': return entry.runId`; cleanup unsubscribes.
- `BackgroundTasksDialog.tsx` adds `WorkflowDetailBody` (inline,
  matches MonitorDetailBody style) — renders workflow name,
  description, status, runtime, current phase, agent dispatch
  counts (M/N), the phase tree (capped at MAX_VISIBLE_PHASES=20
  with "+N more above"), and the log tail (capped at
  MAX_VISIBLE_LOG_LINES=10). rowLabel switch surfaces
  `[workflow] <name> · <phase> (M/N)`. DetailBody + statusVerb
  switches gain workflow cases.
- `BackgroundTaskViewContext.tsx` cancelSelected: `case 'workflow':
  registry.cancel(runId, Date.now())`. Idempotent with the
  WorkflowTool's signal.aborted catch path.
- `BackgroundTasksDialog.test.tsx` entryId mock gains workflow case.

## New /workflows slash command

- `packages/cli/src/ui/commands/workflowsCommand.ts` + tests.
  Bare `/workflows` lists active + completed runs (running first,
  then terminal by endTime DESC). `/workflows <runId>` opens a
  per-run detail dump (meta block, status, runtime, phase tree,
  recent logs, errors).
- Gated by `Config.isWorkflowsEnabled()` in BuiltinCommandLoader —
  command vanishes from typeahead when the flag is off. Already-
  defined env-var overrides (`QWEN_CODE_ENABLE_WORKFLOWS` opt-in,
  `QWEN_CODE_DISABLE_WORKFLOWS` kill switch) inherited for free.
- Interactive mode adds a "Tip: focus the Background tasks pill"
  redirect; non-interactive / acp modes omit the tip since they
  have no dialog.

## Scope deferrals

- **ACP daemon protocol widening** (acp-bridge bridgeTypes / status /
  tasksSnapshot) is deferred to a follow-up PR. Workflows remain
  invisible to SDK + web-shell consumers in P4b; the CLI-internal
  surface is complete.
- **Phase-tree token rollup** (per-phase token totals in the detail
  body) needs P5's budget tracker. The infrastructure (registry
  records, emitter fire sites) is ready for the column when P5 lands.
- **Save / inspect subcommands** (`/workflows save <runId>` to
  materialize a script) are future enhancements; the slash command
  ships with list + detail only.

## Verification

- 223/223 tests pass on the workflow surface — 14 new registry tests,
  6 real-LLM E2E scenarios against qwen3-coder-plus (DashScope),
  plus all existing P3/P4a tests continuing to pass with the new
  emitter wiring.
- 73/73 CLI tests pass across BackgroundTasksPill, BackgroundTasks
  Dialog, useBackgroundTaskView, workflowsCommand.
- `tsc --noEmit` clean (0 P4b errors in core + cli).
- `prettier --check` + `eslint` clean on all touched files.

* fix(core): bound rejectThenablesInMeta against cyclic meta input (#4721)

Round 4 review fix.

**(Suggestion, wenshao R4)** The R3 thenable walker recursed without a
cycle guard. A meta literal that builds a cyclic object via spread
overflows the call stack:

    export const meta = {
      name: 'x',
      description: 'y',
      ...(function () { const a = {}; a.self = a; return a; })(),
    }

vm-eval returns the cyclic object cleanly; `rejectThenablesInMeta`
walks `Object.values(...)` and recurses into `a.self === a` forever,
producing `RangeError: Maximum call stack size exceeded`. The
walker exists to reject Promises before they leave a dangling
rejection, but the walk itself must terminate on any shape vm-eval
can return — not just on the happy-path acyclic shape.

Fix: thread an optional `seen = new WeakSet<object>()` parameter,
early-return on `seen.has(value)`. Bounds the recursion against
both cycles AND shared subgraphs (where the same node is reached
through multiple keys), and keeps the walker O(N) on the eval'd
size.

Two RED-first regression tests:
- Direct self-reference via spread: `{ ...{self: itself} }`
- Cycle reached through nested arrays/objects: `{ ...{items: [{ref: outer}]} }`

Both previously threw RangeError; both now succeed (validateMeta
silently drops the non-contract `self` / `items` fields, so the
returned meta is just `{ name, description }` — only reachable if
the walker terminates first).

`validateMeta` does NOT recurse into nested objects (it walks the
top-level contract fields + iterates `phases[]` one level deep with
direct property access), so no sibling drift — only the thenable
walker needed the guard.

* test(cli): stub isWorkflowsEnabled in BuiltinCommandLoader mock config (#4721)

CI fix for c3f9d84 (Workflow P4b).

P4b added a gated `workflowsCommand` to BuiltinCommandLoader:

    this.config?.isWorkflowsEnabled() ? workflowsCommand : null,

The optional-chain only guards `config` being null/undefined — once
config is truthy, `.isWorkflowsEnabled()` invokes the method directly.
The existing `mockConfig` in BuiltinCommandLoader.test.ts stubbed
`isLspEnabled` / `getFolderTrust` / `getManagedAutoMemoryEnabled`
but never `isWorkflowsEnabled`, so the new call hit `undefined()` and
threw `TypeError: this.config?.isWorkflowsEnabled is not a function`.
10 tests (×3 OS) red on `e9ad07683` for this single reason.

Add `isWorkflowsEnabled: vi.fn().mockReturnValue(false)` to the mock,
matching the existing pattern. Default to `false` so the loader does
not add `workflowsCommand` to the assertions that count exact builtin
output — those tests are unchanged.

* test(core): cover P4b registry integration + orchestrator emitter (#4721)

Round 5 fix for the two Critical findings on the P4b commit.

## workflow.test.ts — registry integration seam (+3 tests)

\`fakeConfig()\` returns \`{}\`, so \`config.getWorkflowRunRegistry?.()\`
short-circuits to undefined in every existing test. The whole P4b
integration path inside \`WorkflowTool.execute()\` — \`register()\` on
start, the emitter closure firing into the registry, post-run
\`complete()\`, catch-arm \`fail()\` / \`cancel()\` branching — is
never exercised.

Add a \`configWithRegistry()\` helper that builds a config holding a
real \`WorkflowRunRegistry\` and returns the registry handle for
inspection. Three new tests pin:

- **success path**: registry entry transitions to \`completed\` with
  meta synthesised from \`meta.name\` (the tool fast-tracks
  description = meta.name when default = runId), correct phases
  array, agent counts \`1/1\`, script result mirrored, \`endTime\` set.
- **failure path**: registry entry transitions to \`failed\`, error
  message recorded verbatim, phases up to the throw preserved.
- **abort path**: pre-aborted signal causes the catch arm to record
  \`cancelled\` (not \`failed\`) so the dialog distinguishes
  user-initiated stops from script bugs.

## workflow-orchestrator.test.ts — emitter callbacks (+3 tests)

The \`emitter\` field on \`WorkflowRunRequest\` and its firing sites
(sandbox \`safePhase\` / \`safeLog\`, orchestrator \`countedDispatch\`
before + after) are the only channel keeping the registry record in
sync with the live run. Three new tests pin:

- **happy-path ordering**: with all four callbacks wired to an event
  log, the script \`phase('Plan') → log('starting') → agent →
  phase('Build') → agent\` emits seven events in expected order
  with expected payloads (\`label\` threaded through both
  \`agentDispatched\` and \`agentCompleted\`).
- **rejection path**: dispatch throwing \`dispatch-boom\` fires
  \`agentCompleted(label, 'dispatch-boom')\` — pins the symmetric
  emit-on-throw at workflow-orchestrator.ts:1124 catch arm.
- **defensive try/catch**: every callback throwing should be
  swallowed so orchestration still completes. Pins each emit site's
  try/catch wrapper individually.

Total: 6 new tests, all green. typecheck 0, prettier clean.

* fix(core+cli): R7 review fixes — 6 substantive + tmux re-verified (#4721)

Wenshao's R7 review (with real build + tmux verification on the
merged state) approved the PR but surfaced 6 valid findings. All
fixed, all RED-first tested, all verified end-to-end against
qwen3-coder-plus via DashScope.

## 1. Dialog-cancel drops accumulated logs (Critical)

`registry.setRecentLogs(...)` previously guarded `status === 'running'`
only. Dialog-initiated cancel marks `status='cancelled'` synchronously
BEFORE the tool's catch arm tries to write logs — so cancelled runs
always showed an empty Logs section in the dialog. Allow the write
after a `'cancelled'` transition too; keep `'completed'` and `'failed'`
as final-state rejects. 2 regression tests: cancel-then-logs-still-
writes, and complete/fail-still-reject.

## 2. WorkflowRunRegistry missing session-reset wiring (Critical)

Sibling drift miss from P4b: `BackgroundTaskRegistry` /
`BackgroundShellRegistry` / `MonitorRegistry` all exposed `reset()` +
`abortAll()`, and `backgroundWorkUtils` (`hasBlockingBackgroundWork`,
`resetBackgroundStateForSessionSwitch`) wired all three. `Workflow
RunRegistry` had neither. Result: `/clear` and session-resume ran
while a workflow was mid-run (orphaned dispatch loop), and terminal
rows leaked from session to session in the pill / dialog /
`/workflows` list.

Added `hasRunningEntries()`, `reset()` (drops entries, no controller
touch), `abortAll()` (cancels every running entry + aborts its
controller). Wired into both `backgroundWorkUtils` helpers + updated
their tests for the 4-sibling shape.

## 3. Phase dedup inconsistency — sandbox vs registry (Critical)

`phase('X'); phase('X')` previously yielded `outcome.phases = ['X','X']`
(sandbox `safePhase` unconditional push) but `entry.phases = ['X']`
(registry `onPhaseStarted` collapsed). The same run showed different
phase counts in the terminal `returnDisplay` JSON vs the live UI.
The `agent({phase})` wrapper already deduped (`__b.lastPhase()`); my
docstring on `safePhase` claimed it deduped too, but it didn't.

Fix at the sandbox layer (single source of truth): `safePhase` skips
when `phases[last] === t`. Registry-side dedup is now redundant but
harmless (defense in depth, doesn't double-collapse). Updated test
in workflow-sandbox.test.ts pins
`phase('X'); phase('X'); phase('Y'); phase('X')` → `['X','Y','X']`.

## 4. /workflows tip pointed at non-functional path (UX/docs)

`/workflows` tip text said *"focus the Background tasks pill in the
footer (use ↓ from an empty composer) and press Enter for the
interactive dialog with phase tree + live updates."* But
`setPillFocused(true)` doesn't exist anywhere in the codebase
(confirmed by wenshao's grep, and reproducible on my own tmux runs
where `↓ Enter` never opened the dialog — I previously misattributed
to a tmux limitation). The dialog IS reachable through other paths
but the tip's specific instructions are wrong.

Soften the tip to point at the actually-working text-mode detail
view: `Tip: use /workflows <runId> for the per-run detail view
(name, description, phase tree, recent logs).` — same information,
working instructions.

## 5. `runId` validation comment was aspirational (docs)

Comment on `workflow-orchestrator.ts` `run()` claimed *"validates
the shape (`wf_<hex>`)"* but the code is `const runId = req.runId
?? generateRunId();` — no validation. Caller (`WorkflowTool`) does
use the same `wf_<8hex>` generator as `generateRunId()`, so the
behavior is safe in practice. Fixed the comment to describe what
the code actually does (trusts the caller, no validation).

## 6. Duplicate extractAndStripMeta test (test hygiene)

Two tests at workflow-sandbox.test.ts:227 and :242 used identical
source `{ name: args.x, description: 'd' }` — copilot R1 originally
flagged this, I declined as bot finding, wenshao re-confirmed. The
intent was to pin two distinct things: (a) generic unknown
identifier throws, (b) the bridge global `args` specifically is
not reachable. Updated the first test to use `totallyUnknown` (a
genuine unknown name) and kept the second as the explicit `args`
regression — now the two tests pin different things.

## Verification

- 231/231 core workflow tests pass (registry + sandbox + orchestrator
  + tool) — +6 new from R7 RED-first regression tests
- 81/81 CLI ripple tests pass (pill + dialog + hook + command +
  backgroundWorkUtils)
- tsc 0 errors on core + cli (after rebuilding core dist for the
  new registry methods)
- prettier + eslint clean on all touched files
- **tmux re-verified end-to-end** against qwen3-coder-plus via
  DashScope on a fresh `npm run bundle`:
  - Phase dedup: `phase("Phase A"); phase("Phase A")` →
    `outcome.phases = ["Phase A", "Phase B"]` (was `["Phase A",
    "Phase A", "Phase B"]` pre-fix) — confirmed both in the tool
    result JSON AND in `/workflows wf_xxx · 2 phases`
  - New `/workflows` tip text rendered as expected, no more
    advertising broken pill focus path
  - `/workflows <runId>` detail dump still works: name, status,
    runtime, phases tree, agent counts

* test(core): R7 dialog-cancel race integration test (#4721)

The unit test in workflow-run-registry.test.ts pins the setRecentLogs
guard widening in isolation. This integration test stands up the full
production wiring — real WorkflowTool, real WorkflowRunRegistry, real
sandbox, real emitter — and reproduces the exact dialog-cancel race
that the R7 fix targets:

1. Start execute() with a controllable dispatch that hangs until
   externally rejected.
2. Wait for the run to register + the dispatch to be in flight + at
   least one log() call to have accumulated.
3. Simulate the dialog: call registry.cancel(runId) directly. This
   is the exact entry point cancelSelected() uses in
   BackgroundTaskViewContext.cancelSelected for kind='workflow'.
4. Cascade the dispatch rejection (the production path: the registry's
   abortController abort propagates through dispatchController → the
   orchestrator's limiter → the in-flight dispatch).
5. Await execute() — the tool's catch arm runs setRecentLogs with the
   accumulated logs.
6. Assert: status='cancelled' AND recentLogs contains the script's
   log('before agent dispatch') entry.

RED→GREEN verified: temporarily reverted the setRecentLogs guard to
the pre-R7 single-state form, the test failed with
`AssertionError: expected 0 to be greater than 0` (recentLogs was
empty because the guard rejected). Restored fix, test passes.

Production reachability note: the dialog itself is not currently
reachable through the TUI pill focus chain (setPillFocused(true)
does not exist anywhere in the codebase — wenshao R7 verification
finding #1, pre-existing infra gap out of P4 scope). This
integration test is the closest available real-scenario
verification for the fix without modifying out-of-scope code; the
test drives the EXACT registry.cancel + dispatch rejection +
catch-arm sequence the dialog would trigger.

* test(cli): stub getWorkflowRunRegistry in clearCommand + useResumeCommand mocks (#4721)

CI fix for b53bc4e (the R7 fix commit).

R7 (commit b53bc4e) wired WorkflowRunRegistry's new reset() /
abortAll() / hasRunningEntries() methods into backgroundWorkUtils.ts
(hasBlockingBackgroundWork + resetBackgroundStateForSessionSwitch).
clearCommand.ts and useResumeCommand.ts call both utils, but their
mock configs didn't stub getWorkflowRunRegistry — so once the util
started calling it, every clearCommand test threw
`TypeError: config.getWorkflowRunRegistry is not a function`. CI
ubuntu/macos/windows × 10 tests × clearCommand.test.ts went red.

This is the same sibling-drift miss as the BuiltinCommandLoader fix
in 2491911 (R7 pre-cursor): I added a new Config method, every
existing mock that ships a Config-shaped object goes stale until
stubbed.

Fix: add the same `getWorkflowRunRegistry` stub shape to:
- clearCommand.test.ts: 3 sites (default mock + non-interactive mock
  + blocked-background mock)
- useResumeCommand.test.ts: 4 sites (all 4 mock configs)

Stub shape mirrors the 3 sibling registries' interfaces exposed via
backgroundWorkUtils: `hasRunningEntries`, `reset`, `abortAll`.
Returns false / vi.fn() so default behavior matches "no workflow
running" + "reset is observable". Existing tests that exercise the
blocked-background path (hasUnfinalizedTasks: true) keep working
because workflow's hasRunningEntries=false still allows the
agent-side block to trigger.

Verification: 24/24 in clearCommand+useResumeCommand pass locally;
the 7-file P4 + impacted suite pass 105/105 (registries + commands +
hooks + dialog + pill + workflowsCommand + backgroundWorkUtils).
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.

3 participants