Skip to content

feat(serve): mutation gating helper and --require-auth#4236

Merged
wenshao merged 5 commits into
mainfrom
feat-serve-mutation-gate-require-auth
May 17, 2026
Merged

feat(serve): mutation gating helper and --require-auth#4236
wenshao merged 5 commits into
mainfrom
feat-serve-mutation-gate-require-auth

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

Summary

  • Implements #4175 Wave 4 PR 15: a centralized state-changing-route gate that Wave 4 follow-ups (memory CRUD, file edit, MCP restart, device-flow auth) will reuse instead of open-coding bearerAuth checks per route, plus a --require-auth deployment knob that hardens the loopback developer default for shared dev hosts / CI runners / multi-tenant workstations.
  • Only formally depends on PR 7 (#4231 merged 2026-05-17), so it runs in parallel with the Wave 2.5 queue rather than blocking on heartbeat / replay / lifecycle work.
  • Default off, bit-for-bit backward compatible. Existing routes opt into the gate's non-strict mode; the conditional require_auth capability tag is absent unless the operator passed the flag.

What's in this PR

  1. createMutationGate({ tokenConfigured, requireAuth }) helper in packages/cli/src/serve/auth.ts. Single per-route choke point with the following 4-cell behavior matrix:

    daemon config route opts result
    requireAuth: true any passthrough (1)
    token configured any passthrough (2)
    no token (loopback dev) strict: false passthrough
    no token (loopback dev) strict: true 401 token_required

    (1) global bearerAuth already 401'd unauthenticated requests; (2) global bearerAuth enforcing.

    Wave 1-2 mutation routes (POST /session, /session/:id/{load,resume,prompt,cancel,model}, /permission/:requestId) opt in with the default non-strict factory call so the helper acts as a centralization marker — grep gateMutation finds all mutation routes — without changing today's behavior. Wave 4 routes (PR 16-21) will pass { strict: true } to require a token even on no-token loopback defaults.

  2. --require-auth CLI flag + ServeOptions.requireAuth. Boot refuses without a token (boot-loud, like the existing non-loopback check); closes the /health exemption when on so /health also requires Authorization: Bearer …; stderr breadcrumb (qwen serve: --require-auth enabled (bearer token mandatory on every route, including loopback /health).) so the hardened mode is visible in journald/docker logs without parsing /capabilities.

  3. Conditional require_auth capability tag on /capabilities.features, advertised only when the operator opted in. Tag presence = behavior is on; absence = either an older daemon predating the tag OR a current daemon without --require-auth. Adds a CONDITIONAL_SERVE_FEATURES registry primitive so future per-deployment toggles (e.g. eventual redact_errors) follow the same shape rather than each one growing its own ad-hoc emit branch.

Out of scope (intentionally deferred)

  • No new mutation routes flipped to strict: true yet. The helper exists, existing routes stay non-strict for backward compatibility, Wave 4 PRs will flip the strict bit on routes that genuinely need token-required-on-loopback (memory writes, file edits, MCP restart, device-flow auth).
  • No PermissionMediator / pair-token revocation. Those are Wave 5 PR 24 (depends on PR 8 + PR 22 bridge extraction); not in PR 15's scope.
  • No audit log / per-route policy strategies. The factory shape leaves room (one factory builds N route handlers) but landing audit hooks here would conflict with PR 24's PermissionMediator surface; better to do audit once at that layer.

Engineering principles checklist (Stage 1.5 wave PRs)

  • Independently mergeable (only PR 7 dependency, which is in)
  • Backward compatible (existing routes pass mutate() non-strict; capability tag conditional; no removed routes / event fields / CLI behavior)
  • Default off (requireAuth: false default; require_auth tag absent unless flag set; existing mutation routes stay non-strict)
  • qwen serve Stage 1 routes / SDK behavior preserved (245/245 serve tests pass, including pre-PR capability + bearer auth assertions; only the historical EXPECTED_STAGE1_FEATURES / Object.keys(SERVE_CAPABILITY_REGISTRY) test data was extended to cover the new registry entry)
  • Gradual migration (Wave 4 routes opt into strict: true independently as they land)
  • Reversible (revert this PR → all routes fall back to global-only bearerAuth; no schema changes; no external state)
  • Tests-first (5 new unit tests for the gate behavior matrix; 5 added in server.test.ts for capability + /health + boot-check coverage)

Validation

  • Commands run:
    npm run typecheck --workspace packages/cli
    npx vitest run packages/cli/src/serve
    npx eslint packages/cli/src/serve packages/cli/src/commands/serve.ts
  • Expected result: typecheck + lint clean; 245/245 serve tests pass (5 new in auth.test.ts, +5 in server.test.ts).
  • Observed result: matches expected.
  • Quickest reviewer verification path: npx vitest run packages/cli/src/serve/auth.test.ts packages/cli/src/serve/server.test.ts then inspect the createMutationGate matrix tests in auth.test.ts and the #4175 PR 15 tagged tests in server.test.ts.

Scope / Risk

  • Main risk or tradeoff: existing mutation routes thread mutate() (default non-strict) as a centralization marker. This adds one no-op middleware per route on the hot path — measurable only as a function-call-and-return; the factory caches a single shared passthrough handler when global auth is on so we don't allocate one closure per route call.
  • Not covered / not validated: SDK-side DaemonClient / DaemonSessionClient adapter for the require_auth capability tag (clients can detect the tag today via caps.features array indexOf, but a typed accessor would be cleaner — out of scope here, can land alongside Wave 4 routes that consume it).
  • Breaking changes / migration notes: none. Loopback no-token developer default unchanged. --require-auth is opt-in. The conditional require_auth tag is additive; older clients indexing into caps.features as a Set<string> see it harmlessly absent.

Testing Matrix

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

Local macOS validation covered targeted typecheck, vitest (full packages/cli/src/serve suite — 245/245), eslint. Windows / Linux left to CI.

Linked Issues / Bugs

What's next after this lands

Wave 4 mutation routes (PR 16 memory + agents CRUD; PR 17 approval-mode + tools enable + MCP restart + workspace init; PR 18 FileSystemService boundary; PR 19 read-only file routes; PR 20 file write/edit (also depends on PR 8); PR 21 device-flow auth) all become unblocked. The "broad mutation routes share one gating helper rather than open-code auth per route" property of the Wave 4 batch is the deliverable PR 15 makes possible.

🤖 Generated with Qwen Code

@github-actions

Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR (#4236) implements a centralized mutation gate helper and --require-auth flag for the qwen serve daemon, addressing issue #4175 (Wave 4 PR 15). The changes introduce a reusable authentication gate for state-changing routes and a deployment toggle that hardens the loopback developer default for shared/CI environments. The implementation is well-documented, backward-compatible by default, and includes comprehensive tests.

🔍 General Feedback

  • Strong security posture: The PR correctly identifies the threat model (any local user can access loopback) and provides an opt-in hardening mechanism without breaking existing single-user workflows.
  • Excellent documentation: Comments throughout the code explain the "why" behind decisions, including RFC references, threat models, and deployment scenarios.
  • Good test coverage: Tests cover all four cells of the behavior matrix, conditional capability advertisement, and boot-time validation.
  • Clean architecture: The createMutationGate factory pattern provides a single choke point for future Wave 4 mutation routes while keeping existing routes backward-compatible.
  • Allocation-conscious: The factory caches the passthrough handler when auth is globally enforced, avoiding redundant closure allocations per request.

🎯 Specific Feedback

🟡 High

  • File: packages/cli/src/serve/auth.ts:230-235 - The createMutationGate function returns a pre-built passthrough handler when requireAuth || tokenConfigured, but the closure still captures deps unnecessarily. Consider returning a module-level singleton passthrough handler instead of creating one per factory call to further reduce allocations:

    const PASSTHROUGH: RequestHandler = (_req, _res, next) => next();
    // ... then in createMutationGate:
    if (deps.requireAuth || deps.tokenConfigured) {
      return () => PASSTHROUGH;
    }
  • File: packages/cli/src/serve/capabilities.ts:93-102 - The getAdvertisedServeFeatures function has a hardcoded check for require_auth within the conditional features gate. This works for now but doesn't scale well as more conditional features are added. Consider making CONDITIONAL_SERVE_FEATURES a Map that maps feature names to their toggle predicates:

    const CONDITIONAL_SERVE_FEATURES = new Map<ServeFeature, (toggles: AdvertiseFeatureToggles) => boolean>([
      ['require_auth', (toggles) => toggles.requireAuth === true],
    ]);

🟢 Medium

  • File: packages/cli/src/serve/server.ts:217-225 - The mutation gate factory is instantiated at app construction time, which is correct. However, the comment mentions "Wave 4 PRs will pass { strict: true }" but the current code passes default options to all existing routes. Consider adding an inline comment on each route that currently uses mutate() to clarify whether it's a "centralization marker" (non-strict) or a "future strict candidate" to help reviewers track adoption:

    app.post('/session', mutate(), async (req, res) => {
      // Non-strict: centralization marker only; backward-compatible
  • File: packages/cli/src/commands/serve.ts:82-88 - The --require-auth option description mentions "Hardens the loopback developer default" but doesn't explicitly call out that /health also requires auth when this flag is set. This is a key behavioral change that operators should be aware of before enabling. Consider adding:

    'When enabled, /health also requires Authorization header (no loopback exemption).',
    

🔵 Low

  • File: packages/cli/src/serve/auth.ts:180-188 - The behavior matrix comment is excellent but uses markdown table syntax within a JSDoc comment. Consider using a simpler format that renders better in IDE hovers:

    * Behavior matrix:
    * - requireAuth=true + any route opts  passthrough
    * - token configured + any route opts  passthrough
    * - no token (loopback dev) + strict=false  passthrough
    * - no token (loopback dev) + strict=true  401 token_required
  • File: packages/cli/src/serve/types.ts:88-97 - The requireAuth JSDoc comment is thorough but could benefit from a cross-reference to the protocol documentation section that describes the require_auth capability tag. Consider adding:

    * @see docs/developers/qwen-serve-protocol.md#conditional-tags
  • File: packages/cli/src/serve/capabilities.ts:44-48 - The require_auth capability descriptor comment mentions "Wave 4 follow-ups can extend this object" but the CONDITIONAL_SERVE_FEATURES set on line 58 also needs updating. Consider adding a note that both locations must be updated together, or better yet, derive the set from the registry automatically.

✅ Highlights

  • Excellent security thinking: The boot-time validation that refuses to start with --require-auth but no token configured prevents operators from mistakenly believing their deployment is hardened when it isn't.
  • Clean capability advertisement: The conditional require_auth tag in /capabilities allows SDK clients to detect hardened mode pre-flight and surface clear "this deployment requires auth" hints rather than parsing 401 bodies.
  • Thoughtful error shape: The code: 'token_required' error response from strict routes is distinct from bearerAuth's plain Unauthorized, enabling SDK clients to provide contextual remediation guidance.
  • Comprehensive test coverage: Tests verify the behavior matrix, conditional feature advertisement, /health gating, and boot-time validation. The allocation test (verifying passthrough handler caching) is a nice regression anchor.
  • Operator-friendly logging: The stderr breadcrumb when --require-auth is enabled helps operators confirm the hardening is active without parsing /capabilities responses.

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

Adds a centralized mutation-gating helper for serve mutation routes and introduces a --require-auth boot flag that hardens the loopback developer default by forcing bearer-token enforcement on every route (including /health). Today's mutation routes adopt the helper as a no-op marker; Wave 4 routes will opt into strict: true later. A conditional require_auth capability tag is advertised only when the operator opts in.

Changes:

  • New createMutationGate({ tokenConfigured, requireAuth }) factory in serve/auth.ts with a 4-cell behavior matrix; existing mutation routes thread mutate() as a centralization marker.
  • New --require-auth CLI flag and ServeOptions.requireAuth; boot refuses without a token, drops the loopback /health exemption, and emits a stderr breadcrumb.
  • New CONDITIONAL_SERVE_FEATURES registry primitive plus AdvertiseFeatureToggles; require_auth tag advertised only when toggled on; docs updated.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/cli/src/serve/auth.ts Adds createMutationGate factory + types implementing the gate matrix.
packages/cli/src/serve/auth.test.ts New unit tests covering the four matrix cells and passthrough identity caching.
packages/cli/src/serve/server.ts Wires mutate() onto existing mutation routes; conditionally registers /health pre/post bearer based on requireAuth.
packages/cli/src/serve/server.test.ts Adds capability advertisement tests and /health + boot-check coverage for --require-auth.
packages/cli/src/serve/capabilities.ts Adds require_auth to registry, CONDITIONAL_SERVE_FEATURES, and toggle-aware advertisement.
packages/cli/src/serve/types.ts Documents new requireAuth?: boolean on ServeOptions.
packages/cli/src/serve/runQwenServe.ts Boot-check refusing --require-auth without token; stderr breadcrumb when enabled.
packages/cli/src/serve/index.ts Re-exports new auth helpers, types, and CONDITIONAL_SERVE_FEATURES.
packages/cli/src/commands/serve.ts Adds --require-auth yargs option, wired to runQwenServe.
docs/users/qwen-serve.md User-facing docs for --require-auth with example.
docs/developers/qwen-serve-protocol.md Protocol doc updates for --require-auth, conditional tags table, and corrected feature list snippet.

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

Comment thread packages/cli/src/serve/auth.ts Outdated
doudouOUC added a commit that referenced this pull request May 17, 2026
Three small follow-ups from the automated reviewers on PR #4236:

1. **Drop misleading `--require-auth` from `token_required` error
   message** (Copilot inline auth.ts:262). The strict-mode 401
   listed three remediations but `--require-auth` is paired-required
   with a token at boot — naming it standalone would loop the operator
   into a different boot error. Keep the two valid standalone fixes
   (env var, --token); add inline note explaining the omission.
   `auth.test.ts` regex updated to `not.toMatch(/--require-auth/)`
   to anchor the new wording.

2. **Mention `/health` gating in `--require-auth` CLI description**
   (auto-reviewer Medium #2). Operators flipping the flag without
   reading the protocol doc would get paged when k8s/Compose probes
   start 401-ing. One sentence in the yargs description prevents that.

3. **Drift insurance comment between registry and
   `CONDITIONAL_SERVE_FEATURES`** (auto-reviewer Low #3). Document
   the four-step procedure for adding a new conditional tag so a
   future contributor doesn't update only the registry and silently
   advertise the tag unconditionally. Notes the Map<predicate>
   refactor as the right move when a second tag lands.

Deferred (not in this fix-up):
- Module-level PASSTHROUGH singleton (High #1) — micro-optimization,
  unmeasurable.
- Map<feature, predicate> for conditional features (High #2) —
  premature abstraction with one tag.
- Per-route `// non-strict marker` comments (Medium #1) — noise.
- `@see` cross-ref in types.ts (Low #2) — sugar.
- JSDoc bullet-list vs table (Low #1) — current format is fine.

Refs: #4175 #4236

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 77.15% 77.15% 78.33% 80.57%
Core 79.17% 79.17% 81.8% 82.69%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   77.15 |    80.57 |   78.33 |   77.15 |                   
 src               |   75.73 |    69.15 |   80.55 |   75.73 |                   
  gemini.tsx       |   68.53 |     66.4 |   76.47 |   68.53 | ...29,946-949,957 
  ...ractiveCli.ts |      80 |    68.61 |   78.57 |      80 | ...1020,1058,1161 
  ...liCommands.ts |   74.51 |     72.5 |     100 |   74.51 | ...41-265,290,391 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |    62.2 |    76.42 |   63.88 |    62.2 |                   
  acpAgent.ts      |   64.99 |    76.85 |   70.96 |   64.99 | ...64-966,980-988 
  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 |   76.86 |    71.84 |   86.07 |   76.86 |                   
  ...ryReplayer.ts |   65.93 |    75.67 |   81.81 |   65.93 | ...40-255,268-269 
  Session.ts       |   76.24 |    70.53 |   88.23 |   76.24 | ...2534,2540-2543 
  ...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/auth          |    97.7 |    94.81 |   95.45 |    97.7 |                   
  allProviders.ts  |     100 |      100 |     100 |     100 |                   
  ...iderConfig.ts |    97.6 |    95.04 |     100 |    97.6 | ...61,411,433-434 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/auth/install  |   98.57 |    88.88 |     100 |   98.57 |                   
  ...nstallPlan.ts |   98.57 |    88.88 |     100 |   98.57 | 80,93             
 ...viders/alibaba |   96.96 |    66.66 |   66.66 |   96.96 |                   
  ...baStandard.ts |     100 |      100 |     100 |     100 |                   
  codingPlan.ts    |   93.67 |    66.66 |   66.66 |   93.67 | 83,87-89,94       
  tokenPlan.ts     |     100 |      100 |     100 |     100 |                   
 ...oviders/custom |     100 |      100 |     100 |     100 |                   
  ...omProvider.ts |     100 |      100 |     100 |     100 |                   
 ...roviders/oauth |    91.5 |    77.03 |   97.05 |    91.5 |                   
  openrouter.ts    |   84.37 |    33.33 |     100 |   84.37 | 43-48             
  ...outerOAuth.ts |    91.9 |    79.06 |   96.87 |    91.9 | ...53-655,699-701 
 ...ers/thirdParty |     100 |      100 |     100 |     100 |                   
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/commands      |   55.55 |    85.71 |   43.47 |   55.55 |                   
  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         |    10.3 |      100 |       0 |    10.3 | ...48-123,125-164 
 ...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.5 |    88.95 |   81.81 |    84.5 |                   
  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         |   60.24 |    28.57 |     100 |   60.24 | ...81,83-87,89-93 
 ...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.87 |    85.31 |   88.09 |   92.87 |                   
  auth.ts          |   86.98 |    80.32 |     100 |   86.98 | ...26-227,243-244 
  config.ts        |   88.67 |     85.1 |      80 |   88.67 | ...1825,1827-1835 
  keyBindings.ts   |   96.55 |       50 |     100 |   96.55 | 193-196           
  ...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 | ...80-581,584-585 
 ...active/control |   77.04 |    88.23 |      80 |   77.04 |                   
  ...rolContext.ts |    7.14 |        0 |       0 |    7.14 | 49-84             
  ...Dispatcher.ts |   91.66 |    91.83 |   88.88 |   91.66 | ...54-372,388,391 
  ...rolService.ts |       8 |        0 |       0 |       8 | 46-179            
 ...ol/controllers |    7.04 |       80 |   13.33 |    7.04 |                   
  ...Controller.ts |   19.32 |      100 |      60 |   19.32 | 81-118,127-210    
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |    3.96 |      100 |   11.11 |    3.96 | ...61-379,389-494 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |    5.21 |      100 |       0 |    5.21 | ...21-433,442-471 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   97.98 |    93.72 |   95.18 |   97.98 |                   
  ...putAdapter.ts |   97.89 |    92.82 |   98.07 |   97.89 | ...1303,1398-1399 
  ...putAdapter.ts |      96 |    91.66 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.28 |      100 |      90 |   98.28 | 81-82,122-123     
  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         |   84.26 |    83.45 |   92.59 |   84.26 |                   
  auth.ts          |   88.49 |    88.37 |    87.5 |   88.49 | ...49-150,153-155 
  capabilities.ts  |     100 |     90.9 |     100 |     100 | 144               
  eventBus.ts      |   88.88 |    87.87 |   85.71 |   88.88 | ...38-446,524-526 
  httpAcpBridge.ts |   82.03 |    80.38 |   97.01 |   82.03 | ...3593,3624-3665 
  ...oryChannel.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  loopbackBinds.ts |     100 |      100 |     100 |     100 |                   
  runQwenServe.ts  |   79.74 |    87.09 |   83.33 |   79.74 | ...51-467,492-494 
  server.ts        |    85.9 |    85.14 |    87.5 |    85.9 | ...1387,1444-1453 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services      |   91.67 |    91.24 |   97.56 |   91.67 |                   
  ...mandLoader.ts |     100 |    93.75 |     100 |     100 | 93                
  ...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 |    91.66 |     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.35 |    83.07 |     100 |   97.35 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.45 |       70 |     100 |   92.45 | ...22,144,151,160 
  tipRegistry.ts   |     100 |    95.23 |     100 |     100 | 33                
  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            |   66.39 |    73.22 |   57.14 |   66.39 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |   65.03 |    64.98 |   52.94 |   65.03 | ...2951,2955-2959 
  ...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        |   52.72 |      100 |   23.52 |   52.72 | ...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       |   55.06 |    51.13 |   35.48 |   55.06 |                   
  AuthDialog.tsx   |   64.26 |    44.44 |   16.66 |   64.26 | ...59,366-388,392 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |    39.5 |       32 |   38.46 |    39.5 | ...69,472,478,481 
  useAuth.ts       |   76.63 |    68.29 |     100 |   76.63 | ...48,493-499,560 
  ...rSetupFlow.ts |   44.61 |    33.33 |      50 |   44.61 | ...57-378,395-438 
 src/ui/commands   |   72.96 |    80.73 |   81.43 |   72.96 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |     100 |      100 |     100 |     100 |                   
  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 |   34.78 |    22.22 |   45.45 |   34.78 | ...86-521,532-533 
  copyCommand.ts   |   98.28 |    94.89 |     100 |   98.28 | ...80,280,321,327 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |   99.02 |    86.11 |     100 |   99.02 | 222,226           
  ...ryCommand.tsx |   68.09 |    77.77 |   77.77 |   68.09 | ...56-261,315-323 
  docsCommand.ts   |     100 |    88.88 |     100 |     100 | 25                
  doctorCommand.ts |   93.37 |    84.12 |     100 |   93.37 | ...55-156,158-159 
  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 |   48.66 |     90.9 |   63.63 |   48.66 | ...05-109,159-211 
  forgetCommand.ts |   26.82 |      100 |      50 |   26.82 | 18-51             
  goalCommand.ts   |    93.1 |    84.61 |     100 |    93.1 | ...67-170,182-185 
  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        
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  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 |   15.04 |      100 |      25 |   15.04 | ...90-106,109-136 
  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 |   65.53 |    75.02 |   70.76 |   65.53 |                   
  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.39 |       75 |     100 |   89.39 | 35,37-42,44       
  ...odeDialog.tsx |     9.7 |      100 |       0 |     9.7 | 35-47,50-182      
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   14.63 |      100 |       0 |   14.63 | 18-56             
  ...TextInput.tsx |   77.01 |       76 |     100 |   77.01 | ...20,234-236,263 
  Composer.tsx     |    80.8 |     64.7 |     100 |    80.8 | ...85,103,154,167 
  ...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 |    12.2 |      100 |       0 |    12.2 | 64-490            
  ...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       |   79.54 |    54.54 |     100 |   79.54 | ...05-109,133-134 
  ...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 |    89.88 |     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  |   82.75 |    78.96 |   83.33 |   82.75 | ...1425,1490,1540 
  ...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 
  ...elsDialog.tsx |   71.05 |    69.11 |   72.72 |   71.05 | ...77,590,601-603 
  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 |   78.43 |    66.66 |     100 |   78.43 | ...20-422,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.92 |    73.21 |     100 |   66.92 | ...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 | ...45-147,154-156 
  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            
 ...nts/agent-view |   38.33 |    70.83 |   36.36 |   38.33 |                   
  ...atContent.tsx |    8.79 |      100 |       0 |    8.79 | 53-265,271-273    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |    9.95 |      100 |       0 |    9.95 | 57-308            
  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,98-106,124 
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |   45.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.63 |    84.44 |   85.29 |   75.63 |                   
  ...sksDialog.tsx |   70.92 |    80.39 |   76.19 |   70.92 | ...1118,1194-1196 
  ...TasksPill.tsx |   63.75 |    86.95 |     100 |   63.75 | 44,86-106,114-122 
  ...gentPanel.tsx |   99.53 |    93.18 |     100 |   99.53 | 123               
 ...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 | 17-128            
  ...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 |   81.81 |     79.2 |   72.22 |   81.81 |                   
  ...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 |   58.41 |     7.69 |      50 |   58.41 | ...12-113,140-143 
  ...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.02 |    69.23 |   33.33 |   81.02 | ...24-426,433-435 
  ...upMessage.tsx |      84 |    93.61 |     100 |      84 | ...56-383,405-420 
  ToolMessage.tsx  |   88.84 |    75.71 |    92.3 |   88.84 | ...44-749,776-778 
 ...ponents/shared |   85.36 |    78.48 |   95.77 |   85.36 |                   
  ...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.01 |    48.78 |      80 |   77.01 | ...08-212,224-230 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  text-buffer.ts   |   83.68 |    78.55 |   97.61 |   83.68 | ...2270-2272,2368 
  ...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 |   42.16 |    69.23 |   21.42 |   42.16 |                   
  ContextUsage.tsx |     4.7 |      100 |       0 |     4.7 | ...52-167,170-456 
  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.11 |    77.66 |   80.35 |   77.11 |                   
  ...ewContext.tsx |    64.7 |    85.71 |      50 |    64.7 | ...22-225,231-241 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   95.18 |    67.56 |      50 |   95.18 | ...94-195,222-226 
  ...deContext.tsx |     100 |      100 |     100 |     100 |                   
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   81.88 |    82.26 |     100 |   81.88 | ...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 | 113-114           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 177-178           
  ...deContext.tsx |   76.08 |    72.72 |     100 |   76.08 | 47-48,52-59,77-78 
 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.32 |    82.38 |   86.66 |   82.32 |                   
  ...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.56 |    62.75 |   61.53 |   75.56 | ...78,902,921-925 
  ...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.77 |    89.09 |     100 |   92.77 | ...86-187,220-223 
  ...ifications.ts |   92.07 |    96.29 |     100 |   92.07 | 116-124           
  ...tIndicator.ts |     100 |    93.75 |     100 |     100 | 63                
  ...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 |   95.95 |    82.75 |     100 |   95.95 | ...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 |   76.92 |       50 |     100 |   76.92 | 55,68,71-75,88-96 
  ...eteCommand.ts |   78.53 |    88.57 |     100 |   78.53 | ...96-104,112-113 
  ...ialogClose.ts |   15.38 |      100 |     100 |   15.38 | 83-148            
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |     97.7 |     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 |   54.47 |       50 |   33.33 |   54.47 | ...69-171,193-194 
  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 |   76.96 |       74 |   91.66 |   76.96 | ...2461,2474-2482 
  ...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       
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  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 
  ...derUpdates.ts |   86.38 |    77.19 |     100 |   86.38 | ...22,281-293,341 
  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.65 |     100 |   96.98 | ...83-184,238-241 
  ...sionPicker.ts |   92.02 |    89.47 |     100 |   92.02 | ...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.67 |    85.41 |   94.73 |   82.67 | ...68-670,678-714 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |   97.67 |    91.66 |     100 |   97.67 | ...28-332,344-347 
  ...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 |                   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  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             
 ...i/manageModels |   93.61 |       48 |     100 |   93.61 |                   
  manageModels.ts  |   93.61 |       48 |     100 |   93.61 | ...63-166,179,209 
 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 |    7.14 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |    7.14 |     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.91 |    82.93 |   92.56 |   83.91 |                   
  ...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 | 29,51             
  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.71 |    87.41 |     100 |   94.71 | ...43,428,432-433 
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |   98.98 |    97.14 |     100 |   98.98 | 94                
  ...storyUtils.ts |   61.06 |    69.62 |      90 |   61.06 | ...64,412,417-439 
  ...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.35 |    94.38 |   91.66 |   97.35 | ...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         |   75.88 |    89.54 |   93.38 |   75.88 |                   
  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  |   71.06 |       75 |     100 |   71.06 | ...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        |   98.67 |    96.36 |     100 |   98.67 | 67-68             
  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           
  ...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    
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-1047            
  settingsUtils.ts |   82.89 |    90.67 |   89.47 |   82.89 | ...52-663,670-678 
  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    |   90.32 |    89.65 |    87.5 |   90.32 | ...18-219,224-228 
  ...InfoFields.ts |   87.61 |       65 |     100 |   87.61 | ...22-123,144-145 
  ...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                
  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          |   79.17 |    82.69 |    81.8 |   79.17 |                   
 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        |   87.58 |    79.07 |   91.76 |   87.58 |                   
  ...transcript.ts |   92.25 |    85.71 |     100 |   92.25 | ...87,306-307,438 
  ...ent-resume.ts |    82.5 |     71.5 |   77.41 |    82.5 | ...1035-1039,1042 
  ...ound-tasks.ts |    95.4 |    86.48 |     100 |    95.4 | ...55-756,827-828 
  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.14 |     76.7 |   71.42 |   81.14 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   76.49 |    72.35 |   60.86 |   76.49 | ...1608,1635-1682 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   81.19 |    71.73 |   60.86 |   81.19 | ...98-399,402-403 
  ...nteractive.ts |   79.71 |    79.62 |      75 |   79.71 | ...54,456,458,461 
  ...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        |   77.22 |    79.73 |   63.34 |   77.22 |                   
  config.ts        |   75.37 |    77.98 |   58.68 |   75.37 | ...3483,3494-3506 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   95.07 |    93.44 |   89.47 |   95.07 | ...66-267,270-271 
 ...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          |   86.68 |    82.18 |   89.86 |   86.68 |                   
  baseLlmClient.ts |   92.35 |    80.85 |   86.66 |   92.35 | ...34,342-356,495 
  client.ts        |   85.49 |    77.12 |   84.84 |   85.49 | ...1735,1774-1777 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...63,365,372-375 
  ...lScheduler.ts |   82.97 |    81.44 |   93.47 |   82.97 | ...2431,2483-2487 
  geminiChat.ts    |   89.32 |     84.8 |   91.48 |   89.32 | ...1454,1521-1522 
  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 |   85.71 |    70.58 |     100 |   85.71 | ...90-191,205-214 
  ...issionFlow.ts |   98.59 |    94.73 |     100 |   98.59 | 93                
  prompts.ts       |   89.16 |    86.41 |   76.92 |   89.16 | ...-965,1168-1169 
  tokenLimits.ts   |     100 |    89.47 |     100 |     100 | 51-52             
  ...okTriggers.ts |   99.31 |    90.41 |     100 |   99.31 | 124,135           
  turn.ts          |   96.42 |    88.88 |     100 |   96.42 | ...00,413-414,462 
 ...ntentGenerator |   94.92 |    82.59 |   93.87 |   94.92 |                   
  ...tGenerator.ts |   96.48 |    84.28 |   92.59 |   96.48 | ...01,919-923,963 
  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 |    92.1 |    80.38 |   90.32 |    92.1 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   92.08 |    80.38 |   90.32 |   92.08 | ...85,895-896,924 
 ...ntentGenerator |   81.66 |    84.08 |    90.9 |   81.66 |                   
  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         |   52.38 |    44.44 |      50 |   52.38 | ...77,81-85,89-93 
  ...tGenerator.ts |    66.4 |    70.58 |   88.88 |    66.4 | ...51-157,168-169 
  pipeline.ts      |   93.67 |     84.9 |     100 |   93.67 | ...80-481,489,554 
  ...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.69 |    89.17 |   95.45 |   96.69 |                   
  dashscope.ts     |   97.29 |    89.77 |   93.33 |   97.29 | ...81-282,358-359 
  deepseek.ts      |   95.55 |    90.56 |     100 |   95.55 | ...31-132,145-146 
  default.ts       |   94.62 |    86.36 |   85.71 |   94.62 | 86-87,157-159     
  index.ts         |     100 |      100 |     100 |     100 |                   
  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 |                   
 src/extension     |   60.56 |    79.46 |    78.4 |   60.56 |                   
  ...-converter.ts |   62.35 |    47.82 |      90 |   62.35 | ...90-791,800-832 
  ...ionManager.ts |   47.04 |    82.06 |    65.9 |   47.04 | ...1398,1408-1427 
  ...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        |   44.94 |    88.52 |      60 |   44.94 | ...53-359,398-451 
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   97.29 |    93.75 |     100 |   97.29 | ...64,184-185,274 
  npm.ts           |   48.66 |    76.08 |      75 |   48.66 | ...18-420,427-431 
  override.ts      |   94.11 |    88.88 |     100 |   94.11 | 63-64,81-82       
  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      |   46.91 |     92.3 |   71.87 |   46.91 |                   
  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.22 |      100 |   16.66 |   13.22 | 88-458,518-568    
  ...onToolGate.ts |     100 |    96.29 |     100 |     100 | 93                
  ...nGenerator.ts |    38.4 |    95.12 |   33.33 |    38.4 | ...16-318,353-383 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/goals         |   88.38 |    81.14 |   94.11 |   88.38 |                   
  ...eGoalStore.ts |   81.57 |    92.85 |   81.81 |   81.57 | ...37-140,148-156 
  goalHook.ts      |   96.89 |    90.24 |     100 |   96.89 | 93-98             
  goalJudge.ts     |    83.4 |    73.13 |     100 |    83.4 | ...19-320,328-330 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/hooks         |   83.48 |    84.87 |   86.83 |   83.48 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.ts |     100 |      100 |     100 |     100 |                   
  ...terpolator.ts |   96.66 |    93.33 |     100 |   96.66 | 66-67             
  ...HookRunner.ts |   96.68 |    87.23 |     100 |   96.68 | 110-112,231-233   
  ...Aggregator.ts |    96.4 |    90.78 |     100 |    96.4 | ...91,293-294,367 
  ...entHandler.ts |   94.56 |    83.78 |   93.33 |   94.56 | ...38,795-796,806 
  hookPlanner.ts   |   84.13 |    76.59 |      90 |   84.13 | ...38,144,162-173 
  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 |   93.63 |    89.47 |      90 |   93.63 | ...45-353,427-428 
  ...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.18 |    92.04 |   85.71 |   91.18 | ...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        |   67.43 |       76 |   65.62 |   67.43 |                   
  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        |   79.56 |    69.38 |   88.88 |   79.56 | ...40-245,269-280 
  ...ceSelector.ts |   91.86 |    77.27 |     100 |   91.86 | ...07,109-110,118 
  scan.ts          |   87.91 |    68.42 |     100 |   87.91 | ...47-48,58,82-87 
  ...entPlanner.ts |    11.5 |      100 |       0 |    11.5 | ...57-192,210-298 
  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 |                   
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   89.31 |    85.55 |    87.5 |   89.31 |                   
  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.63 |    92.53 |     100 |   98.63 | 161,323,329       
  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   |   71.18 |    88.76 |   48.57 |   71.18 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   81.42 |    86.66 |      80 |   81.42 | ...29-830,837-846 
  rule-parser.ts   |   95.99 |    93.22 |     100 |   95.99 | ...-864,1013-1015 
  ...-semantics.ts |   58.28 |    85.27 |    30.2 |   58.28 | ...1604-1614,1643 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/prompts       |   83.63 |      100 |    87.5 |   83.63 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |     100 |      100 |     100 |     100 |                   
 src/qwen          |   86.01 |    79.48 |   97.18 |   86.01 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   84.99 |    74.81 |   93.33 |   84.99 | ...,985-1001,1031 
  ...kenManager.ts |   83.76 |    76.22 |     100 |   83.76 | ...62-767,788-793 
 src/services      |   85.26 |    83.73 |   90.81 |   85.26 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   98.44 |    91.83 |     100 |   98.44 | 268-269           
  ...ionService.ts |    95.6 |    96.36 |     100 |    95.6 | ...32,400,402-406 
  ...ingService.ts |   84.15 |       84 |   82.85 |   84.15 | ...1241,1258-1259 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |     100 |    96.77 |     100 |     100 | 133,182           
  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.25 |    74.35 |    92.3 |   86.25 | ...46-655,696-699 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |      90 |    84.44 |   88.88 |      90 | ...89,191,269-276 
  ...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.79 |       70 |   94.87 |   73.79 | ...1365,1393-1394 
  ...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.04 |      100 |       0 |   12.04 | 49-160            
  ...ionService.ts |   90.05 |    79.71 |   96.55 |   90.05 | ...1272,1276-1277 
  sessionTitle.ts  |   93.87 |    69.81 |     100 |   93.87 | ...33-236,267-268 
  ...ionService.ts |   81.07 |    77.92 |   89.28 |   81.07 | ...1923,1929-1934 
  ...UseSummary.ts |   94.73 |    87.71 |     100 |   94.73 | ...73-175,225-226 
  ...reeCleanup.ts |   14.56 |      100 |   33.33 |   14.56 | 58-185            
 ...icrocompaction |   97.69 |    89.79 |     100 |   97.69 |                   
  microcompact.ts  |   97.69 |    89.79 |     100 |   97.69 | ...68,229,233,314 
 src/skills        |    87.5 |    83.86 |   94.23 |    87.5 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |     93.1 |     100 |     100 | 93,112            
  skill-load.ts    |   92.94 |    81.63 |     100 |   92.94 | ...06,226,238-240 
  skill-manager.ts |   83.31 |    79.66 |   90.32 |   83.31 | ...1120,1127-1131 
  skill-paths.ts   |   86.74 |    77.77 |     100 |   86.74 | ...00-101,106-107 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/subagents     |   83.13 |    80.24 |   95.23 |   83.13 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   77.21 |    72.09 |   92.85 |   77.21 | ...1180,1202-1203 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 51-56,69-74,78-83 
 src/telemetry     |   74.59 |     85.9 |   78.77 |   74.59 |                   
  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       |    74.9 |    82.95 |   74.54 |    74.9 | ...58-978,981-992 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |   90.45 |    83.56 |   76.92 |   90.45 | ...17-318,338-342 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   90.69 |    87.87 |     100 |   90.69 | ...67-471,482-485 
  ...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,104            
  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         |    77.8 |    81.31 |    86.3 |    77.8 |                   
  ...erQuestion.ts |   88.93 |    76.74 |    90.9 |   88.93 | ...39-340,347-348 
  cron-create.ts   |   97.75 |    88.88 |   83.33 |   97.75 | 30-31             
  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          |   80.52 |    85.98 |   73.33 |   80.52 | ...15-716,803-853 
  ...r-worktree.ts |   82.43 |    68.75 |    87.5 |   82.43 | ...67-170,236-237 
  exit-worktree.ts |   83.47 |       84 |    90.9 |   83.47 | ...80-281,286-299 
  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 |   69.73 |    75.29 |   71.42 |   69.73 | ...29-732,749-786 
  mcp-client.ts    |   33.18 |    77.41 |   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       |   92.36 |    83.94 |      92 |   92.36 | ...29,558-561,574 
  ...nforcement.ts |   82.44 |       90 |     100 |   82.44 | 174-185,234-247   
  read-file.ts     |   95.07 |     88.6 |      90 |   95.07 | ...99,290-293,296 
  ripGrep.ts       |   94.59 |    85.71 |   93.33 |   94.59 | ...60,463,541-542 
  ...-transport.ts |    6.34 |      100 |       0 |    6.34 | 47-145            
  send-message.ts  |   89.32 |    91.66 |   83.33 |   89.32 | 44-45,68-76       
  shell.ts         |   72.96 |     79.6 |    91.3 |   72.96 | ...4216,4265-4271 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |   88.11 |    91.17 |   84.61 |   88.11 | ...95,399,422-444 
  ...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.79 |       75 |   80.48 |   74.79 | ...92-793,801-802 
  tool-search.ts   |   95.19 |    86.48 |    92.3 |   95.19 | ...47-153,208-213 
  tools.ts         |   91.98 |    90.19 |   88.88 |   91.98 | ...50-451,467-473 
  web-fetch.ts     |   88.59 |    79.48 |    92.3 |   88.59 | ...12-313,315-316 
  write-file.ts    |   82.23 |    81.17 |   83.33 |   82.23 | ...65-668,680-715 
 src/tools/agent   |   75.01 |    82.55 |   74.62 |   75.01 |                   
  agent.ts         |   75.29 |    82.86 |    75.4 |   75.29 | ...2203,2265-2272 
  fork-subagent.ts |   69.62 |    71.42 |   66.66 |   69.62 | ...04-105,140-151 
 src/utils         |   88.81 |    87.49 |   93.55 |   88.81 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...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 |    86.66 |     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.84 |   94.73 |    95.9 | 106-107,214-218   
  editHelper.ts    |   93.63 |    83.52 |     100 |   93.63 | ...28-429,463-464 
  editor.ts        |   97.61 |    95.71 |     100 |   97.61 | ...70-271,273-274 
  ...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.92 |    79.59 |   53.33 |   70.92 | ...03-219,223-229 
  fetch.ts         |   70.18 |    71.42 |   71.42 |   70.18 | ...42,148,161,186 
  fileUtils.ts     |   91.41 |    86.13 |      95 |   91.41 | ...1182,1186-1192 
  forkedAgent.ts   |    78.5 |    70.73 |   85.71 |    78.5 | ...30-436,441-447 
  formatters.ts    |   54.54 |       50 |     100 |   54.54 | 12-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      |   56.66 |    85.71 |      75 |   56.66 | ...2,72-73,97-148 
  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 |                   
  ...yDiscovery.ts |    83.9 |    79.36 |     100 |    83.9 | ...16,319,411-414 
  ...tProcessor.ts |   93.63 |       90 |     100 |   93.63 | ...96-302,384-385 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  modelId.ts       |   98.55 |    96.87 |     100 |   98.55 | 103               
  ...kerChecker.ts |   88.75 |    85.71 |     100 |   88.75 | 69-70,87-93       
  notebook.ts      |   94.35 |    84.78 |     100 |   94.35 | ...10,122,174-176 
  openaiLogger.ts  |   88.05 |    84.09 |     100 |   88.05 | ...44-146,169-174 
  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 |                   
  ...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.96 |    86.95 |     100 |   87.96 | ...05-207,223-234 
  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 
  ...tchOptions.ts |   81.72 |    85.04 |   95.23 |   81.72 | ...11,536,565-574 
  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   |   82.93 |    89.89 |     100 |   82.93 | ...1522,1529-1533 
  ...lAstParser.ts |   95.58 |    85.79 |     100 |   95.58 | ...1059-1061,1071 
  ...nlyChecker.ts |   95.75 |    92.39 |     100 |   95.75 | ...00-301,313-314 
  sideQuery.ts     |   98.73 |    94.59 |     100 |   98.73 | 111               
  ...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.61 |     100 |      92 | 49-53,65-69       
 ...ils/filesearch |   85.77 |    81.06 |   96.42 |   85.77 |                   
  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.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Replying point-by-point to the Review Summary (github-actions). Adopt-now items pushed in 21f46af; defer/reject reasoning below.

🟡 High

  • High pre-release: fix ci #1 — module-level PASSTHROUGH singleton (auth.ts:230-235). Defer. createMutationGate is called once per createServeApp() (i.e. once per daemon boot), not per request, so hoisting saves a single closure allocation per daemon process lifetime — unmeasurable. The current local placement also documents intent more clearly (the closure lives next to the comment that explains why it's pre-built). Worth revisiting if the factory ever grows hot-path callers.

  • High Where is the config saved? #2Map<feature, predicate> for CONDITIONAL_SERVE_FEATURES (capabilities.ts:93-102). Defer. Premature abstraction with one tag — a Set plus one if (feature === 'require_auth') branch is honest about the surface today. CLAUDE.md guidance: "three similar lines is better than a premature abstraction." When a second conditional tag lands (e.g. eventual redact_errors), refactor to Map<ServeFeature, (toggles) => boolean> then. Drift insurance pushed in 21f46af — see Low 如何自定义密钥文件 .env可能与其他文件冲突 #3 reply below — calls out the refactor as the right move at that point and documents the four-step procedure for adding the next tag in the meantime.

🟢 Medium

  • Medium pre-release: fix ci #1 — per-route // Non-strict: centralization marker comments. Reject (politely). Repeating the same comment on 7 routes is clutter; createMutationGate's JSDoc already explains the marker pattern, and Wave 4 PRs flipping strict: true will be visible in diff anyway. The grep target (gateMutation / mutate( calls) gives reviewers the tracking ability without per-route boilerplate.

  • Medium Where is the config saved? #2--require-auth CLI description should mention /health. Adopted in 21f46af. Good operator-facing visibility catch — flipping the flag without reading the protocol doc would page operators when k8s/Compose probes start 401-ing. The yargs description now includes /health also requires Authorization when enabled (no loopback exemption — k8s/Compose probes must pass the bearer too).

🔵 Low

  • Low pre-release: fix ci #1 — JSDoc behavior matrix as bullet list instead of markdown table. Reject. Modern IDEs (VS Code, JetBrains) render markdown tables in hover popovers; bullets lose the column alignment that makes the matrix readable at a glance. Verified locally — VS Code TS hover renders the table cleanly.

  • Low Where is the config saved? #2@see docs/... cross-reference in types.ts. Defer. Optional sugar. The protocol doc already mentions --require-auth and the conditional require_auth tag; the reverse link is symmetry, not signal. Happy to add if a follow-up PR is touching types.ts for other reasons.

  • Low 如何自定义密钥文件 .env可能与其他文件冲突 #3 — drift between registry and CONDITIONAL_SERVE_FEATURES set. Adopted in 21f46af. Real risk — a future contributor adding a conditional tag could update the registry but forget the set, silently advertising the tag to every client. The set's JSDoc now documents the four-step "add a new conditional tag" procedure (register in registry → add to set → extend AdvertiseFeatureToggles → branch in getAdvertisedServeFeatures) and notes the Map<predicate> refactor as the right move when a second tag lands.

Validation after fix-up

  • npx vitest run packages/cli/src/serve/auth.test.ts packages/cli/src/serve/server.test.ts → 118/118 pass
  • npm run typecheck --workspace packages/cli → clean
  • npx eslint packages/cli/src/serve/auth.ts packages/cli/src/serve/auth.test.ts packages/cli/src/serve/capabilities.ts packages/cli/src/commands/serve.ts → clean

Net of this comment + 21f46af: 3 of 7 suggestions adopted (the two correctness/operator-visibility items + drift insurance), 4 deferred or rejected with reasoning. Ready for human review.

@doudouOUC doudouOUC requested a review from wenshao May 17, 2026 09:26

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

Nit pass — four non-blocking suggestions. Overall the PR is well-scoped and disciplined about backward compatibility; the createMutationGate shape gives Wave 4 routes a clean handoff. Tests + boot-loud rejection of --require-auth + no token are particularly nice.

Comment thread packages/cli/src/serve/capabilities.ts Outdated
Comment thread packages/cli/src/serve/auth.ts Outdated
Comment thread packages/cli/src/serve/auth.ts Outdated
Comment thread docs/users/qwen-serve.md Outdated
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Acknowledging the Code Coverage Summary — informational only, no action needed.

Coverage on the files this PR touches (post-21f46af0):

File Lines Notes
packages/cli/src/serve/auth.ts 88.59% New createMutationGate factory + 4-cell matrix all covered by auth.test.ts. Uncovered ranges are pre-existing bearerAuth SP/HTAB fall-throughs the PR didn't touch.
packages/cli/src/serve/capabilities.ts 96.82% Only uncovered lines (126-127) are the defensive "feature predates protocol version" fall-through; not reachable until a v2 protocol lands and would require synthesizing a registry entry to exercise.
packages/cli/src/serve/runQwenServe.ts 79.91% All new branches (requireAuth && !token boot refusal, requireAuth happy-path stderr breadcrumb, gated /health 401) covered by the new runQwenServe tests. Pre-existing uncovered ranges are the SIGINT force-close timer paths the PR didn't touch.
packages/cli/src/serve/server.ts 84.47% All new code (exposeHealthPreAuth branch, conditional require_auth advertisement, mutate() route threading) exercised. Pre-existing uncovered are 5xx error-fallback branches.

No coverage threshold was breached and no specific lines are flagged in the bot's report; closing as no-action.

doudouOUC added 2 commits May 17, 2026 18:39
Implements issue #4175 Wave 4 PR 15. Adds the centralized
state-changing-route gate that Wave 4 follow-ups (memory CRUD, file
edit, MCP restart, device-flow auth) will reuse, plus the
`--require-auth` deployment knob that hardens the loopback developer
default for shared dev hosts / CI runners.

- `createMutationGate({ tokenConfigured, requireAuth })` factory in
  serve/auth.ts — per-route middleware with a 4-cell behavior matrix:
  pass-through under `requireAuth` or any token configured;
  `401 token_required` for `strict: true` routes on no-token loopback
  defaults; baseline pass-through otherwise.
- Existing Wave 1-2 mutation routes (POST /session, /session/:id/{load,
  resume,prompt,cancel,model}, /permission/:requestId) opt into the
  default non-strict factory call as the centralization marker. Wave 4
  routes will pass `{ strict: true }` to require a token even on
  loopback.
- `--require-auth` CLI flag + `ServeOptions.requireAuth`. Boot refuses
  without a token; closes the `/health` exemption when on so loopback
  `/health` also requires bearer auth; stderr breadcrumb so the
  hardened mode is visible in journald/docker logs.
- Conditional `require_auth` capability tag advertised only when the
  flag is on. New `CONDITIONAL_SERVE_FEATURES` registry primitive so
  future per-deployment toggles follow the same shape.
- 5 new unit tests in auth.test.ts covering the gate matrix; 5 added
  in server.test.ts for capability advertisement, conditional tag,
  /health 401 under --require-auth, and runQwenServe boot
  refusal + happy path. 245/245 serve tests pass; typecheck + eslint
  clean.

Refs: #4175

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
Three small follow-ups from the automated reviewers on PR #4236:

1. **Drop misleading `--require-auth` from `token_required` error
   message** (Copilot inline auth.ts:262). The strict-mode 401
   listed three remediations but `--require-auth` is paired-required
   with a token at boot — naming it standalone would loop the operator
   into a different boot error. Keep the two valid standalone fixes
   (env var, --token); add inline note explaining the omission.
   `auth.test.ts` regex updated to `not.toMatch(/--require-auth/)`
   to anchor the new wording.

2. **Mention `/health` gating in `--require-auth` CLI description**
   (auto-reviewer Medium #2). Operators flipping the flag without
   reading the protocol doc would get paged when k8s/Compose probes
   start 401-ing. One sentence in the yargs description prevents that.

3. **Drift insurance comment between registry and
   `CONDITIONAL_SERVE_FEATURES`** (auto-reviewer Low #3). Document
   the four-step procedure for adding a new conditional tag so a
   future contributor doesn't update only the registry and silently
   advertise the tag unconditionally. Notes the Map<predicate>
   refactor as the right move when a second tag lands.

Deferred (not in this fix-up):
- Module-level PASSTHROUGH singleton (High #1) — micro-optimization,
  unmeasurable.
- Map<feature, predicate> for conditional features (High #2) —
  premature abstraction with one tag.
- Per-route `// non-strict marker` comments (Medium #1) — noise.
- `@see` cross-ref in types.ts (Low #2) — sugar.
- JSDoc bullet-list vs table (Low #1) — current format is fine.

Refs: #4175 #4236

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
@doudouOUC doudouOUC force-pushed the feat-serve-mutation-gate-require-auth branch from 21f46af to da4bf18 Compare May 17, 2026 10:40
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Rebased onto origin/main (resolves conflicts with merged PR 8 #4232 + 3 other commits).

origin/main advanced 4 commits since this branch was opened — most relevant: PR 8 (#4232 feat(serve): add session-scoped permission route) which touched the same files (`server.ts`, `server.test.ts`, `capabilities.ts`).

Conflicts and how they were resolved

File Conflict Resolution
packages/cli/src/serve/server.ts PR 8 added a NEW route POST /session/:id/permission/:requestId immediately above the legacy POST /permission/:requestId. PR 15 had threaded mutate() onto the legacy route. The conflict landed on the route registration line. Kept PR 8's new session-scoped route AND threaded mutate() onto BOTH the new session-scoped route and the legacy route — both are mutation routes per the issue's "Route safety definitions" section, so consistency demands both adopt the helper.
packages/cli/src/serve/capabilities.ts Auto-merged: PR 8's session_permission_vote registry entry sits above PR 15's conditional require_auth entry. Auto-merge result preserved; both tags coexist correctly.
packages/cli/src/serve/server.test.ts Auto-merged: PR 8's expanded EXPECTED_STAGE1_FEATURES (now 14 entries with session_permission_vote) flows into PR 15's EXPECTED_REGISTERED_FEATURES = [...EXPECTED_STAGE1_FEATURES, 'require_auth'] constant. Auto-merge result preserved; the registry-superset-of-advertised relationship still holds with the post-PR-8 list.

Validation post-rebase

  • npx vitest run packages/cli/src/serve258/258 pass (was 245 pre-rebase; +13 from PR 8's bridge + server tests)
  • npm run typecheck --workspace packages/cli → clean
  • npx eslint packages/cli/src/serve packages/cli/src/commands/serve.ts → clean

Branch state

  • da4bf18ce137... — fixup (address PR #4236 review feedback)
  • a7cd64779... — main commit (mutation gating helper and --require-auth)

Force-pushed with --force-with-lease to keep the linear two-commit history. PR is back to mergeable: MERGEABLE (the BLOCKED state status is the standard "awaiting human review approval" gate).

Comment thread docs/developers/qwen-serve-protocol.md Outdated
Comment thread packages/cli/src/serve/capabilities.ts Outdated
Comment thread packages/cli/src/serve/server.ts
Five small follow-ups from @wenshao + DeepSeek (via Qwen Code /review)
on PR #4236:

1. **Map<predicate> refactor for `CONDITIONAL_SERVE_FEATURES`**
   (review threads #3254467192 + #3254485912). Two reviewers asked
   for the same shape on the grounds that the `Set` + per-feature
   `if`-branch needed FOUR coordinated changes per new conditional
   tag and silently fail-CLOSED when the branch was missed. The Map
   collapses the predicate-decision and the set-membership into one
   entry per feature — adding a new conditional tag is now two
   coordinated changes (registry + Map entry) and a missing predicate
   is a TypeScript error rather than a silent omission. JSDoc
   updated.

2. **Drift-insurance test that iterates `CONDITIONAL_SERVE_FEATURES`**
   (review thread #3254467192 option 1, layered on top of #1).
   `server.test.ts` now walks every Map entry and asserts the
   predicate accepts/rejects as expected; future entries that don't
   add an assertion branch fail the test loudly so a missing
   predicate cannot ship silently. Adoption-of-record for the Map
   shape rather than relying on a hand-maintained invariant.

3. **Cache `strictDenier` for allocation symmetry** (review thread
   #3254467193). Wave 4 PRs will mount strict mode on multiple
   routes; without the cache each `mutate({strict:true})` call would
   allocate a fresh 401 closure. Now both the passthrough and the
   strict denier are pre-built singletons. Identity assertion in
   `auth.test.ts` anchors the cache so a future change that loses it
   surfaces in CI.

4. **Doc cosmetic — extra blank line in qwen-serve.md** (review
   thread #3254467198). Single blank line between the `>` quoted
   example and the following non-quoted bash block now.

5. **Doc correctness — `require_auth` is post-auth confirmation**
   (review thread #3254485910 from DeepSeek). When `--require-auth`
   is on, the global `bearerAuth` middleware gates every route
   including `/capabilities`, so an unauthenticated client cannot
   pre-flight `caps.features` to discover that auth is required —
   the discovery surface is the 401 response body itself. Both
   `qwen-serve.md` and `qwen-serve-protocol.md` rewritten to
   describe the tag as a post-authentication confirmation, matching
   the auth.ts JSDoc which already stated this correctly.

Trade-offs documented (no code change):

- **Body-parser ordering** (review thread #3254485915 from DeepSeek)
  noted as a comment block in `auth.ts`. Strict-mode 401 fires AFTER
  `express.json()` because the gate is per-route middleware. On
  loopback no-token defaults a strict route therefore parses the
  request body before refusing it — bounded by
  `express.json({limit: '10mb'})` × `--max-connections` (256
  default). Strict routes Wave 4 actually adds carry small bodies in
  legitimate use, so this isn't a production hot path. Future routes
  accepting large bodies should lift the gate to app-level (maintain
  a strict-path Set in `createServeApp`); flagged as a Wave 4
  follow-up rather than re-architecting the helper.

- **`bearerAuth` body-shape inconsistency** (review thread
  #3254467197 from @wenshao) flagged as a Wave 4 cross-PR
  follow-up. `bearerAuth` returns `{error: 'Unauthorized'}` while
  the strict gate returns `{code: 'token_required', error: '...'}`;
  SDK clients have to branch on both shapes. Standardizing
  `bearerAuth` to also carry a `code` field is orthogonal to this
  PR's scope.

Validation: 260/260 cli serve tests pass (was 258 — added the drift
insurance test + strict denier identity test); typecheck + eslint
clean.

Refs: #4175 #4236

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
@doudouOUC doudouOUC self-assigned this May 17, 2026
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@doudouOUC doudouOUC requested a review from wenshao May 17, 2026 11:16
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

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

Verified locally on 6149866 (force-push after the round-2 review). All eight concerns from the prior two rounds are addressed, and the four-cell behavior matrix verified end-to-end against a real qwen serve listener.

Local validation

Command Result
npm run typecheck --workspace packages/cli exit 0
npx vitest run packages/cli/src/serve 271/271 passed across 5 files
npx eslint packages/cli/src/serve packages/cli/src/commands/serve.ts 0 problems

Manual /capabilities + /health matrix probe

Spawned the daemon in all three operationally distinct modes and confirmed behavior:

Default (no --require-auth):

  • /health no auth → 200 (loopback exemption intact)
  • /capabilities no auth → 200, require_auth absent from features
  • stderr: bearer auth disabled (loopback default)

--require-auth without a token:

  • Boot refuses with Refusing to start with --require-auth set but no bearer token configured. Set QWEN_SERVER_TOKEN or pass --token, or omit --require-auth ...
  • Exit code 1 (boot-loud, matches the existing non-loopback check)

--require-auth + token:

  • stderr breadcrumb fires: --require-auth enabled (bearer token mandatory on every route, including loopback /health).
  • /health no auth → 401 (loopback exemption correctly closed)
  • /health with Authorization: Bearer … → 200
  • /capabilities no auth → 401 {"error":"Unauthorized"}
  • /capabilities with auth → 200, require_auth present in features
  • Wrong token → 401 {"error":"Unauthorized"}

The capability tag only appears post-authentication, matching the docs/developers/qwen-serve-protocol.md update describing it as a post-auth confirmation rather than a pre-flight discovery surface.

Prior-round concerns vs HEAD 6149866

  1. Error-message remediation list named --require-auth standalone (Copilot)auth.ts:276-291 lists only QWEN_SERVER_TOKEN and --token; auth.test.ts:85 anchors not.toMatch(/--require-auth/). Inline comment in the error block explains why.
  2. Strict denier closure allocated per route call (allocation symmetry)auth.ts:276 pre-builds strictDenier singleton beside passthrough; factory body reduces to a ternary. auth.test.ts:105 asserts identity across multiple gate({strict: true}) calls.
  3. bearerAuth vs createMutationGate body-shape drift (cross-PR) → Acknowledged as Wave 4 follow-up. Probe 5 above confirms the two shapes still differ ({error: 'Unauthorized'} vs {code: 'token_required'}); SDK normalization at the client boundary is the agreed near-term workaround.
  4. docs/users/qwen-serve.md extra blank line → Fixed.
  5. require_auth advertised as pre-flight discovery in protocol doc — but /capabilities is itself behind bearerAuth when ondocs/developers/qwen-serve-protocol.md:19 rewritten; the tag is now correctly described as a post-authentication confirmation, with the 401 response body called out as the actual discovery surface for unauthenticated callers.
  6. CONDITIONAL_SERVE_FEATURES was a Set + per-feature if-branch (4-step coordination, silent fail-CLOSED on miss)capabilities.ts:105-110 converted to ReadonlyMap<ServeFeature, (toggles) => boolean>; predicate lives next to the key, missing predicate is a TS error rather than a silent miss. 4-step process down to 2.
  7. No test pins the conditional-feature contractserver.test.ts:411 honors every entry in CONDITIONAL_SERVE_FEATURES iterates the Map and throws new Error(...) for any unrecognized entry, forcing the contributor adding the next conditional tag to also add the assertion branch. Drift insurance as adoption-of-record.
  8. express.json() runs before strict gate → body parsed before refusal → Documented as a known trade-off in auth.ts:256-269 (loopback-only surface, ~2.5GB transient worst case at 256 connections × 10MB, Wave 4 routes with small bodies); the comment also points at the future fix (lift strict-path gates to app-level via a Set in createServeApp).

Out-of-scope items the author explicitly deferred

Per the PR description, no Wave 1-2 mutation routes get flipped to { strict: true } yet — they thread mutate() as a centralization marker only. SDK-side DaemonClient adapter for the require_auth tag is deferred to a Wave 4 PR that has a consumer for it. Both are reasonable and consistent with "default off, bit-for-bit backward compatible."

CI

gh pr checks 4236 currently shows Classify PR: queued — CI started after force-push but the test matrix hasn't completed yet. Local 271-test run + 6-probe manual matrix gives me sufficient signal to approve now; the CI run should be a sanity check rather than a discovery vector.

LGTM.

@wenshao

wenshao commented May 17, 2026

Copy link
Copy Markdown
Collaborator

Local validation report

Verified on 6149866 (post-force-push HEAD). Each row is the exact command and result.

Unit / lint / typecheck

Command Result
npm run typecheck --workspace packages/cli exit 0
npx vitest run packages/cli/src/serve 271/271 passed across 5 files
npx eslint packages/cli/src/serve packages/cli/src/commands/serve.ts 0 problems

Real qwen serve behavior-matrix probe

The PR's Manual / release pipeline test-plan item was unchecked. Filled in by spawning the daemon in all three operationally distinct modes from packages/cli/dist/index.js and curl-probing the live HTTP surface:

Mode A — default (no --require-auth, loopback no-token):

qwen serve: bearer auth disabled (loopback default).

GET /health           → HTTP 200    (loopback exemption preserved)
GET /capabilities     → HTTP 200
  features:           [..., session_events, typed_event_schema, ...]
  require_auth:       ABSENT

Mode B — --require-auth without a token (boot check):

qwen serve: Refusing to start with --require-auth set but no bearer token
  configured. Set QWEN_SERVER_TOKEN or pass --token, or omit --require-auth
  to keep the loopback developer default.

exit 1

Refusal is boot-loud, matches the existing non-loopback "no token" check shape.

Mode C — --require-auth with a token (the hardened mode):

qwen serve: --require-auth enabled (bearer token mandatory on every route,
  including loopback /health).

GET /health           no Auth   → HTTP 401            (loopback exemption closed)
GET /health           Bearer ✓  → HTTP 200
GET /capabilities     no Auth   → HTTP 401 {"error":"Unauthorized"}
GET /capabilities     Bearer ✓  → HTTP 200
  features:                       [..., permission_vote, require_auth]
  require_auth:                   PRESENT
GET /capabilities     Bearer ✗  → HTTP 401 {"error":"Unauthorized"}

The conditional require_auth tag only materializes once the operator opts in AND the caller authenticates — matching the protocol-doc framing as a post-authentication confirmation rather than a pre-flight discovery surface.

Body-shape observation (still applies, deferred to Wave 4)

The two failure paths still surface different bodies for "missing auth":

  • Global bearerAuth 401 (e.g., wrong-token probe above): {"error":"Unauthorized"}
  • createMutationGate strict 401 (verified via auth.test.ts): {"code":"token_required","error":"…"}

Author acknowledged on the previous round as a Wave 4 follow-up; SDK-side normalization can land alongside the first strict route that has a consumer. Not blocking PR 15.

CI

gh pr checks 4236 was still showing Classify PR: queued at approval time — GitHub CI restarted after force-push and the test matrix wasn't done. Local 271-test run + 6-probe manual matrix is sufficient signal; CI is a sanity backstop on top.

@wenshao wenshao merged commit 4e06967 into main May 17, 2026
9 checks passed
doudouOUC added a commit that referenced this pull request May 18, 2026
…4250)

* refactor(serve): add FileSystemService boundary (#4175 PR 18)

Introduce a per-request workspace filesystem boundary inside the
`qwen serve` daemon. The boundary centralizes path canonicalization,
symlink-aware boundary checks, ignore/trust policy, size/binary
limits, and audit hooks behind a single typed surface — preparing
PR 19 (read-only file routes) and PR 20 (write/edit routes) to
share a guarded chokepoint instead of re-implementing path safety
per route.

Wave 4 PR 18 of #4175 — pure refactor, no new HTTP routes; depends
on PR 12 (#4241) and PR 15 (#4236), both merged.

New module under `packages/cli/src/serve/fs/`:

- `paths.ts` extracts `canonicalizeWorkspace` from `httpAcpBridge.ts`
  (re-exported there for backward compatibility) and adds:
  - `ResolvedPath` brand and `Intent` union (read/write/edit/list/
    glob/stat) with exhaustiveness checks at the trust gate
  - `hasSuspiciousPathPattern` — detects NTFS ADS, 8.3 short names,
    long-path prefixes, UNC paths, trailing dots, DOS device names,
    and three-or-more-dot path components (claude-code-style)
  - `findExistingAncestor` with explicit ENOTDIR rejection so a
    regular file in a path component throws `parse_error` rather
    than passing boundary inspection and 500-ing later
  - `resolveWithinWorkspace` running a chain-aware realpath check
    with ENOENT-tolerant ancestor walk for write/stat intents
- `errors.ts` defines `FsError` / `FsErrorKind` plus `wrapAsFsError`,
  which categorizes raw `fs.promises` errnos (EACCES → permission_
  denied, ELOOP → symlink_escape, ENOTDIR → parse_error, etc.) so
  body-level failures emit audit events instead of escaping
  uncategorized
- `policy.ts` carries `MAX_READ_BYTES` (256 KiB), `MAX_WRITE_BYTES`
  (5 MiB), `BINARY_PROBE_BYTES` (4 KiB), `shouldIgnore` (file/
  directory aware), and `assertTrustedForIntent` with an
  exhaustive switch over `Intent`
- `audit.ts` emits typed `fs.access` / `fs.denied` `BridgeEvent`
  frames with SHA-256-hashed paths, optional raw-path passthrough
  via `QWEN_AUDIT_RAW_PATHS=1`, and discriminator `kind` fields so
  SDK consumers can exhaustively narrow `event.data`
- `workspaceFileSystem.ts` — `WorkspaceFileSystem` interface +
  `createWorkspaceFileSystemFactory` with eight methods (resolve,
  stat, readText, readBytes, list, glob, writeText, edit). Every
  body method funnels failures through `recordAndWrap`, which
  wraps raw fs errors and always emits an `fs.denied` audit event
  before rethrowing. `readText` enforces `MAX_READ_BYTES` *before*
  delegating to the slurping core service so unbounded requests
  against multi-gigabyte files can no longer OOM the daemon.
  `glob` realpath-checks each hit against the canonical workspace
  and reports filtered escapes via a single aggregated `fs.denied`
  event with the dropped count
- `index.ts` is the barrel re-export PR 19/20 will import from

Modified files:

- `packages/cli/src/serve/httpAcpBridge.ts` — extracted
  `canonicalizeWorkspace` to `fs/paths.ts`; the bridge re-exports
  it so existing callers in `server.ts` and `runQwenServe.ts` keep
  working
- `packages/cli/src/serve/server.ts` — added
  `fsFactory?: WorkspaceFileSystemFactory` to `ServeAppDeps`;
  `createServeApp` builds a strict default (`trusted: false`,
  warn-once no-op `emit`) when none is injected so a future
  refactor that forgets `fsFactory` injection cannot silently
  allow writes against an untrusted workspace; factory parked on
  `app.locals` for PR 19/20 route handlers
- `packages/core/src/index.ts` — re-exports `Ignore`,
  `loadIgnoreRules`, and `LoadIgnoreRulesOptions` from
  `utils/filesearch/ignore.js` for cli consumption

411 serve tests pass; typecheck clean.

Engineering principles checklist:
- [x] Independently mergeable (no new routes, no new capability tag)
- [x] Backward compatible (no removed routes / event fields / CLI behavior)
- [x] Default off (no public surface change; PR 19/20 will activate routes)
- [x] qwen serve Stage 1 routes preserved
- [x] Gradual migration (PR 19/20 will adopt the boundary)
- [x] Reversible (single PR rollback)
- [x] Tests-first (101 unit tests across the new module + contract test)

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): address PR review feedback (#4250)

Codex + Copilot review found 8 substantive issues against
7b0db4c; this commit fixes all of them. The first two are
P1 build-breakers introduced by the pre-commit `eslint --fix`
auto-promoting value imports to `import type` — 31 fs tests
were failing post-commit until this fix.

Issue list (links to PR comments at #4250):

1. Import type erased runtime values —
   workspaceFileSystem.ts:10-15. eslint's
   consistent-type-imports rule rewrote
   `import { Ignore, StandardFileSystemService, loadIgnoreRules,
   type WriteTextFileOptions }` -> `import type { ... }` because
   it saw type-only usage of `Ignore`. That erased
   `loadIgnoreRules` (called at runtime) and
   `StandardFileSystemService` (constructed at runtime), causing
   TS1361/TS2206 and runtime ReferenceErrors. Restored as a
   value import with inline `type` modifiers per-symbol and an
   eslint-disable line + comment so future autofixes don't
   repeat the regression.

2. Same import-type erasure in contract.test.ts:14 — `isFsError`
   was lumped under `import type` even though it's called at
   runtime. Same fix shape.

3. edit() OOM hole — workspaceFileSystem.ts. The earlier review
   pass added a pre-stat MAX_READ_BYTES gate to readText but
   missed edit, which fsp.readFile's the whole file before any
   size check. Multi-GB targets inside the workspace could OOM
   the daemon. Now stat-first; refuses above the cap with
   file_too_large; also rejects binary files (string indexOf
   over arbitrary bytes is meaningless).

4. glob accepted absolute / device patterns —
   workspaceFileSystem.ts. The ..-segment check stopped lexical
   traversal but /etc/** / C:\\Users\\foo\\** /
   \\\\server\\share\\** / //server/share/** still reached
   globAsync, walking outside the workspace before per-hit
   filtering dropped the results. Now rejects these patterns
   up-front with parse_error so no I/O happens outside.

5. glob ignore filter probed every hit as `file` —
   workspaceFileSystem.ts. The underlying `ignore` library
   needs a trailing-slash probe for dist/ / .git/-style
   directory patterns; probing as `file` silently leaked
   directory matches. Now lstats each hit and routes 'directory'
   vs 'file' to shouldIgnore so dir-only ignore rules actually
   match.

6. ReadTextOptions.line off-by-one — workspaceFileSystem.ts. The
   public option was documented as 1-based but forwarded as-is
   to readFileWithLineAndLimit, which is 0-based. A request with
   line: 1 returned content starting at the second line. Now
   converts 1-based -> 0-based at the boundary; doc clarified;
   truncation check uses the converted index.

7. ServeAppDeps.fsFactory JSDoc said trusted=true — server.ts:96.
   Stale from before the strict-default refactor in the same
   review pass. Rewrote to match the actual trusted: false +
   warn-once emit behavior.

8. MAX_READ_BYTES JSDoc said reads above cap return truncated —
   policy.ts:18. Stale from before the hard-cap refactor; now
   correctly states the cap throws file_too_large and that soft
   truncation only applies under the cap via enforceReadSize.

7 new tests cover the new behaviors:
- POSIX-absolute pattern rejection
- Win32 / UNC pattern rejection (4 variants)
- directory-pattern ignore (dist/)
- edit file-too-large
- edit binary refusal
- readText line: 1 returns from first line
- readText line: 2 starts from second line

418/418 serve tests pass; typecheck + eslint clean.

Deferred follow-ups (per PR review reply):
- glob maxResults is applied after globAsync materializes every
  match. A streaming iterator (glob.iterate) would bound the
  walk too. Non-trivial; tracked as a separate hardening
  follow-up since current behavior is correctness-safe (just
  not optimal under huge trees).
- Per-path glob escape hash in audit hint (currently aggregated
  count) — can revisit once PR 19 wires the routes and we see
  real audit volume.
- EVENT_SCHEMA_VERSION migration mechanism — orthogonal; the
  whole BridgeEvent schema lacks one and that's a Wave 5+
  concern.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 3 review-round-2 findings (#4250)

The wenshao + DeepSeek reviewer pass on a81ada4 surfaced 3 more
issues; this commit fixes them.

1. Dangling-symlink write escape (paths.ts) — Critical security
   bug. A request like `write /ws/escape` where `<ws>/escape` is
   a symlink whose target doesn't exist YET would pass the
   ENOENT-tolerant ancestor walk: realpath fails ENOENT, the
   walk-up returns `<ws>` as ancestor, the canonical becomes
   `<ws>/escape`, containment passes — but the eventual write
   follows the symlink and creates the file at the symlink
   target outside the workspace. lstat-then-readlink before the
   ancestor walk catches this; the symlink target is itself
   resolved via the deepest existing ancestor so macOS
   /private/var canonicalization stays consistent with
   boundCanonical (an absolute target inside the workspace
   tmpdir would otherwise have been false-flagged on macOS).

2. glob realpath catch over-reported symlink_escape
   (workspaceFileSystem.ts) — every realpath failure inside the
   per-hit boundary loop was counted as `symlink_escape`. EIO,
   EACCES, ENAMETOOLONG, EBUSY are environmental failures, not
   security events; mislabeling them poisoned the audit signal
   for operators trying to investigate genuine escape attempts.
   Now distinguished: ENOENT/ELOOP count as escapes; other
   errnos count as transient errors and emit a separate
   aggregated `fs.denied` with errorKind: 'permission_denied'.

3. policy.ts:enforceReadSize JSDoc said the boundary "intentionally
   does NOT throw" — stale after a81ada4's hard-cap refactor.
   Rewrote to clarify the helper is the soft truncation gate that
   only fires under the hard cap; readText itself enforces the
   hard cap with file_too_large via its pre-stat check. The
   readme/contract is now consistent with workspaceFileSystem.ts.

2 new tests:
- dangling symlink targeting outside-workspace path → symlink_escape
- dangling symlink targeting future-inside-workspace path → succeeds
  (ahead-of-mkdir flow for atomic-write-via-rename)

420/420 serve tests pass; typecheck clean.

Remaining tracked follow-ups (per PR review reply):
- list/glob brand cast (P2 deferred per PR description)
- glob audit pathHash hashes pattern not paths
- edit() TOCTOU read-modify-write race (atomic-via-temp + rename)
- wrapAsFsError ENOSPC/EIO mapping to a distinct kind
- runQwenServe → fsFactory injection integration test
- glob maxResults streaming (glob.iterate)

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): cross-platform ENOTDIR detection (#4250 CI fix)

Windows CI test failure on a81ada4 surfaced a real cross-platform
bug in `findExistingAncestor`. POSIX returns `ENOTDIR` when
`fs.stat` traverses through a non-directory in a path component
(e.g. `${ws}/file.txt/leaf` where `file.txt` is a regular file).
**Windows returns `ENOENT` for the same case.** The errno-based
guard added in a81ada4 only branched on `ENOTDIR`, so the
Windows path silently fell through to the ancestor walk and the
boundary returned a "canonical" the eventual write could not
honor — `WorkspaceFileSystem - audit always emits on body errors >
rejects ENOTDIR ancestor walk with parse_error rather than
passing boundary` failed with `expected false to be true` on the
windows-latest runner.

Fix: switch from errno-based detection (platform-divergent) to
dirent-kind detection. After `fs.stat` succeeds during the
walk-up, if the existing ancestor is NOT a directory AND there
are unresolved tail components, throw `parse_error`. Both `ENOENT`
and `ENOTDIR` from `fs.stat` are now treated as "the *current*
path doesn't resolve, keep walking" — the post-walk kind check
fires regardless of which errno surfaced. Cross-platform-safe.

The local 110/110 fs tests still pass on macOS/Linux; the Windows
case will exercise the kind-check branch on next CI run.

macOS CI failures on the same workflow run (`InputPrompt.test.tsx`
placeholder reuse, `SettingsDialog.test.tsx` 5s timeout) are pre-
existing flaky UI tests, NOT touched by this PR.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 4 review-round-3 findings (#4250)

DeepSeek's third review pass (after b38e821) flagged four more
issues; this commit fixes all of them.

1. Multi-hop dangling symlink bypass (paths.ts) — Critical
   security fix. The earlier single-readlink fix at efd7a46
   was bypassed by chained dangling symlinks: <ws>/leak ->
   <ws>/middle -> /scratch/evil where every layer is a symlink
   and the final target doesn't exist. The fix only readlink'd
   the first hop (<ws>/middle), saw it was inside the workspace
   via findExistingAncestor, and let the chain through. The OS
   write at <ws>/leak would then follow both hops and create
   /scratch/evil. Now loops lstat + readlink up to
   MAX_ANCESTOR_HOPS, tracks visited inodes for cycle detection,
   and only validates containment on the fully-dereferenced
   leaf. Cycle detection rejects with symlink_escape; chains
   that exceed the hop bound also surface as symlink_escape
   with a "too long or contains a cycle" hint.

2. opts.line validation (workspaceFileSystem.ts) — the docstring
   committed to "1-based positive integer" but Infinity / floats
   / negative values flowed through to readFileWithLineAndLimit
   and degraded silently. Now enforces Number.isSafeInteger +
   line >= 1 at the boundary; everything else throws
   parse_error. Test covers Infinity, -Infinity, 0, -1, 1.5,
   NaN.

3. io_error FsErrorKind (errors.ts) — wrapAsFsError previously
   conflated EIO/EBUSY/ENAMETOOLONG/EMFILE/ENFILE/ENOSPC/ETXTBSY
   under permission_denied. Monitoring pipelines that key on
   errorKind for security alerting would page oncall on a full
   disk. New io_error kind (HTTP 503) maps the environmental-
   failure errnos with distinct hints. EACCES/EPERM stay on
   permission_denied (literal access denial); EIO (failing
   disk), EBUSY (busy file), ENAMETOOLONG (PATH_MAX),
   EMFILE/ENFILE (fd exhaustion), ENOSPC (df -h reporting
   100%), ETXTBSY (text-busy) all route to io_error.

4. glob audit kind taxonomy (workspaceFileSystem.ts) — three-way
   classification mirrors wrapAsFsError so the per-hit realpath
   catch surfaces ENOENT/ELOOP -> symlink_escape, EACCES/EPERM
   -> permission_denied, everything else -> io_error. Each
   class emits its own aggregated fs.denied event.

5. edit() matchedIgnore (workspaceFileSystem.ts) — readText and
   writeText both stamp matchedIgnore in their access audit;
   edit didn't, so operators monitoring fs.access events couldn't
   distinguish edits to .gitignore'd files (build artifacts,
   logs) from edits to tracked source. Added the same
   shouldIgnore + matchedIgnore plumbing that readText uses.

8 new tests:
- multi-hop dangling symlink (security)
- symlink cycle (security)
- ENOSPC/EIO/EBUSY/ETXTBSY/ENAMETOOLONG -> io_error mapping
- io_error -> HTTP 503
- EMFILE/ENFILE updated to io_error (was permission_denied)
- opts.line rejects Infinity/-Infinity/0/-1/1.5/NaN
- edit() audit records matchedIgnore on .log file

426/426 serve tests pass; typecheck clean.

Remaining tracked follow-ups (per PR review reply):
- list/glob brand cast (P2 deferred per PR description)
- glob audit pathHash hashes pattern not paths
- edit() TOCTOU read-modify-write race (atomic-via-temp + rename) — pinned to PR 20
- runQwenServe → fsFactory injection integration test — pinned to PR 19
- glob maxResults streaming (glob.iterate)

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 3 review-round-4 critical findings (#4250)

DeepSeek's fourth review pass surfaced three more critical bugs;
all are fixed in-PR.

1. TOCTOU symlink substitution in readText/readBytes/edit
   (workspaceFileSystem.ts) — Critical. fsp.stat(p) and the
   subsequent lowFs.readTextFile(p) (or fsp.readFile / fsp.readFile
   for edit) are two separate syscalls. An attacker who can write
   into the workspace can swap p from a regular file to a symlink
   pointing outside between the two calls; pre-stat sees the
   original, the read follows the swap.

   Fix: assertInodeStableAfterRead(p, preIno) — after each read,
   re-lstat p; reject with symlink_escape if the path is now a
   symlink (post.isSymbolicLink()) or its inode changed (preIno
   !== post.ino, with a 0-ino fallback for procfs / virtual
   mounts that don't report meaningful inodes). Catches the
   swap-and-leave attack and the swap-and-keep-swapped attack.
   Residual race (attacker swaps back AFTER our read but BEFORE
   our lstat) is much smaller than the original window and
   outside PR 18's threat model; fd-based reading via
   fsp.open + fileHandle.read would close it entirely but
   requires a new variant of lowFs that takes a FileHandle —
   tracked as a follow-up.

2. UTF-8 truncation corruption in readText (workspaceFileSystem.ts)
   — Critical. buf.subarray(0, sizeOutcome.bytesToRead).toString
   ('utf-8') silently emits U+FFFD when bytesToRead falls
   mid-codepoint (CJK, emoji). A downstream consumer parsing
   JSON or source code over the truncated content would see
   broken trailing bytes; meta.truncated would be true but the
   prefix is corrupt. A subsequent edit() with the corrupted
   string as oldText would also fail to match the on-disk content.

   Fix: safeUtf8Truncate(buf, maxBytes) walks back from maxBytes
   through any continuation bytes (0b10xxxxxx), then verifies
   the leading byte's full sequence fits within the cap; drops
   the leading byte if it doesn't. The result is always a clean
   prefix at a valid codepoint boundary. Test pins '中文测试' (12
   bytes / 3 bytes per char) truncated at 7 bytes -> '中文' (no
   U+FFFD).

3. glob opts.cwd bypasses workspace boundary
   (workspaceFileSystem.ts) — Critical. opts.cwd was used
   directly as the glob root with no validation against
   boundWorkspace. ResolvedPath is a brand cast and a stale
   or forged value lets a glob('**/*', { cwd: '/etc' })
   enumerate files outside the workspace. The pattern-side
   absolute / UNC checks added in a81ada4 only constrain
   the *pattern*; cwd is the actual hazard.

   Fix: at the entry point of glob(), path.resolve cwd and
   isWithinRoot-check against boundWorkspace. Throws
   path_outside_workspace if cwd is outside, even when the
   pattern itself is harmlessly relative. Test pins the case
   with cwd: scratch (outside workspace).

3 new tests:
- readText with mid-operation symlink swap -> symlink_escape
- safeUtf8Truncate keeps CJK codepoints intact at 7-byte cap
- glob with opts.cwd outside workspace -> path_outside_workspace

429/429 serve tests pass; typecheck + eslint clean.

Remaining tracked follow-ups (Post-PR-18 hardening, in #4175):
- list/glob brand-cast contract (PR 19)
- runQwenServe → fsFactory injection contract test (PR 19)
- edit() write-side TOCTOU + atomic-via-temp + expectedHash (PR 20)
- glob audit pathHash (independent audit.ts commit)
- glob maxResults streaming (independent hardening)
- glob pattern preflight refactor to reuse hasSuspiciousPathPattern (cosmetic)

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 7 review-round-5 findings (#4250)

DeepSeek round-5 surfaced 9 comments; 7 are real findings (the
other 2 — UTF-8 truncation + glob opts.cwd — already fixed in
5468342 from round-4 and replied-to inline). Stack on round-4.

1. edit() empty-oldText silent-prepend (workspaceFileSystem.ts) —
   Critical silent data corruption. JS `''.indexOf('')` returns 0,
   so without an empty-string guard
   `current.slice(0,0) + newText + current.slice(0)` = `newText +
   current` — silently prepends `newText` to the whole file with
   a success audit event. PR 20 routes that thread user-supplied
   `oldText` verbatim must not be able to trigger this. Now
   throws `parse_error` BEFORE the read with a hint explaining
   why empty matches are rejected.

2. DOS device name regex misses bare names + first-extension forms
   (paths.ts) — Windows attack surface. The earlier
   /\.(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i only caught the
   last-extension form (file.CON). NTFS reserves these names
   regardless of extension: CON, NUL, CON.txt, NUL.dat,
   CON.foo.bar are ALL reserved device handles. New regex
   /(^|\.)(CON|...|LPT[1-9])(\.|$)/i covers all four positions
   (bare / first-ext / last-ext / middle-ext) while still
   admitting legitimate substrings (BACON, concat.txt, precon.go).

3. Buffer.byteLength replaces Buffer.from for size-only checks
   (workspaceFileSystem.ts) — 5 MB heap allocation per write
   eliminated. `writeText` and `edit()` previously materialized
   the entire UTF-8 payload (up to MAX_WRITE_BYTES) just to read
   `.length`; `Buffer.byteLength(content, 'utf-8')` returns the
   count without allocating.

4. edit() error message includes oldText snippet
   (workspaceFileSystem.ts) — Production debuggability. The
   earlier hint was just "edit() expects oldText to appear
   verbatim in the file" — at 3 AM an operator can't tell whether
   the mismatch is whitespace, a stale file, or a wrong target.
   Now includes a JSON-quoted truncated snippet (max 80 chars +
   ellipsis) of the searched-for text.

5. recordAndWrap forwards FsError message into fs.denied audit
   (workspaceFileSystem.ts + audit.ts) — Audit observability gap.
   Audit consumers debugging an incident saw `errorKind` + `hint`
   but lost the underlying OS error detail (path, errno text,
   byte count). FsDeniedAuditPayload now carries an optional
   `message` field; recordAndWrap forwards `fs.message`
   automatically.

6. EISDIR / ENOTDIR distinct hints (errors.ts) — UX. Both shared
   the same hint "a path component is not a directory where one
   was expected" — for EISDIR (path IS a directory but a file was
   expected) the wording was reversed. Now distinct hints with
   the errno name explicitly cited.

7. kindFromStats / kindFromDirent merged into kindFromStatLike
   (workspaceFileSystem.ts) — duplicate function bodies removed.
   Both fs.Stats and fs.Dirent expose the same isFile /
   isDirectory / isSymbolicLink interface, and both targets
   (FsStat['kind'], FsEntry['kind']) are the same 4-value union.
   Single helper avoids drift if the union grows.

4 new tests:
- bare/multi-ext DOS device names (CON, NUL, CON.txt, CON.foo.bar)
  + legitimate substrings (BACON, concat, precon, contemplating)
- edit() empty oldText -> parse_error + file unchanged
- edit() not-found error includes searched snippet in hint
- fs.denied audit payload carries FsError message

Plus the 2 already-fixed items (UTF-8 boundary, glob opts.cwd)
have new test coverage from round-4.

433/433 serve tests pass; typecheck + eslint clean.

Stack: 7b0db4ca81ada4efd7a46b38e821911cb8e5468342 → THIS

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 7 review-round-6 findings (#4250)

Round-6 review (wenshao + gpt-5.5) flagged 7 items including
two Critical and one privacy regression I introduced in round-5.

1. writeText / edit pre-write symlink guard
   (workspaceFileSystem.ts) — Critical, two reviewers (wenshao
   #CsBcq + gpt-5.5 #CsB3M) independently flagged.
   `atomicWriteFile` (`packages/core/src/utils/atomicFileWrite.ts`)
   resolves symlinks at write time, so a swap between the
   boundary's `resolve()` and `lowFs.writeTextFile()` would let
   the write follow the symlink to outside the workspace.
   `writeText` had no read phase, so its window was wider than
   `edit()`'s. New helper `assertNotSymlinkBeforeWrite(p)` lstats
   the path immediately before each `lowFs.writeTextFile` call;
   ENOENT is fine (ahead-of-create flow), but an actual symlink
   throws `symlink_escape`. Used in `writeText` AND `edit()`.
   Residual race after this guard but before the write completes
   is the deferred PR 20 atomic-via-temp follow-up.

2. recordAndWrap message field bypassed privacy mode
   (audit.ts) — Critical privacy regression I introduced at
   ebd9e78 (round-5). The new `FsDeniedAuditPayload.message`
   field forwarded `FsError.message` unconditionally — and many
   throw-sites embed `${p}` (absolute paths) or user-supplied
   `oldText` snippets into the message. Privacy-mode operators
   (without `QWEN_AUDIT_RAW_PATHS=1`) saw paths leak through the
   message field even though they explicitly disabled raw-path
   logging. Fixed: `message` is now gated behind `includeRawPaths`
   alongside `relPath`. Privacy mode = no path-bearing fields,
   period. Operators wanting forensic context opt in via
   `QWEN_AUDIT_RAW_PATHS=1` and accept both fields together.

3. glob opts.cwd via symlink (workspaceFileSystem.ts) — gpt-5.5
   #CsB3P. Textual `path.resolve(cwd) + isWithinRoot` admits
   `<ws>/link` even when `<ws>/link → /etc` is a symlink to
   outside; `globAsync` walks `/etc` before the per-hit filter
   drops results. Switched to `fsp.realpath(path.resolve(cwd))`
   so the containment check sees the actual walk root. ENOENT
   on cwd surfaces as `path_not_found`.

4. readText OOM via concurrent file growth
   (workspaceFileSystem.ts) — wenshao #CsBeE. The pre-stat
   `MAX_READ_BYTES` gate only sees the size at stat time; a
   concurrent writer can grow the file before the actual
   `readFileWithLineAndLimit` slurp. Added post-read
   `Buffer.byteLength(result.content) > MAX_READ_BYTES` check.
   The proper fix (fd-based read tying size + read to the same
   inode) is a hardening follow-up; this byte-length check is
   the defense-in-depth layer.

5. readBytes maxBytes can widen past MAX_READ_BYTES
   (policy.ts) — wenshao #CsBj5. `enforceReadBytesSize(st.size,
   opts.maxBytes)` used the caller-supplied `maxBytes` as the
   ceiling, replacing rather than clamping `MAX_READ_BYTES`. A
   future PR 19/20 route forwarding `req.query.maxBytes` could
   blindly bypass the daemon's 256 KiB safety cap. Now clamps
   via `Math.min(maxBytes, MAX_READ_BYTES)`.

6. ENOENT_TOLERATING_INTENTS docstring + test (paths.ts) —
   wenshao #CsBk3. The Intent docstring only mentioned `'write'`
   tolerating ENOENT; `'stat'` was in the set undocumented. A
   future maintainer removing `'stat'` thinking it was a
   copy-paste error would silently change behavior (stat on a
   concurrently-deleted path would throw `path_not_found` from
   the resolver instead of letting `fsp.lstat` throw `ENOENT`
   naturally). Amended docstring to call out `'stat'`'s rationale
   explicitly + added contract corpus case.

6 new tests:
- glob cwd via symlink to outside → path_outside_workspace
- writeText with mid-operation symlink swap → symlink_escape +
  outside file unchanged
- edit with mid-operation symlink swap → symlink_escape + outside
  file unchanged
- readBytes opts.maxBytes attempting widening → file_too_large
- fs.denied message field absent in privacy mode (default)
- fs.denied message field present in raw-paths mode (forensic)
- contract corpus: resolve('newdir/leaf', 'stat') succeeds for
  ENOENT path

439/439 serve tests pass; typecheck + eslint clean.

Stack: 7b0db4ca81ada4efd7a46b38e821911cb8e5468342ebd9e78 → THIS

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 5 review-round-7 findings (#4250)

glm-5.1 round-7 review surfaced 6 items; 5 are real fixes, 1 was
already addressed in 7f4f30d (writeText pre-write symlink
guard — reviewer looked at ebd9e78 snapshot ~8h before the
round-6 fix). Stack on round-6.

1. edit() bypassed lowFs.readTextFile (workspaceFileSystem.ts) —
   Critical encoding round-trip corruption. The earlier
   `fsp.readFile(p, 'utf-8')` included the UTF-8 BOM verbatim in
   `current`, breaking `oldText` matching even when the user
   passed the exact source text from a previous read; lost
   iconv-supported codepage handling (GBK / Big5 / Shift_JIS),
   so non-UTF-8 files would mojibake into `current` and
   round-trip-corrupt on write-back; and the subsequent
   `lowFs.writeTextFile` passed no `_meta`, stripping the BOM
   and forcing UTF-8 on write-back even when the original was
   BOM'd or non-UTF-8. Fix: read via `lowFs.readTextFile` AND
   forward `readResult._meta` into the write-back. Test pins
   UTF-8 BOM round-trip end-to-end.

2. hasSuspiciousPathPattern multi-digit and POSIX false positive
   (paths.ts) — `/~\\d/` only matched single-digit; NTFS
   allocates `~10+` on >9 collisions. POSIX false-positives:
   editor swap files, backup tools used `~N` legitimately. Fixed
   to `/~\\d+/` and gated behind `process.platform === 'win32'`,
   matching the ADS-colon check.

3. canonicalizeWorkspace redundant per-request syscall
   (paths.ts) — Performance. Factory canonicalizes once at
   build; every `resolveWithinWorkspace` also ran realpathSync
   on the same path, blocking the event loop. Added
   CANONICAL_BOUND_CACHE Map keyed on the input string;
   steady-state size = 1 per `1 daemon = 1 workspace`.

4. readBytes opts.maxBytes API contract
   (workspaceFileSystem.ts) — Semantic mismatch. Parameter name
   promised window-read; impl only used it as a hard reject
   gate. Now truncates the buffer post-read so `readBytes(p,
   { maxBytes: 1024 })` on a 200 KB file returns 1 KB. Hard
   `MAX_READ_BYTES` cap still throws for files above it.

5. glob walks node_modules and .git unnecessarily
   (workspaceFileSystem.ts) — Performance. Without an `ignore`
   option, `globAsync` traversed every file under those dirs
   before our per-hit `shouldIgnore` filter. Now passes
   `ignore: ['**/node_modules/**', '**/.git/**']` to
   short-circuit traversal. Post-filter via `shouldIgnore`
   remains authoritative.

5 new tests:
- 8.3 short-name regex Windows / POSIX split
- readBytes truncates 2048-byte file to 1024 with maxBytes
- readBytes throws file_too_large only above hard cap
- edit() preserves UTF-8 BOM round-trip
- glob prunes node_modules and .git

442/442 serve tests pass; typecheck + eslint clean.

Stack: 7b0db4ca81ada4efd7a46b38e821911cb8e5468342ebd9e787f4f30d → THIS

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 10 review-round-8/9 findings (#4250)

Two more review passes (gpt-5.5 + DeepSeek + wenshao) flagged 13
items; 10 are real fixes, 3 are reviewer-stale-snapshot or
already-tracked. Stack on round-7.

Critical (5):

1. paths.ts symlink-escape hint embedded the symlink target
   (gpt-5.5) — Privacy regression sibling to round-6 audit
   `message` gate. `recordDenied` always forwards `hint` into
   `fs.denied` even with `QWEN_AUDIT_RAW_PATHS` off; the hint
   `'symlink points to /Users/alice/secret'` leaks the
   attacker's intended exfiltration path through audit. Hint is
   now path-free; operators wanting the resolved target enable
   `QWEN_AUDIT_RAW_PATHS=1` and read it from `relPath` /
   `message`.

2. paths.ts dangling-symlink chain discarded its verified
   canonical (DeepSeek) — After the multi-hop walk validated
   `cursor → canonicalTarget` was inside the workspace, the
   code fell through to `findExistingAncestor(absolute)`,
   re-walking from the original input and discarding the
   verified result. An attacker swapping an intermediate
   symlink between the verification and the re-walk could
   produce a different canonical than the one validated. The
   verified `canonicalTarget` is now captured in
   `symlinkResolvedCanonical` and used directly; the
   `findExistingAncestor(absolute)` fallthrough only runs when
   no symlink was traversed.

3. workspaceFileSystem.ts readBytes missing post-read size
   check (DeepSeek) — Same TOCTOU shape as `readText`'s
   round-6 fix. The pre-stat `enforceReadBytesSize` sees the
   size at stat time; a concurrent appender keeps the same
   inode but grows the file past the cap before
   `fsp.readFile` returns. `assertInodeStableAfterRead`
   catches inode changes but not same-inode growth. Added a
   post-read `buf.length > MAX_READ_BYTES` check matching
   `readText`'s defense-in-depth pattern.

4. errors.ts wrapAsFsError default = permission_denied
   (DeepSeek) — Misclassified non-errno errors (`TypeError`,
   programmer-error throws, native module exceptions) as
   security denials, paging security oncall for what should
   be a developer ticket. New `internal_error` kind (HTTP
   500) is the new default; `permission_denied` reserved for
   actual `EACCES`/`EPERM`.

5. audit.ts AuditContext.sessionId not forwarded to
   BridgeEvent (DeepSeek) — Multi-session daemons couldn't
   trace audit events back to the session that triggered
   them. `originatorClientId` identifies the client, not the
   session. Added optional `sessionId` field to both
   `FsAccessAuditPayload` and `FsDeniedAuditPayload`,
   forwarded from `ctx.sessionId` when present.

Improvements (4):

6. workspaceFileSystem.ts glob cwd realpath redundant when
   cwd === boundWorkspace (wenshao) — `boundWorkspace` is
   already canonicalized by the factory (`realpathSync.native`
   at build time), so calling `fsp.realpath` per-request when
   no `opts.cwd` was supplied is a redundant async syscall.
   Added a short-circuit.

7. workspaceFileSystem.ts kindFromStatLike JSDoc orphaned
   (wenshao) — Inserting `assertNotSymlinkBeforeWrite` between
   the JSDoc and `kindFromStatLike` left the doc floating
   above the wrong function. IDE hovers showed the wrong
   description. Moved the doc back to its function.

8. workspaceFileSystem.ts shared mutable Ignore object
   (DeepSeek) — `createWorkspaceFileSystemFactory` builds one
   `Ignore` instance and shares it across every
   `WorkspaceFileSystemImpl` returned by `forRequest()`.
   `Ignore.add(): this` is a public mutator. A future
   "per-session ignore rules" feature calling `.add()` from a
   request handler would silently corrupt all concurrent
   sessions. `Object.freeze` turns the cross-request mutation
   into a `TypeError` rather than a silent leak.

9. server.ts createDefaultFsAuditEmit one-shot warned
   (DeepSeek) — Permanent silent no-op after the first event;
   only logged the event `type` with no pathHash / errorKind /
   intent. If PR 19 forgets the real factory injection, every
   write 403s and audit is silent past the first warning —
   exactly the regression the warning exists to surface.
   Periodic warning (every 100th drop) + first-event context
   (errorKind, intent, pathHash) makes the regression
   actionable in production logs.

Cleanup (1):

10. workspaceFileSystem.ts safeUtf8Truncate dead code
    (DeepSeek noted as "off-by-one") — The lead-byte
    seqLen-check block was dead code: `subarray(0, end)`
    already excludes the leading byte at `end`, so no
    further adjustment is ever needed. Removed the block;
    function is now 4 lines and still produces a valid
    codepoint prefix. Reviewer's suggested fix
    (`buf[end-1] → buf[end]`) was technically correct but
    redundant with the subarray cut.

Already-fixed (3 reviewer-stale-snapshot, reply + resolve):

- writeText pre-write symlink guard — fixed in 7f4f30d
- edit() read-modify-write race — already deferred to PR 20
  atomic-via-temp follow-up
- glob maxResults walk-bound — already follow-up #5

3 new tests + 2 updated:
- wrapAsFsError unknown errno → internal_error (default change)
- internal_error has HTTP 500
- non-Error throwables → internal_error (not permission_denied)
- readBytes post-stat growth → file_too_large
- existing wrapAsFsError test updated for new default

445/445 serve tests pass; typecheck + eslint clean.

Stack: 7b0db4ca81ada4efd7a46b38e821911cb8e5468342ebd9e787f4f30d1dc9d22 → THIS

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* fix(serve/fs): close 3 review-round-10 findings (#4250)

Round-10 review (wenshao) flagged 4 items, all marked
non-blocking by the reviewer. 3 are landed in-PR; 1 (glob
withFileTypes optimization) is moved to issue #4175 follow-ups
since the reviewer themselves recommended "test it on a 100K-file
workspace after PR 19 lands."

1. canonicalizeWorkspace docstring (paths.ts) — Documentation.
   The earlier FIXME warned about sync syscall blocking the
   event loop without mentioning the `CANONICAL_BOUND_CACHE`
   added in 1dc9d22 brings steady-state cost to zero. Future
   reviewers reading the FIXME again would re-flag the
   already-mitigated concern. Added a paragraph noting cache hit
   rate = 100% under the `1 daemon = 1 workspace` model so
   per-request blocking only happens at boot or on fresh
   workspace values (e.g. tests).

2. enforceReadBytesSize dead `maxBytes` parameter (policy.ts) —
   Round-7 changed `readBytes` to use post-read truncation for
   the soft window cap, leaving the function's `maxBytes`
   parameter unused at the only callsite. The
   `Math.min(maxBytes, MAX_READ_BYTES)` clamp branch became dead
   code. Reviewer's option 1: tighten the signature to
   `enforceReadBytesSize(fileBytes: number): void`. Done — the
   function is now purely the hard-cap enforcer its name
   implies; soft-window truncation lives in the orchestrator's
   `buf.subarray(0, opts.maxBytes)` post-read step where it's
   visible alongside the cap check it complements.

3. relForAudit cross-drive sentinel (audit.ts) — Windows-only
   privacy edge case. `path.relative('C:\\ws', 'D:\\evil')`
   returns `'D:\\evil'` (an absolute path) because Win32 can't
   express cross-drive relatives. Even when raw-paths mode is
   ENABLED, the audit `relPath` field would carry the off-drive
   absolute path, exposing the attacker's drive letter +
   directory. Added a `path.isAbsolute(rel)` post-check that
   substitutes a `<cross-drive>` sentinel — audit consumers see
   the cross-drive case distinctly without leaking the offending
   path. This was previously P2 deferred in PR 18's description
   ("Windows cross-drive path.relative"); reviewer's "few lines
   beats deferring" assessment was right.

Tracked as follow-up (not in this commit):

4. glob withFileTypes optimization — Replace per-hit `lstat`
   (line ~664) with `glob` v10's `withFileTypes: true` so each
   hit comes back as a `Path` object with `isDirectory()` /
   `isFile()` / `isSymbolicLink()` already available. Saves N
   syscalls in large workspaces. Non-trivial restructure (return
   type changes from `string[]` to `Path[]`). Reviewer
   themselves marked "[performance / non-blocking]" and said
   "test it on a 100K-file workspace after PR 19 lands so we
   know whether it's worth it." Added to issue #4175 body's
   Post-PR-18 hardening follow-ups.

446/446 serve tests pass; typecheck + eslint clean.

Stack: 7b0db4ca81ada4efd7a46b38e821911cb8e5468342ebd9e787f4f30d1dc9d22a33d459 → THIS

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
@yiliang114 yiliang114 added the skip-changelog Exclude from release notes label May 20, 2026
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog Exclude from release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants