@bastani/atomic 0.9.10 → 0.9.11-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2339) hide show
  1. package/CHANGELOG.md +255 -0
  2. package/README.md +15 -10
  3. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  4. package/dist/builtin/intercom/package.json +4 -4
  5. package/dist/builtin/mcp/CHANGELOG.md +18 -0
  6. package/dist/builtin/mcp/README.md +3 -3
  7. package/dist/builtin/mcp/cli.js +0 -1
  8. package/dist/builtin/mcp/commands.ts +7 -3
  9. package/dist/builtin/mcp/config.ts +0 -2
  10. package/dist/builtin/mcp/package.json +6 -6
  11. package/dist/builtin/mcp/sampling-handler.ts +1 -5
  12. package/dist/builtin/mcp/types.ts +0 -1
  13. package/dist/builtin/subagents/CHANGELOG.md +40 -0
  14. package/dist/builtin/subagents/README.md +10 -1
  15. package/dist/builtin/subagents/agents/code-simplifier.md +48 -103
  16. package/dist/builtin/subagents/agents/codebase-analyzer.md +28 -131
  17. package/dist/builtin/subagents/agents/codebase-locator.md +25 -84
  18. package/dist/builtin/subagents/agents/codebase-online-researcher.md +61 -260
  19. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +30 -208
  20. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +29 -151
  21. package/dist/builtin/subagents/agents/codebase-research-locator.md +22 -119
  22. package/dist/builtin/subagents/agents/debugger.md +29 -65
  23. package/dist/builtin/subagents/agents/worker.md +24 -31
  24. package/dist/builtin/subagents/package.json +5 -5
  25. package/dist/builtin/subagents/prompts/gather-context-and-clarify.md +18 -11
  26. package/dist/builtin/subagents/prompts/parallel-cleanup.md +34 -33
  27. package/dist/builtin/subagents/prompts/parallel-context-build.md +22 -36
  28. package/dist/builtin/subagents/prompts/parallel-handoff-plan.md +20 -52
  29. package/dist/builtin/subagents/prompts/parallel-research.md +17 -41
  30. package/dist/builtin/subagents/prompts/parallel-review.md +23 -28
  31. package/dist/builtin/subagents/prompts/review-loop.md +18 -27
  32. package/dist/builtin/subagents/skills/subagent/SKILL.md +6 -6
  33. package/dist/builtin/subagents/src/agents/agent-discovery.ts +26 -15
  34. package/dist/builtin/subagents/src/agents/agent-loaders.ts +48 -34
  35. package/dist/builtin/subagents/src/agents/agent-management-helpers.ts +80 -31
  36. package/dist/builtin/subagents/src/agents/agent-management.ts +194 -55
  37. package/dist/builtin/subagents/src/agents/agent-overrides.ts +85 -32
  38. package/dist/builtin/subagents/src/agents/agent-paths.ts +4 -1
  39. package/dist/builtin/subagents/src/agents/agent-selection.ts +1 -1
  40. package/dist/builtin/subagents/src/agents/agent-serializer.ts +1 -4
  41. package/dist/builtin/subagents/src/agents/agents.ts +9 -9
  42. package/dist/builtin/subagents/src/agents/chain-serializer.ts +9 -5
  43. package/dist/builtin/subagents/src/agents/identity.ts +12 -3
  44. package/dist/builtin/subagents/src/agents/skills-paths.ts +64 -24
  45. package/dist/builtin/subagents/src/agents/skills.ts +29 -26
  46. package/dist/builtin/subagents/src/extension/doctor.ts +31 -20
  47. package/dist/builtin/subagents/src/extension/fanout-child.ts +27 -12
  48. package/dist/builtin/subagents/src/extension/index.ts +119 -43
  49. package/dist/builtin/subagents/src/extension/prompt-guidance.ts +4 -2
  50. package/dist/builtin/subagents/src/extension/schemas.ts +304 -177
  51. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +137 -73
  52. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +49 -21
  53. package/dist/builtin/subagents/src/intercom/result-intercom.ts +76 -38
  54. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +12 -6
  55. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +183 -57
  56. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +15 -18
  57. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +67 -26
  58. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +8 -8
  59. package/dist/builtin/subagents/src/runs/background/async-execution.ts +1 -1
  60. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +86 -32
  61. package/dist/builtin/subagents/src/runs/background/async-resume.ts +93 -36
  62. package/dist/builtin/subagents/src/runs/background/async-status.ts +82 -27
  63. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +12 -5
  64. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +3 -1
  65. package/dist/builtin/subagents/src/runs/background/completion-notification.ts +3 -1
  66. package/dist/builtin/subagents/src/runs/background/notify.ts +38 -18
  67. package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +28 -14
  68. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +147 -79
  69. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +20 -5
  70. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +7 -2
  71. package/dist/builtin/subagents/src/runs/background/result-status.ts +11 -5
  72. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +18 -5
  73. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +104 -35
  74. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +28 -7
  75. package/dist/builtin/subagents/src/runs/background/run-status.ts +120 -32
  76. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +98 -39
  77. package/dist/builtin/subagents/src/runs/background/subagent-runner-dynamic.ts +185 -37
  78. package/dist/builtin/subagents/src/runs/background/subagent-runner-finalize.ts +23 -9
  79. package/dist/builtin/subagents/src/runs/background/subagent-runner-output.ts +6 -2
  80. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel-helpers.ts +34 -18
  81. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel.ts +175 -26
  82. package/dist/builtin/subagents/src/runs/background/subagent-runner-sequential.ts +68 -23
  83. package/dist/builtin/subagents/src/runs/background/subagent-runner-state.ts +200 -49
  84. package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +82 -49
  85. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +33 -11
  86. package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +16 -6
  87. package/dist/builtin/subagents/src/runs/background/subagent-runner-utils.ts +2 -3
  88. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +11 -8
  89. package/dist/builtin/subagents/src/runs/foreground/chain-execution-details.ts +5 -2
  90. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +115 -31
  91. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +71 -54
  92. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +104 -37
  93. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +102 -61
  94. package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +24 -10
  95. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +9 -6
  96. package/dist/builtin/subagents/src/runs/foreground/detached-cleanup-barrier.ts +22 -22
  97. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-control.ts +45 -40
  98. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +6 -5
  99. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +118 -43
  100. package/dist/builtin/subagents/src/runs/foreground/execution-detach-reservations.ts +17 -7
  101. package/dist/builtin/subagents/src/runs/foreground/execution-detach-route.ts +5 -3
  102. package/dist/builtin/subagents/src/runs/foreground/execution-intercom-detach.ts +39 -35
  103. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +81 -42
  104. package/dist/builtin/subagents/src/runs/foreground/execution-structured-retries.ts +27 -21
  105. package/dist/builtin/subagents/src/runs/foreground/execution-updates.ts +2 -5
  106. package/dist/builtin/subagents/src/runs/foreground/execution-utils.ts +9 -4
  107. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +69 -25
  108. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +36 -19
  109. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +95 -62
  110. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +30 -9
  111. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +106 -96
  112. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +68 -36
  113. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +262 -83
  114. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +6 -1
  115. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +67 -41
  116. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +129 -44
  117. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +20 -3
  118. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-worktree.ts +8 -6
  119. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +65 -47
  120. package/dist/builtin/subagents/src/runs/shared/chain-outputs.ts +37 -12
  121. package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +120 -34
  122. package/dist/builtin/subagents/src/runs/shared/final-drain.ts +12 -7
  123. package/dist/builtin/subagents/src/runs/shared/intercom-group.ts +3 -2
  124. package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +8 -3
  125. package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +42 -20
  126. package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +8 -4
  127. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +103 -38
  128. package/dist/builtin/subagents/src/runs/shared/nested-events-control.ts +30 -20
  129. package/dist/builtin/subagents/src/runs/shared/nested-events-core.ts +45 -20
  130. package/dist/builtin/subagents/src/runs/shared/nested-events-projection.ts +57 -23
  131. package/dist/builtin/subagents/src/runs/shared/nested-events-registry.ts +77 -29
  132. package/dist/builtin/subagents/src/runs/shared/nested-events-sanitize.ts +99 -42
  133. package/dist/builtin/subagents/src/runs/shared/nested-events.ts +32 -26
  134. package/dist/builtin/subagents/src/runs/shared/nested-path.ts +22 -17
  135. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +31 -10
  136. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +10 -8
  137. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +59 -33
  138. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +17 -13
  139. package/dist/builtin/subagents/src/runs/shared/run-history.ts +13 -6
  140. package/dist/builtin/subagents/src/runs/shared/single-output.ts +11 -6
  141. package/dist/builtin/subagents/src/runs/shared/spawn-env.ts +21 -0
  142. package/dist/builtin/subagents/src/runs/shared/structured-output.ts +33 -19
  143. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +59 -48
  144. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +18 -11
  145. package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +41 -11
  146. package/dist/builtin/subagents/src/runs/shared/worktree-post-create.ts +46 -10
  147. package/dist/builtin/subagents/src/runs/shared/worktree-root.ts +21 -5
  148. package/dist/builtin/subagents/src/runs/shared/worktree.ts +89 -34
  149. package/dist/builtin/subagents/src/shared/artifacts.ts +137 -7
  150. package/dist/builtin/subagents/src/shared/event-jsonl-writer.ts +49 -13
  151. package/dist/builtin/subagents/src/shared/fast-mode.ts +13 -6
  152. package/dist/builtin/subagents/src/shared/fork-context.ts +7 -4
  153. package/dist/builtin/subagents/src/shared/formatters.ts +14 -9
  154. package/dist/builtin/subagents/src/shared/jsonl-writer.ts +2 -1
  155. package/dist/builtin/subagents/src/shared/model-info.ts +10 -3
  156. package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +7 -6
  157. package/dist/builtin/subagents/src/shared/session-tokens.ts +2 -1
  158. package/dist/builtin/subagents/src/shared/settings.ts +43 -43
  159. package/dist/builtin/subagents/src/shared/status-format.ts +10 -2
  160. package/dist/builtin/subagents/src/shared/types-async.ts +22 -19
  161. package/dist/builtin/subagents/src/shared/types-config.ts +1 -1
  162. package/dist/builtin/subagents/src/shared/types-depth.ts +23 -11
  163. package/dist/builtin/subagents/src/shared/types-results.ts +49 -6
  164. package/dist/builtin/subagents/src/shared/types-runtime.ts +16 -17
  165. package/dist/builtin/subagents/src/shared/types.ts +2 -2
  166. package/dist/builtin/subagents/src/shared/utils.ts +34 -20
  167. package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +44 -47
  168. package/dist/builtin/subagents/src/slash/saved-chain-mapping.ts +5 -7
  169. package/dist/builtin/subagents/src/slash/slash-bridge.ts +3 -3
  170. package/dist/builtin/subagents/src/slash/slash-commands.ts +78 -52
  171. package/dist/builtin/subagents/src/slash/slash-live-state.ts +59 -32
  172. package/dist/builtin/subagents/src/tui/render-chain-graph.ts +120 -32
  173. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +85 -22
  174. package/dist/builtin/subagents/src/tui/render-helpers.ts +16 -6
  175. package/dist/builtin/subagents/src/tui/render-layout.ts +8 -3
  176. package/dist/builtin/subagents/src/tui/render-result-compact.ts +125 -43
  177. package/dist/builtin/subagents/src/tui/render-result.ts +128 -85
  178. package/dist/builtin/subagents/src/tui/render-stable-output.ts +17 -13
  179. package/dist/builtin/subagents/src/tui/render-status-progress.ts +52 -20
  180. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +130 -28
  181. package/dist/builtin/subagents/src/tui/render-widget.ts +54 -27
  182. package/dist/builtin/subagents/src/tui/render.ts +10 -3
  183. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  184. package/dist/builtin/web-access/package.json +2 -2
  185. package/dist/builtin/workflows/CHANGELOG.md +127 -0
  186. package/dist/builtin/workflows/README.md +83 -70
  187. package/dist/builtin/workflows/ambient.d.ts +0 -5
  188. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +7 -4
  189. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +1 -1
  190. package/dist/builtin/workflows/builtin/adversarial-verification.ts +2 -1
  191. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +5 -2
  192. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +5 -7
  193. package/dist/builtin/workflows/builtin/classify-and-act.ts +3 -2
  194. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +6 -3
  195. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +5 -5
  196. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +3 -2
  197. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +7 -4
  198. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +6 -3
  199. package/dist/builtin/workflows/builtin/generate-and-filter.ts +3 -2
  200. package/dist/builtin/workflows/builtin/goal-artifacts.ts +1 -1
  201. package/dist/builtin/workflows/builtin/goal-models.ts +39 -39
  202. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +94 -0
  203. package/dist/builtin/workflows/builtin/goal-prompts.ts +70 -285
  204. package/dist/builtin/workflows/builtin/goal-reducer.ts +1 -1
  205. package/dist/builtin/workflows/builtin/goal-runner.ts +72 -116
  206. package/dist/builtin/workflows/builtin/goal.ts +12 -11
  207. package/dist/builtin/workflows/builtin/index.d.ts +17 -112
  208. package/dist/builtin/workflows/builtin/index.ts +1 -2
  209. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +20 -12
  210. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +3 -0
  211. package/dist/builtin/workflows/builtin/loop-until-done.ts +3 -2
  212. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +149 -88
  213. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +102 -78
  214. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +164 -56
  215. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +16 -21
  216. package/dist/builtin/workflows/builtin/open-claude-design.ts +2 -1
  217. package/dist/builtin/workflows/builtin/ralph-core.ts +61 -57
  218. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +41 -45
  219. package/dist/builtin/workflows/builtin/ralph-models.ts +37 -47
  220. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +37 -117
  221. package/dist/builtin/workflows/builtin/ralph-runner.ts +83 -108
  222. package/dist/builtin/workflows/builtin/ralph.ts +6 -5
  223. package/dist/builtin/workflows/builtin/shared-prompts.ts +117 -70
  224. package/dist/builtin/workflows/builtin/steering-context.ts +51 -0
  225. package/dist/builtin/workflows/builtin/tournament-prompts.ts +21 -12
  226. package/dist/builtin/workflows/builtin/tournament-runner.ts +3 -0
  227. package/dist/builtin/workflows/builtin/tournament.ts +3 -2
  228. package/dist/builtin/workflows/package.json +4 -4
  229. package/dist/builtin/workflows/skills/create-spec/SKILL.md +1 -1
  230. package/dist/builtin/workflows/skills/impeccable/SKILL.md +33 -129
  231. package/dist/builtin/workflows/skills/impeccable/agents/impeccable_asset_producer.toml +11 -3
  232. package/dist/builtin/workflows/skills/impeccable/agents/impeccable_documenter.toml +26 -0
  233. package/dist/builtin/workflows/skills/impeccable/agents/impeccable_finish_reviewer.toml +35 -0
  234. package/dist/builtin/workflows/skills/impeccable/reference/android.md +1 -1
  235. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +72 -189
  236. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +10 -9
  237. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +2 -2
  238. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +19 -108
  239. package/dist/builtin/workflows/skills/impeccable/reference/clarify.md +59 -253
  240. package/dist/builtin/workflows/skills/impeccable/reference/colorize.md +51 -222
  241. package/dist/builtin/workflows/skills/impeccable/reference/craft-floor.md +45 -0
  242. package/dist/builtin/workflows/skills/impeccable/reference/craft.md +3 -121
  243. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +28 -20
  244. package/dist/builtin/workflows/skills/impeccable/reference/degraded/asset-producer.md +97 -0
  245. package/dist/builtin/workflows/skills/impeccable/reference/degraded/documenter.md +23 -0
  246. package/dist/builtin/workflows/skills/impeccable/reference/degraded/finish-reviewer.md +32 -0
  247. package/dist/builtin/workflows/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  248. package/dist/builtin/workflows/skills/impeccable/reference/delight.md +47 -279
  249. package/dist/builtin/workflows/skills/impeccable/reference/distill.md +2 -2
  250. package/dist/builtin/workflows/skills/impeccable/reference/doctor.md +53 -0
  251. package/dist/builtin/workflows/skills/impeccable/reference/document.md +60 -73
  252. package/dist/builtin/workflows/skills/impeccable/reference/harden.md +1 -12
  253. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +23 -12
  254. package/dist/builtin/workflows/skills/impeccable/reference/init.md +64 -163
  255. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +1 -1
  256. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +52 -153
  257. package/dist/builtin/workflows/skills/impeccable/reference/live.md +47 -36
  258. package/dist/builtin/workflows/skills/impeccable/reference/new-work.md +105 -0
  259. package/dist/builtin/workflows/skills/impeccable/reference/{product.md → operate.md} +6 -5
  260. package/dist/builtin/workflows/skills/impeccable/reference/optimize.md +4 -4
  261. package/dist/builtin/workflows/skills/impeccable/reference/overdrive.md +1 -4
  262. package/dist/builtin/workflows/skills/impeccable/reference/polish.md +68 -212
  263. package/dist/builtin/workflows/skills/impeccable/reference/quieter.md +3 -3
  264. package/dist/builtin/workflows/skills/impeccable/reference/routing.md +18 -0
  265. package/dist/builtin/workflows/skills/impeccable/reference/shape.md +38 -144
  266. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +48 -269
  267. package/dist/builtin/workflows/skills/impeccable/reference/visualize.md +38 -0
  268. package/dist/builtin/workflows/skills/impeccable/scripts/command-metadata.json +1 -1
  269. package/dist/builtin/workflows/skills/impeccable/scripts/concept-seed.mjs +584 -0
  270. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +117 -9
  271. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +486 -59
  272. package/dist/builtin/workflows/skills/impeccable/scripts/critique-storage.mjs +16 -45
  273. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +96 -10
  274. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +143 -26
  275. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +181 -12
  276. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +3187 -182
  277. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +102 -7
  278. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +282 -70
  279. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +183 -15
  280. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +22 -7
  281. package/dist/builtin/workflows/skills/impeccable/scripts/detector/findings.mjs +7 -1
  282. package/dist/builtin/workflows/skills/impeccable/scripts/detector/node/file-system.mjs +16 -2
  283. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +155 -42
  284. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +2988 -155
  285. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/constants.mjs +11 -0
  286. package/dist/builtin/workflows/skills/impeccable/scripts/doctor.mjs +336 -0
  287. package/dist/builtin/workflows/skills/impeccable/scripts/generate-image.mjs +232 -0
  288. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +92 -30
  289. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +414 -120
  290. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +25 -8
  291. package/dist/builtin/workflows/skills/impeccable/scripts/lib/artifact-schema.mjs +93 -0
  292. package/dist/builtin/workflows/skills/impeccable/scripts/lib/composition-catalog.mjs +165 -0
  293. package/dist/builtin/workflows/skills/impeccable/scripts/lib/concept-catalog.mjs +329 -0
  294. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +20 -5
  295. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-paths.mjs +16 -8
  296. package/dist/builtin/workflows/skills/impeccable/scripts/lib/provider.mjs +1 -0
  297. package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness-deep.mjs +455 -0
  298. package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness-notice.mjs +169 -0
  299. package/dist/builtin/workflows/skills/impeccable/scripts/lib/staleness.mjs +457 -0
  300. package/dist/builtin/workflows/skills/impeccable/scripts/lib/surface-briefs.mjs +151 -0
  301. package/dist/builtin/workflows/skills/impeccable/scripts/lib/target-slug.mjs +33 -0
  302. package/dist/builtin/workflows/skills/impeccable/scripts/lib/template-extensions.mjs +146 -0
  303. package/dist/builtin/workflows/skills/impeccable/scripts/live/completion.mjs +10 -1
  304. package/dist/builtin/workflows/skills/impeccable/scripts/live/event-validation.mjs +15 -0
  305. package/dist/builtin/workflows/skills/impeccable/scripts/live/generation-preflight.mjs +149 -0
  306. package/dist/builtin/workflows/skills/impeccable/scripts/live/poll-lanes.mjs +14 -0
  307. package/dist/builtin/workflows/skills/impeccable/scripts/live/session-store.mjs +109 -32
  308. package/dist/builtin/workflows/skills/impeccable/scripts/live/source-lock.mjs +105 -0
  309. package/dist/builtin/workflows/skills/impeccable/scripts/live/source-search.mjs +105 -0
  310. package/dist/builtin/workflows/skills/impeccable/scripts/live/sveltekit-adapter.mjs +8 -6
  311. package/dist/builtin/workflows/skills/impeccable/scripts/live/tanstack-adapter.mjs +280 -0
  312. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +212 -69
  313. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-dom.js +5 -1
  314. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-session.js +7 -1
  315. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +565 -115
  316. package/dist/builtin/workflows/skills/impeccable/scripts/live-commit-manual-edits.mjs +3 -0
  317. package/dist/builtin/workflows/skills/impeccable/scripts/live-inject.mjs +198 -14
  318. package/dist/builtin/workflows/skills/impeccable/scripts/live-insert.mjs +24 -6
  319. package/dist/builtin/workflows/skills/impeccable/scripts/live-manual-edit-evidence.mjs +11 -3
  320. package/dist/builtin/workflows/skills/impeccable/scripts/live-poll.mjs +44 -14
  321. package/dist/builtin/workflows/skills/impeccable/scripts/live-server.mjs +405 -34
  322. package/dist/builtin/workflows/skills/impeccable/scripts/live-status.mjs +9 -5
  323. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +81 -67
  324. package/dist/builtin/workflows/skills/impeccable/scripts/live.mjs +7 -2
  325. package/dist/builtin/workflows/skills/impeccable/scripts/palette.mjs +76 -81
  326. package/dist/builtin/workflows/skills/impeccable/scripts/pin.mjs +4 -4
  327. package/dist/builtin/workflows/skills/impeccable/scripts/serve-question.mjs +890 -0
  328. package/dist/builtin/workflows/skills/impeccable/scripts/surface-brief.mjs +74 -0
  329. package/dist/builtin/workflows/skills/prompt-engineer/SKILL.md +57 -252
  330. package/dist/builtin/workflows/skills/prompt-engineer/references/advanced_patterns.md +70 -226
  331. package/dist/builtin/workflows/skills/prompt-engineer/references/core_prompting.md +64 -103
  332. package/dist/builtin/workflows/skills/prompt-engineer/references/quality_improvement.md +81 -155
  333. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +5 -5
  334. package/dist/builtin/workflows/src/authoring/typebox-defaults.ts +160 -197
  335. package/dist/builtin/workflows/src/authoring/workflow.ts +137 -132
  336. package/dist/builtin/workflows/src/authoring.d.ts +7 -3
  337. package/dist/builtin/workflows/src/durable/backend.ts +512 -385
  338. package/dist/builtin/workflows/src/durable/boundary-lifecycle.ts +172 -0
  339. package/dist/builtin/workflows/src/durable/boundary-topology.ts +475 -0
  340. package/dist/builtin/workflows/src/durable/child-invocation.ts +14 -0
  341. package/dist/builtin/workflows/src/durable/child-primitive.ts +115 -74
  342. package/dist/builtin/workflows/src/durable/completed-catalog-stage-groups.ts +290 -0
  343. package/dist/builtin/workflows/src/durable/completed-catalog.ts +419 -189
  344. package/dist/builtin/workflows/src/durable/completed-inspection.ts +118 -105
  345. package/dist/builtin/workflows/src/durable/completed-subtree.ts +35 -0
  346. package/dist/builtin/workflows/src/durable/dbos-backend.ts +484 -414
  347. package/dist/builtin/workflows/src/durable/dbos-embedded-postgres-root.ts +172 -0
  348. package/dist/builtin/workflows/src/durable/dbos-embedded-postgres.ts +183 -137
  349. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +415 -189
  350. package/dist/builtin/workflows/src/durable/dbos-lifecycle.ts +128 -100
  351. package/dist/builtin/workflows/src/durable/dbos-local-postgres.ts +75 -68
  352. package/dist/builtin/workflows/src/durable/dbos-metadata.ts +138 -101
  353. package/dist/builtin/workflows/src/durable/dbos-prompt-reservations.ts +316 -259
  354. package/dist/builtin/workflows/src/durable/dbos-sdk-handle.ts +138 -117
  355. package/dist/builtin/workflows/src/durable/dbos-status-transition.ts +48 -43
  356. package/dist/builtin/workflows/src/durable/dbos-tombstone.ts +14 -14
  357. package/dist/builtin/workflows/src/durable/durable-hash.ts +9 -11
  358. package/dist/builtin/workflows/src/durable/factory.ts +70 -16
  359. package/dist/builtin/workflows/src/durable/format-version.ts +1 -1
  360. package/dist/builtin/workflows/src/durable/index.ts +69 -68
  361. package/dist/builtin/workflows/src/durable/local-command.ts +50 -37
  362. package/dist/builtin/workflows/src/durable/prompt-reservation-state.ts +156 -149
  363. package/dist/builtin/workflows/src/durable/prompt-reservations.ts +16 -16
  364. package/dist/builtin/workflows/src/durable/resume-catalog.ts +11 -11
  365. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +27 -25
  366. package/dist/builtin/workflows/src/durable/resume-runtime.ts +261 -222
  367. package/dist/builtin/workflows/src/durable/retention-policy.ts +31 -30
  368. package/dist/builtin/workflows/src/durable/run-timing.ts +42 -41
  369. package/dist/builtin/workflows/src/durable/scoped-backend.ts +261 -193
  370. package/dist/builtin/workflows/src/durable/stage-primitive.ts +466 -370
  371. package/dist/builtin/workflows/src/durable/stage-topology-validation.ts +247 -0
  372. package/dist/builtin/workflows/src/durable/stage-topology.ts +53 -0
  373. package/dist/builtin/workflows/src/durable/tool-failure-checkpoint.ts +49 -0
  374. package/dist/builtin/workflows/src/durable/tool-outcome.ts +130 -0
  375. package/dist/builtin/workflows/src/durable/tool-primitive.ts +642 -137
  376. package/dist/builtin/workflows/src/durable/types.ts +243 -132
  377. package/dist/builtin/workflows/src/durable/ui-primitive.ts +243 -164
  378. package/dist/builtin/workflows/src/durable/workflow-child-result.ts +61 -0
  379. package/dist/builtin/workflows/src/durable/workflow-status-transition.ts +16 -15
  380. package/dist/builtin/workflows/src/engine/graph-inference.ts +76 -76
  381. package/dist/builtin/workflows/src/engine/options.ts +35 -35
  382. package/dist/builtin/workflows/src/engine/primitives/chain.ts +27 -22
  383. package/dist/builtin/workflows/src/engine/primitives/exit.ts +1 -1
  384. package/dist/builtin/workflows/src/engine/primitives/parallel.ts +64 -52
  385. package/dist/builtin/workflows/src/engine/primitives/task.ts +110 -95
  386. package/dist/builtin/workflows/src/engine/primitives/ui.ts +46 -50
  387. package/dist/builtin/workflows/src/engine/primitives/workflow.ts +194 -151
  388. package/dist/builtin/workflows/src/engine/replay.ts +6 -6
  389. package/dist/builtin/workflows/src/engine/run-durable-admission.ts +50 -0
  390. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +64 -37
  391. package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +30 -24
  392. package/dist/builtin/workflows/src/engine/run-durable-topology.ts +288 -0
  393. package/dist/builtin/workflows/src/engine/run-returned-status.ts +60 -57
  394. package/dist/builtin/workflows/src/engine/run-terminal-event.ts +39 -0
  395. package/dist/builtin/workflows/src/engine/run-terminal-failure.ts +53 -0
  396. package/dist/builtin/workflows/src/engine/run-tool-admission-boundary.ts +99 -0
  397. package/dist/builtin/workflows/src/engine/run-tool-control-registry.ts +179 -0
  398. package/dist/builtin/workflows/src/engine/run-tool-execution-tracker.ts +164 -0
  399. package/dist/builtin/workflows/src/engine/run-tool-node-lifecycle.ts +152 -0
  400. package/dist/builtin/workflows/src/engine/run.ts +680 -477
  401. package/dist/builtin/workflows/src/engine/runtime.ts +160 -140
  402. package/dist/builtin/workflows/src/engine/workflow-activity.ts +7 -3
  403. package/dist/builtin/workflows/src/engine/workflow-tool-abort.ts +92 -0
  404. package/dist/builtin/workflows/src/extension/atomic-stage-session.ts +123 -120
  405. package/dist/builtin/workflows/src/extension/background-ui-adapter.ts +108 -112
  406. package/dist/builtin/workflows/src/extension/companions.ts +116 -116
  407. package/dist/builtin/workflows/src/extension/completed-stage-intercom-ask.ts +123 -111
  408. package/dist/builtin/workflows/src/extension/config-file-loader.ts +153 -148
  409. package/dist/builtin/workflows/src/extension/config-loader.ts +264 -283
  410. package/dist/builtin/workflows/src/extension/discovery-loaders.ts +81 -84
  411. package/dist/builtin/workflows/src/extension/discovery.ts +267 -272
  412. package/dist/builtin/workflows/src/extension/dispatcher.ts +214 -217
  413. package/dist/builtin/workflows/src/extension/extension-factory.ts +103 -93
  414. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +135 -107
  415. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +414 -409
  416. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +270 -261
  417. package/dist/builtin/workflows/src/extension/index.bundle.mjs +74625 -0
  418. package/dist/builtin/workflows/src/extension/index.ts +37 -37
  419. package/dist/builtin/workflows/src/extension/lifecycle-notification-delivery.ts +64 -64
  420. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +386 -369
  421. package/dist/builtin/workflows/src/extension/mcp.ts +35 -35
  422. package/dist/builtin/workflows/src/extension/postmortem-deps.ts +31 -35
  423. package/dist/builtin/workflows/src/extension/public-types.ts +162 -185
  424. package/dist/builtin/workflows/src/extension/render-call.ts +85 -84
  425. package/dist/builtin/workflows/src/extension/render-component.ts +9 -11
  426. package/dist/builtin/workflows/src/extension/render-result.ts +453 -348
  427. package/dist/builtin/workflows/src/extension/renderers.ts +35 -35
  428. package/dist/builtin/workflows/src/extension/runtime-active-block-claim.ts +37 -37
  429. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +146 -138
  430. package/dist/builtin/workflows/src/extension/runtime.ts +346 -258
  431. package/dist/builtin/workflows/src/extension/status-writer.ts +95 -95
  432. package/dist/builtin/workflows/src/extension/ui-surface.ts +224 -218
  433. package/dist/builtin/workflows/src/extension/wiring.ts +307 -281
  434. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +109 -108
  435. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +257 -224
  436. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +84 -83
  437. package/dist/builtin/workflows/src/extension/workflow-command-utils.ts +126 -132
  438. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +213 -199
  439. package/dist/builtin/workflows/src/extension/workflow-model-catalog.ts +24 -0
  440. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +110 -95
  441. package/dist/builtin/workflows/src/extension/workflow-policy.ts +7 -9
  442. package/dist/builtin/workflows/src/extension/workflow-ports.ts +30 -34
  443. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +41 -17
  444. package/dist/builtin/workflows/src/extension/workflow-reload-coordinator.ts +46 -46
  445. package/dist/builtin/workflows/src/extension/workflow-reload-report.ts +28 -25
  446. package/dist/builtin/workflows/src/extension/workflow-resume-picker-rows.ts +48 -45
  447. package/dist/builtin/workflows/src/extension/workflow-resume-shadow.ts +28 -31
  448. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +539 -453
  449. package/dist/builtin/workflows/src/extension/workflow-schema.ts +149 -91
  450. package/dist/builtin/workflows/src/extension/workflow-stage-results.ts +182 -194
  451. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +154 -116
  452. package/dist/builtin/workflows/src/extension/workflow-targets.ts +159 -125
  453. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +204 -216
  454. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +520 -294
  455. package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +146 -150
  456. package/dist/builtin/workflows/src/extension/workflow-tool-registration.ts +43 -46
  457. package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +354 -206
  458. package/dist/builtin/workflows/src/extension/workflow-tool.ts +123 -117
  459. package/dist/builtin/workflows/src/intercom/intercom-bridge.ts +17 -20
  460. package/dist/builtin/workflows/src/intercom/intercom-routing.ts +62 -62
  461. package/dist/builtin/workflows/src/intercom/result-intercom.ts +123 -123
  462. package/dist/builtin/workflows/src/runs/background/cancellation-registry.ts +72 -59
  463. package/dist/builtin/workflows/src/runs/background/durable-resume-transition.ts +45 -45
  464. package/dist/builtin/workflows/src/runs/background/job-tracker.ts +69 -34
  465. package/dist/builtin/workflows/src/runs/background/quit-tool-node.ts +71 -0
  466. package/dist/builtin/workflows/src/runs/background/quit.ts +359 -138
  467. package/dist/builtin/workflows/src/runs/background/resume-acknowledgements.ts +70 -69
  468. package/dist/builtin/workflows/src/runs/background/run-inspect.ts +85 -74
  469. package/dist/builtin/workflows/src/runs/background/runner.ts +122 -121
  470. package/dist/builtin/workflows/src/runs/background/startup-admission.ts +56 -60
  471. package/dist/builtin/workflows/src/runs/background/status.ts +324 -284
  472. package/dist/builtin/workflows/src/runs/background/workflow-lifecycle-aggregate.ts +24 -26
  473. package/dist/builtin/workflows/src/runs/foreground/executor-abort.ts +311 -309
  474. package/dist/builtin/workflows/src/runs/foreground/executor-child-boundary.ts +229 -199
  475. package/dist/builtin/workflows/src/runs/foreground/executor-child-helpers.ts +56 -51
  476. package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +168 -152
  477. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +255 -217
  478. package/dist/builtin/workflows/src/runs/foreground/executor-exit-manager.ts +178 -173
  479. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +273 -269
  480. package/dist/builtin/workflows/src/runs/foreground/executor-inputs.ts +59 -56
  481. package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +375 -351
  482. package/dist/builtin/workflows/src/runs/foreground/executor-outputs.ts +123 -127
  483. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +360 -295
  484. package/dist/builtin/workflows/src/runs/foreground/executor-queued-user-message.ts +47 -47
  485. package/dist/builtin/workflows/src/runs/foreground/executor-run-finalizers.ts +125 -104
  486. package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +189 -189
  487. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +294 -242
  488. package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +141 -127
  489. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +219 -179
  490. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +406 -354
  491. package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +143 -126
  492. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +50 -47
  493. package/dist/builtin/workflows/src/runs/foreground/executor-task-context.ts +1 -1
  494. package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +148 -153
  495. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +157 -110
  496. package/dist/builtin/workflows/src/runs/foreground/executor.ts +10 -10
  497. package/dist/builtin/workflows/src/runs/foreground/postmortem-stage-chat.ts +176 -100
  498. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +363 -257
  499. package/dist/builtin/workflows/src/runs/foreground/stage-delivery-activity.ts +86 -0
  500. package/dist/builtin/workflows/src/runs/foreground/stage-queued-user-messages.ts +95 -0
  501. package/dist/builtin/workflows/src/runs/foreground/stage-runner-candidate.ts +8 -8
  502. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +233 -221
  503. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +630 -494
  504. package/dist/builtin/workflows/src/runs/foreground/stage-runner-message-admission.ts +182 -181
  505. package/dist/builtin/workflows/src/runs/foreground/stage-runner-messages.ts +80 -80
  506. package/dist/builtin/workflows/src/runs/foreground/stage-runner-options.ts +78 -74
  507. package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +102 -104
  508. package/dist/builtin/workflows/src/runs/foreground/stage-runner-pause.ts +200 -0
  509. package/dist/builtin/workflows/src/runs/foreground/stage-runner-replacement.ts +31 -32
  510. package/dist/builtin/workflows/src/runs/foreground/stage-runner-send-user-message.ts +115 -98
  511. package/dist/builtin/workflows/src/runs/foreground/stage-runner-session-options.ts +29 -42
  512. package/dist/builtin/workflows/src/runs/foreground/stage-runner-session.ts +44 -52
  513. package/dist/builtin/workflows/src/runs/foreground/stage-runner-structured-output.ts +63 -58
  514. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +161 -124
  515. package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +27 -23
  516. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +10 -10
  517. package/dist/builtin/workflows/src/runs/foreground/stage-tool-execution-buffer.ts +33 -31
  518. package/dist/builtin/workflows/src/runs/shared/concurrency.ts +53 -53
  519. package/dist/builtin/workflows/src/runs/shared/graph-inference.ts +1 -1
  520. package/dist/builtin/workflows/src/runs/shared/model-fallback-candidates.ts +257 -385
  521. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +378 -363
  522. package/dist/builtin/workflows/src/runs/shared/prompt-callsite.ts +55 -55
  523. package/dist/builtin/workflows/src/runs/shared/validate-inputs.ts +75 -84
  524. package/dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts +1 -3
  525. package/dist/builtin/workflows/src/runs/shared/worktree-cwd.ts +96 -83
  526. package/dist/builtin/workflows/src/runs/shared/worktree-diff.ts +9 -2
  527. package/dist/builtin/workflows/src/runs/shared/worktree-git-runner.ts +21 -6
  528. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +60 -30
  529. package/dist/builtin/workflows/src/runs/shared/worktree-post-create.ts +12 -5
  530. package/dist/builtin/workflows/src/runs/shared/worktree-setup.ts +51 -30
  531. package/dist/builtin/workflows/src/runs/shared/worktree.ts +4 -4
  532. package/dist/builtin/workflows/src/sdk-surface.ts +38 -19
  533. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +17 -16
  534. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +299 -280
  535. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +76 -10
  536. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +311 -219
  537. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +255 -156
  538. package/dist/builtin/workflows/src/shared/intercom-group.ts +33 -26
  539. package/dist/builtin/workflows/src/shared/persistence-compaction-policy.ts +30 -30
  540. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +435 -379
  541. package/dist/builtin/workflows/src/shared/persistence-restore.ts +187 -181
  542. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +211 -207
  543. package/dist/builtin/workflows/src/shared/prompt-callsite-context.ts +3 -3
  544. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +87 -94
  545. package/dist/builtin/workflows/src/shared/resume-continuation.ts +1 -1
  546. package/dist/builtin/workflows/src/shared/returned-run-status.ts +91 -77
  547. package/dist/builtin/workflows/src/shared/run-visibility.ts +2 -2
  548. package/dist/builtin/workflows/src/shared/schema-introspection.ts +50 -60
  549. package/dist/builtin/workflows/src/shared/serializable.ts +72 -86
  550. package/dist/builtin/workflows/src/shared/session-transcript.ts +44 -39
  551. package/dist/builtin/workflows/src/shared/stage-prompt.ts +183 -202
  552. package/dist/builtin/workflows/src/shared/stage-ui-broker.ts +270 -274
  553. package/dist/builtin/workflows/src/shared/store-factory.ts +9 -7
  554. package/dist/builtin/workflows/src/shared/store-internal.ts +172 -168
  555. package/dist/builtin/workflows/src/shared/store-prompt-methods.ts +196 -200
  556. package/dist/builtin/workflows/src/shared/store-public-types.ts +208 -196
  557. package/dist/builtin/workflows/src/shared/store-run-methods.ts +238 -222
  558. package/dist/builtin/workflows/src/shared/store-stage-methods.ts +284 -283
  559. package/dist/builtin/workflows/src/shared/store-tool-node-methods.ts +53 -0
  560. package/dist/builtin/workflows/src/shared/store-types.ts +269 -235
  561. package/dist/builtin/workflows/src/shared/store.ts +7 -7
  562. package/dist/builtin/workflows/src/shared/timing.ts +36 -34
  563. package/dist/builtin/workflows/src/shared/types.ts +257 -217
  564. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +11 -4
  565. package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +81 -58
  566. package/dist/builtin/workflows/src/shared/workflow-failures-classifier.ts +247 -221
  567. package/dist/builtin/workflows/src/shared/workflow-failures-contract.ts +45 -46
  568. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +330 -351
  569. package/dist/builtin/workflows/src/shared/workflow-failures-signals.ts +212 -157
  570. package/dist/builtin/workflows/src/shared/workflow-failures.ts +10 -10
  571. package/dist/builtin/workflows/src/shared/workflow-run-ownership.ts +49 -0
  572. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +157 -184
  573. package/dist/builtin/workflows/src/tui/chat-surface.ts +332 -355
  574. package/dist/builtin/workflows/src/tui/color-utils.ts +21 -30
  575. package/dist/builtin/workflows/src/tui/connectors.ts +57 -61
  576. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +168 -192
  577. package/dist/builtin/workflows/src/tui/edge.ts +10 -9
  578. package/dist/builtin/workflows/src/tui/graph-canvas.ts +131 -134
  579. package/dist/builtin/workflows/src/tui/graph-theme.ts +139 -148
  580. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +6 -6
  581. package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +146 -179
  582. package/dist/builtin/workflows/src/tui/graph-view-input.ts +295 -329
  583. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +291 -320
  584. package/dist/builtin/workflows/src/tui/graph-view-render.ts +171 -190
  585. package/dist/builtin/workflows/src/tui/graph-view-state.ts +319 -287
  586. package/dist/builtin/workflows/src/tui/graph-view-types.ts +65 -58
  587. package/dist/builtin/workflows/src/tui/header.ts +140 -156
  588. package/dist/builtin/workflows/src/tui/host-input-form.ts +47 -45
  589. package/dist/builtin/workflows/src/tui/inline-form-card.ts +277 -266
  590. package/dist/builtin/workflows/src/tui/inline-form-editor-text.ts +61 -62
  591. package/dist/builtin/workflows/src/tui/inline-form-editor.ts +407 -412
  592. package/dist/builtin/workflows/src/tui/inline-form-overlay.ts +203 -214
  593. package/dist/builtin/workflows/src/tui/inline-form-store.ts +24 -24
  594. package/dist/builtin/workflows/src/tui/inputs-overlay.ts +116 -129
  595. package/dist/builtin/workflows/src/tui/inputs-picker-editing.ts +111 -115
  596. package/dist/builtin/workflows/src/tui/inputs-picker-input.ts +255 -269
  597. package/dist/builtin/workflows/src/tui/inputs-picker-render.ts +248 -236
  598. package/dist/builtin/workflows/src/tui/inputs-picker-types.ts +111 -140
  599. package/dist/builtin/workflows/src/tui/inputs-picker.ts +12 -12
  600. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +49 -57
  601. package/dist/builtin/workflows/src/tui/layout.ts +124 -133
  602. package/dist/builtin/workflows/src/tui/mouse-input.ts +45 -47
  603. package/dist/builtin/workflows/src/tui/node-card.ts +230 -218
  604. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +381 -398
  605. package/dist/builtin/workflows/src/tui/overlay-terminal-modes.ts +20 -19
  606. package/dist/builtin/workflows/src/tui/prompt-card-input.ts +183 -194
  607. package/dist/builtin/workflows/src/tui/prompt-card-render.ts +243 -302
  608. package/dist/builtin/workflows/src/tui/prompt-card-select.ts +76 -98
  609. package/dist/builtin/workflows/src/tui/prompt-card-state.ts +40 -40
  610. package/dist/builtin/workflows/src/tui/prompt-card-text.ts +76 -83
  611. package/dist/builtin/workflows/src/tui/prompt-card.ts +3 -3
  612. package/dist/builtin/workflows/src/tui/renderers.ts +6 -6
  613. package/dist/builtin/workflows/src/tui/run-detail.ts +300 -256
  614. package/dist/builtin/workflows/src/tui/session-list.ts +19 -18
  615. package/dist/builtin/workflows/src/tui/session-overlays.ts +82 -87
  616. package/dist/builtin/workflows/src/tui/session-picker.ts +258 -275
  617. package/dist/builtin/workflows/src/tui/stage-chat-composer-drafts.ts +11 -11
  618. package/dist/builtin/workflows/src/tui/stage-chat-layout.ts +55 -62
  619. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +220 -263
  620. package/dist/builtin/workflows/src/tui/stage-chat-view-custom-ui.ts +91 -102
  621. package/dist/builtin/workflows/src/tui/stage-chat-view-delivery-activity.ts +113 -0
  622. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +154 -208
  623. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +174 -208
  624. package/dist/builtin/workflows/src/tui/stage-chat-view-live-events.ts +53 -23
  625. package/dist/builtin/workflows/src/tui/stage-chat-view-pending-tools.ts +2 -4
  626. package/dist/builtin/workflows/src/tui/stage-chat-view-render-helpers.ts +85 -88
  627. package/dist/builtin/workflows/src/tui/stage-chat-view-render-settings.ts +39 -17
  628. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +435 -409
  629. package/dist/builtin/workflows/src/tui/stage-chat-view-status.ts +24 -25
  630. package/dist/builtin/workflows/src/tui/stage-chat-view-transcript.ts +181 -197
  631. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +125 -128
  632. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +210 -205
  633. package/dist/builtin/workflows/src/tui/status-helpers.ts +65 -69
  634. package/dist/builtin/workflows/src/tui/status-list.ts +316 -294
  635. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +213 -214
  636. package/dist/builtin/workflows/src/tui/submit-pane.ts +114 -130
  637. package/dist/builtin/workflows/src/tui/switcher.ts +120 -128
  638. package/dist/builtin/workflows/src/tui/text-helpers.ts +136 -145
  639. package/dist/builtin/workflows/src/tui/toast.ts +68 -67
  640. package/dist/builtin/workflows/src/tui/widget.ts +265 -285
  641. package/dist/builtin/workflows/src/tui/workflow-attach-pane-handle.ts +12 -17
  642. package/dist/builtin/workflows/src/tui/workflow-attach-pane-types.ts +81 -91
  643. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +468 -479
  644. package/dist/builtin/workflows/src/tui/workflow-list.ts +130 -145
  645. package/dist/builtin/workflows/src/tui/workflow-notice-card.ts +127 -144
  646. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +217 -217
  647. package/dist/builtin/workflows/src/tui/workflow-status.ts +7 -0
  648. package/dist/builtin/workflows/src/workflows/identity.ts +12 -12
  649. package/dist/builtin/workflows/src/workflows/registry.ts +66 -68
  650. package/dist/bun/cli.js.map +1 -1
  651. package/dist/bun/internal-intercom-broker.d.ts.map +1 -1
  652. package/dist/bun/internal-intercom-broker.js +1 -1
  653. package/dist/bun/internal-intercom-broker.js.map +1 -1
  654. package/dist/bun/register-bedrock.js +1 -1
  655. package/dist/bun/register-bedrock.js.map +1 -1
  656. package/dist/cli/args.d.ts +8 -1
  657. package/dist/cli/args.d.ts.map +1 -1
  658. package/dist/cli/args.js +28 -22
  659. package/dist/cli/args.js.map +1 -1
  660. package/dist/cli/config-selector.d.ts +4 -2
  661. package/dist/cli/config-selector.d.ts.map +1 -1
  662. package/dist/cli/config-selector.js +2 -2
  663. package/dist/cli/config-selector.js.map +1 -1
  664. package/dist/cli/credential-print.d.ts +170 -0
  665. package/dist/cli/credential-print.d.ts.map +1 -0
  666. package/dist/cli/credential-print.js +513 -0
  667. package/dist/cli/credential-print.js.map +1 -0
  668. package/dist/cli/list-models.d.ts +2 -2
  669. package/dist/cli/list-models.d.ts.map +1 -1
  670. package/dist/cli/list-models.js +6 -7
  671. package/dist/cli/list-models.js.map +1 -1
  672. package/dist/cli/project-trust.d.ts +1 -1
  673. package/dist/cli/project-trust.d.ts.map +1 -1
  674. package/dist/cli/project-trust.js.map +1 -1
  675. package/dist/cli/session-picker.d.ts.map +1 -1
  676. package/dist/cli/session-picker.js +2 -1
  677. package/dist/cli/session-picker.js.map +1 -1
  678. package/dist/cli/startup-ui.d.ts +3 -0
  679. package/dist/cli/startup-ui.d.ts.map +1 -1
  680. package/dist/cli/startup-ui.js +58 -2
  681. package/dist/cli/startup-ui.js.map +1 -1
  682. package/dist/cli.js +2 -0
  683. package/dist/cli.js.map +1 -1
  684. package/dist/config-command-parser.d.ts +9 -0
  685. package/dist/config-command-parser.d.ts.map +1 -0
  686. package/dist/config-command-parser.js +22 -0
  687. package/dist/config-command-parser.js.map +1 -0
  688. package/dist/config-self-update.d.ts +6 -2
  689. package/dist/config-self-update.d.ts.map +1 -1
  690. package/dist/config-self-update.js +23 -13
  691. package/dist/config-self-update.js.map +1 -1
  692. package/dist/config.d.ts +5 -3
  693. package/dist/config.d.ts.map +1 -1
  694. package/dist/config.js +36 -14
  695. package/dist/config.js.map +1 -1
  696. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  697. package/dist/core/agent-session-accessors.js +125 -24
  698. package/dist/core/agent-session-accessors.js.map +1 -1
  699. package/dist/core/agent-session-auto-compaction.d.ts +4 -3
  700. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  701. package/dist/core/agent-session-auto-compaction.js +59 -64
  702. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  703. package/dist/core/agent-session-bash.d.ts +10 -7
  704. package/dist/core/agent-session-bash.d.ts.map +1 -1
  705. package/dist/core/agent-session-bash.js +34 -13
  706. package/dist/core/agent-session-bash.js.map +1 -1
  707. package/dist/core/agent-session-compaction.d.ts +11 -1
  708. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  709. package/dist/core/agent-session-compaction.js +138 -27
  710. package/dist/core/agent-session-compaction.js.map +1 -1
  711. package/dist/core/agent-session-custom-message-commit.d.ts +14 -0
  712. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -0
  713. package/dist/core/agent-session-custom-message-commit.js +145 -0
  714. package/dist/core/agent-session-custom-message-commit.js.map +1 -0
  715. package/dist/core/agent-session-delivery-forwarding.d.ts +9 -0
  716. package/dist/core/agent-session-delivery-forwarding.d.ts.map +1 -0
  717. package/dist/core/agent-session-delivery-forwarding.js +36 -0
  718. package/dist/core/agent-session-delivery-forwarding.js.map +1 -0
  719. package/dist/core/agent-session-events.d.ts +1 -1
  720. package/dist/core/agent-session-events.d.ts.map +1 -1
  721. package/dist/core/agent-session-events.js +58 -19
  722. package/dist/core/agent-session-events.js.map +1 -1
  723. package/dist/core/agent-session-export.d.ts +1 -1
  724. package/dist/core/agent-session-export.d.ts.map +1 -1
  725. package/dist/core/agent-session-export.js +32 -25
  726. package/dist/core/agent-session-export.js.map +1 -1
  727. package/dist/core/agent-session-extension-bindings.d.ts +2 -6
  728. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  729. package/dist/core/agent-session-extension-bindings.js +38 -18
  730. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  731. package/dist/core/agent-session-message-queue.d.ts +7 -4
  732. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  733. package/dist/core/agent-session-message-queue.js +101 -98
  734. package/dist/core/agent-session-message-queue.js.map +1 -1
  735. package/dist/core/agent-session-methods.d.ts +52 -45
  736. package/dist/core/agent-session-methods.d.ts.map +1 -1
  737. package/dist/core/agent-session-methods.js.map +1 -1
  738. package/dist/core/agent-session-models.d.ts +3 -39
  739. package/dist/core/agent-session-models.d.ts.map +1 -1
  740. package/dist/core/agent-session-models.js +22 -160
  741. package/dist/core/agent-session-models.js.map +1 -1
  742. package/dist/core/agent-session-persistent-custom-messages.d.ts +47 -0
  743. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -0
  744. package/dist/core/agent-session-persistent-custom-messages.js +269 -0
  745. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -0
  746. package/dist/core/agent-session-post-tool-compaction.d.ts +1 -1
  747. package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
  748. package/dist/core/agent-session-post-tool-compaction.js +34 -11
  749. package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
  750. package/dist/core/agent-session-prompt.d.ts +2 -0
  751. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  752. package/dist/core/agent-session-prompt.js +52 -25
  753. package/dist/core/agent-session-prompt.js.map +1 -1
  754. package/dist/core/agent-session-queue-pause.d.ts +10 -0
  755. package/dist/core/agent-session-queue-pause.d.ts.map +1 -0
  756. package/dist/core/agent-session-queue-pause.js +87 -0
  757. package/dist/core/agent-session-queue-pause.js.map +1 -0
  758. package/dist/core/agent-session-retry.d.ts +0 -11
  759. package/dist/core/agent-session-retry.d.ts.map +1 -1
  760. package/dist/core/agent-session-retry.js +44 -63
  761. package/dist/core/agent-session-retry.js.map +1 -1
  762. package/dist/core/agent-session-runtime-auth.d.ts +6 -0
  763. package/dist/core/agent-session-runtime-auth.d.ts.map +1 -0
  764. package/dist/core/agent-session-runtime-auth.js +18 -0
  765. package/dist/core/agent-session-runtime-auth.js.map +1 -0
  766. package/dist/core/agent-session-runtime.d.ts +36 -1
  767. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  768. package/dist/core/agent-session-runtime.js +62 -6
  769. package/dist/core/agent-session-runtime.js.map +1 -1
  770. package/dist/core/agent-session-services.d.ts +4 -9
  771. package/dist/core/agent-session-services.d.ts.map +1 -1
  772. package/dist/core/agent-session-services.js +25 -25
  773. package/dist/core/agent-session-services.js.map +1 -1
  774. package/dist/core/agent-session-state.d.ts +1 -1
  775. package/dist/core/agent-session-state.d.ts.map +1 -1
  776. package/dist/core/agent-session-state.js +3 -0
  777. package/dist/core/agent-session-state.js.map +1 -1
  778. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  779. package/dist/core/agent-session-tool-hooks.js +8 -7
  780. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  781. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  782. package/dist/core/agent-session-tool-registry.js +6 -20
  783. package/dist/core/agent-session-tool-registry.js.map +1 -1
  784. package/dist/core/agent-session-transfer.d.ts +4 -0
  785. package/dist/core/agent-session-transfer.d.ts.map +1 -0
  786. package/dist/core/agent-session-transfer.js +67 -0
  787. package/dist/core/agent-session-transfer.js.map +1 -0
  788. package/dist/core/agent-session-tree.d.ts +1 -1
  789. package/dist/core/agent-session-tree.d.ts.map +1 -1
  790. package/dist/core/agent-session-tree.js +13 -3
  791. package/dist/core/agent-session-tree.js.map +1 -1
  792. package/dist/core/agent-session-types.d.ts +28 -14
  793. package/dist/core/agent-session-types.d.ts.map +1 -1
  794. package/dist/core/agent-session-types.js +4 -4
  795. package/dist/core/agent-session-types.js.map +1 -1
  796. package/dist/core/agent-session.d.ts +27 -13
  797. package/dist/core/agent-session.d.ts.map +1 -1
  798. package/dist/core/agent-session.js +17 -12
  799. package/dist/core/agent-session.js.map +1 -1
  800. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  801. package/dist/core/anthropic-thinking-guard.js +4 -2
  802. package/dist/core/anthropic-thinking-guard.js.map +1 -1
  803. package/dist/core/async/format.d.ts +1 -1
  804. package/dist/core/async/format.d.ts.map +1 -1
  805. package/dist/core/async/format.js +3 -1
  806. package/dist/core/async/format.js.map +1 -1
  807. package/dist/core/async/job-manager.d.ts.map +1 -1
  808. package/dist/core/async/job-manager.js +20 -5
  809. package/dist/core/async/job-manager.js.map +1 -1
  810. package/dist/core/async/session-manager.d.ts.map +1 -1
  811. package/dist/core/async/session-manager.js.map +1 -1
  812. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  813. package/dist/core/atomic-guide-command.js +34 -35
  814. package/dist/core/atomic-guide-command.js.map +1 -1
  815. package/dist/core/auth-guidance.d.ts.map +1 -1
  816. package/dist/core/auth-guidance.js.map +1 -1
  817. package/dist/core/auth-storage-backends.d.ts +5 -0
  818. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  819. package/dist/core/auth-storage-backends.js +77 -7
  820. package/dist/core/auth-storage-backends.js.map +1 -1
  821. package/dist/core/auth-storage.d.ts +13 -136
  822. package/dist/core/auth-storage.d.ts.map +1 -1
  823. package/dist/core/auth-storage.js +68 -358
  824. package/dist/core/auth-storage.js.map +1 -1
  825. package/dist/core/bash-executor.d.ts +5 -3
  826. package/dist/core/bash-executor.d.ts.map +1 -1
  827. package/dist/core/bash-executor.js +5 -4
  828. package/dist/core/bash-executor.js.map +1 -1
  829. package/dist/core/builtin-packages.d.ts.map +1 -1
  830. package/dist/core/builtin-packages.js +1 -10
  831. package/dist/core/builtin-packages.js.map +1 -1
  832. package/dist/core/cache-stats.d.ts +25 -0
  833. package/dist/core/cache-stats.d.ts.map +1 -0
  834. package/dist/core/cache-stats.js +70 -0
  835. package/dist/core/cache-stats.js.map +1 -0
  836. package/dist/core/callback-activity.d.ts.map +1 -1
  837. package/dist/core/callback-activity.js.map +1 -1
  838. package/dist/core/codex-errors.d.ts +3 -0
  839. package/dist/core/codex-errors.d.ts.map +1 -0
  840. package/dist/core/codex-errors.js +12 -0
  841. package/dist/core/codex-errors.js.map +1 -0
  842. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  843. package/dist/core/codex-fast-mode.js.map +1 -1
  844. package/dist/core/compaction/branch-summarization.d.ts +15 -5
  845. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  846. package/dist/core/compaction/branch-summarization.js +23 -12
  847. package/dist/core/compaction/branch-summarization.js.map +1 -1
  848. package/dist/core/compaction/compaction-boundary.d.ts +18 -1
  849. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -1
  850. package/dist/core/compaction/compaction-boundary.js +12 -8
  851. package/dist/core/compaction/compaction-boundary.js.map +1 -1
  852. package/dist/core/compaction/compaction-runner.d.ts +63 -6
  853. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  854. package/dist/core/compaction/compaction-runner.js +244 -17
  855. package/dist/core/compaction/compaction-runner.js.map +1 -1
  856. package/dist/core/compaction/compaction-types.d.ts +53 -2
  857. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  858. package/dist/core/compaction/compaction-types.js.map +1 -1
  859. package/dist/core/compaction/compaction.d.ts.map +1 -1
  860. package/dist/core/compaction/compaction.js +5 -1
  861. package/dist/core/compaction/compaction.js.map +1 -1
  862. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -1
  863. package/dist/core/compaction/deleted-ranges.js.map +1 -1
  864. package/dist/core/compaction/fallback-planner.d.ts +64 -0
  865. package/dist/core/compaction/fallback-planner.d.ts.map +1 -0
  866. package/dist/core/compaction/fallback-planner.js +75 -0
  867. package/dist/core/compaction/fallback-planner.js.map +1 -0
  868. package/dist/core/compaction/index.d.ts +7 -4
  869. package/dist/core/compaction/index.d.ts.map +1 -1
  870. package/dist/core/compaction/index.js +7 -4
  871. package/dist/core/compaction/index.js.map +1 -1
  872. package/dist/core/compaction/planner-outcome.d.ts +86 -0
  873. package/dist/core/compaction/planner-outcome.d.ts.map +1 -0
  874. package/dist/core/compaction/planner-outcome.js +122 -0
  875. package/dist/core/compaction/planner-outcome.js.map +1 -0
  876. package/dist/core/compaction/range-planner-diagnostics.d.ts +58 -6
  877. package/dist/core/compaction/range-planner-diagnostics.d.ts.map +1 -1
  878. package/dist/core/compaction/range-planner-diagnostics.js +71 -31
  879. package/dist/core/compaction/range-planner-diagnostics.js.map +1 -1
  880. package/dist/core/compaction/range-planner.d.ts +39 -8
  881. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  882. package/dist/core/compaction/range-planner.js +140 -51
  883. package/dist/core/compaction/range-planner.js.map +1 -1
  884. package/dist/core/compaction/region-trimming.d.ts +34 -0
  885. package/dist/core/compaction/region-trimming.d.ts.map +1 -0
  886. package/dist/core/compaction/region-trimming.js +73 -0
  887. package/dist/core/compaction/region-trimming.js.map +1 -0
  888. package/dist/core/compaction/transcript-serialization.d.ts +8 -1
  889. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -1
  890. package/dist/core/compaction/transcript-serialization.js +78 -27
  891. package/dist/core/compaction/transcript-serialization.js.map +1 -1
  892. package/dist/core/context-tool-pairing.d.ts +19 -0
  893. package/dist/core/context-tool-pairing.d.ts.map +1 -0
  894. package/dist/core/context-tool-pairing.js +50 -0
  895. package/dist/core/context-tool-pairing.js.map +1 -0
  896. package/dist/core/copilot-env-routing.d.ts +36 -0
  897. package/dist/core/copilot-env-routing.d.ts.map +1 -0
  898. package/dist/core/copilot-env-routing.js +94 -0
  899. package/dist/core/copilot-env-routing.js.map +1 -0
  900. package/dist/core/diagnostics.d.ts +8 -0
  901. package/dist/core/diagnostics.d.ts.map +1 -1
  902. package/dist/core/diagnostics.js.map +1 -1
  903. package/dist/core/exec.d.ts.map +1 -1
  904. package/dist/core/exec.js +5 -0
  905. package/dist/core/exec.js.map +1 -1
  906. package/dist/core/experimental.d.ts.map +1 -1
  907. package/dist/core/experimental.js +2 -1
  908. package/dist/core/experimental.js.map +1 -1
  909. package/dist/core/extensions/agent-events.d.ts +10 -0
  910. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  911. package/dist/core/extensions/agent-events.js.map +1 -1
  912. package/dist/core/extensions/api-types.d.ts +9 -3
  913. package/dist/core/extensions/api-types.d.ts.map +1 -1
  914. package/dist/core/extensions/api-types.js.map +1 -1
  915. package/dist/core/extensions/context-types.d.ts +10 -1
  916. package/dist/core/extensions/context-types.d.ts.map +1 -1
  917. package/dist/core/extensions/context-types.js.map +1 -1
  918. package/dist/core/extensions/event-types.d.ts +2 -2
  919. package/dist/core/extensions/event-types.d.ts.map +1 -1
  920. package/dist/core/extensions/event-types.js.map +1 -1
  921. package/dist/core/extensions/index.d.ts +7 -5
  922. package/dist/core/extensions/index.d.ts.map +1 -1
  923. package/dist/core/extensions/index.js +2 -2
  924. package/dist/core/extensions/index.js.map +1 -1
  925. package/dist/core/extensions/loader-api.d.ts +1 -1
  926. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  927. package/dist/core/extensions/loader-api.js +63 -29
  928. package/dist/core/extensions/loader-api.js.map +1 -1
  929. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  930. package/dist/core/extensions/loader-core.js +3 -2
  931. package/dist/core/extensions/loader-core.js.map +1 -1
  932. package/dist/core/extensions/loader-discovery.d.ts.map +1 -1
  933. package/dist/core/extensions/loader-discovery.js.map +1 -1
  934. package/dist/core/extensions/loader-resources.d.ts.map +1 -1
  935. package/dist/core/extensions/loader-resources.js.map +1 -1
  936. package/dist/core/extensions/loader-runtime.d.ts +2 -4
  937. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  938. package/dist/core/extensions/loader-runtime.js +214 -13
  939. package/dist/core/extensions/loader-runtime.js.map +1 -1
  940. package/dist/core/extensions/loader-virtual-modules.d.ts +17 -0
  941. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  942. package/dist/core/extensions/loader-virtual-modules.js +288 -19
  943. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  944. package/dist/core/extensions/loader.d.ts.map +1 -1
  945. package/dist/core/extensions/loader.js.map +1 -1
  946. package/dist/core/extensions/message-types.d.ts +12 -4
  947. package/dist/core/extensions/message-types.d.ts.map +1 -1
  948. package/dist/core/extensions/message-types.js.map +1 -1
  949. package/dist/core/extensions/provider-types.d.ts +16 -5
  950. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  951. package/dist/core/extensions/provider-types.js.map +1 -1
  952. package/dist/core/extensions/reactive-widget.d.ts.map +1 -1
  953. package/dist/core/extensions/reactive-widget.js.map +1 -1
  954. package/dist/core/extensions/runner-context.d.ts +29 -1
  955. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  956. package/dist/core/extensions/runner-context.js +56 -0
  957. package/dist/core/extensions/runner-context.js.map +1 -1
  958. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  959. package/dist/core/extensions/runner-events.js.map +1 -1
  960. package/dist/core/extensions/runner-registries.d.ts +2 -1
  961. package/dist/core/extensions/runner-registries.d.ts.map +1 -1
  962. package/dist/core/extensions/runner-registries.js +8 -0
  963. package/dist/core/extensions/runner-registries.js.map +1 -1
  964. package/dist/core/extensions/runner.d.ts +7 -2
  965. package/dist/core/extensions/runner.d.ts.map +1 -1
  966. package/dist/core/extensions/runner.js +65 -23
  967. package/dist/core/extensions/runner.js.map +1 -1
  968. package/dist/core/extensions/runtime-types.d.ts +35 -2
  969. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  970. package/dist/core/extensions/runtime-types.js.map +1 -1
  971. package/dist/core/extensions/session-events.d.ts.map +1 -1
  972. package/dist/core/extensions/session-events.js.map +1 -1
  973. package/dist/core/extensions/tool-types.d.ts +7 -3
  974. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  975. package/dist/core/extensions/tool-types.js.map +1 -1
  976. package/dist/core/extensions/types.d.ts +15 -2
  977. package/dist/core/extensions/types.d.ts.map +1 -1
  978. package/dist/core/extensions/types.js +1 -1
  979. package/dist/core/extensions/types.js.map +1 -1
  980. package/dist/core/extensions/ui-types.d.ts +34 -6
  981. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  982. package/dist/core/extensions/ui-types.js.map +1 -1
  983. package/dist/core/fallback-models.d.ts +37 -0
  984. package/dist/core/fallback-models.d.ts.map +1 -0
  985. package/dist/core/fallback-models.js +57 -0
  986. package/dist/core/fallback-models.js.map +1 -0
  987. package/dist/core/flattened-tool-arguments.d.ts +4 -7
  988. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  989. package/dist/core/flattened-tool-arguments.js +4 -7
  990. package/dist/core/flattened-tool-arguments.js.map +1 -1
  991. package/dist/core/footer-data-provider.d.ts +10 -0
  992. package/dist/core/footer-data-provider.d.ts.map +1 -1
  993. package/dist/core/footer-data-provider.js +5 -3
  994. package/dist/core/footer-data-provider.js.map +1 -1
  995. package/dist/core/http-dispatcher.d.ts +1 -0
  996. package/dist/core/http-dispatcher.d.ts.map +1 -1
  997. package/dist/core/http-dispatcher.js +42 -8
  998. package/dist/core/http-dispatcher.js.map +1 -1
  999. package/dist/core/keybindings.d.ts +7 -2
  1000. package/dist/core/keybindings.d.ts.map +1 -1
  1001. package/dist/core/keybindings.js +3 -1
  1002. package/dist/core/keybindings.js.map +1 -1
  1003. package/dist/core/messages.d.ts +24 -4
  1004. package/dist/core/messages.d.ts.map +1 -1
  1005. package/dist/core/messages.js +64 -13
  1006. package/dist/core/messages.js.map +1 -1
  1007. package/dist/core/model-capabilities.d.ts +18 -0
  1008. package/dist/core/model-capabilities.d.ts.map +1 -0
  1009. package/dist/core/model-capabilities.js +22 -0
  1010. package/dist/core/model-capabilities.js.map +1 -0
  1011. package/dist/core/model-config.d.ts +542 -0
  1012. package/dist/core/model-config.d.ts.map +1 -0
  1013. package/dist/core/{model-registry-schemas.js → model-config.js} +92 -32
  1014. package/dist/core/model-config.js.map +1 -0
  1015. package/dist/core/model-refresh-timeout.d.ts +9 -0
  1016. package/dist/core/model-refresh-timeout.d.ts.map +1 -0
  1017. package/dist/core/model-refresh-timeout.js +9 -0
  1018. package/dist/core/model-refresh-timeout.js.map +1 -0
  1019. package/dist/core/model-registry.d.ts +26 -94
  1020. package/dist/core/model-registry.d.ts.map +1 -1
  1021. package/dist/core/model-registry.js +65 -382
  1022. package/dist/core/model-registry.js.map +1 -1
  1023. package/dist/core/model-resolver-cli.d.ts +2 -2
  1024. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  1025. package/dist/core/model-resolver-cli.js +2 -2
  1026. package/dist/core/model-resolver-cli.js.map +1 -1
  1027. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  1028. package/dist/core/model-resolver-defaults.js +5 -1
  1029. package/dist/core/model-resolver-defaults.js.map +1 -1
  1030. package/dist/core/model-resolver-initial.d.ts +4 -4
  1031. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  1032. package/dist/core/model-resolver-initial.js +26 -17
  1033. package/dist/core/model-resolver-initial.js.map +1 -1
  1034. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  1035. package/dist/core/model-resolver-patterns.js +0 -2
  1036. package/dist/core/model-resolver-patterns.js.map +1 -1
  1037. package/dist/core/model-resolver-scope.d.ts +5 -3
  1038. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  1039. package/dist/core/model-resolver-scope.js +32 -8
  1040. package/dist/core/model-resolver-scope.js.map +1 -1
  1041. package/dist/core/model-resolver-types.d.ts +2 -0
  1042. package/dist/core/model-resolver-types.d.ts.map +1 -1
  1043. package/dist/core/model-resolver-types.js.map +1 -1
  1044. package/dist/core/model-resolver.d.ts +3 -3
  1045. package/dist/core/model-resolver.d.ts.map +1 -1
  1046. package/dist/core/model-resolver.js +1 -1
  1047. package/dist/core/model-resolver.js.map +1 -1
  1048. package/dist/core/model-runtime-auth.d.ts +7 -0
  1049. package/dist/core/model-runtime-auth.d.ts.map +1 -0
  1050. package/dist/core/model-runtime-auth.js +17 -0
  1051. package/dist/core/model-runtime-auth.js.map +1 -0
  1052. package/dist/core/model-runtime-providers.d.ts +6 -0
  1053. package/dist/core/model-runtime-providers.d.ts.map +1 -0
  1054. package/dist/core/model-runtime-providers.js +22 -0
  1055. package/dist/core/model-runtime-providers.js.map +1 -0
  1056. package/dist/core/model-runtime-restoration.d.ts +6 -0
  1057. package/dist/core/model-runtime-restoration.d.ts.map +1 -0
  1058. package/dist/core/model-runtime-restoration.js +21 -0
  1059. package/dist/core/model-runtime-restoration.js.map +1 -0
  1060. package/dist/core/model-runtime-snapshot.d.ts +19 -0
  1061. package/dist/core/model-runtime-snapshot.d.ts.map +1 -0
  1062. package/dist/core/model-runtime-snapshot.js +111 -0
  1063. package/dist/core/model-runtime-snapshot.js.map +1 -0
  1064. package/dist/core/model-runtime-streaming.d.ts +17 -0
  1065. package/dist/core/model-runtime-streaming.d.ts.map +1 -0
  1066. package/dist/core/model-runtime-streaming.js +66 -0
  1067. package/dist/core/model-runtime-streaming.js.map +1 -0
  1068. package/dist/core/model-runtime-types.d.ts +21 -0
  1069. package/dist/core/model-runtime-types.d.ts.map +1 -0
  1070. package/dist/core/model-runtime-types.js +2 -0
  1071. package/dist/core/model-runtime-types.js.map +1 -0
  1072. package/dist/core/model-runtime.d.ts +79 -0
  1073. package/dist/core/model-runtime.d.ts.map +1 -0
  1074. package/dist/core/model-runtime.js +417 -0
  1075. package/dist/core/model-runtime.js.map +1 -0
  1076. package/dist/core/models-store.d.ts +2 -10
  1077. package/dist/core/models-store.d.ts.map +1 -1
  1078. package/dist/core/models-store.js +2 -29
  1079. package/dist/core/models-store.js.map +1 -1
  1080. package/dist/core/oauth-login.d.ts +24 -0
  1081. package/dist/core/oauth-login.d.ts.map +1 -0
  1082. package/dist/core/oauth-login.js +92 -0
  1083. package/dist/core/oauth-login.js.map +1 -0
  1084. package/dist/core/oauth-provider-metadata.d.ts +5 -0
  1085. package/dist/core/oauth-provider-metadata.d.ts.map +1 -0
  1086. package/dist/core/oauth-provider-metadata.js +33 -0
  1087. package/dist/core/oauth-provider-metadata.js.map +1 -0
  1088. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  1089. package/dist/core/output-guard.d.ts +44 -0
  1090. package/dist/core/output-guard.d.ts.map +1 -1
  1091. package/dist/core/output-guard.js +63 -0
  1092. package/dist/core/output-guard.js.map +1 -1
  1093. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  1094. package/dist/core/package-manager-auto-resources.js +18 -5
  1095. package/dist/core/package-manager-auto-resources.js.map +1 -1
  1096. package/dist/core/package-manager-git.d.ts.map +1 -1
  1097. package/dist/core/package-manager-git.js +27 -11
  1098. package/dist/core/package-manager-git.js.map +1 -1
  1099. package/dist/core/package-manager-npm.d.ts.map +1 -1
  1100. package/dist/core/package-manager-npm.js +4 -2
  1101. package/dist/core/package-manager-npm.js.map +1 -1
  1102. package/dist/core/package-manager-operations.d.ts.map +1 -1
  1103. package/dist/core/package-manager-operations.js +2 -2
  1104. package/dist/core/package-manager-operations.js.map +1 -1
  1105. package/dist/core/package-manager-paths.d.ts.map +1 -1
  1106. package/dist/core/package-manager-paths.js.map +1 -1
  1107. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  1108. package/dist/core/package-manager-resolver.js +71 -26
  1109. package/dist/core/package-manager-resolver.js.map +1 -1
  1110. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  1111. package/dist/core/package-manager-resource-collector.js +13 -7
  1112. package/dist/core/package-manager-resource-collector.js.map +1 -1
  1113. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  1114. package/dist/core/package-manager-resource-files.js +4 -3
  1115. package/dist/core/package-manager-resource-files.js.map +1 -1
  1116. package/dist/core/package-manager-resource-patterns.d.ts +1 -0
  1117. package/dist/core/package-manager-resource-patterns.d.ts.map +1 -1
  1118. package/dist/core/package-manager-resource-patterns.js +15 -0
  1119. package/dist/core/package-manager-resource-patterns.js.map +1 -1
  1120. package/dist/core/package-manager-settings.d.ts.map +1 -1
  1121. package/dist/core/package-manager-settings.js +3 -2
  1122. package/dist/core/package-manager-settings.js.map +1 -1
  1123. package/dist/core/package-manager-source.d.ts.map +1 -1
  1124. package/dist/core/package-manager-source.js +16 -9
  1125. package/dist/core/package-manager-source.js.map +1 -1
  1126. package/dist/core/package-manager-types.d.ts +3 -0
  1127. package/dist/core/package-manager-types.d.ts.map +1 -1
  1128. package/dist/core/package-manager-types.js.map +1 -1
  1129. package/dist/core/package-manager.d.ts +1 -1
  1130. package/dist/core/package-manager.d.ts.map +1 -1
  1131. package/dist/core/package-manager.js +2 -3
  1132. package/dist/core/package-manager.js.map +1 -1
  1133. package/dist/core/prompt-templates-async.d.ts.map +1 -1
  1134. package/dist/core/prompt-templates-async.js +15 -7
  1135. package/dist/core/prompt-templates-async.js.map +1 -1
  1136. package/dist/core/prompt-templates.d.ts +1 -0
  1137. package/dist/core/prompt-templates.d.ts.map +1 -1
  1138. package/dist/core/prompt-templates.js +4 -4
  1139. package/dist/core/prompt-templates.js.map +1 -1
  1140. package/dist/core/provider-composer-internal.d.ts +53 -0
  1141. package/dist/core/provider-composer-internal.d.ts.map +1 -0
  1142. package/dist/core/provider-composer-internal.js +273 -0
  1143. package/dist/core/provider-composer-internal.js.map +1 -0
  1144. package/dist/core/provider-composer.d.ts +16 -0
  1145. package/dist/core/provider-composer.d.ts.map +1 -0
  1146. package/dist/core/provider-composer.js +108 -0
  1147. package/dist/core/provider-composer.js.map +1 -0
  1148. package/dist/core/provider-context-usage.d.ts.map +1 -1
  1149. package/dist/core/provider-context-usage.js +2 -1
  1150. package/dist/core/provider-context-usage.js.map +1 -1
  1151. package/dist/core/provider-display-names.d.ts.map +1 -1
  1152. package/dist/core/provider-display-names.js +4 -0
  1153. package/dist/core/provider-display-names.js.map +1 -1
  1154. package/dist/core/remote-catalog-provider.d.ts +1 -1
  1155. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  1156. package/dist/core/remote-catalog-provider.js +82 -105
  1157. package/dist/core/remote-catalog-provider.js.map +1 -1
  1158. package/dist/core/resolve-config-value.d.ts +8 -7
  1159. package/dist/core/resolve-config-value.d.ts.map +1 -1
  1160. package/dist/core/resolve-config-value.js +26 -23
  1161. package/dist/core/resolve-config-value.js.map +1 -1
  1162. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  1163. package/dist/core/resource-loader-assets.js +58 -8
  1164. package/dist/core/resource-loader-assets.js.map +1 -1
  1165. package/dist/core/resource-loader-context-files.d.ts +6 -0
  1166. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  1167. package/dist/core/resource-loader-context-files.js +85 -4
  1168. package/dist/core/resource-loader-context-files.js.map +1 -1
  1169. package/dist/core/resource-loader-core.d.ts +13 -3
  1170. package/dist/core/resource-loader-core.d.ts.map +1 -1
  1171. package/dist/core/resource-loader-core.js +39 -19
  1172. package/dist/core/resource-loader-core.js.map +1 -1
  1173. package/dist/core/resource-loader-discovery.d.ts.map +1 -1
  1174. package/dist/core/resource-loader-discovery.js.map +1 -1
  1175. package/dist/core/resource-loader-extensions.d.ts +1 -0
  1176. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  1177. package/dist/core/resource-loader-extensions.js +140 -12
  1178. package/dist/core/resource-loader-extensions.js.map +1 -1
  1179. package/dist/core/resource-loader-helpers.d.ts.map +1 -1
  1180. package/dist/core/resource-loader-helpers.js +1 -0
  1181. package/dist/core/resource-loader-helpers.js.map +1 -1
  1182. package/dist/core/resource-loader-internals.d.ts +14 -7
  1183. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  1184. package/dist/core/resource-loader-internals.js.map +1 -1
  1185. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  1186. package/dist/core/resource-loader-package-resources.js +15 -2
  1187. package/dist/core/resource-loader-package-resources.js.map +1 -1
  1188. package/dist/core/resource-loader-paths.js.map +1 -1
  1189. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  1190. package/dist/core/resource-loader-reload.js +29 -13
  1191. package/dist/core/resource-loader-reload.js.map +1 -1
  1192. package/dist/core/resource-loader-source-info.d.ts +1 -1
  1193. package/dist/core/resource-loader-source-info.d.ts.map +1 -1
  1194. package/dist/core/resource-loader-source-info.js +7 -4
  1195. package/dist/core/resource-loader-source-info.js.map +1 -1
  1196. package/dist/core/resource-loader-types.d.ts +12 -6
  1197. package/dist/core/resource-loader-types.d.ts.map +1 -1
  1198. package/dist/core/resource-loader-types.js.map +1 -1
  1199. package/dist/core/resource-loader.d.ts +1 -1
  1200. package/dist/core/resource-loader.d.ts.map +1 -1
  1201. package/dist/core/resource-loader.js +1 -1
  1202. package/dist/core/resource-loader.js.map +1 -1
  1203. package/dist/core/runtime-credentials.d.ts +16 -0
  1204. package/dist/core/runtime-credentials.d.ts.map +1 -0
  1205. package/dist/core/runtime-credentials.js +42 -0
  1206. package/dist/core/runtime-credentials.js.map +1 -0
  1207. package/dist/core/sdk-exports.d.ts +2 -2
  1208. package/dist/core/sdk-exports.d.ts.map +1 -1
  1209. package/dist/core/sdk-exports.js +2 -2
  1210. package/dist/core/sdk-exports.js.map +1 -1
  1211. package/dist/core/sdk-types.d.ts +16 -18
  1212. package/dist/core/sdk-types.d.ts.map +1 -1
  1213. package/dist/core/sdk-types.js.map +1 -1
  1214. package/dist/core/sdk.d.ts +2 -1
  1215. package/dist/core/sdk.d.ts.map +1 -1
  1216. package/dist/core/sdk.js +61 -132
  1217. package/dist/core/sdk.js.map +1 -1
  1218. package/dist/core/session-manager-archive.d.ts.map +1 -1
  1219. package/dist/core/session-manager-archive.js +5 -2
  1220. package/dist/core/session-manager-archive.js.map +1 -1
  1221. package/dist/core/session-manager-classification.js +3 -3
  1222. package/dist/core/session-manager-classification.js.map +1 -1
  1223. package/dist/core/session-manager-core.d.ts +5 -4
  1224. package/dist/core/session-manager-core.d.ts.map +1 -1
  1225. package/dist/core/session-manager-core.js +29 -33
  1226. package/dist/core/session-manager-core.js.map +1 -1
  1227. package/dist/core/session-manager-entries.d.ts +5 -8
  1228. package/dist/core/session-manager-entries.d.ts.map +1 -1
  1229. package/dist/core/session-manager-entries.js +4 -9
  1230. package/dist/core/session-manager-entries.js.map +1 -1
  1231. package/dist/core/session-manager-history.d.ts +8 -3
  1232. package/dist/core/session-manager-history.d.ts.map +1 -1
  1233. package/dist/core/session-manager-history.js +90 -27
  1234. package/dist/core/session-manager-history.js.map +1 -1
  1235. package/dist/core/session-manager-list.d.ts.map +1 -1
  1236. package/dist/core/session-manager-list.js +1 -1
  1237. package/dist/core/session-manager-list.js.map +1 -1
  1238. package/dist/core/session-manager-migrations.js.map +1 -1
  1239. package/dist/core/session-manager-storage.d.ts +6 -0
  1240. package/dist/core/session-manager-storage.d.ts.map +1 -1
  1241. package/dist/core/session-manager-storage.js +38 -5
  1242. package/dist/core/session-manager-storage.js.map +1 -1
  1243. package/dist/core/session-manager-types.d.ts +10 -8
  1244. package/dist/core/session-manager-types.d.ts.map +1 -1
  1245. package/dist/core/session-manager-types.js.map +1 -1
  1246. package/dist/core/session-manager-validation.d.ts.map +1 -1
  1247. package/dist/core/session-manager-validation.js +3 -16
  1248. package/dist/core/session-manager-validation.js.map +1 -1
  1249. package/dist/core/session-manager.d.ts +4 -4
  1250. package/dist/core/session-manager.d.ts.map +1 -1
  1251. package/dist/core/session-manager.js +2 -2
  1252. package/dist/core/session-manager.js.map +1 -1
  1253. package/dist/core/settings-manager-basic-accessors.d.ts +7 -4
  1254. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  1255. package/dist/core/settings-manager-basic-accessors.js +35 -50
  1256. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  1257. package/dist/core/settings-manager-core.d.ts +2 -4
  1258. package/dist/core/settings-manager-core.d.ts.map +1 -1
  1259. package/dist/core/settings-manager-core.js +5 -51
  1260. package/dist/core/settings-manager-core.js.map +1 -1
  1261. package/dist/core/settings-manager-resource-accessors.d.ts +3 -0
  1262. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  1263. package/dist/core/settings-manager-resource-accessors.js +16 -0
  1264. package/dist/core/settings-manager-resource-accessors.js.map +1 -1
  1265. package/dist/core/settings-manager-ui-accessors.js +2 -1
  1266. package/dist/core/settings-manager-ui-accessors.js.map +1 -1
  1267. package/dist/core/settings-manager.d.ts +1 -1
  1268. package/dist/core/settings-manager.d.ts.map +1 -1
  1269. package/dist/core/settings-manager.js.map +1 -1
  1270. package/dist/core/settings-storage.d.ts +3 -1
  1271. package/dist/core/settings-storage.d.ts.map +1 -1
  1272. package/dist/core/settings-storage.js +23 -1
  1273. package/dist/core/settings-storage.js.map +1 -1
  1274. package/dist/core/settings-types.d.ts +10 -6
  1275. package/dist/core/settings-types.d.ts.map +1 -1
  1276. package/dist/core/settings-types.js.map +1 -1
  1277. package/dist/core/skills-async.d.ts.map +1 -1
  1278. package/dist/core/skills-async.js +18 -8
  1279. package/dist/core/skills-async.js.map +1 -1
  1280. package/dist/core/slash-commands.d.ts +1 -0
  1281. package/dist/core/slash-commands.d.ts.map +1 -1
  1282. package/dist/core/slash-commands.js +127 -24
  1283. package/dist/core/slash-commands.js.map +1 -1
  1284. package/dist/core/source-info.d.ts +3 -1
  1285. package/dist/core/source-info.d.ts.map +1 -1
  1286. package/dist/core/source-info.js +2 -0
  1287. package/dist/core/source-info.js.map +1 -1
  1288. package/dist/core/summarization-retry.d.ts +11 -0
  1289. package/dist/core/summarization-retry.d.ts.map +1 -0
  1290. package/dist/core/summarization-retry.js +21 -0
  1291. package/dist/core/summarization-retry.js.map +1 -0
  1292. package/dist/core/system-prompt.d.ts.map +1 -1
  1293. package/dist/core/system-prompt.js +15 -18
  1294. package/dist/core/system-prompt.js.map +1 -1
  1295. package/dist/core/thinking-blocks.d.ts.map +1 -1
  1296. package/dist/core/thinking-blocks.js +2 -2
  1297. package/dist/core/thinking-blocks.js.map +1 -1
  1298. package/dist/core/tools/artifact-protocol.d.ts.map +1 -1
  1299. package/dist/core/tools/artifact-protocol.js.map +1 -1
  1300. package/dist/core/tools/artifacts.d.ts.map +1 -1
  1301. package/dist/core/tools/artifacts.js.map +1 -1
  1302. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  1303. package/dist/core/tools/ask-user-question/ask-user-question.js +1 -1
  1304. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  1305. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts +1 -1
  1306. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  1307. package/dist/core/tools/ask-user-question/state/build-questionnaire.js +1 -1
  1308. package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -1
  1309. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  1310. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +2 -4
  1311. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  1312. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  1313. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  1314. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
  1315. package/dist/core/tools/ask-user-question/view/body-residual-spacer.js.map +1 -1
  1316. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  1317. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -1
  1318. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts +1 -1
  1319. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
  1320. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js.map +1 -1
  1321. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts +1 -1
  1322. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
  1323. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js +1 -3
  1324. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -1
  1325. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  1326. package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -1
  1327. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
  1328. package/dist/core/tools/ask-user-question/view/components/tab-bar.js.map +1 -1
  1329. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts +1 -1
  1330. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
  1331. package/dist/core/tools/ask-user-question/view/dialog-builder.js +1 -1
  1332. package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -1
  1333. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  1334. package/dist/core/tools/ask-user-question/view/props-adapter.js +2 -4
  1335. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  1336. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts +1 -1
  1337. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
  1338. package/dist/core/tools/ask-user-question/view/tab-content-strategy.js +2 -9
  1339. package/dist/core/tools/ask-user-question/view/tab-content-strategy.js.map +1 -1
  1340. package/dist/core/tools/bash-async-execution.d.ts.map +1 -1
  1341. package/dist/core/tools/bash-async-execution.js +11 -4
  1342. package/dist/core/tools/bash-async-execution.js.map +1 -1
  1343. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  1344. package/dist/core/tools/bash-async-jobs.js +14 -2
  1345. package/dist/core/tools/bash-async-jobs.js.map +1 -1
  1346. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  1347. package/dist/core/tools/bash-async-output.js +6 -2
  1348. package/dist/core/tools/bash-async-output.js.map +1 -1
  1349. package/dist/core/tools/bash-interceptor.d.ts.map +1 -1
  1350. package/dist/core/tools/bash-interceptor.js +20 -4
  1351. package/dist/core/tools/bash-interceptor.js.map +1 -1
  1352. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -1
  1353. package/dist/core/tools/bash-leading-cd.js +9 -3
  1354. package/dist/core/tools/bash-leading-cd.js.map +1 -1
  1355. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  1356. package/dist/core/tools/bash-pty-native.js.map +1 -1
  1357. package/dist/core/tools/bash-session-environment.d.ts +13 -0
  1358. package/dist/core/tools/bash-session-environment.d.ts.map +1 -0
  1359. package/dist/core/tools/bash-session-environment.js +47 -0
  1360. package/dist/core/tools/bash-session-environment.js.map +1 -0
  1361. package/dist/core/tools/bash.d.ts +4 -1
  1362. package/dist/core/tools/bash.d.ts.map +1 -1
  1363. package/dist/core/tools/bash.js +134 -30
  1364. package/dist/core/tools/bash.js.map +1 -1
  1365. package/dist/core/tools/block-resolver.d.ts.map +1 -1
  1366. package/dist/core/tools/block-resolver.js.map +1 -1
  1367. package/dist/core/tools/conflict-registry.d.ts.map +1 -1
  1368. package/dist/core/tools/conflict-registry.js.map +1 -1
  1369. package/dist/core/tools/directory-tree.d.ts.map +1 -1
  1370. package/dist/core/tools/directory-tree.js +20 -4
  1371. package/dist/core/tools/directory-tree.js.map +1 -1
  1372. package/dist/core/tools/edit.d.ts.map +1 -1
  1373. package/dist/core/tools/edit.js +37 -13
  1374. package/dist/core/tools/edit.js.map +1 -1
  1375. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  1376. package/dist/core/tools/fetch-url.js +127 -41
  1377. package/dist/core/tools/fetch-url.js.map +1 -1
  1378. package/dist/core/tools/find.d.ts.map +1 -1
  1379. package/dist/core/tools/find.js +176 -46
  1380. package/dist/core/tools/find.js.map +1 -1
  1381. package/dist/core/tools/grep.d.ts.map +1 -1
  1382. package/dist/core/tools/grep.js +43 -10
  1383. package/dist/core/tools/grep.js.map +1 -1
  1384. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -1
  1385. package/dist/core/tools/hashline-engine/apply.js +6 -6
  1386. package/dist/core/tools/hashline-engine/apply.js.map +1 -1
  1387. package/dist/core/tools/hashline-engine/block.js +1 -1
  1388. package/dist/core/tools/hashline-engine/block.js.map +1 -1
  1389. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
  1390. package/dist/core/tools/hashline-engine/format.js +1 -1
  1391. package/dist/core/tools/hashline-engine/format.js.map +1 -1
  1392. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -1
  1393. package/dist/core/tools/hashline-engine/input.js +7 -7
  1394. package/dist/core/tools/hashline-engine/input.js.map +1 -1
  1395. package/dist/core/tools/hashline-engine/parser.js +1 -1
  1396. package/dist/core/tools/hashline-engine/parser.js.map +1 -1
  1397. package/dist/core/tools/hashline-engine/patcher.js +5 -5
  1398. package/dist/core/tools/hashline-engine/patcher.js.map +1 -1
  1399. package/dist/core/tools/hashline-engine/prefixes.js +4 -4
  1400. package/dist/core/tools/hashline-engine/prefixes.js.map +1 -1
  1401. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -1
  1402. package/dist/core/tools/hashline-engine/recovery.js +4 -2
  1403. package/dist/core/tools/hashline-engine/recovery.js.map +1 -1
  1404. package/dist/core/tools/hashline-engine/snapshots.js +5 -5
  1405. package/dist/core/tools/hashline-engine/snapshots.js.map +1 -1
  1406. package/dist/core/tools/hashline.d.ts.map +1 -1
  1407. package/dist/core/tools/hashline.js +24 -13
  1408. package/dist/core/tools/hashline.js.map +1 -1
  1409. package/dist/core/tools/index.d.ts +6 -6
  1410. package/dist/core/tools/index.d.ts.map +1 -1
  1411. package/dist/core/tools/index.js +6 -6
  1412. package/dist/core/tools/index.js.map +1 -1
  1413. package/dist/core/tools/ls.d.ts.map +1 -1
  1414. package/dist/core/tools/ls.js +2 -1
  1415. package/dist/core/tools/ls.js.map +1 -1
  1416. package/dist/core/tools/notebook.d.ts.map +1 -1
  1417. package/dist/core/tools/notebook.js +13 -3
  1418. package/dist/core/tools/notebook.js.map +1 -1
  1419. package/dist/core/tools/read-document-extract.d.ts.map +1 -1
  1420. package/dist/core/tools/read-document-extract.js +30 -10
  1421. package/dist/core/tools/read-document-extract.js.map +1 -1
  1422. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  1423. package/dist/core/tools/read-selectors.js +59 -15
  1424. package/dist/core/tools/read-selectors.js.map +1 -1
  1425. package/dist/core/tools/read-url.d.ts.map +1 -1
  1426. package/dist/core/tools/read-url.js +41 -7
  1427. package/dist/core/tools/read-url.js.map +1 -1
  1428. package/dist/core/tools/read.d.ts.map +1 -1
  1429. package/dist/core/tools/read.js +307 -72
  1430. package/dist/core/tools/read.js.map +1 -1
  1431. package/dist/core/tools/resource-selectors.d.ts +4 -0
  1432. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  1433. package/dist/core/tools/resource-selectors.js +324 -84
  1434. package/dist/core/tools/resource-selectors.js.map +1 -1
  1435. package/dist/core/tools/search-details.d.ts.map +1 -1
  1436. package/dist/core/tools/search-details.js +13 -1
  1437. package/dist/core/tools/search-details.js.map +1 -1
  1438. package/dist/core/tools/search-line-ranges.d.ts.map +1 -1
  1439. package/dist/core/tools/search-line-ranges.js +5 -2
  1440. package/dist/core/tools/search-line-ranges.js.map +1 -1
  1441. package/dist/core/tools/search-native.d.ts.map +1 -1
  1442. package/dist/core/tools/search-native.js.map +1 -1
  1443. package/dist/core/tools/search.d.ts.map +1 -1
  1444. package/dist/core/tools/search.js +244 -90
  1445. package/dist/core/tools/search.js.map +1 -1
  1446. package/dist/core/tools/structured-output.d.ts +1 -1
  1447. package/dist/core/tools/structured-output.d.ts.map +1 -1
  1448. package/dist/core/tools/structured-output.js.map +1 -1
  1449. package/dist/core/tools/todos-execute.d.ts.map +1 -1
  1450. package/dist/core/tools/todos-execute.js +4 -4
  1451. package/dist/core/tools/todos-execute.js.map +1 -1
  1452. package/dist/core/tools/todos-locks.d.ts.map +1 -1
  1453. package/dist/core/tools/todos-locks.js.map +1 -1
  1454. package/dist/core/tools/todos-mutations.js +1 -1
  1455. package/dist/core/tools/todos-mutations.js.map +1 -1
  1456. package/dist/core/tools/todos-paths.js +2 -2
  1457. package/dist/core/tools/todos-paths.js.map +1 -1
  1458. package/dist/core/tools/todos-render.d.ts.map +1 -1
  1459. package/dist/core/tools/todos-render.js +2 -2
  1460. package/dist/core/tools/todos-render.js.map +1 -1
  1461. package/dist/core/tools/todos-storage.d.ts.map +1 -1
  1462. package/dist/core/tools/todos-storage.js +1 -2
  1463. package/dist/core/tools/todos-storage.js.map +1 -1
  1464. package/dist/core/tools/todos-types.d.ts.map +1 -1
  1465. package/dist/core/tools/todos-types.js +1 -11
  1466. package/dist/core/tools/todos-types.js.map +1 -1
  1467. package/dist/core/tools/tool-definition-wrapper.d.ts +3 -0
  1468. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  1469. package/dist/core/tools/tool-definition-wrapper.js +9 -5
  1470. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  1471. package/dist/core/tools/url-ip-guards.d.ts.map +1 -1
  1472. package/dist/core/tools/url-ip-guards.js +27 -17
  1473. package/dist/core/tools/url-ip-guards.js.map +1 -1
  1474. package/dist/core/tools/write.d.ts.map +1 -1
  1475. package/dist/core/tools/write.js +88 -29
  1476. package/dist/core/tools/write.js.map +1 -1
  1477. package/dist/core/usage-totals.d.ts +18 -0
  1478. package/dist/core/usage-totals.d.ts.map +1 -0
  1479. package/dist/core/usage-totals.js +43 -0
  1480. package/dist/core/usage-totals.js.map +1 -0
  1481. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  1482. package/dist/core/workflow-stage-admission.js +8 -2
  1483. package/dist/core/workflow-stage-admission.js.map +1 -1
  1484. package/dist/extensions/index.d.ts +3 -0
  1485. package/dist/extensions/index.d.ts.map +1 -0
  1486. package/dist/extensions/index.js +5 -0
  1487. package/dist/extensions/index.js.map +1 -0
  1488. package/dist/extensions/llama/client.d.ts +61 -0
  1489. package/dist/extensions/llama/client.d.ts.map +1 -0
  1490. package/dist/extensions/llama/client.js +300 -0
  1491. package/dist/extensions/llama/client.js.map +1 -0
  1492. package/dist/extensions/llama/huggingface-ui.d.ts +33 -0
  1493. package/dist/extensions/llama/huggingface-ui.d.ts.map +1 -0
  1494. package/dist/extensions/llama/huggingface-ui.js +166 -0
  1495. package/dist/extensions/llama/huggingface-ui.js.map +1 -0
  1496. package/dist/extensions/llama/huggingface.d.ts +23 -0
  1497. package/dist/extensions/llama/huggingface.d.ts.map +1 -0
  1498. package/dist/extensions/llama/huggingface.js +139 -0
  1499. package/dist/extensions/llama/huggingface.js.map +1 -0
  1500. package/dist/extensions/llama/index.d.ts +3 -0
  1501. package/dist/extensions/llama/index.d.ts.map +1 -0
  1502. package/dist/extensions/llama/index.js +208 -0
  1503. package/dist/extensions/llama/index.js.map +1 -0
  1504. package/dist/extensions/llama/provider.d.ts +10 -0
  1505. package/dist/extensions/llama/provider.d.ts.map +1 -0
  1506. package/dist/extensions/llama/provider.js +107 -0
  1507. package/dist/extensions/llama/provider.js.map +1 -0
  1508. package/dist/extensions/llama/ui.d.ts +42 -0
  1509. package/dist/extensions/llama/ui.d.ts.map +1 -0
  1510. package/dist/extensions/llama/ui.js +237 -0
  1511. package/dist/extensions/llama/ui.js.map +1 -0
  1512. package/dist/index-extensions.d.ts +2 -2
  1513. package/dist/index-extensions.d.ts.map +1 -1
  1514. package/dist/index-extensions.js +1 -1
  1515. package/dist/index-extensions.js.map +1 -1
  1516. package/dist/index.d.ts +22 -23
  1517. package/dist/index.d.ts.map +1 -1
  1518. package/dist/index.js +26 -27
  1519. package/dist/index.js.map +1 -1
  1520. package/dist/main-app-mode.d.ts +1 -0
  1521. package/dist/main-app-mode.d.ts.map +1 -1
  1522. package/dist/main-app-mode.js +3 -0
  1523. package/dist/main-app-mode.js.map +1 -1
  1524. package/dist/main-deferred-startup.d.ts +1 -1
  1525. package/dist/main-deferred-startup.d.ts.map +1 -1
  1526. package/dist/main-deferred-startup.js +16 -15
  1527. package/dist/main-deferred-startup.js.map +1 -1
  1528. package/dist/main-early-input.js.map +1 -1
  1529. package/dist/main-first-time-setup.d.ts +6 -0
  1530. package/dist/main-first-time-setup.d.ts.map +1 -0
  1531. package/dist/main-first-time-setup.js +10 -0
  1532. package/dist/main-first-time-setup.js.map +1 -0
  1533. package/dist/main-runtime-api-key.d.ts +6 -0
  1534. package/dist/main-runtime-api-key.d.ts.map +1 -0
  1535. package/dist/main-runtime-api-key.js +6 -0
  1536. package/dist/main-runtime-api-key.js.map +1 -0
  1537. package/dist/main-session-options.d.ts +2 -2
  1538. package/dist/main-session-options.d.ts.map +1 -1
  1539. package/dist/main-session-options.js +3 -7
  1540. package/dist/main-session-options.js.map +1 -1
  1541. package/dist/main-session.d.ts.map +1 -1
  1542. package/dist/main-session.js +5 -4
  1543. package/dist/main-session.js.map +1 -1
  1544. package/dist/main-types.d.ts +2 -2
  1545. package/dist/main-types.d.ts.map +1 -1
  1546. package/dist/main-types.js.map +1 -1
  1547. package/dist/main.d.ts +1 -1
  1548. package/dist/main.d.ts.map +1 -1
  1549. package/dist/main.js +173 -69
  1550. package/dist/main.js.map +1 -1
  1551. package/dist/migrations-config-values.d.ts.map +1 -1
  1552. package/dist/migrations-config-values.js +3 -1
  1553. package/dist/migrations-config-values.js.map +1 -1
  1554. package/dist/migrations.d.ts.map +1 -1
  1555. package/dist/migrations.js.map +1 -1
  1556. package/dist/modes/index.d.ts +1 -1
  1557. package/dist/modes/index.d.ts.map +1 -1
  1558. package/dist/modes/index.js.map +1 -1
  1559. package/dist/modes/interactive/chat-input-actions.d.ts +1 -1
  1560. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  1561. package/dist/modes/interactive/chat-input-actions.js +30 -103
  1562. package/dist/modes/interactive/chat-input-actions.js.map +1 -1
  1563. package/dist/modes/interactive/components/atomic-banner.d.ts +9 -8
  1564. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
  1565. package/dist/modes/interactive/components/atomic-banner.js +68 -22
  1566. package/dist/modes/interactive/components/atomic-banner.js.map +1 -1
  1567. package/dist/modes/interactive/components/atomic-working-status.d.ts +52 -0
  1568. package/dist/modes/interactive/components/atomic-working-status.d.ts.map +1 -0
  1569. package/dist/modes/interactive/components/atomic-working-status.js +215 -0
  1570. package/dist/modes/interactive/components/atomic-working-status.js.map +1 -0
  1571. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -1
  1572. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  1573. package/dist/modes/interactive/components/chat-message-renderer.js +7 -6
  1574. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  1575. package/dist/modes/interactive/components/chat-session-host-actions.d.ts +3 -2
  1576. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  1577. package/dist/modes/interactive/components/chat-session-host-actions.js +93 -17
  1578. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  1579. package/dist/modes/interactive/components/chat-session-host-editor.d.ts +4 -4
  1580. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  1581. package/dist/modes/interactive/components/chat-session-host-editor.js +12 -7
  1582. package/dist/modes/interactive/components/chat-session-host-editor.js.map +1 -1
  1583. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  1584. package/dist/modes/interactive/components/chat-session-host-events.js +84 -15
  1585. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  1586. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts +1 -1
  1587. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  1588. package/dist/modes/interactive/components/chat-session-host-rendering.js +49 -14
  1589. package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
  1590. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts +36 -2
  1591. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  1592. package/dist/modes/interactive/components/chat-session-host-runtime.js +129 -25
  1593. package/dist/modes/interactive/components/chat-session-host-runtime.js.map +1 -1
  1594. package/dist/modes/interactive/components/chat-session-host-state.d.ts +11 -2
  1595. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  1596. package/dist/modes/interactive/components/chat-session-host-state.js +5 -0
  1597. package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
  1598. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -1
  1599. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js.map +1 -1
  1600. package/dist/modes/interactive/components/chat-session-host-types.d.ts +19 -2
  1601. package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
  1602. package/dist/modes/interactive/components/chat-session-host-types.js.map +1 -1
  1603. package/dist/modes/interactive/components/chat-session-host-utils.d.ts +1 -1
  1604. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  1605. package/dist/modes/interactive/components/chat-session-host-utils.js +3 -8
  1606. package/dist/modes/interactive/components/chat-session-host-utils.js.map +1 -1
  1607. package/dist/modes/interactive/components/chat-session-host.d.ts +17 -4
  1608. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  1609. package/dist/modes/interactive/components/chat-session-host.js +26 -3
  1610. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  1611. package/dist/modes/interactive/components/chat-transcript.d.ts +1 -1
  1612. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  1613. package/dist/modes/interactive/components/chat-transcript.js +5 -12
  1614. package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
  1615. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -1
  1616. package/dist/modes/interactive/components/compaction-boundary-message.js +20 -8
  1617. package/dist/modes/interactive/components/compaction-boundary-message.js.map +1 -1
  1618. package/dist/modes/interactive/components/config-selector-list.d.ts +6 -6
  1619. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  1620. package/dist/modes/interactive/components/config-selector-list.js +66 -125
  1621. package/dist/modes/interactive/components/config-selector-list.js.map +1 -1
  1622. package/dist/modes/interactive/components/config-selector-project-scope.d.ts +4 -0
  1623. package/dist/modes/interactive/components/config-selector-project-scope.d.ts.map +1 -0
  1624. package/dist/modes/interactive/components/config-selector-project-scope.js +59 -0
  1625. package/dist/modes/interactive/components/config-selector-project-scope.js.map +1 -0
  1626. package/dist/modes/interactive/components/config-selector.d.ts +8 -5
  1627. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  1628. package/dist/modes/interactive/components/config-selector.js +46 -13
  1629. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  1630. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  1631. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  1632. package/dist/modes/interactive/components/custom-editor.d.ts +11 -0
  1633. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  1634. package/dist/modes/interactive/components/custom-editor.js +21 -5
  1635. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  1636. package/dist/modes/interactive/components/custom-entry.d.ts +16 -0
  1637. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
  1638. package/dist/modes/interactive/components/custom-entry.js +45 -0
  1639. package/dist/modes/interactive/components/custom-entry.js.map +1 -0
  1640. package/dist/modes/interactive/components/custom-message.d.ts +3 -1
  1641. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  1642. package/dist/modes/interactive/components/custom-message.js +10 -5
  1643. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  1644. package/dist/modes/interactive/components/diff.js +2 -2
  1645. package/dist/modes/interactive/components/diff.js.map +1 -1
  1646. package/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  1647. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  1648. package/dist/modes/interactive/components/extension-editor.js +14 -43
  1649. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  1650. package/dist/modes/interactive/components/first-time-setup.d.ts +24 -0
  1651. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
  1652. package/dist/modes/interactive/components/first-time-setup.js +80 -0
  1653. package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
  1654. package/dist/modes/interactive/components/footer.d.ts +7 -1
  1655. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  1656. package/dist/modes/interactive/components/footer.js +68 -39
  1657. package/dist/modes/interactive/components/footer.js.map +1 -1
  1658. package/dist/modes/interactive/components/host-input-form-mount.d.ts.map +1 -1
  1659. package/dist/modes/interactive/components/host-input-form-mount.js +4 -2
  1660. package/dist/modes/interactive/components/host-input-form-mount.js.map +1 -1
  1661. package/dist/modes/interactive/components/host-input-form.d.ts +2 -0
  1662. package/dist/modes/interactive/components/host-input-form.d.ts.map +1 -1
  1663. package/dist/modes/interactive/components/host-input-form.js +61 -24
  1664. package/dist/modes/interactive/components/host-input-form.js.map +1 -1
  1665. package/dist/modes/interactive/components/host-session-picker.d.ts.map +1 -1
  1666. package/dist/modes/interactive/components/host-session-picker.js +8 -3
  1667. package/dist/modes/interactive/components/host-session-picker.js.map +1 -1
  1668. package/dist/modes/interactive/components/idle-status.d.ts +8 -0
  1669. package/dist/modes/interactive/components/idle-status.d.ts.map +1 -0
  1670. package/dist/modes/interactive/components/idle-status.js +13 -0
  1671. package/dist/modes/interactive/components/idle-status.js.map +1 -0
  1672. package/dist/modes/interactive/components/index.d.ts +8 -5
  1673. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  1674. package/dist/modes/interactive/components/index.js +5 -2
  1675. package/dist/modes/interactive/components/index.js.map +1 -1
  1676. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  1677. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  1678. package/dist/modes/interactive/components/login-dialog.d.ts +5 -5
  1679. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  1680. package/dist/modes/interactive/components/login-dialog.js +15 -9
  1681. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  1682. package/dist/modes/interactive/components/model-selector.d.ts +4 -3
  1683. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  1684. package/dist/modes/interactive/components/model-selector.js +70 -72
  1685. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  1686. package/dist/modes/interactive/components/oauth-selector.d.ts +4 -3
  1687. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  1688. package/dist/modes/interactive/components/oauth-selector.js +19 -18
  1689. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  1690. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  1691. package/dist/modes/interactive/components/scoped-models-selector.js +22 -13
  1692. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  1693. package/dist/modes/interactive/components/session-selector-list.d.ts +1 -1
  1694. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  1695. package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
  1696. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  1697. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  1698. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  1699. package/dist/modes/interactive/components/settings-selector-handlers.js +6 -0
  1700. package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
  1701. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  1702. package/dist/modes/interactive/components/settings-selector-items.js +14 -0
  1703. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  1704. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  1705. package/dist/modes/interactive/components/settings-selector-submenus.js.map +1 -1
  1706. package/dist/modes/interactive/components/settings-selector-types.d.ts +4 -0
  1707. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  1708. package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
  1709. package/dist/modes/interactive/components/startup-identity.d.ts +22 -0
  1710. package/dist/modes/interactive/components/startup-identity.d.ts.map +1 -0
  1711. package/dist/modes/interactive/components/startup-identity.js +58 -0
  1712. package/dist/modes/interactive/components/startup-identity.js.map +1 -0
  1713. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  1714. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  1715. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  1716. package/dist/modes/interactive/components/tree-selector-content.js +0 -6
  1717. package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
  1718. package/dist/modes/interactive/components/tree-selector-model.d.ts.map +1 -1
  1719. package/dist/modes/interactive/components/tree-selector-model.js +1 -10
  1720. package/dist/modes/interactive/components/tree-selector-model.js.map +1 -1
  1721. package/dist/modes/interactive/components/tree-selector-viewport.d.ts.map +1 -1
  1722. package/dist/modes/interactive/components/tree-selector-viewport.js.map +1 -1
  1723. package/dist/modes/interactive/components/working-status.d.ts +5 -11
  1724. package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
  1725. package/dist/modes/interactive/components/working-status.js +14 -15
  1726. package/dist/modes/interactive/components/working-status.js.map +1 -1
  1727. package/dist/modes/interactive/external-editor.d.ts +23 -0
  1728. package/dist/modes/interactive/external-editor.d.ts.map +1 -0
  1729. package/dist/modes/interactive/external-editor.js +120 -0
  1730. package/dist/modes/interactive/external-editor.js.map +1 -0
  1731. package/dist/modes/interactive/interactive-agent-events.js +73 -28
  1732. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  1733. package/dist/modes/interactive/interactive-auth-login.js +36 -28
  1734. package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
  1735. package/dist/modes/interactive/interactive-auth-routing.d.ts +4 -1
  1736. package/dist/modes/interactive/interactive-auth-routing.d.ts.map +1 -1
  1737. package/dist/modes/interactive/interactive-auth-routing.js +92 -65
  1738. package/dist/modes/interactive/interactive-auth-routing.js.map +1 -1
  1739. package/dist/modes/interactive/interactive-autocomplete.js +24 -14
  1740. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  1741. package/dist/modes/interactive/interactive-bash-compact.d.ts +2 -1
  1742. package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
  1743. package/dist/modes/interactive/interactive-bash-compact.js +26 -8
  1744. package/dist/modes/interactive/interactive-bash-compact.js.map +1 -1
  1745. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  1746. package/dist/modes/interactive/interactive-deferred-startup.js +61 -27
  1747. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  1748. package/dist/modes/interactive/interactive-editor-actions.js +9 -13
  1749. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  1750. package/dist/modes/interactive/interactive-extension-context.js +4 -4
  1751. package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
  1752. package/dist/modes/interactive/interactive-extension-custom-ui.js +15 -8
  1753. package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
  1754. package/dist/modes/interactive/interactive-extension-dialogs.js +51 -25
  1755. package/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
  1756. package/dist/modes/interactive/interactive-extension-runtime.js +15 -6
  1757. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  1758. package/dist/modes/interactive/interactive-extension-widgets.js +1 -1
  1759. package/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  1760. package/dist/modes/interactive/interactive-global-clear.d.ts +55 -3
  1761. package/dist/modes/interactive/interactive-global-clear.d.ts.map +1 -1
  1762. package/dist/modes/interactive/interactive-global-clear.js +61 -3
  1763. package/dist/modes/interactive/interactive-global-clear.js.map +1 -1
  1764. package/dist/modes/interactive/interactive-hotkeys-debug.js +2 -3
  1765. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  1766. package/dist/modes/interactive/interactive-initial-session-binding.d.ts +4 -0
  1767. package/dist/modes/interactive/interactive-initial-session-binding.d.ts.map +1 -0
  1768. package/dist/modes/interactive/interactive-initial-session-binding.js +23 -0
  1769. package/dist/modes/interactive/interactive-initial-session-binding.js.map +1 -0
  1770. package/dist/modes/interactive/interactive-input-handling.d.ts +2 -1
  1771. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  1772. package/dist/modes/interactive/interactive-input-handling.js +82 -69
  1773. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  1774. package/dist/modes/interactive/interactive-key-identity.d.ts +22 -0
  1775. package/dist/modes/interactive/interactive-key-identity.d.ts.map +1 -0
  1776. package/dist/modes/interactive/interactive-key-identity.js +29 -0
  1777. package/dist/modes/interactive/interactive-key-identity.js.map +1 -0
  1778. package/dist/modes/interactive/interactive-mode-base.d.ts +18 -13
  1779. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  1780. package/dist/modes/interactive/interactive-mode-base.js +26 -18
  1781. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  1782. package/dist/modes/interactive/interactive-mode-deps.d.ts +20 -22
  1783. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  1784. package/dist/modes/interactive/interactive-mode-deps.js +16 -18
  1785. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  1786. package/dist/modes/interactive/interactive-mode-helpers.d.ts +3 -2
  1787. package/dist/modes/interactive/interactive-mode-helpers.d.ts.map +1 -1
  1788. package/dist/modes/interactive/interactive-mode-helpers.js +8 -18
  1789. package/dist/modes/interactive/interactive-mode-helpers.js.map +1 -1
  1790. package/dist/modes/interactive/interactive-mode-surface.d.ts +28 -17
  1791. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  1792. package/dist/modes/interactive/interactive-mode-surface.js +1 -0
  1793. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  1794. package/dist/modes/interactive/interactive-mode-types.d.ts +2 -2
  1795. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  1796. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  1797. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  1798. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  1799. package/dist/modes/interactive/interactive-mode.js +1 -0
  1800. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  1801. package/dist/modes/interactive/interactive-model-catalog-startup.d.ts +12 -0
  1802. package/dist/modes/interactive/interactive-model-catalog-startup.d.ts.map +1 -0
  1803. package/dist/modes/interactive/interactive-model-catalog-startup.js +22 -0
  1804. package/dist/modes/interactive/interactive-model-catalog-startup.js.map +1 -0
  1805. package/dist/modes/interactive/interactive-model-routing.js +41 -111
  1806. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  1807. package/dist/modes/interactive/interactive-onboarding.js +7 -7
  1808. package/dist/modes/interactive/interactive-onboarding.js.map +1 -1
  1809. package/dist/modes/interactive/interactive-pause.d.ts +4 -0
  1810. package/dist/modes/interactive/interactive-pause.d.ts.map +1 -0
  1811. package/dist/modes/interactive/interactive-pause.js +8 -0
  1812. package/dist/modes/interactive/interactive-pause.js.map +1 -0
  1813. package/dist/modes/interactive/interactive-process-lifecycle.js +43 -12
  1814. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  1815. package/dist/modes/interactive/interactive-prompt-restore.d.ts +58 -0
  1816. package/dist/modes/interactive/interactive-prompt-restore.d.ts.map +1 -0
  1817. package/dist/modes/interactive/interactive-prompt-restore.js +87 -0
  1818. package/dist/modes/interactive/interactive-prompt-restore.js.map +1 -0
  1819. package/dist/modes/interactive/interactive-prompt-turn.d.ts +2 -0
  1820. package/dist/modes/interactive/interactive-prompt-turn.d.ts.map +1 -0
  1821. package/dist/modes/interactive/interactive-prompt-turn.js +56 -0
  1822. package/dist/modes/interactive/interactive-prompt-turn.js.map +1 -0
  1823. package/dist/modes/interactive/interactive-queueing.js +6 -9
  1824. package/dist/modes/interactive/interactive-queueing.js.map +1 -1
  1825. package/dist/modes/interactive/interactive-render-chat.js +128 -48
  1826. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  1827. package/dist/modes/interactive/interactive-resource-disclosure.js +1 -3
  1828. package/dist/modes/interactive/interactive-resource-disclosure.js.map +1 -1
  1829. package/dist/modes/interactive/interactive-resource-paths.js +17 -21
  1830. package/dist/modes/interactive/interactive-resource-paths.js.map +1 -1
  1831. package/dist/modes/interactive/interactive-resource-rendering.js +110 -12
  1832. package/dist/modes/interactive/interactive-resource-rendering.js.map +1 -1
  1833. package/dist/modes/interactive/interactive-selectors.js +13 -5
  1834. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  1835. package/dist/modes/interactive/interactive-session-routing.js +19 -4
  1836. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  1837. package/dist/modes/interactive/interactive-session-runtime.js +12 -1
  1838. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  1839. package/dist/modes/interactive/interactive-slash-commands.js +23 -4
  1840. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  1841. package/dist/modes/interactive/interactive-startup-chat-container.d.ts +20 -0
  1842. package/dist/modes/interactive/interactive-startup-chat-container.d.ts.map +1 -0
  1843. package/dist/modes/interactive/interactive-startup-chat-container.js +26 -0
  1844. package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -0
  1845. package/dist/modes/interactive/interactive-startup.d.ts +1 -1
  1846. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  1847. package/dist/modes/interactive/interactive-startup.js +37 -37
  1848. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  1849. package/dist/modes/interactive/interactive-submission.d.ts +24 -0
  1850. package/dist/modes/interactive/interactive-submission.d.ts.map +1 -0
  1851. package/dist/modes/interactive/interactive-submission.js +10 -0
  1852. package/dist/modes/interactive/interactive-submission.js.map +1 -0
  1853. package/dist/modes/interactive/interactive-summarization-retry-events.d.ts +8 -0
  1854. package/dist/modes/interactive/interactive-summarization-retry-events.d.ts.map +1 -0
  1855. package/dist/modes/interactive/interactive-summarization-retry-events.js +48 -0
  1856. package/dist/modes/interactive/interactive-summarization-retry-events.js.map +1 -0
  1857. package/dist/modes/interactive/login-provider-options.d.ts +21 -0
  1858. package/dist/modes/interactive/login-provider-options.d.ts.map +1 -0
  1859. package/dist/modes/interactive/login-provider-options.js +50 -0
  1860. package/dist/modes/interactive/login-provider-options.js.map +1 -0
  1861. package/dist/modes/interactive/theme/catppuccin-frappe.json +1 -1
  1862. package/dist/modes/interactive/theme/catppuccin-latte.json +1 -1
  1863. package/dist/modes/interactive/theme/catppuccin-macchiato.json +1 -1
  1864. package/dist/modes/interactive/theme/catppuccin-mocha.json +9 -1
  1865. package/dist/modes/interactive/theme/dark.json +1 -1
  1866. package/dist/modes/interactive/theme/global-theme.d.ts +1 -1
  1867. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  1868. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  1869. package/dist/modes/interactive/theme/light.json +1 -1
  1870. package/dist/modes/interactive/theme/theme-class.d.ts +4 -0
  1871. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  1872. package/dist/modes/interactive/theme/theme-class.js +7 -0
  1873. package/dist/modes/interactive/theme/theme-class.js.map +1 -1
  1874. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  1875. package/dist/modes/interactive/theme/theme-loading.js +5 -1
  1876. package/dist/modes/interactive/theme/theme-loading.js.map +1 -1
  1877. package/dist/modes/interactive/theme/theme-schema.d.ts +32 -0
  1878. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  1879. package/dist/modes/interactive/theme/theme-schema.js +8 -0
  1880. package/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  1881. package/dist/modes/interactive/theme/theme-schema.json +15 -2
  1882. package/dist/modes/interactive/theme/theme.d.ts +2 -2
  1883. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  1884. package/dist/modes/interactive/theme/theme.js +2 -2
  1885. package/dist/modes/interactive/theme/theme.js.map +1 -1
  1886. package/dist/modes/interactive/whimsical-messages.d.ts +1 -0
  1887. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -1
  1888. package/dist/modes/interactive/whimsical-messages.js +2 -2
  1889. package/dist/modes/interactive/whimsical-messages.js.map +1 -1
  1890. package/dist/modes/interactive-engine/activity-watchdog.d.ts +16 -6
  1891. package/dist/modes/interactive-engine/activity-watchdog.d.ts.map +1 -1
  1892. package/dist/modes/interactive-engine/activity-watchdog.js +12 -6
  1893. package/dist/modes/interactive-engine/activity-watchdog.js.map +1 -1
  1894. package/dist/modes/interactive-engine/create-isolated-runtime.d.ts.map +1 -1
  1895. package/dist/modes/interactive-engine/create-isolated-runtime.js +8 -5
  1896. package/dist/modes/interactive-engine/create-isolated-runtime.js.map +1 -1
  1897. package/dist/modes/interactive-engine/engine-args.d.ts.map +1 -1
  1898. package/dist/modes/interactive-engine/engine-args.js +0 -1
  1899. package/dist/modes/interactive-engine/engine-args.js.map +1 -1
  1900. package/dist/modes/interactive-engine/engine-child-liveness.d.ts.map +1 -1
  1901. package/dist/modes/interactive-engine/engine-child-liveness.js +6 -4
  1902. package/dist/modes/interactive-engine/engine-child-liveness.js.map +1 -1
  1903. package/dist/modes/interactive-engine/engine-custom-ui.d.ts +3 -2
  1904. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  1905. package/dist/modes/interactive-engine/engine-custom-ui.js +37 -28
  1906. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  1907. package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts +16 -0
  1908. package/dist/modes/interactive-engine/engine-diagnostic-view.d.ts.map +1 -0
  1909. package/dist/modes/interactive-engine/engine-diagnostic-view.js +19 -0
  1910. package/dist/modes/interactive-engine/engine-diagnostic-view.js.map +1 -0
  1911. package/dist/modes/interactive-engine/engine-dialog-host.d.ts +33 -0
  1912. package/dist/modes/interactive-engine/engine-dialog-host.d.ts.map +1 -0
  1913. package/dist/modes/interactive-engine/engine-dialog-host.js +129 -0
  1914. package/dist/modes/interactive-engine/engine-dialog-host.js.map +1 -0
  1915. package/dist/modes/interactive-engine/engine-generation.d.ts +29 -0
  1916. package/dist/modes/interactive-engine/engine-generation.d.ts.map +1 -0
  1917. package/dist/modes/interactive-engine/engine-generation.js +11 -0
  1918. package/dist/modes/interactive-engine/engine-generation.js.map +1 -0
  1919. package/dist/modes/interactive-engine/engine-health.d.ts +107 -0
  1920. package/dist/modes/interactive-engine/engine-health.d.ts.map +1 -0
  1921. package/dist/modes/interactive-engine/engine-health.js +250 -0
  1922. package/dist/modes/interactive-engine/engine-health.js.map +1 -0
  1923. package/dist/modes/interactive-engine/engine-input-form.d.ts +1 -1
  1924. package/dist/modes/interactive-engine/engine-input-form.d.ts.map +1 -1
  1925. package/dist/modes/interactive-engine/engine-input-form.js +21 -4
  1926. package/dist/modes/interactive-engine/engine-input-form.js.map +1 -1
  1927. package/dist/modes/interactive-engine/engine-monitor.d.ts +8 -1
  1928. package/dist/modes/interactive-engine/engine-monitor.d.ts.map +1 -1
  1929. package/dist/modes/interactive-engine/engine-monitor.js +19 -16
  1930. package/dist/modes/interactive-engine/engine-monitor.js.map +1 -1
  1931. package/dist/modes/interactive-engine/engine-render-service.d.ts.map +1 -1
  1932. package/dist/modes/interactive-engine/engine-render-service.js +19 -24
  1933. package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
  1934. package/dist/modes/interactive-engine/engine-session-picker.d.ts.map +1 -1
  1935. package/dist/modes/interactive-engine/engine-session-picker.js +4 -2
  1936. package/dist/modes/interactive-engine/engine-session-picker.js.map +1 -1
  1937. package/dist/modes/interactive-engine/extension-ui-bridge.d.ts +18 -3
  1938. package/dist/modes/interactive-engine/extension-ui-bridge.d.ts.map +1 -1
  1939. package/dist/modes/interactive-engine/extension-ui-bridge.js +41 -45
  1940. package/dist/modes/interactive-engine/extension-ui-bridge.js.map +1 -1
  1941. package/dist/modes/interactive-engine/input-form-host.d.ts +1 -0
  1942. package/dist/modes/interactive-engine/input-form-host.d.ts.map +1 -1
  1943. package/dist/modes/interactive-engine/input-form-host.js +18 -3
  1944. package/dist/modes/interactive-engine/input-form-host.js.map +1 -1
  1945. package/dist/modes/interactive-engine/isolated-auth.d.ts +10 -0
  1946. package/dist/modes/interactive-engine/isolated-auth.d.ts.map +1 -0
  1947. package/dist/modes/interactive-engine/isolated-auth.js +15 -0
  1948. package/dist/modes/interactive-engine/isolated-auth.js.map +1 -0
  1949. package/dist/modes/interactive-engine/isolated-runtime.d.ts +44 -11
  1950. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  1951. package/dist/modes/interactive-engine/isolated-runtime.js +173 -106
  1952. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  1953. package/dist/modes/interactive-engine/protocol.d.ts +26 -2
  1954. package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
  1955. package/dist/modes/interactive-engine/protocol.js +192 -63
  1956. package/dist/modes/interactive-engine/protocol.js.map +1 -1
  1957. package/dist/modes/interactive-engine/remote-component.d.ts +38 -0
  1958. package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
  1959. package/dist/modes/interactive-engine/remote-component.js +111 -22
  1960. package/dist/modes/interactive-engine/remote-component.js.map +1 -1
  1961. package/dist/modes/interactive-engine/remote-input-ownership.d.ts +48 -0
  1962. package/dist/modes/interactive-engine/remote-input-ownership.d.ts.map +1 -0
  1963. package/dist/modes/interactive-engine/remote-input-ownership.js +42 -0
  1964. package/dist/modes/interactive-engine/remote-input-ownership.js.map +1 -0
  1965. package/dist/modes/interactive-engine/remote-model-catalog.d.ts +17 -0
  1966. package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -0
  1967. package/dist/modes/interactive-engine/remote-model-catalog.js +80 -0
  1968. package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -0
  1969. package/dist/modes/interactive-engine/remote-queue-pause.d.ts +13 -0
  1970. package/dist/modes/interactive-engine/remote-queue-pause.d.ts.map +1 -0
  1971. package/dist/modes/interactive-engine/remote-queue-pause.js +38 -0
  1972. package/dist/modes/interactive-engine/remote-queue-pause.js.map +1 -0
  1973. package/dist/modes/interactive-engine/remote-renderer.d.ts +3 -1
  1974. package/dist/modes/interactive-engine/remote-renderer.d.ts.map +1 -1
  1975. package/dist/modes/interactive-engine/remote-renderer.js +41 -10
  1976. package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
  1977. package/dist/modes/interactive-engine/session-picker-host.d.ts +1 -0
  1978. package/dist/modes/interactive-engine/session-picker-host.d.ts.map +1 -1
  1979. package/dist/modes/interactive-engine/session-picker-host.js +4 -0
  1980. package/dist/modes/interactive-engine/session-picker-host.js.map +1 -1
  1981. package/dist/modes/print-mode.d.ts.map +1 -1
  1982. package/dist/modes/print-mode.js +5 -0
  1983. package/dist/modes/print-mode.js.map +1 -1
  1984. package/dist/modes/rpc/jsonl.d.ts +9 -7
  1985. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  1986. package/dist/modes/rpc/jsonl.js +23 -24
  1987. package/dist/modes/rpc/jsonl.js.map +1 -1
  1988. package/dist/modes/rpc/queued-writer.d.ts +33 -0
  1989. package/dist/modes/rpc/queued-writer.d.ts.map +1 -0
  1990. package/dist/modes/rpc/queued-writer.js +104 -0
  1991. package/dist/modes/rpc/queued-writer.js.map +1 -0
  1992. package/dist/modes/rpc/rpc-bash-request-owners.d.ts +24 -0
  1993. package/dist/modes/rpc/rpc-bash-request-owners.d.ts.map +1 -0
  1994. package/dist/modes/rpc/rpc-bash-request-owners.js +45 -0
  1995. package/dist/modes/rpc/rpc-bash-request-owners.js.map +1 -0
  1996. package/dist/modes/rpc/rpc-client-api.d.ts +17 -4
  1997. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  1998. package/dist/modes/rpc/rpc-client-api.js +92 -25
  1999. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  2000. package/dist/modes/rpc/rpc-client-process.d.ts +11 -4
  2001. package/dist/modes/rpc/rpc-client-process.d.ts.map +1 -1
  2002. package/dist/modes/rpc/rpc-client-process.js +77 -29
  2003. package/dist/modes/rpc/rpc-client-process.js.map +1 -1
  2004. package/dist/modes/rpc/rpc-client-waits.d.ts +17 -1
  2005. package/dist/modes/rpc/rpc-client-waits.d.ts.map +1 -1
  2006. package/dist/modes/rpc/rpc-client-waits.js +25 -0
  2007. package/dist/modes/rpc/rpc-client-waits.js.map +1 -1
  2008. package/dist/modes/rpc/rpc-client.d.ts +68 -9
  2009. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  2010. package/dist/modes/rpc/rpc-client.js +205 -158
  2011. package/dist/modes/rpc/rpc-client.js.map +1 -1
  2012. package/dist/modes/rpc/rpc-command-handler.d.ts +10 -3
  2013. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  2014. package/dist/modes/rpc/rpc-command-handler.js +165 -43
  2015. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  2016. package/dist/modes/rpc/rpc-command-timeouts.d.ts +30 -0
  2017. package/dist/modes/rpc/rpc-command-timeouts.d.ts.map +1 -0
  2018. package/dist/modes/rpc/rpc-command-timeouts.js +52 -0
  2019. package/dist/modes/rpc/rpc-command-timeouts.js.map +1 -0
  2020. package/dist/modes/rpc/rpc-event-buffer.d.ts.map +1 -1
  2021. package/dist/modes/rpc/rpc-event-buffer.js +3 -1
  2022. package/dist/modes/rpc/rpc-event-buffer.js.map +1 -1
  2023. package/dist/modes/rpc/rpc-extension-ui.d.ts +4 -2
  2024. package/dist/modes/rpc/rpc-extension-ui.d.ts.map +1 -1
  2025. package/dist/modes/rpc/rpc-extension-ui.js +50 -17
  2026. package/dist/modes/rpc/rpc-extension-ui.js.map +1 -1
  2027. package/dist/modes/rpc/rpc-generation-buffer.d.ts +21 -0
  2028. package/dist/modes/rpc/rpc-generation-buffer.d.ts.map +1 -0
  2029. package/dist/modes/rpc/rpc-generation-buffer.js +35 -0
  2030. package/dist/modes/rpc/rpc-generation-buffer.js.map +1 -0
  2031. package/dist/modes/rpc/rpc-input-scheduler.d.ts +3 -3
  2032. package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
  2033. package/dist/modes/rpc/rpc-input-scheduler.js +19 -8
  2034. package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
  2035. package/dist/modes/rpc/rpc-input.d.ts +7 -1
  2036. package/dist/modes/rpc/rpc-input.d.ts.map +1 -1
  2037. package/dist/modes/rpc/rpc-input.js +14 -4
  2038. package/dist/modes/rpc/rpc-input.js.map +1 -1
  2039. package/dist/modes/rpc/rpc-keybindings-reload.d.ts.map +1 -1
  2040. package/dist/modes/rpc/rpc-keybindings-reload.js.map +1 -1
  2041. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  2042. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  2043. package/dist/modes/rpc/rpc-mode.js +23 -23
  2044. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  2045. package/dist/modes/rpc/rpc-model-fallback-prompt.d.ts +4 -0
  2046. package/dist/modes/rpc/rpc-model-fallback-prompt.d.ts.map +1 -0
  2047. package/dist/modes/rpc/rpc-model-fallback-prompt.js +11 -0
  2048. package/dist/modes/rpc/rpc-model-fallback-prompt.js.map +1 -0
  2049. package/dist/modes/rpc/rpc-oauth-client.d.ts +21 -0
  2050. package/dist/modes/rpc/rpc-oauth-client.d.ts.map +1 -0
  2051. package/dist/modes/rpc/rpc-oauth-client.js +81 -0
  2052. package/dist/modes/rpc/rpc-oauth-client.js.map +1 -0
  2053. package/dist/modes/rpc/rpc-oauth-interaction.d.ts +9 -0
  2054. package/dist/modes/rpc/rpc-oauth-interaction.d.ts.map +1 -0
  2055. package/dist/modes/rpc/rpc-oauth-interaction.js +49 -0
  2056. package/dist/modes/rpc/rpc-oauth-interaction.js.map +1 -0
  2057. package/dist/modes/rpc/rpc-output-buffer.d.ts +2 -1
  2058. package/dist/modes/rpc/rpc-output-buffer.d.ts.map +1 -1
  2059. package/dist/modes/rpc/rpc-output-buffer.js +7 -40
  2060. package/dist/modes/rpc/rpc-output-buffer.js.map +1 -1
  2061. package/dist/modes/rpc/rpc-pending-requests.d.ts +37 -0
  2062. package/dist/modes/rpc/rpc-pending-requests.d.ts.map +1 -0
  2063. package/dist/modes/rpc/rpc-pending-requests.js +57 -0
  2064. package/dist/modes/rpc/rpc-pending-requests.js.map +1 -0
  2065. package/dist/modes/rpc/rpc-provider-auth.d.ts +22 -0
  2066. package/dist/modes/rpc/rpc-provider-auth.d.ts.map +1 -0
  2067. package/dist/modes/rpc/rpc-provider-auth.js +117 -0
  2068. package/dist/modes/rpc/rpc-provider-auth.js.map +1 -0
  2069. package/dist/modes/rpc/rpc-responses.d.ts +0 -1
  2070. package/dist/modes/rpc/rpc-responses.d.ts.map +1 -1
  2071. package/dist/modes/rpc/rpc-responses.js +0 -14
  2072. package/dist/modes/rpc/rpc-responses.js.map +1 -1
  2073. package/dist/modes/rpc/rpc-session-binding.d.ts +2 -1
  2074. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  2075. package/dist/modes/rpc/rpc-session-binding.js +71 -43
  2076. package/dist/modes/rpc/rpc-session-binding.js.map +1 -1
  2077. package/dist/modes/rpc/rpc-terminal-drain.d.ts +52 -0
  2078. package/dist/modes/rpc/rpc-terminal-drain.d.ts.map +1 -0
  2079. package/dist/modes/rpc/rpc-terminal-drain.js +85 -0
  2080. package/dist/modes/rpc/rpc-terminal-drain.js.map +1 -0
  2081. package/dist/modes/rpc/rpc-transport-error.d.ts +49 -0
  2082. package/dist/modes/rpc/rpc-transport-error.d.ts.map +1 -0
  2083. package/dist/modes/rpc/rpc-transport-error.js +78 -0
  2084. package/dist/modes/rpc/rpc-transport-error.js.map +1 -0
  2085. package/dist/modes/rpc/rpc-types.d.ts +188 -25
  2086. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  2087. package/dist/modes/rpc/rpc-types.js.map +1 -1
  2088. package/dist/package-manager-cli-parser.js.map +1 -1
  2089. package/dist/package-manager-cli.d.ts +3 -3
  2090. package/dist/package-manager-cli.d.ts.map +1 -1
  2091. package/dist/package-manager-cli.js +66 -53
  2092. package/dist/package-manager-cli.js.map +1 -1
  2093. package/dist/rpc-entry.js +6 -2
  2094. package/dist/rpc-entry.js.map +1 -1
  2095. package/dist/self-update-plan.d.ts +12 -0
  2096. package/dist/self-update-plan.d.ts.map +1 -0
  2097. package/dist/self-update-plan.js +42 -0
  2098. package/dist/self-update-plan.js.map +1 -0
  2099. package/dist/utils/child-process.d.ts.map +1 -1
  2100. package/dist/utils/child-process.js.map +1 -1
  2101. package/dist/utils/clipboard-image.js +1 -1
  2102. package/dist/utils/clipboard-image.js.map +1 -1
  2103. package/dist/utils/clipboard-native.d.ts +1 -0
  2104. package/dist/utils/clipboard-native.d.ts.map +1 -1
  2105. package/dist/utils/clipboard-native.js.map +1 -1
  2106. package/dist/utils/clipboard.d.ts +3 -0
  2107. package/dist/utils/clipboard.d.ts.map +1 -1
  2108. package/dist/utils/clipboard.js +31 -8
  2109. package/dist/utils/clipboard.js.map +1 -1
  2110. package/dist/utils/compile-cache.d.ts +8 -0
  2111. package/dist/utils/compile-cache.d.ts.map +1 -0
  2112. package/dist/utils/compile-cache.js +31 -0
  2113. package/dist/utils/compile-cache.js.map +1 -0
  2114. package/dist/utils/fs-watch.d.ts.map +1 -1
  2115. package/dist/utils/fs-watch.js +1 -3
  2116. package/dist/utils/fs-watch.js.map +1 -1
  2117. package/dist/utils/git.js +1 -1
  2118. package/dist/utils/git.js.map +1 -1
  2119. package/dist/utils/interactive-engine-bootstrap.d.ts +67 -0
  2120. package/dist/utils/interactive-engine-bootstrap.d.ts.map +1 -0
  2121. package/dist/utils/interactive-engine-bootstrap.js +117 -0
  2122. package/dist/utils/interactive-engine-bootstrap.js.map +1 -0
  2123. package/dist/utils/interactive-engine-env.d.ts +41 -0
  2124. package/dist/utils/interactive-engine-env.d.ts.map +1 -0
  2125. package/dist/utils/interactive-engine-env.js +67 -0
  2126. package/dist/utils/interactive-engine-env.js.map +1 -0
  2127. package/dist/utils/markit.d.ts.map +1 -1
  2128. package/dist/utils/markit.js +15 -5
  2129. package/dist/utils/markit.js.map +1 -1
  2130. package/dist/utils/shell.js +2 -2
  2131. package/dist/utils/shell.js.map +1 -1
  2132. package/dist/utils/split-launcher.d.ts.map +1 -1
  2133. package/dist/utils/split-launcher.js +1 -2
  2134. package/dist/utils/split-launcher.js.map +1 -1
  2135. package/dist/utils/tools-manager.d.ts.map +1 -1
  2136. package/dist/utils/tools-manager.js +8 -34
  2137. package/dist/utils/tools-manager.js.map +1 -1
  2138. package/dist/utils/version-check.d.ts +1 -0
  2139. package/dist/utils/version-check.d.ts.map +1 -1
  2140. package/dist/utils/version-check.js +7 -5
  2141. package/dist/utils/version-check.js.map +1 -1
  2142. package/docs/changelog.mdx +24 -2
  2143. package/docs/compaction.md +101 -18
  2144. package/docs/custom-provider.md +40 -8
  2145. package/docs/development.md +14 -15
  2146. package/docs/docs.json +12 -2
  2147. package/docs/environment-variables.md +37 -0
  2148. package/docs/extensions.md +166 -18
  2149. package/docs/index.md +1 -0
  2150. package/docs/intercom.md +1 -1
  2151. package/docs/json.md +11 -5
  2152. package/docs/keybindings.md +19 -2
  2153. package/docs/llama-cpp.md +72 -0
  2154. package/docs/models/artificial-analysis-index.md +82 -0
  2155. package/docs/models/model-selection.md +63 -0
  2156. package/docs/models/pareto-efficiency.md +64 -0
  2157. package/docs/models.md +41 -50
  2158. package/docs/packages.md +4 -2
  2159. package/docs/prompt-templates.md +1 -0
  2160. package/docs/providers.md +89 -23
  2161. package/docs/quickstart.md +21 -17
  2162. package/docs/rpc.md +97 -68
  2163. package/docs/sdk.md +68 -71
  2164. package/docs/security.md +15 -0
  2165. package/docs/session-format.md +6 -15
  2166. package/docs/sessions.md +1 -1
  2167. package/docs/settings.md +29 -18
  2168. package/docs/skills.md +8 -0
  2169. package/docs/subagents.md +24 -8
  2170. package/docs/themes.md +10 -1
  2171. package/docs/tui.md +14 -7
  2172. package/docs/usage.md +71 -2
  2173. package/docs/windows.md +1 -1
  2174. package/docs/workflows.md +1253 -534
  2175. package/examples/extensions/README.md +2 -2
  2176. package/examples/extensions/border-status-editor.ts +1 -6
  2177. package/examples/extensions/custom-compaction.ts +7 -2
  2178. package/examples/extensions/custom-footer.ts +1 -1
  2179. package/examples/extensions/custom-provider-anthropic/index.ts +14 -2
  2180. package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
  2181. package/examples/extensions/gondolin/index.ts +1 -1
  2182. package/examples/extensions/handoff.ts +9 -2
  2183. package/examples/extensions/hello.ts +1 -1
  2184. package/examples/extensions/message-renderer.ts +2 -2
  2185. package/examples/extensions/minimal-mode.ts +6 -6
  2186. package/examples/extensions/overlay-qa-focus-components.ts +0 -1
  2187. package/examples/extensions/overlay-qa-streaming-input-components.ts +2 -2
  2188. package/examples/extensions/overlay-qa-tests.ts +16 -14
  2189. package/examples/extensions/overlay-qa-toggle-passive-components.ts +3 -5
  2190. package/examples/extensions/overlay-test.ts +3 -6
  2191. package/examples/extensions/plan-mode/index.ts +1 -1
  2192. package/examples/extensions/preset.ts +1 -1
  2193. package/examples/extensions/qna.ts +1 -1
  2194. package/examples/extensions/sandbox/package-lock.json +3 -3
  2195. package/examples/extensions/structured-output.ts +9 -9
  2196. package/examples/extensions/subagent/display.ts +115 -127
  2197. package/examples/extensions/subagent/index.ts +310 -349
  2198. package/examples/extensions/subagent/render.ts +244 -345
  2199. package/examples/extensions/subagent/runner.ts +223 -230
  2200. package/examples/extensions/subagent/schemas.ts +39 -46
  2201. package/examples/extensions/subagent/types.ts +22 -22
  2202. package/examples/extensions/summarize.ts +4 -1
  2203. package/examples/extensions/tic-tac-toe.ts +4 -4
  2204. package/examples/extensions/todo.ts +1 -1
  2205. package/examples/extensions/tool-override.ts +1 -1
  2206. package/examples/extensions/trigger-compact.ts +4 -1
  2207. package/examples/extensions/working-indicator.ts +3 -2
  2208. package/examples/rpc-extension-ui-components.ts +1 -2
  2209. package/examples/sdk/02-custom-model.ts +8 -10
  2210. package/examples/sdk/03-custom-prompt.ts +1 -6
  2211. package/examples/sdk/06-extensions.ts +1 -6
  2212. package/examples/sdk/07-context-files.ts +1 -6
  2213. package/examples/sdk/09-api-keys-and-oauth.ts +17 -23
  2214. package/examples/sdk/12-full-control.ts +10 -12
  2215. package/examples/sdk/README.md +17 -21
  2216. package/npm-shrinkwrap.json +305 -314
  2217. package/package.json +17 -15
  2218. package/dist/builtin/cursor/CHANGELOG.md +0 -275
  2219. package/dist/builtin/cursor/LICENSE +0 -26
  2220. package/dist/builtin/cursor/README.md +0 -24
  2221. package/dist/builtin/cursor/index.ts +0 -9
  2222. package/dist/builtin/cursor/package.json +0 -47
  2223. package/dist/builtin/cursor/src/auth.ts +0 -352
  2224. package/dist/builtin/cursor/src/catalog-cache.ts +0 -155
  2225. package/dist/builtin/cursor/src/config.ts +0 -123
  2226. package/dist/builtin/cursor/src/conversation-state.ts +0 -135
  2227. package/dist/builtin/cursor/src/cursor-models-raw.json +0 -412
  2228. package/dist/builtin/cursor/src/model-mapper.ts +0 -369
  2229. package/dist/builtin/cursor/src/model-reference.ts +0 -282
  2230. package/dist/builtin/cursor/src/models.ts +0 -54
  2231. package/dist/builtin/cursor/src/native-loader.ts +0 -71
  2232. package/dist/builtin/cursor/src/proto/README.md +0 -34
  2233. package/dist/builtin/cursor/src/proto/agent_pb.ts +0 -15294
  2234. package/dist/builtin/cursor/src/proto/protobuf-codec-base64.ts +0 -22
  2235. package/dist/builtin/cursor/src/proto/protobuf-codec-json.ts +0 -44
  2236. package/dist/builtin/cursor/src/proto/protobuf-codec-request.ts +0 -311
  2237. package/dist/builtin/cursor/src/proto/protobuf-codec-wire.ts +0 -248
  2238. package/dist/builtin/cursor/src/proto/protobuf-codec.ts +0 -200
  2239. package/dist/builtin/cursor/src/provider.ts +0 -301
  2240. package/dist/builtin/cursor/src/stream.ts +0 -494
  2241. package/dist/builtin/cursor/src/transport-errors.ts +0 -74
  2242. package/dist/builtin/cursor/src/transport-frame.ts +0 -56
  2243. package/dist/builtin/cursor/src/transport-http2.ts +0 -122
  2244. package/dist/builtin/cursor/src/transport-native-client.ts +0 -161
  2245. package/dist/builtin/cursor/src/transport-run-stream.ts +0 -188
  2246. package/dist/builtin/cursor/src/transport-timeouts.ts +0 -87
  2247. package/dist/builtin/cursor/src/transport-types.ts +0 -143
  2248. package/dist/builtin/cursor/src/transport.ts +0 -26
  2249. package/dist/builtin/workflows/builtin/deep-research-codebase-runner.ts +0 -492
  2250. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +0 -383
  2251. package/dist/builtin/workflows/builtin/deep-research-codebase.d.ts +0 -35
  2252. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +0 -47
  2253. package/dist/builtin/workflows/skills/impeccable/reference/brand.md +0 -108
  2254. package/dist/builtin/workflows/skills/impeccable/reference/codex.md +0 -105
  2255. package/dist/builtin/workflows/skills/impeccable/reference/interaction-design.md +0 -189
  2256. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +0 -516
  2257. package/dist/core/context-window.d.ts +0 -54
  2258. package/dist/core/context-window.d.ts.map +0 -1
  2259. package/dist/core/context-window.js +0 -110
  2260. package/dist/core/context-window.js.map +0 -1
  2261. package/dist/core/copilot-anthropic-sse-repair.d.ts +0 -23
  2262. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +0 -1
  2263. package/dist/core/copilot-anthropic-sse-repair.js +0 -340
  2264. package/dist/core/copilot-anthropic-sse-repair.js.map +0 -1
  2265. package/dist/core/copilot-errors.d.ts +0 -9
  2266. package/dist/core/copilot-errors.d.ts.map +0 -1
  2267. package/dist/core/copilot-errors.js +0 -32
  2268. package/dist/core/copilot-errors.js.map +0 -1
  2269. package/dist/core/copilot-gemini-payload-sanitizer.d.ts +0 -72
  2270. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +0 -1
  2271. package/dist/core/copilot-gemini-payload-sanitizer.js +0 -296
  2272. package/dist/core/copilot-gemini-payload-sanitizer.js.map +0 -1
  2273. package/dist/core/copilot-gemini-reasoning.d.ts +0 -126
  2274. package/dist/core/copilot-gemini-reasoning.d.ts.map +0 -1
  2275. package/dist/core/copilot-gemini-reasoning.js +0 -265
  2276. package/dist/core/copilot-gemini-reasoning.js.map +0 -1
  2277. package/dist/core/copilot-gemini-tool-arguments.d.ts +0 -42
  2278. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +0 -1
  2279. package/dist/core/copilot-gemini-tool-arguments.js +0 -159
  2280. package/dist/core/copilot-gemini-tool-arguments.js.map +0 -1
  2281. package/dist/core/copilot-hosts.d.ts +0 -12
  2282. package/dist/core/copilot-hosts.d.ts.map +0 -1
  2283. package/dist/core/copilot-hosts.js +0 -33
  2284. package/dist/core/copilot-hosts.js.map +0 -1
  2285. package/dist/core/copilot-model-catalog.d.ts +0 -114
  2286. package/dist/core/copilot-model-catalog.d.ts.map +0 -1
  2287. package/dist/core/copilot-model-catalog.js +0 -392
  2288. package/dist/core/copilot-model-catalog.js.map +0 -1
  2289. package/dist/core/copilot-model-static-fallbacks.d.ts +0 -43
  2290. package/dist/core/copilot-model-static-fallbacks.d.ts.map +0 -1
  2291. package/dist/core/copilot-model-static-fallbacks.js +0 -50
  2292. package/dist/core/copilot-model-static-fallbacks.js.map +0 -1
  2293. package/dist/core/copilot-model-synthesis.d.ts +0 -10
  2294. package/dist/core/copilot-model-synthesis.d.ts.map +0 -1
  2295. package/dist/core/copilot-model-synthesis.js +0 -91
  2296. package/dist/core/copilot-model-synthesis.js.map +0 -1
  2297. package/dist/core/model-registry-auth.d.ts +0 -8
  2298. package/dist/core/model-registry-auth.d.ts.map +0 -1
  2299. package/dist/core/model-registry-auth.js +0 -83
  2300. package/dist/core/model-registry-auth.js.map +0 -1
  2301. package/dist/core/model-registry-builtins.d.ts +0 -10
  2302. package/dist/core/model-registry-builtins.d.ts.map +0 -1
  2303. package/dist/core/model-registry-builtins.js +0 -171
  2304. package/dist/core/model-registry-builtins.js.map +0 -1
  2305. package/dist/core/model-registry-custom-loader.d.ts +0 -3
  2306. package/dist/core/model-registry-custom-loader.d.ts.map +0 -1
  2307. package/dist/core/model-registry-custom-loader.js +0 -254
  2308. package/dist/core/model-registry-custom-loader.js.map +0 -1
  2309. package/dist/core/model-registry-dynamic.d.ts +0 -7
  2310. package/dist/core/model-registry-dynamic.d.ts.map +0 -1
  2311. package/dist/core/model-registry-dynamic.js +0 -201
  2312. package/dist/core/model-registry-dynamic.js.map +0 -1
  2313. package/dist/core/model-registry-loader.d.ts +0 -5
  2314. package/dist/core/model-registry-loader.d.ts.map +0 -1
  2315. package/dist/core/model-registry-loader.js +0 -28
  2316. package/dist/core/model-registry-loader.js.map +0 -1
  2317. package/dist/core/model-registry-schemas.d.ts +0 -1273
  2318. package/dist/core/model-registry-schemas.d.ts.map +0 -1
  2319. package/dist/core/model-registry-schemas.js.map +0 -1
  2320. package/dist/core/model-registry-types.d.ts +0 -82
  2321. package/dist/core/model-registry-types.d.ts.map +0 -1
  2322. package/dist/core/model-registry-types.js +0 -2
  2323. package/dist/core/model-registry-types.js.map +0 -1
  2324. package/dist/core/oauth-compat.d.ts +0 -17
  2325. package/dist/core/oauth-compat.d.ts.map +0 -1
  2326. package/dist/core/oauth-compat.js +0 -2
  2327. package/dist/core/oauth-compat.js.map +0 -1
  2328. package/dist/core/oauth-provider-bridge.d.ts +0 -40
  2329. package/dist/core/oauth-provider-bridge.d.ts.map +0 -1
  2330. package/dist/core/oauth-provider-bridge.js +0 -169
  2331. package/dist/core/oauth-provider-bridge.js.map +0 -1
  2332. package/dist/modes/interactive/components/context-window-selector.d.ts +0 -53
  2333. package/dist/modes/interactive/components/context-window-selector.d.ts.map +0 -1
  2334. package/dist/modes/interactive/components/context-window-selector.js +0 -136
  2335. package/dist/modes/interactive/components/context-window-selector.js.map +0 -1
  2336. package/dist/modes/rpc/bounded-writer.d.ts +0 -27
  2337. package/dist/modes/rpc/bounded-writer.d.ts.map +0 -1
  2338. package/dist/modes/rpc/bounded-writer.js +0 -103
  2339. package/dist/modes/rpc/bounded-writer.js.map +0 -1
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  BORDER_SAFE_TAGS,
3
+ EM_DASH_CHARS_PER_DASH,
4
+ EM_DASH_FLOOR,
3
5
  GENERIC_FONTS,
4
6
  KNOWN_SERIF_FONTS,
5
7
  OVERUSED_FONTS,
@@ -25,8 +27,15 @@ const DETECTOR_IS_BROWSER = typeof window !== 'undefined';
25
27
 
26
28
  // ─── Section 3: Pure Detection ──────────────────────────────────────────────
27
29
 
28
- function checkBorders(tag, widths, colors, radius) {
29
- if (BORDER_SAFE_TAGS.has(tag)) return [];
30
+ function checkBorders(tag, widths, colors, radius, opts = {}) {
31
+ // Badge-shaped <span>s (own visible background) are a real stripe target
32
+ // for the top/bottom variant — the inline-tag exemption exists to quiet
33
+ // text-level borders, not chips. They skip the left/right arms below.
34
+ const spanBadge = tag === 'span' && !!opts.badgeLike;
35
+ if (BORDER_SAFE_TAGS.has(tag) && !spanBadge) return [];
36
+ // A live status/alert region wears a colored single-edge border as a
37
+ // severity accent (toast, snackbar, callout), not as the side-tab tell.
38
+ if (opts.statusContext) return [];
30
39
  const findings = [];
31
40
  const sides = ['Top', 'Right', 'Bottom', 'Left'];
32
41
 
@@ -42,10 +51,20 @@ function checkBorders(tag, widths, colors, radius) {
42
51
  const isSide = side === 'Left' || side === 'Right';
43
52
 
44
53
  if (isSide) {
54
+ if (spanBadge) continue;
45
55
  if (radius > 0) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` });
46
56
  else if (w >= 3) findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px` });
47
57
  } else {
48
58
  if (radius > 0 && w >= 2) findings.push({ id: 'border-accent-on-rounded', snippet: `border-${sn}: ${w}px + border-radius: ${radius}px` });
59
+ // Horizontal variant of the side-tab stripe: a thick chromatic accent
60
+ // riding the top or bottom edge of a card/badge/container. Same
61
+ // dominant-edge + chroma gates as left/right, 3-12px band. Selected-
62
+ // tab underlines are exempt via opts.tabContext (adapters look for
63
+ // tablist/nav/tab ancestors and aria-selected); links, buttons,
64
+ // table cells, and <hr> never reach here (BORDER_SAFE_TAGS).
65
+ else if (!opts.tabContext && w >= 3 && w <= 12) {
66
+ findings.push({ id: 'side-tab', snippet: `border-${sn}: ${w}px` });
67
+ }
49
68
  }
50
69
  }
51
70
 
@@ -67,23 +86,45 @@ function isEmojiOnlyText(text) {
67
86
  function checkColors(opts) {
68
87
  const { tag, textColor, bgColor, effectiveBg, effectiveBgStops, fontSize, fontWeight, hasDirectText, isEmojiOnly, bgClip, bgImage, classList } = opts;
69
88
  if (SAFE_TAGS.has(tag)) {
70
- // Exception for <a> and <button> elements styled as buttons. SAFE_TAGS
71
- // exists to suppress contrast noise on inline links and unstyled controls,
72
- // where the element has no own background and the contrast against the
73
- // ancestor surface is already the intended visual. When the element has
74
- // its own opaque background and direct text, it is a styled button and
75
- // contrast on its own surface is a real, frequent bug worth flagging.
76
- const isStyledButton = (tag === 'a' || tag === 'button')
77
- && hasDirectText
78
- && bgColor && bgColor.a > 0.5;
79
- if (!isStyledButton) return [];
89
+ // Exception for elements styled as controls or chips. SAFE_TAGS exists to
90
+ // suppress contrast noise on inline links and unstyled spans, where the
91
+ // element has no own background and the contrast against the ancestor
92
+ // surface is already the intended visual. When the element paints its own
93
+ // opaque background under direct text, it is a styled button, chip, or
94
+ // badge regardless of tag, and contrast on its own surface is a real,
95
+ // frequent bug worth flagging. (The shipped miss: a <span> severity chip
96
+ // whose white text lost a specificity fight and rendered muted-on-red at
97
+ // 1.2:1; the old a/button-only exception never looked at it.) The 9px
98
+ // font floor keeps sub-text decorations out.
99
+ const isStyledControl = hasDirectText
100
+ && ((bgColor && bgColor.a > 0.5)
101
+ // A gradient painted on the element itself is an own surface the
102
+ // same way a solid background is. Without this branch a nav CTA
103
+ // built as `<a>` with `background: linear-gradient(…)` and a text
104
+ // color that fails against every stop sails through on the
105
+ // SAFE_TAGS suppression (the shipped escape).
106
+ || (bgImage && /gradient/i.test(bgImage)))
107
+ && fontSize >= 9;
108
+ if (!isStyledControl) return [];
80
109
  }
81
110
  const findings = [];
82
111
 
83
112
  if (hasDirectText && textColor && !isEmojiOnly) {
113
+ // Gradient-clipped text (`background-clip: text`, typically with a
114
+ // transparent text-fill) paints its glyphs *with* the element's own
115
+ // gradient. The `color` value the cascade still reports is never painted,
116
+ // and the gradient is the fill, not a backdrop — so measuring `color`
117
+ // against that gradient (which resolveGradientStops picks up as the
118
+ // element's own background-image) is a guaranteed false positive
119
+ // (issue #409 Case A). Skip the backdrop-contrast checks; the gradient-text
120
+ // rule below still flags the pattern itself. Skipping a rule beats a false
121
+ // positive here — the true painted contrast can't be measured from `color`.
122
+ const isGradientClippedText = bgClip === 'text';
84
123
  // Run background-dependent checks against either a solid bg or, if the
85
124
  // ancestor is a gradient, against every gradient stop (use the worst case).
86
- const bgs = effectiveBg ? [effectiveBg] : (effectiveBgStops && effectiveBgStops.length ? effectiveBgStops : null);
125
+ const bgs = isGradientClippedText
126
+ ? null
127
+ : (effectiveBg ? [effectiveBg] : (effectiveBgStops && effectiveBgStops.length ? effectiveBgStops : null));
87
128
  if (bgs) {
88
129
  // Gray on colored background — flag if every stop is chromatic
89
130
  const textLum = relativeLuminance(textColor);
@@ -113,7 +154,11 @@ function checkColors(opts) {
113
154
  // like `text-paper/60` on `bg-ink` sections are the FP pattern.
114
155
  const isAlphaFallbackFP = !DETECTOR_IS_BROWSER && !effectiveBg && (textColor.a != null && textColor.a < 1);
115
156
  if (!isAlphaFallbackFP) {
116
- findings.push({ id: 'low-contrast', snippet: `${ratio.toFixed(1)}:1 (need ${threshold}:1) text ${colorToHex(textColor)} on ${colorToHex(bgs[worstIdx])}` });
157
+ // Near-threshold ratios (e.g. 4.497) would round to the threshold
158
+ // itself at one decimal and read as "4.5 needs 4.5" — show two
159
+ // decimals there so the finding stays legible.
160
+ const ratioLabel = ratio.toFixed(1) === threshold.toFixed(1) ? ratio.toFixed(2) : ratio.toFixed(1);
161
+ findings.push({ id: 'low-contrast', snippet: `${ratioLabel}:1 (need ${threshold}:1) — text ${colorToHex(textColor)} on ${colorToHex(bgs[worstIdx])}` });
117
162
  }
118
163
  }
119
164
  }
@@ -159,6 +204,27 @@ function checkColors(opts) {
159
204
  return findings;
160
205
  }
161
206
 
207
+ // WCAG contrast for the :hover state of an element whose hover rules change
208
+ // its text color and/or background. The classic miss: a nav CTA whose
209
+ // author-intended hover pair passes AA, but a broader selector (e.g.
210
+ // `.nav-links a:hover`) wins the specificity fight and swaps in a color
211
+ // that fails. Only fires on elements that present as styled controls —
212
+ // direct text plus an opaque-ish own background in either state — so plain
213
+ // inline links keep the same suppression they get in checkColors.
214
+ function checkHoverContrast(opts) {
215
+ const { tag, textColor, bg, ownBgAlpha, fontSize, fontWeight, hasDirectText, isEmojiOnly } = opts;
216
+ if (!hasDirectText || isEmojiOnly || !textColor || !bg) return [];
217
+ if (SAFE_TAGS.has(tag) && !(ownBgAlpha != null && ownBgAlpha > 0.5)) return [];
218
+ const ratio = contrastRatio(textColor, bg);
219
+ const isLargeText = fontSize >= WCAG_LARGE_TEXT_PX || (fontSize >= WCAG_LARGE_BOLD_TEXT_PX && fontWeight >= 700);
220
+ const threshold = isLargeText ? 3.0 : 4.5;
221
+ if (ratio >= threshold) return [];
222
+ return [{
223
+ id: 'low-contrast',
224
+ snippet: `:hover state ${ratio.toFixed(1)}:1 (need ${threshold}:1) — text ${colorToHex(textColor)} on ${colorToHex(bg)}`,
225
+ }];
226
+ }
227
+
162
228
  function isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg) {
163
229
  if (!hasShadow && !hasBorder) return false;
164
230
  return hasRadius || hasBg;
@@ -304,26 +370,57 @@ function isAccentColor(cssColor) {
304
370
  return false;
305
371
  }
306
372
 
373
+ function resolveHeroHeadingSizePx(value) {
374
+ const input = String(value || '').trim().toLowerCase();
375
+ if (!input) return 0;
376
+
377
+ const simpleLengthPx = (token) => {
378
+ const match = /^(-?\d*\.?\d+)\s*(px|rem|em|%)?$/.exec(String(token || '').trim());
379
+ if (!match) return null;
380
+ const amount = Number(match[1]);
381
+ if (!Number.isFinite(amount)) return null;
382
+ if (match[2] === 'rem' || match[2] === 'em') return amount * 16;
383
+ if (match[2] === '%') return amount * 0.16;
384
+ return amount;
385
+ };
386
+
387
+ const direct = simpleLengthPx(input);
388
+ if (direct !== null) return direct;
389
+
390
+ // Static CSS engines cannot resolve viewport units, but clamp's min/max
391
+ // bounds still tell us whether the heading can ever reach hero scale.
392
+ const clamp = /^clamp\((.*)\)$/.exec(input);
393
+ if (clamp) {
394
+ const parts = clamp[1].split(',');
395
+ if (parts.length === 3) {
396
+ const bounds = [simpleLengthPx(parts[0]), simpleLengthPx(parts[2])]
397
+ .filter((candidate) => candidate !== null);
398
+ if (bounds.length > 0) return Math.max(...bounds);
399
+ }
400
+ }
401
+
402
+ return 0;
403
+ }
404
+
307
405
  // Sibling-relationship rule. Anchor on a hero-scale h1, look at the
308
406
  // previousElementSibling, and gate on EITHER the classic tracked-
309
407
  // uppercase eyebrow OR the modern accent-colored bold eyebrow.
310
408
  function checkHeroEyebrow(opts) {
311
409
  const {
312
410
  headingTag, headingText, headingFontSize,
411
+ headingInApplicationContext,
313
412
  siblingTag, siblingText, siblingTextTransform,
314
413
  siblingFontSize, siblingLetterSpacing,
315
414
  siblingFontWeight, siblingColor,
415
+ siblingHasAccentDashPseudo,
316
416
  } = opts;
317
417
  if (headingTag !== 'h1') return [];
318
- // We previously gated on headingFontSize >= 48 to anchor "hero scale".
319
- // But modern hero h1s use clamp() / vw / var(--text-*), none of which
320
- // jsdom can resolve the computed value comes back as "2em" or
321
- // "var(--text-9xl)" and parseFloat returns 2 or NaN. The gate fails
322
- // on virtually every Tailwind v4 / framework build. The other gates
323
- // (sibling text 2-60 chars, font-size ≤ 14px, accent-bold OR
324
- // tracked-caps) are tight enough to avoid false positives on non-
325
- // hero h1s — a tiny tan label directly above any h1 is the
326
- // antipattern regardless of how big the h1 ends up.
418
+ // This is specifically a marketing-hero cliché, not a ban on compact
419
+ // context labels in product UI (for example, a station name inside a tab
420
+ // panel). Browser-computed sizes are reliable; the static adapter also
421
+ // resolves ordinary px/rem/em and clamp() bounds before reaching here.
422
+ if (headingInApplicationContext) return [];
423
+ if (!(headingFontSize >= 48)) return [];
327
424
  if (!siblingTag) return [];
328
425
  // An h2 above an h1 is a different anti-pattern (heading hierarchy / dual
329
426
  // headings) — never an eyebrow.
@@ -344,23 +441,31 @@ function checkHeroEyebrow(opts) {
344
441
  const weight = Number(siblingFontWeight) || 400;
345
442
  const isAccentBold = weight >= 700 && isAccentColor(siblingColor || '');
346
443
 
347
- if (!isClassicTracked && !isAccentBold) return [];
444
+ // Branch C: dash-prefix eyebrow — sentence case, low tracking, regular
445
+ // weight, but announced by a short chromatic ::before/::after bar
446
+ // (the kicker dash). Same label-above-headline pattern, third styling.
447
+ const isDashPrefixed = !!siblingHasAccentDashPseudo;
448
+
449
+ if (!isClassicTracked && !isAccentBold && !isDashPrefixed) return [];
348
450
 
349
451
  const headingTextSnippet = (headingText || '').trim().slice(0, 60);
350
452
  const eyebrowSnippet = text.slice(0, 40);
351
- const style = isClassicTracked ? 'tracked-caps' : 'accent-bold';
453
+ const style = isClassicTracked ? 'tracked-caps' : isAccentBold ? 'accent-bold' : 'dash-prefix';
352
454
  return [{
353
455
  id: 'hero-eyebrow-chip',
354
456
  snippet: `eyebrow chip (${style}) "${eyebrowSnippet}" above ${headingTag} "${headingTextSnippet}"`,
355
457
  }];
356
458
  }
357
459
 
358
- function checkRepeatedSectionKickers(opts) {
359
- const { candidates, minCount = 3 } = opts;
360
- if (!Array.isArray(candidates) || candidates.length < minCount) return [];
460
+ // Outright ban: one kicker is one too many, so every collected candidate is
461
+ // a finding. The judgment lives in the candidate gate (isKickerCandidate) and
462
+ // the collector's context skips, not in a repetition count.
463
+ function checkKickerAboveHeading(opts) {
464
+ const { candidates } = opts;
465
+ if (!Array.isArray(candidates)) return [];
361
466
  return candidates.map(candidate => ({
362
- id: 'repeated-section-kickers',
363
- snippet: `repeated section kicker "${candidate.kickerText}" before ${candidate.headingTag} "${candidate.headingText}" (${candidates.length} on page)`,
467
+ id: 'kicker-above-heading',
468
+ snippet: `kicker "${candidate.kickerText}" above ${candidate.headingTag} "${candidate.headingText}"`,
364
469
  }));
365
470
  }
366
471
 
@@ -409,38 +514,882 @@ function checkMotion(opts) {
409
514
  return findings;
410
515
  }
411
516
 
517
+ // Locate the color token in a single shadow layer. Returns
518
+ // { color, start, end } where color is the parsed {r,g,b,a} (null when the
519
+ // token exists but can't be parsed — e.g. an unresolved var() or an exotic
520
+ // color space), or null when no color token is present at all. Handles both
521
+ // serialization orders: computed style puts the color first
522
+ // ("rgb(…) 0px 0px 20px"), authored CSS usually puts it last
523
+ // ("0 0 20px #3b82f6").
524
+ function findShadowColor(layer) {
525
+ const fn = layer.match(/(?:rgba?|hsla?|hwb|oklch|oklab|lch|lab|color)\([^)]*\)/i);
526
+ if (fn) return { color: parseAnyColor(fn[0]), start: fn.index, end: fn.index + fn[0].length };
527
+ const hex = layer.match(/#[0-9a-fA-F]{3,8}\b/);
528
+ if (hex) return { color: parseAnyColor(hex[0]), start: hex.index, end: hex.index + hex[0].length };
529
+ const wordRe = /[a-zA-Z][a-zA-Z]*/g;
530
+ let m;
531
+ while ((m = wordRe.exec(layer)) !== null) {
532
+ const named = CSS_NAMED_COLORS[m[0].toLowerCase()];
533
+ if (named) return { color: { ...named, a: 1 }, start: m.index, end: m.index + m[0].length };
534
+ }
535
+ return null;
536
+ }
537
+
538
+ // Extract the length values of a shadow layer in declaration order, with the
539
+ // color token removed so its components aren't misread as lengths. Handles
540
+ // computed-style px values AND authored unitless zeros ("0 0 20px"); rem/em
541
+ // approximate at 16px. Result order is offset-x, offset-y, blur, [spread].
542
+ function extractShadowLengths(layer, colorStart, colorEnd) {
543
+ const stripped = colorStart != null
544
+ ? layer.slice(0, colorStart) + ' ' + layer.slice(colorEnd)
545
+ : layer;
546
+ const vals = [];
547
+ const re = /(-?\d*\.?\d+)(px|rem|em)?/g;
548
+ let m;
549
+ while ((m = re.exec(stripped)) !== null) {
550
+ let v = parseFloat(m[1]);
551
+ if (m[2] === 'rem' || m[2] === 'em') v *= 16;
552
+ vals.push(v);
553
+ }
554
+ return vals;
555
+ }
556
+
412
557
  function checkGlow(opts) {
413
- const { boxShadow, effectiveBg } = opts;
414
- if (!boxShadow || boxShadow === 'none') return [];
415
- if (!effectiveBg) return [];
558
+ const { boxShadow, textShadow, effectiveBg } = opts;
559
+ const onDarkBg = effectiveBg ? relativeLuminance(effectiveBg) < 0.1 : false;
560
+
561
+ // Scan one shadow list. Two glow tells, in any color format:
562
+ // 1. Zero-offset chromatic halo (0 0 Npx <color>) — slop on ANY
563
+ // background; the light radiates evenly outward, which is never how
564
+ // real elevation shadows behave. Achromatic zero-offset shadows stay
565
+ // legal (soft ambient elevation), as do focus rings (blur 0).
566
+ // 2. Any chromatic shadow with real blur on a dark background — the
567
+ // classic dark-mode glow accent.
568
+ const scan = (value, prop) => {
569
+ if (!value || value === 'none') return null;
570
+ // Split multiple shadows (commas not inside parentheses)
571
+ for (const layer of value.split(/,(?![^(]*\))/)) {
572
+ const colorInfo = findShadowColor(layer);
573
+ // No color token, or one we can't resolve (unresolved var(), exotic
574
+ // color space): don't guess — skip rather than false-positive.
575
+ if (!colorInfo || !colorInfo.color) continue;
576
+ const color = colorInfo.color;
577
+ if (!hasChroma(color, 30)) continue;
578
+ const vals = extractShadowLengths(layer, colorInfo.start, colorInfo.end);
579
+ // Third value is blur (offset-x, offset-y, blur, [spread])
580
+ if (vals.length < 3 || vals[2] <= 4) continue;
581
+ if (vals[0] === 0 && vals[1] === 0) {
582
+ return { id: 'dark-glow', snippet: `Zero-offset ${prop} glow (${colorToHex(color)})` };
583
+ }
584
+ if (onDarkBg) {
585
+ return { id: 'dark-glow', snippet: `Colored ${prop} glow (${colorToHex(color)}) on dark background` };
586
+ }
587
+ }
588
+ return null;
589
+ };
590
+
591
+ const found = scan(boxShadow, 'box-shadow') || scan(textShadow, 'text-shadow');
592
+ return found ? [found] : [];
593
+ }
416
594
 
417
- // Only flag on dark backgrounds (luminance < 0.1)
418
- const bgLum = relativeLuminance(effectiveBg);
419
- if (bgLum >= 0.1) return [];
595
+ // Collect CSS custom property declarations from raw stylesheet/HTML text.
596
+ // First declaration wins (:root declarations usually come first); good
597
+ // enough for the single-level var() resolution the text engines need.
598
+ function collectCssCustomProps(content) {
599
+ const map = new Map();
600
+ const re = /(--[\w-]+)\s*:\s*([^;{}]+)/g;
601
+ let m;
602
+ while ((m = re.exec(content)) !== null) {
603
+ if (!map.has(m[1])) map.set(m[1], m[2].trim());
604
+ }
605
+ return map;
606
+ }
420
607
 
421
- // Split multiple shadows (commas not inside parentheses)
422
- const parts = boxShadow.split(/,(?![^(]*\))/);
423
- for (const shadow of parts) {
424
- const colorMatch = shadow.match(/rgba?\([^)]+\)/);
425
- if (!colorMatch) continue;
426
- const color = parseRgb(colorMatch[0]);
427
- if (!color || !hasChroma(color, 30)) continue;
608
+ // Text-level glow scan shared by the regex engine and the page-level HTML
609
+ // pattern pass. Resolves single-level var() refs against custom properties
610
+ // collected from the same text, then applies the same two glow tells as
611
+ // checkGlow: zero-offset chromatic halo (any background) and chromatic
612
+ // blurred shadow when the page has a dark background. Returns
613
+ // [{ index, snippet }] — index is the offset of the shadow declaration.
614
+ // Dark-page heuristic for raw CSS/HTML text: dark hex/rgb literals, Tailwind
615
+ // dark bg utilities, or a ROOT-scoped (body/html/:root or <body style>)
616
+ // background that resolves — via var() — to a dark color. The var/modern-
617
+ // color extension is deliberately root-scoped: a light page with one dark
618
+ // accent chip must not turn every tinted drop shadow into a "dark page"
619
+ // signal. Shared by the glow and radial-halo text scanners.
620
+ function cssTextHasDarkRootBg(content, customProps) {
621
+ const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/i;
622
+ const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
623
+ if (darkBgRe.test(content) || twDarkBg.test(content)) return true;
624
+ const rootScopes = [];
625
+ const blockRe = /(?:^|[}\s,;>])(?:body|html|:root)\s*(?:,[^{]*)?\{([^}]*)\}/gi;
626
+ let sm;
627
+ while ((sm = blockRe.exec(content)) !== null) rootScopes.push(sm[1]);
628
+ const inlineBody = content.match(/<body[^>]*\bstyle\s*=\s*"([^"]*)"/i);
629
+ if (inlineBody) rootScopes.push(inlineBody[1]);
630
+ for (const scope of rootScopes) {
631
+ const bgRe = /background(?:-color)?\s*:\s*([^;{}]+)/gi;
632
+ let bm;
633
+ while ((bm = bgRe.exec(scope)) !== null) {
634
+ const c = parseAnyColor(resolveVarRefs(bm[1].trim(), customProps));
635
+ if (c && (c.a ?? 1) > 0.5 && relativeLuminance(c) < 0.1) return true;
636
+ }
637
+ }
638
+ return false;
639
+ }
428
640
 
429
- // Extract px values — in computed style: "color Xpx Ypx BLURpx [SPREADpx]"
430
- const afterColor = shadow.substring(shadow.indexOf(colorMatch[0]) + colorMatch[0].length);
431
- const beforeColor = shadow.substring(0, shadow.indexOf(colorMatch[0]));
432
- const pxVals = [...beforeColor.matchAll(/([\d.]+)px/g), ...afterColor.matchAll(/([\d.]+)px/g)]
433
- .map(m => parseFloat(m[1]));
641
+ function scanCssTextForGlow(content) {
642
+ const customProps = collectCssCustomProps(content);
643
+ const hasDarkBg = cssTextHasDarkRootBg(content, customProps);
434
644
 
435
- // Third value is blur (offset-x, offset-y, blur, [spread])
436
- if (pxVals.length >= 3 && pxVals[2] > 4) {
437
- return [{ id: 'dark-glow', snippet: `Colored glow (${colorToHex(color)}) on dark background` }];
645
+ const results = [];
646
+ const shadowRe = /\b(box-shadow|text-shadow)\s*:\s*([^;{}]+)/gi;
647
+ let m;
648
+ while ((m = shadowRe.exec(content)) !== null) {
649
+ const prop = m[1].toLowerCase();
650
+ const value = resolveVarRefs(m[2].trim(), customProps);
651
+ for (const layer of value.split(/,(?![^(]*\))/)) {
652
+ const colorInfo = findShadowColor(layer);
653
+ if (!colorInfo || !colorInfo.color || !hasChroma(colorInfo.color, 30)) continue;
654
+ const vals = extractShadowLengths(layer, colorInfo.start, colorInfo.end);
655
+ if (vals.length < 3 || vals[2] <= 4) continue;
656
+ const zeroOffset = vals[0] === 0 && vals[1] === 0;
657
+ if (!zeroOffset && !hasDarkBg) continue;
658
+ results.push({
659
+ index: m.index,
660
+ snippet: zeroOffset
661
+ ? `Zero-offset ${prop} glow (${colorToHex(colorInfo.color)})`
662
+ : `Colored ${prop} glow (${colorToHex(colorInfo.color)}) on dark page`,
663
+ });
664
+ break; // one finding per declaration
438
665
  }
439
666
  }
667
+ return results;
668
+ }
440
669
 
670
+ // Decorative grid or line-field backgrounds drawn with hairline
671
+ // linear-gradient layers tiled by a fixed pixel cell. Shared by the HTML
672
+ // pattern pass and the regex source engine so standalone CSS, component
673
+ // styles, and inline styles receive the same coverage. Both signals must
674
+ // co-occur in one declaration block; unrelated rules must not add up across
675
+ // the file. Returns [{ index, snippet }], capped at one finding per source to
676
+ // match the page-level HTML check's existing behavior.
677
+ function scanCssTextForGridBackground(content) {
678
+ const hairlineRe = /\b\d{1,3}px\s*,\s*transparent\s+\d{1,3}px/gi;
679
+ const invertedHairlineRe = /transparent\s+calc\(100%\s*-\s*\d{1,3}px\)/gi;
680
+ const sizeDeclPxRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\b/i;
681
+ const sizeDeclPxPairRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\s+\d{1,3}px/i;
682
+ const shorthandPxAnyRe = /\/\s*\d{1,3}px\b/;
683
+ const shorthandPxPairRe = /\/\s*\d{1,3}px\s+\d{1,3}px/;
684
+ const bgDeclRe = /\bbackground(?:-image)?\s*:\s*([^;{}"']*)/gi;
685
+ const blockRe = /\{([^{}]*)\}|style\s*=\s*"([^"]*)"|style\s*=\s*'([^']*)'/gi;
686
+ let blk;
687
+ while ((blk = blockRe.exec(content)) !== null) {
688
+ const block = blk[1] || blk[2] || blk[3] || '';
689
+ let hairlineCount = 0;
690
+ let bgJoined = '';
691
+ let bm;
692
+ bgDeclRe.lastIndex = 0;
693
+ while ((bm = bgDeclRe.exec(block)) !== null) {
694
+ hairlineCount += (bm[1].match(hairlineRe) || []).length;
695
+ hairlineCount += (bm[1].match(invertedHairlineRe) || []).length;
696
+ bgJoined += `${bm[1]};`;
697
+ }
698
+ if (hairlineCount === 0) continue;
699
+ const hasPxCell = sizeDeclPxRe.test(block) || shorthandPxAnyRe.test(bgJoined);
700
+ const hasPxPairCell = sizeDeclPxPairRe.test(block) || shorthandPxPairRe.test(bgJoined);
701
+ if ((hairlineCount >= 2 && hasPxCell) || hasPxPairCell) {
702
+ return [{
703
+ index: blk.index,
704
+ snippet: hairlineCount >= 2
705
+ ? 'two-axis grid-line gradient background'
706
+ : 'px-tiled hairline line-field background',
707
+ }];
708
+ }
709
+ }
441
710
  return [];
442
711
  }
443
712
 
713
+ // Decorative chromatic halo drawn as a radial-gradient background on a dark
714
+ // page: a saturated center stop dissolving to transparent. The gradient
715
+ // sibling of the dark-glow shadow tell. Mechanical gates, in order:
716
+ // * page has a dark root background (shared heuristic with the glow scan)
717
+ // * declaration has no url() layer (photographic imagery is exempt)
718
+ // * the gradient's first color stop is chromatic (RGB spread >= 24) and
719
+ // visible (alpha >= 0.7 — deliberately translucent light-scene washes
720
+ // composite with content instead of painting a flat halo, and stay legal)
721
+ // * the gradient's last stop is transparent / near-zero alpha
722
+ // * no small pixel-sized stop positions (<= 24px = dot/texture patterns)
723
+ // * not a repeating-* gradient
724
+ // Achromatic vignettes fail the chroma gate; panel sheens that fade to an
725
+ // opaque surface color fail the transparent-end gate.
726
+ function scanCssTextForRadialHalo(content) {
727
+ const customProps = collectCssCustomProps(content);
728
+ if (!cssTextHasDarkRootBg(content, customProps)) return [];
729
+
730
+ const findings = [];
731
+ const seen = new Set();
732
+ const declRe = /background(?:-image)?\s*:\s*([^;{}]+)/gi;
733
+ let m;
734
+ while ((m = declRe.exec(content)) !== null) {
735
+ const value = resolveVarRefs(m[1].trim(), customProps);
736
+ if (/url\s*\(/i.test(value)) continue;
737
+
738
+ const gradRe = /(repeating-)?radial-gradient\(/gi;
739
+ let g;
740
+ while ((g = gradRe.exec(value)) !== null) {
741
+ if (g[1]) continue; // repeating-* = pattern, not halo
742
+ // Balanced-paren capture of the gradient arguments.
743
+ let depth = 0, end = -1;
744
+ const open = value.indexOf('(', g.index);
745
+ for (let i = open; i < value.length; i++) {
746
+ if (value[i] === '(') depth++;
747
+ else if (value[i] === ')') { depth--; if (depth === 0) { end = i; break; } }
748
+ }
749
+ if (end < 0) break;
750
+ const args = splitTopLevelCommas(value.slice(open + 1, end));
751
+ if (args.length < 2) continue;
752
+
753
+ // Optional prelude (shape / size / `at <pos>`) carries no color.
754
+ const colorTokenRe = /(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color-mix)\([^)]*(?:\([^)]*\))?[^)]*\)|#[0-9a-f]{3,8}\b|\btransparent\b/i;
755
+ const stops = args.filter(a => colorTokenRe.test(a));
756
+ if (stops.length < 2) continue;
757
+
758
+ // Dot/texture exemption: px-sized stop positions mean a repeating
759
+ // background-size pattern, not a page-scale halo.
760
+ const pxStop = stops.some(s => {
761
+ const pm = s.match(/(-?[\d.]+)px\b/);
762
+ return pm && Math.abs(parseFloat(pm[1])) <= 24;
763
+ });
764
+ if (pxStop) continue;
765
+
766
+ const first = stops[0].match(colorTokenRe);
767
+ const last = stops[stops.length - 1].match(colorTokenRe);
768
+ if (!first || !last) continue;
769
+
770
+ const lastColor = /^transparent$/i.test(last[0]) ? { r: 0, g: 0, b: 0, a: 0 } : parseAnyColor(last[0]);
771
+ if (!lastColor || (lastColor.a ?? 1) > 0.05) continue;
772
+
773
+ const firstColor = /^transparent$/i.test(first[0]) ? null : parseAnyColor(first[0]);
774
+ if (!firstColor) continue;
775
+ if ((firstColor.a ?? 1) < 0.7) continue;
776
+ const spread = Math.max(firstColor.r, firstColor.g, firstColor.b) - Math.min(firstColor.r, firstColor.g, firstColor.b);
777
+ if (spread < 24) continue;
778
+
779
+ const snippet = `radial-gradient halo (${colorToHex(firstColor)} → transparent) on dark page`;
780
+ if (seen.has(snippet)) continue;
781
+ seen.add(snippet);
782
+ findings.push({ index: m.index, snippet });
783
+ }
784
+ }
785
+ return findings;
786
+ }
787
+
788
+ // ---------------------------------------------------------------------------
789
+ // Text-level CSS rule-block scanners (pseudo-element stripes, pulsing dots)
790
+ // ---------------------------------------------------------------------------
791
+
792
+ // Iterate `selector { declarations }` pairs in raw CSS/HTML text. The block
793
+ // body excludes braces, so nested structures (@media, @keyframes) naturally
794
+ // yield their innermost rules with the innermost selector text. Callers
795
+ // create the regex locally — a shared /g instance is not re-entrant.
796
+ const CSS_RULE_BLOCK_SOURCE = String.raw`([^{};]+)\{([^{}]*)\}`;
797
+
798
+ // Parse a declaration block into a prop → value map (last declaration wins,
799
+ // approximating the cascade inside one block). Values keep their raw text
800
+ // with any !important suffix stripped.
801
+ function parseCssDeclBlock(block) {
802
+ const decls = new Map();
803
+ for (const part of String(block || '').split(';')) {
804
+ const idx = part.indexOf(':');
805
+ if (idx <= 0) continue;
806
+ const prop = part.slice(0, idx).trim().toLowerCase();
807
+ const value = part.slice(idx + 1).replace(/\s*!important\s*$/i, '').trim();
808
+ if (prop && value) decls.set(prop, value);
809
+ }
810
+ return decls;
811
+ }
812
+
813
+ function cssLengthToPx(value) {
814
+ const m = String(value || '').trim().match(/^(-?[\d.]+)(px|rem|em)$/i);
815
+ if (!m) return null;
816
+ const n = parseFloat(m[1]);
817
+ return m[2].toLowerCase() === 'px' ? n : n * 16;
818
+ }
819
+
820
+ function isZeroOffset(value) {
821
+ return value != null && /^-?0(?:px|%|rem|em)?$/.test(String(value).trim());
822
+ }
823
+
824
+ // Side-tab variant: the accent stripe drawn as an absolutely-positioned
825
+ // ::before/::after pseudo-element (narrow colored box hugging a vertical
826
+ // edge) instead of a border-left/right. The element-level border checks
827
+ // never see it — pseudo-elements aren't part of the DOM the cascade walks —
828
+ // so this scans stylesheet text directly, mirroring the border rule's
829
+ // gates: >= 3px thick, chromatic fill, full height against a side edge.
830
+ function scanCssTextForPseudoStripe(rawContent) {
831
+ // Blank comment bodies byte-for-byte so commented-out rules are not
832
+ // scanned as live CSS and every rule keeps its source offset (each
833
+ // finding carries `index` so line-based callers can attribute it and
834
+ // line-scoped inline ignores can match).
835
+ const content = String(rawContent || '').replace(/\/\*[\s\S]*?\*\//g,
836
+ (block) => block.replace(/[^\n]/g, ' '));
837
+ const customProps = collectCssCustomProps(content);
838
+ const findings = [];
839
+ const seen = new Set();
840
+ const ruleRe = new RegExp(CSS_RULE_BLOCK_SOURCE, 'g');
841
+ let m;
842
+ while ((m = ruleRe.exec(content)) !== null) {
843
+ const selector = m[1].trim();
844
+ if (!/::?(?:before|after)\b/i.test(selector)) continue;
845
+ // Keep the border rule's prose exemptions (blockquote bars etc.).
846
+ if (/\b(?:blockquote|pre|code|nav|hr)\b/i.test(selector)) continue;
847
+ const decls = parseCssDeclBlock(m[2]);
848
+ const position = decls.get('position');
849
+ if (position !== 'absolute' && position !== 'fixed') continue;
850
+
851
+ const widthPx = cssLengthToPx(resolveVarRefs(
852
+ decls.get('width') || decls.get('inline-size') || '', customProps));
853
+ const heightPx = cssLengthToPx(resolveVarRefs(
854
+ decls.get('height') || decls.get('block-size') || '', customProps));
855
+ const verticalCandidate = widthPx != null && widthPx >= 3 && widthPx <= 12;
856
+ // Horizontal variant (top/bottom stripe) carries extra exemptions:
857
+ // link/button underline affordances, selected-state indicators
858
+ // (aria-selected="true", aria-current, active/current/selected class
859
+ // hints), and state-conditional (:hover/:focus/...) affordances are
860
+ // not stripes. Tab-strip membership alone ([role=tab], .tabs, bare
861
+ // [aria-selected]) is NOT exempt — a stripe on every tab in the
862
+ // group is decoration; only the selected item's underline stays.
863
+ const horizontalCandidate = heightPx != null && heightPx >= 3 && heightPx <= 12
864
+ && !/(?:^|[\s>+~,(])(?:a|button|summary|tr|td|th|table|li)(?![\w-])/i.test(selector)
865
+ && !/\[aria-selected\s*[*^$|~]?=\s*["']?true/i.test(selector)
866
+ && !/\[aria-current(?!\s*[*^$|~]?=\s*["']?false)/i.test(selector)
867
+ && !/(?:^|[\s._[-])(?:active|current|selected|btn[\w-]*|button[\w-]*|link[\w-]*)(?![\w])/i.test(selector)
868
+ && !/:(?:hover|focus|focus-visible|focus-within|active|checked)\b/i.test(selector);
869
+ if (!verticalCandidate && !horizontalCandidate) continue;
870
+
871
+ // Resolve edge offsets, letting an `inset` shorthand fill the gaps.
872
+ const offsets = {
873
+ top: decls.get('top'), right: decls.get('right'),
874
+ bottom: decls.get('bottom'), left: decls.get('left'),
875
+ };
876
+ const inset = decls.get('inset');
877
+ if (inset) {
878
+ const p = inset.split(/\s+/);
879
+ const [t, r, b, l] =
880
+ p.length === 1 ? [p[0], p[0], p[0], p[0]]
881
+ : p.length === 2 ? [p[0], p[1], p[0], p[1]]
882
+ : p.length === 3 ? [p[0], p[1], p[2], p[1]]
883
+ : p;
884
+ if (offsets.top == null) offsets.top = t;
885
+ if (offsets.right == null) offsets.right = r;
886
+ if (offsets.bottom == null) offsets.bottom = b;
887
+ if (offsets.left == null) offsets.left = l;
888
+ }
889
+ if (offsets.left == null) offsets.left = decls.get('inset-inline-start');
890
+ if (offsets.right == null) offsets.right = decls.get('inset-inline-end');
891
+
892
+ const heightValue = String(resolveVarRefs(
893
+ decls.get('height') || decls.get('block-size') || '', customProps)).trim();
894
+ const widthValue = String(resolveVarRefs(
895
+ decls.get('width') || decls.get('inline-size') || '', customProps)).trim();
896
+
897
+ let edge = null;
898
+ let thicknessPx = null;
899
+ if (verticalCandidate) {
900
+ // Full-height stripes hug both corners; the "floating" variant backs
901
+ // off each end by a small inset (top/bottom a few px) so the bar
902
+ // clears the card's corners. Both read as the same side-tab accent —
903
+ // corner treatment is styling, not a different pattern.
904
+ const topPx = cssLengthToPx(resolveVarRefs(String(offsets.top ?? ''), customProps));
905
+ const bottomPx = cssLengthToPx(resolveVarRefs(String(offsets.bottom ?? ''), customProps));
906
+ const fullHeight = (isZeroOffset(offsets.top) && isZeroOffset(offsets.bottom))
907
+ || /^100(?:\.0*)?%$/.test(heightValue)
908
+ || (topPx != null && bottomPx != null
909
+ && topPx >= 0 && topPx <= 20 && bottomPx >= 0 && bottomPx <= 20);
910
+ if (fullHeight) {
911
+ edge = isZeroOffset(offsets.left) ? 'left'
912
+ : isZeroOffset(offsets.right) ? 'right' : null;
913
+ thicknessPx = widthPx;
914
+ }
915
+ }
916
+ if (!edge && horizontalCandidate) {
917
+ const fullWidth = (isZeroOffset(offsets.left) && isZeroOffset(offsets.right))
918
+ || /^100(?:\.0*)?%$/.test(widthValue);
919
+ if (fullWidth) {
920
+ edge = isZeroOffset(offsets.top) ? 'top'
921
+ : isZeroOffset(offsets.bottom) ? 'bottom' : null;
922
+ thicknessPx = heightPx;
923
+ }
924
+ }
925
+ if (!edge) continue;
926
+
927
+ // Chromatic fill only — a neutral hairline divider is not an accent
928
+ // stripe. Unresolvable colors err toward detection, matching the
929
+ // border rule's unknown-format default.
930
+ const bg = String(resolveVarRefs(
931
+ decls.get('background-color') || decls.get('background') || '', customProps)).trim();
932
+ if (!bg || /^(?:none|transparent|inherit|initial|unset|currentcolor)$/i.test(bg)) continue;
933
+ const colorToken = bg.match(/(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb)\([^)]*\)|#[0-9a-f]{3,8}\b/i);
934
+ const parsed = parseAnyColor(colorToken ? colorToken[0] : bg);
935
+ if (parsed) {
936
+ if ((parsed.a ?? 1) < 0.1) continue;
937
+ const spread = Math.max(parsed.r, parsed.g, parsed.b) - Math.min(parsed.r, parsed.g, parsed.b);
938
+ if (spread < 30) continue;
939
+ } else if (/^(?:white|black|gray|grey|silver)$/i.test(bg)) {
940
+ continue;
941
+ }
942
+
943
+ if (seen.has(selector)) continue;
944
+ seen.add(selector);
945
+ // The selector group absorbs whitespace trailing the previous rule;
946
+ // advance past it so `index` points at the selector itself.
947
+ const selectorStart = m.index + (m[1].length - m[1].trimStart().length);
948
+ findings.push({
949
+ id: 'side-tab',
950
+ snippet: `${selector} — absolute ${thicknessPx}px pseudo-element stripe (${edge}: 0)`,
951
+ index: selectorStart,
952
+ });
953
+ }
954
+ return findings;
955
+ }
956
+
957
+ // Side-tab stripe drawn as a single-edge inset box-shadow
958
+ // (x or y offset 3-12px, other axis 0, no blur/spread, chromatic color):
959
+ // paints a bar along one edge with no border property involved, so the
960
+ // element-level border checks never see it. Selection-state indicators
961
+ // are exempt — an inset stripe on [aria-current] / .active / [role=tab]
962
+ // marks the selected item; the same stripe unconditionally on every item
963
+ // is decoration and flags.
964
+ function scanCssTextForInsetStripe(content) {
965
+ const customProps = collectCssCustomProps(content);
966
+ const findings = [];
967
+ const seen = new Set();
968
+ const ruleRe = new RegExp(CSS_RULE_BLOCK_SOURCE, 'g');
969
+ let m;
970
+ while ((m = ruleRe.exec(content)) !== null) {
971
+ const selector = m[1].trim();
972
+ // Selection-state contexts: current-item markers and interaction
973
+ // states. Tab-strip membership alone ([role=tab], .tabs, bare
974
+ // [aria-selected]) is NOT exempt — a stripe on every tab in the
975
+ // group is decoration; only the selected item's indicator stays.
976
+ if (/:(?:hover|focus|focus-visible|focus-within|active|checked|target)\b/i.test(selector)) continue;
977
+ if (/\[aria-selected\s*[*^$|~]?=\s*["']?true/i.test(selector)) continue;
978
+ if (/\[aria-current(?!\s*[*^$|~]?=\s*["']?false)/i.test(selector)) continue;
979
+ if (/(?:^|[\s._[-])(?:active|current|selected)(?![\w])/i.test(selector)) continue;
980
+ // Structural tags where a single-edge inset shadow is depth/quoting,
981
+ // not an accent stripe.
982
+ if (/(?:^|[\s>+~,(])(?:button|hr|tr|td|th|table|blockquote|pre|code)(?![\w-])/i.test(selector)) continue;
983
+
984
+ const decls = parseCssDeclBlock(m[2]);
985
+ const shadow = decls.get('box-shadow');
986
+ if (!shadow || !/\binset\b/i.test(shadow)) continue;
987
+ // Narrow fixed-width elements (logo marks, icon glyphs) use inset
988
+ // fills as artwork, not edge stripes. Stripe targets — cards, badges,
989
+ // menu items — are wider or leave width to layout.
990
+ const declaredWidth = cssLengthToPx(resolveVarRefs(decls.get('width') || decls.get('inline-size') || '', customProps));
991
+ if (declaredWidth != null && declaredWidth <= 40) continue;
992
+ const value = resolveVarRefs(shadow, customProps);
993
+ for (const layer of value.split(/,(?![^(]*\))/)) {
994
+ if (!/\binset\b/i.test(layer)) continue;
995
+ const colorInfo = findShadowColor(layer);
996
+ // Unresolvable colors (currentColor, external vars): don't guess.
997
+ if (!colorInfo || !colorInfo.color) continue;
998
+ const c = colorInfo.color;
999
+ if ((c.a ?? 1) < 0.1) continue;
1000
+ const chroma = Math.max(c.r, c.g, c.b) - Math.min(c.r, c.g, c.b);
1001
+ if (chroma < 30) continue;
1002
+ const vals = extractShadowLengths(layer, colorInfo.start, colorInfo.end);
1003
+ const x = vals[0] || 0, y = vals[1] || 0, blur = vals[2] || 0, sp = vals[3] || 0;
1004
+ if (blur !== 0 || sp !== 0) continue;
1005
+ const ax = Math.abs(x), ay = Math.abs(y);
1006
+ const isStripe = (ax >= 3 && ax <= 12 && ay === 0) || (ay >= 3 && ay <= 12 && ax === 0);
1007
+ if (!isStripe) continue;
1008
+ if (seen.has(selector)) break;
1009
+ seen.add(selector);
1010
+ const edge = ay === 0 ? (x > 0 ? 'left' : 'right') : (y > 0 ? 'top' : 'bottom');
1011
+ findings.push({
1012
+ id: 'side-tab',
1013
+ snippet: `${selector} — inset box-shadow ${ay === 0 ? ax : ay}px stripe (${edge})`,
1014
+ });
1015
+ break;
1016
+ }
1017
+ }
1018
+ return findings;
1019
+ }
1020
+
1021
+ // Collect @keyframes names whose body travels horizontally — the marquee
1022
+ // loop. X travel is measured across every translateX/translate/translate3d
1023
+ // X component in the body: a centered element animating something else
1024
+ // keeps a constant -50% X (zero travel) and never qualifies, while a
1025
+ // ticker moves from its resting position to a large offset. Keyframes
1026
+ // with a single X sample that also vary scale/opacity read as pulses or
1027
+ // breathes, not marquees.
1028
+ function collectMarqueeKeyframes(content) {
1029
+ const names = new Set();
1030
+ const re = /@(?:-webkit-)?keyframes\s+([\w-]+)\s*\{/g;
1031
+ let m;
1032
+ while ((m = re.exec(content)) !== null) {
1033
+ let depth = 1;
1034
+ let i = re.lastIndex;
1035
+ while (i < content.length && depth > 0) {
1036
+ const ch = content.charCodeAt(i);
1037
+ if (ch === 0x7b /* { */) depth++;
1038
+ else if (ch === 0x7d /* } */) depth--;
1039
+ i++;
1040
+ }
1041
+ const body = content.slice(re.lastIndex, Math.max(re.lastIndex, i - 1));
1042
+ re.lastIndex = i;
1043
+
1044
+ // Only percentage travel qualifies: a content marquee translates by a
1045
+ // fraction of its own (unknown) track width, so generated tickers use
1046
+ // -50% / -100%. Pixel-travel loops are bespoke product animations —
1047
+ // sweeping playheads, progress indicators — not marquees.
1048
+ const pct = [];
1049
+ const xRe = /\btranslate(?:X|3d)?\(\s*(-?[\d.]+)%/gi;
1050
+ let xm;
1051
+ while ((xm = xRe.exec(body)) !== null) pct.push(parseFloat(xm[1]));
1052
+ if (pct.length === 0) continue;
1053
+ if (pct.length === 1 && /\bscale\(|\bopacity\s*:/i.test(body)) continue;
1054
+ // Implicit start: a lone declared X animates from the element's
1055
+ // resting position, so its magnitude is the travel.
1056
+ const travelPct = pct.length > 1 ? Math.max(...pct) - Math.min(...pct) : Math.abs(pct[0]);
1057
+ if (travelPct >= 20) names.add(m[1]);
1058
+ }
1059
+ return names;
1060
+ }
1061
+
1062
+ // Auto-scrolling marquee: a <marquee> element, or an infinite animation
1063
+ // bound to a keyframe loop that travels a large horizontal distance.
1064
+ // Rotation/opacity animations never qualify (no X travel); JS-driven
1065
+ // carousels with user controls have no infinite CSS X-loop to match.
1066
+ function scanCssTextForMarquee(content) {
1067
+ const findings = [];
1068
+ if (/<marquee\b/i.test(content)) {
1069
+ findings.push({ id: 'marquee', snippet: '<marquee> element' });
1070
+ }
1071
+ const marqueeKeyframes = collectMarqueeKeyframes(content);
1072
+ if (marqueeKeyframes.size === 0) return findings;
1073
+ const seen = new Set();
1074
+ const ruleRe = new RegExp(CSS_RULE_BLOCK_SOURCE, 'g');
1075
+ let m;
1076
+ while ((m = ruleRe.exec(content)) !== null) {
1077
+ const selector = m[1].trim();
1078
+ const decls = parseCssDeclBlock(m[2]);
1079
+ for (const name of infiniteAnimationNames(decls)) {
1080
+ if (!marqueeKeyframes.has(name)) continue;
1081
+ const key = `${selector} ${name}`;
1082
+ if (seen.has(key)) continue;
1083
+ seen.add(key);
1084
+ findings.push({ id: 'marquee', snippet: `${selector} — infinite horizontal loop animation "${name}"` });
1085
+ }
1086
+ }
1087
+ return findings;
1088
+ }
1089
+
1090
+ // Collect @keyframes names and whether each one reads as a "pulse" —
1091
+ // i.e. it varies opacity, scale, or box-shadow. Rotation-only keyframes
1092
+ // (spinners) are explicitly not pulses.
1093
+ function collectPulseKeyframes(content) {
1094
+ const map = new Map();
1095
+ const re = /@(?:-webkit-)?keyframes\s+([\w-]+)\s*\{/g;
1096
+ let m;
1097
+ while ((m = re.exec(content)) !== null) {
1098
+ let depth = 1;
1099
+ let i = re.lastIndex;
1100
+ while (i < content.length && depth > 0) {
1101
+ const ch = content.charCodeAt(i);
1102
+ if (ch === 0x7b /* { */) depth++;
1103
+ else if (ch === 0x7d /* } */) depth--;
1104
+ i++;
1105
+ }
1106
+ const body = content.slice(re.lastIndex, Math.max(re.lastIndex, i - 1));
1107
+ const pulses = /\bopacity\s*:/i.test(body)
1108
+ || /\bbox-shadow\s*:/i.test(body)
1109
+ || /\btransform\s*:[^;{}]*\bscale/i.test(body);
1110
+ if (!map.has(m[1]) || pulses) map.set(m[1], pulses);
1111
+ re.lastIndex = i;
1112
+ }
1113
+ return map;
1114
+ }
1115
+
1116
+ const ANIMATION_VALUE_KEYWORDS = new Set([
1117
+ 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear',
1118
+ 'infinite', 'alternate', 'alternate-reverse', 'normal', 'reverse',
1119
+ 'none', 'forwards', 'backwards', 'both', 'running', 'paused',
1120
+ 'step-start', 'step-end', 'inherit', 'initial', 'unset',
1121
+ ]);
1122
+
1123
+ // Extract animation names that run with iteration-count: infinite from a
1124
+ // declaration block (shorthand layers or animation-name + iteration-count).
1125
+ function infiniteAnimationNames(decls) {
1126
+ const out = [];
1127
+ const shorthand = decls.get('animation');
1128
+ if (shorthand) {
1129
+ for (const layer of shorthand.split(/,(?![^(]*\))/)) {
1130
+ if (!/\binfinite\b/i.test(layer)) continue;
1131
+ const name = layer.split(/\s+/).find(t =>
1132
+ /^[a-zA-Z_-][\w-]*$/.test(t) && !ANIMATION_VALUE_KEYWORDS.has(t.toLowerCase()));
1133
+ if (name) out.push(name);
1134
+ }
1135
+ }
1136
+ const nameDecl = decls.get('animation-name');
1137
+ if (nameDecl && /\binfinite\b/i.test(decls.get('animation-iteration-count') || '')) {
1138
+ for (const raw of nameDecl.split(',')) {
1139
+ const t = raw.trim();
1140
+ if (t && t.toLowerCase() !== 'none') out.push(t);
1141
+ }
1142
+ }
1143
+ return out;
1144
+ }
1145
+
1146
+ function isRoundDotRadius(radiusValue, w, h) {
1147
+ if (!radiusValue) return false;
1148
+ const first = String(radiusValue).trim().split(/\s+/)[0];
1149
+ const pct = first.match(/^([\d.]+)%$/);
1150
+ if (pct) return parseFloat(pct[1]) >= 40;
1151
+ const px = cssLengthToPx(first);
1152
+ if (px == null) return false;
1153
+ return px >= 999 || px >= 0.4 * Math.min(w, h);
1154
+ }
1155
+
1156
+ // Remove @media blocks whose condition is prefers-reduced-motion: reduce.
1157
+ // Those blocks describe the accessibility fallback, not the default
1158
+ // experience that ships — an `animation: none` reset inside one must not
1159
+ // mask the resting-state animation the page plays for everyone else.
1160
+ function stripReducedMotionBlocks(content) {
1161
+ const re = /@media[^{]*prefers-reduced-motion\s*:\s*reduce[^{]*\{/gi;
1162
+ let out = '';
1163
+ let last = 0;
1164
+ let m;
1165
+ while ((m = re.exec(content)) !== null) {
1166
+ let depth = 1;
1167
+ let i = re.lastIndex;
1168
+ while (i < content.length && depth > 0) {
1169
+ const ch = content.charCodeAt(i);
1170
+ if (ch === 0x7b /* { */) depth++;
1171
+ else if (ch === 0x7d /* } */) depth--;
1172
+ i++;
1173
+ }
1174
+ out += content.slice(last, m.index);
1175
+ last = i;
1176
+ re.lastIndex = i;
1177
+ }
1178
+ return out + content.slice(last);
1179
+ }
1180
+
1181
+ // Source-index ranges of <header> and <nav> landmark elements in an HTML
1182
+ // string. Lets string-level scans decide whether a matched element sits in
1183
+ // the page chrome (the hero/nav region) without needing a DOM.
1184
+ function landmarkSourceRanges(content) {
1185
+ const ranges = [];
1186
+ for (const tag of ['header', 'nav']) {
1187
+ const re = new RegExp(`<${tag}\\b|</${tag}\\s*>`, 'gi');
1188
+ const stack = [];
1189
+ let m;
1190
+ while ((m = re.exec(content)) !== null) {
1191
+ if (m[0].charAt(1) === '/') {
1192
+ const start = stack.pop();
1193
+ if (start != null) ranges.push([start, m.index]);
1194
+ } else {
1195
+ stack.push(m.index);
1196
+ }
1197
+ }
1198
+ }
1199
+ return ranges;
1200
+ }
1201
+
1202
+ function indexInSourceRanges(index, ranges) {
1203
+ return ranges.some(([start, end]) => index >= start && index < end);
1204
+ }
1205
+
1206
+ // Does any element targeted by the final compound of `selector` appear
1207
+ // inside a header/nav landmark range of the HTML source? Resolves the last
1208
+ // .class or #id token of the selector against class/id attributes; a
1209
+ // tag-only compound is never resolvable this way and returns false
1210
+ // (conservative: no promotion without placement evidence).
1211
+ function selectorHitsLandmark(content, selector, ranges) {
1212
+ if (!ranges || ranges.length === 0) return false;
1213
+ const last = selector.split(/[\s>+~]+/).filter(Boolean).pop() || '';
1214
+ const idMatch = last.match(/#([A-Za-z_][\w-]*)/);
1215
+ const classMatch = last.match(/\.([A-Za-z_][\w-]*)/);
1216
+ let attrRe = null;
1217
+ if (idMatch) {
1218
+ const id = idMatch[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1219
+ attrRe = new RegExp(`<[a-zA-Z][^>]*\\bid\\s*=\\s*["']${id}["']`, 'gi');
1220
+ } else if (classMatch) {
1221
+ const cls = classMatch[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1222
+ attrRe = new RegExp(`<[a-zA-Z][^>]*\\bclass\\s*=\\s*["'][^"']*(?<![\\w-])${cls}(?![\\w-])[^"']*["']`, 'gi');
1223
+ }
1224
+ if (!attrRe) return false;
1225
+ let m;
1226
+ while ((m = attrRe.exec(content)) !== null) {
1227
+ if (indexInSourceRanges(m.index, ranges)) return true;
1228
+ }
1229
+ return false;
1230
+ }
1231
+
1232
+ // Small circular indicator bound to an infinite pulse animation — the
1233
+ // decorative "live" dot. Gates: tiny (<= 16px square-ish), round
1234
+ // (border-radius >= 40% or pill values), and an infinite animation whose
1235
+ // keyframes vary opacity/scale/box-shadow (or a pulse/blink/ping name when
1236
+ // the keyframes aren't in the scanned text). Rotation-only animations
1237
+ // (spinners) never flag.
1238
+ //
1239
+ // Declarations for one selector are merged across rule blocks before the
1240
+ // predicate runs: size in the base rule plus the animation added in a
1241
+ // second block (or inside a matching @media block) is the construction
1242
+ // that ships. prefers-reduced-motion: reduce overrides are stripped first
1243
+ // so their animation resets don't mask the default experience. A dot whose
1244
+ // element sits inside a header/nav landmark is the hero liveness cliché
1245
+ // and is promoted to error severity; occurrences elsewhere keep the
1246
+ // registry default severity.
1247
+ function scanCssTextForPulsingDot(content) {
1248
+ const customProps = collectCssCustomProps(content);
1249
+ const keyframes = collectPulseKeyframes(content);
1250
+ const heroRanges = landmarkSourceRanges(content);
1251
+ const findings = [];
1252
+ const seen = new Set();
1253
+
1254
+ // Merge declarations per selector across rule blocks, approximating the
1255
+ // cascade: later declarations for the same property win. Comma lists are
1256
+ // split so `.a, .b { … }` contributes to both selectors. Comments are
1257
+ // stripped first so they neither pollute selector keys nor smuggle a
1258
+ // comma into the selector-list split.
1259
+ const scanText = stripReducedMotionBlocks(content).replace(/\/\*[\s\S]*?\*\//g, ' ');
1260
+ const merged = new Map();
1261
+ const ruleRe = new RegExp(CSS_RULE_BLOCK_SOURCE, 'g');
1262
+ let m;
1263
+ while ((m = ruleRe.exec(scanText)) !== null) {
1264
+ const decls = parseCssDeclBlock(m[2]);
1265
+ if (decls.size === 0) continue;
1266
+ for (const rawSelector of m[1].split(',')) {
1267
+ const selector = rawSelector.trim();
1268
+ if (!selector || selector.startsWith('@')) continue;
1269
+ let acc = merged.get(selector);
1270
+ if (!acc) {
1271
+ acc = new Map();
1272
+ merged.set(selector, acc);
1273
+ }
1274
+ for (const [prop, value] of decls) acc.set(prop, value);
1275
+ }
1276
+ }
1277
+
1278
+ for (const [selector, decls] of merged) {
1279
+ const names = infiniteAnimationNames(decls);
1280
+ if (names.length === 0) continue;
1281
+ const pulseName = names.find(n => {
1282
+ const known = keyframes.get(n);
1283
+ if (known != null) return known;
1284
+ return /pulse|blink|ping/i.test(n);
1285
+ });
1286
+ if (!pulseName) continue;
1287
+
1288
+ const w = cssLengthToPx(resolveVarRefs(
1289
+ decls.get('width') || decls.get('inline-size') || '', customProps));
1290
+ const h = cssLengthToPx(resolveVarRefs(
1291
+ decls.get('height') || decls.get('block-size') || '', customProps));
1292
+ if (w == null || h == null || w < 2 || h < 2 || w > 16 || h > 16) continue;
1293
+
1294
+ const radius = resolveVarRefs(decls.get('border-radius') || '', customProps);
1295
+ if (!isRoundDotRadius(radius, w, h)) continue;
1296
+
1297
+ if (seen.has(selector)) continue;
1298
+ seen.add(selector);
1299
+ const inLandmark = selectorHitsLandmark(content, selector, heroRanges);
1300
+ findings.push({
1301
+ id: 'pulsing-dot',
1302
+ snippet: `${selector} — ${w}x${h}px dot with infinite "${pulseName}" animation${inLandmark ? ' in header/nav' : ''}`,
1303
+ selector,
1304
+ ...(inLandmark ? { severity: 'error' } : {}),
1305
+ });
1306
+ }
1307
+
1308
+ // Tailwind utilities: animate-ping / animate-pulse on a tiny rounded-full
1309
+ // element declared entirely in the class attribute.
1310
+ const classRe = /class\s*=\s*(?:"([^"]*)"|'([^']*)')/gi;
1311
+ let cm;
1312
+ while ((cm = classRe.exec(content)) !== null) {
1313
+ const cls = cm[1] || cm[2] || '';
1314
+ const anim = cls.match(/\banimate-(ping|pulse)\b/);
1315
+ if (!anim) continue;
1316
+ if (!/\brounded-full\b/.test(cls)) continue;
1317
+ if (!/\b(?:w|h|size)-(?:1|1\.5|2|2\.5|3|3\.5|4)\b/.test(cls)) continue;
1318
+ const key = `tw:${cls}`;
1319
+ if (seen.has(key)) continue;
1320
+ seen.add(key);
1321
+ const inLandmark = indexInSourceRanges(cm.index, heroRanges);
1322
+ findings.push({
1323
+ id: 'pulsing-dot',
1324
+ snippet: `animate-${anim[1]} on tiny rounded-full element${inLandmark ? ' in header/nav' : ''}`,
1325
+ ...(inLandmark ? { severity: 'error' } : {}),
1326
+ });
1327
+ }
1328
+
1329
+ return findings;
1330
+ }
1331
+
1332
+ // Shape-assembled illustration: a large inline SVG composing a pictorial
1333
+ // scene from many primitive shapes (rect / circle / ellipse / polygon) in
1334
+ // several fill colors — the clip-art hero mascot. Gates keep the legitimate
1335
+ // SVG population out:
1336
+ // • icons and logos: intrinsic size gate (>= 200px on both axes, from
1337
+ // width/height attributes or the viewBox when no explicit size is set)
1338
+ // • charts / labeled diagrams: more than two <text>/<tspan> nodes exempts
1339
+ // the graphic (axis labels, callouts)
1340
+ // • line drawings / technical diagrams: primitive count < 8 or fewer
1341
+ // than 3 distinct fills never qualifies (stroke-only art has no fills)
1342
+ // • tiling background textures: any <pattern> definition exempts
1343
+ function scanHtmlForShapeAssembledIllustration(html) {
1344
+ const findings = [];
1345
+ const svgRe = /<svg\b[^>]*>[\s\S]*?<\/svg>/gi;
1346
+ let m;
1347
+ while ((m = svgRe.exec(html)) !== null) {
1348
+ const block = m[0];
1349
+ const openTag = (block.match(/^<svg\b[^>]*>/i) || [''])[0];
1350
+
1351
+ // Data-bearing or annotated graphics: axis labels and callout text
1352
+ // mark a chart or diagram, not a mascot.
1353
+ const textCount = (block.match(/<(?:text|tspan)\b/gi) || []).length;
1354
+ if (textCount > 2) continue;
1355
+ // Tiling texture definitions are decorative backgrounds, not scenes.
1356
+ if (/<pattern\b/i.test(block)) continue;
1357
+
1358
+ const primitives = (block.match(/<(?:rect|circle|ellipse|polygon)\b/gi) || []).length;
1359
+ if (primitives < 8) continue;
1360
+
1361
+ // Intrinsic size: explicit width/height attributes win; fall back to
1362
+ // the viewBox box. Percentage or missing sizes stay unresolvable on
1363
+ // that axis and the viewBox speaks for them.
1364
+ const attrDim = (name) => {
1365
+ // (?<![-\w]) keeps compound attributes like stroke-width from
1366
+ // masquerading as the svg's own width.
1367
+ const am = openTag.match(new RegExp(`(?<![-\\w])${name}\\s*=\\s*["']\\s*([\\d.]+)(?:px)?\\s*["']`, 'i'));
1368
+ return am ? parseFloat(am[1]) : null;
1369
+ };
1370
+ const vb = openTag.match(/\bviewBox\s*=\s*["']\s*[-\d.]+[\s,]+[-\d.]+[\s,]+([\d.]+)[\s,]+([\d.]+)\s*["']/i);
1371
+ const w = attrDim('width') ?? (vb ? parseFloat(vb[1]) : null);
1372
+ const h = attrDim('height') ?? (vb ? parseFloat(vb[2]) : null);
1373
+ if (w == null || h == null || w < 200 || h < 200) continue;
1374
+
1375
+ // Distinct fill paints (attributes and inline styles), excluding
1376
+ // non-paints. Multiple fills are what turn a shape pile into a scene.
1377
+ const fills = new Set();
1378
+ for (const fm of block.matchAll(/\bfill\s*[:=]\s*["']?\s*([^"';>}\s]+)/gi)) {
1379
+ const paint = fm[1].trim().toLowerCase();
1380
+ if (!paint || ['none', 'transparent', 'currentcolor', 'inherit'].includes(paint)) continue;
1381
+ fills.add(paint);
1382
+ }
1383
+ if (fills.size < 3) continue;
1384
+
1385
+ findings.push({
1386
+ id: 'shape-assembled-illustration',
1387
+ snippet: `inline <svg> scene: ${primitives} primitive shapes, ~${Math.round(w)}x${Math.round(h)}px, ${fills.size} fill colors`,
1388
+ });
1389
+ }
1390
+ return findings;
1391
+ }
1392
+
444
1393
  /**
445
1394
  * Regex-on-HTML checks shared between browser and Node page-level detection.
446
1395
  * These don't need DOM access, just the raw HTML string.
@@ -474,6 +1423,16 @@ function checkHtmlPatterns(html) {
474
1423
  findings.push({ id: 'gradient-text', snippet: 'bg-clip-text + bg-gradient (Tailwind)' });
475
1424
  }
476
1425
 
1426
+ // --- Borders ---
1427
+
1428
+ // Side-tab accent stripe drawn as an absolutely-positioned pseudo-element
1429
+ // (no border property involved, so the element-level border checks and
1430
+ // the border-left regexes never see it).
1431
+ findings.push(...scanCssTextForPseudoStripe(html));
1432
+
1433
+ // Side-tab accent stripe drawn as a single-edge inset box-shadow.
1434
+ findings.push(...scanCssTextForInsetStripe(html));
1435
+
477
1436
  // --- Layout ---
478
1437
 
479
1438
  // Monotonous spacing
@@ -549,33 +1508,34 @@ function checkHtmlPatterns(html) {
549
1508
  }
550
1509
  }
551
1510
 
552
- // --- Dark glow ---
1511
+ // Pulsing status dots (tiny circular elements on infinite pulse animations)
1512
+ findings.push(...scanCssTextForPulsingDot(html));
553
1513
 
554
- const darkBgRe = /background(?:-color)?\s*:\s*(?:#(?:0[0-9a-f]|1[0-9a-f]|2[0-3])[0-9a-f]{4}\b|#(?:0|1)[0-9a-f]{2}\b|rgb\(\s*(\d{1,2})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})\s*\))/gi;
555
- const twDarkBg = /\bbg-(?:gray|slate|zinc|neutral|stone)-(?:9\d{2}|800)\b/;
556
- if (darkBgRe.test(html) || twDarkBg.test(html)) {
557
- const shadowRe = /box-shadow\s*:\s*([^;{}]+)/gi;
558
- let shm;
559
- while ((shm = shadowRe.exec(html)) !== null) {
560
- const val = shm[1];
561
- const colorMatch = val.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
562
- if (!colorMatch) continue;
563
- const [r, g, b] = [+colorMatch[1], +colorMatch[2], +colorMatch[3]];
564
- if ((Math.max(r, g, b) - Math.min(r, g, b)) < 30) continue;
565
- const pxVals = [...val.matchAll(/(\d+)px|(?<![.\d])\b(0)\b(?![.\d])/g)].map(p => +(p[1] || p[2]));
566
- if (pxVals.length >= 3 && pxVals[2] > 4) {
567
- findings.push({ id: 'dark-glow', snippet: `Colored glow (rgb(${r},${g},${b})) on dark page` });
568
- break;
569
- }
570
- }
1514
+ // Shape-assembled illustrations (large pictorial SVGs built from primitives)
1515
+ findings.push(...scanHtmlForShapeAssembledIllustration(html));
1516
+
1517
+ // Auto-scrolling marquees (<marquee> or infinite horizontal loop animations)
1518
+ findings.push(...scanCssTextForMarquee(html));
1519
+
1520
+ // --- Dark glow / chromatic halo shadows ---
1521
+
1522
+ const glowHits = scanCssTextForGlow(html);
1523
+ if (glowHits.length > 0) {
1524
+ findings.push({ id: 'dark-glow', snippet: glowHits[0].snippet });
571
1525
  }
572
1526
 
573
- // --- Provider tells (gated): repeating-gradient stripes (GPT) ---
1527
+ // Radial-gradient background halo (gradient-drawn sibling of dark-glow)
1528
+ const haloHits = scanCssTextForRadialHalo(html);
1529
+ if (haloHits.length > 0) {
1530
+ findings.push({ id: 'radial-halo', snippet: haloHits[0].snippet });
1531
+ }
1532
+
1533
+ // --- Generated-UI tells: repeating-gradient stripes ---
574
1534
  if (/repeating-(?:linear|radial|conic)-gradient\s*\(/i.test(html)) {
575
1535
  findings.push({ id: 'repeating-stripes-gradient', snippet: 'repeating-gradient decorative stripes' });
576
1536
  }
577
1537
 
578
- // --- Provider tells (gated): two-axis grid-line background (Codex/GPT) ---
1538
+ // --- Generated-UI tells: two-axis grid-line background ---
579
1539
  // The Codex grid tell is two hairline `linear-gradient(... <color> 1px,
580
1540
  // transparent 1px)` layers (one per axis) tiled by a repeating
581
1541
  // `background-size` cell. Both signals must co-occur in the SAME style block
@@ -588,30 +1548,12 @@ function checkHtmlPatterns(html) {
588
1548
  // in for the second axis. Colors like `oklch(96% 0.012 82 / 0.055)` carry
589
1549
  // nested parens, so match the hairline stop directly rather than parsing
590
1550
  // whole gradient layers.
591
- {
592
- const hairlineRe = /\b\d{1,3}px\s*,\s*transparent\s+\d{1,3}px/gi;
593
- const gridSizeRe = /background-size\s*:[^;{}"']*\b\d{1,3}px\b/i;
594
- const bgDeclRe = /\bbackground(?:-image)?\s*:\s*([^;{}"']*)/gi;
595
- const blockRe = /\{([^{}]*)\}|style\s*=\s*"([^"]*)"|style\s*=\s*'([^']*)'/gi;
596
- let blk;
597
- while ((blk = blockRe.exec(html)) !== null) {
598
- const block = blk[1] || blk[2] || blk[3] || '';
599
- if (!gridSizeRe.test(block)) continue;
600
- let hairlineCount = 0;
601
- let bm;
602
- bgDeclRe.lastIndex = 0;
603
- while ((bm = bgDeclRe.exec(block)) !== null) {
604
- const stops = bm[1].match(hairlineRe);
605
- if (stops) hairlineCount += stops.length;
606
- }
607
- if (hairlineCount >= 2) {
608
- findings.push({ id: 'codex-grid-background', snippet: 'two-axis grid-line gradient background' });
609
- break;
610
- }
611
- }
1551
+ const gridHits = scanCssTextForGridBackground(html);
1552
+ if (gridHits.length > 0) {
1553
+ findings.push({ id: 'codex-grid-background', snippet: gridHits[0].snippet });
612
1554
  }
613
1555
 
614
- // --- Provider tells (gated): "X theater" framing copy (GPT) ---
1556
+ // --- Generated-copy tells: "X theater" framing copy ---
615
1557
  // Lives here (regex-on-HTML) rather than in the text-content analyzers so it
616
1558
  // runs in the bundled browser path too, not just the CLI/static path.
617
1559
  {
@@ -620,7 +1562,7 @@ function checkHtmlPatterns(html) {
620
1562
  if (tm) findings.push({ id: 'theater-slop-phrase', snippet: `"${tm[0].trim()}"` });
621
1563
  }
622
1564
 
623
- // --- Provider tells (gated): image hover transform (Gemini) ---
1565
+ // --- Generated-UI tells: image hover transform ---
624
1566
  // A CSS `img...:hover { transform: ... }` rule, or a Tailwind hover:scale /
625
1567
  // hover:rotate / hover:translate utility on an <img>. Each distinct
626
1568
  // mechanism is its own finding.
@@ -649,7 +1591,11 @@ function checkHtmlPatterns(html) {
649
1591
  // `background: #abc`. Real browsers always decompose, so the fallback is
650
1592
  // a no-op there.
651
1593
  function readOwnBackgroundColor(el, computedStyle) {
652
- const bg = parseRgb(computedStyle.backgroundColor);
1594
+ // Real browsers keep wide-gamut/computed color functions (oklch(), oklab(),
1595
+ // color-mix() results) in getComputedStyle output, which plain parseRgb
1596
+ // misses — a flat oklch button background would silently skip every
1597
+ // contrast check without the parseAnyColor fallback.
1598
+ const bg = parseRgb(computedStyle.backgroundColor) || parseAnyColor(computedStyle.backgroundColor);
653
1599
  if (DETECTOR_IS_BROWSER || (bg && bg.a >= 0.1)) return bg;
654
1600
  const rawStyle = el.getAttribute?.('style') || '';
655
1601
  const bgMatch = rawStyle.match(/background(?:-color)?\s*:\s*([^;]+)/i);
@@ -671,6 +1617,18 @@ function readOwnBackgroundColor(el, computedStyle) {
671
1617
 
672
1618
  function resolveBackground(el, win, customPropMap) {
673
1619
  let current = el;
1620
+ // Translucent layers (0.1 < a < 1) found on the way down to an opaque
1621
+ // base. A browser composites these over the base; the old behavior
1622
+ // either returned them as-if-opaque (browser mode) or skipped them
1623
+ // entirely (static mode), both of which misstate the effective surface
1624
+ // for contrast checks (e.g. `background: color-mix(in oklab, var(--hot)
1625
+ // 16%, transparent)` chips on dark pages).
1626
+ const overlays = [];
1627
+ const flatten = (base) => {
1628
+ let acc = base;
1629
+ for (let i = overlays.length - 1; i >= 0; i--) acc = compositeColorOver(overlays[i], acc);
1630
+ return acc;
1631
+ };
674
1632
  while (current && current.nodeType === 1) {
675
1633
  const style = DETECTOR_IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
676
1634
  const bgImage = style.backgroundImage || '';
@@ -683,7 +1641,9 @@ function resolveBackground(el, win, customPropMap) {
683
1641
  // decorative. The old behavior bailed on any gradient ancestor, which
684
1642
  // caused massive false-positive contrast findings on grain-textured
685
1643
  // body backgrounds.
686
- let bg = parseRgb(style.backgroundColor);
1644
+ // Real browsers serialize wide-gamut computed values as oklab()/oklch()
1645
+ // (e.g. any color-mix() result), which plain parseRgb misses.
1646
+ let bg = parseRgb(style.backgroundColor) || parseAnyColor(style.backgroundColor);
687
1647
  if (!DETECTOR_IS_BROWSER && (!bg || bg.a < 0.1)) {
688
1648
  // jsdom returns literal "var(--X)" / "oklch(...)" strings. Resolve
689
1649
  // through customPropMap so Tailwind v4 color tokens become RGB.
@@ -703,7 +1663,8 @@ function resolveBackground(el, win, customPropMap) {
703
1663
  }
704
1664
 
705
1665
  if (bg && bg.a > 0.1) {
706
- if (DETECTOR_IS_BROWSER || bg.a >= 0.5) return bg;
1666
+ if (bg.a >= 0.99) return flatten(bg);
1667
+ overlays.push(bg);
707
1668
  }
708
1669
  // No solid bg-color at this level. If THIS level has a gradient/url
709
1670
  // with no underlying solid color we can read:
@@ -719,41 +1680,66 @@ function resolveBackground(el, win, customPropMap) {
719
1680
  // bgs worth checking against).
720
1681
  if (hasGradientOrUrl) {
721
1682
  if (current.tagName === 'BODY' || current.tagName === 'HTML') {
722
- return { r: 255, g: 255, b: 255, a: 1 };
1683
+ return flatten({ r: 255, g: 255, b: 255, a: 1 });
723
1684
  }
724
1685
  return null;
725
1686
  }
726
1687
  current = current.parentElement;
727
1688
  }
728
- return { r: 255, g: 255, b: 255 };
1689
+ return flatten({ r: 255, g: 255, b: 255, a: 1 });
729
1690
  }
730
1691
 
731
1692
  // Walk parents looking for a gradient background and return its color stops.
732
1693
  // Used as a fallback when resolveBackground() returns null because the
733
1694
  // effective background is a gradient (no single solid color to compare against).
734
- function resolveGradientStops(el, win) {
1695
+ function resolveGradientStops(el, win, customPropMap) {
735
1696
  let current = el;
736
1697
  while (current && current.nodeType === 1) {
737
1698
  const style = DETECTOR_IS_BROWSER ? getComputedStyle(current) : win.getComputedStyle(current);
738
1699
  const bgImage = style.backgroundImage || '';
1700
+ let stops = null;
739
1701
  if (bgImage && bgImage !== 'none' && /gradient/i.test(bgImage)) {
740
- const stops = parseGradientColors(bgImage);
741
- if (stops.length > 0) return stops;
1702
+ const parsed = parseGradientColors(bgImage);
1703
+ if (parsed.length > 0) stops = parsed;
742
1704
  }
743
- if (!DETECTOR_IS_BROWSER) {
1705
+ if (!stops && !DETECTOR_IS_BROWSER) {
744
1706
  // jsdom doesn't decompose `background:` shorthand — peek at the raw inline style
745
1707
  const rawStyle = current.getAttribute?.('style') || '';
746
1708
  const bgMatch = rawStyle.match(/background(?:-image)?\s*:\s*([^;]+)/i);
747
1709
  if (bgMatch && /gradient/i.test(bgMatch[1])) {
748
- const stops = parseGradientColors(bgMatch[1]);
749
- if (stops.length > 0) return stops;
1710
+ const parsed = parseGradientColors(bgMatch[1]);
1711
+ if (parsed.length > 0) stops = parsed;
750
1712
  }
751
1713
  }
1714
+ if (stops) return compositeGradientStops(stops, current, win, customPropMap);
752
1715
  current = current.parentElement;
753
1716
  }
754
1717
  return null;
755
1718
  }
756
1719
 
1720
+ // A translucent gradient stop (e.g. a faint `rgba(52,192,168,0.09)` accent
1721
+ // glow) paints over whatever surface sits beneath the gradient — the browser
1722
+ // composites it, so its effective color is far closer to the base than to the
1723
+ // full-opacity accent. Treating the stop as opaque flags every text child of a
1724
+ // softly-glowing section as low-contrast (issue #409 Case B). Composite each
1725
+ // alpha stop over the resolved surface beneath the gradient element. When that
1726
+ // surface isn't resolvable (another gradient above, no opaque ancestor), drop
1727
+ // the translucent stop rather than guess: a dropped stop can't manufacture a
1728
+ // false finding, and skipping beats a wrong ratio.
1729
+ function compositeGradientStops(stops, gradientEl, win, customPropMap) {
1730
+ const hasAlpha = stops.some(s => (s.a ?? 1) < 0.99);
1731
+ if (!hasAlpha) return stops;
1732
+ const base = resolveBackground(gradientEl.parentElement || gradientEl, win, customPropMap);
1733
+ const out = [];
1734
+ for (const s of stops) {
1735
+ const a = s.a ?? 1;
1736
+ if (a >= 0.99) { out.push(s); continue; }
1737
+ if (base) out.push(compositeColorOver(s, base));
1738
+ // else: unresolvable base — drop the translucent stop (skip, don't guess).
1739
+ }
1740
+ return out.length ? out : null;
1741
+ }
1742
+
757
1743
  // Parse a single CSS length token to pixels. Accepts "12px", "50%", a
758
1744
  // shorthand like "12px 4px" (uses the first value), or empty / null.
759
1745
  // Returns the pixel value, or null when the input is unparseable.
@@ -787,6 +1773,43 @@ function resolveBorderRadiusPx(el, style, widthPx, win) {
787
1773
 
788
1774
  // Browser adapters — call getComputedStyle/getBoundingClientRect on live DOM
789
1775
 
1776
+ // Selected-state context for accent stripes. Only an actual selection
1777
+ // marker exempts the stripe as the standard active-item indicator:
1778
+ // aria-selected="true", aria-current (any non-false value), or an
1779
+ // active/current/selected class hint. Tab-strip MEMBERSHIP alone
1780
+ // ([role=tablist]/[role=tab]/.tabs ancestry, aria-selected="false")
1781
+ // deliberately does not — a chromatic stripe repeated on every tab in
1782
+ // the group, or on every menu item, is decoration, not state; the
1783
+ // selected item's own underline stays legal.
1784
+ function isTabContextElement(el) {
1785
+ if (!el) return false;
1786
+ try {
1787
+ if (el.closest?.('[aria-selected="true"], [aria-current]:not([aria-current="false"])')) return true;
1788
+ } catch { /* selector engine differences — fall through to class scan */ }
1789
+ let cur = el, depth = 0;
1790
+ while (cur && cur.nodeType === 1 && depth < 6) {
1791
+ const cls = String(cur.getAttribute?.('class') || cur.className || '');
1792
+ if (/(?:^|[\s_-])(?:active|current|selected)(?:$|[\s_-])/i.test(cls)) return true;
1793
+ cur = cur.parentElement;
1794
+ depth++;
1795
+ }
1796
+ return false;
1797
+ }
1798
+
1799
+ // Status-surface context for accent borders. On a live status/alert region
1800
+ // (role=status|alert|alertdialog|log, or aria-live=polite|assertive) a colored
1801
+ // single-edge border is the established severity-accent convention — a toast,
1802
+ // snackbar, or callout bar — not the decorative side-tab tell. The element
1803
+ // itself or a wrapping live region qualifies. This never fires from the
1804
+ // CSS-only / regex scanners, which have no role information.
1805
+ function isStatusContextElement(el) {
1806
+ if (!el) return false;
1807
+ try {
1808
+ if (el.closest?.('[role="status"], [role="alert"], [role="alertdialog"], [role="log"], [aria-live="polite"], [aria-live="assertive"]')) return true;
1809
+ } catch { /* selector engine differences — fall through */ }
1810
+ return false;
1811
+ }
1812
+
790
1813
  function checkElementBordersDOM(el) {
791
1814
  const tag = el.tagName.toLowerCase();
792
1815
  if (BORDER_SAFE_TAGS.has(tag)) return [];
@@ -799,7 +1822,105 @@ function checkElementBordersDOM(el) {
799
1822
  widths[s] = parseFloat(style[`border${s}Width`]) || 0;
800
1823
  colors[s] = style[`border${s}Color`] || '';
801
1824
  }
802
- return checkBorders(tag, widths, colors, parseFloat(style.borderRadius) || 0);
1825
+ const ownBg = parseRgb(style.backgroundColor) || parseAnyColor(style.backgroundColor);
1826
+ return checkBorders(tag, widths, colors, parseFloat(style.borderRadius) || 0, {
1827
+ tabContext: isTabContextElement(el),
1828
+ statusContext: isStatusContextElement(el),
1829
+ badgeLike: !!(ownBg && (ownBg.a ?? 1) > 0.1),
1830
+ });
1831
+ }
1832
+
1833
+ // Browser-side twin of scanCssTextForPseudoStripe. The text scanner reads
1834
+ // stylesheet source, so a stripe whose color only exists at runtime (an
1835
+ // inline per-card custom property, a JS-assigned var) or whose geometry
1836
+ // resolves in layout never matches it. In a real browser the pseudo-element's
1837
+ // computed style carries the actual used color and px geometry — check those
1838
+ // directly. Gates mirror the text scanner: 3-12px thick, chromatic fill,
1839
+ // spanning (nearly) the full edge; corner rounding on the host card is
1840
+ // irrelevant. Exemptions stay narrow: structural/prose tags, real selection
1841
+ // markers (isTabContextElement), and button/link affordances for the
1842
+ // horizontal variant.
1843
+ function checkElementPseudoStripeDOM(el) {
1844
+ const tag = el.tagName.toLowerCase();
1845
+ if (BORDER_SAFE_TAGS.has(tag) || tag === 'summary') return [];
1846
+ if (el.closest?.('nav, blockquote, pre')) return [];
1847
+ if (!isRenderedForBrowserRule(el)) return [];
1848
+ const rect = el.getBoundingClientRect();
1849
+ if (rect.width < 40 || rect.height < 20) return [];
1850
+ if (isTabContextElement(el)) return [];
1851
+
1852
+ const findings = [];
1853
+ for (const which of ['::before', '::after']) {
1854
+ let ps;
1855
+ try { ps = getComputedStyle(el, which); } catch { continue; }
1856
+ if (!ps || ps.content === 'none' || ps.content === '') continue;
1857
+ if (ps.position !== 'absolute' && ps.position !== 'fixed') continue;
1858
+ if ((parseFloat(ps.opacity) || 0) <= 0.01 || ps.display === 'none') continue;
1859
+ const w = parseFloat(ps.width) || 0;
1860
+ const h = parseFloat(ps.height) || 0;
1861
+ if (!(w > 0 && h > 0)) continue;
1862
+
1863
+ // Used values: for absolutely-positioned boxes the browser resolves
1864
+ // both edge offsets after layout, so left/right (and top/bottom) are
1865
+ // real distances, never "auto".
1866
+ const left = parseFloat(ps.left);
1867
+ const right = parseFloat(ps.right);
1868
+ const top = parseFloat(ps.top);
1869
+ const bottom = parseFloat(ps.bottom);
1870
+ const hugs = (v) => Number.isFinite(v) && v >= -2 && v <= 2;
1871
+
1872
+ let edge = null;
1873
+ let thickness = null;
1874
+ // Vertical stripe: narrow box spanning (nearly) the full height of the
1875
+ // host, hugging its left or right edge. "Nearly" tolerates the floating
1876
+ // variant that backs off each end by a small inset.
1877
+ if (w >= 3 && w <= 12 && h >= rect.height - 44 && h >= rect.height * 0.5) {
1878
+ edge = hugs(left) ? 'left' : hugs(right) ? 'right' : null;
1879
+ thickness = w;
1880
+ }
1881
+ // Horizontal stripe riding the top or bottom edge. Button/link-styled
1882
+ // hosts keep their underline affordances.
1883
+ if (!edge && h >= 3 && h <= 12 && w >= rect.width - 44 && w >= rect.width * 0.5) {
1884
+ const cls = String(el.getAttribute?.('class') || el.className || '');
1885
+ if (!/(?:^|[\s_-])(?:btn|button|link)(?:$|[\s\w_-])/i.test(cls)) {
1886
+ edge = hugs(top) ? 'top' : hugs(bottom) ? 'bottom' : null;
1887
+ thickness = h;
1888
+ }
1889
+ }
1890
+ if (!edge) continue;
1891
+
1892
+ const bg = parseRgb(ps.backgroundColor) || parseAnyColor(ps.backgroundColor);
1893
+ if (!bg || (bg.a ?? 1) < 0.1) continue;
1894
+ if (Math.max(bg.r, bg.g, bg.b) - Math.min(bg.r, bg.g, bg.b) < 30) continue;
1895
+
1896
+ findings.push({
1897
+ id: 'side-tab',
1898
+ snippet: `${classSelector(el)}${which} — absolute ${thickness}px pseudo-element stripe (${edge})`,
1899
+ });
1900
+ }
1901
+ return findings;
1902
+ }
1903
+
1904
+ // Full-cover surface pseudo (browser): a ::before/::after positioned
1905
+ // absolute/fixed whose box covers (nearly) the whole host and carries an
1906
+ // opaque background. That pseudo is the element's visible surface even
1907
+ // though the element's own background-color reads transparent — the nav-CTA
1908
+ // construction that otherwise escapes every own-background contrast gate.
1909
+ function readPseudoSurfaceDOM(el, rect) {
1910
+ for (const which of ['::before', '::after']) {
1911
+ let ps;
1912
+ try { ps = getComputedStyle(el, which); } catch { continue; }
1913
+ if (!ps || ps.content === 'none' || ps.content === '') continue;
1914
+ if (ps.position !== 'absolute' && ps.position !== 'fixed') continue;
1915
+ if (ps.display === 'none' || (parseFloat(ps.opacity) || 1) < 0.9) continue;
1916
+ const w = parseFloat(ps.width) || 0;
1917
+ const h = parseFloat(ps.height) || 0;
1918
+ if (w < rect.width - 4 || h < rect.height - 4) continue;
1919
+ const bg = parseRgb(ps.backgroundColor) || parseAnyColor(ps.backgroundColor);
1920
+ if (!bg || (bg.a ?? 1) < 0.9) continue;
1921
+ return bg;
1922
+ }
1923
+ return null;
803
1924
  }
804
1925
 
805
1926
  function checkElementColorsDOM(el) {
@@ -812,11 +1933,24 @@ function checkElementColorsDOM(el) {
812
1933
  const style = getComputedStyle(el);
813
1934
  const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
814
1935
  const hasDirectText = directText.trim().length > 0;
815
- const effectiveBg = resolveBackground(el);
1936
+ let effectiveBg = resolveBackground(el);
1937
+ let ownBg = readOwnBackgroundColor(el, style);
1938
+ if (!ownBg || (ownBg.a ?? 1) <= 0.5) {
1939
+ const pseudoSurface = readPseudoSurfaceDOM(el, rect);
1940
+ if (pseudoSurface) {
1941
+ ownBg = pseudoSurface;
1942
+ effectiveBg = pseudoSurface;
1943
+ }
1944
+ }
816
1945
  return checkColors({
817
1946
  tag,
818
- textColor: parseRgb(style.color),
819
- bgColor: readOwnBackgroundColor(el, style),
1947
+ // Chrome serializes computed colors specified in modern spaces as
1948
+ // oklch()/oklab() strings; without the parseAnyColor fallback the text
1949
+ // color comes back null and the low-contrast / gray-on-color checks
1950
+ // silently never run (the shipped miss: a nav CTA whose text color was
1951
+ // an oklch token near its own oklch background).
1952
+ textColor: parseRgb(style.color) || parseAnyColor(style.color),
1953
+ bgColor: ownBg,
820
1954
  effectiveBg,
821
1955
  effectiveBgStops: effectiveBg ? null : resolveGradientStops(el),
822
1956
  fontSize: parseFloat(style.fontSize) || 16,
@@ -877,6 +2011,21 @@ function checkElementItalicSerifDOM(el) {
877
2011
  });
878
2012
  }
879
2013
 
2014
+ function domAccentDashPseudo(el) {
2015
+ for (const which of ['::before', '::after']) {
2016
+ let ps;
2017
+ try { ps = getComputedStyle(el, which); } catch { continue; }
2018
+ if (!ps || ps.content === 'none' || ps.content === '') continue;
2019
+ const w = parseFloat(ps.width) || 0;
2020
+ const h = parseFloat(ps.height) || 0;
2021
+ if (!(w >= 8 && w <= 80 && h >= 1 && h <= 6)) continue;
2022
+ const bg = parseRgb(ps.backgroundColor) || parseAnyColor(ps.backgroundColor);
2023
+ if (!bg || (bg.a ?? 1) < 0.1) continue;
2024
+ if (Math.max(bg.r, bg.g, bg.b) - Math.min(bg.r, bg.g, bg.b) >= 30) return true;
2025
+ }
2026
+ return false;
2027
+ }
2028
+
880
2029
  function checkElementHeroEyebrowDOM(el) {
881
2030
  const tag = el.tagName.toLowerCase();
882
2031
  if (tag !== 'h1') return [];
@@ -888,6 +2037,7 @@ function checkElementHeroEyebrowDOM(el) {
888
2037
  headingTag: tag,
889
2038
  headingText: el.textContent || '',
890
2039
  headingFontSize: parseFloat(headStyle.fontSize) || 0,
2040
+ headingInApplicationContext: !!el.closest('[role="tabpanel"], [role="dialog"], [role="application"], dialog'),
891
2041
  siblingTag: sibling.tagName.toLowerCase(),
892
2042
  siblingText: sibling.textContent || '',
893
2043
  siblingTextTransform: sibStyle.textTransform || '',
@@ -895,6 +2045,7 @@ function checkElementHeroEyebrowDOM(el) {
895
2045
  siblingLetterSpacing: parseFloat(sibStyle.letterSpacing) || 0,
896
2046
  siblingFontWeight: sibStyle.fontWeight || '',
897
2047
  siblingColor: sibStyle.color || '',
2048
+ siblingHasAccentDashPseudo: domAccentDashPseudo(sibling),
898
2049
  });
899
2050
  }
900
2051
 
@@ -959,8 +2110,10 @@ function resolveVarRefs(raw, customPropMap, depth = 0) {
959
2110
  // detector's contrast / color checks.
960
2111
  function oklchToRgb(L, C, H) {
961
2112
  const hRad = (H * Math.PI) / 180;
962
- const a = C * Math.cos(hRad);
963
- const b = C * Math.sin(hRad);
2113
+ return oklabToRgb(L, C * Math.cos(hRad), C * Math.sin(hRad));
2114
+ }
2115
+
2116
+ function oklabToRgb(L, a, b) {
964
2117
  const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
965
2118
  const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
966
2119
  const s_ = L - 0.0894841775 * a - 1.2914855480 * b;
@@ -980,13 +2133,178 @@ function oklchToRgb(L, C, H) {
980
2133
  };
981
2134
  }
982
2135
 
983
- // Extended color parser: rgb/rgba/hex/oklch. Returns null on no match.
984
- // Use this when the input might be any CSS color form; use plain parseRgb
985
- // when you only expect computed rgb() values from real browsers.
2136
+ function hslToRgb(h, s, l) {
2137
+ h = ((h % 360) + 360) % 360;
2138
+ const c = (1 - Math.abs(2 * l - 1)) * s;
2139
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
2140
+ const m0 = l - c / 2;
2141
+ const [r, g, b] =
2142
+ h < 60 ? [c, x, 0] :
2143
+ h < 120 ? [x, c, 0] :
2144
+ h < 180 ? [0, c, x] :
2145
+ h < 240 ? [0, x, c] :
2146
+ h < 300 ? [x, 0, c] : [c, 0, x];
2147
+ return {
2148
+ r: Math.round((r + m0) * 255),
2149
+ g: Math.round((g + m0) * 255),
2150
+ b: Math.round((b + m0) * 255),
2151
+ a: 1,
2152
+ };
2153
+ }
2154
+
2155
+ function hwbToRgb(h, w, bl) {
2156
+ if (w + bl >= 1) {
2157
+ const g = Math.round((w / (w + bl)) * 255);
2158
+ return { r: g, g, b: g, a: 1 };
2159
+ }
2160
+ const base = hslToRgb(h, 1, 0.5);
2161
+ const mix = (c) => Math.round(((c / 255) * (1 - w - bl) + w) * 255);
2162
+ return { r: mix(base.r), g: mix(base.g), b: mix(base.b), a: 1 };
2163
+ }
2164
+
2165
+ // Common CSS named colors — the handful that actually show up in generated
2166
+ // UIs, not the full 148-name spec list. Includes the achromatic names so a
2167
+ // named gray parses (and correctly reads as no-chroma) instead of being
2168
+ // treated as an unknown color.
2169
+ const CSS_NAMED_COLORS = {
2170
+ black: { r: 0, g: 0, b: 0 },
2171
+ white: { r: 255, g: 255, b: 255 },
2172
+ gray: { r: 128, g: 128, b: 128 },
2173
+ grey: { r: 128, g: 128, b: 128 },
2174
+ silver: { r: 192, g: 192, b: 192 },
2175
+ dimgray: { r: 105, g: 105, b: 105 },
2176
+ darkgray: { r: 169, g: 169, b: 169 },
2177
+ lightgray: { r: 211, g: 211, b: 211 },
2178
+ gainsboro: { r: 220, g: 220, b: 220 },
2179
+ whitesmoke: { r: 245, g: 245, b: 245 },
2180
+ red: { r: 255, g: 0, b: 0 },
2181
+ crimson: { r: 220, g: 20, b: 60 },
2182
+ tomato: { r: 255, g: 99, b: 71 },
2183
+ coral: { r: 255, g: 127, b: 80 },
2184
+ salmon: { r: 250, g: 128, b: 114 },
2185
+ orange: { r: 255, g: 165, b: 0 },
2186
+ gold: { r: 255, g: 215, b: 0 },
2187
+ yellow: { r: 255, g: 255, b: 0 },
2188
+ olive: { r: 128, g: 128, b: 0 },
2189
+ lime: { r: 0, g: 255, b: 0 },
2190
+ green: { r: 0, g: 128, b: 0 },
2191
+ teal: { r: 0, g: 128, b: 128 },
2192
+ turquoise: { r: 64, g: 224, b: 208 },
2193
+ cyan: { r: 0, g: 255, b: 255 },
2194
+ aqua: { r: 0, g: 255, b: 255 },
2195
+ skyblue: { r: 135, g: 206, b: 235 },
2196
+ dodgerblue: { r: 30, g: 144, b: 255 },
2197
+ blue: { r: 0, g: 0, b: 255 },
2198
+ navy: { r: 0, g: 0, b: 128 },
2199
+ indigo: { r: 75, g: 0, b: 130 },
2200
+ rebeccapurple: { r: 102, g: 51, b: 153 },
2201
+ purple: { r: 128, g: 0, b: 128 },
2202
+ violet: { r: 238, g: 130, b: 238 },
2203
+ orchid: { r: 218, g: 112, b: 214 },
2204
+ magenta: { r: 255, g: 0, b: 255 },
2205
+ fuchsia: { r: 255, g: 0, b: 255 },
2206
+ hotpink: { r: 255, g: 105, b: 180 },
2207
+ pink: { r: 255, g: 192, b: 203 },
2208
+ maroon: { r: 128, g: 0, b: 0 },
2209
+ };
2210
+
2211
+ // Split a string on top-level commas (ignoring commas nested in parens).
2212
+ function splitTopLevelCommas(str) {
2213
+ const parts = [];
2214
+ let depth = 0, start = 0;
2215
+ for (let i = 0; i < str.length; i++) {
2216
+ const ch = str[i];
2217
+ if (ch === '(') depth++;
2218
+ else if (ch === ')') depth = Math.max(0, depth - 1);
2219
+ else if (ch === ',' && depth === 0) {
2220
+ parts.push(str.slice(start, i).trim());
2221
+ start = i + 1;
2222
+ }
2223
+ }
2224
+ const tail = str.slice(start).trim();
2225
+ if (tail) parts.push(tail);
2226
+ return parts;
2227
+ }
2228
+
2229
+ // Evaluate a CSS color-mix() expression to {r,g,b,a}. Returns null when
2230
+ // the expression can't be resolved (unresolved var(), unknown colors).
2231
+ //
2232
+ // Mixing is done with premultiplied alpha in sRGB regardless of the
2233
+ // declared interpolation space. That is exact for the dominant generated-UI
2234
+ // pattern — `color-mix(in oklab, <color> N%, transparent)` — where the
2235
+ // result is simply <color> at alpha N% in ANY rectangular space, and a
2236
+ // close-enough approximation for opaque-opaque mixes (the detector only
2237
+ // consumes these values for contrast/chroma thresholds, not for display).
2238
+ function parseColorMix(str) {
2239
+ const m = String(str).trim().match(/^color-mix\(/i);
2240
+ if (!m) return null;
2241
+ // Balanced-paren capture of the arguments.
2242
+ let depth = 0, end = -1;
2243
+ const open = str.indexOf('(');
2244
+ for (let i = open; i < str.length; i++) {
2245
+ if (str[i] === '(') depth++;
2246
+ else if (str[i] === ')') { depth--; if (depth === 0) { end = i; break; } }
2247
+ }
2248
+ if (end < 0) return null;
2249
+ const args = splitTopLevelCommas(str.slice(open + 1, end));
2250
+ if (args.length !== 3 || !/^in\s/i.test(args[0])) return null;
2251
+
2252
+ const parseComponent = (component) => {
2253
+ // Percentage may lead or trail the color per spec.
2254
+ let pct = null;
2255
+ let colorStr = component;
2256
+ const trail = component.match(/\s+([\d.]+)%$/);
2257
+ const lead = component.match(/^([\d.]+)%\s+/);
2258
+ if (trail) { pct = parseFloat(trail[1]); colorStr = component.slice(0, trail.index).trim(); }
2259
+ else if (lead) { pct = parseFloat(lead[1]); colorStr = component.slice(lead[0].length).trim(); }
2260
+ let color;
2261
+ if (/^transparent$/i.test(colorStr)) color = { r: 0, g: 0, b: 0, a: 0 };
2262
+ else color = parseAnyColor(colorStr);
2263
+ if (!color) return null;
2264
+ return { color, pct };
2265
+ };
2266
+
2267
+ const c1 = parseComponent(args[1]);
2268
+ const c2 = parseComponent(args[2]);
2269
+ if (!c1 || !c2) return null;
2270
+ let p1 = c1.pct, p2 = c2.pct;
2271
+ if (p1 == null && p2 == null) { p1 = 50; p2 = 50; }
2272
+ else if (p1 == null) p1 = 100 - p2;
2273
+ else if (p2 == null) p2 = 100 - p1;
2274
+ const sum = p1 + p2;
2275
+ if (sum <= 0) return null;
2276
+ // Per spec: weights normalize to sum; when sum < 100 the result alpha is
2277
+ // additionally scaled by sum/100.
2278
+ const w1 = p1 / sum, w2 = p2 / sum;
2279
+ const alphaScale = sum < 100 ? sum / 100 : 1;
2280
+ const a1 = c1.color.a ?? 1, a2 = c2.color.a ?? 1;
2281
+ const a = (a1 * w1 + a2 * w2) * alphaScale;
2282
+ if (a <= 0) return { r: 0, g: 0, b: 0, a: 0 };
2283
+ const mix = (ch) => Math.round((c1.color[ch] * a1 * w1 + c2.color[ch] * a2 * w2) / (a1 * w1 + a2 * w2));
2284
+ return { r: mix('r'), g: mix('g'), b: mix('b'), a: Math.min(1, a) };
2285
+ }
2286
+
2287
+ // Composite a translucent color over an opaque(ish) base (simple
2288
+ // source-over in sRGB). Returns an opaque {r,g,b,a:1}.
2289
+ function compositeColorOver(top, base) {
2290
+ const a = top.a ?? 1;
2291
+ return {
2292
+ r: Math.round(top.r * a + base.r * (1 - a)),
2293
+ g: Math.round(top.g * a + base.g * (1 - a)),
2294
+ b: Math.round(top.b * a + base.b * (1 - a)),
2295
+ a: 1,
2296
+ };
2297
+ }
2298
+
2299
+ // Extended color parser: rgb/rgba/hex/oklch/oklab/hsl/hwb/color-mix/common
2300
+ // named colors. Returns null on no match. Use this when the input might be
2301
+ // any CSS color form; use plain parseRgb when you only expect computed rgb()
2302
+ // values from real browsers.
986
2303
  function parseAnyColor(s) {
987
2304
  if (!s || typeof s !== 'string') return null;
988
2305
  const str = s.trim();
989
2306
  if (str === 'transparent' || str === 'currentcolor' || str === 'inherit') return null;
2307
+ if (/^color-mix\(/i.test(str)) return parseColorMix(str);
990
2308
  let m;
991
2309
  m = str.match(/rgba?\(\s*(\d+(?:\.\d+)?)\s*,?\s*(\d+(?:\.\d+)?)\s*,?\s*(\d+(?:\.\d+)?)(?:\s*[,/]\s*([\d.]+))?\s*\)/);
992
2310
  if (m) return { r: Math.round(+m[1]), g: Math.round(+m[2]), b: Math.round(+m[3]), a: m[4] !== undefined ? +m[4] : 1 };
@@ -1024,6 +2342,41 @@ function parseAnyColor(s) {
1024
2342
  }
1025
2343
  return rgb;
1026
2344
  }
2345
+ // OKLAB — a/b are signed axes; percentages map 100% → 0.4.
2346
+ m = str.match(/oklab\(\s*([\d.]+)(%?)\s+(-?[\d.]+)(%?)\s+(-?[\d.]+)(%?)(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
2347
+ if (m) {
2348
+ const L = m[2] === '%' ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
2349
+ const a = m[4] === '%' ? parseFloat(m[3]) * 0.004 : parseFloat(m[3]);
2350
+ const b = m[6] === '%' ? parseFloat(m[5]) * 0.004 : parseFloat(m[5]);
2351
+ const rgb = oklabToRgb(L, a, b);
2352
+ if (m[7] !== undefined) {
2353
+ const alpha = parseFloat(m[7]);
2354
+ rgb.a = m[8] === '%' ? alpha / 100 : alpha;
2355
+ }
2356
+ return rgb;
2357
+ }
2358
+ // HSL/HSLA — comma or space syntax, optional deg on hue.
2359
+ m = str.match(/hsla?\(\s*(-?[\d.]+)(?:deg)?\s*[,\s]\s*([\d.]+)%\s*[,\s]\s*([\d.]+)%(?:\s*[,/]\s*([\d.]+)(%)?)?\s*\)/i);
2360
+ if (m) {
2361
+ const rgb = hslToRgb(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
2362
+ if (m[4] !== undefined) {
2363
+ const alpha = parseFloat(m[4]);
2364
+ rgb.a = m[5] === '%' ? alpha / 100 : alpha;
2365
+ }
2366
+ return rgb;
2367
+ }
2368
+ // HWB — hue whiteness% blackness%.
2369
+ m = str.match(/hwb\(\s*(-?[\d.]+)(?:deg)?\s+([\d.]+)%\s+([\d.]+)%(?:\s*\/\s*([\d.]+)(%)?)?\s*\)/i);
2370
+ if (m) {
2371
+ const rgb = hwbToRgb(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
2372
+ if (m[4] !== undefined) {
2373
+ const alpha = parseFloat(m[4]);
2374
+ rgb.a = m[5] === '%' ? alpha / 100 : alpha;
2375
+ }
2376
+ return rgb;
2377
+ }
2378
+ const named = CSS_NAMED_COLORS[str.toLowerCase()];
2379
+ if (named) return { ...named, a: 1 };
1027
2380
  return null;
1028
2381
  }
1029
2382
 
@@ -1036,7 +2389,7 @@ function parseColorResolved(str, customPropMap) {
1036
2389
  return parseAnyColor(resolved);
1037
2390
  }
1038
2391
 
1039
- const REPEATED_KICKER_SKIP_SELECTOR = [
2392
+ const KICKER_SKIP_SELECTOR = [
1040
2393
  'nav',
1041
2394
  'form',
1042
2395
  'table',
@@ -1055,7 +2408,7 @@ const REPEATED_KICKER_SKIP_SELECTOR = [
1055
2408
  '[data-impeccable-allow-kickers]',
1056
2409
  ].join(',');
1057
2410
 
1058
- const REPEATED_KICKER_CARD_CONTEXT_SELECTOR = [
2411
+ const KICKER_CARD_CONTEXT_SELECTOR = [
1059
2412
  'article',
1060
2413
  'button',
1061
2414
  'a',
@@ -1073,23 +2426,32 @@ function cleanInlineText(el) {
1073
2426
  .trim();
1074
2427
  }
1075
2428
 
1076
- function isRepeatedKickerCardContext(heading, kicker) {
1077
- const item = heading.closest?.(REPEATED_KICKER_CARD_CONTEXT_SELECTOR);
2429
+ function isKickerCardContext(heading, kicker) {
2430
+ const item = heading.closest?.(KICKER_CARD_CONTEXT_SELECTOR);
1078
2431
  return Boolean(item && (!item.contains || item.contains(kicker)));
1079
2432
  }
1080
2433
 
1081
- function isRepeatedKickerCandidate(opts) {
2434
+ // Meta lines above headlines join category and date (or path crumbs) with
2435
+ // separator glyphs, or carry a year. A kicker is one short phrase; metadata
2436
+ // keeps its markers.
2437
+ const KICKER_META_TEXT_RE = /[·•|]|\s[\/›»>]\s|\b(19|20)\d{2}\b/;
2438
+ // Legal and document numbering: "Section 4.2", "Article IX", "§ 12.3",
2439
+ // dotted decimal outlines. The label identifies the clause, so it stays.
2440
+ const KICKER_DOC_NUMBERING_RE = /^(§|\d+(\.\d+)+\b|(section|article|clause|appendix|exhibit|schedule|chapter|part|rule|title)\s+([\divxlc]+\b|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\b)/i;
2441
+
2442
+ function isKickerCandidate(opts) {
1082
2443
  const {
1083
- headingTag,
2444
+ headingLevel,
1084
2445
  headingText,
1085
2446
  headingFontSize,
1086
2447
  kickerTag,
1087
2448
  kickerText,
1088
2449
  kickerTextTransform,
2450
+ kickerFontVariant,
1089
2451
  kickerFontSize,
1090
2452
  kickerLetterSpacing,
1091
2453
  } = opts;
1092
- if (!['h2', 'h3', 'h4'].includes(headingTag)) return false;
2454
+ if (!headingLevel || headingLevel > 4) return false;
1093
2455
  if (!headingText || headingText.length < 3) return false;
1094
2456
  if (/^\/[\w-]+/i.test(headingText.replace(/^"|"$/g, '').trim())) return false;
1095
2457
  if (!(headingFontSize >= 20)) return false;
@@ -1097,9 +2459,13 @@ function isRepeatedKickerCandidate(opts) {
1097
2459
  if (!['p', 'span', 'div', 'small'].includes(kickerTag)) return false;
1098
2460
  if (!kickerText || kickerText.length < 2 || kickerText.length > 34) return false;
1099
2461
  if (/^step\s*\d+/i.test(kickerText) || /^\d{1,2}$/.test(kickerText)) return false;
2462
+ if (KICKER_META_TEXT_RE.test(kickerText)) return false;
2463
+ if (KICKER_DOC_NUMBERING_RE.test(kickerText)) return false;
1100
2464
 
2465
+ const isSmallCaps = /small-caps/.test(kickerFontVariant || '');
1101
2466
  const isUppercased = kickerTextTransform === 'uppercase'
1102
- || (/[A-Z]/.test(kickerText) && !/[a-z]/.test(kickerText));
2467
+ || (/[A-Z]/.test(kickerText) && !/[a-z]/.test(kickerText))
2468
+ || isSmallCaps;
1103
2469
  if (!isUppercased) return false;
1104
2470
  if (!(kickerFontSize > 0 && kickerFontSize <= 14)) return false;
1105
2471
  const minTrackedSpacing = Math.max(1, kickerFontSize * 0.08);
@@ -1107,37 +2473,64 @@ function isRepeatedKickerCandidate(opts) {
1107
2473
  return true;
1108
2474
  }
1109
2475
 
1110
- function collectRepeatedSectionKickerCandidates(doc, getStyle, resolveLetterSpacing) {
2476
+ // Resolve a heading level for the anchor element: 1-4 for h1-h4, aria-level
2477
+ // (default 2) for role="heading" elements, 0 otherwise.
2478
+ function kickerHeadingLevel(heading) {
2479
+ const tag = heading.tagName.toLowerCase();
2480
+ const byTag = /^h([1-6])$/.exec(tag);
2481
+ if (byTag) return parseInt(byTag[1], 10);
2482
+ const role = heading.getAttribute?.('role') || '';
2483
+ if (role.toLowerCase() !== 'heading') return 0;
2484
+ const ariaLevel = parseInt(heading.getAttribute?.('aria-level') || '', 10);
2485
+ return Number.isFinite(ariaLevel) && ariaLevel >= 1 ? ariaLevel : 2;
2486
+ }
2487
+
2488
+ function collectKickerCandidates(doc, getStyle, resolveLetterSpacing) {
1111
2489
  const candidates = [];
1112
- for (const heading of doc.querySelectorAll('h2, h3, h4')) {
1113
- if (heading.closest?.(REPEATED_KICKER_SKIP_SELECTOR)) continue;
2490
+ for (const heading of doc.querySelectorAll('h1, h2, h3, h4, [role="heading"]')) {
2491
+ const headingLevel = kickerHeadingLevel(heading);
2492
+ if (!headingLevel || headingLevel > 4) continue;
2493
+ if (heading.closest?.(KICKER_SKIP_SELECTOR)) continue;
2494
+ // Application contexts (tab panels, dialogs) use compact context labels
2495
+ // above headings to describe state, not to decorate. Same carve-out the
2496
+ // hero-eyebrow rule makes.
2497
+ if (heading.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog')) continue;
1114
2498
  const kicker = heading.previousElementSibling;
1115
- if (!kicker || kicker.closest?.(REPEATED_KICKER_SKIP_SELECTOR)) continue;
1116
- if (isRepeatedKickerCardContext(heading, kicker)) continue;
2499
+ if (!kicker || kicker.closest?.(KICKER_SKIP_SELECTOR)) continue;
2500
+ if (isKickerCardContext(heading, kicker)) continue;
1117
2501
 
1118
2502
  const headingStyle = getStyle(heading);
1119
2503
  const kickerStyle = getStyle(kicker);
2504
+ const headingTag = heading.tagName.toLowerCase();
1120
2505
  const headingText = (heading.textContent || '').replace(/\s+/g, ' ').trim();
1121
2506
  const kickerText = cleanInlineText(kicker) || (kicker.textContent || '').replace(/\s+/g, ' ').trim();
1122
2507
  const headingFontSize = resolveLetterSpacing(headingStyle.fontSize || '', 16) || parseFloat(headingStyle.fontSize) || 0;
1123
2508
  const kickerFontSize = resolveLetterSpacing(kickerStyle.fontSize || '', 16) || parseFloat(kickerStyle.fontSize) || 0;
1124
2509
  const kickerLetterSpacing = resolveLetterSpacing(kickerStyle.letterSpacing || '', kickerFontSize);
1125
2510
 
1126
- if (!isRepeatedKickerCandidate({
1127
- headingTag: heading.tagName.toLowerCase(),
2511
+ if (!isKickerCandidate({
2512
+ headingLevel,
1128
2513
  headingText,
1129
2514
  headingFontSize,
1130
2515
  kickerTag: kicker.tagName.toLowerCase(),
1131
2516
  kickerText,
1132
2517
  kickerTextTransform: kickerStyle.textTransform || '',
2518
+ kickerFontVariant: `${kickerStyle.fontVariant || ''} ${kickerStyle.fontVariantCaps || ''}`,
1133
2519
  kickerFontSize,
1134
2520
  kickerLetterSpacing,
1135
2521
  })) {
1136
2522
  continue;
1137
2523
  }
1138
2524
 
2525
+ // A tracked-caps eyebrow above a hero-scale h1 belongs to
2526
+ // hero-eyebrow-chip (which also covers the accent-bold and dash-prefix
2527
+ // stylings there). Stand down so one element gets one finding.
2528
+ if (headingTag === 'h1' && headingFontSize >= 48 && kickerLetterSpacing >= 1.6) {
2529
+ continue;
2530
+ }
2531
+
1139
2532
  candidates.push({
1140
- headingTag: heading.tagName.toLowerCase(),
2533
+ headingTag,
1141
2534
  headingText: headingText.replace(/^"|"$/g, '').slice(0, 60),
1142
2535
  kickerText: kickerText.slice(0, 40),
1143
2536
  });
@@ -1145,13 +2538,177 @@ function collectRepeatedSectionKickerCandidates(doc, getStyle, resolveLetterSpac
1145
2538
  return candidates;
1146
2539
  }
1147
2540
 
1148
- function checkRepeatedSectionKickersDOM() {
1149
- const candidates = collectRepeatedSectionKickerCandidates(
2541
+ function checkKickerAboveHeadingDOM() {
2542
+ const candidates = collectKickerCandidates(
2543
+ document,
2544
+ (el) => getComputedStyle(el),
2545
+ (value, fontSize) => resolveLengthPx(value, fontSize) || 0,
2546
+ );
2547
+ return checkKickerAboveHeading({ candidates });
2548
+ }
2549
+
2550
+ // ── Numbered section labels ─────────────────────────────────────────────────
2551
+ // Sibling of the kicker-above-heading rule: instead of a tracked uppercase word,
2552
+ // the section scaffold is a tiny numeric index riding beside each section
2553
+ // heading — bare and zero-padded, or an index joined to a short micro-label
2554
+ // by a separator glyph. The kicker rule deliberately excludes bare 1-2 digit
2555
+ // labels; this rule owns that shape.
2556
+
2557
+ const NUMBERED_LABEL_TAGS = new Set(['span', 'p', 'div', 'small', 'em', 'strong', 'b']);
2558
+
2559
+ // Returns { index, text } when the trimmed text reads as a section index
2560
+ // label, else null. Two accepted shapes: a zero-padded/two-digit bare index,
2561
+ // or a 1-2 digit index followed by a non-word separator and a short label.
2562
+ function parseNumberedLabelText(rawText) {
2563
+ const text = (rawText || '').replace(/\s+/g, ' ').trim();
2564
+ if (!text || text.length > 40) return null;
2565
+ let m = /^(\d{2})$/.exec(text);
2566
+ if (!m) m = /^(\d{1,2})\s*[^\w\s]\s*\S/.exec(text);
2567
+ if (!m) return null;
2568
+ const index = parseInt(m[1], 10);
2569
+ if (!Number.isFinite(index) || index > 40) return null;
2570
+ return { index, text };
2571
+ }
2572
+
2573
+ function isNumberedSectionLabelCandidate(opts) {
2574
+ const {
2575
+ headingTag, headingText, headingFontSize,
2576
+ labelTag, labelIndex, labelText,
2577
+ labelFontSize, labelLetterSpacing, labelFontWeight,
2578
+ labelFontFamily, labelTextTransform, labelColor,
2579
+ } = opts;
2580
+ if (!['h2', 'h3', 'h4'].includes(headingTag)) return false;
2581
+ if (!headingText || headingText.length < 3) return false;
2582
+ if (!labelTag || !NUMBERED_LABEL_TAGS.has(labelTag)) return false;
2583
+ if (labelIndex == null || !labelText) return false;
2584
+ // Tiny rendered size is the tell — a display-scale section number is a
2585
+ // different (deliberate) device and stays legal.
2586
+ if (!(labelFontSize > 0 && labelFontSize <= 13)) return false;
2587
+ // The heading must be visibly larger where we can resolve its size.
2588
+ // clamp()/var() sizes come back unparseable (0) in the static engine —
2589
+ // the remaining gates carry the check there.
2590
+ if (headingFontSize > 0 && headingFontSize < labelFontSize * 1.3) return false;
2591
+ // Deliberate micro-label styling separates the scaffold from incidental
2592
+ // small text: mono face, bold weight, tracking, uppercase, or accent color.
2593
+ const weight = Number(labelFontWeight) || 400;
2594
+ return /mono/i.test(labelFontFamily || '')
2595
+ || weight >= 600
2596
+ || (labelLetterSpacing || 0) >= 0.5
2597
+ || (labelTextTransform || '') === 'uppercase'
2598
+ || isAccentColor(labelColor || '');
2599
+ }
2600
+
2601
+ function collectNumberedSectionLabelCandidates(doc, getStyle, resolveLetterSpacing) {
2602
+ const candidates = [];
2603
+ const seenLabels = new Set();
2604
+ for (const heading of doc.querySelectorAll('h2, h3, h4')) {
2605
+ if (heading.closest?.(KICKER_SKIP_SELECTOR)) continue;
2606
+ // The index sits either directly before the heading, or before the
2607
+ // wrapper the heading leads (label | <div><h2>…</h2>…</div>).
2608
+ let label = heading.previousElementSibling;
2609
+ if (!label) {
2610
+ const parent = heading.parentElement;
2611
+ const firstChild = parent?.children?.[0];
2612
+ if (firstChild === heading) label = parent.previousElementSibling;
2613
+ }
2614
+ if (!label || seenLabels.has(label)) continue;
2615
+ if (label.closest?.(KICKER_SKIP_SELECTOR)) continue;
2616
+ if (HEADING_TAGS.has(label.tagName.toLowerCase())) continue;
2617
+ if (isKickerCardContext(heading, label)) continue;
2618
+
2619
+ const labelText = cleanInlineText(label) || (label.textContent || '').replace(/\s+/g, ' ').trim();
2620
+ const parsed = parseNumberedLabelText(labelText);
2621
+ if (!parsed) continue;
2622
+
2623
+ const headingStyle = getStyle(heading);
2624
+ const labelStyle = getStyle(label);
2625
+ const headingText = (heading.textContent || '').replace(/\s+/g, ' ').trim();
2626
+ const headingFontSize = resolveLetterSpacing(headingStyle.fontSize || '', 16) || parseFloat(headingStyle.fontSize) || 0;
2627
+ const labelFontSize = resolveLetterSpacing(labelStyle.fontSize || '', 16) || parseFloat(labelStyle.fontSize) || 0;
2628
+
2629
+ if (!isNumberedSectionLabelCandidate({
2630
+ headingTag: heading.tagName.toLowerCase(),
2631
+ headingText,
2632
+ headingFontSize,
2633
+ labelTag: label.tagName.toLowerCase(),
2634
+ labelIndex: parsed.index,
2635
+ labelText: parsed.text,
2636
+ labelFontSize,
2637
+ labelLetterSpacing: resolveLetterSpacing(labelStyle.letterSpacing || '', labelFontSize),
2638
+ labelFontWeight: labelStyle.fontWeight || '',
2639
+ labelFontFamily: labelStyle.fontFamily || '',
2640
+ labelTextTransform: labelStyle.textTransform || '',
2641
+ labelColor: labelStyle.color || '',
2642
+ })) {
2643
+ continue;
2644
+ }
2645
+
2646
+ seenLabels.add(label);
2647
+ candidates.push({
2648
+ index: parsed.index,
2649
+ labelText: parsed.text.slice(0, 24),
2650
+ headingTag: heading.tagName.toLowerCase(),
2651
+ headingText: headingText.replace(/^"|"$/g, '').slice(0, 60),
2652
+ });
2653
+ }
2654
+ return candidates;
2655
+ }
2656
+
2657
+ function checkNumberedSectionLabels(opts) {
2658
+ const { candidates, minCount = 2 } = opts;
2659
+ if (!Array.isArray(candidates) || candidates.length < minCount) return [];
2660
+ // A repeated identical number is some other device; the scaffold counts up.
2661
+ const distinctIndices = new Set(candidates.map(c => c.index));
2662
+ if (distinctIndices.size < 2) return [];
2663
+ return candidates.map(candidate => ({
2664
+ id: 'numbered-section-labels',
2665
+ snippet: `tiny numbered label "${candidate.labelText}" beside ${candidate.headingTag} "${candidate.headingText}" (${candidates.length} on page)`,
2666
+ }));
2667
+ }
2668
+
2669
+ function checkNumberedSectionLabelsFromDoc(doc, win) {
2670
+ const candidates = collectNumberedSectionLabelCandidates(
2671
+ doc,
2672
+ (el) => win.getComputedStyle(el),
2673
+ (value, fontSize) => resolveLengthPx(value, fontSize) || 0,
2674
+ );
2675
+ return checkNumberedSectionLabels({ candidates });
2676
+ }
2677
+
2678
+ function checkNumberedSectionLabelsDOM() {
2679
+ const candidates = collectNumberedSectionLabelCandidates(
1150
2680
  document,
1151
2681
  (el) => getComputedStyle(el),
1152
2682
  (value, fontSize) => resolveLengthPx(value, fontSize) || 0,
1153
2683
  );
1154
- return checkRepeatedSectionKickers({ candidates });
2684
+ return checkNumberedSectionLabels({ candidates });
2685
+ }
2686
+
2687
+ // Em-dash overuse (ADVISORY) — pure logic shared by the browser DOM check.
2688
+ // Mirrors the regex/static-HTML analyzer in engines/regex/detect-text.mjs:
2689
+ // two gates (absolute floor + density) so a long article using a few dashes is
2690
+ // left alone while a short, dash-per-clause page is flagged. Operates on
2691
+ // already-rendered text, so no HTML-entity decoding is needed (the browser has
2692
+ // resolved `&mdash;` to the literal glyph). Exported for jsdom unit tests.
2693
+ function checkEmDashOveruse(text) {
2694
+ const body = typeof text === 'string' ? text.replace(/\s+/g, ' ') : '';
2695
+ let count = 0;
2696
+ const re = /[—]|--(?=\S)/g;
2697
+ while (re.exec(body) !== null) count++;
2698
+ if (count < EM_DASH_FLOOR) return [];
2699
+ if (body.length > count * EM_DASH_CHARS_PER_DASH) return [];
2700
+ return [{ id: 'em-dash-overuse', snippet: `${count} em-dashes in body text` }];
2701
+ }
2702
+
2703
+ function checkEmDashOveruseDOM() {
2704
+ const body = document.body;
2705
+ if (!body) return [];
2706
+ // innerText reflects rendered, visible text; fall back to textContent for
2707
+ // engines (jsdom) that don't compute innerText.
2708
+ const text = typeof body.innerText === 'string' && body.innerText
2709
+ ? body.innerText
2710
+ : (body.textContent || '');
2711
+ return checkEmDashOveruse(text);
1155
2712
  }
1156
2713
 
1157
2714
  function checkElementMotionDOM(el) {
@@ -1170,7 +2727,14 @@ function checkElementMotionDOM(el) {
1170
2727
  function checkElementGlowDOM(el) {
1171
2728
  const tag = el.tagName.toLowerCase();
1172
2729
  const style = getComputedStyle(el);
1173
- if (!style.boxShadow || style.boxShadow === 'none') return [];
2730
+ const boxShadow = style.boxShadow && style.boxShadow !== 'none' ? style.boxShadow : '';
2731
+ // text-shadow inherits: only check the element that introduces it, so one
2732
+ // declaration doesn't produce a finding on every descendant.
2733
+ let textShadow = style.textShadow && style.textShadow !== 'none' ? style.textShadow : '';
2734
+ if (textShadow && el.parentElement && getComputedStyle(el.parentElement).textShadow === textShadow) {
2735
+ textShadow = '';
2736
+ }
2737
+ if (!boxShadow && !textShadow) return [];
1174
2738
  // Use parent's background — glow radiates outward, so the surrounding context matters
1175
2739
  // If resolveBackground returns null (gradient), try to infer from the gradient colors
1176
2740
  let parentBg = el.parentElement ? resolveBackground(el.parentElement) : resolveBackground(el);
@@ -1193,7 +2757,7 @@ function checkElementGlowDOM(el) {
1193
2757
  cur = cur.parentElement;
1194
2758
  }
1195
2759
  }
1196
- return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg: parentBg });
2760
+ return checkGlow({ tag, boxShadow, textShadow, effectiveBg: parentBg });
1197
2761
  }
1198
2762
 
1199
2763
  function checkElementAIPaletteDOM(el) {
@@ -1253,6 +2817,131 @@ function checkElementAIPaletteDOM(el) {
1253
2817
  return findings;
1254
2818
  }
1255
2819
 
2820
+ // ─── Decorative radial spotlight glow ───────────────────────────────────────
2821
+ // A soft, low-opacity chromatic radial-gradient fading to transparent, painted
2822
+ // as a decorative wash behind a hero or section. The translucent sibling of the
2823
+ // `radial-halo` tell: `radial-halo` requires a saturated, near-opaque center on
2824
+ // a dark page; this catches the low-alpha "spotlight" the halo gate lets slip
2825
+ // (e.g. `radial-gradient(circle at 52% 38%, rgba(80,111,255,0.26),
2826
+ // transparent 44%)`). The two alpha bands are disjoint, so they never
2827
+ // double-report the same declaration.
2828
+ const SPOTLIGHT_COLOR_TOKEN_RE = /(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color-mix)\([^)]*(?:\([^)]*\))?[^)]*\)|#[0-9a-f]{3,8}\b|\btransparent\b/i;
2829
+
2830
+ // Parse the FIRST non-repeating radial-gradient in a background value into its
2831
+ // ordered color stops. Each stop is { color: {r,g,b,a} | null, transparent }.
2832
+ // Returns null when there is no plain radial-gradient to read.
2833
+ function parseRadialGradientStops(value) {
2834
+ if (!value || !/radial-gradient/i.test(value)) return null;
2835
+ const gradRe = /(repeating-)?radial-gradient\(/gi;
2836
+ let g;
2837
+ while ((g = gradRe.exec(value)) !== null) {
2838
+ if (g[1]) continue; // repeating-* is a pattern, not a spotlight
2839
+ let depth = 0, end = -1;
2840
+ const open = value.indexOf('(', g.index);
2841
+ for (let i = open; i < value.length; i++) {
2842
+ if (value[i] === '(') depth++;
2843
+ else if (value[i] === ')') { depth--; if (depth === 0) { end = i; break; } }
2844
+ }
2845
+ if (end < 0) return null;
2846
+ const args = splitTopLevelCommas(value.slice(open + 1, end));
2847
+ // The optional prelude (shape / size / `at <pos>`) carries no color token.
2848
+ const stopArgs = args.filter(a => SPOTLIGHT_COLOR_TOKEN_RE.test(a));
2849
+ if (stopArgs.length < 2) return null;
2850
+ return stopArgs.map(a => {
2851
+ const tok = a.match(SPOTLIGHT_COLOR_TOKEN_RE);
2852
+ if (!tok) return { color: null, transparent: false };
2853
+ if (/^transparent$/i.test(tok[0])) return { color: null, transparent: true };
2854
+ const color = parseAnyColor(tok[0]);
2855
+ return { color, transparent: !!color && (color.a ?? 1) <= 0.05 };
2856
+ });
2857
+ }
2858
+ return null;
2859
+ }
2860
+
2861
+ // Pure gate. `label` is a stable identifier the fixture test keys on.
2862
+ function checkRadialSpotlight({ gradientValue, width, height, label }) {
2863
+ const stops = parseRadialGradientStops(gradientValue);
2864
+ if (!stops || stops.length < 2) return [];
2865
+
2866
+ // Must fade OUT: the last stop is transparent / near-zero alpha. A gradient
2867
+ // between two visible surfaces is a real background, not a floating glow.
2868
+ const last = stops[stops.length - 1];
2869
+ const lastAlpha = last.transparent ? 0 : (last.color ? (last.color.a ?? 1) : 1);
2870
+ if (lastAlpha > 0.05) return [];
2871
+
2872
+ // The visible (non-transparent, parseable) color stops.
2873
+ const colored = stops.filter(s => !s.transparent && s.color && (s.color.a ?? 1) > 0.05);
2874
+ if (colored.length === 0) return [];
2875
+ // One soft glow, not a multi-color composition: at most two visible stops.
2876
+ if (colored.length > 2) return [];
2877
+ // Every visible stop must be LOW opacity. Any opaque stop means a real fill
2878
+ // or a saturated halo (`radial-halo`'s job), not this translucent spotlight.
2879
+ if (colored.some(s => (s.color.a ?? 1) >= 0.45)) return [];
2880
+ // At least one visible stop must be chromatic. A neutral (grayscale)
2881
+ // near-black / near-white vignette is a legitimate lighting move, exempt.
2882
+ const chromatic = colored.find(s => hasChroma(s.color, 24));
2883
+ if (!chromatic) return [];
2884
+
2885
+ // Decorative-scale gate. Badges, avatars, and actual small "lights" are
2886
+ // exempt; a spotlight glow only reads as slop when it washes a large surface.
2887
+ if (!(width >= 240 && height >= 160)) return [];
2888
+
2889
+ const alpha = (chromatic.color.a ?? 1).toFixed(2);
2890
+ const name = label || 'section';
2891
+ return [{
2892
+ id: 'radial-spotlight-glow',
2893
+ snippet: `radial-gradient spotlight glow "${name}" (${colorToHex(chromatic.color)} a${alpha} → transparent) on ${Math.round(width)}x${Math.round(height)} surface`,
2894
+ }];
2895
+ }
2896
+
2897
+ // Read the raw radial-gradient source off an element's computed style, with a
2898
+ // fallback to the `background` shorthand and the inline style attribute for
2899
+ // engines that don't decompose the shorthand into backgroundImage.
2900
+ function elementGradientValue(style, el) {
2901
+ const bgImage = style.backgroundImage && style.backgroundImage !== 'none' ? style.backgroundImage : '';
2902
+ if (/radial-gradient/i.test(bgImage)) return bgImage;
2903
+ const bg = style.background || '';
2904
+ if (/radial-gradient/i.test(bg)) return bg;
2905
+ const rawStyle = el?.getAttribute?.('style') || '';
2906
+ const m = rawStyle.match(/background(?:-image)?\s*:\s*([^;]+)/i);
2907
+ if (m && /radial-gradient/i.test(m[1])) return m[1];
2908
+ return '';
2909
+ }
2910
+
2911
+ function spotlightLabel(el) {
2912
+ const dataName = el.getAttribute?.('data-name');
2913
+ if (dataName) return dataName;
2914
+ if (typeof el.id === 'string' && el.id) return el.id;
2915
+ const cls = typeof el.className === 'string' ? el.className.trim().split(/\s+/)[0] : '';
2916
+ if (cls) return cls;
2917
+ return el.tagName ? el.tagName.toLowerCase() : 'section';
2918
+ }
2919
+
2920
+ function checkElementRadialSpotlightDOM(el) {
2921
+ const style = getComputedStyle(el);
2922
+ const gradientValue = elementGradientValue(style, el);
2923
+ if (!gradientValue) return [];
2924
+ const rect = el.getBoundingClientRect();
2925
+ return checkRadialSpotlight({
2926
+ gradientValue,
2927
+ width: rect.width,
2928
+ height: rect.height,
2929
+ label: spotlightLabel(el),
2930
+ });
2931
+ }
2932
+
2933
+ function checkElementRadialSpotlight(el, style, tag, window) {
2934
+ const gradientValue = elementGradientValue(style, el);
2935
+ if (!gradientValue) return [];
2936
+ // Static engine does no layout — read explicit pixel dimensions from CSS.
2937
+ return checkRadialSpotlight({
2938
+ gradientValue,
2939
+ width: parseFloat(style.width) || 0,
2940
+ height: parseFloat(style.height) || 0,
2941
+ label: spotlightLabel(el),
2942
+ });
2943
+ }
2944
+
1256
2945
  const QUALITY_TEXT_TAGS = new Set(['p', 'li', 'td', 'th', 'dd', 'blockquote', 'figcaption']);
1257
2946
 
1258
2947
  // Resolve a CSS font-size value to pixels by walking up the parent chain.
@@ -1374,6 +3063,55 @@ function textDescendantsFlushSides(el, rect) {
1374
3063
  return flush;
1375
3064
  }
1376
3065
 
3066
+ // Screen-reader-only ("visually hidden") text is exempt from the tiny-text
3067
+ // floors: it is never rendered, so its size is irrelevant. Detect the two
3068
+ // standard idioms — a known sr-only class on the element or an ancestor, and
3069
+ // the clip / 1px-box pattern. Works in both jsdom (declared styles) and the
3070
+ // browser (computed styles).
3071
+ const SR_ONLY_SELECTOR = '.sr-only, .visually-hidden, .visuallyhidden, .screen-reader, .screen-reader-only, .screenreader, .a11y-hidden, .hidden-visually, [class*="sr-only" i], [class*="visually-hidden" i], [class*="visuallyhidden" i], [class*="screen-reader" i], [class*="screenreader" i]';
3072
+ function isVisuallyHidden(el, style) {
3073
+ if ((el.matches && el.matches(SR_ONLY_SELECTOR)) || (el.closest && el.closest(SR_ONLY_SELECTOR))) return true;
3074
+ const pos = style.position || '';
3075
+ if (pos === 'absolute' || pos === 'fixed') {
3076
+ const clip = style.clip || '';
3077
+ const clipPath = style.clipPath || style.webkitClipPath || style['clip-path'] || '';
3078
+ if (/rect\(\s*0/.test(clip) || /inset\(\s*(?:50%|99|100%)/.test(clipPath)) return true;
3079
+ const w = parseFloat(style.width);
3080
+ const h = parseFloat(style.height);
3081
+ const overflow = style.overflow || '';
3082
+ if ((w === 1 || h === 1) && (overflow === 'hidden' || overflow === 'clip')) return true;
3083
+ }
3084
+ return false;
3085
+ }
3086
+
3087
+ // Elements whose text is never painted: document metadata and script/style
3088
+ // payloads. Their JS / CSS / JSON-LD text satisfies `hasDirectText`, and on
3089
+ // sites that set `html { font-size: 62.5% }` their inherited computed size is
3090
+ // 10px — so the text-size floors flag them as tiny body copy even though
3091
+ // nothing renders (issue #408: dozens of phantom "10px body text" findings on
3092
+ // every Shopify page). Exclude them, plus anything the cascade resolves to
3093
+ // display:none / visibility:hidden. The jsdom path can't lay out, so the
3094
+ // tag/attribute-based exclusions carry the weight there; the display checks are
3095
+ // computed-style reads that resolve without layout in both adapters.
3096
+ const NON_RENDERED_TAGS = new Set([
3097
+ 'script', 'style', 'title', 'noscript', 'template', 'head',
3098
+ 'meta', 'link', 'base', 'param', 'source', 'track', 'datalist',
3099
+ 'col', 'colgroup', 'map', 'area',
3100
+ ]);
3101
+ function isNonRenderedText(el, tag, style) {
3102
+ const t = (tag || '').toLowerCase();
3103
+ if (NON_RENDERED_TAGS.has(t)) return true;
3104
+ // Descendants of <head> never render even when the tag itself would
3105
+ // (some sites nest <noscript>/<template> content there).
3106
+ if (el && el.closest && el.closest('head')) return true;
3107
+ if (style) {
3108
+ if (style.display === 'none') return true;
3109
+ const vis = style.visibility;
3110
+ if (vis === 'hidden' || vis === 'collapse') return true;
3111
+ }
3112
+ return false;
3113
+ }
3114
+
1377
3115
  // Pure quality checks. Most run on computed CSS and DOM-only inputs (work in
1378
3116
  // jsdom and the browser). Two checks (line-length, cramped-padding) gate on
1379
3117
  // element rect dimensions, which jsdom can't compute — pass `rect: null` from
@@ -1384,8 +3122,13 @@ function textDescendantsFlushSides(el, rect) {
1384
3122
  function checkQuality(opts) {
1385
3123
  const { el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect, lineMax = 80, viewportWidth = 0, win = null } = opts;
1386
3124
  const findings = [];
1387
- // Skip browser extension injected elements
1388
- const elId = el.id || '';
3125
+ // Skip browser extension injected elements. Read the id via getAttribute
3126
+ // whenever `el.id` is not a string: on a <form> (and other
3127
+ // [LegacyOverrideBuiltIns] hosts) a named control like <input name="id">
3128
+ // shadows the builtin `id` getter and returns the control element, whose
3129
+ // `.startsWith` is undefined and throws (issue #407 — every Shopify product
3130
+ // form ships an <input name="id">).
3131
+ const elId = typeof el.id === 'string' ? el.id : (el.getAttribute?.('id') || '');
1389
3132
  if (elId.startsWith('claude-') || elId.startsWith('cic-')) return findings;
1390
3133
 
1391
3134
  // --- Line length too long --- (browser-only: needs rect.width)
@@ -1653,11 +3396,67 @@ function checkQuality(opts) {
1653
3396
  const skipTags = ['sub', 'sup', 'code', 'kbd', 'samp', 'var', 'caption', 'figcaption'];
1654
3397
  const inUIContext = el.closest && el.closest('button, a, label, summary, pre, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], nav, footer, [aria-hidden="true"], [class*="badge" i], [class*="caption" i], [class*="chip" i], [class*="code" i], [class*="console" i], [class*="diff" i], [class*="label" i], [class*="meta" i], [class*="mock" i], [class*="pill" i], [class*="preview" i], [class*="tag" i], [class*="terminal" i], [class*="writes" i]');
1655
3398
  const isUppercase = style.textTransform === 'uppercase';
1656
- if (!skipTags.includes(tag) && !inUIContext && !isUppercase) {
3399
+ if (!skipTags.includes(tag) && !inUIContext && !isUppercase && !isNonRenderedText(el, tag, style)) {
1657
3400
  findings.push({ id: 'tiny-text', snippet: `${fontSize}px body text` });
1658
3401
  }
1659
3402
  }
1660
3403
 
3404
+ // --- Undersized functional / UI text ---
3405
+ // Complements `tiny-text` above, which owns long body copy and deliberately
3406
+ // EXEMPTS the UI furniture layer (nav, footer, links, buttons, labels,
3407
+ // uppercase micro-labels). This rule targets exactly that blind spot: the
3408
+ // interactive and short content-bearing text — nav items, buttons, labels,
3409
+ // table cells, meta rows, timecodes — shipped below an 11px floor.
3410
+ //
3411
+ // The live failure it closes: a build shipped its entire furniture layer at
3412
+ // 8px, and the design hook waved it through because 8px had been added to
3413
+ // the DESIGN.md size ramp. Being on the ramp is a token argument, not a
3414
+ // legibility one, so this rule ignores the design system entirely — a value
3415
+ // on the ramp is still flagged.
3416
+ //
3417
+ // Floors: 11px for anything functional. The floor holds inside a footer;
3418
+ // only NON-interactive legal smallprint gets the softer 10px floor. Exempts
3419
+ // sup/sub, visually-hidden (sr-only) text, and code/terminal contexts.
3420
+ // Uppercase letterspaced micro-labels are still functional — not exempt.
3421
+ {
3422
+ const directText = [...el.childNodes]
3423
+ .filter(n => n.nodeType === 3)
3424
+ .map(n => n.textContent || '')
3425
+ .join('')
3426
+ .replace(/\s+/g, ' ')
3427
+ .trim();
3428
+ const dtLen = directText.length;
3429
+ // `option` renders (in native select popups) so it stays a local skip;
3430
+ // script/style/title/noscript/head-descendants and display:none /
3431
+ // visibility:hidden are handled by isNonRenderedText (shared with tiny-text).
3432
+ const UI_SKIP_TAGS = new Set(['sub', 'sup', 'option']);
3433
+ // jsdom resolves the parent chain in resolveFontSizePx, so em/rem/%-sized
3434
+ // text that computes at or above the floor never reaches here. The browser
3435
+ // adapter additionally catches values only resolvable with real layout
3436
+ // (e.g. viewport-relative units, cascade winners set in linked sheets).
3437
+ if (fontSize > 0 && fontSize < 11 && dtLen >= 2 && !UI_SKIP_TAGS.has(tag) && !isNonRenderedText(el, tag, style)) {
3438
+ const EXEMPT_CONTEXT = 'pre, code, kbd, samp, var, svg, [aria-hidden="true"], [class*="terminal" i], [class*="console" i], [class*="code" i], [class*="mock" i], [class*="editor" i], [class*="syntax" i], [class*="diff" i]';
3439
+ const isExemptContext = (el.matches && el.matches(EXEMPT_CONTEXT)) || (el.closest && el.closest(EXEMPT_CONTEXT));
3440
+ if (!isExemptContext && !isVisuallyHidden(el, style)) {
3441
+ const INTERACTIVE = 'a[href], button, summary, label, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"], [role="checkbox"], [role="radio"], [role="switch"], [role="treeitem"], [tabindex]';
3442
+ const FURNITURE = 'nav, [role="navigation"], td, th, [role="gridcell"], [role="cell"], caption, figcaption, dt, dd, footer, [class*="meta" i], [class*="label" i], [class*="badge" i], [class*="chip" i], [class*="pill" i], [class*="tag" i], [class*="kicker" i], [class*="eyebrow" i], [class*="breadcrumb" i], [class*="timestamp" i], [class*="category" i], [class*="caption" i], [class*="nav" i]';
3443
+ const SMALLPRINT = 'small, footer, [class*="legal" i], [class*="copyright" i], [class*="fineprint" i], [class*="fine-print" i], [class*="smallprint" i], [class*="small-print" i], [class*="disclaimer" i], [class*="disclosure" i], [class*="footnote" i]';
3444
+ const isInteractive = (el.matches && el.matches(INTERACTIVE)) || (el.closest && el.closest(INTERACTIVE));
3445
+ const isFurniture = (el.matches && el.matches(FURNITURE)) || (el.closest && el.closest(FURNITURE));
3446
+ const isSmallprint = (el.matches && el.matches(SMALLPRINT)) || (el.closest && el.closest(SMALLPRINT));
3447
+ const floor = (!isInteractive && isSmallprint) ? 10 : 11;
3448
+ // Fire on functional text only: interactive, structural furniture, or
3449
+ // any short (<=20-char) run — the label / meta / timecode shape. Long
3450
+ // non-furniture body copy stays with `tiny-text`, so the two rules
3451
+ // never double-flag the same element.
3452
+ if (fontSize < floor && (isInteractive || isFurniture || dtLen <= 20)) {
3453
+ const excerpt = directText.slice(0, 40);
3454
+ findings.push({ id: 'undersized-ui-text', snippet: `${fontSize}px functional text "${excerpt}" (below ${floor}px floor)` });
3455
+ }
3456
+ }
3457
+ }
3458
+ }
3459
+
1661
3460
  // --- All-caps body text ---
1662
3461
  if (hasDirectText && textLen > 30 && style.textTransform === 'uppercase') {
1663
3462
  if (!['h1','h2','h3','h4','h5','h6'].includes(tag)) {
@@ -1750,7 +3549,7 @@ function checkElementQuality(el, style, tag, window) {
1750
3549
  return checkQuality({ el, tag, style, hasDirectText, textLen, fontSize, lineHeightPx, letterSpacingPx, rect: null, win: window });
1751
3550
  }
1752
3551
 
1753
- function checkElementBorders(tag, style, overrides, resolvedRadius) {
3552
+ function checkElementBorders(tag, style, overrides, resolvedRadius, el = null) {
1754
3553
  const sides = ['Top', 'Right', 'Bottom', 'Left'];
1755
3554
  const widths = {}, colors = {};
1756
3555
  for (const s of sides) {
@@ -1777,7 +3576,12 @@ function checkElementBorders(tag, style, overrides, resolvedRadius) {
1777
3576
  const radius = resolvedRadius != null
1778
3577
  ? resolvedRadius
1779
3578
  : (parseFloat(style.borderRadius) || 0);
1780
- return checkBorders(tag, widths, colors, radius);
3579
+ const ownBg = parseAnyColor(style.backgroundColor);
3580
+ return checkBorders(tag, widths, colors, radius, {
3581
+ tabContext: isTabContextElement(el),
3582
+ statusContext: isStatusContextElement(el),
3583
+ badgeLike: !!(ownBg && (ownBg.a ?? 1) > 0.1),
3584
+ });
1781
3585
  }
1782
3586
 
1783
3587
  function checkElementColors(el, style, tag, window, customPropMap, hasAnchorInheritRule) {
@@ -1818,12 +3622,32 @@ function checkElementColors(el, style, tag, window, customPropMap, hasAnchorInhe
1818
3622
  }
1819
3623
  }
1820
3624
 
3625
+ // Own background: resolve var()/oklch() tokens through the custom-property
3626
+ // map first (mirrors the textColor path above). Without this a chip whose
3627
+ // background is `var(--sev)` reads as no-own-bg in the static engine and
3628
+ // the styled-control contrast exception never engages.
3629
+ let ownBg = (customPropMap ? parseColorResolved(style.backgroundColor, customPropMap) : null)
3630
+ || readOwnBackgroundColor(el, style);
3631
+
3632
+ // Full-cover surface pseudo (static): the cascade pass marks elements
3633
+ // whose ::before/::after paints an opaque covering surface. When the
3634
+ // element itself has no usable own background, that pseudo is the real
3635
+ // surface for contrast purposes.
3636
+ let finalEffectiveBg = effectiveBg;
3637
+ if ((!ownBg || (ownBg.a ?? 1) <= 0.5) && typeof window.getPseudoSurface === 'function') {
3638
+ const pseudoSurface = window.getPseudoSurface(el);
3639
+ if (pseudoSurface) {
3640
+ ownBg = pseudoSurface;
3641
+ finalEffectiveBg = pseudoSurface;
3642
+ }
3643
+ }
3644
+
1821
3645
  return checkColors({
1822
3646
  tag,
1823
3647
  textColor,
1824
- bgColor: readOwnBackgroundColor(el, style),
1825
- effectiveBg,
1826
- effectiveBgStops: effectiveBg ? null : resolveGradientStops(el, window),
3648
+ bgColor: ownBg,
3649
+ effectiveBg: finalEffectiveBg,
3650
+ effectiveBgStops: finalEffectiveBg ? null : resolveGradientStops(el, window, customPropMap),
1827
3651
  fontSize: parseFloat(style.fontSize) || 16,
1828
3652
  fontWeight: parseInt(style.fontWeight) || 400,
1829
3653
  hasDirectText,
@@ -1834,6 +3658,50 @@ function checkElementColors(el, style, tag, window, customPropMap, hasAnchorInhe
1834
3658
  });
1835
3659
  }
1836
3660
 
3661
+ // Static-engine adapter for hover-state contrast. Relies on the static
3662
+ // cascade's hover pass (css-cascade.mjs) exposing a per-element hover style
3663
+ // via window.getHoverStyle — present only when a :hover rule changed the
3664
+ // element's color or background-color relative to its resting state.
3665
+ function checkElementHoverContrast(el, style, tag, window) {
3666
+ if (typeof window.getHoverStyle !== 'function') return [];
3667
+ const hover = window.getHoverStyle(el);
3668
+ if (!hover) return [];
3669
+
3670
+ const directText = [...el.childNodes].filter(n => n.nodeType === 3).map(n => n.textContent).join('');
3671
+ if (directText.trim().length === 0) return [];
3672
+
3673
+ const textColor = parseAnyColor(hover.color);
3674
+ if (!textColor || (textColor.a != null && textColor.a < 1)) return [];
3675
+
3676
+ const restingOwnBg = parseAnyColor(style.backgroundColor);
3677
+ const hoverOwnBg = parseAnyColor(hover.backgroundColor);
3678
+ const ownBg = hoverOwnBg || restingOwnBg;
3679
+
3680
+ // Effective hover background: the element's own hover bg composited over
3681
+ // whatever sits underneath. Bail when the surface can't be resolved to a
3682
+ // solid color — gradient ancestors are handled (as at rest) by the
3683
+ // resting-state check, not duplicated here.
3684
+ let bg = null;
3685
+ if (ownBg && ownBg.a >= 0.99) {
3686
+ bg = ownBg;
3687
+ } else {
3688
+ const under = resolveBackground(el.parentElement || el, window, null);
3689
+ if (!under) return [];
3690
+ bg = ownBg && ownBg.a > 0.1 ? compositeColorOver(ownBg, under) : under;
3691
+ }
3692
+
3693
+ return checkHoverContrast({
3694
+ tag,
3695
+ textColor,
3696
+ bg,
3697
+ ownBgAlpha: ownBg ? ownBg.a ?? 1 : null,
3698
+ fontSize: parseFloat(style.fontSize) || 16,
3699
+ fontWeight: parseInt(style.fontWeight) || 400,
3700
+ hasDirectText: true,
3701
+ isEmojiOnly: isEmojiOnlyText(directText),
3702
+ });
3703
+ }
3704
+
1837
3705
  function checkElementIconTile(el, tag, window) {
1838
3706
  if (!HEADING_TAGS.has(tag)) return [];
1839
3707
  const sibling = el.previousElementSibling;
@@ -1902,7 +3770,8 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) {
1902
3770
  return checkHeroEyebrow({
1903
3771
  headingTag: tag,
1904
3772
  headingText: el.textContent || '',
1905
- headingFontSize: parseFloat(headingFontSizeRaw) || 0,
3773
+ headingFontSize: resolveHeroHeadingSizePx(headingFontSizeRaw),
3774
+ headingInApplicationContext: !!el.closest?.('[role="tabpanel"], [role="dialog"], [role="application"], dialog'),
1906
3775
  siblingTag: sibling.tagName.toLowerCase(),
1907
3776
  siblingText: sibling.textContent || '',
1908
3777
  siblingTextTransform: sibStyle.textTransform || '',
@@ -1910,16 +3779,21 @@ function checkElementHeroEyebrow(el, style, tag, window, customPropMap) {
1910
3779
  siblingLetterSpacing: resolveLengthPx(letterSpacingRaw, siblingFontSize) || 0,
1911
3780
  siblingFontWeight: fontWeightRaw || '',
1912
3781
  siblingColor: colorRaw || '',
3782
+ // Static cascade marks elements matched by a ::before/::after rule
3783
+ // whose geometry is a short chromatic dash (css-cascade.mjs).
3784
+ siblingHasAccentDashPseudo: typeof window.hasAccentDashPseudo === 'function'
3785
+ ? window.hasAccentDashPseudo(sibling)
3786
+ : false,
1913
3787
  });
1914
3788
  }
1915
3789
 
1916
- function checkRepeatedSectionKickersFromDoc(doc, win) {
1917
- const candidates = collectRepeatedSectionKickerCandidates(
3790
+ function checkKickerAboveHeadingFromDoc(doc, win) {
3791
+ const candidates = collectKickerCandidates(
1918
3792
  doc,
1919
3793
  (el) => win.getComputedStyle(el),
1920
3794
  (value, fontSize) => resolveLengthPx(value, fontSize) || 0,
1921
3795
  );
1922
- return checkRepeatedSectionKickers({ candidates });
3796
+ return checkKickerAboveHeading({ candidates });
1923
3797
  }
1924
3798
 
1925
3799
  function checkElementMotion(tag, style) {
@@ -1933,8 +3807,10 @@ function checkElementMotion(tag, style) {
1933
3807
  }
1934
3808
 
1935
3809
  function checkElementGlow(tag, style, effectiveBg) {
1936
- if (!style.boxShadow || style.boxShadow === 'none') return [];
1937
- return checkGlow({ tag, boxShadow: style.boxShadow, effectiveBg });
3810
+ const boxShadow = style.boxShadow && style.boxShadow !== 'none' ? style.boxShadow : '';
3811
+ const textShadow = style.textShadow && style.textShadow !== 'none' ? style.textShadow : '';
3812
+ if (!boxShadow && !textShadow) return [];
3813
+ return checkGlow({ tag, boxShadow, textShadow, effectiveBg });
1938
3814
  }
1939
3815
 
1940
3816
  // ─── Section 6: Page-Level Checks ───────────────────────────────────────────
@@ -2044,6 +3920,157 @@ function checkLayout() {
2044
3920
  return findings;
2045
3921
  }
2046
3922
 
3923
+ // Heading rhythm (browser-only): a heading binds to the content it
3924
+ // introduces, so its rendered space above must exceed its space below.
3925
+ // Margins alone can't be trusted (collapsing, flex rows, section padding),
3926
+ // so this measures actual getBoundingClientRect gaps between the heading
3927
+ // and the nearest content genuinely above / below it. Fires only when two
3928
+ // or more headings violate the principle — a single occurrence is noise.
3929
+ function checkHeadingRhythmDOM() {
3930
+ const MIN_VIOLATIONS = 2;
3931
+ const CARD_EXEMPT_HEIGHT = 200;
3932
+ const MAX_BELOW_PX = 160; // beyond this the heading isn't binding to nearby content at all
3933
+ const MIN_DEFICIT_PX = 12;
3934
+
3935
+ function isVisibleFlow(el) {
3936
+ const style = getComputedStyle(el);
3937
+ if (style.display === 'none' || style.visibility === 'hidden') return false;
3938
+ if (parseFloat(style.opacity || '1') <= 0.05) return false;
3939
+ if (style.position === 'absolute' || style.position === 'fixed' || style.position === 'sticky') return false;
3940
+ const rect = el.getBoundingClientRect();
3941
+ return rect.width >= 1 && rect.height >= 1;
3942
+ }
3943
+
3944
+ // Edges only count when they share the heading's column — grid layouts
3945
+ // put content beside a heading, and a far-away element in another column
3946
+ // says nothing about the heading's vertical rhythm.
3947
+ function overlapsX(sr, rect) {
3948
+ return Math.min(sr.right, rect.right) - Math.max(sr.left, rect.left) >= 8;
3949
+ }
3950
+
3951
+ // Does this container draw its own top boundary (background, top border,
3952
+ // shadow)? Crossing out of such a container means the container edge is
3953
+ // the separator above the heading, not raw whitespace — exempt.
3954
+ function hasOwnTopBoundary(el) {
3955
+ const style = getComputedStyle(el);
3956
+ const bg = parseAnyColor(style.backgroundColor || '');
3957
+ if (bg && (bg.a ?? 1) > 0.05) return true;
3958
+ if ((parseFloat(style.borderTopWidth) || 0) > 0) return true;
3959
+ if (style.boxShadow && style.boxShadow !== 'none') return true;
3960
+ return false;
3961
+ }
3962
+
3963
+ // Eyebrows, kickers, and index labels sitting directly on top of a
3964
+ // heading belong to the heading's own cluster — space above is measured
3965
+ // from the top of the cluster, not from the label to the heading.
3966
+ function clusterTop(h, rect) {
3967
+ const headingFontSize = parseFloat(getComputedStyle(h).fontSize) || 16;
3968
+ let topEl = h;
3969
+ let top = rect.top;
3970
+ for (let i = 0; i < 3; i++) {
3971
+ const sib = topEl.previousElementSibling;
3972
+ if (!sib || !isVisibleFlow(sib)) break;
3973
+ const sr = sib.getBoundingClientRect();
3974
+ if (!overlapsX(sr, rect)) break;
3975
+ const gap = top - sr.bottom;
3976
+ if (gap < 0 || gap >= 28 || sr.height > 60) break;
3977
+ const text = (sib.textContent || '').trim();
3978
+ const sibFontSize = parseFloat(getComputedStyle(sib).fontSize) || 16;
3979
+ const labelLike = sibFontSize < headingFontSize * 0.75 || text.length <= 40;
3980
+ if (!labelLike || text.length > 80) break;
3981
+ topEl = sib;
3982
+ top = sr.top;
3983
+ }
3984
+ return { topEl, top };
3985
+ }
3986
+
3987
+ // Nearest content edge strictly above the heading cluster. Walks
3988
+ // previous siblings, then out through ancestors. Skips elements that
3989
+ // vertically overlap (flex-row companions, sticky rails) or sit in
3990
+ // another column. Returns null when nothing qualifies — first content
3991
+ // on the page, or the top of a visually bounded container.
3992
+ function edgeAbove(startEl, top, rect) {
3993
+ let node = startEl;
3994
+ while (node && node !== document.body) {
3995
+ let sib = node.previousElementSibling;
3996
+ while (sib) {
3997
+ if (isVisibleFlow(sib)) {
3998
+ const sr = sib.getBoundingClientRect();
3999
+ if (sr.bottom <= top + 2 && overlapsX(sr, rect)) return sr.bottom;
4000
+ }
4001
+ sib = sib.previousElementSibling;
4002
+ }
4003
+ const parent = node.parentElement;
4004
+ if (!parent || parent === document.body) return null;
4005
+ // Leaving a container upward: if it draws its own top edge, that
4006
+ // edge separates the heading from whatever sits above.
4007
+ if (hasOwnTopBoundary(parent)) return null;
4008
+ node = parent;
4009
+ }
4010
+ return null;
4011
+ }
4012
+
4013
+ // Nearest content edge strictly below the heading — the block the
4014
+ // heading introduces. Crosses wrappers freely (headings often share a
4015
+ // row wrapper with an eyebrow or index label).
4016
+ function edgeBelow(h, rect) {
4017
+ let node = h;
4018
+ while (node && node !== document.body) {
4019
+ let sib = node.nextElementSibling;
4020
+ while (sib) {
4021
+ if (isVisibleFlow(sib)) {
4022
+ const sr = sib.getBoundingClientRect();
4023
+ if (sr.top >= rect.bottom - 2 && overlapsX(sr, rect)) return sr.top;
4024
+ }
4025
+ sib = sib.nextElementSibling;
4026
+ }
4027
+ node = node.parentElement;
4028
+ }
4029
+ return null;
4030
+ }
4031
+
4032
+ function insideSmallCard(h) {
4033
+ let cur = h.parentElement;
4034
+ while (cur && cur !== document.body) {
4035
+ if (isCardLikeDOM(cur)) {
4036
+ const cr = cur.getBoundingClientRect();
4037
+ if (cr.height < CARD_EXEMPT_HEIGHT) return true;
4038
+ }
4039
+ cur = cur.parentElement;
4040
+ }
4041
+ return false;
4042
+ }
4043
+
4044
+ const candidates = [];
4045
+ for (const h of document.querySelectorAll('h2, h3, h4')) {
4046
+ if (!isVisibleFlow(h)) continue;
4047
+ const text = (h.textContent || '').trim().replace(/\s+/g, ' ');
4048
+ if (text.length < 3) continue;
4049
+ const rect = h.getBoundingClientRect();
4050
+ const belowTop = edgeBelow(h, rect);
4051
+ if (belowTop == null) continue; // heading introduces nothing measurable
4052
+ const { topEl, top } = clusterTop(h, rect);
4053
+ const aboveBottom = edgeAbove(topEl, top, rect);
4054
+ if (aboveBottom == null) continue; // first content, or bounded container
4055
+ if (insideSmallCard(h)) continue;
4056
+ const above = Math.max(0, top - aboveBottom);
4057
+ const below = Math.max(0, belowTop - rect.bottom);
4058
+ if (below < 6 || below > MAX_BELOW_PX) continue;
4059
+ // Violation: the space above clearly fails to exceed the space below.
4060
+ // Near-equal gaps are ambiguous rather than inverted, so they pass.
4061
+ if (above < below * 0.75 && below - above >= MIN_DEFICIT_PX) {
4062
+ candidates.push({ el: h, tag: h.tagName.toLowerCase(), text: text.slice(0, 60), above, below });
4063
+ }
4064
+ }
4065
+
4066
+ if (candidates.length < MIN_VIOLATIONS) return [];
4067
+ return candidates.map(c => ({
4068
+ type: 'heading-rhythm',
4069
+ detail: `${c.tag} "${c.text}" has ${Math.round(c.above)}px above vs ${Math.round(c.below)}px below — it reads as bound to the block above (${candidates.length} headings on page)`,
4070
+ el: c.el,
4071
+ }));
4072
+ }
4073
+
2047
4074
  // Node page-level checks — take document/window as parameters
2048
4075
 
2049
4076
  function checkPageTypography(doc, win) {
@@ -2186,6 +4213,135 @@ function checkPageLayout(doc, win) {
2186
4213
  return findings;
2187
4214
  }
2188
4215
 
4216
+ // ── Repeated text inside one container ──────────────────────────────────────
4217
+ // The same literal string rendered 3+ times in structurally different spots
4218
+ // inside one bordered/elevated container — typically a status word wired
4219
+ // into every slot of a card template. Legitimate repetition is structural:
4220
+ // table columns, calendar grids, nav/menu lists, and templated sibling rows
4221
+ // all repeat text in *parallel* positions, so occurrences whose element
4222
+ // paths inside the container are identical (or live in dedicated repetition
4223
+ // structures) never count. Only 3+ occurrences at 3+ distinct structural
4224
+ // positions flag.
4225
+
4226
+ const REPEATED_TEXT_SKIP_SELECTOR = [
4227
+ 'table',
4228
+ 'select',
4229
+ 'datalist',
4230
+ 'nav',
4231
+ 'menu',
4232
+ '[role="navigation"]',
4233
+ '[role="menu"]',
4234
+ '[role="menubar"]',
4235
+ '[role="listbox"]',
4236
+ '[role="grid"]',
4237
+ '[role="tablist"]',
4238
+ '[role="radiogroup"]',
4239
+ '[aria-hidden="true"]',
4240
+ ].join(',');
4241
+
4242
+ const REPEATED_TEXT_CONTAINER_TAGS = new Set([
4243
+ 'div', 'section', 'article', 'aside', 'main', 'figure', 'form', 'fieldset', 'details', 'li',
4244
+ ]);
4245
+
4246
+ // A container worth attributing text to: visibly bounded (border on most
4247
+ // sides or an elevation shadow) and surface-like (radius or own background).
4248
+ function isRepeatedTextContainer(style) {
4249
+ if (!style) return false;
4250
+ const hasShadow = !!(style.boxShadow && style.boxShadow !== 'none' && style.boxShadow !== '');
4251
+ const borderSides = ['Top', 'Right', 'Bottom', 'Left']
4252
+ .filter(side => (parseFloat(style[`border${side}Width`]) || 0) >= 1).length;
4253
+ const hasBorder = borderSides >= 3;
4254
+ const hasRadius = (parseFloat(style.borderRadius) || 0) > 0;
4255
+ const bg = parseRgb(style.backgroundColor) || parseAnyColor(style.backgroundColor);
4256
+ const hasBg = !!(bg && (bg.a ?? 1) > 0.1);
4257
+ return isCardLikeFromProps(hasShadow, hasBorder, hasRadius, hasBg);
4258
+ }
4259
+
4260
+ function collectRepeatedContainerTextFindings(doc, getStyle, opts = {}) {
4261
+ const isVisible = opts.isVisible || (() => true);
4262
+ const findings = [];
4263
+
4264
+ const containers = [];
4265
+ const containerSet = new Set();
4266
+ for (const el of doc.querySelectorAll('*')) {
4267
+ if (!REPEATED_TEXT_CONTAINER_TAGS.has(el.tagName.toLowerCase())) continue;
4268
+ if (el.closest?.(REPEATED_TEXT_SKIP_SELECTOR)) continue;
4269
+ if (!isRepeatedTextContainer(getStyle(el))) continue;
4270
+ containers.push(el);
4271
+ containerSet.add(el);
4272
+ }
4273
+
4274
+ for (const container of containers) {
4275
+ if (!isVisible(container)) continue;
4276
+ const descendants = container.querySelectorAll('*');
4277
+ // Page-scale wrappers that merely happen to carry a background are not
4278
+ // the "one card" this rule reasons about.
4279
+ if (descendants.length > 250) continue;
4280
+
4281
+ const groups = new Map();
4282
+ for (const d of descendants) {
4283
+ // Attribute text to the innermost container only.
4284
+ let anc = d.parentElement;
4285
+ let ownedByInner = false;
4286
+ while (anc && anc !== container) {
4287
+ if (containerSet.has(anc)) { ownedByInner = true; break; }
4288
+ anc = anc.parentElement;
4289
+ }
4290
+ if (ownedByInner) continue;
4291
+ if (d.closest?.(REPEATED_TEXT_SKIP_SELECTOR)) continue;
4292
+ // Icon-font glyph names read as text but render as symbols.
4293
+ if (/icon|material-symbols|(?:^|\s)fa[srlbd]?(?:\s|-|$)/i.test(String(d.getAttribute?.('class') || ''))) continue;
4294
+ if (!isVisible(d)) continue;
4295
+
4296
+ const direct = [...d.childNodes]
4297
+ .filter(n => n.nodeType === 3)
4298
+ .map(n => n.textContent)
4299
+ .join(' ')
4300
+ .replace(/\s+/g, ' ')
4301
+ .trim();
4302
+ if (direct.length < 4 || direct.length > 48) continue;
4303
+ if (!/[a-zA-Z]/.test(direct)) continue;
4304
+
4305
+ // Structural signature: the element path from the occurrence up to
4306
+ // the container. Parallel/templated repetition shares one signature.
4307
+ const sig = [];
4308
+ for (let cur = d; cur && cur !== container; cur = cur.parentElement) {
4309
+ const cls = String(cur.getAttribute?.('class') || '')
4310
+ .trim().split(/\s+/).filter(Boolean).sort().join('.');
4311
+ sig.push(cur.tagName.toLowerCase() + (cls ? `.${cls}` : ''));
4312
+ }
4313
+ if (!groups.has(direct)) groups.set(direct, []);
4314
+ groups.get(direct).push(sig.join('>'));
4315
+ }
4316
+
4317
+ for (const [text, sigs] of groups) {
4318
+ if (sigs.length < 3) continue;
4319
+ if (new Set(sigs).size < 3) continue;
4320
+ findings.push({
4321
+ id: 'repeated-container-text',
4322
+ snippet: `"${text.slice(0, 40)}" rendered ${sigs.length}× in distinct spots inside ${classSelector(container)}`,
4323
+ });
4324
+ }
4325
+ }
4326
+ return findings;
4327
+ }
4328
+
4329
+ function checkRepeatedContainerTextFromDoc(doc, win) {
4330
+ return collectRepeatedContainerTextFindings(
4331
+ doc,
4332
+ (el) => win.getComputedStyle(el),
4333
+ { isVisible: (el) => String(win.getComputedStyle(el).display || '') !== 'none' },
4334
+ );
4335
+ }
4336
+
4337
+ function checkRepeatedContainerTextDOM() {
4338
+ return collectRepeatedContainerTextFindings(
4339
+ document,
4340
+ (el) => getComputedStyle(el),
4341
+ { isVisible: isRenderedForBrowserRule },
4342
+ );
4343
+ }
4344
+
2189
4345
  // ─── Cream / beige palette (the default "tasteful" AI surface) ────────────────
2190
4346
  // A warm, lightly-tinted off-white page background — light, with R≥G≥B and a
2191
4347
  // small warm tint (not white, not a strong color). The current reflex surface.
@@ -2299,7 +4455,7 @@ function checkElementOversizedH1DOM(el) {
2299
4455
  return checkOversizedH1({ tag, fontSize, headingText, rect, viewportWidth, viewportHeight });
2300
4456
  }
2301
4457
 
2302
- // ─── GPT tell: hairline border + wide diffuse shadow (gated --gpt) ────────────
4458
+ // ─── Generated-UI tell: hairline border + wide diffuse shadow ────────────────
2303
4459
  const CSS_COLOR_TOKEN_RE = /(?:rgba?|hsla?|oklch|oklab|lab|lch|color)\([^)]*\)|#[0-9a-fA-F]{3,8}\b|\b(?:black|white|transparent|currentcolor)\b/gi;
2304
4460
 
2305
4461
  function shadowLayerAlpha(layer) {
@@ -2624,22 +4780,674 @@ function checkElementTextOverflowDOM(el) {
2624
4780
  if (el.clientWidth > 0 && delta >= 16) {
2625
4781
  return [{ id: 'text-overflow', snippet: `${classSelector(el)} overflows its box by ${Math.round(delta)}px` }];
2626
4782
  }
4783
+
4784
+ // Inline text owners have no client geometry (clientWidth/scrollWidth are
4785
+ // both 0), so the scrollWidth path above never sees them. Their overflow
4786
+ // registers only on a block ancestor, and that ancestor has no direct text
4787
+ // so the ownership gate skips it. (The shipped miss: a nowrap inline
4788
+ // <span> spilling 45px past its fixed-width grid cell.) Measure the inline
4789
+ // box against the padding box of its nearest block container instead.
4790
+ if (el.clientWidth === 0 && rect && rect.width > 0) {
4791
+ let container = el.parentElement;
4792
+ while (container && container.clientWidth === 0) container = container.parentElement;
4793
+ if (!container) return [];
4794
+ // Transforms make rect comparisons lie; skip anything on that path.
4795
+ for (let p = el; p && p !== container.parentElement; p = p.parentElement) {
4796
+ const t = getComputedStyle(p).transform;
4797
+ if (t && t !== 'none') return [];
4798
+ }
4799
+ const cRect = container.getBoundingClientRect();
4800
+ const contentRight = cRect.left + container.clientLeft + container.clientWidth;
4801
+ const spill = rect.right - contentRight;
4802
+ if (spill >= 16) {
4803
+ return [{ id: 'text-overflow', snippet: `${classSelector(el)} overflows its container by ${Math.round(spill)}px` }];
4804
+ }
4805
+ }
2627
4806
  return [];
2628
4807
  }
2629
4808
 
4809
+ // ---------------------------------------------------------------------------
4810
+ // Blinking cursor (browser-only)
4811
+ // ---------------------------------------------------------------------------
4812
+
4813
+ // Block / underscore glyphs commonly used as a fake text cursor.
4814
+ const CURSOR_GLYPH_RE = /^[_|▀-▟■▮❙❚|]$/;
4815
+
4816
+ // How far down the page still counts as the first-viewport / hero region.
4817
+ // Hero compositions regularly run past a literal viewport height, so the
4818
+ // gate is a landing-region budget, not an exact fold line.
4819
+ const CURSOR_FIRST_VIEWPORT_PX = 1200;
4820
+
4821
+ // Do the named @keyframes only toggle visibility (opacity dropping to ~0 or
4822
+ // visibility:hidden), i.e. a blink rather than a fade/move/spin? Walks the
4823
+ // live CSSOM; cross-origin sheets are skipped.
4824
+ function keyframesToggleVisibilityDOM(name) {
4825
+ if (!name) return false;
4826
+ for (const sheet of document.styleSheets) {
4827
+ let rules;
4828
+ try { rules = sheet.cssRules || sheet.rules; } catch { continue; }
4829
+ if (!rules) continue;
4830
+ const stack = [...rules];
4831
+ while (stack.length) {
4832
+ const rule = stack.shift();
4833
+ if (rule.cssRules && rule.type !== 7) { stack.push(...rule.cssRules); continue; }
4834
+ if (rule.type !== 7 || rule.name !== name) continue; // 7 = KEYFRAMES_RULE
4835
+ let togglesOut = false;
4836
+ for (const frame of rule.cssRules || []) {
4837
+ const fs = frame.style;
4838
+ if (!fs) continue;
4839
+ for (let i = 0; i < fs.length; i++) {
4840
+ const prop = fs[i];
4841
+ if (prop === 'opacity') {
4842
+ if ((parseFloat(fs.getPropertyValue('opacity')) || 0) <= 0.15) togglesOut = true;
4843
+ } else if (prop === 'visibility') {
4844
+ if (/hidden/i.test(fs.getPropertyValue('visibility'))) togglesOut = true;
4845
+ } else if (prop !== 'animation-timing-function') {
4846
+ return false; // keyframes animate something else — not a blink
4847
+ }
4848
+ }
4849
+ }
4850
+ return togglesOut;
4851
+ }
4852
+ }
4853
+ return false;
4854
+ }
4855
+
4856
+ // Decorative blinking cursor: a small block / underscore element bound to an
4857
+ // infinite blink animation, sitting in the first-viewport region of a page.
4858
+ // Real editable surfaces (inputs, textareas, contenteditable, role=textbox)
4859
+ // draw their own caret and are exempt. Round pulsing dots stay with the
4860
+ // pulsing-dot rule.
4861
+ function checkElementBlinkingCursorDOM(el) {
4862
+ const tag = el.tagName.toLowerCase();
4863
+ if (['input', 'textarea', 'select', 'img', 'svg', 'script', 'style'].includes(tag)) return [];
4864
+ const style = getComputedStyle(el);
4865
+
4866
+ const iterations = (style.animationIterationCount || '').split(',').map(s => s.trim());
4867
+ if (!iterations.includes('infinite')) return [];
4868
+ const names = (style.animationName || '').split(',').map(s => s.trim()).filter(n => n && n !== 'none');
4869
+ if (names.length === 0) return [];
4870
+ const blinkName = names.find(n => /blink|caret|cursor/i.test(n))
4871
+ || names.find(n => keyframesToggleVisibilityDOM(n));
4872
+ if (!blinkName) return [];
4873
+
4874
+ // Real caret contexts are exempt.
4875
+ if (el.isContentEditable || el.closest('[contenteditable=""], [contenteditable="true"], [role="textbox"]')) return [];
4876
+
4877
+ const rect = el.getBoundingClientRect();
4878
+ if (rect.width <= 0 || rect.height <= 0) return [];
4879
+
4880
+ // First-viewport gate: the hero cliché, not a footer terminal.
4881
+ const pageTop = rect.top + (window.scrollY || 0);
4882
+ if (pageTop > CURSOR_FIRST_VIEWPORT_PX) return [];
4883
+
4884
+ // Cursor shape: a lone block/underscore glyph, or an empty solid
4885
+ // rectangle sized like a text caret (block or underscore form).
4886
+ const text = (el.textContent || '').trim();
4887
+ const glyphCursor = text.length === 1 && CURSOR_GLYPH_RE.test(text);
4888
+ let blockCursor = false;
4889
+ if (!glyphCursor) {
4890
+ if (text.length > 0 || el.childElementCount > 0) return [];
4891
+ const bg = parseAnyColor(style.backgroundColor || '');
4892
+ const filled = bg && (bg.a ?? 1) > 0.2;
4893
+ const hasBorderFill = ['Left', 'Right', 'Bottom'].some(
4894
+ side => (parseFloat(style[`border${side}Width`]) || 0) >= 1,
4895
+ );
4896
+ if (!filled && !hasBorderFill) return [];
4897
+ const vertical = rect.width >= 1 && rect.width <= 24 && rect.height >= 6 && rect.height <= 48 && rect.height >= rect.width;
4898
+ const underscore = rect.height >= 1 && rect.height <= 6 && rect.width >= 4 && rect.width <= 24;
4899
+ if (!vertical && !underscore) return [];
4900
+ // Round dots are the pulsing-dot rule's territory.
4901
+ const radiusPx = parseFloat(style.borderRadius) || 0;
4902
+ if (radiusPx >= 0.4 * Math.min(rect.width, rect.height)) return [];
4903
+ blockCursor = true;
4904
+ }
4905
+ if (!glyphCursor && !blockCursor) return [];
4906
+
4907
+ // Hero-region promotion: a fake caret blinking in the first ~900px or
4908
+ // inside the page chrome is the shipped hero cliché, not an incidental
4909
+ // flourish. Promote those from the registry's advisory to warning;
4910
+ // lower first-viewport occurrences keep the default severity.
4911
+ const inHeroRegion = pageTop <= 900
4912
+ || !!(el.closest && el.closest('header, nav, [role="banner"], [role="navigation"]'));
4913
+ return [{
4914
+ id: 'blinking-cursor',
4915
+ snippet: `${classSelector(el)} — ${Math.round(rect.width)}x${Math.round(rect.height)}px blinking cursor (animation "${blinkName}") in the first viewport`,
4916
+ ...(inHeroRegion ? { severity: 'warning' } : {}),
4917
+ }];
4918
+ }
4919
+
4920
+ // ---------------------------------------------------------------------------
4921
+ // Content invisible at rest (browser-only, driven by the URL engine)
4922
+ // ---------------------------------------------------------------------------
4923
+
4924
+ // Tags whose text never renders, or whose hidden state is legitimate UI
4925
+ // (templates, dialogs, native select options). Text inside them stays out of
4926
+ // both the numerator and the denominator.
4927
+ const HIDDEN_TEXT_EXCLUDE_TAGS = new Set([
4928
+ 'script', 'style', 'noscript', 'template', 'title', 'head', 'meta', 'link',
4929
+ 'option', 'optgroup', 'select', 'datalist', 'dialog',
4930
+ ]);
4931
+
4932
+ // Measure how many text characters currently render invisible (computed
4933
+ // opacity ~0 or visibility hidden anywhere on the ancestor chain) versus
4934
+ // visible. display:none / [hidden] / aria-hidden subtrees are legitimately
4935
+ // hidden UI (menus, tab panels, templates): they are excluded from the
4936
+ // denominator entirely rather than counted as invisible.
4937
+ function measureHiddenTextDOM() {
4938
+ const cache = new Map();
4939
+ function stateOf(el) {
4940
+ if (!el || el.nodeType !== 1 || el === document.documentElement) return 'visible';
4941
+ const cached = cache.get(el);
4942
+ if (cached) return cached;
4943
+ let state;
4944
+ const tag = el.tagName.toLowerCase();
4945
+ if (HIDDEN_TEXT_EXCLUDE_TAGS.has(tag)) {
4946
+ state = 'excluded';
4947
+ } else {
4948
+ const parentState = stateOf(el.parentElement);
4949
+ if (parentState === 'excluded') {
4950
+ state = 'excluded';
4951
+ } else {
4952
+ const style = getComputedStyle(el);
4953
+ if (style.display === 'none' || el.hidden || el.getAttribute('aria-hidden') === 'true'
4954
+ || String(style.contentVisibility || '').toLowerCase() === 'hidden') {
4955
+ state = 'excluded';
4956
+ } else if (parentState === 'invisible'
4957
+ || (parseFloat(style.opacity) || 0) <= 0.02
4958
+ || /^(hidden|collapse)$/.test(style.visibility)) {
4959
+ state = 'invisible';
4960
+ } else {
4961
+ state = 'visible';
4962
+ }
4963
+ }
4964
+ }
4965
+ cache.set(el, state);
4966
+ return state;
4967
+ }
4968
+
4969
+ let totalChars = 0;
4970
+ let hiddenChars = 0;
4971
+ const hiddenSamples = [];
4972
+ for (const el of document.querySelectorAll('body *')) {
4973
+ let len = 0;
4974
+ for (const node of el.childNodes) {
4975
+ if (node.nodeType === 3) len += node.textContent.replace(/\s+/g, ' ').trim().length;
4976
+ }
4977
+ if (!len) continue;
4978
+ const state = stateOf(el);
4979
+ if (state === 'excluded') continue;
4980
+ totalChars += len;
4981
+ if (state === 'invisible') {
4982
+ hiddenChars += len;
4983
+ if (hiddenSamples.length < 3) {
4984
+ const text = String(el.textContent || '').replace(/\s+/g, ' ').trim().slice(0, 40);
4985
+ if (text) hiddenSamples.push(text);
4986
+ }
4987
+ }
4988
+ }
4989
+ return { totalChars, hiddenChars, hiddenSamples };
4990
+ }
4991
+
4992
+ // Pure threshold check over a measureHiddenTextDOM() result. The URL engine
4993
+ // calls it AFTER a reveal sweep (scroll through the document so every
4994
+ // IntersectionObserver / scroll reveal had its chance to fire, then back to
4995
+ // the top): a healthy reveal-on-scroll page drops to ~0 invisible text after
4996
+ // the sweep, while a page whose reveal script died keeps most of its text at
4997
+ // opacity 0 forever. Fires only when the invisible share stays above 30%
4998
+ // with a real amount of text behind it.
4999
+ function checkContentHiddenAtRest({ totalChars = 0, hiddenChars = 0, hiddenSamples = [] } = {}) {
5000
+ if (totalChars < 200 || hiddenChars < 150) return [];
5001
+ const share = hiddenChars / totalChars;
5002
+ if (share <= 0.3) return [];
5003
+ const sample = hiddenSamples.length ? ` (e.g. "${hiddenSamples[0]}")` : '';
5004
+ return [{
5005
+ id: 'content-hidden-at-rest',
5006
+ snippet: `${Math.round(share * 100)}% of page text (${hiddenChars} of ${totalChars} chars) stays at opacity 0 / visibility hidden after reveal handlers ran${sample}`,
5007
+ }];
5008
+ }
5009
+
5010
+ // ---------------------------------------------------------------------------
5011
+ // Edge-flush cards in horizontal scrollers (browser-only)
5012
+ // ---------------------------------------------------------------------------
5013
+
5014
+ // A visually-defined card (own opaque background, or borders on 2+ sides)
5015
+ // inside a horizontal scroller, sitting flush against one edge of the
5016
+ // scroller's clip box at rest while keeping a clear gutter on the other
5017
+ // side. The canonical bug: the first snap panel is sized wider than the
5018
+ // scroller, so its cards end exactly at the clip edge with their rounded
5019
+ // corners cut, while every sibling panel keeps its inset. Cards that extend
5020
+ // far past the edge are deliberate peeks and stay exempt.
5021
+ function checkEdgeFlushCardsDOM() {
5022
+ const findings = [];
5023
+ const vh = window.innerHeight || 800;
5024
+ const isScroller = (s) => /(auto|scroll)/.test(s.overflowX || '') || /(auto|scroll)/.test(s.overflow || '');
5025
+
5026
+ for (const scroller of document.querySelectorAll('*')) {
5027
+ const style = getComputedStyle(scroller);
5028
+ if (!isScroller(style)) continue;
5029
+ if (scroller.scrollWidth <= scroller.clientWidth + 8) continue;
5030
+ // At rest only: a user-scrolled or snapped-forward scroller legitimately
5031
+ // shows cut cards at both edges.
5032
+ if (scroller.scrollLeft > 4) continue;
5033
+ const scRect = scroller.getBoundingClientRect();
5034
+ if (scRect.width < 120 || scRect.height < 60) continue;
5035
+ // Landing-region gate: the defect matters where the page opens.
5036
+ if (scRect.top + (window.scrollY || 0) > 2 * vh) continue;
5037
+ const contentLeft = scRect.left + scroller.clientLeft;
5038
+ const contentRight = contentLeft + scroller.clientWidth;
5039
+
5040
+ const flush = [];
5041
+ for (const card of scroller.querySelectorAll('*')) {
5042
+ if (!isRenderedForBrowserRule(card)) continue;
5043
+ // Attribute cards to their nearest scroller only (nested scrollers).
5044
+ let owner = card.parentElement;
5045
+ while (owner && owner !== scroller && !isScroller(getComputedStyle(owner))) owner = owner.parentElement;
5046
+ if (owner !== scroller) continue;
5047
+ const cs = getComputedStyle(card);
5048
+ const rect = card.getBoundingClientRect();
5049
+ if (rect.width < 80 || rect.height < 40) continue;
5050
+ const bg = parseAnyColor(cs.backgroundColor || '');
5051
+ const hasBg = !!(bg && (bg.a ?? 1) > 0.5);
5052
+ const borderSides = ['Top', 'Right', 'Bottom', 'Left']
5053
+ .filter(side => (parseFloat(cs[`border${side}Width`]) || 0) > 0).length;
5054
+ if (!hasBg && borderSides < 2) continue;
5055
+ const leftGutter = rect.left - contentLeft;
5056
+ const rightGap = contentRight - rect.right;
5057
+ // Flush right with a left gutter, or the mirror. The -24 floor keeps
5058
+ // deliberately peeking next-cards (cut mid-card) exempt.
5059
+ const flushRight = leftGutter >= 6 && rightGap < 8 && rightGap > -24;
5060
+ const flushLeft = rightGap >= 6 && leftGutter < 8 && leftGutter > -24;
5061
+ if (!flushRight && !flushLeft) continue;
5062
+ flush.push({ card, edge: flushRight ? 'right' : 'left', gap: Math.round(flushRight ? rightGap : leftGutter) });
5063
+ }
5064
+ if (flush.length === 0) continue;
5065
+ const worst = flush.reduce((a, b) => (b.gap < a.gap ? b : a));
5066
+ findings.push({
5067
+ el: scroller,
5068
+ type: 'edge-flush-cards',
5069
+ detail: `${flush.length} card${flush.length === 1 ? '' : 's'} flush against the ${worst.edge} edge of ${classSelector(scroller)} at rest (${worst.gap}px gap, e.g. ${classSelector(worst.card)})`,
5070
+ });
5071
+ }
5072
+ return findings;
5073
+ }
5074
+
5075
+ // ---------------------------------------------------------------------------
5076
+ // Text occlusion / element overlap (browser-only)
5077
+ // ---------------------------------------------------------------------------
5078
+
5079
+ // An opaque decorated box: a near-solid background fill or two-plus visible
5080
+ // borders make it hide whatever sits behind it. Gradient / image fills are
5081
+ // deliberately excluded — a scrim gradient over hero imagery is a contrast
5082
+ // layer, not an occluder, and belongs to the pixel low-contrast rule.
5083
+ function isOpaqueDecoratedBox(cs) {
5084
+ if (!cs) return false;
5085
+ const bg = parseAnyColor(cs.backgroundColor || '');
5086
+ if (bg && (bg.a ?? 1) > 0.6) return true;
5087
+ const borderSides = ['Top', 'Right', 'Bottom', 'Left'].filter((side) => {
5088
+ if ((parseFloat(cs[`border${side}Width`]) || 0) <= 0) return false;
5089
+ const bc = parseAnyColor(cs[`border${side}Color`] || '');
5090
+ return bc && (bc.a ?? 1) > 0.3;
5091
+ }).length;
5092
+ return borderSides >= 2;
5093
+ }
5094
+
5095
+ // Is this element lifted out of normal flow into a layer that can cover
5096
+ // siblings? Two normal-flow blocks stacked vertically cannot truly hide each
5097
+ // other's ink — an overlap between their rects is line-box bleed from tight
5098
+ // leading (a display headline reaching up over the line before it), not
5099
+ // occlusion. Only out-of-flow positioning (absolute / fixed / sticky) moves an
5100
+ // element off its own row onto the pixels of another; an in-place transform or
5101
+ // relative nudge on a display headline does not.
5102
+ function isLayeredElement(el) {
5103
+ for (let cur = el; cur && cur.nodeType === 1 && cur !== document.body; cur = cur.parentElement) {
5104
+ const pos = String(getComputedStyle(cur).position || 'static');
5105
+ if (pos === 'absolute' || pos === 'fixed' || pos === 'sticky') return true;
5106
+ }
5107
+ return false;
5108
+ }
5109
+
5110
+ function elementDirectText(el) {
5111
+ let t = '';
5112
+ for (const node of el.childNodes || []) {
5113
+ if (node.nodeType === 3) t += node.textContent;
5114
+ }
5115
+ return t.trim();
5116
+ }
5117
+
5118
+ // Rendered gate that, unlike isRenderedForBrowserRule, does NOT exempt
5119
+ // aria-hidden subtrees: a decorative aria-hidden box still paints on screen
5120
+ // and can still visually cover real text.
5121
+ function isPaintedForOcclusion(el) {
5122
+ for (let cur = el; cur && cur.nodeType === 1; cur = cur.parentElement) {
5123
+ const style = getComputedStyle(cur);
5124
+ const visibility = String(style.visibility || '').toLowerCase();
5125
+ if (style.display === 'none' || visibility === 'hidden' || visibility === 'collapse') return false;
5126
+ if ((parseFloat(style.opacity) || 0) <= 0.05) return false;
5127
+ if (String(style.contentVisibility || '').toLowerCase() === 'hidden') return false;
5128
+ }
5129
+ return true;
5130
+ }
5131
+
5132
+ // Detects text that is actually painted UNDER an opaque box or another text
5133
+ // run (the reader can't read it), plus two structural overlap tells the
5134
+ // elementFromPoint probe can't reach: a large headline whose edge tucks behind
5135
+ // an opaque card, and an inline element whose leaked padding-box (a common
5136
+ // class-name-collision bug) covers a sibling.
5137
+ //
5138
+ // The occlusion probe is viewport-bound: elementFromPoint only answers for the
5139
+ // scan's current viewport (scroll 0), so the ground-truth paths cover the
5140
+ // first-viewport composition where collisions matter most. The inline-leak
5141
+ // path is pure geometry and runs anywhere on the page.
5142
+ const OCCLUSION_TEXT_SKIP_TAGS = new Set(['script', 'style', 'noscript', 'template', 'title']);
5143
+
5144
+ function checkTextOcclusionDOM() {
5145
+ const findings = [];
5146
+ const seenVictims = new Set();
5147
+ const vw = window.innerWidth || 1280;
5148
+ const vh = window.innerHeight || 800;
5149
+
5150
+ const isFloated = (cs) => {
5151
+ const f = String(cs.cssFloat || cs.float || 'none').toLowerCase();
5152
+ return f === 'left' || f === 'right';
5153
+ };
5154
+ const isMarqueeish = (el, cs) => {
5155
+ if (el.tagName === 'MARQUEE') return true;
5156
+ const ident = `${el.getAttribute?.('class') || ''} ${el.getAttribute?.('id') || ''}`;
5157
+ if (/\b(marquee|ticker|scroller|carousel|conveyor)\b/i.test(ident)) return true;
5158
+ const anim = String(cs.animationName || '').toLowerCase();
5159
+ return /marquee|ticker|scroll/.test(anim);
5160
+ };
5161
+ // A fixed or sticky overlay (status bar, toolbar, sticky header) floats above
5162
+ // scrolling content by design — whatever sits under it at rest scrolls clear,
5163
+ // so it is not occluding the page.
5164
+ const isPinnedOverlay = (el) => {
5165
+ for (let cur = el; cur && cur.nodeType === 1 && cur !== document.body; cur = cur.parentElement) {
5166
+ const pos = String(getComputedStyle(cur).position || 'static');
5167
+ if (pos === 'fixed' || pos === 'sticky') return true;
5168
+ }
5169
+ return false;
5170
+ };
5171
+
5172
+ // Collect renderable text owners in / near the first viewport for the
5173
+ // elementFromPoint probe. SVG <text> counts too.
5174
+ const textEls = [];
5175
+ for (const el of document.querySelectorAll('body *')) {
5176
+ const tag = el.tagName.toLowerCase();
5177
+ if (OCCLUSION_TEXT_SKIP_TAGS.has(tag)) continue;
5178
+ const inSvg = !!el.closest('svg');
5179
+ if (inSvg && tag !== 'text') continue;
5180
+ const text = inSvg ? (el.textContent || '').trim() : elementDirectText(el);
5181
+ if (text.length < 2) continue;
5182
+ if (!isPaintedForOcclusion(el)) continue;
5183
+ let rect; try { rect = el.getBoundingClientRect(); } catch { continue; }
5184
+ if (rect.width < 6 || rect.height < 6) continue;
5185
+ // Viewport-bound probe: keep text whose box overlaps the live viewport.
5186
+ if (rect.bottom <= 0 || rect.top >= vh) continue;
5187
+ textEls.push({ el, rect, text, inSvg });
5188
+ }
5189
+
5190
+ for (const victim of textEls) {
5191
+ const { el, rect, text } = victim;
5192
+ if (seenVictims.has(el)) continue;
5193
+ const style = getComputedStyle(el);
5194
+ if (isScreenReaderOnlyTextStyle(style, { width: rect.width, height: rect.height, clientWidth: el.clientWidth, clientHeight: el.clientHeight })) continue;
5195
+
5196
+ const cols = Math.max(6, Math.min(30, Math.round(rect.width / 12)));
5197
+ const rows = Math.max(1, Math.min(4, Math.round(rect.height / 14)));
5198
+ let total = 0;
5199
+ let occluded = 0;
5200
+ let occluderEl = null;
5201
+ let occluderKind = '';
5202
+ for (let i = 0; i < cols; i++) {
5203
+ const x = rect.left + rect.width * ((i + 0.5) / cols);
5204
+ if (x < 1 || x > vw - 1) continue;
5205
+ for (let j = 0; j < rows; j++) {
5206
+ const y = rect.top + rect.height * ((j + 0.5) / rows);
5207
+ if (y < 1 || y > vh - 1) continue;
5208
+ total++;
5209
+ const top = document.elementFromPoint(x, y);
5210
+ if (!top) continue;
5211
+ // Text visible here: the probe returns the text itself, a descendant,
5212
+ // or one of its ancestors (the text's own container / background).
5213
+ if (top === el || el.contains(top) || top.contains(el)) continue;
5214
+ const topCs = getComputedStyle(top);
5215
+ if (isFloated(topCs) || isMarqueeish(top, topCs) || isPinnedOverlay(top)) continue;
5216
+ const topTag = top.tagName.toLowerCase();
5217
+ // Text sitting under a raw image/video is contrast territory (deduped
5218
+ // against the pixel low-contrast rule); leave those alone here.
5219
+ if (['img', 'video', 'canvas', 'picture'].includes(topTag)) continue;
5220
+ const topHasText = elementDirectText(top).length > 0 || !!top.closest('svg');
5221
+ if (isOpaqueDecoratedBox(topCs)) {
5222
+ occluded++;
5223
+ if (!occluderEl) { occluderEl = top; occluderKind = 'box'; }
5224
+ } else if (topHasText) {
5225
+ occluded++;
5226
+ if (!occluderEl) { occluderEl = top; occluderKind = 'text'; }
5227
+ }
5228
+ }
5229
+ }
5230
+ if (total === 0 || !occluderEl) continue;
5231
+ const occFrac = occluded / total;
5232
+ // A solid box's paint fills its rect, so box coverage is real at a lower
5233
+ // bar. Text coverage rides on elementFromPoint returning the occluder's box
5234
+ // (line box / container), which can exceed its actual glyph ink, so the
5235
+ // text bar is higher — partial overlaps below it are crowding, not burial.
5236
+ if (occFrac < (occluderKind === 'text' ? 0.45 : 0.3)) continue;
5237
+
5238
+ // (i) Substantial occlusion: a real slab of the text is behind something.
5239
+ if (occluderKind === 'text') {
5240
+ // Two SVG texts inside the same emblem (concentric arcs, monogram) are one
5241
+ // decorative unit, not a collision.
5242
+ const victimSvg = el.closest('svg');
5243
+ const occSvg = occluderEl.closest('svg');
5244
+ if (victimSvg && occSvg && victimSvg === occSvg) continue;
5245
+ // Both sides in plain flow: the overlap is line-box bleed from tight
5246
+ // leading (a big headline reaching up over its own eyebrow), not one text
5247
+ // run painted over another.
5248
+ if (!isLayeredElement(el) && !isLayeredElement(occluderEl)) continue;
5249
+ }
5250
+ seenVictims.add(el);
5251
+ findings.push({
5252
+ el,
5253
+ type: 'text-occlusion',
5254
+ detail: `${classSelector(el)} "${text.slice(0, 24)}" is ${Math.round(occFrac * 100)}% covered by ${occluderKind === 'text' ? 'overlapping text' : 'an opaque element'} (${classSelector(occluderEl)})`,
5255
+ });
5256
+ }
5257
+
5258
+ // (ii) Headline overhanging an opaque card: a display-scale line whose bulk
5259
+ // sits outside a bounded content card but whose edge clips into it. The text
5260
+ // may still paint on top and stay readable, but the two layers were dropped
5261
+ // on the same pixels — a placement collision, not a composition.
5262
+ const cards = [];
5263
+ for (const el of document.querySelectorAll('body *')) {
5264
+ if (el.closest('svg')) continue;
5265
+ if (!isPaintedForOcclusion(el)) continue;
5266
+ const cs = getComputedStyle(el);
5267
+ const bg = parseAnyColor(cs.backgroundColor || '');
5268
+ const bgImg = cs.backgroundImage || '';
5269
+ if (!bg || (bg.a ?? 1) <= 0.7) continue;
5270
+ if (bgImg && bgImg !== 'none' && /(gradient|url)\(/i.test(bgImg)) continue;
5271
+ const hasBorder = ['Top', 'Right', 'Bottom', 'Left'].some((s) => (parseFloat(cs[`border${s}Width`]) || 0) > 0);
5272
+ const hasShadow = cs.boxShadow && cs.boxShadow !== 'none';
5273
+ if (!hasBorder && !hasShadow) continue;
5274
+ if (isPinnedOverlay(el)) continue;
5275
+ let cr; try { cr = el.getBoundingClientRect(); } catch { continue; }
5276
+ if (cr.width < 100 || cr.width > 0.8 * vw || cr.height < 60) continue;
5277
+ cards.push({ el, rect: cr });
5278
+ }
5279
+ for (const victim of textEls) {
5280
+ const { el, rect, text } = victim;
5281
+ if (seenVictims.has(el)) continue;
5282
+ const style = getComputedStyle(el);
5283
+ if ((parseFloat(style.fontSize) || 16) < 40) continue;
5284
+ let lineHeight = parseFloat(style.lineHeight);
5285
+ if (!Number.isFinite(lineHeight)) lineHeight = (parseFloat(style.fontSize) || 16) * 1.2;
5286
+ const centerX = rect.left + rect.width / 2;
5287
+ for (const card of cards) {
5288
+ if (card.el === el || el.contains(card.el) || card.el.contains(el)) continue;
5289
+ const ix = Math.max(0, Math.min(rect.right, card.rect.right) - Math.max(rect.left, card.rect.left));
5290
+ const iy = Math.max(0, Math.min(rect.bottom, card.rect.bottom) - Math.max(rect.top, card.rect.top));
5291
+ if (ix < 8 || iy < 0.5 * lineHeight) continue;
5292
+ // The headline's bulk must sit outside the card — only its edge clips in.
5293
+ if (centerX >= card.rect.left && centerX <= card.rect.right) continue;
5294
+ if (ix > 0.5 * rect.width) continue;
5295
+ seenVictims.add(el);
5296
+ findings.push({
5297
+ el,
5298
+ type: 'text-occlusion',
5299
+ detail: `${classSelector(el)} "${text.slice(0, 24)}" overhangs ${classSelector(card.el)} by ${Math.round(ix)}px — the headline and the card collide`,
5300
+ });
5301
+ break;
5302
+ }
5303
+ }
5304
+
5305
+ // (iii) Inline padding leak: an inline element with an opaque background and
5306
+ // large vertical padding paints a filled block whose padding-box overflows
5307
+ // its line (inline padding reserves no vertical space), so the fill lands on
5308
+ // the content above and below instead of enclosing its own text. The
5309
+ // canonical bug is a class-name collision that hands a decorative marker a
5310
+ // payoff card's padding. The tell is a rendered height several times the line
5311
+ // height, which distinguishes the leak from a padded inline highlight.
5312
+ for (const el of document.querySelectorAll('body *')) {
5313
+ if (el.closest('svg')) continue;
5314
+ if (!isPaintedForOcclusion(el)) continue;
5315
+ const cs = getComputedStyle(el);
5316
+ if (cs.display !== 'inline') continue;
5317
+ const bg = parseAnyColor(cs.backgroundColor || '');
5318
+ if (!bg || (bg.a ?? 1) <= 0.6) continue;
5319
+ const padTop = parseFloat(cs.paddingTop) || 0;
5320
+ const padBottom = parseFloat(cs.paddingBottom) || 0;
5321
+ if (padTop + padBottom < 24) continue;
5322
+ let rect; try { rect = el.getBoundingClientRect(); } catch { continue; }
5323
+ if (rect.width < 12 || rect.height < 24) continue;
5324
+ const fontSize = parseFloat(cs.fontSize) || 16;
5325
+ let lineHeight = parseFloat(cs.lineHeight);
5326
+ if (!Number.isFinite(lineHeight)) lineHeight = fontSize * 1.4;
5327
+ // The padding box has to overflow the line by a clear margin — a padded
5328
+ // inline highlight sits at roughly one line height, the leak at several.
5329
+ if (rect.height < 2.2 * lineHeight) continue;
5330
+ if (seenVictims.has(el)) continue;
5331
+ // Name a neighbour the fill lands on, if one is nearby (paint state aside,
5332
+ // reveal-on-scroll siblings still occupy the space it covers).
5333
+ let overlaps = null;
5334
+ for (const other of el.parentElement ? el.parentElement.children : []) {
5335
+ if (other === el || el.contains(other) || other.contains(el)) continue;
5336
+ if (getComputedStyle(other).display === 'none') continue;
5337
+ const oRect = other.getBoundingClientRect();
5338
+ const ix = Math.max(0, Math.min(rect.right, oRect.right) - Math.max(rect.left, oRect.left));
5339
+ const iy = Math.max(0, Math.min(rect.bottom, oRect.bottom) - Math.max(rect.top, oRect.top));
5340
+ if (ix > 4 && iy > 4 && (other.textContent || '').trim().length > 0) { overlaps = other; break; }
5341
+ }
5342
+ seenVictims.add(el);
5343
+ findings.push({
5344
+ el,
5345
+ type: 'text-occlusion',
5346
+ detail: `${classSelector(el)} is an inline element whose opaque fill leaks ${Math.round(rect.height)}px past its line${overlaps ? ` onto ${classSelector(overlaps)}` : ''}`,
5347
+ });
5348
+ }
5349
+
5350
+ return findings;
5351
+ }
5352
+
5353
+ // ---------------------------------------------------------------------------
5354
+ // First-viewport column overflow — the stretched-hero signature (browser-only)
5355
+ // ---------------------------------------------------------------------------
5356
+
5357
+ // A multi-column composition that opens the page (grid/flex with two or more
5358
+ // side-by-side columns, each a real share of the width) where one column's
5359
+ // content runs far past the fold while its sibling fits inside a single
5360
+ // viewport. The row stretches to the tall column, so the short one floats in a
5361
+ // screen-and-a-half of dead space and the fold falls deep inside a single
5362
+ // section. Single-column pages and full-page heroes (no sibling column) are
5363
+ // exempt because there is no fitting sibling to contrast against.
5364
+ function checkFirstViewportColumnOverflowDOM() {
5365
+ const findings = [];
5366
+ const vw = window.innerWidth || 1280;
5367
+ const vh = window.innerHeight || 800;
5368
+ const isMultiCol = (s) => /(^|inline-)(grid|flex)$/.test(String(s.display || ''));
5369
+
5370
+ for (const el of document.querySelectorAll('body *')) {
5371
+ const style = getComputedStyle(el);
5372
+ if (!isMultiCol(style)) continue;
5373
+ let rect; try { rect = el.getBoundingClientRect(); } catch { continue; }
5374
+ if (rect.width < 0.5 * vw) continue;
5375
+ const pageTop = rect.top + (window.scrollY || 0);
5376
+ const pageBottom = pageTop + rect.height;
5377
+ // The fold must fall inside this container: it opens within the first
5378
+ // viewport and runs past it.
5379
+ if (pageTop >= vh * 0.9 || pageBottom <= vh) continue;
5380
+
5381
+ // Direct children that read as side-by-side columns: a real width share,
5382
+ // not full-bleed (stacked single column), sharing the container's top row.
5383
+ const cols = [];
5384
+ for (const child of el.children) {
5385
+ const cs = getComputedStyle(child);
5386
+ if (cs.display === 'none') continue;
5387
+ if (String(cs.position || '') === 'absolute' || String(cs.position || '') === 'fixed') continue;
5388
+ let cr; try { cr = child.getBoundingClientRect(); } catch { continue; }
5389
+ const wShare = cr.width / rect.width;
5390
+ if (wShare < 0.25 || wShare > 0.9) continue;
5391
+ if (cr.height < 40) continue;
5392
+ // Content extent: how far the child's own content actually reaches,
5393
+ // independent of a stretched row height.
5394
+ let contentBottom = cr.top;
5395
+ for (const d of child.querySelectorAll('*')) {
5396
+ const ds = getComputedStyle(d);
5397
+ if (ds.position === 'absolute' || ds.position === 'fixed') continue;
5398
+ if (ds.display === 'none' || ds.visibility === 'hidden') continue;
5399
+ let dr; try { dr = d.getBoundingClientRect(); } catch { continue; }
5400
+ if (dr.width > 0 && dr.height > 0) contentBottom = Math.max(contentBottom, dr.bottom);
5401
+ }
5402
+ cols.push({ child, top: cr.top, contentH: contentBottom - cr.top });
5403
+ }
5404
+ if (cols.length < 2) continue;
5405
+ // Side-by-side: the two candidate columns must share the top row.
5406
+ cols.sort((a, b) => b.contentH - a.contentH);
5407
+ const tall = cols[0];
5408
+ const shortest = cols[cols.length - 1];
5409
+ if (Math.abs(tall.top - shortest.top) > 0.25 * vh) continue;
5410
+ if (tall.contentH <= vh * 1.4) continue;
5411
+ if (shortest.contentH > vh) continue;
5412
+
5413
+ findings.push({
5414
+ el,
5415
+ type: 'first-viewport-column-overflow',
5416
+ detail: `${classSelector(el)} opens the page with one column running ${Math.round(tall.contentH / vh * 100)}% of the viewport tall while a sibling fits in ${Math.round(shortest.contentH / vh * 100)}% — the fold falls deep inside the section`,
5417
+ });
5418
+ }
5419
+ return findings;
5420
+ }
5421
+
2630
5422
  export {
5423
+ CSS_NAMED_COLORS,
2631
5424
  checkBorders,
2632
5425
  isEmojiOnlyText,
2633
5426
  checkColors,
5427
+ checkHoverContrast,
5428
+ checkElementHoverContrast,
5429
+ parseColorMix,
5430
+ compositeColorOver,
2634
5431
  isCardLikeFromProps,
2635
5432
  checkIconTile,
2636
5433
  resolveSerif,
2637
5434
  checkItalicSerif,
2638
5435
  isAccentColor,
2639
5436
  checkHeroEyebrow,
2640
- checkRepeatedSectionKickers,
5437
+ checkKickerAboveHeading,
2641
5438
  checkMotion,
2642
5439
  checkGlow,
5440
+ scanCssTextForGlow,
5441
+ scanCssTextForGridBackground,
5442
+ scanCssTextForRadialHalo,
5443
+ scanCssTextForPseudoStripe,
5444
+ scanCssTextForInsetStripe,
5445
+ scanCssTextForMarquee,
5446
+ collectMarqueeKeyframes,
5447
+ collectCssCustomProps,
5448
+ cssLengthToPx,
5449
+ scanCssTextForPulsingDot,
5450
+ scanHtmlForShapeAssembledIllustration,
2643
5451
  checkHtmlPatterns,
2644
5452
  readOwnBackgroundColor,
2645
5453
  resolveBackground,
@@ -2657,12 +5465,28 @@ export {
2657
5465
  parseAnyColor,
2658
5466
  parseColorResolved,
2659
5467
  cleanInlineText,
2660
- isRepeatedKickerCandidate,
2661
- collectRepeatedSectionKickerCandidates,
2662
- checkRepeatedSectionKickersDOM,
5468
+ isKickerCandidate,
5469
+ collectKickerCandidates,
5470
+ checkKickerAboveHeadingDOM,
5471
+ parseNumberedLabelText,
5472
+ isNumberedSectionLabelCandidate,
5473
+ collectNumberedSectionLabelCandidates,
5474
+ checkNumberedSectionLabels,
5475
+ checkNumberedSectionLabelsFromDoc,
5476
+ checkNumberedSectionLabelsDOM,
5477
+ checkEmDashOveruse,
5478
+ checkEmDashOveruseDOM,
5479
+ isRepeatedTextContainer,
5480
+ collectRepeatedContainerTextFindings,
5481
+ checkRepeatedContainerTextFromDoc,
5482
+ checkRepeatedContainerTextDOM,
5483
+ checkElementPseudoStripeDOM,
2663
5484
  checkElementMotionDOM,
2664
5485
  checkElementGlowDOM,
2665
5486
  checkElementAIPaletteDOM,
5487
+ checkElementRadialSpotlightDOM,
5488
+ checkElementRadialSpotlight,
5489
+ checkRadialSpotlight,
2666
5490
  resolveFontSizePx,
2667
5491
  resolveLengthPx,
2668
5492
  checkQuality,
@@ -2675,7 +5499,7 @@ export {
2675
5499
  checkElementIconTile,
2676
5500
  checkElementItalicSerif,
2677
5501
  checkElementHeroEyebrow,
2678
- checkRepeatedSectionKickersFromDoc,
5502
+ checkKickerAboveHeadingFromDoc,
2679
5503
  checkElementMotion,
2680
5504
  checkElementGlow,
2681
5505
  checkTypography,
@@ -2698,4 +5522,13 @@ export {
2698
5522
  checkElementClippedOverflowDOM,
2699
5523
  isScreenReaderOnlyTextStyle,
2700
5524
  checkElementTextOverflowDOM,
5525
+ checkHeadingRhythmDOM,
5526
+ checkElementBlinkingCursorDOM,
5527
+ measureHiddenTextDOM,
5528
+ checkContentHiddenAtRest,
5529
+ checkEdgeFlushCardsDOM,
5530
+ isOpaqueDecoratedBox,
5531
+ isLayeredElement,
5532
+ checkTextOcclusionDOM,
5533
+ checkFirstViewportColumnOverflowDOM,
2701
5534
  };