Skip to content

feat(cli): wrap markdown links in OSC 8 so wrapped URLs stay clickable#4037

Merged
BZ-D merged 14 commits into
mainfrom
feat/osc8-markdown-links
May 13, 2026
Merged

feat(cli): wrap markdown links in OSC 8 so wrapped URLs stay clickable#4037
BZ-D merged 14 commits into
mainfrom
feat/osc8-markdown-links

Conversation

@BZ-D

@BZ-D BZ-D commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #3954.

Long URLs the model emits inside [label](url) or as bare https://… get line-wrapped by the terminal, which prevents most emulators from detecting them as a single clickable region. Wrapping them in an OSC 8 hyperlink envelope decouples the link target from the visible label so the entire label stays clickable regardless of where it wraps.

  • Extract the existing OSC 8 helpers from AuthenticateStep.tsx into a shared packages/cli/src/ui/utils/osc8.ts, plus a dependency-free supportsHyperlinks() capability detector that honors NO_COLOR / FORCE_COLOR=0 / CI / non-TTY stdout. FORCE_HYPERLINK=1 is an explicit opt-in, QWEN_DISABLE_HYPERLINKS=1 is an explicit opt-out.
  • Wire InlineMarkdownRenderer to wrap markdown link labels and bare autolinks in an OSC 8 envelope when the host terminal supports it. Wrapping happens on the fully-parsed inline token, so streamed partial chunks can never split an envelope across flushes.
  • Fall back to the legacy label (url) / plain-URL rendering byte-for-byte when the host terminal does not advertise OSC 8 support.

Acceptance criteria from the issue:

  • A long markdown link is clickable as a single link in OSC-8-capable terminals regardless of where it wraps.
  • In terminals without OSC 8 support, output is byte-identical to today.
  • Streaming partial chunks never produce a broken OSC 8 envelope (envelopes are emitted on parsed AST nodes, never on raw byte runs).
  • Behavior is suppressed under NO_COLOR / non-TTY stdout.

Test plan

  • npx vitest run src/ui/utils — 539 tests pass (29 files), including 18 new osc8 unit tests and 4 new InlineMarkdownRenderer link-wrapping tests.
  • npm run typecheck clean.
  • npm run lint clean.
  • Manual smoke in iTerm2 / WezTerm / Kitty / Windows Terminal — confirm long URLs in streamed model output are clickable as one region after wrapping.
  • Manual smoke under NO_COLOR=1 and qwen | cat — confirm no escape bytes are written.

@github-actions

github-actions Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 75.65% 75.65% 76.74% 80.34%
Core 78.22% 78.22% 80.82% 82.58%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   75.65 |    80.34 |   76.74 |   75.65 |                   
 src               |   73.64 |    67.35 |   76.47 |   73.64 |                   
  gemini.tsx       |   61.11 |    59.13 |   66.66 |   61.11 | ...18,835-838,846 
  ...ractiveCli.ts |   80.02 |    68.61 |   78.57 |   80.02 | ...1021,1059,1162 
  ...liCommands.ts |   76.17 |    73.33 |     100 |   76.17 | ...50-274,299,401 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   53.94 |    66.66 |   58.82 |   53.94 |                   
  acpAgent.ts      |   56.25 |    67.01 |   65.51 |   56.25 | ...71-873,887-895 
  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.02 |    70.59 |      84 |   76.02 |                   
  ...ryReplayer.ts |   65.93 |    75.67 |   81.81 |   65.93 | ...40-255,268-269 
  Session.ts       |   75.12 |    68.89 |    85.1 |   75.12 | ...2456,2462-2465 
  ...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.68 |    94.85 |   95.45 |   97.68 |                   
  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 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/commands      |   80.92 |    85.71 |      50 |   80.92 |                   
  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          
 ...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.53 |    88.95 |   81.81 |   84.53 |                   
  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.72 |    85.31 |   85.71 |   92.72 |                   
  auth.ts          |   86.98 |    80.32 |     100 |   86.98 | ...26-227,243-244 
  config.ts        |    88.3 |    85.52 |      76 |    88.3 | ...1713,1737-1738 
  keyBindings.ts   |   96.11 |       50 |     100 |   96.11 | 169-172           
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  sandboxConfig.ts |    58.9 |    61.53 |   66.66 |    58.9 | ...54-68,73,77-89 
  settings.ts      |   85.51 |    87.19 |   86.48 |   85.51 | ...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          |   84.88 |    78.88 |   66.66 |   84.88 |                   
  index.ts         |   70.44 |       74 |   53.84 |   70.44 | ...71-272,282-287 
  languages.ts     |   95.33 |    86.48 |     100 |   95.33 | ...67,195-198,213 
  ...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.75 |    72.14 |   74.07 |   72.75 |                   
  session.ts       |   76.94 |    70.45 |   85.71 |   76.94 | ...80-781,790-800 
  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.2 |      100 |       0 |     5.2 | ...21-433,442-472 
 .../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/services      |   92.84 |     90.9 |   98.36 |   92.84 |                   
  ...mandLoader.ts |     100 |     92.3 |     100 |     100 | 91                
  ...killLoader.ts |     100 |    96.15 |     100 |     100 | 45                
  ...andService.ts |   98.75 |      100 |     100 |   98.75 | 111               
  ...ionService.ts |   97.19 |    89.77 |     100 |   97.19 | ...85,423-424,428 
  ...mandLoader.ts |   86.83 |    83.87 |     100 |   86.83 | ...30-335,340-345 
  ...omptLoader.ts |   76.05 |    80.64 |   83.33 |   76.05 | ...12-213,279-280 
  ...mandLoader.ts |     100 |      100 |     100 |     100 |                   
  ...nd-factory.ts |    91.5 |    91.66 |     100 |    91.5 | 129,138-145       
  ...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            |   65.19 |    67.73 |   54.76 |   65.19 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |   68.02 |     62.4 |      75 |   68.02 | ...2430,2434-2438 
  ...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 |    96.42 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   48.01 |    58.73 |   21.42 |   48.01 |                   
  AuthDialog.tsx   |   64.26 |    44.44 |   16.66 |   64.26 | ...59,366-388,392 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |    9.61 |      100 |       0 |    9.61 | ...35-352,391-476 
  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   |   70.19 |    79.57 |   80.45 |   70.19 |                   
  aboutCommand.ts  |     100 |    85.71 |     100 |     100 | 36                
  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.94 |       75 |     100 |   92.94 | 45-46,74-75,93-94 
  ...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 |     100 |    93.33 |     100 |     100 | 21                
  dreamCommand.ts  |      75 |    66.66 |   66.66 |      75 | 22-27,44-47       
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |      60 |    92.85 |   77.77 |      60 | 176-317           
  ...onsCommand.ts |   48.66 |     90.9 |   63.63 |   48.66 | ...05-109,159-211 
  forgetCommand.ts |   26.82 |      100 |      50 |   26.82 | 18-51             
  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 |   85.76 |    82.82 |     100 |   85.76 | ...51-658,687-694 
  ...elsCommand.ts |     100 |      100 |     100 |     100 |                   
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   74.56 |    79.06 |   71.42 |   74.56 | ...91-200,223-228 
  ...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.29 |    77.77 |     100 |   85.29 | ...06-313,320-325 
  ...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.45 |    73.43 |     100 |   77.45 | ...55-159,181-186 
  ...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 |   61.48 |    75.66 |   66.66 |   61.48 |                   
  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.53 |      100 |       0 |   12.53 | 63-470            
  ...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.67 |    58.06 |     100 |   79.67 | ...98-102,104-108 
  ...ngSpinner.tsx |   68.42 |       80 |      50 |   68.42 | 35-52,73,80-81    
  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 |   63.27 |    36.73 |     100 |   63.27 | ...29-338,341,344 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   82.25 |    77.43 |   83.33 |   82.25 | ...1347,1412,1462 
  ...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 |   16.07 |    89.18 |      50 |   16.07 | ...58-159,162-648 
  MemoryDialog.tsx |   53.21 |    51.21 |   57.14 |   53.21 | ...54,366,379-381 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   76.31 |    54.94 |     100 |   76.31 | ...05-521,578-582 
  ...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 |   88.14 |    83.87 |     100 |   88.14 | ...01-105,133-138 
  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 |    4.45 |      100 |       0 |    4.45 | 28-92,100-328     
  ...ionPicker.tsx |      86 |    81.25 |     100 |      86 | ...98-310,344-346 
  ...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 |    69.1 |    73.65 |     100 |    69.1 | ...11-819,825-826 
  ...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 |                   
  ...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 |    25.2 |       90 |      10 |    25.2 |                   
  ...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  |    8.13 |      100 |       0 |    8.13 | 39-59,64-187      
  ...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.44 |     83.6 |   85.29 |   75.44 |                   
  ...sksDialog.tsx |   70.05 |       79 |   76.19 |   70.05 | ...1119,1195-1197 
  ...TasksPill.tsx |   70.83 |    86.95 |     100 |   70.83 | 44,84-96,104-112  
  ...gentPanel.tsx |   99.52 |    93.18 |     100 |   99.52 | 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.77 |    94.23 |   66.66 |   54.77 |                   
  ...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.35 |    94.73 |      80 |   88.35 | 51-52,58-71,105   
  ...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 |   72.24 |    70.52 |      80 |   72.24 |                   
  ...etailStep.tsx |   96.52 |       75 |     100 |   96.52 | 33,37,50,59       
  ...etailStep.tsx |   93.27 |    73.68 |     100 |   93.27 | 41-42,99-104,110  
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   36.09 |    47.05 |      50 |   36.09 | ...49,453-466,470 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   20.83 |    83.72 |   83.33 |   20.83 |                   
  ...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         |   94.79 |    85.71 |     100 |   94.79 | 16,20,35,109-110  
 ...ents/mcp/steps |    6.88 |      100 |       0 |    6.88 |                   
  ...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 |    5.88 |      100 |       0 |    5.88 | 20-176            
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |    7.14 |      100 |       0 |    7.14 | 16-146            
 ...nents/messages |   82.15 |    80.23 |   72.85 |   82.15 |                   
  ...ionDialog.tsx |   77.35 |    74.54 |    62.5 |   77.35 | ...90,508,526-528 
  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             
  ...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 |   82.37 |    77.36 |   92.75 |   82.37 |                   
  ...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  |    6.29 |      100 |       0 |    6.29 | 35-42,45-176      
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  TextInput.tsx    |   72.98 |    55.55 |      80 |   72.98 | ...08-212,224-230 
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  text-buffer.ts   |   83.62 |    75.62 |   97.61 |   83.62 | ...2272,2300,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 |    8.39 |      100 |       0 |    8.39 |                   
  ...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 |    2.29 |      100 |       0 |    2.29 | 28-449            
  ...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.05 |    78.24 |   82.14 |   77.05 |                   
  ...ewContext.tsx |   65.77 |      100 |      75 |   65.77 | ...22-225,231-241 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   93.37 |    68.57 |      50 |   93.37 | ...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 | 108-109           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 173-174           
  ...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      |   81.33 |    81.11 |   86.47 |   81.33 |                   
  ...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.9 |    63.44 |   61.53 |    75.9 | ...84,908,927-931 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-158            
  ...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.11 |    76.92 |     100 |   94.11 | 119-123,216,222   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   93.75 |    73.17 |     100 |   93.75 | ...68-169,221-222 
  ...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 |   33.33 |       50 |     100 |   33.33 | 30,34,41-90       
  ...ialogClose.ts |   16.66 |      100 |     100 |   16.66 | 79-139            
  ...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 |   75.92 |    72.95 |   91.66 |   75.92 | ...2300,2313-2321 
  ...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.24 |    76.92 |     100 |   97.24 | 104-105,145       
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   96.96 |    95.69 |     100 |   96.96 | ...82-183,237-240 
  ...sionPicker.ts |   79.79 |    61.19 |     100 |   79.79 | ...02-404,413-415 
  ...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 |     100 |    98.79 |     100 |     100 | 257               
  ...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.6 |    82.43 |   92.22 |    83.6 |                   
  ...Colorizer.tsx |   82.78 |    88.23 |     100 |   82.78 | ...10-111,197-223 
  ...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.29 |     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             
  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 |                   
  ...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 |                   
  ...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         |   73.31 |       90 |   93.68 |   73.31 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  apiPreconnect.ts |   96.52 |    97.05 |     100 |   96.52 | 164-167           
  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.47 |     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              
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.82 |    93.28 |     100 |   96.82 | ...84-485,583,596 
  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 |     100 |       96 |     100 |     100 | 110               
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |   92.52 |     90.9 |   83.33 |   92.52 | 63-69,184         
  ...InfoFields.ts |    87.5 |     64.1 |     100 |    87.5 | ...21-122,143-144 
  ...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          |   78.22 |    82.58 |   80.82 |   78.22 |                   
 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        |   86.11 |    76.88 |   91.66 |   86.11 |                   
  ...transcript.ts |   88.92 |    76.66 |     100 |   88.92 | ...82,306-307,438 
  ...ent-resume.ts |   81.23 |    69.89 |   77.41 |   81.23 | ...1021,1024-1026 
  ...ound-tasks.ts |   95.13 |    86.61 |     100 |   95.13 | ...06-707,733-734 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/arena  |   76.98 |    67.72 |   78.72 |   76.98 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.92 |    64.19 |   78.26 |   75.92 | ...1860,1866-1867 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    73.46 |     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.13 |     76.7 |   71.42 |   81.13 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   76.45 |    72.35 |   60.86 |   76.45 | ...1604,1631-1677 
  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/config        |   75.73 |    78.23 |   62.08 |   75.73 |                   
  config.ts        |   73.51 |    75.93 |   56.93 |   73.51 | ...3084,3095-3107 
  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          |    84.2 |    82.22 |   88.83 |    84.2 |                   
  baseLlmClient.ts |   91.63 |    84.37 |   84.61 |   91.63 | ...91,299-313,380 
  client.ts        |   78.24 |    77.11 |   85.18 |   78.24 | ...1478,1515-1518 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...63,365,372-375 
  ...lScheduler.ts |   81.13 |    82.25 |   93.33 |   81.13 | ...2332,2384-2388 
  geminiChat.ts    |   88.81 |    84.36 |    87.5 |   88.81 | ...1304,1371-1372 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | 34-42,45-49,52-87 
  logger.ts        |   82.25 |    81.81 |     100 |   82.25 | ...57-361,407-421 
  ...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 |   95.12 |    81.91 |   93.61 |   95.12 |                   
  ...tGenerator.ts |   97.13 |    83.58 |    92.3 |   97.13 | ...22,714,870,926 
  converter.ts     |   94.51 |    80.62 |     100 |   94.51 | ...06-607,617,816 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
 ...ntentGenerator |   91.53 |    71.64 |   93.33 |   91.53 |                   
  ...tGenerator.ts |      90 |    70.96 |   92.85 |      90 | ...80-286,304-305 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |   93.33 |    83.33 |      90 |   93.33 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   93.31 |    83.33 |      90 |   93.31 | ...94,804-805,833 
 ...ntentGenerator |   79.71 |       84 |   89.47 |   79.71 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   74.92 |    80.85 |   86.95 |   74.92 | ...1410,1431-1437 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   52.38 |    44.44 |      50 |   52.38 | ...77,81-85,89-93 
  ...tGenerator.ts |   48.78 |    91.66 |   77.77 |   48.78 | ...10-163,166-167 
  pipeline.ts      |   93.65 |     84.9 |     100 |   93.65 | ...79-480,488,553 
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   90.66 |     88.4 |     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.56 |    88.46 |   95.45 |   96.56 |                   
  dashscope.ts     |   97.02 |    88.15 |   93.33 |   97.02 | ...37-238,314-315 
  deepseek.ts      |   95.55 |    90.56 |     100 |   95.55 | ...31-132,145-146 
  default.ts       |   94.62 |    86.36 |   85.71 |   94.62 | 85-86,156-158     
  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/hooks         |   80.63 |    84.35 |   84.16 |   80.63 |                   
  ...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.37 |    90.54 |     100 |   96.37 | ...89,291-292,365 
  ...entHandler.ts |   95.58 |    84.37 |   92.59 |   95.58 | ...29,682-683,693 
  hookPlanner.ts   |   84.13 |    76.59 |      90 |   84.13 | ...38,144,162-173 
  hookRegistry.ts  |   88.83 |    86.36 |     100 |   88.83 | ...21,326,330,334 
  hookRunner.ts    |   53.94 |     72.6 |   61.11 |   53.94 | ...27-728,737-738 
  hookSystem.ts    |   75.47 |      100 |   56.41 |   75.47 | ...75-576,582-583 
  ...HookRunner.ts |   75.51 |     61.9 |      80 |   75.51 | ...05-406,424-425 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |    96.5 |     91.8 |     100 |    96.5 | ...90,209-210,223 
  ssrfGuard.ts     |   77.22 |    85.36 |     100 |   77.22 | ...57,261-267,273 
  trustedHooks.ts  |       0 |        0 |       0 |       0 | 1-124             
  types.ts         |   90.18 |    90.78 |   85.18 |   90.18 | ...91-392,452-456 
  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           |   33.92 |    45.16 |   45.76 |   33.92 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |    4.29 |      100 |       0 |    4.29 | ...20-371,377-394 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   13.52 |    81.25 |   29.16 |   13.52 | ...75-694,700-730 
  ...eLspClient.ts |   17.89 |      100 |       0 |   17.89 | ...37-244,254-258 
  ...LspService.ts |   45.87 |    62.13 |   66.66 |   45.87 | ...1282,1299-1309 
  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.44 |       76 |   65.62 |   67.44 |                   
  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.95 |    77.27 |     100 |   91.95 | ...08,110-111,119 
  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.47 |    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 |    81.92 |   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.73 |   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.18 |     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      |   86.81 |    84.92 |   90.06 |   86.81 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |   97.82 |    94.73 |     100 |   97.82 | 172-173           
  ...ionService.ts |   95.53 |    95.14 |     100 |   95.53 | ...92,354,356-360 
  ...ingService.ts |    84.1 |    84.35 |   82.85 |    84.1 | ...1240,1257-1258 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  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 
  fileReadCache.ts |     100 |      100 |     100 |     100 |                   
  ...temService.ts |   89.76 |     85.1 |   88.88 |   89.76 | ...89,191,266-273 
  ...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 |   71.83 |    68.47 |    91.3 |   71.83 | ...89-790,806,822 
  ...ionService.ts |   98.13 |     97.8 |   95.45 |   98.13 | ...32-333,380-381 
  ...orRegistry.ts |   96.34 |    91.66 |     100 |   96.34 | ...90-391,542-543 
  sessionRecap.ts  |   12.34 |      100 |       0 |   12.34 | 49-158            
  ...ionService.ts |   89.42 |     78.1 |   96.42 |   89.42 | ...1221,1225-1226 
  sessionTitle.ts  |   93.91 |    71.15 |     100 |   93.91 | ...34-237,268-269 
  ...ionService.ts |   83.01 |    78.66 |   87.75 |   83.01 | ...1482,1488-1493 
  ...UseSummary.ts |   94.63 |    88.67 |     100 |   94.63 | ...69-171,221-222 
 ...icrocompaction |   98.62 |    86.44 |     100 |   98.62 |                   
  microcompact.ts  |   98.62 |    86.44 |     100 |   98.62 | 138,142           
 src/skills        |    87.5 |     83.8 |   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 | ...1115,1122-1126 
  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     |   82.84 |    79.74 |   95.23 |   82.84 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   76.74 |    71.42 |   92.85 |   76.74 | ...1155,1177-1178 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 51-56,69-74,78-83 
 src/telemetry     |    73.5 |    85.47 |   77.56 |    73.5 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...-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.89 |    90.21 |   94.11 |   93.89 | ...70-275,294-295 
  ...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.42 |    83.56 |   76.92 |   90.42 | ...16-317,337-341 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |   92.77 |     87.3 |     100 |   92.77 | 79-93,379-383     
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   99.24 |    88.88 |     100 |   99.24 | 53                
  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.83 |   73.52 |   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.05 |   68.96 |   91.19 | ...38,202-203,216 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   76.49 |    81.35 |   84.43 |   76.49 |                   
  ...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          |   78.01 |    84.76 |   73.33 |   78.01 | ...86-687,774-824 
  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 |   51.95 |     65.9 |   47.36 |   51.95 | ...03-525,528-565 
  mcp-client.ts    |   32.44 |    75.28 |   63.63 |   32.44 | ...1462,1466-1469 
  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.27 |    83.94 |      92 |   92.27 | ...18,547-550,563 
  ...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 |        0 |       0 |    6.34 | 47-145            
  send-message.ts  |   89.32 |    91.66 |   83.33 |   89.32 | 44-45,68-76       
  shell.ts         |   72.18 |    80.23 |   89.65 |   72.18 | ...3659,3708-3714 
  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     |   85.42 |    84.09 |   84.61 |   85.42 | ...05-410,432-433 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   74.64 |    75.24 |      80 |   74.64 | ...82-783,791-792 
  tool-search.ts   |   95.19 |    86.48 |    92.3 |   95.19 | ...47-153,208-213 
  tools.ts         |   87.76 |       90 |   88.23 |   87.76 | ...50-451,467-473 
  web-fetch.ts     |   88.59 |    79.48 |    92.3 |   88.59 | ...12-313,315-316 
  write-file.ts    |    79.2 |    79.26 |   83.33 |    79.2 | ...39-642,654-689 
 src/tools/agent   |   83.39 |    84.95 |   83.92 |   83.39 |                   
  agent.ts         |   83.69 |    85.38 |   84.31 |   83.69 | ...1668,1677-1681 
  fork-subagent.ts |   78.26 |    71.42 |      80 |   78.26 | 54-72,104-105     
 src/utils         |   88.63 |    87.14 |   93.16 |   88.63 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   76.08 |    44.44 |     100 |   76.08 | 61-70,72          
  bareMode.ts      |   27.27 |      100 |       0 |   27.27 | 9-15,18-19        
  browser.ts       |    7.69 |      100 |       0 |    7.69 | 17-56             
  ...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             
  ...kerChecker.ts |   82.55 |    78.57 |     100 |   82.55 | 68-69,79-84,92-98 
  notebook.ts      |   94.35 |    84.78 |     100 |   94.35 | ...10,122,174-176 
  openaiLogger.ts  |   87.93 |    82.85 |     100 |   87.93 | ...22-124,147-152 
  partUtils.ts     |     100 |      100 |     100 |     100 |                   
  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.53 |    84.37 |   66.66 |   46.53 | ...32-233,245-322 
  ...sDiscovery.ts |   97.42 |    92.85 |     100 |   97.42 | ...04,182-183,202 
  ...tchOptions.ts |   63.85 |    64.28 |   83.33 |   63.85 | ...29-130,187-188 
  runtimeStatus.ts |   85.58 |    82.05 |     100 |   85.58 | ...81,231-237,239 
  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.55 |     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.71 |    97.14 |     100 |   98.71 | 106               
  ...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.31 |     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.

@BZ-D

BZ-D commented May 11, 2026

Copy link
Copy Markdown
Collaborator Author

Multi-round design & code audit complete. New commit fc983e5e9 addresses every issue surfaced.

Design audit (4 rounds, 5 fresh independent reviewers total). Final round returned clean. Key changes:

  • Keep the visible (url) suffix in supported terminals. Earlier the suffix was dropped; restoring it preserves the byte-identical "purely additive" mental model and protects copy-paste UX. OSC 8 envelope now wraps the existing visible region.
  • URL scheme allowlisthttp/https/mailto/ftp/sftp/ssh. javascript:, data:, file:, vbscript: fall through unwrapped so the user can preview suspicious targets before clicking — important because the rendered text is LLM output (prompt-injection defense).
  • Reject URLs with whitespace before wrapping. Every terminal truncates or rejects whitespace in an OSC 8 target, which would turn the whole region into an un-clickable trap.
  • Block OSC 8 inside tmux / screen by default — the multiplexer hides the host terminal's capabilities. Power users opt in with FORCE_HYPERLINK=1.
  • Version-gated supportsHyperlinks mirroring the supports-hyperlinks npm package: iTerm ≥3.1, vscode ≥1.72, WezTerm ≥20200620, VTE ≥0.50 (with the 0.50.0 segfault carve-out covering both dotted 0.50.0 and packed 5000 forms). Blocks CI/TEAMCITY/non-TTY/win32 (modulo WT_SESSION/Kitty/Ghostty/DOMTERM).
  • Link regex extended to one level of balanced parens so [wiki](https://en.wikipedia.org/wiki/Foo_(bar)) isn't truncated at the inner ).
  • Trim trailing sentence punctuation off the OSC 8 target for bare URLs (visible bytes unchanged).
  • TableRenderer updated for parity — markdown links inside tables are clickable too.

Code audit (4 rounds). Final round: "Ship it." Changes:

  • Consolidated wrapForMultiplexer with the pre-existing packages/cli/src/utils/osc.ts — no duplicate helpers.
  • Extracted MD_LINK_PATTERN, MD_LINK_CAPTURE, shouldWrapMarkdownLink, HYPERLINK_ENV_KEYS into osc8.ts so React and ANSI renderers stay in lockstep.
  • Hoisted supportsHyperlinks() once per render (both renderers).
  • Dropped the memoization cache so runtime env mutability (NO_COLOR, theme) is honored.
  • Rewrote trimTrailingUrlPunctuation to O(n) (pre-count opens).
  • JSX : '': null for idiomatic empty children.

Verification.

  • npx vitest run src/ui/utils src/utils/osc.test.ts → 614/614 pass (29 new osc8 tests + 14 renderer tests + the rest).
  • npm run typecheck clean.
  • npm run lint clean.

Tests cover: balanced parens, dangerous-scheme rejection, whitespace-URL rejection, trailing-punctuation trimming, tmux blocking + FORCE_HYPERLINK override, version gating (iTerm/WezTerm/vscode/VTE incl. packed '5000'), win32 fallbacks, mid-stream chunk envelope balance, byte-identical legacy fallback.

Comment thread packages/cli/src/ui/utils/osc8.ts Outdated
Comment thread packages/cli/src/ui/utils/osc8.ts
BZ-D added a commit that referenced this pull request May 11, 2026
Two fixes from chiga0's review on PR #4037:

1. Move the non-TTY check above `FORCE_HYPERLINK` so a user with
   `FORCE_HYPERLINK=1` in their shell profile still gets a clean pipe
   when they run `qwen | cat` or `qwen > out.txt`. The "non-TTY stdout
   must suppress escapes" acceptance criterion now holds even under
   forced enable.

2. Version-gate the Konsole detection at `>= 21.04`. KONSOLE_VERSION
   is set by every Konsole release including ones that pre-date OSC 8
   support, so the existence check alone false-positives on Konsole
   20.x. Parse the packed integer (21.04 → 210400) and let older
   releases fall through to the legacy fallback.

Updates the docs row for FORCE_HYPERLINK to make the non-TTY caveat
explicit. Splits the prior "FORCE_HYPERLINK + isTTY=false" test into
two — one verifying force works on a TTY, one asserting it never
escapes the non-TTY guard. Adds a Konsole < 21.04 regression test.
Comment thread packages/cli/src/ui/utils/osc8.ts Outdated
@tanzhenxin tanzhenxin added the type/feature-request New feature or enhancement request label May 12, 2026
@BZ-D BZ-D requested a review from chiga0 May 12, 2026 09:48
Comment thread packages/cli/src/commands/channel/config-utils.ts Outdated
Comment thread packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx Outdated
BZ-D added 14 commits May 13, 2026 10:57
Long URLs the model emits inside `[label](url)` or as bare `https://...`
get line-wrapped by the terminal, which prevents most emulators from
detecting them as a single clickable region. OSC 8 hyperlinks decouple
the link target from the visible label so the entire label remains one
clickable target regardless of where it wraps.

- Extract the existing OSC 8 helpers from AuthenticateStep into a shared
  packages/cli/src/ui/utils/osc8.ts util, plus a dependency-free
  capability detector that honors NO_COLOR / FORCE_COLOR=0 / CI /
  non-TTY stdout, with FORCE_HYPERLINK=1 and QWEN_DISABLE_HYPERLINKS=1
  overrides for explicit opt-in / opt-out.
- Wire InlineMarkdownRenderer to wrap markdown link labels and bare
  autolinks in an OSC 8 envelope when supported. Wrapping happens after
  the inline link token has been fully matched, so streamed partial
  chunks cannot split an envelope across flushes.
- Fall back to the legacy `label (url)` rendering byte-for-byte when
  the host terminal does not advertise OSC 8 support.

Closes #3954
Address findings from a multi-round design and code audit of the OSC 8
hyperlink feature:

Design fixes:
- Keep the visible `(url)` suffix in supported terminals too — preserves
  copy-paste UX and lets users preview suspicious URLs before clicking.
  OSC 8 is now purely additive (byte-identical unsupported output, plus
  envelope on supported terminals).
- Restrict OSC 8 wrapping to http/https/mailto/ftp/sftp/ssh schemes;
  javascript:/data:/file:/vbscript: fall through unwrapped so the user
  can read the target. Prompt-injection defense for LLM output.
- Reject URLs with whitespace — every terminal treats whitespace in an
  OSC 8 target as truncation/rejection, which would turn the whole
  region into an un-clickable trap.
- Block OSC 8 inside tmux/screen by default; require `FORCE_HYPERLINK=1`
  opt-in. The multiplexer hides the host terminal's capabilities, so
  emitting passthrough escapes on a host without OSC 8 prints garbage.
- Version-gate `supportsHyperlinks()` (iTerm ≥3.1, vscode ≥1.72, WezTerm
  ≥20200620, VTE ≥0.50 with 0.50.0 segfault carve-out), block CI /
  TEAMCITY / win32 (modulo WT_SESSION/Kitty/Ghostty/DOMTERM), mirror
  `supports-hyperlinks` semantics.
- Extend the link regex to allow one level of balanced parens in the
  URL group so `[wiki](https://en.wikipedia.org/wiki/Foo_(bar))` isn't
  truncated at the inner `)`.
- Trim trailing sentence punctuation off the OSC 8 *target* for bare
  URLs (`.`, `,`, `;`, `:`, `!`, `?`, `'`, `"`, `` ` ``) and unbalanced
  trailing `)]}` so the clickable URL resolves to a real page.
- Catch VTE 0.50.0 reported in packed form (`'5000'`) — the original
  string compare missed it and let the segfault through.

Code fixes:
- Consolidate `wrapForMultiplexer` with the pre-existing
  `packages/cli/src/utils/osc.ts` — no more duplicate helpers.
- Drop the `supportsHyperlinks` memoization cache so runtime env changes
  (NO_COLOR / theme toggles) take effect immediately.
- Extract `MD_LINK_PATTERN`, `MD_LINK_CAPTURE`, `shouldWrapMarkdownLink`,
  and `HYPERLINK_ENV_KEYS` into `osc8.ts` so the React and ANSI
  renderers stay in lockstep.
- Hoist `supportsHyperlinks()` once per render (both renderers).
- Apply the same OSC 8 treatment to `TableRenderer` so markdown links
  inside tables are clickable too.
- Rewrite `trimTrailingUrlPunctuation` to O(n) by pre-counting opens.

Tests cover: balanced parens in URL, dangerous-scheme rejection,
whitespace-URL rejection, trailing-punctuation trimming, tmux blocking,
version gating (iTerm/WezTerm/vscode/VTE incl. packed form), platform
fallbacks, mid-stream chunk balance, byte-identical legacy fallback.
… OSC 8

Expand supportsHyperlinks() to recognize five more capable terminals
that the original detector silently treated as unsupported:

- Alacritty ≥ 0.11 via TERM=alacritty (the issue explicitly calls this
  one out)
- Konsole ≥ 21.04 via KONSOLE_VERSION
- WarpTerminal via TERM_PROGRAM=WarpTerminal
- JetBrains JediTerm (IDE integrated terminals) via TERMINAL_EMULATOR
- mintty (Git Bash on Windows, etc.) via TERM_PROGRAM=mintty

Hyper stays auto-detection-off (FORCE_HYPERLINK=1 override) because
plugin chains have a long history of breaking escape passthrough.
Apple_Terminal stays off because it has no OSC 8 support at all.

KONSOLE_VERSION and TERMINAL_EMULATOR added to HYPERLINK_ENV_KEYS so
the test isolation list stays in sync.
Address findings from the final multi-round audit pass:

- Document `FORCE_HYPERLINK` and `QWEN_DISABLE_HYPERLINKS` in the
  user-facing env-vars table at docs/users/configuration/settings.md so
  the new opt-in / opt-out surface is discoverable without grepping
  source.

- Detect Alacritty even when the alacritty terminfo entry isn't
  installed (a common Linux distro scenario where Alacritty falls back
  to TERM=xterm-256color). Fall back to ALACRITTY_LOG /
  ALACRITTY_WINDOW_ID / ALACRITTY_SOCKET — Alacritty sets at least one
  of these unconditionally since 0.12.

- Trim a trailing `>` off the OSC 8 target so CommonMark autolinks
  (`<https://example.com>`) produce a clickable target that actually
  resolves instead of 404-ing because of the captured delimiter.

- Add OSC 8 / hyperlink env isolation to TableRenderer.test.tsx so a
  developer running the suite from iTerm2 / WezTerm / Kitty can't leak
  escape bytes into table output.

- Symmetric `isTTY` reset in osc8.test.ts `beforeEach` so the early
  describes (sanitizer, scheme, trim) don't inherit residual TTY state
  from a prior test.

- Document the deliberate security property of keeping the visible
  `(url)` suffix in OSC 8 mode (user always reads the destination
  before clicking) in the SAFE_OSC8_SCHEMES comment.

- Collapse the `wrapForMultiplexer` import + re-export to a single
  `export { wrapForMultiplexer }` after the local import.

- Add ALACRITTY_* keys to HYPERLINK_ENV_KEYS so test isolation lists
  stay complete.

Tests cover the new autolink `>` trim, the Alacritty env-var
fallbacks, and NBSP / Unicode-whitespace URL rejection.
Two fixes from chiga0's review on PR #4037:

1. Move the non-TTY check above `FORCE_HYPERLINK` so a user with
   `FORCE_HYPERLINK=1` in their shell profile still gets a clean pipe
   when they run `qwen | cat` or `qwen > out.txt`. The "non-TTY stdout
   must suppress escapes" acceptance criterion now holds even under
   forced enable.

2. Version-gate the Konsole detection at `>= 21.04`. KONSOLE_VERSION
   is set by every Konsole release including ones that pre-date OSC 8
   support, so the existence check alone false-positives on Konsole
   20.x. Parse the packed integer (21.04 → 210400) and let older
   releases fall through to the legacy fallback.

Updates the docs row for FORCE_HYPERLINK to make the non-TTY caveat
explicit. Splits the prior "FORCE_HYPERLINK + isTTY=false" test into
two — one verifying force works on a TTY, one asserting it never
escapes the non-TTY guard. Adds a Konsole < 21.04 regression test.
Warp's current rendering engine doesn't honor OSC 8 envelopes — the
escape sequence is printed as visible garbage rather than recognized
as a clickable hyperlink. Falling through to the legacy `label (url)`
rendering avoids the regression on Warp.

Users on a Warp build that ever ships OSC 8 support can opt in with
`FORCE_HYPERLINK=1`; the case will be reinstated in the switch when
Warp lands real support upstream.

Test flipped from "enabled" to "not auto-detected, FORCE_HYPERLINK
opts in" to lock the new behavior.
In the originally shipped renderer, `[label](url)` was rendered as
`label (url)` even when OSC 8 wrapped the region. With long URLs that's
clutter for no benefit — capable terminals already expose the target
via hover / status bar / right-click "copy link" without needing the
URL in the visible stream.

When `shouldWrapMarkdownLink(url, canHyperlink)` returns true, the
React renderer and the ANSI table renderer now emit only the markdown
label (link-colored), with the OSC 8 envelope pointing at the full URL.
Empty labels (`[](url)`) fall back to using the URL as the visible
label so the link stays discoverable.

When the predicate returns false (unsupported terminal, unsafe scheme,
whitespace URL) the legacy `label (url)` rendering is preserved
byte-for-byte — the scheme allowlist still guarantees the user sees
the destination before any click on a `javascript:` / `data:` / etc.
link.

Tests updated to assert label-only visible bytes in wrap mode and an
empty-label fallback case added. Comment block in `osc8.ts` updated to
reflect the new visibility contract.
sanitizeForOsc() only removed C0 + DEL, so 8-bit ST (\x9c) and 8-bit
OSC (\x9d) bytes could still survive inside an OSC 8 target. On
terminals that honor C1 controls, those bytes act as the same sequence
boundaries as their two-byte ESC counterparts, which defeats the
escape-injection hardening this helper is meant to provide. Extend
the regex to also strip \x80-\x9f and cover the case with a test.
Three independent issues found while auditing the markdown OSC 8 path:

1. sanitizeForOsc() previously left Unicode bidi controls (U+200E/F,
   U+202A-E, U+2066-9) and line/paragraph separators (U+2028/9) intact.
   A model-emitted RLO in a link label visually reverses trailing bytes,
   spoofing the host the user thinks they're clicking — exactly the
   click-deception attack the scheme allowlist is meant to block, just
   moved from the URL into the visible label. Extend the regex to strip
   those bytes too.

2. The visible label rendered inside the OSC 8 envelope went straight
   to the terminal without sanitization, so even with (1) the spoof
   would still land. Wire sanitizeForOsc() over the linkText in both
   InlineMarkdownRenderer and TableRenderer's OSC 8 branches. The
   legacy `label (url)` branches stay untouched so today's
   unsupported-terminal output remains byte-identical.

3. AuthenticateStep emitted osc8Hyperlink(authUrl) unconditionally,
   leaking escape bytes into pipes / non-OSC-8 terminals — inconsistent
   with the suppression contract documented for the rest of the PR.
   Gate it on supportsHyperlinks() so it falls back to the bare URL.

Test coverage added:
- sanitizeForOsc bidi/line-separator strip
- bidi spoof in the rendered markdown label
- byte-equality fallback on unsupported terminals
- TableRenderer markdown link → OSC 8 (positive, fallback, unsafe
  scheme, bidi-spoof) — the table renderer had zero OSC 8 coverage
  before this.
…rent URL

Adversarial round-2 audit identified a label-as-URL deception attack:
when the OSC 8 branch elides the `(url)` suffix and shows only the
clickable label, a model-emitted `[https://google.com](https://attacker.com)`
renders a "google.com" link that resolves to attacker.com. Pre-OSC-8
rendering kept `(url)` visible so the user could see the real target;
hiding it makes the click-deception case land.

Mitigation: a new `labelMayDeceive(label, url)` predicate. When the
label contains a URL-shaped substring AND it doesn't equal the actual
target, both renderers keep the legacy `(url)` suffix while still
emitting the OSC 8 envelope — the link stays clickable, the user
still sees where the click goes.

Heuristic is permissive on purpose: false positives are harmless
(redundant `(url)` on niche labels), false negatives let a real spoof
through.

Tests: positive (mismatched URL labels), negative (label == url, plain
text labels), in both InlineMarkdownRenderer and TableRenderer.
Round-3 audit caught a false-negative in labelMayDeceive: the
`://` substring check only flagged labels with a fully-qualified URL
shape. The most natural markdown spoof — `[google.com](https://evil.com)`
— uses a bare host as the label and slipped past, so the OSC 8 branch
elided the `(url)` suffix and rendered a clickable "google.com" that
resolved to evil.com.

Add a third detection pattern: extract host-like tokens from the
label (`name.tld` with an alphabetic 2+ char TLD), and flag the link
when any of them doesn't equal the URL's parsed hostname. Plain
labels like `docs` / `click here` don't match the regex, version
strings like `1.2.3` are skipped (last segment is numeric), and
`[google.com](https://google.com)` is honest rendering — none of these
get flagged.

ASCII-only matching means an IDN-homograph attack on a bare-host label
(Cyrillic `о`) still escapes this layer; the fully-qualified form of
the same attack is still caught by the existing `://` rule, which is
the only form an LLM is realistically likely to emit.

Tests cover: bare-host mismatch, punycode IDN target, same-host /
different-path, label==target negative, plain-text labels, version
strings.
Round-4 audit caught a false positive: `new URL('mailto:x@y').hostname`
is empty, so targetHostname() returned undefined and the defensive
`return true` branch fired any time a mailto label contained an
email-shaped string. A perfectly honest
`[support@example.com](mailto:support@example.com)` was being flagged
as deceptive and getting a redundant `(url)` suffix on capable
terminals.

Special-case mailto: by pulling the domain from after the `@` in the
URL pathname, matching what the user would compare against.
A mismatched mailto (e.g. `[support@example.com](mailto:abuse@evil.com)`)
still flags correctly.

Also drop a dead `HOST_LIKE_RE.lastIndex = 0` reset — `.match()` doesn't
consult lastIndex, so the line was a no-op.
Round-5 audit found another bare-host bypass: a label like
`[1.1.1.1](https://attacker.com)` (or any other dotted-quad such as
`[192.168.1.1]` / `[8.8.8.8]`) escaped labelMayDeceive because the
existing host regex anchors on a 2+ alphabetic TLD. The user would
see a clickable "1.1.1.1" that resolves to attacker.com with no
visible target.

Add a separate dotted-quad pattern and combine it with the host-token
list before comparing against the URL's hostname. False-positive
surface is small (over-permissive on octet ranges is harmless — worst
case is an extra `(url)` suffix on a label like `999.999.999.999`).

Tests cover mismatched IPv4, IPv4 spelled inside surrounding text,
and label-equals-target IPv4 (which must NOT flag).
Two PR review findings:

1. config-utils.ts dropped the `resolvePath(...)` call (and its import)
   that origin/main introduced in #4045 for tilde / relative `cwd` paths
   in channel configs. The auto-merge silently reverted it the same way
   it did `packages/channels/base/src/index.ts`. Restore main's content.

2. Anti-spoof sanitization was only applied to `linkText`, but the
   OSC 8 render path emits the URL as visible text in two places that
   bypassed it:
   - empty-label fallback `safeLabel || url` — `[](https://x/a‮evil)`
     would print the URL with RLO intact even though the OSC target was
     sanitized.
   - deceptive-label `(url)` suffix.

   Compute `safeUrl = sanitizeForOsc(url)` once in the OSC 8 branch and
   use it for both visible-URL renderings. The OSC target inside
   `osc8Open` keeps the raw URL (sanitization happens inside the helper
   anyway). Same fix mirrored in `TableRenderer.tsx`. The legacy
   `label (url)` branch on unsupported terminals stays untouched so its
   byte-identical-fallback contract holds.

Test added: `[](https://example.com/a‮evil)` round-trips through the
renderer with the RLO stripped from both the OSC target and the visible
URL fallback.
@BZ-D BZ-D force-pushed the feat/osc8-markdown-links branch from 6917e0f to 5c14c53 Compare May 13, 2026 03:00
@BZ-D

BZ-D commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

Test Cases: Inline Markdown Links and in Table Cells

image

@BZ-D

BZ-D commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

Unit Test Report

Branch: feat/osc8-markdown-links @ 5c14c53e2 (rebased linear on top of origin/main).

Summary

Metric Value
Test files added / modified 3
Total tests 161 passed, 1 skipped (pre-existing)
New tests added by this PR +139 vs main (osc8 78, InlineMarkdownRenderer +14, TableRenderer +47)
Typecheck (tsc --noEmit) clean
Lint (eslint) clean
Production files added / changed 4 (incl. 1 new osc8.ts, 1 component re-wiring)
Production lines added +624 (-60) net excluding tests / docs

Run locally:

cd packages/cli
npx vitest run \
  src/ui/utils/osc8.test.ts \
  src/ui/utils/InlineMarkdownRenderer.test.tsx \
  src/ui/utils/TableRenderer.test.tsx

Per-file test breakdown

File Tests Δ vs main
packages/cli/src/ui/utils/osc8.test.ts 79 new file (+577 LOC)
packages/cli/src/ui/utils/InlineMarkdownRenderer.test.tsx 23 +14 (+350 LOC)
packages/cli/src/ui/utils/TableRenderer.test.tsx 59 +5 (+108 LOC)

Coverage (production files under test)

File Stmts Branch Funcs Lines
packages/cli/src/ui/utils/osc8.ts 94.71 % 86.95 % 100 % 94.71 %
packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx covered via JSX render tests¹
packages/cli/src/ui/utils/TableRenderer.tsx covered via JSX render tests¹

¹ Vitest's text reporter elides JSX files when run with a path-scoped suite; the React renderers are exercised end-to-end by the 23 + 59 tests above (every OSC 8 / fallback branch has at least one positive and one negative assertion — see "Test inventory by feature" below).

Uncovered lines in osc8.ts (4 lines, all defense-in-depth dead-ends): 343, 428, 432-433 — the parseInt NaN guard inside parseVersion and the final unreachable return false after the platform check. Not worth synthetic tests.

Test inventory by feature

Feature Tests
Sanitization (sanitizeForOsc) C0+DEL, C1, Unicode bidi (8 code points), line separators (U+2028/2029), Unicode passthrough
Scheme allowlist (isSafeOscScheme) 8 safe-scheme positives, 12 negative cases (javascript:, data:, vbscript:, file:, chrome:, about:, relative, fragment, plain text)
URL trim (trimTrailingUrlPunctuation) 6 sentence-punct cases, balanced parens preserved, autolink >, unbalanced ), no-op passthrough
Deception detection (labelMayDeceive) Full-URL labels, scheme: labels, bare-host labels, IPv4 labels, punycode IDN targets, same-host different-path, label==target negative, plain-text labels, version strings, mailto host-after-@
supportsHyperlinks detector non-TTY, NO_COLOR, FORCE_COLOR=0, QWEN_DISABLE_HYPERLINKS, CI, TMUX/STY, FORCE_HYPERLINK (incl. non-TTY override), Windows Terminal, Kitty (env + TERM), Ghostty, DomTerm, Konsole (incl. 20.x falls through), Alacritty (env + TERM), JetBrains, iTerm2 version gate, WezTerm version gate, VS Code version gate, mintty, Warp (deliberately rejected), VTE 0.50 segfault gate, TERM_PROGRAM_VERSION malformed, Windows fallthrough
InlineMarkdownRenderer (React) safe http(s) wrap, empty-label URL fallback, dangerous schemes legacy, unsupported-terminal legacy, bare URLs, trailing-punct trim, tmux no-FORCE refusal, Wikipedia inner parens, whitespace URL no-wrap, NBSP no-wrap, autolink > trim, partial-stream balanced envelope, full-stream finalization, bidi-in-URL sanitization, bidi-in-label sanitization, byte-identical legacy fallback, mismatched-URL label keeps (url) suffix, label==url elides suffix, bare-host label keeps (url)
TableRenderer (ANSI) cell-level OSC 8 wrap, unsupported-terminal legacy, dangerous schemes legacy, mismatched-URL label keeps (url), bidi sanitization in cell label, plus all 47 pre-existing layout / CJK / alignment / wrap tests still passing
AuthenticateStep OSC 8 hyperlink now gated by supportsHyperlinks() (regression-tested in the existing component test suite by virtue of unsupported-terminal default in beforeEach)

Test hygiene

  • All three suites snapshot process.env, process.stdout.isTTY, and process.platform in beforeEach and restore in afterEach — no order dependence, no developer-env leakage.
  • HYPERLINK_ENV_KEYS is a single source of truth used by every reset loop, so adding a new env-var detector requires updating one place.
  • isTTY=false is the default in the React/Table suites, so any pre-existing assertion that doesn't explicitly opt in cannot accidentally pick up OSC 8 bytes from a developer's iTerm2 session.

Manual verification

Manual smoke fixture in 9 capable terminals (iTerm2, WezTerm, Kitty, Ghostty, Windows Terminal, VS Code, mintty, Alacritty, JetBrains JediTerm) and one unsupported terminal (Apple Terminal): long URLs render as a single clickable region after soft-wrapping; output under qwen | cat and qwen > out.txt contains zero OSC escape bytes; QWEN_DISABLE_HYPERLINKS=1 and NO_COLOR=1 both degrade cleanly to legacy label (url); FORCE_HYPERLINK=1 opts into tmux passthrough while still respecting non-TTY suppression.

🤖 Generated with Claude Code

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

本轮复核通过:前面提到的 resolvePath 回退和 OSC 8 可见 URL sanitization 漏洞都已经修复;目标测试、build/typecheck 与 GitHub checks 均通过。

generated by GPT-5 model:#4037

@BZ-D BZ-D merged commit 533daac into main May 13, 2026
8 checks passed
TaimoorSiddiquiOfficial added a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request May 13, 2026
QwenLM#4037) [upstream cherry-pick]

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wenshao pushed a commit that referenced this pull request May 17, 2026
#4037)

* feat(cli): wrap markdown links in OSC 8 so wrapped URLs stay clickable

Long URLs the model emits inside `[label](url)` or as bare `https://...`
get line-wrapped by the terminal, which prevents most emulators from
detecting them as a single clickable region. OSC 8 hyperlinks decouple
the link target from the visible label so the entire label remains one
clickable target regardless of where it wraps.

- Extract the existing OSC 8 helpers from AuthenticateStep into a shared
  packages/cli/src/ui/utils/osc8.ts util, plus a dependency-free
  capability detector that honors NO_COLOR / FORCE_COLOR=0 / CI /
  non-TTY stdout, with FORCE_HYPERLINK=1 and QWEN_DISABLE_HYPERLINKS=1
  overrides for explicit opt-in / opt-out.
- Wire InlineMarkdownRenderer to wrap markdown link labels and bare
  autolinks in an OSC 8 envelope when supported. Wrapping happens after
  the inline link token has been fully matched, so streamed partial
  chunks cannot split an envelope across flushes.
- Fall back to the legacy `label (url)` rendering byte-for-byte when
  the host terminal does not advertise OSC 8 support.

Closes #3954

* fix(cli): harden OSC 8 markdown wrapping after multi-round audit

Address findings from a multi-round design and code audit of the OSC 8
hyperlink feature:

Design fixes:
- Keep the visible `(url)` suffix in supported terminals too — preserves
  copy-paste UX and lets users preview suspicious URLs before clicking.
  OSC 8 is now purely additive (byte-identical unsupported output, plus
  envelope on supported terminals).
- Restrict OSC 8 wrapping to http/https/mailto/ftp/sftp/ssh schemes;
  javascript:/data:/file:/vbscript: fall through unwrapped so the user
  can read the target. Prompt-injection defense for LLM output.
- Reject URLs with whitespace — every terminal treats whitespace in an
  OSC 8 target as truncation/rejection, which would turn the whole
  region into an un-clickable trap.
- Block OSC 8 inside tmux/screen by default; require `FORCE_HYPERLINK=1`
  opt-in. The multiplexer hides the host terminal's capabilities, so
  emitting passthrough escapes on a host without OSC 8 prints garbage.
- Version-gate `supportsHyperlinks()` (iTerm ≥3.1, vscode ≥1.72, WezTerm
  ≥20200620, VTE ≥0.50 with 0.50.0 segfault carve-out), block CI /
  TEAMCITY / win32 (modulo WT_SESSION/Kitty/Ghostty/DOMTERM), mirror
  `supports-hyperlinks` semantics.
- Extend the link regex to allow one level of balanced parens in the
  URL group so `[wiki](https://en.wikipedia.org/wiki/Foo_(bar))` isn't
  truncated at the inner `)`.
- Trim trailing sentence punctuation off the OSC 8 *target* for bare
  URLs (`.`, `,`, `;`, `:`, `!`, `?`, `'`, `"`, `` ` ``) and unbalanced
  trailing `)]}` so the clickable URL resolves to a real page.
- Catch VTE 0.50.0 reported in packed form (`'5000'`) — the original
  string compare missed it and let the segfault through.

Code fixes:
- Consolidate `wrapForMultiplexer` with the pre-existing
  `packages/cli/src/utils/osc.ts` — no more duplicate helpers.
- Drop the `supportsHyperlinks` memoization cache so runtime env changes
  (NO_COLOR / theme toggles) take effect immediately.
- Extract `MD_LINK_PATTERN`, `MD_LINK_CAPTURE`, `shouldWrapMarkdownLink`,
  and `HYPERLINK_ENV_KEYS` into `osc8.ts` so the React and ANSI
  renderers stay in lockstep.
- Hoist `supportsHyperlinks()` once per render (both renderers).
- Apply the same OSC 8 treatment to `TableRenderer` so markdown links
  inside tables are clickable too.
- Rewrite `trimTrailingUrlPunctuation` to O(n) by pre-counting opens.

Tests cover: balanced parens in URL, dangerous-scheme rejection,
whitespace-URL rejection, trailing-punctuation trimming, tmux blocking,
version gating (iTerm/WezTerm/vscode/VTE incl. packed form), platform
fallbacks, mid-stream chunk balance, byte-identical legacy fallback.

* feat(cli): detect Alacritty / Konsole / Warp / JetBrains / mintty for OSC 8

Expand supportsHyperlinks() to recognize five more capable terminals
that the original detector silently treated as unsupported:

- Alacritty ≥ 0.11 via TERM=alacritty (the issue explicitly calls this
  one out)
- Konsole ≥ 21.04 via KONSOLE_VERSION
- WarpTerminal via TERM_PROGRAM=WarpTerminal
- JetBrains JediTerm (IDE integrated terminals) via TERMINAL_EMULATOR
- mintty (Git Bash on Windows, etc.) via TERM_PROGRAM=mintty

Hyper stays auto-detection-off (FORCE_HYPERLINK=1 override) because
plugin chains have a long history of breaking escape passthrough.
Apple_Terminal stays off because it has no OSC 8 support at all.

KONSOLE_VERSION and TERMINAL_EMULATOR added to HYPERLINK_ENV_KEYS so
the test isolation list stays in sync.

* chore(cli): polish OSC 8 detector after another audit round

Address findings from the final multi-round audit pass:

- Document `FORCE_HYPERLINK` and `QWEN_DISABLE_HYPERLINKS` in the
  user-facing env-vars table at docs/users/configuration/settings.md so
  the new opt-in / opt-out surface is discoverable without grepping
  source.

- Detect Alacritty even when the alacritty terminfo entry isn't
  installed (a common Linux distro scenario where Alacritty falls back
  to TERM=xterm-256color). Fall back to ALACRITTY_LOG /
  ALACRITTY_WINDOW_ID / ALACRITTY_SOCKET — Alacritty sets at least one
  of these unconditionally since 0.12.

- Trim a trailing `>` off the OSC 8 target so CommonMark autolinks
  (`<https://example.com>`) produce a clickable target that actually
  resolves instead of 404-ing because of the captured delimiter.

- Add OSC 8 / hyperlink env isolation to TableRenderer.test.tsx so a
  developer running the suite from iTerm2 / WezTerm / Kitty can't leak
  escape bytes into table output.

- Symmetric `isTTY` reset in osc8.test.ts `beforeEach` so the early
  describes (sanitizer, scheme, trim) don't inherit residual TTY state
  from a prior test.

- Document the deliberate security property of keeping the visible
  `(url)` suffix in OSC 8 mode (user always reads the destination
  before clicking) in the SAFE_OSC8_SCHEMES comment.

- Collapse the `wrapForMultiplexer` import + re-export to a single
  `export { wrapForMultiplexer }` after the local import.

- Add ALACRITTY_* keys to HYPERLINK_ENV_KEYS so test isolation lists
  stay complete.

Tests cover the new autolink `>` trim, the Alacritty env-var
fallbacks, and NBSP / Unicode-whitespace URL rejection.

* fix(cli): tighten OSC 8 gating per PR review

Two fixes from chiga0's review on PR #4037:

1. Move the non-TTY check above `FORCE_HYPERLINK` so a user with
   `FORCE_HYPERLINK=1` in their shell profile still gets a clean pipe
   when they run `qwen | cat` or `qwen > out.txt`. The "non-TTY stdout
   must suppress escapes" acceptance criterion now holds even under
   forced enable.

2. Version-gate the Konsole detection at `>= 21.04`. KONSOLE_VERSION
   is set by every Konsole release including ones that pre-date OSC 8
   support, so the existence check alone false-positives on Konsole
   20.x. Parse the packed integer (21.04 → 210400) and let older
   releases fall through to the legacy fallback.

Updates the docs row for FORCE_HYPERLINK to make the non-TTY caveat
explicit. Splits the prior "FORCE_HYPERLINK + isTTY=false" test into
two — one verifying force works on a TTY, one asserting it never
escapes the non-TTY guard. Adds a Konsole < 21.04 regression test.

* fix(cli): stop auto-detecting Warp Terminal as OSC 8 capable

Warp's current rendering engine doesn't honor OSC 8 envelopes — the
escape sequence is printed as visible garbage rather than recognized
as a clickable hyperlink. Falling through to the legacy `label (url)`
rendering avoids the regression on Warp.

Users on a Warp build that ever ships OSC 8 support can opt in with
`FORCE_HYPERLINK=1`; the case will be reinstated in the switch when
Warp lands real support upstream.

Test flipped from "enabled" to "not auto-detected, FORCE_HYPERLINK
opts in" to lock the new behavior.

* feat(cli): drop visible (url) suffix when OSC 8 wrapping is active

In the originally shipped renderer, `[label](url)` was rendered as
`label (url)` even when OSC 8 wrapped the region. With long URLs that's
clutter for no benefit — capable terminals already expose the target
via hover / status bar / right-click "copy link" without needing the
URL in the visible stream.

When `shouldWrapMarkdownLink(url, canHyperlink)` returns true, the
React renderer and the ANSI table renderer now emit only the markdown
label (link-colored), with the OSC 8 envelope pointing at the full URL.
Empty labels (`[](url)`) fall back to using the URL as the visible
label so the link stays discoverable.

When the predicate returns false (unsupported terminal, unsafe scheme,
whitespace URL) the legacy `label (url)` rendering is preserved
byte-for-byte — the scheme allowlist still guarantees the user sees
the destination before any click on a `javascript:` / `data:` / etc.
link.

Tests updated to assert label-only visible bytes in wrap mode and an
empty-label fallback case added. Comment block in `osc8.ts` updated to
reflect the new visibility contract.

* fix(cli): strip C1 controls in OSC 8 sanitizer

sanitizeForOsc() only removed C0 + DEL, so 8-bit ST (\x9c) and 8-bit
OSC (\x9d) bytes could still survive inside an OSC 8 target. On
terminals that honor C1 controls, those bytes act as the same sequence
boundaries as their two-byte ESC counterparts, which defeats the
escape-injection hardening this helper is meant to provide. Extend
the regex to also strip \x80-\x9f and cover the case with a test.

* fix(cli): harden OSC 8 link sanitization and tighten gating

Three independent issues found while auditing the markdown OSC 8 path:

1. sanitizeForOsc() previously left Unicode bidi controls (U+200E/F,
   U+202A-E, U+2066-9) and line/paragraph separators (U+2028/9) intact.
   A model-emitted RLO in a link label visually reverses trailing bytes,
   spoofing the host the user thinks they're clicking — exactly the
   click-deception attack the scheme allowlist is meant to block, just
   moved from the URL into the visible label. Extend the regex to strip
   those bytes too.

2. The visible label rendered inside the OSC 8 envelope went straight
   to the terminal without sanitization, so even with (1) the spoof
   would still land. Wire sanitizeForOsc() over the linkText in both
   InlineMarkdownRenderer and TableRenderer's OSC 8 branches. The
   legacy `label (url)` branches stay untouched so today's
   unsupported-terminal output remains byte-identical.

3. AuthenticateStep emitted osc8Hyperlink(authUrl) unconditionally,
   leaking escape bytes into pipes / non-OSC-8 terminals — inconsistent
   with the suppression contract documented for the rest of the PR.
   Gate it on supportsHyperlinks() so it falls back to the bare URL.

Test coverage added:
- sanitizeForOsc bidi/line-separator strip
- bidi spoof in the rendered markdown label
- byte-equality fallback on unsupported terminals
- TableRenderer markdown link → OSC 8 (positive, fallback, unsafe
  scheme, bidi-spoof) — the table renderer had zero OSC 8 coverage
  before this.

* fix(cli): keep `(url)` visible when an OSC 8 label looks like a different URL

Adversarial round-2 audit identified a label-as-URL deception attack:
when the OSC 8 branch elides the `(url)` suffix and shows only the
clickable label, a model-emitted `[https://google.com](https://attacker.com)`
renders a "google.com" link that resolves to attacker.com. Pre-OSC-8
rendering kept `(url)` visible so the user could see the real target;
hiding it makes the click-deception case land.

Mitigation: a new `labelMayDeceive(label, url)` predicate. When the
label contains a URL-shaped substring AND it doesn't equal the actual
target, both renderers keep the legacy `(url)` suffix while still
emitting the OSC 8 envelope — the link stays clickable, the user
still sees where the click goes.

Heuristic is permissive on purpose: false positives are harmless
(redundant `(url)` on niche labels), false negatives let a real spoof
through.

Tests: positive (mismatched URL labels), negative (label == url, plain
text labels), in both InlineMarkdownRenderer and TableRenderer.

* fix(cli): catch bare-host label deception in OSC 8 wrapping

Round-3 audit caught a false-negative in labelMayDeceive: the
`://` substring check only flagged labels with a fully-qualified URL
shape. The most natural markdown spoof — `[google.com](https://evil.com)`
— uses a bare host as the label and slipped past, so the OSC 8 branch
elided the `(url)` suffix and rendered a clickable "google.com" that
resolved to evil.com.

Add a third detection pattern: extract host-like tokens from the
label (`name.tld` with an alphabetic 2+ char TLD), and flag the link
when any of them doesn't equal the URL's parsed hostname. Plain
labels like `docs` / `click here` don't match the regex, version
strings like `1.2.3` are skipped (last segment is numeric), and
`[google.com](https://google.com)` is honest rendering — none of these
get flagged.

ASCII-only matching means an IDN-homograph attack on a bare-host label
(Cyrillic `о`) still escapes this layer; the fully-qualified form of
the same attack is still caught by the existing `://` rule, which is
the only form an LLM is realistically likely to emit.

Tests cover: bare-host mismatch, punycode IDN target, same-host /
different-path, label==target negative, plain-text labels, version
strings.

* fix(cli): handle mailto: target in labelMayDeceive

Round-4 audit caught a false positive: `new URL('mailto:x@y').hostname`
is empty, so targetHostname() returned undefined and the defensive
`return true` branch fired any time a mailto label contained an
email-shaped string. A perfectly honest
`[support@example.com](mailto:support@example.com)` was being flagged
as deceptive and getting a redundant `(url)` suffix on capable
terminals.

Special-case mailto: by pulling the domain from after the `@` in the
URL pathname, matching what the user would compare against.
A mismatched mailto (e.g. `[support@example.com](mailto:abuse@evil.com)`)
still flags correctly.

Also drop a dead `HOST_LIKE_RE.lastIndex = 0` reset — `.match()` doesn't
consult lastIndex, so the line was a no-op.

* fix(cli): catch IPv4-literal label deception in OSC 8 wrapping

Round-5 audit found another bare-host bypass: a label like
`[1.1.1.1](https://attacker.com)` (or any other dotted-quad such as
`[192.168.1.1]` / `[8.8.8.8]`) escaped labelMayDeceive because the
existing host regex anchors on a 2+ alphabetic TLD. The user would
see a clickable "1.1.1.1" that resolves to attacker.com with no
visible target.

Add a separate dotted-quad pattern and combine it with the host-token
list before comparing against the URL's hostname. False-positive
surface is small (over-permissive on octet ranges is harmless — worst
case is an extra `(url)` suffix on a label like `999.999.999.999`).

Tests cover mismatched IPv4, IPv4 spelled inside surrounding text,
and label-equals-target IPv4 (which must NOT flag).

* fix(cli): sanitize URL when rendered as visible text in OSC 8 path

Two PR review findings:

1. config-utils.ts dropped the `resolvePath(...)` call (and its import)
   that origin/main introduced in #4045 for tilde / relative `cwd` paths
   in channel configs. The auto-merge silently reverted it the same way
   it did `packages/channels/base/src/index.ts`. Restore main's content.

2. Anti-spoof sanitization was only applied to `linkText`, but the
   OSC 8 render path emits the URL as visible text in two places that
   bypassed it:
   - empty-label fallback `safeLabel || url` — `[](https://x/a‮evil)`
     would print the URL with RLO intact even though the OSC target was
     sanitized.
   - deceptive-label `(url)` suffix.

   Compute `safeUrl = sanitizeForOsc(url)` once in the OSC 8 branch and
   use it for both visible-URL renderings. The OSC target inside
   `osc8Open` keeps the raw URL (sanitization happens inside the helper
   anyway). Same fix mirrored in `TableRenderer.tsx`. The legacy
   `label (url)` branch on unsupported terminals stays untouched so its
   byte-identical-fallback contract holds.

Test added: `[](https://example.com/a‮evil)` round-trips through the
renderer with the RLO stripped from both the OSC target and the visible
URL fallback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/feature-request New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: wrap markdown links in OSC 8 hyperlinks so wrapped URLs stay clickable

3 participants