@ashlr/hub 3.12.0 → 3.14.0

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 (719) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +29 -16
  3. package/dist/authority-surface.json +274 -148
  4. package/dist/build-identity.json +2 -2
  5. package/dist/cli/authority.d.ts +145 -33
  6. package/dist/cli/authority.js +1292 -315
  7. package/dist/cli/authority.js.map +1 -1
  8. package/dist/cli/cloud.js +12 -3
  9. package/dist/cli/cloud.js.map +1 -1
  10. package/dist/cli/comms.d.ts +33 -19
  11. package/dist/cli/comms.js +128 -151
  12. package/dist/cli/comms.js.map +1 -1
  13. package/dist/cli/completions.js +1 -1
  14. package/dist/cli/completions.js.map +1 -1
  15. package/dist/cli/daemon.js +29 -0
  16. package/dist/cli/daemon.js.map +1 -1
  17. package/dist/cli/director.d.ts +8 -12
  18. package/dist/cli/director.js +17 -52
  19. package/dist/cli/director.js.map +1 -1
  20. package/dist/cli/fleet.js +4 -1
  21. package/dist/cli/fleet.js.map +1 -1
  22. package/dist/cli/help.js +1 -0
  23. package/dist/cli/help.js.map +1 -1
  24. package/dist/cli/leader.d.ts +27 -3
  25. package/dist/cli/leader.js +253 -4
  26. package/dist/cli/leader.js.map +1 -1
  27. package/dist/core/authority/protected-paths.js +11 -0
  28. package/dist/core/authority/protected-paths.js.map +1 -1
  29. package/dist/core/authority/resident.d.ts +101 -0
  30. package/dist/core/authority/resident.js +246 -0
  31. package/dist/core/authority/resident.js.map +1 -0
  32. package/dist/core/authority/server-enforcement.d.ts +110 -0
  33. package/dist/core/authority/server-enforcement.js +260 -0
  34. package/dist/core/authority/server-enforcement.js.map +1 -0
  35. package/dist/core/authority/standing-grant.d.ts +24 -4
  36. package/dist/core/authority/standing-grant.js +19 -3
  37. package/dist/core/authority/standing-grant.js.map +1 -1
  38. package/dist/core/authority/types.d.ts +43 -0
  39. package/dist/core/authority/types.js +6 -0
  40. package/dist/core/authority/types.js.map +1 -1
  41. package/dist/core/cloud/budget.js +11 -1
  42. package/dist/core/cloud/budget.js.map +1 -1
  43. package/dist/core/cloud/cloud-api.d.ts +25 -2
  44. package/dist/core/cloud/cloud-api.js +139 -5
  45. package/dist/core/cloud/cloud-api.js.map +1 -1
  46. package/dist/core/cloud/pr-actions.d.ts +93 -0
  47. package/dist/core/cloud/pr-actions.js +436 -0
  48. package/dist/core/cloud/pr-actions.js.map +1 -0
  49. package/dist/core/cloud/pr-preview.d.ts +128 -0
  50. package/dist/core/cloud/pr-preview.js +228 -0
  51. package/dist/core/cloud/pr-preview.js.map +1 -0
  52. package/dist/core/cloud/store.d.ts +2 -0
  53. package/dist/core/cloud/store.js +28 -0
  54. package/dist/core/cloud/store.js.map +1 -1
  55. package/dist/core/cloud/timeline-api.d.ts +24 -0
  56. package/dist/core/cloud/timeline-api.js +46 -0
  57. package/dist/core/cloud/timeline-api.js.map +1 -0
  58. package/dist/core/cloud/timeline-types.d.ts +62 -0
  59. package/dist/core/cloud/timeline-types.js +36 -0
  60. package/dist/core/cloud/timeline-types.js.map +1 -0
  61. package/dist/core/cloud/timeline.d.ts +102 -0
  62. package/dist/core/cloud/timeline.js +925 -0
  63. package/dist/core/cloud/timeline.js.map +1 -0
  64. package/dist/core/cloud/tracker.js +67 -0
  65. package/dist/core/cloud/tracker.js.map +1 -1
  66. package/dist/core/cloud/types.d.ts +38 -0
  67. package/dist/core/cloud/types.js +1 -1
  68. package/dist/core/cloud/types.js.map +1 -1
  69. package/dist/core/comms/change-digest.d.ts +130 -0
  70. package/dist/core/comms/change-digest.js +451 -0
  71. package/dist/core/comms/change-digest.js.map +1 -0
  72. package/dist/core/comms/director-prompt.d.ts +4 -5
  73. package/dist/core/comms/director-prompt.js +4 -71
  74. package/dist/core/comms/director-prompt.js.map +1 -1
  75. package/dist/core/comms/director.d.ts +24 -61
  76. package/dist/core/comms/director.js +29 -382
  77. package/dist/core/comms/director.js.map +1 -1
  78. package/dist/core/comms/dispatch.d.ts +37 -12
  79. package/dist/core/comms/dispatch.js +244 -177
  80. package/dist/core/comms/dispatch.js.map +1 -1
  81. package/dist/core/comms/elon-dialogue.d.ts +23 -15
  82. package/dist/core/comms/elon-dialogue.js +36 -307
  83. package/dist/core/comms/elon-dialogue.js.map +1 -1
  84. package/dist/core/comms/fleet-pulse.js +4 -7
  85. package/dist/core/comms/fleet-pulse.js.map +1 -1
  86. package/dist/core/comms/handlers.d.ts +10 -6
  87. package/dist/core/comms/handlers.js +14 -48
  88. package/dist/core/comms/handlers.js.map +1 -1
  89. package/dist/core/comms/migrations.d.ts +47 -0
  90. package/dist/core/comms/migrations.js +125 -0
  91. package/dist/core/comms/migrations.js.map +1 -0
  92. package/dist/core/comms/requests.d.ts +32 -5
  93. package/dist/core/comms/requests.js +101 -5
  94. package/dist/core/comms/requests.js.map +1 -1
  95. package/dist/core/comms/telegram-channel.d.ts +104 -0
  96. package/dist/core/comms/telegram-channel.js +622 -0
  97. package/dist/core/comms/telegram-channel.js.map +1 -0
  98. package/dist/core/comms/telegram-thread-map.d.ts +56 -0
  99. package/dist/core/comms/telegram-thread-map.js +144 -0
  100. package/dist/core/comms/telegram-thread-map.js.map +1 -0
  101. package/dist/core/daemon/capacity-publisher.d.ts +8 -1
  102. package/dist/core/daemon/capacity-publisher.js +40 -2
  103. package/dist/core/daemon/capacity-publisher.js.map +1 -1
  104. package/dist/core/daemon/loop.js +18 -4
  105. package/dist/core/daemon/loop.js.map +1 -1
  106. package/dist/core/daemon/service-install-authority.d.ts +10 -2
  107. package/dist/core/daemon/service-install-authority.js +14 -5
  108. package/dist/core/daemon/service-install-authority.js.map +1 -1
  109. package/dist/core/daemon/service.d.ts +15 -0
  110. package/dist/core/daemon/service.js +26 -0
  111. package/dist/core/daemon/service.js.map +1 -1
  112. package/dist/core/daemon/tick-deadline.d.ts +44 -0
  113. package/dist/core/daemon/tick-deadline.js +83 -0
  114. package/dist/core/daemon/tick-deadline.js.map +1 -0
  115. package/dist/core/daemon/tick-hooks.d.ts +7 -0
  116. package/dist/core/daemon/tick-hooks.js.map +1 -1
  117. package/dist/core/daemon/tick-progress.d.ts +39 -0
  118. package/dist/core/daemon/tick-progress.js +194 -0
  119. package/dist/core/daemon/tick-progress.js.map +1 -0
  120. package/dist/core/dashboard.js +3 -1
  121. package/dist/core/dashboard.js.map +1 -1
  122. package/dist/core/fleet/cloud-intake.d.ts +95 -0
  123. package/dist/core/fleet/cloud-intake.js +557 -0
  124. package/dist/core/fleet/cloud-intake.js.map +1 -0
  125. package/dist/core/fleet/dispatch-router.d.ts +13 -0
  126. package/dist/core/fleet/dispatch-router.js +32 -8
  127. package/dist/core/fleet/dispatch-router.js.map +1 -1
  128. package/dist/core/fleet/fleet-merge-state.d.ts +16 -0
  129. package/dist/core/fleet/fleet-merge-state.js.map +1 -1
  130. package/dist/core/fleet/host-merge.d.ts +14 -0
  131. package/dist/core/fleet/host-merge.js +24 -2
  132. package/dist/core/fleet/host-merge.js.map +1 -1
  133. package/dist/core/fleet/merge-gates.d.ts +21 -0
  134. package/dist/core/fleet/merge-gates.js +39 -5
  135. package/dist/core/fleet/merge-gates.js.map +1 -1
  136. package/dist/core/fleet/mirrors.d.ts +162 -6
  137. package/dist/core/fleet/mirrors.js +1051 -49
  138. package/dist/core/fleet/mirrors.js.map +1 -1
  139. package/dist/core/fleet/standing-merge-pass.d.ts +8 -0
  140. package/dist/core/fleet/standing-merge-pass.js +102 -1
  141. package/dist/core/fleet/standing-merge-pass.js.map +1 -1
  142. package/dist/core/fleet/status.d.ts +17 -0
  143. package/dist/core/fleet/status.js +22 -0
  144. package/dist/core/fleet/status.js.map +1 -1
  145. package/dist/core/fleet/tick-hooks-live.d.ts +10 -1
  146. package/dist/core/fleet/tick-hooks-live.js +57 -8
  147. package/dist/core/fleet/tick-hooks-live.js.map +1 -1
  148. package/dist/core/fleet/verify-check-run.d.ts +90 -0
  149. package/dist/core/fleet/verify-check-run.js +274 -0
  150. package/dist/core/fleet/verify-check-run.js.map +1 -0
  151. package/dist/core/git.d.ts +17 -0
  152. package/dist/core/git.js +124 -25
  153. package/dist/core/git.js.map +1 -1
  154. package/dist/core/goals/open-goals.d.ts +47 -0
  155. package/dist/core/goals/open-goals.js +33 -0
  156. package/dist/core/goals/open-goals.js.map +1 -0
  157. package/dist/core/inbox/merge.d.ts +5 -0
  158. package/dist/core/inbox/merge.js +58 -2
  159. package/dist/core/inbox/merge.js.map +1 -1
  160. package/dist/core/integrations/locus.d.ts +28 -0
  161. package/dist/core/integrations/locus.js +155 -30
  162. package/dist/core/integrations/locus.js.map +1 -1
  163. package/dist/core/integrations/telegram-format.d.ts +40 -0
  164. package/dist/core/integrations/telegram-format.js +123 -0
  165. package/dist/core/integrations/telegram-format.js.map +1 -0
  166. package/dist/core/integrations/telegram.d.ts +60 -4
  167. package/dist/core/integrations/telegram.js +161 -28
  168. package/dist/core/integrations/telegram.js.map +1 -1
  169. package/dist/core/mcp-registry.d.ts +12 -0
  170. package/dist/core/mcp-registry.js +70 -0
  171. package/dist/core/mcp-registry.js.map +1 -1
  172. package/dist/core/observability/rollup.d.ts +12 -10
  173. package/dist/core/observability/rollup.js +44 -17
  174. package/dist/core/observability/rollup.js.map +1 -1
  175. package/dist/core/resources/connection-monitor.d.ts +9 -0
  176. package/dist/core/resources/connection-monitor.js +19 -9
  177. package/dist/core/resources/connection-monitor.js.map +1 -1
  178. package/dist/core/resources/quota-refresh-lease.d.ts +17 -0
  179. package/dist/core/resources/quota-refresh-lease.js +34 -1
  180. package/dist/core/resources/quota-refresh-lease.js.map +1 -1
  181. package/dist/core/routing/budget-api.d.ts +18 -1
  182. package/dist/core/routing/budget-api.js +120 -10
  183. package/dist/core/routing/budget-api.js.map +1 -1
  184. package/dist/core/routing/budget-store.d.ts +22 -1
  185. package/dist/core/routing/budget-store.js +22 -8
  186. package/dist/core/routing/budget-store.js.map +1 -1
  187. package/dist/core/routing/readiness-types.d.ts +82 -0
  188. package/dist/core/routing/readiness-types.js +23 -0
  189. package/dist/core/routing/readiness-types.js.map +1 -0
  190. package/dist/core/routing/readiness.d.ts +103 -0
  191. package/dist/core/routing/readiness.js +480 -0
  192. package/dist/core/routing/readiness.js.map +1 -0
  193. package/dist/core/sandbox/confine.js +22 -1
  194. package/dist/core/sandbox/confine.js.map +1 -1
  195. package/dist/core/sandbox/policy.d.ts +23 -0
  196. package/dist/core/sandbox/policy.js +100 -0
  197. package/dist/core/sandbox/policy.js.map +1 -1
  198. package/dist/core/types.d.ts +13 -0
  199. package/dist/core/types.js.map +1 -1
  200. package/dist/core/universe/builtins/preparation/manifest.json +1 -1
  201. package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +1204 -939
  202. package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +1319 -1054
  203. package/dist/core/usage/frontier-usage.d.ts +20 -2
  204. package/dist/core/usage/frontier-usage.js +75 -17
  205. package/dist/core/usage/frontier-usage.js.map +1 -1
  206. package/dist/core/verse/account-health.js +9 -3
  207. package/dist/core/verse/account-health.js.map +1 -1
  208. package/dist/core/verse/accounts.d.ts +45 -0
  209. package/dist/core/verse/accounts.js +246 -17
  210. package/dist/core/verse/accounts.js.map +1 -1
  211. package/dist/core/verse/activity-api.d.ts +5 -1
  212. package/dist/core/verse/activity-api.js +62 -14
  213. package/dist/core/verse/activity-api.js.map +1 -1
  214. package/dist/core/verse/apps-api.js +23 -11
  215. package/dist/core/verse/apps-api.js.map +1 -1
  216. package/dist/core/verse/apps.d.ts +11 -9
  217. package/dist/core/verse/apps.js +2 -2
  218. package/dist/core/verse/apps.js.map +1 -1
  219. package/dist/core/verse/authority-api.d.ts +17 -3
  220. package/dist/core/verse/authority-api.js +122 -7
  221. package/dist/core/verse/authority-api.js.map +1 -1
  222. package/dist/core/verse/autonomy-ladder.d.ts +172 -0
  223. package/dist/core/verse/autonomy-ladder.js +251 -0
  224. package/dist/core/verse/autonomy-ladder.js.map +1 -0
  225. package/dist/core/verse/context-fit.js +7 -5
  226. package/dist/core/verse/context-fit.js.map +1 -1
  227. package/dist/core/verse/control-api.d.ts +23 -1
  228. package/dist/core/verse/control-api.js +117 -10
  229. package/dist/core/verse/control-api.js.map +1 -1
  230. package/dist/core/verse/fleet-live-api.js +13 -5
  231. package/dist/core/verse/fleet-live-api.js.map +1 -1
  232. package/dist/core/verse/folder-io.d.ts +50 -0
  233. package/dist/core/verse/folder-io.js +142 -0
  234. package/dist/core/verse/folder-io.js.map +1 -0
  235. package/dist/core/verse/git-api.js +5 -4
  236. package/dist/core/verse/git-api.js.map +1 -1
  237. package/dist/core/verse/git-ops.d.ts +10 -1
  238. package/dist/core/verse/git-ops.js +18 -2
  239. package/dist/core/verse/git-ops.js.map +1 -1
  240. package/dist/core/verse/github-api.d.ts +1 -1
  241. package/dist/core/verse/github-api.js +5 -4
  242. package/dist/core/verse/github-api.js.map +1 -1
  243. package/dist/core/verse/github-repo.js +118 -14
  244. package/dist/core/verse/github-repo.js.map +1 -1
  245. package/dist/core/verse/leader-api.d.ts +5 -4
  246. package/dist/core/verse/leader-api.js +237 -10
  247. package/dist/core/verse/leader-api.js.map +1 -1
  248. package/dist/core/verse/local-models.d.ts +10 -0
  249. package/dist/core/verse/local-models.js +10 -1
  250. package/dist/core/verse/local-models.js.map +1 -1
  251. package/dist/core/verse/login-path.d.ts +73 -0
  252. package/dist/core/verse/login-path.js +123 -14
  253. package/dist/core/verse/login-path.js.map +1 -1
  254. package/dist/core/verse/mcp-control-api.js +4 -4
  255. package/dist/core/verse/mcp-control-api.js.map +1 -1
  256. package/dist/core/verse/mcp-scope.d.ts +22 -0
  257. package/dist/core/verse/mcp-scope.js +41 -1
  258. package/dist/core/verse/mcp-scope.js.map +1 -1
  259. package/dist/core/verse/mcp-seat-view.d.ts +10 -0
  260. package/dist/core/verse/mcp-seat-view.js +30 -4
  261. package/dist/core/verse/mcp-seat-view.js.map +1 -1
  262. package/dist/core/verse/overnight-api.js +20 -2
  263. package/dist/core/verse/overnight-api.js.map +1 -1
  264. package/dist/core/verse/path-guard.d.ts +24 -0
  265. package/dist/core/verse/path-guard.js +91 -22
  266. package/dist/core/verse/path-guard.js.map +1 -1
  267. package/dist/core/verse/preview-api.js +22 -0
  268. package/dist/core/verse/preview-api.js.map +1 -1
  269. package/dist/core/verse/projects.d.ts +21 -0
  270. package/dist/core/verse/projects.js +82 -0
  271. package/dist/core/verse/projects.js.map +1 -1
  272. package/dist/core/verse/seat-readiness.d.ts +25 -0
  273. package/dist/core/verse/seat-readiness.js +43 -6
  274. package/dist/core/verse/seat-readiness.js.map +1 -1
  275. package/dist/core/verse/session-controls-api.js +5 -3
  276. package/dist/core/verse/session-controls-api.js.map +1 -1
  277. package/dist/core/verse/session-handoff.d.ts +26 -0
  278. package/dist/core/verse/session-handoff.js +90 -12
  279. package/dist/core/verse/session-handoff.js.map +1 -1
  280. package/dist/core/verse/terminal-api.js +7 -4
  281. package/dist/core/verse/terminal-api.js.map +1 -1
  282. package/dist/core/verse/terminal.js +9 -5
  283. package/dist/core/verse/terminal.js.map +1 -1
  284. package/dist/core/verse/verse-api.d.ts +3 -1
  285. package/dist/core/verse/verse-api.js +87 -27
  286. package/dist/core/verse/verse-api.js.map +1 -1
  287. package/dist/core/verse/workspaces.d.ts +51 -0
  288. package/dist/core/verse/workspaces.js +177 -47
  289. package/dist/core/verse/workspaces.js.map +1 -1
  290. package/dist/core/vision/leader-apply.d.ts +40 -0
  291. package/dist/core/vision/leader-apply.js +101 -30
  292. package/dist/core/vision/leader-apply.js.map +1 -1
  293. package/dist/core/vision/leader-cadence.d.ts +114 -0
  294. package/dist/core/vision/leader-cadence.js +127 -0
  295. package/dist/core/vision/leader-cadence.js.map +1 -0
  296. package/dist/core/vision/leader-goal-evidence.d.ts +25 -0
  297. package/dist/core/vision/leader-goal-evidence.js +36 -0
  298. package/dist/core/vision/leader-goal-evidence.js.map +1 -0
  299. package/dist/core/vision/leader-health.d.ts +53 -0
  300. package/dist/core/vision/leader-health.js +92 -0
  301. package/dist/core/vision/leader-health.js.map +1 -0
  302. package/dist/core/vision/leader-operator.d.ts +176 -0
  303. package/dist/core/vision/leader-operator.js +355 -0
  304. package/dist/core/vision/leader-operator.js.map +1 -0
  305. package/dist/core/vision/leader-run-chain.d.ts +41 -0
  306. package/dist/core/vision/leader-run-chain.js +54 -0
  307. package/dist/core/vision/leader-run-chain.js.map +1 -0
  308. package/dist/core/vision/leader-seat-plan.d.ts +68 -0
  309. package/dist/core/vision/leader-seat-plan.js +93 -0
  310. package/dist/core/vision/leader-seat-plan.js.map +1 -0
  311. package/dist/core/vision/leader-seat.d.ts +71 -5
  312. package/dist/core/vision/leader-seat.js +235 -33
  313. package/dist/core/vision/leader-seat.js.map +1 -1
  314. package/dist/core/vision/leader-thread-types.d.ts +104 -0
  315. package/dist/core/vision/leader-thread-types.js +9 -0
  316. package/dist/core/vision/leader-thread-types.js.map +1 -0
  317. package/dist/core/vision/leader-thread.d.ts +138 -0
  318. package/dist/core/vision/leader-thread.js +926 -0
  319. package/dist/core/vision/leader-thread.js.map +1 -0
  320. package/dist/core/vision/leader-types.d.ts +75 -1
  321. package/dist/core/vision/leader.d.ts +88 -18
  322. package/dist/core/vision/leader.js +269 -69
  323. package/dist/core/vision/leader.js.map +1 -1
  324. package/dist/core/web/public/next/assets/{ActionMenu-B4-39f2S.js → ActionMenu-CIiX2X8w.js} +2 -2
  325. package/dist/core/web/public/next/assets/{ActionMenu-B4-39f2S.js.map → ActionMenu-CIiX2X8w.js.map} +1 -1
  326. package/dist/core/web/public/next/assets/Advanced-DHsysoIL.js +2 -0
  327. package/dist/core/web/public/next/assets/{Advanced-B-LqYXvd.js.map → Advanced-DHsysoIL.js.map} +1 -1
  328. package/dist/core/web/public/next/assets/App-CMeAocmW.js +7 -0
  329. package/dist/core/web/public/next/assets/{App-E2zCDRxm.js.map → App-CMeAocmW.js.map} +1 -1
  330. package/dist/core/web/public/next/assets/AppsSection-DISYnMnc.js +2 -0
  331. package/dist/core/web/public/next/assets/{AppsSection-8wXPPhZb.js.map → AppsSection-DISYnMnc.js.map} +1 -1
  332. package/dist/core/web/public/next/assets/AreaTrend-DK2Y2TbZ.js +2 -0
  333. package/dist/core/web/public/next/assets/AreaTrend-DK2Y2TbZ.js.map +1 -0
  334. package/dist/core/web/public/next/assets/AutonomyOffState-CEPSZToP.js +3 -0
  335. package/dist/core/web/public/next/assets/AutonomyOffState-CEPSZToP.js.map +1 -0
  336. package/dist/core/web/public/next/assets/AutonomyOffState-CmSSTHVe.css +1 -0
  337. package/dist/core/web/public/next/assets/AutonomySection-DWc3ciAD.js +2 -0
  338. package/dist/core/web/public/next/assets/{AutonomySection-B46igJKR.js.map → AutonomySection-DWc3ciAD.js.map} +1 -1
  339. package/dist/core/web/public/next/assets/AutonomyStatus-CYaHjJ9U.js +2 -0
  340. package/dist/core/web/public/next/assets/AutonomyStatus-CYaHjJ9U.js.map +1 -0
  341. package/dist/core/web/public/next/assets/{BranchBar-BRkrEQGs.js → BranchBar-BxhdiIpx.js} +2 -2
  342. package/dist/core/web/public/next/assets/{BranchBar-BRkrEQGs.js.map → BranchBar-BxhdiIpx.js.map} +1 -1
  343. package/dist/core/web/public/next/assets/BudgetControl-C-0KhjRS.js +2 -0
  344. package/dist/core/web/public/next/assets/BudgetControl-C-0KhjRS.js.map +1 -0
  345. package/dist/core/web/public/next/assets/{Button-BhO-WKPp.js → Button-D90dzI_o.js} +2 -2
  346. package/dist/core/web/public/next/assets/{Button-BhO-WKPp.js.map → Button-D90dzI_o.js.map} +1 -1
  347. package/dist/core/web/public/next/assets/{CapacityStrip-CkFz5vq2.js → CapacityStrip-qKDBC65C.js} +2 -2
  348. package/dist/core/web/public/next/assets/{CapacityStrip-CkFz5vq2.js.map → CapacityStrip-qKDBC65C.js.map} +1 -1
  349. package/dist/core/web/public/next/assets/ChartFrame-DY-GVUb3.js +2 -0
  350. package/dist/core/web/public/next/assets/ChartFrame-DY-GVUb3.js.map +1 -0
  351. package/dist/core/web/public/next/assets/ChartFrame-DyjmyV8N.css +1 -0
  352. package/dist/core/web/public/next/assets/ChatSection-DlT4QU6a.js +3 -0
  353. package/dist/core/web/public/next/assets/ChatSection-DlT4QU6a.js.map +1 -0
  354. package/dist/core/web/public/next/assets/{CloudBudgetForm-Bd91iStN.js → CloudBudgetForm-BrNun_Yu.js} +2 -2
  355. package/dist/core/web/public/next/assets/{CloudBudgetForm-Bd91iStN.js.map → CloudBudgetForm-BrNun_Yu.js.map} +1 -1
  356. package/dist/core/web/public/next/assets/CommandPalette-C5lDDKN0.js +2 -0
  357. package/dist/core/web/public/next/assets/CommandPalette-C5lDDKN0.js.map +1 -0
  358. package/dist/core/web/public/next/assets/CommandSection-B0xuU-yZ.css +1 -0
  359. package/dist/core/web/public/next/assets/CommandSection-DXFvPgKp.js +3 -0
  360. package/dist/core/web/public/next/assets/CommandSection-DXFvPgKp.js.map +1 -0
  361. package/dist/core/web/public/next/assets/{ConfirmDialog-Bv4hdK2T.js → ConfirmDialog-BR4nZQ2D.js} +2 -2
  362. package/dist/core/web/public/next/assets/{ConfirmDialog-Bv4hdK2T.js.map → ConfirmDialog-BR4nZQ2D.js.map} +1 -1
  363. package/dist/core/web/public/next/assets/{DeleteChatDialog-CH1Ix8Eh.js → DeleteChatDialog-BCfP12GH.js} +2 -2
  364. package/dist/core/web/public/next/assets/{DeleteChatDialog-CH1Ix8Eh.js.map → DeleteChatDialog-BCfP12GH.js.map} +1 -1
  365. package/dist/core/web/public/next/assets/{Dialog-BSsIFNqD.js → Dialog-D3msQlCj.js} +2 -2
  366. package/dist/core/web/public/next/assets/{Dialog-BSsIFNqD.js.map → Dialog-D3msQlCj.js.map} +1 -1
  367. package/dist/core/web/public/next/assets/{DiffPane-BpNuJmth.js → DiffPane-Ix6hvXlY.js} +2 -2
  368. package/dist/core/web/public/next/assets/{DiffPane-BpNuJmth.js.map → DiffPane-Ix6hvXlY.js.map} +1 -1
  369. package/dist/core/web/public/next/assets/{DiffViewer-CJarUvs0.js → DiffViewer-C4WKsCIe.js} +2 -2
  370. package/dist/core/web/public/next/assets/{DiffViewer-CJarUvs0.js.map → DiffViewer-C4WKsCIe.js.map} +1 -1
  371. package/dist/core/web/public/next/assets/DockHost-aWP7liri.js +2 -0
  372. package/dist/core/web/public/next/assets/{DockHost-DW9q0NdR.js.map → DockHost-aWP7liri.js.map} +1 -1
  373. package/dist/core/web/public/next/assets/{EmptyState-DPrd2aak.js → EmptyState-D2IRy3Cg.js} +2 -2
  374. package/dist/core/web/public/next/assets/{EmptyState-DPrd2aak.js.map → EmptyState-D2IRy3Cg.js.map} +1 -1
  375. package/dist/core/web/public/next/assets/{Epistemic-D7MxSmED.js → Epistemic-DMr18muX.js} +2 -2
  376. package/dist/core/web/public/next/assets/{Epistemic-D7MxSmED.js.map → Epistemic-DMr18muX.js.map} +1 -1
  377. package/dist/core/web/public/next/assets/EvidenceTimeline-DXmmz6Fi.js +2 -0
  378. package/dist/core/web/public/next/assets/EvidenceTimeline-DXmmz6Fi.js.map +1 -0
  379. package/dist/core/web/public/next/assets/EvidenceTimeline-PBDAiSuG.css +1 -0
  380. package/dist/core/web/public/next/assets/FleetSection-BfplcwFU.js +3 -0
  381. package/dist/core/web/public/next/assets/FleetSection-BfplcwFU.js.map +1 -0
  382. package/dist/core/web/public/next/assets/Funnel-Dh2yVHws.js +2 -0
  383. package/dist/core/web/public/next/assets/Funnel-Dh2yVHws.js.map +1 -0
  384. package/dist/core/web/public/next/assets/{GearTray-CIPtzeIx.js → GearTray-Dt-ee32-.js} +2 -2
  385. package/dist/core/web/public/next/assets/{GearTray-CIPtzeIx.js.map → GearTray-Dt-ee32-.js.map} +1 -1
  386. package/dist/core/web/public/next/assets/GrowthSection-P9EUfGeA.js +2 -0
  387. package/dist/core/web/public/next/assets/{GrowthSection-iiQg6wvK.js.map → GrowthSection-P9EUfGeA.js.map} +1 -1
  388. package/dist/core/web/public/next/assets/{HandoffDialog-DrQ6ofuy.js → HandoffDialog-CLJQLe7M.js} +2 -2
  389. package/dist/core/web/public/next/assets/{HandoffDialog-DrQ6ofuy.js.map → HandoffDialog-CLJQLe7M.js.map} +1 -1
  390. package/dist/core/web/public/next/assets/{Input-C0MQxEM6.js → Input-D_KRJA9V.js} +2 -2
  391. package/dist/core/web/public/next/assets/{Input-C0MQxEM6.js.map → Input-D_KRJA9V.js.map} +1 -1
  392. package/dist/core/web/public/next/assets/LeaderCard-D-7DlJuR.js +2 -0
  393. package/dist/core/web/public/next/assets/LeaderCard-D-7DlJuR.js.map +1 -0
  394. package/dist/core/web/public/next/assets/LeaderCard-DNsbLwY3.css +1 -0
  395. package/dist/core/web/public/next/assets/LeaderConversation-DGZjWI-E.js +2 -0
  396. package/dist/core/web/public/next/assets/LeaderConversation-DGZjWI-E.js.map +1 -0
  397. package/dist/core/web/public/next/assets/LeaderConversation-DPHUTq6W.css +1 -0
  398. package/dist/core/web/public/next/assets/MatrixHeatmap-C7BCG2d7.js +2 -0
  399. package/dist/core/web/public/next/assets/MatrixHeatmap-C7BCG2d7.js.map +1 -0
  400. package/dist/core/web/public/next/assets/McpSection-DGbRcyv8.js +2 -0
  401. package/dist/core/web/public/next/assets/{McpSection-BRuCcTjT.js.map → McpSection-DGbRcyv8.js.map} +1 -1
  402. package/dist/core/web/public/next/assets/{MessageMarkdown-bRvD5nSc.js → MessageMarkdown-DFvk06qG.js} +3 -3
  403. package/dist/core/web/public/next/assets/{MessageMarkdown-bRvD5nSc.js.map → MessageMarkdown-DFvk06qG.js.map} +1 -1
  404. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-FjjkPKA2.js → MessageMarkdownRenderer-BAv4oEUf.js} +2 -2
  405. package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-FjjkPKA2.js.map → MessageMarkdownRenderer-BAv4oEUf.js.map} +1 -1
  406. package/dist/core/web/public/next/assets/{Meter-CFNLpwA-.js → Meter-CMj-VafN.js} +2 -2
  407. package/dist/core/web/public/next/assets/{Meter-CFNLpwA-.js.map → Meter-CMj-VafN.js.map} +1 -1
  408. package/dist/core/web/public/next/assets/MindSection-C6kLPIjt.js +3 -0
  409. package/dist/core/web/public/next/assets/MindSection-C6kLPIjt.js.map +1 -0
  410. package/dist/core/web/public/next/assets/{MutationTokenDialog-T7gv3a9r.js → MutationTokenDialog-DbnO8aYi.js} +2 -2
  411. package/dist/core/web/public/next/assets/{MutationTokenDialog-T7gv3a9r.js.map → MutationTokenDialog-DbnO8aYi.js.map} +1 -1
  412. package/dist/core/web/public/next/assets/NeedsYouDrawer-crsiX-Nc.js +3 -0
  413. package/dist/core/web/public/next/assets/NeedsYouDrawer-crsiX-Nc.js.map +1 -0
  414. package/dist/core/web/public/next/assets/NeedsYouList-CCsijdWt.js +2 -0
  415. package/dist/core/web/public/next/assets/{NeedsYouList-BgH8ezqf.js.map → NeedsYouList-CCsijdWt.js.map} +1 -1
  416. package/dist/core/web/public/next/assets/NeedsYouList-CH6Mq-E_.css +1 -0
  417. package/dist/core/web/public/next/assets/NewChatDialog-BlCHtOIr.js +3 -0
  418. package/dist/core/web/public/next/assets/{NewChatDialog-Db5Xjrk8.js.map → NewChatDialog-BlCHtOIr.js.map} +1 -1
  419. package/dist/core/web/public/next/assets/OnboardingFlow-Cy9BA9eR.js +3 -0
  420. package/dist/core/web/public/next/assets/OnboardingFlow-Cy9BA9eR.js.map +1 -0
  421. package/dist/core/web/public/next/assets/OnboardingGate-BXpNFoLt.js +3 -0
  422. package/dist/core/web/public/next/assets/{OnboardingGate-C4VXn7BJ.js.map → OnboardingGate-BXpNFoLt.js.map} +1 -1
  423. package/dist/core/web/public/next/assets/PreviewPane-COid5A0f.js +2 -0
  424. package/dist/core/web/public/next/assets/{PreviewPane-BPBbcROb.js.map → PreviewPane-COid5A0f.js.map} +1 -1
  425. package/dist/core/web/public/next/assets/{ProviderLogo-CT2cSfkg.js → ProviderLogo-Cq-CYIwT.js} +2 -2
  426. package/dist/core/web/public/next/assets/{ProviderLogo-CT2cSfkg.js.map → ProviderLogo-Cq-CYIwT.js.map} +1 -1
  427. package/dist/core/web/public/next/assets/{RailStatus-Vgl0GYtd.js → RailStatus-Co5UB6We.js} +2 -2
  428. package/dist/core/web/public/next/assets/{RailStatus-Vgl0GYtd.js.map → RailStatus-Co5UB6We.js.map} +1 -1
  429. package/dist/core/web/public/next/assets/{RefreshIndicator-CSyqpY_q.js → RefreshIndicator-DO9ibnSq.js} +2 -2
  430. package/dist/core/web/public/next/assets/{RefreshIndicator-CSyqpY_q.js.map → RefreshIndicator-DO9ibnSq.js.map} +1 -1
  431. package/dist/core/web/public/next/assets/ResourcePoolConsoleApp-Dr2WRgkq.js +7 -0
  432. package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BX2nkdmZ.js.map → ResourcePoolConsoleApp-Dr2WRgkq.js.map} +1 -1
  433. package/dist/core/web/public/next/assets/ResourcesChrome-CwMnbqI1.js +3 -0
  434. package/dist/core/web/public/next/assets/ResourcesChrome-CwMnbqI1.js.map +1 -0
  435. package/dist/core/web/public/next/assets/ResourcesDrawer-B_Xp_JNc.js +2 -0
  436. package/dist/core/web/public/next/assets/ResourcesDrawer-B_Xp_JNc.js.map +1 -0
  437. package/dist/core/web/public/next/assets/ResourcesDrawer-DrGoO8KG.css +1 -0
  438. package/dist/core/web/public/next/assets/{RouteErrorBoundary-BaC7YVzR.js → RouteErrorBoundary-SkNhhUbt.js} +2 -2
  439. package/dist/core/web/public/next/assets/{RouteErrorBoundary-BaC7YVzR.js.map → RouteErrorBoundary-SkNhhUbt.js.map} +1 -1
  440. package/dist/core/web/public/next/assets/{RunInCloudAction-D5PDHJPE.js → RunInCloudAction-BXPlZwJQ.js} +2 -2
  441. package/dist/core/web/public/next/assets/{RunInCloudAction-D5PDHJPE.js.map → RunInCloudAction-BXPlZwJQ.js.map} +1 -1
  442. package/dist/core/web/public/next/assets/{SeatSelector-Cg75LInq.js → SeatSelector-J5R3zDkB.js} +3 -3
  443. package/dist/core/web/public/next/assets/{SeatSelector-Cg75LInq.js.map → SeatSelector-J5R3zDkB.js.map} +1 -1
  444. package/dist/core/web/public/next/assets/{Segmented-CametjLt.js → Segmented-CGh_-tBb.js} +2 -2
  445. package/dist/core/web/public/next/assets/{Segmented-CametjLt.js.map → Segmented-CGh_-tBb.js.map} +1 -1
  446. package/dist/core/web/public/next/assets/{Select-De52fghU.js → Select-CWnmUzs3.js} +2 -2
  447. package/dist/core/web/public/next/assets/{Select-De52fghU.js.map → Select-CWnmUzs3.js.map} +1 -1
  448. package/dist/core/web/public/next/assets/{SessionGate-CzlcNq3O.js → SessionGate-sp4KsnUo.js} +2 -2
  449. package/dist/core/web/public/next/assets/{SessionGate-CzlcNq3O.js.map → SessionGate-sp4KsnUo.js.map} +1 -1
  450. package/dist/core/web/public/next/assets/{SessionInsightChip-Bk_Owa7u.js → SessionInsightChip-DLFAzdbi.js} +2 -2
  451. package/dist/core/web/public/next/assets/{SessionInsightChip-Bk_Owa7u.js.map → SessionInsightChip-DLFAzdbi.js.map} +1 -1
  452. package/dist/core/web/public/next/assets/{SessionSearch-C6C8DHN8.js → SessionSearch-DbFjwR1h.js} +2 -2
  453. package/dist/core/web/public/next/assets/{SessionSearch-C6C8DHN8.js.map → SessionSearch-DbFjwR1h.js.map} +1 -1
  454. package/dist/core/web/public/next/assets/{SettingsSection-mI9wQYZg.js → SettingsSection-Ch23msJ5.js} +2 -2
  455. package/dist/core/web/public/next/assets/SettingsSection-Ch23msJ5.js.map +1 -0
  456. package/dist/core/web/public/next/assets/SetupChecklist-Bd76MBAR.js +2 -0
  457. package/dist/core/web/public/next/assets/SetupChecklist-Bd76MBAR.js.map +1 -0
  458. package/dist/core/web/public/next/assets/SetupChecklist-BhZpFLsj.css +1 -0
  459. package/dist/core/web/public/next/assets/ShadowDecisions-C5RAY91f.js +3 -0
  460. package/dist/core/web/public/next/assets/ShadowDecisions-C5RAY91f.js.map +1 -0
  461. package/dist/core/web/public/next/assets/{Sheet-CJhK9Dqv.js → Sheet-BZ2AG4Eo.js} +2 -2
  462. package/dist/core/web/public/next/assets/{Sheet-CJhK9Dqv.js.map → Sheet-BZ2AG4Eo.js.map} +1 -1
  463. package/dist/core/web/public/next/assets/{ShortcutsOverlay-DDW-i9Fc.js → ShortcutsOverlay-C8n8y3zf.js} +2 -2
  464. package/dist/core/web/public/next/assets/{ShortcutsOverlay-DDW-i9Fc.js.map → ShortcutsOverlay-C8n8y3zf.js.map} +1 -1
  465. package/dist/core/web/public/next/assets/Sidebar-DN--cPYN.js +3 -0
  466. package/dist/core/web/public/next/assets/Sidebar-DN--cPYN.js.map +1 -0
  467. package/dist/core/web/public/next/assets/{Slider-Bwd9HfXl.js → Slider-KVoJLDD6.js} +2 -2
  468. package/dist/core/web/public/next/assets/{Slider-Bwd9HfXl.js.map → Slider-KVoJLDD6.js.map} +1 -1
  469. package/dist/core/web/public/next/assets/StatTile-C4KgjltA.js +2 -0
  470. package/dist/core/web/public/next/assets/StatTile-C4KgjltA.js.map +1 -0
  471. package/dist/core/web/public/next/assets/StatTile-CNCqDoPO.css +1 -0
  472. package/dist/core/web/public/next/assets/{StatusBadge-DHKRtttH.js → StatusBadge-CXsYqmdU.js} +2 -2
  473. package/dist/core/web/public/next/assets/{StatusBadge-DHKRtttH.js.map → StatusBadge-CXsYqmdU.js.map} +1 -1
  474. package/dist/core/web/public/next/assets/StepBand-BMtVOFpK.js +2 -0
  475. package/dist/core/web/public/next/assets/StepBand-BMtVOFpK.js.map +1 -0
  476. package/dist/core/web/public/next/assets/{Surface-D--LXgQm.js → Surface-BfDmQcKe.js} +2 -2
  477. package/dist/core/web/public/next/assets/{Surface-D--LXgQm.js.map → Surface-BfDmQcKe.js.map} +1 -1
  478. package/dist/core/web/public/next/assets/Swimlane-B9Exxekk.js +2 -0
  479. package/dist/core/web/public/next/assets/Swimlane-B9Exxekk.js.map +1 -0
  480. package/dist/core/web/public/next/assets/Swimlane-Bf4Rjoy9.css +1 -0
  481. package/dist/core/web/public/next/assets/{Switch-PMixU6b0.js → Switch-9NtO_6Lj.js} +2 -2
  482. package/dist/core/web/public/next/assets/{Switch-PMixU6b0.js.map → Switch-9NtO_6Lj.js.map} +1 -1
  483. package/dist/core/web/public/next/assets/{Tag-fordBsrJ.js → Tag-DscCRFcN.js} +2 -2
  484. package/dist/core/web/public/next/assets/{Tag-fordBsrJ.js.map → Tag-DscCRFcN.js.map} +1 -1
  485. package/dist/core/web/public/next/assets/{TerminalPane-BSnGdUuF.js → TerminalPane-B668-DJW.js} +5 -5
  486. package/dist/core/web/public/next/assets/{TerminalPane-BSnGdUuF.js.map → TerminalPane-B668-DJW.js.map} +1 -1
  487. package/dist/core/web/public/next/assets/UniverseConsoleApp-BUV_brY7.js +2 -0
  488. package/dist/core/web/public/next/assets/{UniverseConsoleApp-DtXFhAhw.js.map → UniverseConsoleApp-BUV_brY7.js.map} +1 -1
  489. package/dist/core/web/public/next/assets/{UniverseView-sWm3pnTW.js → UniverseView-DG70zH0W.js} +4 -4
  490. package/dist/core/web/public/next/assets/{UniverseView-sWm3pnTW.js.map → UniverseView-DG70zH0W.js.map} +1 -1
  491. package/dist/core/web/public/next/assets/UsageSection-DY7uOTm1.js +3 -0
  492. package/dist/core/web/public/next/assets/UsageSection-DY7uOTm1.js.map +1 -0
  493. package/dist/core/web/public/next/assets/VerseConsoleApp-CWU6R3By.js +3 -0
  494. package/dist/core/web/public/next/assets/VerseConsoleApp-CWU6R3By.js.map +1 -0
  495. package/dist/core/web/public/next/assets/Workspace-D1nP9BhD.js +10 -0
  496. package/dist/core/web/public/next/assets/{Workspace-BQSZFSeO.js.map → Workspace-D1nP9BhD.js.map} +1 -1
  497. package/dist/core/web/public/next/assets/actions-CY-PjpGc.js +2 -0
  498. package/dist/core/web/public/next/assets/{actions-Ci6I4C0l.js.map → actions-CY-PjpGc.js.map} +1 -1
  499. package/dist/core/web/public/next/assets/actions-DkWhhWIg.css +1 -0
  500. package/dist/core/web/public/next/assets/{apps-model-DlwnfqwJ.js → apps-model-Du02KhAf.js} +2 -2
  501. package/dist/core/web/public/next/assets/{apps-model-DlwnfqwJ.js.map → apps-model-Du02KhAf.js.map} +1 -1
  502. package/dist/core/web/public/next/assets/{authority-model-BFuPezSo.js → authority-model-79SeuKlJ.js} +2 -2
  503. package/dist/core/web/public/next/assets/{authority-model-BFuPezSo.js.map → authority-model-79SeuKlJ.js.map} +1 -1
  504. package/dist/core/web/public/next/assets/autonomy-ladder-BGWmZzP7.css +1 -0
  505. package/dist/core/web/public/next/assets/autonomy-ladder.module-ArDv4bkr.js +2 -0
  506. package/dist/core/web/public/next/assets/autonomy-ladder.module-ArDv4bkr.js.map +1 -0
  507. package/dist/core/web/public/next/assets/budget-queries-D8QV5XtH.js +2 -0
  508. package/dist/core/web/public/next/assets/{budget-queries-DsAjH8ac.js.map → budget-queries-D8QV5XtH.js.map} +1 -1
  509. package/dist/core/web/public/next/assets/{capacity-strip-model-Dn1cNOhz.js → capacity-strip-model-BwZ1ZbkU.js} +2 -2
  510. package/dist/core/web/public/next/assets/{capacity-strip-model-Dn1cNOhz.js.map → capacity-strip-model-BwZ1ZbkU.js.map} +1 -1
  511. package/dist/core/web/public/next/assets/charts-BZ50mZD_.js +2 -0
  512. package/dist/core/web/public/next/assets/charts-BZ50mZD_.js.map +1 -0
  513. package/dist/core/web/public/next/assets/charts-ClCB18la.css +1 -0
  514. package/dist/core/web/public/next/assets/cloud-model-BDivRMqv.js +2 -0
  515. package/dist/core/web/public/next/assets/{cloud-model-CX407gPY.js.map → cloud-model-BDivRMqv.js.map} +1 -1
  516. package/dist/core/web/public/next/assets/{cloud-queries-QAhpExlz.js → cloud-queries-BPQQ5_pJ.js} +2 -2
  517. package/dist/core/web/public/next/assets/{cloud-queries-QAhpExlz.js.map → cloud-queries-BPQQ5_pJ.js.map} +1 -1
  518. package/dist/core/web/public/next/assets/{colors-BZ3a9wFf.js → colors-DvK0wMHH.js} +2 -2
  519. package/dist/core/web/public/next/assets/{colors-BZ3a9wFf.js.map → colors-DvK0wMHH.js.map} +1 -1
  520. package/dist/core/web/public/next/assets/{command-catalog-CF0cQQac.js → command-catalog-BIntSa9l.js} +2 -2
  521. package/dist/core/web/public/next/assets/command-catalog-BIntSa9l.js.map +1 -0
  522. package/dist/core/web/public/next/assets/{context-model-Co4wh3SM.js → context-model-Br8t9jgG.js} +2 -2
  523. package/dist/core/web/public/next/assets/{context-model-Co4wh3SM.js.map → context-model-Br8t9jgG.js.map} +1 -1
  524. package/dist/core/web/public/next/assets/{context-model-DV1zCfAm.js → context-model-CokrgEUq.js} +3 -3
  525. package/dist/core/web/public/next/assets/{context-model-DV1zCfAm.js.map → context-model-CokrgEUq.js.map} +1 -1
  526. package/dist/core/web/public/next/assets/context-queries-eL3caKVG.js +2 -0
  527. package/dist/core/web/public/next/assets/{context-queries-CktFQqSS.js.map → context-queries-eL3caKVG.js.map} +1 -1
  528. package/dist/core/web/public/next/assets/{control-queries-DAvlh_YM.js → control-queries-B9K-wmcB.js} +2 -2
  529. package/dist/core/web/public/next/assets/{control-queries-DAvlh_YM.js.map → control-queries-B9K-wmcB.js.map} +1 -1
  530. package/dist/core/web/public/next/assets/copy-setup-XMgCC5f8.js +2 -0
  531. package/dist/core/web/public/next/assets/copy-setup-XMgCC5f8.js.map +1 -0
  532. package/dist/core/web/public/next/assets/{dock-icons-BFuBbqJl.js → dock-icons-BJ6ZqB2t.js} +2 -2
  533. package/dist/core/web/public/next/assets/{dock-icons-BFuBbqJl.js.map → dock-icons-BJ6ZqB2t.js.map} +1 -1
  534. package/dist/core/web/public/next/assets/{fleet-model-Dfd5JyLR.js → fleet-model-BvDR3gjp.js} +2 -2
  535. package/dist/core/web/public/next/assets/{fleet-model-Dfd5JyLR.js.map → fleet-model-BvDR3gjp.js.map} +1 -1
  536. package/dist/core/web/public/next/assets/{fleet-queries-RtMTd5rR.js → fleet-queries-W2UFVMZJ.js} +2 -2
  537. package/dist/core/web/public/next/assets/{fleet-queries-RtMTd5rR.js.map → fleet-queries-W2UFVMZJ.js.map} +1 -1
  538. package/dist/core/web/public/next/assets/{focus-trap-BdrbWfC4.js → focus-trap-CtI1SlMX.js} +2 -2
  539. package/dist/core/web/public/next/assets/{focus-trap-BdrbWfC4.js.map → focus-trap-CtI1SlMX.js.map} +1 -1
  540. package/dist/core/web/public/next/assets/format-CH4g_sl1.js +2 -0
  541. package/dist/core/web/public/next/assets/format-CH4g_sl1.js.map +1 -0
  542. package/dist/core/web/public/next/assets/{format-DKrNjdwD.js → format-D7nBg2bn.js} +2 -2
  543. package/dist/core/web/public/next/assets/{format-DKrNjdwD.js.map → format-D7nBg2bn.js.map} +1 -1
  544. package/dist/core/web/public/next/assets/{git-model-_BnI_Ogh.js → git-model-Dnkd1TnK.js} +2 -2
  545. package/dist/core/web/public/next/assets/{git-model-_BnI_Ogh.js.map → git-model-Dnkd1TnK.js.map} +1 -1
  546. package/dist/core/web/public/next/assets/global-CMNW_dqn.js +2 -0
  547. package/dist/core/web/public/next/assets/{global-CNNGMril.js.map → global-CMNW_dqn.js.map} +1 -1
  548. package/dist/core/web/public/next/assets/guarded-runners-YeAc9wLQ.js +2 -0
  549. package/dist/core/web/public/next/assets/{guarded-runners-DPh2d32e.js.map → guarded-runners-YeAc9wLQ.js.map} +1 -1
  550. package/dist/core/web/public/next/assets/{icons-0haWy6nh.js → icons-D1sYbCTQ.js} +2 -2
  551. package/dist/core/web/public/next/assets/{icons-0haWy6nh.js.map → icons-D1sYbCTQ.js.map} +1 -1
  552. package/dist/core/web/public/next/assets/{index-QesfXBsb.js → index-DZ-UQ7v8.js} +3 -3
  553. package/dist/core/web/public/next/assets/{index-QesfXBsb.js.map → index-DZ-UQ7v8.js.map} +1 -1
  554. package/dist/core/web/public/next/assets/leader-focus-BjeJv7CW.js +2 -0
  555. package/dist/core/web/public/next/assets/leader-focus-BjeJv7CW.js.map +1 -0
  556. package/dist/core/web/public/next/assets/leader-thread-types-CDb-VPPO.js +2 -0
  557. package/dist/core/web/public/next/assets/leader-thread-types-CDb-VPPO.js.map +1 -0
  558. package/dist/core/web/public/next/assets/{live-model-De-q0ZFK.js → live-model-D_32RAOR.js} +2 -2
  559. package/dist/core/web/public/next/assets/live-model-D_32RAOR.js.map +1 -0
  560. package/dist/core/web/public/next/assets/{mind.module-BrK2nFOL.js → mind.module-7hA92hRg.js} +2 -2
  561. package/dist/core/web/public/next/assets/{mind.module-BrK2nFOL.js.map → mind.module-7hA92hRg.js.map} +1 -1
  562. package/dist/core/web/public/next/assets/mutations-DQPbFdRJ.js +2 -0
  563. package/dist/core/web/public/next/assets/{mutations-DTCtKCn_.js.map → mutations-DQPbFdRJ.js.map} +1 -1
  564. package/dist/core/web/public/next/assets/{nav-KmGX0CVz.js → nav-0dOYe2rJ.js} +2 -2
  565. package/dist/core/web/public/next/assets/{nav-KmGX0CVz.js.map → nav-0dOYe2rJ.js.map} +1 -1
  566. package/dist/core/web/public/next/assets/{overnight-queries-mlO0xzsr.js → overnight-queries-C4wOnFtE.js} +2 -2
  567. package/dist/core/web/public/next/assets/{overnight-queries-mlO0xzsr.js.map → overnight-queries-C4wOnFtE.js.map} +1 -1
  568. package/dist/core/web/public/next/assets/primitives-2A5Eq8FQ.js +1 -0
  569. package/dist/core/web/public/next/assets/{queries-CyBE8nsW.js → queries-BTXWdZzU.js} +2 -2
  570. package/dist/core/web/public/next/assets/{queries-CyBE8nsW.js.map → queries-BTXWdZzU.js.map} +1 -1
  571. package/dist/core/web/public/next/assets/{reasoning-pref-CaoA4_Vl.js → reasoning-pref-Ca6lN6x_.js} +2 -2
  572. package/dist/core/web/public/next/assets/{reasoning-pref-CaoA4_Vl.js.map → reasoning-pref-Ca6lN6x_.js.map} +1 -1
  573. package/dist/core/web/public/next/assets/{reveal-anchor-BP1VqnPU.js → reveal-anchor-DfNLgK_I.js} +2 -2
  574. package/dist/core/web/public/next/assets/{reveal-anchor-BP1VqnPU.js.map → reveal-anchor-DfNLgK_I.js.map} +1 -1
  575. package/dist/core/web/public/next/assets/seat-subscription-C5uPHBsN.js +2 -0
  576. package/dist/core/web/public/next/assets/{seat-subscription-C5Tvr0WQ.js.map → seat-subscription-C5uPHBsN.js.map} +1 -1
  577. package/dist/core/web/public/next/assets/{sidebar-model-B5Qk95k_.js → sidebar-model-CLCdqwkR.js} +2 -2
  578. package/dist/core/web/public/next/assets/{sidebar-model-B5Qk95k_.js.map → sidebar-model-CLCdqwkR.js.map} +1 -1
  579. package/dist/core/web/public/next/assets/{slots-DHKQDoE6.js → slots-C-qBeu-8.js} +3 -3
  580. package/dist/core/web/public/next/assets/{slots-DHKQDoE6.js.map → slots-C-qBeu-8.js.map} +1 -1
  581. package/dist/core/web/public/next/assets/{surface-data-CchClvHo.js → surface-data-Dj71tdD-.js} +2 -2
  582. package/dist/core/web/public/next/assets/{surface-data-CchClvHo.js.map → surface-data-Dj71tdD-.js.map} +1 -1
  583. package/dist/core/web/public/next/assets/surface-prefetch-B0WdJOJG.js +2 -0
  584. package/dist/core/web/public/next/assets/surface-prefetch-B0WdJOJG.js.map +1 -0
  585. package/dist/core/web/public/next/assets/{tasks-model-D6WFVULI.js → tasks-model-O8yR5YXp.js} +2 -2
  586. package/dist/core/web/public/next/assets/{tasks-model-D6WFVULI.js.map → tasks-model-O8yR5YXp.js.map} +1 -1
  587. package/dist/core/web/public/next/assets/{terminal-icons-BvCvuXk2.js → terminal-icons-CoRZq9iO.js} +2 -2
  588. package/dist/core/web/public/next/assets/{terminal-icons-BvCvuXk2.js.map → terminal-icons-CoRZq9iO.js.map} +1 -1
  589. package/dist/core/web/public/next/assets/thread-data-DQB_g66d.js +2 -0
  590. package/dist/core/web/public/next/assets/thread-data-DQB_g66d.js.map +1 -0
  591. package/dist/core/web/public/next/assets/types-IYKrYLku.js +2 -0
  592. package/dist/core/web/public/next/assets/types-IYKrYLku.js.map +1 -0
  593. package/dist/core/web/public/next/assets/{usage-queries-D1SU1t7r.js → usage-queries-AdjEyEtn.js} +2 -2
  594. package/dist/core/web/public/next/assets/{usage-queries-D1SU1t7r.js.map → usage-queries-AdjEyEtn.js.map} +1 -1
  595. package/dist/core/web/public/next/assets/{use-guarded-action-BZmBv7CJ.js → use-guarded-action-CuuMHeDX.js} +2 -2
  596. package/dist/core/web/public/next/assets/{use-guarded-action-BZmBv7CJ.js.map → use-guarded-action-CuuMHeDX.js.map} +1 -1
  597. package/dist/core/web/public/next/assets/{use-ticker-CkJ4-0wU.js → use-ticker-BBcp02SZ.js} +2 -2
  598. package/dist/core/web/public/next/assets/{use-ticker-CkJ4-0wU.js.map → use-ticker-BBcp02SZ.js.map} +1 -1
  599. package/dist/core/web/public/next/assets/{use-token-gate-CYcfk_GS.js → use-token-gate-CbXVYQYI.js} +2 -2
  600. package/dist/core/web/public/next/assets/{use-token-gate-CYcfk_GS.js.map → use-token-gate-CbXVYQYI.js.map} +1 -1
  601. package/dist/core/web/public/next/assets/{useOnboarding-CujRS6PK.js → useOnboarding-C_Fpe_sH.js} +2 -2
  602. package/dist/core/web/public/next/assets/{useOnboarding-CujRS6PK.js.map → useOnboarding-C_Fpe_sH.js.map} +1 -1
  603. package/dist/core/web/public/next/assets/{useSeatsRefresh-BEaQdkZs.js → useSeatsRefresh-Co1CnxLc.js} +2 -2
  604. package/dist/core/web/public/next/assets/{useSeatsRefresh-BEaQdkZs.js.map → useSeatsRefresh-Co1CnxLc.js.map} +1 -1
  605. package/dist/core/web/public/next/assets/{useVerseTranscript-BQ0J6Xhx.js → useVerseTranscript-DgbigUlV.js} +2 -2
  606. package/dist/core/web/public/next/assets/{useVerseTranscript-BQ0J6Xhx.js.map → useVerseTranscript-DgbigUlV.js.map} +1 -1
  607. package/dist/core/web/public/next/assets/{verse-icons-DndgEgKS.js → verse-icons-S56hIiXS.js} +2 -2
  608. package/dist/core/web/public/next/assets/{verse-icons-DndgEgKS.js.map → verse-icons-S56hIiXS.js.map} +1 -1
  609. package/dist/core/web/public/next/assets/verse-model-DAVevkNe.js +2 -0
  610. package/dist/core/web/public/next/assets/{verse-model-MMySehjj.js.map → verse-model-DAVevkNe.js.map} +1 -1
  611. package/dist/core/web/public/next/assets/{warmup-CNEH_-be.js → warmup-B_RAtNZ_.js} +3 -3
  612. package/dist/core/web/public/next/assets/{warmup-CNEH_-be.js.map → warmup-B_RAtNZ_.js.map} +1 -1
  613. package/dist/core/web/public/next/assets/why-seat-model-M_r0wX3b.js +2 -0
  614. package/dist/core/web/public/next/assets/why-seat-model-M_r0wX3b.js.map +1 -0
  615. package/dist/core/web/public/next/assets/{workspace-model-Dukb2wwv.js → workspace-model-dji5-Al3.js} +2 -2
  616. package/dist/core/web/public/next/assets/{workspace-model-Dukb2wwv.js.map → workspace-model-dji5-Al3.js.map} +1 -1
  617. package/dist/core/web/public/next/index.html +1 -1
  618. package/dist/core/web/read-projection-worker.js +2 -1
  619. package/dist/core/web/read-projection-worker.js.map +1 -1
  620. package/dist/core/web/read-projections.d.ts +3 -0
  621. package/dist/core/web/read-projections.js +2 -2
  622. package/dist/core/web/read-projections.js.map +1 -1
  623. package/dist/core/web/visibility.js +3 -4
  624. package/dist/core/web/visibility.js.map +1 -1
  625. package/dist/release-dependency-inventory.json +1 -1
  626. package/docs/AUTONOMY-GAP.md +1 -1
  627. package/package.json +3 -2
  628. package/dist/core/web/public/next/assets/Advanced-B-LqYXvd.js +0 -2
  629. package/dist/core/web/public/next/assets/App-E2zCDRxm.js +0 -7
  630. package/dist/core/web/public/next/assets/AppsSection-8wXPPhZb.js +0 -2
  631. package/dist/core/web/public/next/assets/AreaTrend-BFimExYn.js +0 -2
  632. package/dist/core/web/public/next/assets/AreaTrend-BFimExYn.js.map +0 -1
  633. package/dist/core/web/public/next/assets/AutonomyOffState-B5eEkmiz.js +0 -2
  634. package/dist/core/web/public/next/assets/AutonomyOffState-B5eEkmiz.js.map +0 -1
  635. package/dist/core/web/public/next/assets/AutonomyOffState-DmbE71vI.css +0 -1
  636. package/dist/core/web/public/next/assets/AutonomySection-B46igJKR.js +0 -2
  637. package/dist/core/web/public/next/assets/BudgetControl-T_RjPrw9.js +0 -2
  638. package/dist/core/web/public/next/assets/BudgetControl-T_RjPrw9.js.map +0 -1
  639. package/dist/core/web/public/next/assets/ChartFrame-BVuBe6RP.css +0 -1
  640. package/dist/core/web/public/next/assets/ChartFrame-SygZ7B3x.js +0 -2
  641. package/dist/core/web/public/next/assets/ChartFrame-SygZ7B3x.js.map +0 -1
  642. package/dist/core/web/public/next/assets/ChatSection-D6WybBpB.js +0 -3
  643. package/dist/core/web/public/next/assets/ChatSection-D6WybBpB.js.map +0 -1
  644. package/dist/core/web/public/next/assets/CommandPalette-B6SgDJH-.js +0 -2
  645. package/dist/core/web/public/next/assets/CommandPalette-B6SgDJH-.js.map +0 -1
  646. package/dist/core/web/public/next/assets/CommandSection-CvAVd1w4.css +0 -1
  647. package/dist/core/web/public/next/assets/CommandSection-DgMGoepd.js +0 -2
  648. package/dist/core/web/public/next/assets/CommandSection-DgMGoepd.js.map +0 -1
  649. package/dist/core/web/public/next/assets/DockHost-DW9q0NdR.js +0 -2
  650. package/dist/core/web/public/next/assets/FleetSection-BdZN6rGS.js +0 -3
  651. package/dist/core/web/public/next/assets/FleetSection-BdZN6rGS.js.map +0 -1
  652. package/dist/core/web/public/next/assets/Funnel-BeNQZWv2.js +0 -2
  653. package/dist/core/web/public/next/assets/Funnel-BeNQZWv2.js.map +0 -1
  654. package/dist/core/web/public/next/assets/GrowthSection-iiQg6wvK.js +0 -2
  655. package/dist/core/web/public/next/assets/LeaderCard-BRr3_yE-.js +0 -2
  656. package/dist/core/web/public/next/assets/LeaderCard-BRr3_yE-.js.map +0 -1
  657. package/dist/core/web/public/next/assets/MatrixHeatmap-DZvZeWXL.js +0 -2
  658. package/dist/core/web/public/next/assets/MatrixHeatmap-DZvZeWXL.js.map +0 -1
  659. package/dist/core/web/public/next/assets/McpSection-BRuCcTjT.js +0 -2
  660. package/dist/core/web/public/next/assets/MindSection-BTIiQS8P.js +0 -2
  661. package/dist/core/web/public/next/assets/MindSection-BTIiQS8P.js.map +0 -1
  662. package/dist/core/web/public/next/assets/NeedsYouDrawer-lYN4G_aM.js +0 -2
  663. package/dist/core/web/public/next/assets/NeedsYouDrawer-lYN4G_aM.js.map +0 -1
  664. package/dist/core/web/public/next/assets/NeedsYouList-BgH8ezqf.js +0 -2
  665. package/dist/core/web/public/next/assets/NeedsYouList-CruOt4JT.css +0 -1
  666. package/dist/core/web/public/next/assets/NewChatDialog-Db5Xjrk8.js +0 -3
  667. package/dist/core/web/public/next/assets/OnboardingFlow-LoY-zuSe.js +0 -2
  668. package/dist/core/web/public/next/assets/OnboardingFlow-LoY-zuSe.js.map +0 -1
  669. package/dist/core/web/public/next/assets/OnboardingGate-C4VXn7BJ.js +0 -3
  670. package/dist/core/web/public/next/assets/PreviewPane-BPBbcROb.js +0 -2
  671. package/dist/core/web/public/next/assets/ResourcePoolConsoleApp-BX2nkdmZ.js +0 -7
  672. package/dist/core/web/public/next/assets/ResourcesChrome-B2U2lO1H.js +0 -3
  673. package/dist/core/web/public/next/assets/ResourcesChrome-B2U2lO1H.js.map +0 -1
  674. package/dist/core/web/public/next/assets/ResourcesDrawer-BCFJ2Uq8.css +0 -1
  675. package/dist/core/web/public/next/assets/ResourcesDrawer-DlUKOjl9.js +0 -2
  676. package/dist/core/web/public/next/assets/ResourcesDrawer-DlUKOjl9.js.map +0 -1
  677. package/dist/core/web/public/next/assets/SettingsSection-mI9wQYZg.js.map +0 -1
  678. package/dist/core/web/public/next/assets/Sidebar-DT7ykaqS.js +0 -3
  679. package/dist/core/web/public/next/assets/Sidebar-DT7ykaqS.js.map +0 -1
  680. package/dist/core/web/public/next/assets/StatTile-BuLMsJMx.css +0 -1
  681. package/dist/core/web/public/next/assets/StatTile-DPZ5yyaU.js +0 -2
  682. package/dist/core/web/public/next/assets/StatTile-DPZ5yyaU.js.map +0 -1
  683. package/dist/core/web/public/next/assets/StepBand-DQykLkk_.js +0 -2
  684. package/dist/core/web/public/next/assets/StepBand-DQykLkk_.js.map +0 -1
  685. package/dist/core/web/public/next/assets/Swimlane-BzH6OCee.js +0 -2
  686. package/dist/core/web/public/next/assets/Swimlane-BzH6OCee.js.map +0 -1
  687. package/dist/core/web/public/next/assets/Swimlane-o6_Zsrnr.css +0 -1
  688. package/dist/core/web/public/next/assets/UniverseConsoleApp-DtXFhAhw.js +0 -2
  689. package/dist/core/web/public/next/assets/UsageSection-CiD1DbuF.js +0 -3
  690. package/dist/core/web/public/next/assets/UsageSection-CiD1DbuF.js.map +0 -1
  691. package/dist/core/web/public/next/assets/VerseConsoleApp-BCY5uSok.js +0 -3
  692. package/dist/core/web/public/next/assets/VerseConsoleApp-BCY5uSok.js.map +0 -1
  693. package/dist/core/web/public/next/assets/Workspace-BQSZFSeO.js +0 -10
  694. package/dist/core/web/public/next/assets/actions-BmIf9avl.css +0 -1
  695. package/dist/core/web/public/next/assets/actions-Ci6I4C0l.js +0 -2
  696. package/dist/core/web/public/next/assets/budget-queries-DsAjH8ac.js +0 -2
  697. package/dist/core/web/public/next/assets/charts-C_xJbJiw.js +0 -2
  698. package/dist/core/web/public/next/assets/charts-C_xJbJiw.js.map +0 -1
  699. package/dist/core/web/public/next/assets/charts-DhKTrQLw.css +0 -1
  700. package/dist/core/web/public/next/assets/cloud-model-CX407gPY.js +0 -2
  701. package/dist/core/web/public/next/assets/command-catalog-CF0cQQac.js.map +0 -1
  702. package/dist/core/web/public/next/assets/context-queries-CktFQqSS.js +0 -2
  703. package/dist/core/web/public/next/assets/copy-setup-CqRSfELB.js +0 -2
  704. package/dist/core/web/public/next/assets/copy-setup-CqRSfELB.js.map +0 -1
  705. package/dist/core/web/public/next/assets/format-Dk4Fmxax.js +0 -2
  706. package/dist/core/web/public/next/assets/format-Dk4Fmxax.js.map +0 -1
  707. package/dist/core/web/public/next/assets/global-CNNGMril.js +0 -2
  708. package/dist/core/web/public/next/assets/guarded-runners-DPh2d32e.js +0 -2
  709. package/dist/core/web/public/next/assets/live-model-De-q0ZFK.js.map +0 -1
  710. package/dist/core/web/public/next/assets/mutations-DTCtKCn_.js +0 -2
  711. package/dist/core/web/public/next/assets/primitives-D0Fb3TtS.js +0 -1
  712. package/dist/core/web/public/next/assets/seat-subscription-C5Tvr0WQ.js +0 -2
  713. package/dist/core/web/public/next/assets/surface-prefetch-BWr0LBQn.js +0 -2
  714. package/dist/core/web/public/next/assets/surface-prefetch-BWr0LBQn.js.map +0 -1
  715. package/dist/core/web/public/next/assets/types-D5XOdyw8.js +0 -2
  716. package/dist/core/web/public/next/assets/types-D5XOdyw8.js.map +0 -1
  717. package/dist/core/web/public/next/assets/verse-model-MMySehjj.js +0 -2
  718. package/dist/core/web/public/next/assets/why-seat-model-jlDTwYSc.js +0 -2
  719. package/dist/core/web/public/next/assets/why-seat-model-jlDTwYSc.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../../src/web-ui/routes/verse/chat/NoticeSlot.tsx","../../../../../../src/web-ui/routes/verse/chat/TasksTray.tsx","../../../../../../src/web-ui/routes/verse/chat/composer-state.ts","../../../../../../src/web-ui/routes/verse/composer/composer-text.ts","../../../../../../src/web-ui/routes/verse/composer/AttachmentChips.tsx","../../../../../../src/web-ui/routes/verse/composer/BypassConfirmDialog.tsx","../../../../../../src/web-ui/routes/verse/ContextMeter.tsx","../../../../../../src/web-ui/routes/verse/composer/ContextRing.tsx","../../../../../../src/web-ui/routes/verse/composer/ControlMenu.tsx","../../../../../../src/web-ui/routes/verse/composer/ControlsSheet.tsx","../../../../../../src/web-ui/routes/verse/composer/QueueRow.tsx","../../../../../../src/web-ui/routes/verse/composer/SeatChip.tsx","../../../../../../src/web-ui/routes/verse/composer/SuggestMenu.tsx","../../../../../../src/web-ui/routes/verse/composer/useComposerData.ts","../../../../../../src/web-ui/routes/verse/composer/useFooterFold.ts","../../../../../../src/web-ui/routes/verse/DictationButton.tsx","../../../../../../src/web-ui/routes/verse/health/ComposerSeatBlock.tsx","../../../../../../src/web-ui/routes/verse/Composer.tsx","../../../../../../src/web-ui/routes/verse/chat/activity-model.ts","../../../../../../src/web-ui/routes/verse/chat/ActivityGroup.tsx","../../../../../../src/web-ui/routes/verse/chat/turn-model.ts","../../../../../../src/web-ui/routes/verse/chat/chapter-model.ts","../../../../../../src/web-ui/routes/verse/chat/ChapterRail.tsx","../../../../../../src/web-ui/routes/verse/chat/FileActivity.tsx","../../../../../../src/web-ui/routes/verse/chat/ThinkingBlock.tsx","../../../../../../src/web-ui/routes/verse/chat/TranscriptNav.tsx","../../../../../../src/web-ui/routes/verse/chat/TurnAnnouncer.tsx","../../../../../../src/web-ui/routes/verse/chat/ansi.ts","../../../../../../src/web-ui/routes/verse/chat/CommandOutput.tsx","../../../../../../src/web-ui/routes/verse/chat/DiffBlock.tsx","../../../../../../src/web-ui/routes/verse/ToolUseCard.tsx","../../../../../../src/web-ui/routes/verse/Transcript.tsx","../../../../../../src/web-ui/routes/verse/SeatCapacity.tsx","../../../../../../src/web-ui/routes/verse/health/SeatHealthBanner.tsx","../../../../../../src/web-ui/routes/verse/Workspace.tsx"],"sourcesContent":["/**\n * routes/verse/chat/NoticeSlot.tsx — ONE notice above the composer, not a\n * stack of banners above the transcript (SPEC-310C §2, unit C2).\n *\n * Before 3.10 every advisory had its own row under the header: seat health,\n * the handoff note, the expansive suggestion, the compact panel, a \"Started\n * …\" status — on a bad day four of them pushed the transcript half a screen\n * down, and the one that mattered was wherever it happened to render. Now\n * they share one slot, in a fixed order of consequence:\n *\n * 1. seat health (A2) — the account this chat runs on is broken;\n * 2. engine — the running turn is retrying, was recovered,\n * or has gone quiet (watchdog);\n * 3. context advice — continue in a fresh chat / go expansive;\n * 4. compact — the \"Compact now\" confirm panel.\n *\n * The highest one shows; \"+N more\" opens the rest in place. One exception\n * to the order, deliberately: a notice the operator JUST asked for (they\n * pressed \"Compact now…\") is shown first — hiding the panel they opened\n * behind a seat warning they have already read would make the click look\n * broken. The caller passes that id as `pinnedId`.\n *\n * Each candidate is only passed in when it has something to say, so the\n * slot takes no room at all on a quiet chat.\n */\nimport { useEffect, useId, useState, type ReactNode } from 'react';\nimport styles from './NoticeSlot.module.css';\n\nexport const NOTICE_KINDS = ['seat-health', 'engine', 'context', 'compact'] as const;\nexport type NoticeKind = (typeof NOTICE_KINDS)[number];\n\nconst PRIORITY: Readonly<Record<NoticeKind, number>> = { 'seat-health': 0, engine: 1, context: 2, compact: 3 };\n\nexport interface NoticeCandidate {\n /** Stable per notice (the pinned one and the list keys use it). */\n id: string;\n kind: NoticeKind;\n /** One short phrase for the \"+N more\" list (\"Seat signed out\"). */\n label: string;\n render: () => ReactNode;\n}\n\n/** Priority order; the pinned notice (when present) first. Stable within a kind. */\nexport function orderNotices(candidates: readonly NoticeCandidate[], pinnedId: string | null = null): NoticeCandidate[] {\n return candidates\n .map((c, i) => ({ c, i }))\n .sort((a, b) => {\n if (pinnedId !== null) {\n if (a.c.id === pinnedId && b.c.id !== pinnedId) return -1;\n if (b.c.id === pinnedId && a.c.id !== pinnedId) return 1;\n }\n return PRIORITY[a.c.kind] - PRIORITY[b.c.kind] || a.i - b.i;\n })\n .map(({ c }) => c);\n}\n\nexport interface NoticeSlotProps {\n notices: readonly NoticeCandidate[];\n pinnedId?: string | null;\n}\n\nexport function NoticeSlot({ notices, pinnedId = null }: NoticeSlotProps) {\n const ordered = orderNotices(notices, pinnedId);\n const [expanded, setExpanded] = useState(false);\n const listId = useId();\n const extra = ordered.length - 1;\n // Collapse again once only one is left: \"Show fewer\" with nothing to hide is noise.\n useEffect(() => {\n if (extra <= 0 && expanded) setExpanded(false);\n }, [extra, expanded]);\n\n if (ordered.length === 0) return null;\n const shown = expanded ? ordered : ordered.slice(0, 1);\n const hiddenLabels = ordered.slice(1).map((n) => n.label).join(', ');\n\n return (\n <section className={styles.slot} aria-label=\"Notices\" data-count={ordered.length}>\n <div id={listId} className={styles.list}>\n {shown.map((notice) => (\n <div key={notice.id} className={styles.notice} data-kind={notice.kind}>{notice.render()}</div>\n ))}\n </div>\n {extra > 0 ? (\n <button type=\"button\" className={styles.more} aria-expanded={expanded} aria-controls={listId}\n // The hidden ones are named before they are opened.\n aria-label={expanded ? undefined : `+${extra} more: ${hiddenLabels}`}\n title={expanded ? undefined : hiddenLabels} onClick={() => setExpanded((v) => !v)}>\n {expanded ? 'Show fewer' : `+${extra} more`}\n </button>\n ) : null}\n </section>\n );\n}\n","/**\n * routes/verse/chat/TasksTray.tsx — \"◌ 3 running tasks\", beside the live\n * status line above the composer (SPEC-310C §2, unit C2).\n *\n * The count is this turn's running tool calls and subagents PLUS every\n * other chat with a turn in flight (tasks-model.ts): the operator is waiting\n * on all of them, and until 3.10 the others were only visible by scrolling\n * the sidebar. One click opens the dock's Tasks pane, which lists them and\n * jumps to each. Nothing renders when nothing runs.\n */\nimport { describeTaskCounts, type TaskCounts } from './tasks-model.js';\nimport styles from './TasksTray.module.css';\n\nexport interface TasksTrayProps {\n counts: TaskCounts;\n onOpen: () => void;\n /** The Tasks pane is the dock's visible pane right now. */\n active?: boolean;\n}\n\nexport function TasksTray({ counts, onOpen, active = false }: TasksTrayProps) {\n if (counts.total === 0) return null;\n return (\n <button type=\"button\" className={styles.tray} onClick={onOpen} aria-pressed={active}\n aria-label={`${describeTaskCounts(counts)}. Open the Tasks pane.`} title=\"Open the Tasks pane\">\n <span className={styles.ring} aria-hidden=\"true\" />\n <span className={styles.count}>{counts.total}</span>\n <span className={styles.word}>running task{counts.total === 1 ? '' : 's'}</span>\n </button>\n );\n}\n","/**\n * routes/verse/chat/composer-state.ts — the composer's memory, and an\n * honest estimate of what a message will cost.\n *\n * The memory (per-chat drafts and sent messages in `localStorage`) lives in\n * composer-memory.ts and is re-exported here unchanged; see that file.\n */\n\nimport { CHARS_PER_TOKEN, occupancy } from '../../../../core/verse/context-math.js';\n\nexport {\n clearComposerMemory,\n forgetComposerMemory,\n loadDraft,\n loadHistory,\n pushHistory,\n saveDraft,\n VERSE_DRAFT_STORAGE_KEY,\n VERSE_SENT_STORAGE_KEY,\n} from './composer-memory.js';\n\n// ---------------------------------------------------------------------------\n// Cost\n// ---------------------------------------------------------------------------\n\n/**\n * Characters per token — context-math's CHARS_PER_TOKEN, the one estimator\n * every surface uses (handoff size, fit badges, this hint). A rough\n * English/code average, and rough is the point: the real count is the\n * provider's, which this app does not have before the turn runs. Every\n * surface that shows this figure marks it `≈` and never presents it as\n * measured (DESIGN §6, VERSE-TELEMETRY-V2).\n */\nexport function estimateTokens(text: string): number {\n const trimmed = text.trim();\n if (trimmed.length === 0) return 0;\n return Math.max(1, Math.ceil(trimmed.length / CHARS_PER_TOKEN));\n}\n\n/** `over` = the projection is past the whole window, not just the compaction point. */\nexport type CostTone = 'warn' | 'danger' | 'over';\n\nexport interface CostHint {\n /** Estimated tokens for the draft itself. */\n draftTokens: number;\n /** Occupancy this message would push the session to (current + draft), in tokens. */\n projectedTokens: number;\n /** …as a percent of the context WINDOW (the meter's track), unclamped. */\n projectedPercent: number;\n /** Where the CLI auto-compacts, when known. */\n autoCompactAt: number | null;\n /** True when the projection reaches the compaction point: the CLI compacts before or during this turn. */\n pastCompaction: boolean;\n tone: CostTone;\n /** False when the current occupancy is itself an upper bound (codex before its rollout is read). */\n exact: boolean;\n}\n\n/** The session's context budget as the composer sees it (verse-model `sessionContextBudget`). */\nexport interface CostBudget {\n contextTokens: number | null | undefined;\n contextWindow: number | null | undefined;\n autoCompactAt?: number | null;\n exact?: boolean;\n}\n\n/**\n * What sending this draft would do to the context — or null when the window\n * is unknown, the box is empty, or the session stays comfortable.\n *\n * The tones are context-math `occupancy()`'s — measured against the\n * COMPACTION point when it is known (warn from 80%, danger from 95%), else\n * against the window — which is exactly what colours the ContextMeter, so the\n * hint and the meter can never disagree about when things are getting tight.\n */\nexport function costHint(text: string, budget: CostBudget): CostHint | null {\n const draftTokens = estimateTokens(text);\n if (draftTokens === 0) return null;\n const window = typeof budget.contextWindow === 'number' && budget.contextWindow > 0 ? budget.contextWindow : null;\n if (window === null) return null;\n const used = typeof budget.contextTokens === 'number' && Number.isFinite(budget.contextTokens) ? Math.max(0, budget.contextTokens) : 0;\n const projected = occupancy({\n contextTokens: used + draftTokens,\n contextWindow: window,\n autoCompactAt: budget.autoCompactAt ?? null,\n contextTokensExact: budget.exact,\n });\n if (projected.tone === 'ok' || projected.tone === 'unknown') return null;\n return {\n draftTokens,\n projectedTokens: projected.tokens,\n projectedPercent: Math.min(999, Math.round((projected.ofWindow ?? 0) * 100)),\n autoCompactAt: projected.autoCompactAt,\n pastCompaction: projected.ofCompaction !== null && projected.ofCompaction >= 1,\n tone: projected.tone,\n exact: projected.exact,\n };\n}\n","/**\n * routes/verse/composer/composer-text.ts — the pure text rules behind the\n * composer's `@` finder, `/` commands and attachment tokens (unit C3).\n *\n * Kept free of React and the DOM so every rule is a unit test: what counts as\n * an active trigger at the caret, what a completion inserts and where the\n * caret lands, and how an attachment token is added and removed.\n */\nimport type { VerseFileMatch } from '../../../../core/verse/workbench-types.js';\n\nexport type ComposerTrigger =\n | { kind: 'mention'; query: string; start: number; end: number }\n | { kind: 'command'; query: string; start: number; end: number };\n\n/** Longest `@` query the finder is asked for (the route caps at 200). */\nconst MENTION_QUERY_MAX = 120;\n\n/**\n * The trigger the caret is inside, or null.\n * - `@query` — an `@` at the start of the text or after whitespace, then\n * anything but whitespace up to the caret. An e-mail address (`a@b`) is\n * not a trigger.\n * - `/query` — only as the FIRST thing in the message (a slash command),\n * letters and dashes only, caret still inside it.\n */\nexport function activeTrigger(text: string, caret: number): ComposerTrigger | null {\n if (caret < 0 || caret > text.length) return null;\n const before = text.slice(0, caret);\n const command = /^\\/([a-z-]*)$/i.exec(before);\n if (command) {\n // The rest of the word after the caret belongs to the token too.\n const after = /^[a-z-]*/i.exec(text.slice(caret))?.[0] ?? '';\n return { kind: 'command', query: command[1]!.toLowerCase(), start: 0, end: caret + after.length };\n }\n const mention = /(^|\\s)@([^\\s@]*)$/.exec(before);\n if (mention) {\n const query = mention[2]!;\n if (query.length > MENTION_QUERY_MAX) return null;\n const start = caret - query.length - 1;\n const after = /^[^\\s@]*/.exec(text.slice(caret))?.[0] ?? '';\n return { kind: 'mention', query, start, end: caret + after.length };\n }\n return null;\n}\n\n/** Replace the trigger's span with `insert` (plus one space) and put the caret after it. */\nexport function applyCompletion(text: string, trigger: ComposerTrigger, insert: string): { text: string; caret: number } {\n const tail = text.slice(trigger.end);\n const spacer = tail.startsWith(' ') || insert.endsWith(' ') ? '' : ' ';\n const next = `${text.slice(0, trigger.start)}${insert}${spacer}${tail}`;\n return { text: next, caret: trigger.start + insert.length + spacer.length };\n}\n\n/**\n * The token a file match inserts. A file in the chat's PRIMARY root is\n * written relative (the CLI's cwd is that root); one in another root is\n * written from its root as the server showed it (`~/…`), which the engine\n * expands to an absolute path before the CLI sees it. Paths with spaces are\n * rare in repos but legal: they are quoted, the form Claude Code accepts.\n */\nexport function mentionFor(match: VerseFileMatch, primaryRoot: string | null): string {\n const path = primaryRoot !== null && match.root === primaryRoot ? match.path : `${match.root.replace(/\\/+$/, '')}/${match.path}`;\n return /\\s/.test(path) ? `@\"${path}\"` : `@${path}`;\n}\n\n// ---------------------------------------------------------------------------\n// Slash commands\n// ---------------------------------------------------------------------------\n\nexport type SlashCommandId = 'handoff' | 'compact' | 'new' | 'plan' | 'effort' | 'model';\n\nexport interface SlashCommand {\n id: SlashCommandId;\n title: string;\n description: string;\n}\n\n/** SPEC-310C §2: `/` offers exactly these, in this order. */\nexport const SLASH_COMMANDS: readonly SlashCommand[] = [\n { id: 'handoff', title: '/handoff', description: 'Continue in a fresh chat with a handoff note' },\n { id: 'compact', title: '/compact', description: 'Summarise earlier turns to free context (sent as a turn)' },\n { id: 'new', title: '/new', description: 'Start a new chat on this seat' },\n { id: 'plan', title: '/plan', description: 'Switch this chat to Plan mode — nothing is edited' },\n { id: 'effort', title: '/effort', description: 'Choose the reasoning effort' },\n { id: 'model', title: '/model', description: 'Choose the model' },\n];\n\n/** Commands whose id or title starts with (then contains) the query. */\nexport function matchSlashCommands(query: string): SlashCommand[] {\n const q = query.toLowerCase();\n if (!q) return [...SLASH_COMMANDS];\n const starts = SLASH_COMMANDS.filter((c) => c.id.startsWith(q));\n const contains = SLASH_COMMANDS.filter((c) => !c.id.startsWith(q) && (c.id.includes(q) || c.description.toLowerCase().includes(q)));\n return [...starts, ...contains];\n}\n\n// ---------------------------------------------------------------------------\n// Attachment tokens\n// ---------------------------------------------------------------------------\n\n/** Append an attachment's `@path` token to the draft, on its own if the draft is empty. */\nexport function insertAttachmentRef(text: string, ref: string, caret: number | null = null): { text: string; caret: number } {\n if (caret === null || caret < 0 || caret > text.length) {\n const sep = text.length === 0 || /\\s$/.test(text) ? '' : ' ';\n const next = `${text}${sep}${ref} `;\n return { text: next, caret: next.length };\n }\n const before = text.slice(0, caret);\n const after = text.slice(caret);\n const lead = before.length === 0 || /\\s$/.test(before) ? '' : ' ';\n const trail = after.startsWith(' ') ? '' : ' ';\n const next = `${before}${lead}${ref}${trail}${after}`;\n return { text: next, caret: before.length + lead.length + ref.length + trail.length };\n}\n\n/** Remove every occurrence of an attachment's token (and one space after it). */\nexport function removeAttachmentRef(text: string, ref: string): string {\n if (!ref) return text;\n return text.split(`${ref} `).join('').split(ref).join('').replace(/ {2,}/g, ' ');\n}\n\n/** Bytes as the operator reads them: 812 B, 14 KB, 2.3 MB. */\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '—';\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/** A File as base64 without the `data:` prefix (the route accepts either). */\nexport function readFileAsBase64(file: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error ?? new Error('the file could not be read'));\n reader.onload = () => {\n const result = typeof reader.result === 'string' ? reader.result : '';\n resolve(result.slice(result.indexOf(',') + 1));\n };\n reader.readAsDataURL(file);\n });\n}\n","/**\n * routes/verse/composer/AttachmentChips.tsx — thumbnail chips for the files\n * attached to the message being written (unit C3).\n *\n * An image shows its thumbnail (an object URL of the LOCAL file — the stored\n * copy is private and never served back to the page); anything else shows its\n * extension. Each chip says its state in words: uploading, attached (with\n * size), or why it failed. × removes the chip, the token in the text and the\n * stored copy.\n */\nimport { IconX } from '../../../components/primitives/icons.js';\nimport { formatBytes } from './composer-text.js';\nimport type { AttachmentDraft } from './useComposerData.js';\nimport styles from './composer.module.css';\n\nexport interface AttachmentChipsProps {\n drafts: readonly AttachmentDraft[];\n onRemove: (key: string) => void;\n disabled?: boolean;\n}\n\nfunction extension(name: string): string {\n const dot = name.lastIndexOf('.');\n const ext = dot > 0 ? name.slice(dot + 1) : '';\n return ext ? ext.slice(0, 4).toUpperCase() : 'FILE';\n}\n\nexport function AttachmentChips({ drafts, onRemove, disabled = false }: AttachmentChipsProps) {\n if (drafts.length === 0) return null;\n return (\n <ul className={styles.chips} aria-label=\"Attachments\">\n {drafts.map((draft) => {\n const state = draft.status === 'uploading'\n ? 'uploading'\n : draft.status === 'error'\n ? (draft.error ?? 'not attached')\n : formatBytes(draft.bytes);\n return (\n <li key={draft.key} className={styles.chip} data-status={draft.status}>\n {draft.previewUrl ? (\n <img className={styles.chipThumb} src={draft.previewUrl} alt=\"\" />\n ) : (\n <span className={styles.chipExt} aria-hidden=\"true\">{extension(draft.name)}</span>\n )}\n <span className={styles.chipText}>\n <span className={styles.chipName} title={draft.name}>{draft.name}</span>\n <span className={styles.chipMeta} role={draft.status === 'error' ? 'alert' : undefined}>\n {draft.status === 'uploading' ? <span className={styles.chipSpinner} aria-hidden=\"true\" /> : null}\n {state}\n </span>\n </span>\n <button type=\"button\" className={styles.chipRemove} disabled={disabled && draft.status !== 'error'}\n aria-label={`Remove ${draft.name}`} onClick={() => onRemove(draft.key)}>\n <IconX width={12} height={12} aria-hidden=\"true\" />\n </button>\n </li>\n );\n })}\n </ul>\n );\n}\n","/**\n * routes/verse/composer/BypassConfirmDialog.tsx — the per-chat confirmation\n * in front of Bypass permissions (SPEC-310C §0.6; unit C3).\n *\n * Bypass turns off every permission check the CLI has. It is never a\n * default, never inherited by a new chat, and never one keystroke away: this\n * dialog opens with focus on CANCEL, so Enter-Enter from the picker keeps the\n * chat as it was. Only the explicit red button sends `confirmBypass: true`\n * (the server refuses bypass without it).\n */\nimport { useId, useRef } from 'react';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { Dialog } from '../../../components/primitives/Dialog.js';\nimport { shortcutLabel } from './composer-keys.js';\nimport styles from './composer.module.css';\n\nexport interface BypassConfirmDialogProps {\n open: boolean;\n /** \"Claude Max · Opus 5.5\" — which chat this applies to. */\n chatLabel: string;\n running: boolean;\n onCancel: () => void;\n onConfirm: () => void;\n}\n\nexport function BypassConfirmDialog({ open, chatLabel, running, onCancel, onConfirm }: BypassConfirmDialogProps) {\n const titleId = useId();\n const cancel = useRef<HTMLButtonElement>(null);\n return (\n <Dialog open={open} onClose={onCancel} titleId={titleId} title=\"Bypass permissions for this chat?\" initialFocusRef={cancel}\n description={<>The agent in <strong>{chatLabel}</strong> will run every command and edit every file without asking — including outside the project.</>}>\n <ul className={styles.bypassList}>\n <li>Applies to this chat only{running ? ', from the next turn' : ''}. New chats never inherit it.</li>\n <li>Switch back any time from the Permission menu{shortcutLabel('composer.permission') ? <> (<kbd>{shortcutLabel('composer.permission')}</kbd>)</> : null}.</li>\n <li>Use it in a sandbox or a throwaway worktree, not on work you can’t lose.</li>\n </ul>\n <div className={styles.bypassActions}>\n <Button ref={cancel} variant=\"subtle\" onClick={onCancel}>Cancel</Button>\n <Button variant=\"danger\" onClick={onConfirm}>Bypass for this chat</Button>\n </div>\n </Dialog>\n );\n}\n","/**\n * routes/verse/ContextMeter.tsx — live context occupancy, the session's\n * context mode, and the two pieces of advice that follow from them.\n *\n * DESIGN §4: not a labelled progress bar. The track is a 2px full-width line\n * pinned to the bottom edge of the header strip (the component's own root\n * stays unpositioned so the header is its containing block); the numbers ride\n * alongside in the display font, the tooltip and `aria-valuetext` carry the\n * long form.\n *\n * WHAT CHANGED IN 3.9, and why (docs/VERSE-CONTEXT.md):\n *\n * - The track is the FULL window and carries a tick where the CLI will\n * auto-compact. A 1M Claude model in standard mode compacts near 367k; a\n * bar that only showed \"37% of 1M\" hid the one number that decides when\n * the agent starts forgetting.\n * - Tone comes from context-math `occupancy()`, measured against the\n * COMPACTION point (warn from 80%, danger from 95%) — before the CLI\n * compacts, not after. The old fixed 70/90% of the window turned red only\n * once codex (90%) and grok (80%) had already compacted.\n * - Nothing is clamped: a reading past the window is information, drawn as\n * an `over` state rather than pinned at 100%.\n * - An upper-bound reading (codex before its rollout is read) is prefixed\n * `≤`, never shown as a measurement.\n *\n * Every figure here is computed by context-math, the same module the server\n * uses, so \"compacts ≈367k\" cannot mean two different numbers in two places.\n */\nimport { useEffect, useId, useLayoutEffect, useRef, useState, type CSSProperties, type FormEvent, type KeyboardEvent } from 'react';\nimport type { VerseContextMode, VerseEngine, VerseModelOption, VerseSession, VerseWindowSource } from '../../data/api-types.js';\nimport {\n budgetFor,\n CACHE_IDLE_TTL_MS,\n expansiveAdvice,\n handoffAdvice,\n hasExpansiveMode,\n occupancy,\n OCCUPANCY_DANGER,\n OCCUPANCY_WARN,\n type VerseHandoffAdvice,\n type VerseOccupancyTone,\n} from '../../../core/verse/context-math.js';\nimport { CODEX_EXPANSIVE_METERING_NOTE, formatContextWindow, windowSourceText, type SessionContextBudget } from './verse-model.js';\nimport { dismissVerseAdvice, isVerseAdviceDismissed } from './verse-ui-store.js';\nimport { formatTokens } from './verse-readouts.js';\nimport styles from './Workspace.module.css';\n\n/** Percent-of-compaction thresholds, re-exported so copy can quote them. */\nexport const CONTEXT_WARN_PERCENT = Math.round(OCCUPANCY_WARN * 100);\nexport const CONTEXT_DANGER_PERCENT = Math.round(OCCUPANCY_DANGER * 100);\n\nexport const CONTEXT_MODE_LABEL: Record<VerseContextMode, string> = {\n standard: 'Standard',\n expansive: 'Expansive',\n};\n\n/** Who does the compacting, in words the operator recognises. */\nconst COMPACTOR: Record<VerseEngine, string> = {\n claude: 'Claude Code',\n local: 'Claude Code (driving the local model)',\n codex: 'Codex',\n grok: 'Grok',\n};\n\n/** Full-precision figure for the tooltip, where `367k` would hide the digits. */\nfunction exactFigure(n: number): string {\n return Math.round(n).toLocaleString('en-US');\n}\n\nexport interface ContextMeterProps {\n contextTokens: number | null | undefined;\n contextWindow: number | null | undefined;\n /** Where the CLI auto-compacts in the session's current mode; null/absent = unknown. */\n autoCompactAt?: number | null;\n /** False when `contextTokens` is an upper bound, drawn with a `≤`. Absent = exact. */\n exact?: boolean;\n source?: VerseWindowSource | null;\n mode?: VerseContextMode | null;\n engine?: VerseEngine | null;\n compactionCount?: number;\n /**\n * `inline` draws the track in flow (the Context pane) instead of pinned.\n * `ring` (3.10, the chat header) is a 16px occupancy ring plus the\n * percentage — the same figures and the same tooltip, in the room the\n * header has now that the title carries a breadcrumb.\n */\n variant?: 'header' | 'inline' | 'ring';\n}\n\n/** The meter's words, separated from its markup so the tooltip and the tests read one source. */\nexport function describeContext(props: Omit<ContextMeterProps, 'variant'>): {\n tone: VerseOccupancyTone;\n /** \"≤142k / 1M\" */\n label: string;\n /** \"compacts ≈367k\", or null when the point is unknown. */\n compactLabel: string | null;\n /** Percent of the WINDOW, unclamped; null when the window is unknown. */\n percent: number | null;\n percentLabel: string;\n /** Where the compaction tick sits on the track (0–100), or null when not drawable. */\n tickPercent: number | null;\n /** Fill width on the track (0–100). */\n fillPercent: number;\n /**\n * The reading itself — occupancy, an upper-bound or past-the-window caveat,\n * and where it compacts — without the explanation and provenance lines\n * `title` adds. The composer ring's tooltip is exactly these lines.\n */\n summary: string[];\n title: string;\n} {\n const occ = occupancy({\n contextTokens: typeof props.contextTokens === 'number' && Number.isFinite(props.contextTokens) ? props.contextTokens : 0,\n contextWindow: props.contextWindow ?? null,\n autoCompactAt: props.autoCompactAt ?? null,\n contextTokensExact: props.exact,\n });\n const bound = occ.exact ? '' : '≤';\n // An upper bound past the compaction point proves nothing about the real\n // prompt (occupancy() returns tone 'unknown' for it), so no sentence below\n // may read it as \"past\" anything.\n const boundPast = !occ.exact && occ.untilCompaction === 0;\n const label = `${bound}${formatTokens(occ.tokens)} / ${occ.window !== null ? formatContextWindow(occ.window) : 'n/a'}`;\n const compactLabel = occ.autoCompactAt !== null ? `compacts ≈${formatTokens(occ.autoCompactAt)}` : null;\n const percent = occ.ofWindow !== null ? Math.round(occ.ofWindow * 100) : null;\n const tickPercent = occ.window !== null && occ.autoCompactAt !== null && occ.autoCompactAt < occ.window\n ? (occ.autoCompactAt / occ.window) * 100\n : null;\n const fillPercent = occ.ofWindow !== null ? Math.min(100, Math.max(0, occ.ofWindow * 100)) : 0;\n\n const lines: string[] = [];\n lines.push(occ.window !== null\n ? `Context: ${bound}${exactFigure(occ.tokens)} of ${exactFigure(occ.window)} tokens (${percent}%).`\n : `Context: ${bound}${exactFigure(occ.tokens)} tokens — the window is unknown, so no percentage is shown.`);\n if (!occ.exact) {\n lines.push('Upper bound: this CLI reported only the turn total so far; the exact last-call size replaces it once its session log is read.');\n }\n if (occ.tone === 'over') {\n lines.push('Past the window: the next turn will fail or force the CLI to compact first. Continue in a fresh chat.');\n }\n const modeText = props.mode ? ` (${CONTEXT_MODE_LABEL[props.mode]} mode)` : '';\n if (occ.autoCompactAt !== null) {\n const left = boundPast\n ? 'the upper bound is past that point, which does not mean the real prompt is'\n : occ.untilCompaction === 0\n ? 'at or past that point now'\n : `${occ.exact ? '' : 'at least '}≈${exactFigure(occ.untilCompaction ?? 0)} left`;\n lines.push(`Auto-compacts at ≈${exactFigure(occ.autoCompactAt)} tokens${modeText} — ${left}.`);\n } else if (occ.window !== null) {\n lines.push(`The compaction point is unknown${modeText}; the colour is measured against the whole window.`);\n }\n const summary = [...lines];\n if (occ.autoCompactAt !== null) {\n const who = props.engine ? COMPACTOR[props.engine] : 'The CLI';\n lines.push(`When it compacts, ${who} replaces the earlier conversation with a summary and carries on; early detail then survives only as that summary.`);\n }\n if ((props.compactionCount ?? 0) > 0) {\n const n = props.compactionCount ?? 0;\n lines.push(`Compacted ${n} time${n === 1 ? '' : 's'} so far.`);\n }\n if (occ.window !== null) {\n lines.push(`Window: ${props.source ? windowSourceText(props.source, props.engine) : 'source not recorded (an older session)'}.`);\n }\n return {\n tone: occ.tone,\n label,\n compactLabel,\n percent,\n percentLabel: percent === null ? 'n/a' : `${bound}${percent}%`,\n tickPercent,\n fillPercent,\n summary,\n title: lines.join('\\n'),\n };\n}\n\nconst RING_R = 6;\nconst RING_C = 2 * Math.PI * RING_R;\n\n/**\n * The 16px occupancy ring with the compaction point as a tick — ONE drawing\n * for the chat header (`variant=\"ring\"`) and the composer footer, fed from one\n * `describeContext`, so the two rings cannot disagree about how full the chat\n * is or where it compacts. Each caller brings its own classes (colours live\n * with the surface). Decorative: the caller carries the words.\n */\nexport function ContextRingGlyph({ fillPercent, tickPercent, classes }: {\n fillPercent: number;\n tickPercent: number | null;\n classes: { svg?: string; track?: string; fill?: string; tick?: string };\n}) {\n const angle = tickPercent === null ? null : ((tickPercent / 100) * 360 - 90) * (Math.PI / 180);\n const at = (radius: number) => (angle === null ? null : { x: 8 + Math.cos(angle) * radius, y: 8 + Math.sin(angle) * radius });\n const inner = at(RING_R - 2.5);\n const outer = at(RING_R + 1.5);\n return (\n <svg className={classes.svg} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\" focusable=\"false\">\n <circle className={classes.track} cx=\"8\" cy=\"8\" r={RING_R} />\n <circle className={classes.fill} cx=\"8\" cy=\"8\" r={RING_R}\n strokeDasharray={`${(fillPercent / 100) * RING_C} ${RING_C}`} transform=\"rotate(-90 8 8)\" />\n {inner && outer ? (\n <line className={classes.tick} data-testid=\"compaction-tick\" x1={inner.x} y1={inner.y} x2={outer.x} y2={outer.y} />\n ) : null}\n </svg>\n );\n}\n\nexport function ContextMeter({ variant = 'header', ...props }: ContextMeterProps) {\n const d = describeContext(props);\n // aria-valuenow is bounded by aria-valuemax; the over state is carried by\n // the text and data-tone instead of an out-of-range value.\n const valueNow = d.percent === null ? undefined : Math.min(100, d.percent);\n const valueText = `${d.label} (${d.percentLabel})${d.compactLabel ? `, ${d.compactLabel.replace('≈', 'at about ')}` : ''}${d.tone === 'over' ? ', past the window' : ''}`;\n if (variant === 'ring') {\n return (\n <div className={`${styles.ring} ${styles[`meter-${d.tone}`] ?? ''}`} role=\"meter\" aria-label=\"Context window\"\n aria-valuemin={0} aria-valuemax={100} aria-valuenow={valueNow} aria-valuetext={valueText}\n title={d.title} data-tone={d.tone} data-exact={props.exact === false ? 'false' : undefined}>\n {/* The compaction point is a tick on the ring, like on the line. */}\n <ContextRingGlyph fillPercent={d.fillPercent} tickPercent={d.tickPercent}\n classes={{ svg: styles.ringSvg, track: styles.ringTrack, fill: styles.ringFill, tick: styles.ringTick }} />\n <span className={styles.ringText}>{d.percentLabel}</span>\n </div>\n );\n }\n return (\n <div\n className={`${styles.meter} ${styles[`meter-${d.tone}`] ?? ''} ${variant === 'inline' ? styles.meterInline : ''}`}\n role=\"meter\" aria-label=\"Context window\" aria-valuemin={0} aria-valuemax={100}\n aria-valuenow={valueNow} aria-valuetext={valueText}\n title={d.title} data-tone={d.tone} data-exact={props.exact === false ? 'false' : undefined}\n >\n <div className={styles.meterTrack} aria-hidden=\"true\">\n <div className={styles.meterFill} style={{ width: `${d.fillPercent}%` }} />\n {d.tickPercent !== null ? (\n <div className={styles.meterTick} data-testid=\"compaction-tick\" style={{ left: `${d.tickPercent}%` }} />\n ) : null}\n </div>\n <span className={styles.meterText}>\n <span className={styles.meterTokens}>{d.label}</span>\n {d.compactLabel ? <span className={styles.meterCompact}>· {d.compactLabel}</span> : null}\n <span className={styles.meterPercent}>{d.percentLabel}</span>\n </span>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Context mode — Standard / Expansive\n// ---------------------------------------------------------------------------\n\n/**\n * \"Late turns cost up to ≈2.6× the usage\": every turn re-sends the whole\n * context, so the most a turn can carry grows with the compaction point. The\n * ratio is of the two compaction points (the largest prompt each mode lets a\n * turn reach), rounded to one decimal — an honest ceiling, not a forecast.\n */\nexport function expansiveCostRatio(option: VerseModelOption | null | undefined): number | null {\n const standard = budgetFor(option, 'standard');\n const expansive = budgetFor(option, 'expansive');\n if (!standard || !expansive) return null;\n const s = standard.autoCompactAt ?? standard.contextWindow;\n const e = expansive.autoCompactAt ?? expansive.contextWindow;\n if (s <= 0 || e <= s) return null;\n return Math.round((e / s) * 10) / 10;\n}\n\nfunction modeDetail(option: VerseModelOption | null | undefined, mode: VerseContextMode): string {\n const b = budgetFor(option, mode);\n if (!b) return 'not available for this model';\n const point = b.autoCompactAt ?? b.contextWindow;\n return `compacts ≈${formatTokens(point)} of ${formatContextWindow(b.contextWindow)}`;\n}\n\n/**\n * The cost sentence both the mode menu and the suggestion chip say, word for\n * word. The ratio is SIZE alone. On codex the provider reportedly also meters\n * requests above its standard window at about 2× against plan limits, so the\n * size ratio is not the ceiling there: the copy says so, in the same\n * sentence the new-chat dialog uses (verse-model CODEX_EXPANSIVE_METERING_NOTE),\n * so the three places that price Expansive cannot disagree.\n */\nexport function expansiveCostCopy(option: VerseModelOption | null | undefined, engine?: VerseEngine | null): string {\n const ratio = expansiveCostRatio(option);\n const standard = budgetFor(option, 'standard');\n const point = standard ? formatTokens(standard.autoCompactAt ?? standard.contextWindow) : null;\n const codex = engine === 'codex';\n const base = ratio !== null && point !== null\n ? `Every turn re-sends the whole context, so once it grows past ≈${point} each turn costs more usage — up to ≈${ratio}×${codex ? ' by size alone' : ''} near the expansive limit — and recall of early detail weakens with length.`\n : 'Every turn re-sends the whole context, so a larger budget means more usage per turn as it grows.';\n return codex ? `${base} ${CODEX_EXPANSIVE_METERING_NOTE}` : base;\n}\n\n/**\n * Switching DOWN to Standard while the chat already holds more than\n * Standard's compaction point is not the free flag flip the rest of the menu\n * describes: the CLI compacts on the very next turn (Claude Code checks its\n * threshold before calling the model; codex falls back to its 244.8k limit),\n * which rewrites the conversation, starts the prompt cache over and — on a\n * paid seat — spends a summarization call. Null when switching is free.\n * `definite` is false for an upper-bound reading: the real prompt MAY be under.\n */\nexport function standardSwitchCompacts(\n option: VerseModelOption | null | undefined,\n mode: VerseContextMode,\n reading: Pick<SessionContextBudget, 'contextTokens' | 'exact'> | null | undefined,\n): { point: number; tokens: number; definite: boolean } | null {\n if (mode !== 'expansive' || !reading) return null;\n const standard = budgetFor(option, 'standard');\n if (!standard) return null;\n const point = standard.autoCompactAt ?? standard.contextWindow;\n if (reading.contextTokens < point) return null;\n return { point, tokens: reading.contextTokens, definite: reading.exact };\n}\n\n/**\n * Where the open menu goes, in VIEWPORT coordinates (position: fixed).\n *\n * WHY FIXED, and computed: the chip sits in the right-pinned action cluster,\n * ~119px from the pane's edge, and the menu is up to 380px wide — anchored\n * `right: 0` it started at x = −95 on a 375px screen, and in the app the\n * section's `overflow: hidden` cut it at the rail (x = 56), clipping every\n * label (\"rrent\", \"…of 1M\"). Fixed placement escapes that clip; the three\n * cases keep it inside the viewport with a gutter:\n *\n * 1. right-aligned to the chip (the desktop look) when there is room to its left;\n * 2. left-aligned to the chip when there is room to its right instead;\n * 3. otherwise the full width between the gutters (phone widths).\n *\n * Null when there is no layout to measure (a zero-size anchor — jsdom, a\n * hidden header): the stylesheet's absolute placement stands.\n */\nexport const MODE_MENU_GUTTER = 12;\nexport const MODE_MENU_MIN_WIDTH = 280;\nexport const MODE_MENU_MAX_WIDTH = 380;\nconst MODE_MENU_OFFSET = 8;\n\nexport function modeMenuPlacement(\n anchor: Pick<DOMRect, 'left' | 'right' | 'bottom'>,\n viewport: { width: number; height: number },\n): CSSProperties | null {\n if (!(viewport.width > 0) || !(anchor.right > anchor.left)) return null;\n const top = Math.round(anchor.bottom + MODE_MENU_OFFSET);\n const common: CSSProperties = {\n position: 'fixed',\n top,\n maxHeight: Math.max(160, Math.round(viewport.height - top - MODE_MENU_GUTTER)),\n overflowY: 'auto',\n };\n const roomLeft = anchor.right - MODE_MENU_GUTTER;\n if (roomLeft >= MODE_MENU_MIN_WIDTH) {\n return { ...common, left: 'auto', right: Math.round(viewport.width - anchor.right), maxWidth: Math.min(MODE_MENU_MAX_WIDTH, Math.floor(roomLeft)) };\n }\n const roomRight = viewport.width - MODE_MENU_GUTTER - anchor.left;\n if (roomRight >= MODE_MENU_MIN_WIDTH) {\n return { ...common, left: Math.round(anchor.left), right: 'auto', maxWidth: Math.min(MODE_MENU_MAX_WIDTH, Math.floor(roomRight)) };\n }\n return { ...common, left: MODE_MENU_GUTTER, right: MODE_MENU_GUTTER, width: 'auto', minWidth: 0, maxWidth: 'none' };\n}\n\nexport interface ContextModeControlProps {\n mode: VerseContextMode;\n option: VerseModelOption | null;\n disabled?: boolean;\n /** Why the control is disabled, for the tooltip. */\n disabledReason?: string | null;\n busy?: boolean;\n error?: string | null;\n onChange: (mode: VerseContextMode) => void;\n /**\n * Opens the \"Compact now\" panel. Passed only for sessions whose engine can\n * compact on request (`canCompactNow`); absent → no menu item.\n */\n onCompact?: () => void;\n /** Why \"Compact now…\" cannot run yet (no turns), shown on the disabled item. */\n compactUnavailableReason?: string | null;\n /**\n * False → this model has ONE budget (a 200k Claude model, a local seat): the\n * chip reads \"Context\" and its menu carries only \"Compact now…\", so a manual\n * compaction is reachable on demand on every engine that can do one, not\n * only from the handoff banner once it appears. Default true.\n */\n modesAvailable?: boolean;\n engine?: VerseEngine | null;\n /** The session's current reading and budget — the downgrade warning and the one-budget summary read it. */\n budget?: Pick<SessionContextBudget, 'contextTokens' | 'exact' | 'contextWindow' | 'autoCompactAt'> | null;\n}\n\n/**\n * The context chip beside the meter and its menu. With a real expansive\n * budget (or a session already in expansive, so it can always switch back)\n * it is the Standard / Expansive control; for a claude/local chat without\n * one it is a \"Context\" menu holding \"Compact now…\". Changing mode is an\n * explicit click — Verse never switches it on its own — and applies from the\n * NEXT turn: it changes CLI flags only, never prompt content, so the prompt\n * cache holds — EXCEPT a switch down to Standard while the chat is already\n * past Standard's compaction point, which compacts on the next turn and is\n * said so in the menu (standardSwitchCompacts).\n */\nexport function ContextModeControl({ mode, option, disabled = false, disabledReason = null, busy = false, error = null, onChange, onCompact,\n compactUnavailableReason = null, modesAvailable = true, engine = null, budget = null }: ContextModeControlProps) {\n const [open, setOpen] = useState(false);\n const [placement, setPlacement] = useState<CSSProperties | null>(null);\n const wrap = useRef<HTMLDivElement>(null);\n const button = useRef<HTMLButtonElement>(null);\n const menu = useRef<HTMLDivElement>(null);\n const menuId = useId();\n const expansiveAvailable = hasExpansiveMode(option);\n const downgrade = modesAvailable ? standardSwitchCompacts(option, mode, budget) : null;\n const who = engine ? COMPACTOR[engine] : 'The CLI';\n\n // An error keeps the menu open so the operator sees why nothing changed.\n useEffect(() => {\n if (error) setOpen(true);\n }, [error]);\n\n // A turn starting while the menu is open disables the chip (the engine\n // answers 409 to a mode change mid-turn); a menu left open under a disabled\n // chip would offer choices that can only fail.\n useEffect(() => {\n if (disabled) setOpen(false);\n }, [disabled]);\n\n useEffect(() => {\n if (!open) return;\n const onPointerDown = (event: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(event.target as Node)) setOpen(false);\n };\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n setOpen(false);\n button.current?.focus();\n }\n };\n document.addEventListener('pointerdown', onPointerDown);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('pointerdown', onPointerDown);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n // Placed before paint (no flash at the clipped spot) and again on resize.\n useLayoutEffect(() => {\n if (!open) {\n setPlacement(null);\n return undefined;\n }\n const place = () => {\n const rect = button.current?.getBoundingClientRect();\n setPlacement(rect ? modeMenuPlacement(rect, { width: window.innerWidth, height: window.innerHeight }) : null);\n };\n place();\n window.addEventListener('resize', place);\n return () => window.removeEventListener('resize', place);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n // The current mode first; a one-budget menu has no radios, so its first\n // enabled item; and when even that is disabled, the menu itself, so\n // focus never falls to <body>.\n const target = wrap.current?.querySelector<HTMLButtonElement>('[role=\"menuitemradio\"][aria-checked=\"true\"]')\n ?? wrap.current?.querySelector<HTMLButtonElement>('[role=\"menuitemradio\"]:not(:disabled), [role=\"menuitem\"]:not(:disabled)')\n ?? menu.current;\n target?.focus();\n }, [open]);\n\n function onMenuKey(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n const items = Array.from(wrap.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitemradio\"]:not(:disabled), [role=\"menuitem\"]:not(:disabled)') ?? []);\n if (items.length === 0) return;\n event.preventDefault();\n const index = items.indexOf(document.activeElement as HTMLButtonElement);\n const next = event.key === 'ArrowDown' ? (index + 1) % items.length : (index - 1 + items.length) % items.length;\n items[next]?.focus();\n }\n\n const choose = (next: VerseContextMode) => {\n if (next === mode || busy) return;\n // Close on the choice; the chip shows \"Switching…\" while the write is in\n // flight, and a failure re-opens the menu with the reason (effect above).\n setOpen(false);\n button.current?.focus();\n onChange(next);\n };\n\n const compact = () => {\n setOpen(false);\n // Focus goes back to the chip BEFORE the panel opens: the panel takes it\n // into its own field and, on close, hands it back to whatever held it —\n // the chip — instead of both ending on <body> with the unmounted item.\n button.current?.focus();\n onCompact?.();\n };\n\n // What changes on a mode switch, per CLI: Claude Code takes one flag\n // (--autocompact); codex takes a window AND a compaction limit.\n const flagChange = engine === 'codex'\n ? \"The CLI's window and compaction settings change\"\n : \"Only the CLI's compaction flag changes\";\n const oneBudgetPoint = budget?.autoCompactAt ?? null;\n const chipLabel = modesAvailable ? CONTEXT_MODE_LABEL[mode] : 'Context';\n const ariaLabel = modesAvailable ? `Context mode: ${CONTEXT_MODE_LABEL[mode]}` : 'Context actions';\n const chipTitle = disabled && disabledReason\n ? disabledReason\n : modesAvailable\n ? `Context mode: ${CONTEXT_MODE_LABEL[mode]} — ${modeDetail(option, mode)}`\n : 'Context: this model has one budget — open to compact the chat now';\n\n return (\n <div ref={wrap} className={styles.modeWrap}>\n <button ref={button} type=\"button\" className={styles.modeChip} data-mode={modesAvailable ? mode : undefined}\n aria-haspopup=\"menu\" aria-expanded={open} aria-controls={open ? menuId : undefined}\n aria-label={ariaLabel} disabled={disabled} title={chipTitle}\n onClick={() => setOpen((v) => !v)}>\n {busy ? 'Switching…' : chipLabel}\n </button>\n {open ? (\n <div ref={menu} id={menuId} role=\"menu\" aria-label={modesAvailable ? 'Context mode' : 'Context'} className={styles.modeMenu}\n style={placement ?? undefined} data-placement={placement ? 'fixed' : undefined} tabIndex={-1} onKeyDown={onMenuKey}>\n {modesAvailable ? (\n <>\n <p className={styles.modeMenuHeading}>How much context this chat may hold before the CLI compacts</p>\n {(['standard', 'expansive'] as const).map((m) => {\n const unavailable = m === 'expansive' && !expansiveAvailable;\n const compactsNext = m === 'standard' && downgrade !== null\n ? ` · ${downgrade.definite ? 'compacts' : 'may compact'} on the next turn`\n : '';\n return (\n <button key={m} type=\"button\" role=\"menuitemradio\" aria-checked={mode === m} className={styles.modeItem}\n disabled={unavailable || busy} onClick={() => choose(m)}>\n <span className={styles.modeItemPrimary}>{CONTEXT_MODE_LABEL[m]}{mode === m ? ' · current' : ''}</span>\n <span className={styles.modeItemSecondary}>\n {m === 'standard'\n ? `${modeDetail(option, 'standard')} · lower usage per turn, sharper recall${compactsNext}`\n : unavailable ? 'not available for this model on this seat' : `${modeDetail(option, 'expansive')} · the full native window`}\n </span>\n </button>\n );\n })}\n <p className={styles.modeMenuNote}>{expansiveCostCopy(option, engine)}</p>\n {downgrade ? (\n <>\n <p className={styles.modeMenuNote}>Applies from the next turn.</p>\n <p className={styles.modeMenuWarn} data-testid=\"standard-compacts\">\n {downgrade.definite\n ? `This chat holds ≈${formatTokens(downgrade.tokens)} — past Standard's ≈${formatTokens(downgrade.point)} compaction point, so switching to Standard makes ${who} compact on the next turn`\n : `This chat holds up to ≈${formatTokens(downgrade.tokens)}. If the real prompt is past Standard's ≈${formatTokens(downgrade.point)} compaction point, switching to Standard makes ${who} compact on the next turn`}\n {engine === 'local' ? '' : ': one summarization call that spends usage on this seat'}. The conversation is replaced by a summary and its prompt cache starts over.\n </p>\n </>\n ) : (\n <p className={styles.modeMenuNote}>\n Applies from the next turn. {flagChange} — the conversation and its prompt cache are kept.\n </p>\n )}\n </>\n ) : (\n <p className={styles.modeMenuHeading}>\n {oneBudgetPoint !== null\n ? `${who} compacts this chat on its own at ≈${formatTokens(oneBudgetPoint)}${budget?.contextWindow ? ` of ${formatContextWindow(budget.contextWindow)}` : ''}. This model has one context budget.`\n : 'This model has one context budget; the CLI compacts at its own point.'}\n </p>\n )}\n {onCompact ? (\n <button type=\"button\" role=\"menuitem\" className={`${styles.modeItem} ${modesAvailable ? styles.modeItemAction : ''}`}\n disabled={busy || compactUnavailableReason !== null} onClick={compact}>\n <span className={styles.modeItemPrimary}>Compact now…</span>\n <span className={styles.modeItemSecondary}>\n {compactUnavailableReason ?? 'summarize the conversation so far instead of waiting for the CLI to'}\n </span>\n </button>\n ) : null}\n {error ? <p role=\"alert\" className={styles.modeMenuError}>{error}</p> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Advice — \"continue in a fresh chat\" and \"expansive may help\"\n// ---------------------------------------------------------------------------\n\n/**\n * Re-evaluated on a clock because one reason (\"idle past the cache lifetime\")\n * becomes true with no event at all. A minute is well inside the 1h TTL.\n */\nconst ADVICE_TICK_MS = 60_000;\n\nfunction useNow(intervalMs: number, injected?: number): number {\n const [now, setNow] = useState(() => injected ?? Date.now());\n useEffect(() => {\n if (injected !== undefined) {\n setNow(injected);\n return undefined;\n }\n const id = setInterval(() => setNow(Date.now()), intervalMs);\n return () => clearInterval(id);\n }, [intervalMs, injected]);\n return now;\n}\n\n/**\n * The handoff verdict for a session, from the SAME budget the meter draws.\n *\n * `lastTurnAt` is the last time the chat talked to its provider (verse-store\n * `lastTurnActivityAt`), NOT `session.updatedAt`: a rename or a mode switch\n * bumps `updatedAt` without warming the provider's cache, and measuring idle\n * time from it silenced \"the prompt cache has likely expired\" for another\n * hour while the next turn still re-read everything uncached.\n */\nexport function sessionHandoffAdvice(\n session: Pick<VerseSession, 'usage' | 'compactionCount' | 'status'>,\n budget: SessionContextBudget,\n now: number,\n lastTurnAt: string | null = null,\n): VerseHandoffAdvice {\n return handoffAdvice({\n usage: {\n ...session.usage,\n contextTokens: budget.contextTokens,\n contextWindow: budget.contextWindow,\n autoCompactAt: budget.autoCompactAt,\n contextTokensExact: budget.exact,\n },\n compactionCount: session.compactionCount ?? 0,\n // A running turn is activity: the cache is being refreshed right now.\n lastActivityAt: session.status === 'running' ? null : lastTurnAt,\n now,\n });\n}\n\n/**\n * Whether ContextAdvice would show anything — the SAME rules, without\n * rendering. The notice slot (chat/NoticeSlot) needs to know before it picks\n * which one notice to show.\n */\nexport function contextAdviceVisible(\n session: VerseSession,\n budget: SessionContextBudget,\n modesAvailable: boolean,\n now: number,\n lastTurnAt: string | null = null,\n): boolean {\n const handoff = sessionHandoffAdvice(session, budget, now, lastTurnAt);\n const compactions = session.compactionCount ?? 0;\n if (handoff.level !== 'none' && !isVerseAdviceDismissed(session.id, `handoff:${handoff.level}:${compactions}`)) return true;\n if (!modesAvailable) return false;\n return expansiveAdvice({ session, option: budget.option }).suggest && !isVerseAdviceDismissed(session.id, `expansive:${compactions}`);\n}\n\nexport interface ContextAdviceProps {\n session: VerseSession;\n budget: SessionContextBudget;\n /** Whether the mode control exists for this session (the chip only offers what it can do). */\n modesAvailable: boolean;\n dispatchEnabled: boolean;\n modeBusy?: boolean;\n onHandoff: () => void;\n onSwitchExpansive: () => void;\n /** Opens the \"Compact now\" panel; passed only when `canCompactNow(engine)`. */\n onCompact?: () => void;\n /**\n * When the chat last talked to its provider (verse-store\n * `lastTurnActivityAt`); null/absent → no idle-cache reason.\n */\n lastTurnAt?: string | null;\n /** Injectable clock for tests. */\n now?: number;\n /** 3.10: rendered inside the notice slot, which owns the column and spacing. */\n embedded?: boolean;\n /** Called after \"Not now\" hides a note, so a parent that decides visibility re-checks. */\n onDismiss?: () => void;\n}\n\n/**\n * The banner row under the header. Two independent, dismissible notes:\n *\n * - HANDOFF (handoffAdvice ≥ suggest) — reasons listed in plain sentences,\n * and one action that opens the handoff dialog. Building the note is free;\n * nothing is spent until the operator presses Send in the new chat.\n * - EXPANSIVE (expansiveAdvice) — a suggestion chip with the cost stated.\n * Never an automatic switch.\n *\n * \"Not now\" is remembered per session for the page's lifetime (verse-ui-store,\n * not persisted: advice is evidence, and stale evidence must not outlive a\n * reload), and a note comes back when its evidence escalates.\n */\nexport function ContextAdvice({ session, budget, modesAvailable, dispatchEnabled, modeBusy = false, onHandoff, onSwitchExpansive, onCompact,\n lastTurnAt = null, now: injectedNow, embedded = false, onDismiss }: ContextAdviceProps) {\n const now = useNow(ADVICE_TICK_MS, injectedNow);\n const [, bump] = useState(0);\n const handoff = sessionHandoffAdvice(session, budget, now, lastTurnAt);\n const expansive = modesAvailable\n ? expansiveAdvice({ session, option: budget.option })\n : { suggest: false, reason: null };\n const compactions = session.compactionCount ?? 0;\n const handoffKey = `handoff:${handoff.level}:${compactions}`;\n const expansiveKey = `expansive:${compactions}`;\n const showHandoff = handoff.level !== 'none' && !isVerseAdviceDismissed(session.id, handoffKey);\n const showExpansive = expansive.suggest && !isVerseAdviceDismissed(session.id, expansiveKey);\n if (!showHandoff && !showExpansive) return null;\n\n const running = session.status === 'running';\n const dismiss = (key: string) => {\n dismissVerseAdvice(session.id, key);\n bump((n) => n + 1);\n onDismiss?.();\n };\n const idleExpired = lastTurnAt && !running ? now - Date.parse(lastTurnAt) >= CACHE_IDLE_TTL_MS : false;\n\n return (\n <div className={embedded ? styles.adviceStack : styles.advice}>\n {showHandoff ? (\n <section className={styles.adviceNote} data-level={handoff.level} aria-label=\"Context advice\">\n <div className={styles.adviceBody}>\n <p className={styles.adviceTitle}>\n {handoff.level === 'urge' ? 'Time to continue in a fresh chat' : 'Consider continuing in a fresh chat'}\n </p>\n <ul className={styles.adviceReasons}>\n {handoff.reasons.map((reason) => <li key={reason}>{reason}</li>)}\n </ul>\n <p className={styles.adviceFine}>\n Verse drafts a handoff note from this chat&apos;s log — free. Nothing is sent until you press Send in the new chat.\n {idleExpired ? ' Continuing here instead re-reads the whole context at full cost.' : ''}\n </p>\n </div>\n <div className={styles.adviceActions}>\n <button type=\"button\" className={styles.adviceAction} onClick={onHandoff} disabled={!dispatchEnabled || running}\n title={running ? 'Available when the current turn finishes' : !dispatchEnabled ? 'Sending is disabled on this server' : undefined}>\n Continue in a fresh chat…\n </button>\n {onCompact ? (\n // The in-place alternative: keep this chat, trade its early\n // detail for a summary now. Same guards as the handoff.\n <button type=\"button\" className={styles.ghost} onClick={onCompact} disabled={!dispatchEnabled || running || session.turnCount === 0}\n title={running ? 'Available when the current turn finishes' : !dispatchEnabled ? 'Sending is disabled on this server' : 'Summarize this chat in place instead'}>\n Compact now…\n </button>\n ) : null}\n <button type=\"button\" className={styles.ghost} onClick={() => dismiss(handoffKey)}>Not now</button>\n </div>\n </section>\n ) : null}\n {showExpansive ? (\n <section className={styles.adviceNote} data-level=\"suggest\" data-kind=\"expansive\" aria-label=\"Expansive mode suggestion\">\n <div className={styles.adviceBody}>\n <p className={styles.adviceTitle}>Expansive mode could help</p>\n <p className={styles.adviceFine}>{expansive.reason} {expansiveCostCopy(budget.option, session.engine)}</p>\n </div>\n <div className={styles.adviceActions}>\n {/* Disabled mid-turn: the engine answers 409 to a mode change while a turn runs. */}\n <button type=\"button\" className={styles.adviceAction} onClick={onSwitchExpansive} disabled={!dispatchEnabled || modeBusy || running}\n title={running ? 'Available when the current turn finishes' : undefined}>\n {modeBusy ? 'Switching…' : 'Switch to expansive'}\n </button>\n <button type=\"button\" className={styles.ghost} onClick={() => dismiss(expansiveKey)}>Not now</button>\n </div>\n </section>\n ) : null}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Compact now — the CLI's own /compact, sent as an ordinary turn\n// ---------------------------------------------------------------------------\n\n/**\n * Engines whose CLI compacts on request. Claude Code (which also drives the\n * local seats) accepts `/compact` as the text of a headless turn on a resumed\n * session and answers with a `compact_boundary` (trigger \"manual\") that the\n * adapter already turns into a compaction event — verified for real on a\n * local seat (build-U2 §2). Codex `exec` has no compact verb, and Grok's\n * headless `/compact` is unverified, so neither is offered: a button that\n * sends a literal \"/compact\" to a model that treats it as prose would spend a\n * turn and compact nothing.\n */\nexport function canCompactNow(engine: VerseEngine | null | undefined): boolean {\n return engine === 'claude' || engine === 'local';\n}\n\n/** Longest focus line accepted — a steer for the summary, not a second prompt. */\nexport const COMPACT_FOCUS_MAX = 500;\n\n/**\n * The turn text. The focus is folded to one line: the CLI reads a slash\n * command's arguments from the rest of its line, so a newline would silently\n * drop everything after it.\n */\nexport function compactCommand(focus: string): string {\n const line = focus.replace(/\\s+/g, ' ').trim().slice(0, COMPACT_FOCUS_MAX).trim();\n return line ? `/compact ${line}` : '/compact';\n}\n\n/** The cost sentence, per engine — the part of this panel that must not be vague. */\nexport function compactCostCopy(engine: VerseEngine, tokensLabel: string): string {\n return engine === 'local'\n ? `Free — it runs on the local model — but summarizing ${tokensLabel} tokens runs at that model's speed and can take minutes; the chat is busy until it finishes.`\n : `Spends usage on this seat: one summarization call that reads the whole current context (${tokensLabel} tokens) and writes the summary.`;\n}\n\nexport interface CompactPanelProps {\n engine: VerseEngine;\n budget: Pick<SessionContextBudget, 'contextTokens' | 'autoCompactAt' | 'exact'>;\n running: boolean;\n dispatchEnabled: boolean;\n /** No turn has run yet, so there is no resumable conversation to compact. */\n empty: boolean;\n /** Sends the text through the chat's normal send path; true when it was accepted. */\n onSend: (text: string) => Promise<boolean>;\n onClose: () => void;\n /**\n * Take keyboard focus into the focus field on mount (default true: the\n * panel only ever opens on a click or a menu choice). On unmount focus goes\n * back to whatever held it before — the chip or the banner button — when\n * nothing else has claimed it.\n */\n autoFocus?: boolean;\n}\n\n/**\n * The confirm step for a manual compaction, under the header strip beside the\n * advice notes. It exists because the action is not free on a paid seat and\n * not quick on a local one, and a one-click menu item would hide both.\n *\n * It sends through the SAME path as the composer (Workspace `onSend` →\n * ChatSection `send` → POST …/turns): the token gate, the running state, the\n * transcript entry and the engine's local-only chokepoint all apply exactly\n * as for a typed message. Nothing here is a new way to reach a model.\n */\nexport function CompactPanel({ engine, budget, running, dispatchEnabled, empty, onSend, onClose, autoFocus = true }: CompactPanelProps) {\n const [focus, setFocus] = useState('');\n const [sending, setSending] = useState(false);\n const focusId = useId();\n const input = useRef<HTMLInputElement>(null);\n\n // Opened from the mode menu, the focused menu item unmounts with the menu;\n // without this, keyboard focus fell to <body> and a screen reader heard\n // nothing about the panel that appeared.\n useEffect(() => {\n if (!autoFocus) return undefined;\n const previous = document.activeElement instanceof HTMLElement && document.activeElement !== document.body ? document.activeElement : null;\n input.current?.focus();\n return () => {\n const active = document.activeElement;\n const stranded = active === null || active === document.body || !active.isConnected;\n if (stranded && previous?.isConnected) previous.focus();\n };\n }, [autoFocus]);\n const who = COMPACTOR[engine];\n const tokensLabel = `${budget.exact ? '≈' : '≤'}${formatTokens(budget.contextTokens)}`;\n const blocked = running ? 'Available when the current turn finishes.'\n : !dispatchEnabled ? 'Sending is disabled: this server was started without dispatch.'\n : empty ? 'Nothing to compact yet — this chat has no turns.'\n : null;\n\n async function submit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n if (blocked || sending) return;\n setSending(true);\n try {\n if (await onSend(compactCommand(focus))) onClose();\n } finally {\n setSending(false);\n }\n }\n\n return (\n <section className={styles.adviceNote} data-level=\"suggest\" data-kind=\"compact\" aria-label=\"Compact this chat\">\n <form className={styles.compactForm} onSubmit={(event) => { void submit(event); }}>\n <div className={styles.adviceBody}>\n <p className={styles.adviceTitle}>Compact this chat now</p>\n <p className={styles.adviceFine}>\n {who} replaces the conversation so far with a summary it writes, then carries on from it — what it does on its own\n {budget.autoCompactAt !== null ? ` at ≈${formatTokens(budget.autoCompactAt)}` : ' at its compaction point'}, done now instead.\n Early detail then survives only as that summary.\n </p>\n <p className={styles.adviceFine}>{compactCostCopy(engine, tokensLabel)}</p>\n <label className={styles.compactLabel} htmlFor={focusId}>Keep in focus (optional)</label>\n <input ref={input} id={focusId} className={styles.compactInput} value={focus} maxLength={COMPACT_FOCUS_MAX}\n placeholder=\"e.g. the login fix and its open TODOs\" onChange={(event) => setFocus(event.target.value)} />\n <p className={styles.adviceFine}>\n Sent as the message <code>{compactCommand(focus)}</code>, so it shows in the transcript like any turn.\n </p>\n {blocked ? <p className={styles.adviceFine} role=\"status\">{blocked}</p> : null}\n </div>\n <div className={styles.adviceActions}>\n <button type=\"submit\" className={styles.adviceAction} disabled={blocked !== null || sending}>\n {sending ? 'Sending…' : 'Compact now'}\n </button>\n <button type=\"button\" className={styles.ghost} onClick={onClose}>Cancel</button>\n </div>\n </form>\n </section>\n );\n}\n","/**\n * routes/verse/composer/ContextRing.tsx — \"◔ 31%\" in the composer footer\n * (unit C3): how full this chat's context window is.\n *\n * It IS the header ring, not a lookalike: the percentage, the tone and the\n * compaction tick come from ContextMeter's `describeContext` (context-math\n * `occupancy()`, measured against the compaction point) and are drawn by the\n * same `ContextRingGlyph`, from the SAME budget (verse-model\n * `sessionContextBudget`), so the two can never disagree. Hover or focus\n * shows the reading in full — used / limit tokens and where the CLI\n * compacts. Unknown occupancy is a dashed ring and \"—\", never a zero.\n *\n * The ring is quantity (the fixed azure ramp); it takes the warning colour\n * only as the chat nears the compaction point, and says so in words.\n */\nimport { Tooltip } from '../../../components/primitives/Tooltip.js';\nimport type { VerseEngine } from '../../../data/api-types.js';\nimport { ContextRingGlyph, describeContext } from '../ContextMeter.js';\nimport styles from './composer.module.css';\n\nexport interface ContextRingProps {\n contextTokens: number | null;\n contextWindow: number | null;\n autoCompactAt: number | null;\n exact: boolean;\n /** Who compacts (\"Claude Code\", \"Codex\") — the same words the header uses. */\n engine?: VerseEngine | null;\n}\n\nexport function ContextRing({ contextTokens, contextWindow, autoCompactAt, exact, engine = null }: ContextRingProps) {\n const measured = contextTokens !== null;\n const d = describeContext({ contextTokens, contextWindow, autoCompactAt, exact, engine });\n const [used = '', limit = 'n/a'] = d.label.split(' / ');\n // The accessible name is the short reading; the tooltip (its description) the full one.\n const name = !measured\n ? 'Context — not measured yet'\n : d.percent === null\n ? `Context ${used} tokens — the window is unknown`\n : `Context ${d.percentLabel} — ${used} of ${limit} tokens${d.compactLabel ? `, ${d.compactLabel}` : ''}`;\n return (\n <Tooltip placement=\"top\" content={(\n <span className={styles.tip}>\n {measured ? d.summary.map((line) => <span key={line}>{line}</span>) : <span>Context — not measured yet</span>}\n </span>\n )}>\n <span className={styles.contextRing} role=\"img\" aria-label={name} tabIndex={0}\n data-tone={measured ? d.tone : 'unknown'} data-exact={exact ? undefined : 'false'}>\n <ContextRingGlyph fillPercent={measured ? d.fillPercent : 0} tickPercent={measured ? d.tickPercent : null}\n classes={{ track: styles.ringTrack, fill: styles.contextFill, tick: styles.contextTick }} />\n <span className={styles.contextText} aria-hidden=\"true\">{measured ? d.percentLabel : '—'}</span>\n </span>\n </Tooltip>\n );\n}\n","/**\n * routes/verse/composer/ControlMenu.tsx — one picker in the composer footer\n * (Permission ▾, Model ▾, Effort ▾; unit C3).\n *\n * An ARIA menu button whose items are `menuitemradio`s:\n * - ↑/↓ move (wrapping), Home/End jump, a letter jumps to the next item\n * starting with it, Enter/Space choose, Esc closes and returns focus to\n * the button, Tab closes and lets focus move on;\n * - an UNAVAILABLE option is shown, focusable and announced, but cannot be\n * chosen — its reason sits under its label (never a hover-only tooltip),\n * because \"why can't I pick Max?\" is exactly the question the menu must\n * answer;\n * - a DANGER option (bypass) is painted red and says so in words.\n *\n * `openRequest` lets the owner open it from a shortcut (⌘⇧M/⌘⇧I/⌘⇧E) or a\n * slash command: every new value opens the menu with focus on the checked item.\n *\n * `variant=\"list\"` renders the same items as a plain radio list, for the 375px\n * sheet where a popover over a popover would be unusable.\n *\n * The button shows a SHORT value (\"Accept edits\", \"Effort: High\") and never\n * ellipsizes it; the full value is its accessible name and its tooltip, with\n * the shortcut. Where the footer is too narrow it folds to the icon alone\n * (`iconOnly`) — the name and the tooltip still carry the words, and the\n * tooltip shows on keyboard focus as well as hover (a native `title` never\n * shows on focus, which left a sighted keyboard user with a bare glyph).\n *\n * Unmounted while open (the footer folded it into the ⋯ sheet, or the seat\n * lost the control), it reports itself closed, so the owner's \"a menu is\n * open\" state — which decides whether Esc stops the turn — never outlives it.\n */\nimport { useCallback, useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport type { VerseControlOption } from '../../../../core/verse/workbench-types.js';\nimport { Tooltip, type TooltipPlacement } from '../../../components/primitives/Tooltip.js';\nimport styles from './composer.module.css';\n\nexport interface ControlMenuProps<T extends string> {\n /** Accessible name of the picker (\"Permission mode\"). */\n label: string;\n /** What the button shows for the current value (short: \"Accept edits\", \"Effort: High\"). */\n valueLabel: string;\n /** The full current value, for the accessible name and tooltip (\"Bypass permissions\"). Default: `valueLabel`. */\n valueTitle?: string;\n options: readonly VerseControlOption<T>[];\n /** The checked option; null = none (e.g. effort at the CLI default). */\n value: T | null;\n onChange: (id: T) => void;\n /** An extra first item that resets to the default (effort \"Default\"). */\n defaultOption?: { label: string; description?: string; onSelect: () => void } | null;\n disabled?: boolean;\n /** Why the whole picker is disabled — shown as its tooltip. */\n disabledReason?: string | null;\n /** Shortcut hint shown in the menu header (\"⇧⌘M\"). */\n shortcut?: string;\n /** Bumped to open the menu programmatically. */\n openRequest?: number;\n /** A leading glyph for the button. */\n icon?: ReactNode;\n /** Paint the button as dangerous (bypass is on). */\n danger?: boolean;\n /** One sentence under the header: when a change applies. */\n note?: string | null;\n variant?: 'menu' | 'list';\n /** Collapse the button to its icon (the compact footer). */\n iconOnly?: boolean;\n /**\n * Where the name bubble sits. Default 'left' (flipping right at the edge):\n * BESIDE the button, never above it, because the menu opens upward and a\n * bubble above would cover its last items (see the note at the button).\n */\n tooltipPlacement?: TooltipPlacement;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function ControlMenu<T extends string>({\n label, valueLabel, valueTitle = valueLabel, options, value, onChange, defaultOption = null, disabled = false, disabledReason = null,\n shortcut, openRequest, icon, danger = false, note = null, variant = 'menu', iconOnly = false, tooltipPlacement = 'left', onOpenChange,\n}: ControlMenuProps<T>) {\n const [open, setOpen] = useState(false);\n const wrap = useRef<HTMLDivElement>(null);\n const button = useRef<HTMLButtonElement>(null);\n const menuId = useId();\n const seenRequest = useRef(openRequest);\n // Read by the unmount cleanup, which must see the LAST open state and owner callback.\n const openNow = useRef(open);\n openNow.current = open;\n const reportOpen = useRef(onOpenChange);\n reportOpen.current = onOpenChange;\n\n const setOpenState = useCallback((next: boolean) => {\n setOpen(next);\n onOpenChange?.(next);\n }, [onOpenChange]);\n\n // Unmounted while open (folded into the ⋯ sheet, or the control went away):\n // the menu is gone, so say so — else the owner's Esc-to-stop stays disarmed.\n useEffect(() => () => {\n if (openNow.current) reportOpen.current?.(false);\n }, []);\n\n const close = useCallback((refocus: boolean) => {\n setOpenState(false);\n if (refocus) button.current?.focus();\n }, [setOpenState]);\n\n // Programmatic open (shortcut / slash command).\n useEffect(() => {\n if (openRequest === undefined || openRequest === seenRequest.current) return;\n seenRequest.current = openRequest;\n if (!disabled && variant === 'menu') setOpenState(true);\n }, [openRequest, disabled, variant, setOpenState]);\n\n // Focus the checked item (else the first) when the menu opens.\n useEffect(() => {\n if (!open) return;\n const items = Array.from(wrap.current?.querySelectorAll<HTMLElement>('[role=\"menuitemradio\"]') ?? []);\n const checked = items.find((item) => item.getAttribute('aria-checked') === 'true');\n (checked ?? items[0])?.focus();\n }, [open]);\n\n // Outside click closes without stealing focus back.\n useEffect(() => {\n if (!open) return undefined;\n const onPointerDown = (event: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(event.target as Node)) close(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [open, close]);\n\n function choose(option: VerseControlOption<T>) {\n if (!option.available) return;\n if (variant === 'menu') close(true);\n onChange(option.id);\n }\n\n function onMenuKey(event: KeyboardEvent<HTMLDivElement>) {\n const items = Array.from(wrap.current?.querySelectorAll<HTMLElement>('[role=\"menuitemradio\"]') ?? []);\n if (items.length === 0) return;\n const index = items.indexOf(document.activeElement as HTMLElement);\n const focusAt = (i: number) => items[(i + items.length) % items.length]?.focus();\n switch (event.key) {\n case 'ArrowDown': event.preventDefault(); focusAt(index + 1); return;\n case 'ArrowUp': event.preventDefault(); focusAt(index - 1); return;\n case 'Home': event.preventDefault(); focusAt(0); return;\n case 'End': event.preventDefault(); focusAt(items.length - 1); return;\n case 'Escape':\n if (variant === 'menu') {\n event.preventDefault();\n // Handled here: the composer's own Escape (stop the turn) must not also fire.\n event.stopPropagation();\n close(true);\n }\n return;\n case 'Tab':\n if (variant === 'menu') close(false);\n return;\n default:\n if (event.key.length === 1 && /\\S/.test(event.key) && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const letter = event.key.toLowerCase();\n for (let step = 1; step <= items.length; step++) {\n const candidate = items[(index + step) % items.length]!;\n if ((candidate.dataset['label'] ?? '').toLowerCase().startsWith(letter)) {\n event.preventDefault();\n candidate.focus();\n return;\n }\n }\n }\n }\n }\n\n // In the sheet's plain lists, ONE item per list is in the tab order (the\n // checked one, else the first) and the arrows move within it — a roving\n // tabindex, so Tab walks list → list instead of through every option.\n const checkedId: string | null = value === null ? (defaultOption ? '__default' : null) : value;\n const tabStop = variant === 'list' ? (checkedId ?? (defaultOption ? '__default' : options[0]?.id ?? null)) : null;\n const items = (\n <>\n {defaultOption ? (\n <MenuItem label={defaultOption.label} description={defaultOption.description ?? null} checked={value === null}\n available danger={false} tabbable={tabStop === '__default'}\n onSelect={() => { if (variant === 'menu') close(true); defaultOption.onSelect(); }} />\n ) : null}\n {options.map((option) => (\n <MenuItem key={option.id} label={option.label} description={option.available ? (option.danger ? 'Skips every permission check — confirmed for this chat only' : null) : (option.reason ?? 'Unavailable on this seat')}\n checked={value === option.id} available={option.available} danger={option.danger === true}\n tabbable={tabStop === option.id}\n onSelect={() => choose(option)} />\n ))}\n </>\n );\n\n if (variant === 'list') {\n return (\n <div ref={wrap} className={styles.controlList} role=\"menu\" aria-label={label} onKeyDown={onMenuKey}>\n {/* No per-list note: the sheet says once, above all three, when changes apply. */}\n <p className={styles.menuHeading}>{label}</p>\n {items}\n </div>\n );\n }\n\n return (\n <div ref={wrap} className={styles.controlWrap}>\n {/* The Tooltip primitive, not a native title: a title shows on hover\n only, and folded to its icon this button's words live nowhere else a\n sighted keyboard user can see. The bubble sits BESIDE the button\n (`tooltipPlacement`), never above it: WebKit does not focus a clicked\n button, so a bubble opened by hover sees no blur when the menu opens,\n and above the button it would cover the menu, which opens upward.\n The wrapper is permanent (only its label changes), so folding never\n remounts the button under focus. */}\n <Tooltip label={disabled && disabledReason ? disabledReason : `${label}: ${valueTitle}`}\n shortcut={disabled ? undefined : shortcut} placement={tooltipPlacement}>\n <button ref={button} type=\"button\" className={`${styles.control} ${danger ? styles.controlDanger : ''} ${iconOnly ? styles.controlIconOnly : ''}`}\n aria-haspopup=\"menu\" aria-expanded={open} aria-controls={open ? menuId : undefined}\n aria-label={`${label}: ${valueTitle}`} disabled={disabled}\n onClick={() => setOpenState(!open)}\n onKeyDown={(event) => {\n if ((event.key === 'ArrowUp' || event.key === 'ArrowDown') && !open) {\n event.preventDefault();\n setOpenState(true);\n }\n }}>\n {icon ? <span className={styles.controlIcon} aria-hidden=\"true\">{icon}</span> : null}\n {iconOnly ? null : <span className={styles.controlText}>{valueLabel}</span>}\n <span className={styles.caret} aria-hidden=\"true\" />\n </button>\n </Tooltip>\n {open ? (\n <div id={menuId} role=\"menu\" aria-label={label} className={styles.menu} onKeyDown={onMenuKey}>\n <p className={styles.menuHeading}>\n <span>{label}</span>\n {shortcut ? <kbd className={styles.menuKbd}>{shortcut}</kbd> : null}\n </p>\n {note ? <p className={styles.menuNote}>{note}</p> : null}\n {items}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface MenuItemProps {\n /** In the tab order (the list variant's roving tab stop). */\n tabbable?: boolean;\n label: string;\n description: string | null;\n checked: boolean;\n available: boolean;\n danger: boolean;\n onSelect: () => void;\n}\n\nfunction MenuItem({ tabbable = false, label, description, checked, available, danger, onSelect }: MenuItemProps) {\n const descId = useId();\n return (\n <div role=\"menuitemradio\" tabIndex={tabbable ? 0 : -1} aria-checked={checked} aria-disabled={available ? undefined : true}\n aria-describedby={description ? descId : undefined} data-label={label}\n className={`${styles.menuItem} ${danger ? styles.menuItemDanger : ''}`}\n onClick={() => { if (available) onSelect(); }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n if (available) onSelect();\n }\n }}>\n <span className={styles.menuCheck} aria-hidden=\"true\">{checked ? '✓' : ''}</span>\n <span className={styles.menuText}>\n <span className={styles.menuLabel}>{label}</span>\n {description ? <span id={descId} className={styles.menuDesc}>{description}</span> : null}\n </span>\n </div>\n );\n}\n","/**\n * routes/verse/composer/ControlsSheet.tsx — the 375px composer's \"⋯\": the\n * Permission, Model and Effort pickers in one bottom sheet (SPEC-310C §2 \"At\n * 375px\"; unit C3).\n *\n * A phone footer holds [+] [mic], the seat, ⋯ and Send. A popover per picker\n * would open over the keyboard and off the screen edge, so the three become\n * radio lists in a sheet that rises from the bottom. It traps focus while\n * open, Esc and the backdrop close it, and focus returns to ⋯.\n *\n * Below the pickers: \"Run in cloud\" (3.11 cloud lane) — launches the typed\n * message as a cloud task. It is an import() loaded the first time the sheet\n * opens, so nothing cloud-related is on the chat's first-paint path.\n */\nimport { lazy, Suspense, useId, useRef, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useFocusTrap } from '../../../components/primitives/focus-trap.js';\nimport styles from './composer.module.css';\n\nconst RunInCloudAction = lazy(() => import('../cloud/RunInCloudAction.js').then((m) => ({ default: m.RunInCloudAction })));\n\nexport interface ControlsSheetProps {\n open: boolean;\n onClose: () => void;\n children: ReactNode;\n}\n\nexport function ControlsSheet({ open, onClose, children }: ControlsSheetProps) {\n const panel = useRef<HTMLDivElement>(null);\n const close = useRef<HTMLButtonElement>(null);\n const titleId = useId();\n useFocusTrap({ open, containerRef: panel, onClose, initialFocusRef: close });\n if (!open) return null;\n return createPortal(\n <div className={styles.sheetBackdrop} onMouseDown={(event) => { if (event.target === event.currentTarget) onClose(); }}>\n <div ref={panel} className={styles.sheet} role=\"dialog\" aria-modal=\"true\" aria-labelledby={titleId} tabIndex={-1}>\n <div className={styles.sheetHeader}>\n <h2 id={titleId} className={styles.sheetTitle}>Chat settings</h2>\n <button ref={close} type=\"button\" className={styles.sheetClose} onClick={onClose}>Done</button>\n </div>\n <p className={styles.sheetNote}>Changes apply from the next turn.</p>\n <div className={styles.sheetBody}>\n {children}\n <Suspense fallback={null}>\n <RunInCloudAction />\n </Suspense>\n </div>\n </div>\n </div>,\n document.body,\n );\n}\n","/**\n * routes/verse/composer/QueueRow.tsx — \"Queued, sends when this turn ends\n * [Edit] [Send now] [×]\" above the composer (SPEC-310C §2; unit C3).\n *\n * Hidden when empty. Two states, both said in words:\n * - waiting: the follow-ups send one by one as each turn ends cleanly;\n * - HELD: the last turn failed or was stopped, so nothing is sent blind —\n * the row says why and [Send now] is the way on.\n *\n * Edit pulls the text back into the composer (and off the queue), so a\n * follow-up can be reworded after reading the reply it was waiting for.\n */\nimport { IconX } from '../../../components/primitives/icons.js';\nimport type { VerseQueueResponse } from '../../../../core/verse/workbench-types.js';\nimport styles from './composer.module.css';\n\nexport interface QueueRowProps {\n queue: VerseQueueResponse | null;\n running: boolean;\n disabled?: boolean;\n onEdit: (queueId: string, text: string) => void;\n onSendNow: (queueId: string) => void;\n onRemove: (queueId: string) => void;\n}\n\n/** One line of the queued text, trimmed for the row (the full text is its title). */\nfunction preview(text: string): string {\n const line = text.replace(/\\s+/g, ' ').trim();\n return line.length > 140 ? `${line.slice(0, 139)}…` : line;\n}\n\nexport function QueueRow({ queue, running, disabled = false, onEdit, onSendNow, onRemove }: QueueRowProps) {\n if (!queue || queue.items.length === 0) return null;\n const held = queue.held;\n const count = queue.items.length;\n const heading = held\n ? `Held — ${queue.heldReason ?? 'the last turn did not finish cleanly.'}`\n : running\n ? `Queued · sends when this turn ends`\n : `Queued · sending next`;\n return (\n <section className={`${styles.queue} ${held ? styles.queueHeld : ''}`} aria-label={`${count} queued follow-up${count === 1 ? '' : 's'}`}>\n <p className={styles.queueHeading} role=\"status\">{heading}</p>\n <ol className={styles.queueList}>\n {queue.items.map((item, index) => (\n <li key={item.id} className={styles.queueItem}>\n <span className={styles.queueIndex} aria-hidden=\"true\">{index + 1}</span>\n <span className={styles.queueText} title={item.text}>{preview(item.text)}</span>\n <span className={styles.queueActions}>\n <button type=\"button\" className={styles.queueButton} disabled={disabled}\n aria-label={`Edit queued message ${index + 1}`} onClick={() => onEdit(item.id, item.text)}>Edit</button>\n <button type=\"button\" className={`${styles.queueButton} ${styles.queueButtonPrimary}`} disabled={disabled}\n aria-label={running ? `Stop the turn and send queued message ${index + 1} now` : `Send queued message ${index + 1} now`}\n onClick={() => onSendNow(item.id)}>Send now</button>\n <button type=\"button\" className={styles.queueRemove} disabled={disabled}\n aria-label={`Remove queued message ${index + 1}`} onClick={() => onRemove(item.id)}>\n <IconX width={12} height={12} aria-hidden=\"true\" />\n </button>\n </span>\n </li>\n ))}\n </ol>\n </section>\n );\n}\n","/**\n * routes/verse/composer/SeatChip.tsx — the seat this chat runs on, in the\n * composer footer (SPEC-310C §2 \"Seat chip\"; unit C3).\n *\n * [C] Claude Max ◔ [L] Local\n *\n * - The chip names the SEAT (the account: \"Claude Max\", or \"Local\" for a\n * local model, whose seat label is the model's own name); the model sits\n * beside it in the Model picker, so the footer never says the model twice.\n * - The monogram tile (C/X/G/L, never a vendor logo) carries the engine's\n * identity tick; the ring is the SHORT window's use (5-hour where the\n * provider reports one, else the binding window), drawn in the quantity\n * ramp — never the accent — and switched to the status colours only past\n * the tight / limit points, always with words in the tooltip. A seat with\n * no reading (and every local seat: no limits) draws NO ring — an empty\n * circle said nothing and read as a stuck spinner.\n * - Hover or focus shows plan, every window with its reset, health, and what\n * autonomy may take from this seat (\"autonomy may use 60% · 40% reserved\"),\n * read from the Budget route (A9) only while the tooltip is open.\n * - Click opens the menu: \"Continue on ‹seat›\" (a prefilled handoff when the\n * owner wires it; a new chat otherwise), a new chat on this seat, and\n * Budget mode ▸ (A9's BudgetControl, loaded on demand).\n *\n * A chat is bound to its seat; nothing here mutates the chat's seat.\n */\nimport { lazy, Suspense, useEffect, useId, useRef, useState, type KeyboardEvent } from 'react';\nimport { Dialog } from '../../../components/primitives/Dialog.js';\nimport { useQuery } from '../../../data/hooks.js';\nimport type { VerseSeat } from '../../../data/api-types.js';\nimport type { VerseSeatWindow } from '../../../../core/verse/types.js';\nimport type { BudgetView } from '../../../../core/routing/policy.js';\nimport { budgetQuery } from '../budget/budget-queries.js';\nimport { useSeatHealth } from '../health/useSeatHealth.js';\nimport { capacityRowFor, windowSentence } from '../usage/capacity-strip-model.js';\nimport type { SeatChoice } from '../SeatSelector.js';\nimport { firstRunnableModel, seatCapacity, SEAT_CAPACITY_WORD, seatWindowLabel } from '../verse-model.js';\nimport styles from './composer.module.css';\nimport { usedPercentText } from '../percent-text.js';\nimport { ProviderLogo } from '../../../components/primitives/ProviderLogo.js';\n\nconst BudgetControl = lazy(async () => ({ default: (await import('../budget/BudgetControl.js')).BudgetControl }));\n\nexport interface SeatChipProps {\n seats: readonly VerseSeat[];\n seat: SeatChoice;\n engine: VerseSeat['engine'];\n /** The chat's full seat label (\"Claude Max · Opus 5.5\") — the tooltip and menu heading. */\n label: string;\n /** What the chip itself says (\"Claude Max\", \"Local\"). Default: `label`. */\n name?: string;\n disabled?: boolean;\n /** Collapse to monogram + ring (the 375 footer). */\n compact?: boolean;\n /** \"Continue on ‹seat›\": a handoff prefilled for that seat. Falls back to onNewChat. */\n onContinueOn?: (choice: SeatChoice) => void;\n onNewChat: (choice: SeatChoice) => void;\n}\n\n/**\n * Every window the seat reported: the capacity record's when the server built\n * one, else the older `health.windows` (same ids and honesty rules, fewer\n * fields). Never invented: a seat with neither has no windows.\n */\nexport function seatWindows(seat: VerseSeat | undefined): VerseSeatWindow[] {\n const fromCapacity = seat?.capacity?.windows ?? [];\n if (fromCapacity.length > 0) return fromCapacity;\n return (seat?.health.windows ?? []).map((w) => ({\n id: w.id,\n usedPercent: typeof w.usedPercent === 'number' && Number.isFinite(w.usedPercent) ? w.usedPercent : null,\n resetsAt: w.resetsAt,\n resetDescription: null,\n limitReached: false,\n measured: typeof w.usedPercent === 'number',\n }));\n}\n\n/** The window the ring draws: the 5-hour one when reported, else the fullest one. */\nexport function ringWindow(seat: VerseSeat | undefined): VerseSeatWindow | null {\n const windows = seatWindows(seat).filter((w) => w.usedPercent !== null || w.limitReached);\n const short = windows.find((w) => w.id === 'five_hour');\n if (short) return short;\n if (seat?.capacity?.binding) return seat.capacity.binding;\n return windows.reduce<VerseSeatWindow | null>((top, w) => (top === null || (w.usedPercent ?? 100) > (top.usedPercent ?? 100) ? w : top), null);\n}\n\nfunction ringTone(reading: VerseSeatWindow | null): 'unknown' | 'ok' | 'tight' | 'limit' {\n if (reading?.limitReached) return 'limit';\n if (!reading || reading.usedPercent === null) return 'unknown';\n if (reading.usedPercent >= 100) return 'limit';\n if (reading.usedPercent >= 85) return 'tight';\n return 'ok';\n}\n\n/**\n * Whether the chip draws its capacity ring: a seat with limits and a reading.\n * The ring takes room in the footer (14px + a gap) and arrives with a later\n * roster poll, so the composer folds on this as well as on the words.\n */\nexport function capacityRingShown(seat: VerseSeat | undefined, engine: VerseSeat['engine']): boolean {\n return engine !== 'local' && ringTone(ringWindow(seat)) !== 'unknown';\n}\n\nexport function CapacityRing({ window: reading, size = 14 }: { window: VerseSeatWindow | null; size?: number }) {\n const r = (size - 3) / 2;\n const c = 2 * Math.PI * r;\n // A spent window is a full ring even when the provider gave no percentage.\n const pct = reading?.limitReached ? 100 : reading?.usedPercent ?? null;\n const shown = pct === null ? 0 : Math.max(0, Math.min(100, pct));\n return (\n <svg className={styles.ring} data-tone={ringTone(reading)} width={size} height={size} viewBox={`0 0 ${size} ${size}`} aria-hidden=\"true\">\n <circle className={styles.ringTrack} cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth=\"2\" />\n {pct === null ? null : (\n <circle className={styles.ringFill} cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth=\"2\"\n strokeDasharray={`${(shown / 100) * c} ${c}`} transform={`rotate(-90 ${size / 2} ${size / 2})`} strokeLinecap=\"round\" />\n )}\n </svg>\n );\n}\n\nfunction windowLine(reading: VerseSeatWindow): string {\n const label = seatWindowLabel(reading.id);\n const used = reading.limitReached ? 'limit reached' : reading.usedPercent === null ? 'no reading' : `${usedPercentText(reading.usedPercent)} used`;\n const reset = reading.resetDescription ?? (reading.resetsAt ? `resets ${new Date(reading.resetsAt).toLocaleString(undefined, { weekday: 'short', hour: 'numeric', minute: '2-digit' })}` : null);\n return `${label}: ${used}${reset ? ` · ${reset}` : ''}`;\n}\n\n/** \"Autonomy may use 60% · 40% reserved for you\", from the budget route's effective policy. */\nfunction budgetSentence(budget: BudgetView | null, seatId: string, status: string): { text: string; muted: boolean } {\n const policy = budget?.effective?.[seatId];\n if (!policy) return { text: status === 'error' ? 'Budget: no reading' : 'Budget: loading…', muted: true };\n if (!policy.enabled) return { text: 'Autonomy never uses this seat — it is all yours.', muted: false };\n const reserve = Math.max(0, Math.min(100, Math.round(policy.reservePercent)));\n return { text: `Autonomy may use ${100 - reserve}% · ${reserve}% reserved for you`, muted: false };\n}\n\n/**\n * The detail bubble. Its facts come from THE shared capacity row\n * (usage/capacity-strip-model `capacityRowFor`, C6) — the same projection\n * Apps & Accounts, Usage and the new-chat dialog draw — so the chip can never\n * describe a seat differently from them. Health (A2) and budget (A9) are the\n * app-wide cached reads, fetched only while this bubble is open.\n */\nfunction SeatTooltip({ seats, seat, label }: { seats: readonly VerseSeat[]; seat: VerseSeat | undefined; label: string }) {\n const budget = useQuery(budgetQuery);\n const health = useSeatHealth();\n const row = seat ? capacityRowFor(seats, seat.id, { health: health.data?.seats ?? null, budget: budget.data ?? null }) : null;\n // The shared row's windows (label, %, verbatim reset, the reserve on the\n // binding one); before the roster has a row, the seat record's own.\n const windows: Array<{ id: string; line: string }> = row\n ? row.windows.map((w) => ({ id: w.id, line: windowSentence({ label: '' }, w, w.binding ? row.reserve?.percent ?? null : null).trimStart() }))\n : seatWindows(seat).map((w) => ({ id: w.id, line: windowLine(w) }));\n const budgetLine = seat && seat.engine !== 'local' ? budgetSentence(budget.data ?? null, seat.id, budget.status) : null;\n return (\n <span className={styles.tip}>\n <strong>{label}</strong>\n {row?.plan ?? seat?.capacity?.planType ? <span>Plan: {row?.plan ?? seat?.capacity?.planType}</span> : null}\n {windows.length > 0\n ? windows.map((w) => <span key={w.id}>{w.line}</span>)\n : <span className={styles.tipMuted}>No capacity reading yet</span>}\n {row ? (\n <span>\n Health: {row.word}\n {row.connection && row.connection.connection !== 'connected' && row.connection.connection !== 'unknown' ? ` · ${row.connection.word}` : ''}\n {row.summary ? ` — ${row.summary}` : ''}\n </span>\n ) : null}\n {budgetLine ? <span className={budgetLine.muted ? styles.tipMuted : undefined}>{budgetLine.text}</span> : null}\n {seat && seat.engine === 'local' ? <span>Local — no usage limits</span> : null}\n </span>\n );\n}\n\nexport function SeatChip({ seats, seat, engine, label, name = label, disabled = false, compact = false, onContinueOn, onNewChat }: SeatChipProps) {\n const [open, setOpen] = useState(false);\n const [budgetOpen, setBudgetOpen] = useState(false);\n // The detail bubble is this component's own (not the Tooltip primitive):\n // it must hide while the menu is open, and toggling the primitive's\n // `disabled` swaps its wrapper and remounts the button under the pointer.\n const [tipOpen, setTipOpen] = useState(false);\n const tipTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const tipId = useId();\n const wrap = useRef<HTMLDivElement>(null);\n const button = useRef<HTMLButtonElement>(null);\n const menuId = useId();\n const budgetTitleId = useId();\n const current = seats.find((s) => s.id === seat.seatId);\n const ring = engine === 'local' ? null : ringWindow(current);\n const others = seats.filter((s) => s.id !== seat.seatId && s.health.state !== 'unavailable' && firstRunnableModel(s) !== null);\n const ringText = ring && (ring.usedPercent !== null || ring.limitReached)\n ? `${seatWindowLabel(ring.id)} ${ring.limitReached ? 'limit reached' : `${usedPercentText(ring.usedPercent ?? 0)} used`}`\n : engine === 'local' ? 'no usage limits' : 'no capacity reading';\n\n useEffect(() => () => { if (tipTimer.current) clearTimeout(tipTimer.current); }, []);\n\n function showTip(delayed: boolean) {\n if (tipTimer.current) clearTimeout(tipTimer.current);\n if (!delayed) {\n setTipOpen(true);\n return;\n }\n tipTimer.current = setTimeout(() => setTipOpen(true), 200);\n }\n\n function hideTip() {\n if (tipTimer.current) clearTimeout(tipTimer.current);\n tipTimer.current = null;\n setTipOpen(false);\n }\n\n useEffect(() => {\n if (!open) return undefined;\n const onPointerDown = (event: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(event.target as Node)) setOpen(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n wrap.current?.querySelector<HTMLElement>('[role=\"menuitem\"]')?.focus();\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [open]);\n\n function onMenuKey(event: KeyboardEvent<HTMLDivElement>) {\n const items = Array.from(wrap.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []);\n const index = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n setOpen(false);\n button.current?.focus();\n } else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n const next = event.key === 'ArrowDown' ? index + 1 : index - 1;\n items[(next + items.length) % items.length]?.focus();\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n (event.key === 'Home' ? items[0] : items[items.length - 1])?.focus();\n } else if (event.key === 'Tab') {\n setOpen(false);\n }\n }\n\n function pick(action: () => void) {\n setOpen(false);\n action();\n }\n\n return (\n <div ref={wrap} className={`${styles.seatChipWrap} ${styles[`engine-${engine}`] ?? ''}`}>\n <button ref={button} type=\"button\" className={`${styles.seatChip} ${compact ? styles.seatChipCompact : ''}`}\n aria-haspopup=\"menu\" aria-expanded={open} aria-controls={open ? menuId : undefined} disabled={disabled}\n aria-label={`Seat: ${name}, ${ringText}`}\n aria-describedby={tipOpen && !open ? tipId : undefined}\n onMouseEnter={() => showTip(true)} onMouseLeave={hideTip}\n onFocus={(event) => { if (event.currentTarget.matches(':focus-visible')) showTip(false); }} onBlur={hideTip}\n onKeyDown={(event) => { if (event.key === 'Escape' && tipOpen && !open) { event.stopPropagation(); hideTip(); } }}\n onClick={() => { hideTip(); setOpen((v) => !v); }}>\n <span className={styles.monogram} aria-hidden=\"true\"><ProviderLogo engine={engine} size={13} /></span>\n {compact ? null : <span className={styles.seatChipText}>{name}</span>}\n {capacityRingShown(current, engine) ? <CapacityRing window={ring} /> : null}\n </button>\n {tipOpen && !open ? (\n <span id={tipId} role=\"tooltip\" className={styles.seatTip}>\n <SeatTooltip seats={seats} seat={current} label={label} />\n </span>\n ) : null}\n {open ? (\n <div id={menuId} role=\"menu\" aria-label=\"Seat\" className={`${styles.menu} ${styles.menuWide}`} onKeyDown={onMenuKey}>\n <p className={styles.menuHeading}><span>This chat runs on <strong>{label}</strong></span></p>\n {others.map((s) => {\n const model = firstRunnableModel(s)!;\n const cap = seatCapacity(s);\n return (\n <button key={s.id} type=\"button\" role=\"menuitem\" className={`${styles.menuItem} ${styles[`engine-${s.engine}`] ?? ''}`}\n data-capacity={cap.cls}\n onClick={() => pick(() => (onContinueOn ?? onNewChat)({ seatId: s.id, model: model.id }))}>\n <span className={styles.monogram} aria-hidden=\"true\"><ProviderLogo engine={s.engine} size={13} /></span>\n <span className={styles.menuText}>\n <span className={styles.menuLabel}>{onContinueOn ? 'Continue on' : 'New chat on'} {s.label}</span>\n <span className={styles.menuDesc}>{model.label}{cap.cls === 'unread' ? '' : ` · ${SEAT_CAPACITY_WORD[cap.cls]}, ${cap.text}`}</span>\n </span>\n </button>\n );\n })}\n {others.length === 0 ? <p className={styles.menuNote}>No other seat can take this chat right now.</p> : null}\n <div className={styles.menuSeparator} role=\"separator\" />\n <button type=\"button\" role=\"menuitem\" className={styles.menuItem}\n onClick={() => pick(() => onNewChat({ seatId: seat.seatId, model: seat.model }))}>\n <span className={styles.menuCheck} aria-hidden=\"true\" />\n <span className={styles.menuText}><span className={styles.menuLabel}>New chat on this seat</span></span>\n </button>\n {engine !== 'local' ? (\n <button type=\"button\" role=\"menuitem\" className={styles.menuItem} onClick={() => pick(() => setBudgetOpen(true))}>\n <span className={styles.menuCheck} aria-hidden=\"true\" />\n <span className={styles.menuText}>\n <span className={styles.menuLabel}>Budget mode…</span>\n <span className={styles.menuDesc}>How much of this seat autonomy may use</span>\n </span>\n </button>\n ) : null}\n </div>\n ) : null}\n <Dialog open={budgetOpen} onClose={() => setBudgetOpen(false)} titleId={budgetTitleId} title=\"Budget mode\" widthClassName={styles.budgetDialog}\n description=\"What autonomy may spend on each seat, and what stays reserved for your own chats.\">\n {budgetOpen ? (\n <Suspense fallback={<p className={styles.menuNote}>Loading budget…</p>}>\n <BudgetControl />\n </Suspense>\n ) : null}\n </Dialog>\n </div>\n );\n}\n","/**\n * routes/verse/composer/SuggestMenu.tsx — the listbox the `@` file finder and\n * the `/` commands open above the message box (unit C3).\n *\n * Focus NEVER leaves the textarea: it owns the keys (↑/↓ move, Enter/Tab\n * accept, Esc closes — see Composer's onKeyDown) and points at the active\n * option with `aria-activedescendant`, the combobox pattern, so typing keeps\n * narrowing the list. A pointer can pick too; mousedown is prevented so the\n * click does not blur the textarea first.\n */\nimport type { ReactNode } from 'react';\nimport styles from './composer.module.css';\n\nexport interface SuggestItem {\n id: string;\n primary: ReactNode;\n secondary?: ReactNode;\n /** Shown disabled with this reason (a command this chat cannot run). */\n disabledReason?: string | null;\n}\n\nexport interface SuggestMenuProps {\n id: string;\n label: string;\n items: readonly SuggestItem[];\n activeIndex: number;\n /** Shown instead of the list: \"Searching…\", \"No files match\". */\n status?: string | null;\n onPick: (index: number) => void;\n onHover: (index: number) => void;\n}\n\nexport function suggestOptionId(menuId: string, index: number): string {\n return `${menuId}-opt-${index}`;\n}\n\nexport function SuggestMenu({ id, label, items, activeIndex, status = null, onPick, onHover }: SuggestMenuProps) {\n return (\n <div className={styles.suggest}>\n <p className={styles.suggestHeading}>{label}</p>\n {items.length === 0 ? (\n <p className={styles.suggestStatus} role=\"status\">{status ?? 'Nothing matches'}</p>\n ) : (\n <ul id={id} role=\"listbox\" aria-label={label} className={styles.suggestList}>\n {items.map((item, index) => (\n <li key={item.id} id={suggestOptionId(id, index)} role=\"option\" aria-selected={index === activeIndex}\n aria-disabled={item.disabledReason ? true : undefined}\n className={styles.suggestItem}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => onHover(index)}\n onClick={() => onPick(index)}>\n <span className={styles.suggestPrimary}>{item.primary}</span>\n {item.disabledReason ? <span className={styles.suggestSecondary}>{item.disabledReason}</span>\n : item.secondary ? <span className={styles.suggestSecondary}>{item.secondary}</span> : null}\n </li>\n ))}\n </ul>\n )}\n {items.length > 0 && status ? <p className={styles.suggestStatus} role=\"status\">{status}</p> : null}\n </div>\n );\n}\n","/**\n * routes/verse/composer/useComposerData.ts — the composer's three live\n * resources, one hook each (unit C3):\n *\n * useSessionControls model / effort / permission for this chat + options\n * useFollowUpQueue the server-side queue of follow-ups\n * useAttachmentDrafts files attached to the message being written\n *\n * None of them polls. Controls and the queue are re-read when the chat's\n * turn starts or ends (`running` flips) — the only moments the server changes\n * them on its own — and after each of the composer's own writes. A chat that\n * is not open costs nothing.\n *\n * Every write goes through the caller's token gate (`run`): with no mutation\n * token held, the gate asks for one and resolves null if dismissed, so a\n * refused unlock reads as \"not done\", never as an error.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type {\n VerseAttachment,\n VerseQueueResponse,\n VerseSessionControlsResponse,\n VerseSessionControlsUpdate,\n} from '../../../../core/verse/workbench-types.js';\nimport { VERSE_ATTACHMENT_MAX_BYTES } from '../../../../core/verse/workbench-types.js';\nimport { describeContextError } from '../context/use-token-gate.js';\nimport {\n deleteAttachment,\n enqueueFollowUp,\n fetchQueue,\n fetchSessionControls,\n removeFollowUp,\n sendFollowUpNow,\n updateSessionControls,\n uploadAttachment,\n type VerseQueueSendResult,\n} from './composer-queries.js';\nimport { formatBytes, readFileAsBase64 } from './composer-text.js';\n\nexport type GateRun = <T>(reason: string, action: () => Promise<T>) => Promise<T | null>;\n\n// ---------------------------------------------------------------------------\n// Controls\n// ---------------------------------------------------------------------------\n\nexport interface SessionControlsState {\n view: VerseSessionControlsResponse | null;\n /** True until the first read answered (or failed). */\n loading: boolean;\n /** Why the last read or write failed, in the server's words. */\n error: string | null;\n pending: boolean;\n update: (update: VerseSessionControlsUpdate, reason: string) => Promise<boolean>;\n}\n\nexport function useSessionControls(sessionId: string | null, running: boolean, run: GateRun): SessionControlsState {\n const [view, setView] = useState<VerseSessionControlsResponse | null>(null);\n const [loading, setLoading] = useState(sessionId !== null);\n const [error, setError] = useState<string | null>(null);\n const [pending, setPending] = useState(false);\n\n useEffect(() => {\n if (!sessionId) {\n setView(null);\n setLoading(false);\n return undefined;\n }\n const abort = new AbortController();\n fetchSessionControls(sessionId, abort.signal)\n .then((next) => { setView(next); setError(null); })\n .catch((err: unknown) => {\n if (abort.signal.aborted) return;\n // An older server (no route) leaves the pickers hidden rather than broken.\n setView(null);\n setError(describeContextError(err));\n })\n .finally(() => { if (!abort.signal.aborted) setLoading(false); });\n return () => abort.abort();\n // `running` is a dependency on purpose: `appliesNextTurn` flips with it.\n }, [sessionId, running]);\n\n const update = useCallback(async (change: VerseSessionControlsUpdate, reason: string): Promise<boolean> => {\n if (!sessionId) return false;\n setPending(true);\n setError(null);\n try {\n const next = await run(reason, () => updateSessionControls(sessionId, change));\n if (!next) return false;\n setView(next);\n return true;\n } catch (err) {\n setError(describeContextError(err));\n return false;\n } finally {\n setPending(false);\n }\n }, [sessionId, run]);\n\n return { view, loading, error, pending, update };\n}\n\n// ---------------------------------------------------------------------------\n// Queue\n// ---------------------------------------------------------------------------\n\n/**\n * The queue as the composer may trust it. The route is C3's and additive, so\n * a server mid-upgrade (or a stub answering `{}`) can send a body without\n * `items`; reading `queue.items.length` off that crashed the whole composer.\n * Anything malformed reads as \"no queue\" (the 3.9 behaviour), a partial body\n * keeps only well-formed items, and `held` is true only when it says so.\n */\nexport function normalizeQueue<T extends VerseQueueResponse>(raw: unknown): T | null {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const body = raw as Partial<Record<keyof VerseQueueResponse | 'sentTurnId', unknown>>;\n if (!Array.isArray(body.items)) return null;\n const items = body.items.filter((item): item is VerseQueueResponse['items'][number] => {\n if (item === null || typeof item !== 'object') return false;\n const i = item as Record<string, unknown>;\n return typeof i['id'] === 'string' && typeof i['text'] === 'string';\n });\n return {\n ...(body as object),\n sessionId: typeof body.sessionId === 'string' ? body.sessionId : '',\n items,\n held: body.held === true,\n heldReason: typeof body.heldReason === 'string' ? body.heldReason : null,\n } as T;\n}\n\nexport interface FollowUpQueueState {\n queue: VerseQueueResponse | null;\n error: string | null;\n /** Queue (or, when nothing runs, send) a follow-up. Null when not done. */\n enqueue: (text: string, opts?: { sendNow?: boolean }) => Promise<VerseQueueSendResult | null>;\n remove: (queueId: string) => Promise<boolean>;\n sendNow: (queueId: string) => Promise<boolean>;\n refresh: () => void;\n clearError: () => void;\n}\n\nexport function useFollowUpQueue(sessionId: string | null, running: boolean, run: GateRun): FollowUpQueueState {\n const [queue, setQueue] = useState<VerseQueueResponse | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [nonce, setNonce] = useState(0);\n\n useEffect(() => {\n if (!sessionId) {\n setQueue(null);\n return undefined;\n }\n const abort = new AbortController();\n fetchQueue(sessionId, abort.signal)\n .then((next) => { if (!abort.signal.aborted) setQueue(normalizeQueue(next)); })\n // No route (older server) = no queue; the composer then behaves as 3.9.\n .catch(() => { if (!abort.signal.aborted) setQueue(null); });\n return () => abort.abort();\n }, [sessionId, running, nonce]);\n\n const write = useCallback(async <T extends VerseQueueResponse>(reason: string, action: () => Promise<T>): Promise<T | null> => {\n setError(null);\n try {\n const raw = await run(reason, action);\n if (raw === null) return null;\n // A write that answered with no readable queue still happened: keep\n // the queue we had rather than blanking it, and report it done.\n const result = normalizeQueue<T>(raw);\n if (result) setQueue(result);\n return result ?? raw;\n } catch (err) {\n setError(describeContextError(err));\n return null;\n }\n }, [run]);\n\n const enqueue = useCallback((text: string, opts: { sendNow?: boolean } = {}) => {\n if (!sessionId) return Promise.resolve(null);\n return write(opts.sendNow ? 'Stop the running turn and send this next.' : 'Queue this message to send when the turn ends.',\n () => enqueueFollowUp(sessionId, text, opts));\n }, [sessionId, write]);\n\n const remove = useCallback(async (queueId: string) => {\n if (!sessionId) return false;\n return (await write('Remove this queued message.', () => removeFollowUp(sessionId, queueId))) !== null;\n }, [sessionId, write]);\n\n const sendNow = useCallback(async (queueId: string) => {\n if (!sessionId) return false;\n return (await write('Send this queued message now.', () => sendFollowUpNow(sessionId, queueId))) !== null;\n }, [sessionId, write]);\n\n const refresh = useCallback(() => setNonce((n) => n + 1), []);\n const clearError = useCallback(() => setError(null), []);\n\n return { queue, error, enqueue, remove, sendNow, refresh, clearError };\n}\n\n// ---------------------------------------------------------------------------\n// Attachments\n// ---------------------------------------------------------------------------\n\nexport interface AttachmentDraft {\n /** Client id (stable across the upload). */\n key: string;\n name: string;\n mime: string;\n bytes: number;\n status: 'uploading' | 'ready' | 'error';\n /** Object URL for an image thumbnail; revoked when the chip goes. */\n previewUrl: string | null;\n error: string | null;\n attachment: VerseAttachment | null;\n}\n\nexport interface AttachmentDraftsState {\n drafts: AttachmentDraft[];\n /** Upload files; `onReady` fires once per file with its `@path` token. */\n add: (files: readonly File[], onReady: (ref: string) => void) => void;\n remove: (key: string) => Promise<string | null>;\n /** Forget the chips (after a send) without deleting the files, which the sent message names. */\n reset: () => void;\n uploading: boolean;\n}\n\nlet draftCounter = 0;\n\nexport function useAttachmentDrafts(sessionId: string | null, run: GateRun): AttachmentDraftsState {\n const [drafts, setDrafts] = useState<AttachmentDraft[]>([]);\n const urls = useRef(new Set<string>());\n\n // Every object URL is released with the component (a chat switch unmounts it).\n useEffect(() => () => {\n for (const url of urls.current) URL.revokeObjectURL(url);\n urls.current.clear();\n }, []);\n\n const patch = useCallback((key: string, change: Partial<AttachmentDraft>) => {\n setDrafts((list) => list.map((d) => (d.key === key ? { ...d, ...change } : d)));\n }, []);\n\n const add = useCallback((files: readonly File[], onReady: (ref: string) => void) => {\n if (!sessionId) return;\n for (const file of files) {\n draftCounter += 1;\n const key = `att-${draftCounter}`;\n const image = file.type.startsWith('image/');\n let previewUrl: string | null = null;\n if (image && typeof URL.createObjectURL === 'function') {\n previewUrl = URL.createObjectURL(file);\n urls.current.add(previewUrl);\n }\n const name = file.name || (image ? 'pasted-image.png' : 'attachment');\n const mime = file.type || 'application/octet-stream';\n const draft: AttachmentDraft = { key, name, mime, bytes: file.size, status: 'uploading', previewUrl, error: null, attachment: null };\n setDrafts((list) => [...list, draft]);\n if (file.size > VERSE_ATTACHMENT_MAX_BYTES) {\n patch(key, { status: 'error', error: `${formatBytes(file.size)} — each file must be ${formatBytes(VERSE_ATTACHMENT_MAX_BYTES)} or smaller` });\n continue;\n }\n if (file.size === 0) {\n patch(key, { status: 'error', error: 'The file is empty.' });\n continue;\n }\n void (async () => {\n try {\n const dataBase64 = await readFileAsBase64(file);\n const saved = await run('Attach this file to the chat.', () => uploadAttachment(sessionId, { name, mime, dataBase64 }));\n if (!saved) {\n patch(key, { status: 'error', error: 'Not attached — the chat is locked.' });\n return;\n }\n patch(key, { status: 'ready', attachment: saved, name: saved.name });\n onReady(saved.ref);\n } catch (err) {\n patch(key, { status: 'error', error: describeContextError(err) });\n }\n })();\n }\n }, [sessionId, run, patch]);\n\n const remove = useCallback(async (key: string): Promise<string | null> => {\n const draft = drafts.find((d) => d.key === key);\n if (!draft) return null;\n setDrafts((list) => list.filter((d) => d.key !== key));\n if (draft.previewUrl) {\n URL.revokeObjectURL(draft.previewUrl);\n urls.current.delete(draft.previewUrl);\n }\n if (draft.attachment && sessionId) {\n const attachment = draft.attachment;\n try {\n await run('Remove this attachment.', () => deleteAttachment(sessionId, attachment.id));\n } catch {\n // Already gone on the server: the chip's removal is what matters.\n }\n return attachment.ref;\n }\n return null;\n }, [drafts, sessionId, run]);\n\n const reset = useCallback(() => {\n setDrafts((list) => {\n for (const d of list) {\n if (d.previewUrl) {\n URL.revokeObjectURL(d.previewUrl);\n urls.current.delete(d.previewUrl);\n }\n }\n return [];\n });\n }, []);\n\n return { drafts, add, remove, reset, uploading: drafts.some((d) => d.status === 'uploading') };\n}\n","/**\n * routes/verse/composer/useFooterFold.ts — how far the composer footer folds\n * to fit its one row WITHOUT truncating a label.\n *\n * 0 every control in words\n * 1 \"Effort:\" becomes an icon (\"ıl High\")\n * 2 the seat chip drops to its monogram (+ capacity ring)\n * 3 the permission mode drops to its icon\n * 4 the pickers move into the ⋯ sheet (the phone footer)\n *\n * Measured, not a breakpoint: the labels vary too much (\"Local\" vs \"Personal\n * Codex\", \"Opus 5\" vs \"Qwen3.8 27b-ctx64k\"), so any fixed width either folds\n * a footer that fit or lets one overflow. Every folded control keeps its full\n * words as its accessible name and tooltip.\n *\n * WHEN IT MEASURES (3.10.1 review):\n * - On mount, synchronously, in a layout effect — never waiting for the\n * first ResizeObserver delivery, which lands after the browser has\n * painted. Composer is keyed by chat, so this runs on every chat switch;\n * waiting would flash the unfolded row on each one.\n * - When the WORDS change (the caller's `labels` key, or a web font\n * landing): the widths measured for the old words say nothing about the\n * new ones, so it starts again from fold 0 and climbs, one step per\n * synchronous layout effect, all before paint.\n * - When only the ROOM changes (ResizeObserver): no reset. Narrower → keep\n * climbing from the current fold. Wider → step down, in one render, onto\n * the folds already measured too wide for the old room but no wider than\n * the new one. A width that changes the fold by nothing (every frame of a\n * sidebar drag) renders nothing at all.\n * - Never while `hold` is set (the ⋯ sheet or a picker's menu is open): a\n * re-measure can unmount the very control the overlay belongs to and\n * strand focus. Whatever changed is measured once it closes.\n *\n * Typing never re-measures (it is not in the key), so nothing moves under the\n * caret. Without layout (jsdom, a hidden panel) the width is 0 and it never\n * folds until the footer gets a size.\n */\nimport { useEffect, useLayoutEffect, useRef, useState, type RefObject } from 'react';\nimport { flushSync } from 'react-dom';\n\n/** The fold at which the pickers leave the row for the ⋯ sheet. */\nexport const FOOTER_FOLD_SHEET = 4;\n\nexport interface FooterFoldOptions {\n /** An overlay on the footer is open: measure nothing until it closes. */\n hold?: boolean;\n /**\n * Called from the layout effect each time the fold comes to rest, with the\n * element that had focus inside the footer when this move began (null when\n * focus was elsewhere or nothing moved). A fold can unmount that control\n * (the pickers leave for ⋯, ⋯ leaves for the pickers, or the re-measure\n * after new words briefly passes through fold 0) — the owner puts focus on\n * its replacement.\n */\n onSettle?: (fold: number, focusBefore: Element | null) => void;\n}\n\ninterface FoldMemo {\n /** The words the cached widths belong to; null until the first measurement. */\n key: string | null;\n /** The words on screen now — ahead of `key` while the footer had no size to measure them at. */\n latest: string;\n /** scrollWidth of every fold that overflowed under `key` — its true content width. */\n widths: Array<number | undefined>;\n /** The footer width (clientWidth) the current fold was chosen for. */\n width: number;\n /** The committed fold, for the ResizeObserver callback. */\n fold: number;\n hold: boolean;\n /** A move is under way; `focusBefore` is what had focus in the footer when it began. */\n moving: boolean;\n focusBefore: Element | null;\n}\n\n/** Remember what had focus in the footer when a move starts (once per move). */\nfunction beginMove(s: FoldMemo, node: HTMLElement): void {\n if (s.moving) return;\n s.moving = true;\n const active = typeof document === 'undefined' ? null : document.activeElement;\n s.focusBefore = active && node.contains(active) ? active : null;\n}\n\n/** The lowest fold at or below `fold` whose measured width fits `width` — walking down only across measured folds. */\nfunction unfoldTo(widths: FoldMemo['widths'], fold: number, width: number): number {\n let next = fold;\n while (next > 0) {\n const below = widths[next - 1];\n if (below === undefined || below > width) break;\n next -= 1;\n }\n return next;\n}\n\nexport function useFooterFold(footer: RefObject<HTMLElement | null>, labels: string, options: FooterFoldOptions = {}): number {\n const { hold = false, onSettle } = options;\n const [fold, setFold] = useState(0);\n // Bumped to take a first look at a footer that mounted with no size.\n const [look, setLook] = useState(0);\n const [fontEpoch, setFontEpoch] = useState(0);\n const memo = useRef<FoldMemo>({ key: null, latest: '', widths: [], width: 0, fold: 0, hold: false, moving: false, focusBefore: null });\n const settle = useRef(onSettle);\n settle.current = onSettle;\n const key = `${fontEpoch}|${labels}`;\n\n useLayoutEffect(() => {\n const s = memo.current;\n s.fold = fold;\n s.hold = hold;\n s.latest = key;\n const node = footer.current;\n const width = node?.clientWidth ?? 0;\n if (node && !hold && width > 0) {\n if (s.key !== key) {\n // New words: start again from every label in words.\n s.key = key;\n s.widths = [];\n s.width = width;\n if (fold !== 0) {\n beginMove(s, node);\n setFold(0);\n return;\n }\n } else if (width !== s.width) {\n // Only the room changed while held (or before the first look): no reset.\n s.width = width;\n const lower = unfoldTo(s.widths, fold, width);\n if (lower !== fold) {\n beginMove(s, node);\n setFold(lower);\n return;\n }\n }\n // Strict: scrollWidth rounds, so even a sub-pixel overflow folds — one step early beats a clipped Send.\n const content = node.scrollWidth;\n if (content > width) {\n s.widths[fold] = content;\n if (fold < FOOTER_FOLD_SHEET) {\n beginMove(s, node);\n setFold(fold + 1);\n return;\n }\n }\n }\n const focusBefore = s.focusBefore;\n s.moving = false;\n s.focusBefore = null;\n settle.current?.(fold, focusBefore);\n }, [footer, key, hold, fold, look]);\n\n useEffect(() => {\n const node = footer.current;\n if (!node || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(() => {\n const s = memo.current;\n const width = node.clientWidth;\n // Held: the layout effect measures the new room when the overlay closes.\n if (s.hold || width === 0) return;\n // flushSync: this callback runs after layout and before paint, so the\n // new fold is drawn in the same frame instead of one frame late.\n if (s.key !== s.latest) {\n // Never measured, or the words changed while the footer had no size\n // (a hidden panel): the layout effect takes a full look now.\n flushSync(() => setLook((n) => n + 1));\n return;\n }\n if (width === s.width) return;\n s.width = width;\n let next = unfoldTo(s.widths, s.fold, width);\n if (next === s.fold && node.scrollWidth > width) {\n s.widths[s.fold] = node.scrollWidth;\n next = Math.min(s.fold + 1, FOOTER_FOLD_SHEET);\n }\n if (next === s.fold) return;\n beginMove(s, node);\n flushSync(() => setFold(next));\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, [footer]);\n\n // A web font landing changes every label's width without changing a word.\n useEffect(() => {\n const fonts = typeof document === 'undefined' ? undefined : (document as Document & { fonts?: FontFaceSet }).fonts;\n if (!fonts || typeof fonts.addEventListener !== 'function') return undefined;\n const onLoaded = () => setFontEpoch((n) => n + 1);\n fonts.addEventListener('loadingdone', onLoaded);\n return () => fonts.removeEventListener('loadingdone', onLoaded);\n }, []);\n\n return fold;\n}\n","/**\n * routes/verse/DictationButton.tsx — push-to-talk for the composer via the\n * Web Speech API (`SpeechRecognition` / `webkitSpeechRecognition`). Interim\n * results stream into the textarea through `onInterim`; final segments are\n * committed through `onFinal`. Escape stops listening. When the browser has\n * no recognizer (Firefox, Tauri/WKWebView today) the button is disabled and\n * a tooltip points at Wispr Flow / Superwhisper — system-level dictation\n * types straight into the textarea, so nothing is lost.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport styles from './Composer.module.css';\n\nexport const DICTATION_FALLBACK_HINT = 'Use Wispr Flow / Superwhisper — system dictation works in this box';\n\ninterface RecognitionResultLike {\n isFinal: boolean;\n 0: { transcript: string };\n length: number;\n}\ninterface RecognitionEventLike {\n resultIndex: number;\n results: ArrayLike<RecognitionResultLike>;\n}\ninterface RecognitionLike {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n onresult: ((event: RecognitionEventLike) => void) | null;\n onend: (() => void) | null;\n onerror: ((event: { error?: string }) => void) | null;\n start(): void;\n stop(): void;\n abort(): void;\n}\ntype RecognitionCtor = new () => RecognitionLike;\n\n/** Resolve the recognizer constructor, if this runtime ships one. */\nexport function getSpeechRecognition(): RecognitionCtor | null {\n const w = globalThis as unknown as { SpeechRecognition?: RecognitionCtor; webkitSpeechRecognition?: RecognitionCtor };\n return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;\n}\n\nexport interface DictationButtonProps {\n disabled?: boolean;\n onInterim: (text: string) => void;\n onFinal: (text: string) => void;\n onListeningChange?: (listening: boolean) => void;\n}\n\nexport function DictationButton({ disabled = false, onInterim, onFinal, onListeningChange }: DictationButtonProps) {\n const Recognizer = getSpeechRecognition();\n const [listening, setListening] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const recognizer = useRef<RecognitionLike | null>(null);\n const callbacks = useRef({ onInterim, onFinal, onListeningChange });\n callbacks.current = { onInterim, onFinal, onListeningChange };\n\n const stop = useCallback(() => {\n const active = recognizer.current;\n recognizer.current = null;\n if (active) {\n active.onresult = null;\n active.onend = null;\n active.onerror = null;\n try {\n active.stop();\n } catch {\n /* already stopped */\n }\n }\n setListening(false);\n callbacks.current.onInterim('');\n callbacks.current.onListeningChange?.(false);\n }, []);\n\n const start = useCallback(() => {\n if (!Recognizer || recognizer.current) return;\n let instance: RecognitionLike;\n try {\n instance = new Recognizer();\n } catch {\n setError('Dictation could not start in this browser.');\n return;\n }\n instance.lang = navigator.language || 'en-US';\n instance.continuous = true;\n instance.interimResults = true;\n instance.onresult = (event) => {\n let interim = '';\n let finalText = '';\n for (let i = event.resultIndex; i < event.results.length; i += 1) {\n const result = event.results[i];\n if (!result) continue;\n const transcript = result[0]?.transcript ?? '';\n if (result.isFinal) finalText += transcript;\n else interim += transcript;\n }\n if (finalText) callbacks.current.onFinal(finalText.trim());\n callbacks.current.onInterim(interim);\n };\n instance.onerror = (event) => {\n const code = event.error ?? 'unknown';\n if (code === 'not-allowed' || code === 'service-not-allowed') setError('Microphone access was denied.');\n else if (code !== 'aborted' && code !== 'no-speech') setError(`Dictation stopped (${code}).`);\n stop();\n };\n instance.onend = () => {\n if (recognizer.current === instance) stop();\n };\n recognizer.current = instance;\n setError(null);\n try {\n instance.start();\n } catch {\n recognizer.current = null;\n setError('Dictation could not start in this browser.');\n return;\n }\n setListening(true);\n callbacks.current.onListeningChange?.(true);\n }, [Recognizer, stop]);\n\n // Escape stops dictation from anywhere on the page; unmount stops it too.\n useEffect(() => {\n if (!listening) return;\n function onKey(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault();\n stop();\n }\n }\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [listening, stop]);\n useEffect(() => () => {\n const active = recognizer.current;\n recognizer.current = null;\n try {\n active?.abort();\n } catch {\n /* ignore */\n }\n }, []);\n\n if (!Recognizer) {\n return (\n <span className={styles.dictationWrap}>\n {/* This button previously carried the hint THREE times over: a native\n `title`, an aria-describedby, and a hand-rolled `role=\"tooltip\"`\n span that lived in the DOM permanently at opacity 0 and was\n revealed by a :hover rule on the wrapper. That span is the grey\n slab that overhangs the panel beside the composer. One Tooltip\n replaces all three and is mounted only while it is open. */}\n <Tooltip label={DICTATION_FALLBACK_HINT} placement=\"top\">\n <button type=\"button\" className={styles.dictation} disabled aria-disabled=\"true\">\n <MicIcon />\n <span className=\"visually-hidden\">Dictation unavailable</span>\n </button>\n </Tooltip>\n </span>\n );\n }\n\n return (\n <span className={styles.dictationWrap}>\n {/* Icon-only, so the aria-label stays put: the tooltip describes, it\n does not name. */}\n <Tooltip label={listening ? 'Stop dictation' : 'Dictate'} shortcut={listening ? 'Esc' : undefined} placement=\"top\">\n <button type=\"button\" className={`${styles.dictation} ${listening ? styles.dictationLive : ''}`}\n disabled={disabled && !listening} aria-pressed={listening}\n aria-label={listening ? 'Stop dictation (Esc)' : 'Start dictation'}\n onClick={() => (listening ? stop() : start())}>\n <MicIcon />\n {listening ? <span className={styles.dictationPulse} aria-hidden=\"true\" /> : null}\n </button>\n </Tooltip>\n {error ? <span role=\"alert\" className={styles.dictationError}>{error}</span> : null}\n </span>\n );\n}\n\nfunction MicIcon() {\n return (\n <svg viewBox=\"0 0 20 20\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"7\" y=\"2\" width=\"6\" height=\"10\" rx=\"3\" fill=\"currentColor\" />\n <path d=\"M4.5 9.5a5.5 5.5 0 0 0 11 0M10 15v3M7 18h6\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n </svg>\n );\n}\n","/**\n * routes/verse/health/ComposerSeatBlock.tsx — the Composer's \"this seat can't\n * run a turn\" block (V3.10, unit A2; mounted by the Composer's owner, A6).\n *\n * The session engine refuses a turn on a signed-out or exhausted seat with a\n * 409 (`getSeatReadiness`, core/verse/seats.ts). This block says so BEFORE\n * the operator types a message into a dead end: the reason, the reset time\n * when there is one, Reconnect for a signed-out seat, and the ranked\n * alternatives — computed by the SAME function the engine uses\n * (core/verse/seat-readiness.ts), so the block never offers a seat the server\n * would then refuse.\n *\n * Sessions are seat-bound, so an alternative starts a NEW chat on that seat —\n * the same action as the Composer seat menu's \"New chat on …\" (`onSeatChange`).\n *\n * Renders nothing while the seat is ready.\n */\nimport { useState } from 'react';\nimport type { SeatHealthReport } from '../../../../core/verse/health-types.js';\nimport { rankSeatAlternatives, seatBlock } from '../../../../core/verse/seat-readiness.js';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { IconExternalLink } from '../../../components/primitives/icons.js';\nimport type { VerseSeat } from '../../../data/api-types.js';\nimport type { SeatChoice } from '../SeatSelector.js';\nimport { firstRunnableModel } from '../verse-model.js';\nimport { reconnectSeat } from './health-queries.js';\nimport { useSeatHealth } from './useSeatHealth.js';\nimport styles from './SeatHealth.module.css';\n\n/** At most this many alternatives are offered inline. */\nconst INLINE_ALTERNATIVES = 3;\n\nexport interface ComposerSeatBlockViewProps {\n seats: readonly VerseSeat[];\n /** The seat this chat is bound to. */\n seatId: string;\n reports?: readonly SeatHealthReport[] | null;\n /** Start a new chat on another seat (Composer's `onSeatChange`). */\n onSeatChange: (choice: SeatChoice) => void;\n onReconnect?: (seatId: string) => Promise<void>;\n now?: number;\n}\n\nexport function ComposerSeatBlockView({\n seats,\n seatId,\n reports = null,\n onSeatChange,\n onReconnect = reconnectSeat,\n now = Date.now(),\n}: ComposerSeatBlockViewProps) {\n const [busy, setBusy] = useState(false);\n const [status, setStatus] = useState<{ text: string; error: boolean } | null>(null);\n const seat = seats.find((candidate) => candidate.id === seatId) ?? null;\n if (seat === null) return null;\n const report = reports?.find((candidate) => candidate.seatId === seatId) ?? null;\n const block = seatBlock(seat, report, now);\n if (block === null) return null;\n\n const alternatives = rankSeatAlternatives(seatId, seats, reports, now)\n .map((id) => seats.find((candidate) => candidate.id === id))\n .filter((candidate): candidate is VerseSeat => candidate !== undefined)\n .slice(0, INLINE_ALTERNATIVES);\n\n const reconnect = async (): Promise<void> => {\n setBusy(true);\n setStatus(null);\n try {\n await onReconnect(seatId);\n setStatus({ text: 'Sign-in opened in Terminal. Send again once you have finished there.', error: false });\n } catch (error) {\n setStatus({ text: error instanceof Error && error.message ? error.message : 'The sign-in window could not be opened.', error: true });\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <div className={styles.block} role=\"alert\" data-connection={block.connection}>\n <p className={styles.blockReason}>{block.reason}</p>\n <div className={styles.alternatives}>\n {block.connection === 'signed-out' ? (\n <Button size=\"sm\" variant=\"primary\" icon={<IconExternalLink />} busy={busy} onClick={() => { void reconnect(); }}>\n Reconnect {seat.label}\n </Button>\n ) : null}\n {alternatives.length > 0 ? (\n <>\n <span className={styles.alternativesLabel}>Continue in a new chat on</span>\n {alternatives.map((alternative) => {\n const model = firstRunnableModel(alternative);\n if (model === null) return null;\n return (\n <Button key={alternative.id} size=\"sm\" variant=\"subtle\"\n onClick={() => onSeatChange({ seatId: alternative.id, model: model.id })}>\n {alternative.label}\n </Button>\n );\n })}\n </>\n ) : (\n <span className={styles.alternativesLabel}>No other seat is ready right now.</span>\n )}\n </div>\n {status === null ? null : (\n <p className={styles.status} aria-live=\"polite\" data-error={status.error ? 'true' : undefined}>{status.text}</p>\n )}\n </div>\n );\n}\n\nexport type ComposerSeatBlockProps = Omit<ComposerSeatBlockViewProps, 'reports' | 'now'> & {\n /** False stops polling health (the seat's own capacity still decides). Default true. */\n active?: boolean;\n};\n\n/** Self-fetching: reads the live health reports (polled every 30 s while visible). */\nexport function ComposerSeatBlock({ active = true, ...props }: ComposerSeatBlockProps) {\n const health = useSeatHealth(active);\n return <ComposerSeatBlockView {...props} reports={health.data?.seats ?? null} />;\n}\n","/**\n * routes/verse/Composer.tsx — the message box and its controls (3.10: unit\n * C3; SPEC-310C §2 \"Composer\").\n *\n * [queued follow-ups: Edit · Send now · ×] (hidden when empty; portalled\n * to the host's `queueSlot` when given)\n * ┌──────────────────────────────────────────────────────────────────────┐\n * │ [attachment chips] │\n * │ Ask anything — @ to add files, / for commands │\n * │ [+] 🎙 ✎ Accept edits ▾ [C] Claude Max ◔ Opus 5 ▾ Effort: High ▾ ◔ 31% Send ⏎ │\n * └──────────────────────────────────────────────────────────────────────┘\n *\n * FOOTER: left = how you write (attach, dictate, permission mode); right =\n * where it runs and what it sends (seat, model, effort, context, send). The\n * seat chip names the ACCOUNT and the Model picker the model, so the model is\n * said once. Effort appears only where the seat can set it. Labels are short\n * and never ellipsized; the full words are each control's name and tooltip\n * (shown on keyboard focus too). When the row is too narrow it folds\n * (composer/useFooterFold): \"Effort:\" → an icon, the seat chip → its\n * monogram, the mode → its icon, then the pickers into the ⋯ sheet. Nothing\n * re-folds while the ⋯ sheet or a picker's menu is open, and when a fold\n * takes away the control that had focus, focus lands on its replacement (⋯,\n * or the box). Controls are 28px, 8px apart; typing never reflows the row\n * (while a turn runs, ■ and Queue sit there empty or not; a send in flight\n * keeps Send's width and says so with a busy style).\n *\n * ALWAYS EDITABLE. Enter while a turn runs QUEUES the message on the server\n * (at most 3); the queue sends each follow-up when the turn before it ends\n * cleanly, and HOLDS after a failure or a Stop (queue row says why). ⇧⌘↩\n * stops the running turn and sends this message next. Esc stops the turn —\n * only from an empty box with no menu open, so it never eats a draft.\n *\n * CONTROLS apply from the next turn, even mid-run: Permission (⇧⌘M: Plan,\n * Accept edits, Auto, Bypass — bypass is red and confirmed per chat), Model\n * (⇧⌘I) and Effort (⇧⌘E). An option the seat cannot honour is listed\n * disabled with the reason. Keys come from C0's command catalog; the palette\n * reaches the same actions through the `ashlr:command` window event.\n *\n * ATTACHMENTS: [+] / ⌘U, paste or drop. Each becomes a private copy on the\n * server and an `@path` token in the text, which the engine grants with\n * exactly that folder. `@` fuzzy-finds project files; `/` at the start offers\n * handoff, compact, new, plan, effort, model.\n *\n * At 375px (or a column too narrow for the pickers) the footer folds to\n * [+] [mic] · seat · ⋯ (the pickers in a bottom sheet) · Send.\n *\n * Kept from 3.9: per-chat drafts that survive a reload, ↑ history recall, the\n * pre-send cost hint, dictation, ⌘. to stop, and the seat-health block (A2).\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState, type ClipboardEvent, type DragEvent, type KeyboardEvent, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { VerseSeat } from '../../data/api-types.js';\nimport type { VerseEffort, VersePermissionMode } from '../../../core/verse/workbench-types.js';\nimport { VERSE_QUEUE_MAX } from '../../../core/verse/workbench-types.js';\nimport { MutationTokenDialog } from '../../components/auth/MutationTokenDialog.js';\nimport { IconPlus } from '../../components/primitives/icons.js';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport { costHint, loadDraft, loadHistory, pushHistory, saveDraft, type CostHint } from './chat/composer-state.js';\nimport { AttachmentChips } from './composer/AttachmentChips.js';\nimport { BypassConfirmDialog } from './composer/BypassConfirmDialog.js';\nimport { COMPOSER_COMMAND_IDS, pressesCommand, shortcutLabel, WORKBENCH_COMMAND_EVENT, type ComposerCommandId } from './composer/composer-keys.js';\nimport { searchSessionFiles } from './composer/composer-queries.js';\nimport {\n activeTrigger,\n applyCompletion,\n insertAttachmentRef,\n matchSlashCommands,\n mentionFor,\n removeAttachmentRef,\n type ComposerTrigger,\n type SlashCommand,\n} from './composer/composer-text.js';\nimport { ContextRing } from './composer/ContextRing.js';\nimport { ControlMenu } from './composer/ControlMenu.js';\nimport { ControlsSheet } from './composer/ControlsSheet.js';\nimport { QueueRow } from './composer/QueueRow.js';\nimport { capacityRingShown, SeatChip } from './composer/SeatChip.js';\nimport { SuggestMenu, suggestOptionId, type SuggestItem } from './composer/SuggestMenu.js';\nimport { useAttachmentDrafts, useFollowUpQueue, useSessionControls } from './composer/useComposerData.js';\nimport { FOOTER_FOLD_SHEET, useFooterFold } from './composer/useFooterFold.js';\nimport { useTokenGate } from './context/use-token-gate.js';\nimport { DictationButton } from './DictationButton.js';\nimport { ComposerSeatBlock } from './health/ComposerSeatBlock.js';\nimport type { SeatChoice } from './SeatSelector.js';\nimport { useViewport } from './shell/viewport.js';\nimport { ENGINE_LABEL, modelLabel, seatPillLabel } from './verse-model.js';\nimport { formatTokens } from './verse-readouts.js';\nimport type { VerseFileMatch } from '../../../core/verse/workbench-types.js';\nimport styles from './Composer.module.css';\nimport cstyles from './composer/composer.module.css';\n\nexport interface ComposerProps {\n /** Which chat this box belongs to — drafts, history, controls and the queue are per session. */\n sessionId?: string | null;\n seats: readonly VerseSeat[];\n seat: SeatChoice;\n /** Engine of the current chat (identity tick, and what the seat can do). */\n engine?: VerseSeat['engine'];\n running: boolean;\n disabled: boolean;\n disabledReason?: string | null;\n locked: boolean;\n /** True when this session already has turns — the hint row has done its job. */\n hintSeen?: boolean;\n /**\n * Live context occupancy, for the pre-send cost hint and the footer ring —\n * the SAME budget the header meter draws (verse-model `sessionContextBudget`).\n */\n contextTokens?: number | null;\n contextWindow?: number | null;\n autoCompactAt?: number | null;\n /** False when `contextTokens` is an upper bound (codex before its rollout is read). */\n contextExact?: boolean;\n /** The box was pre-filled with a handoff note that has not been sent yet. */\n handoffDraft?: boolean;\n onSend: (text: string) => Promise<boolean> | boolean;\n onStop: () => void;\n /** Start a new chat on a seat (the seat menu, `/new`). */\n onSeatChange: (choice: SeatChoice) => void;\n /** \"Continue on ‹seat›\": open the handoff prefilled for that seat. Absent → a new chat on it. */\n onContinueOn?: (choice: SeatChoice) => void;\n /** `/handoff`: open this chat's handoff. Absent → the command is listed disabled with where to find it. */\n onHandoff?: () => void;\n /**\n * Why `/handoff` cannot run right now (a turn is running, dispatch is off) —\n * the SAME reason the header's ⋯ item shows. Null/absent = available.\n */\n handoffDisabledReason?: string | null;\n /**\n * Where the \"Queued turns\" row renders. SPEC-310C §2 orders the rows above\n * the composer notice → queued → live status → branch bar; the queue's state\n * lives here, so the host (Workspace) passes the element at the queue's\n * place and the row is portalled into it. Absent/null → the row renders at\n * the top of the composer, as before.\n */\n queueSlot?: HTMLElement | null;\n /**\n * Text another pane drafts INTO the box (Review's \"Add to message\"\n * `path:line: note`, Terminal's \"Send selection to chat\"): each new `nonce`\n * appends `text` on its own paragraph and focuses the box. Never sends —\n * the operator reads it and presses Enter (or ⌘Enter).\n */\n insertRequest?: { nonce: number; text: string } | null;\n autoFocus?: boolean;\n}\n\nconst MAX_TEXT_BYTES = 64 * 1024;\n/** Typing must pause this long before the draft is written to localStorage (see saveDraft). */\nconst DRAFT_WRITE_DEBOUNCE_MS = 400;\n/** The `@` finder waits for a pause in typing before it asks the server. */\nconst MENTION_DEBOUNCE_MS = 120;\n/** DESIGN §5: the box grows to 40% of the viewport, then scrolls. */\nconst MAX_HEIGHT_RATIO = 0.4;\nconst MAX_HEIGHT_FALLBACK_PX = 320;\n\ntype PickerId = 'permission' | 'model' | 'effort';\n\n/**\n * Permission-mode icons as inline SVG, NOT text glyphs (review 3.10 c18).\n * WHY: '◇ ✎ ↻ ⚠' all sit outside the Ashlr Sans Latin subset's unicode-range\n * (design/global.css), so the browser fetched the 230 KB full IBM Plex face\n * on every chat paint just to find that three of the four are not in Plex\n * either and fall back to a system font. SVG costs no font request and draws\n * the same in every OS. Same geometry as components/primitives/icons.tsx\n * (16-unit box, 1.5 stroke, round caps, currentColor); decorative — the menu\n * button already carries the mode's name.\n */\nfunction ModeIcon({ children }: { children: ReactNode }) {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.5} strokeLinecap=\"round\" strokeLinejoin=\"round\" focusable=\"false\" aria-hidden=\"true\">\n {children}\n </svg>\n );\n}\n\nconst PERMISSION_GLYPH: Record<VersePermissionMode, ReactNode> = {\n // Plan: an outlined diamond (was '◇').\n plan: <ModeIcon><path d=\"M8 2.2 13.8 8 8 13.8 2.2 8Z\" /></ModeIcon>,\n // Accept edits: a pencil (was '✎').\n 'accept-edits': <ModeIcon><path d=\"M10.6 2.9 13.1 5.4 5.6 12.9 2.6 13.4 3.1 10.4Z\" /><path d=\"M9.2 4.3 11.7 6.8\" /></ModeIcon>,\n // Auto: a circular arrow (was '↻').\n auto: <ModeIcon><path d=\"M13.2 7.2a5.2 5.2 0 1 0-.7 3.5\" /><path d=\"M13.5 3.4v3.9h-3.9\" /></ModeIcon>,\n // Bypass: a warning triangle (was '⚠').\n bypass: <ModeIcon><path d=\"M8 2.6 14.2 13H1.8L8 2.6Z\" /><path d=\"M8 6.6v3\" /><circle cx=\"8\" cy=\"11.3\" r=\"0.75\" fill=\"currentColor\" stroke=\"none\" /></ModeIcon>,\n};\n\n/** The footer's short mode names; the option's own label (\"Bypass permissions\") is the name and title. */\nconst PERMISSION_SHORT: Record<VersePermissionMode, string> = {\n plan: 'Plan',\n 'accept-edits': 'Accept edits',\n auto: 'Auto',\n bypass: 'Bypass',\n};\n\n/** Effort, once \"Effort:\" folds away: three rising bars (same geometry rules as ModeIcon). */\nconst EFFORT_GLYPH = <ModeIcon><path d=\"M3.5 12.5v-2.5M8 12.5V7.5M12.5 12.5v-8\" /></ModeIcon>;\n\n/** The compact footer's \"more settings\" dots — SVG for the same reason ('⋯' U+22EF is outside the subset and not in Plex). */\nconst MORE_DOTS = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\" focusable=\"false\" aria-hidden=\"true\">\n <circle cx=\"3.5\" cy=\"8\" r=\"1.25\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.25\" />\n <circle cx=\"12.5\" cy=\"8\" r=\"1.25\" />\n </svg>\n);\n\nexport function Composer({ sessionId = null, seats, seat, engine, running, disabled, disabledReason, locked,\n hintSeen = false, contextTokens = null, contextWindow = null, autoCompactAt = null, contextExact = true, handoffDraft = false,\n onSend, onStop, onSeatChange, onContinueOn, onHandoff, handoffDisabledReason = null, queueSlot = null, insertRequest = null,\n autoFocus = false }: ComposerProps) {\n const { compact: phone } = useViewport();\n const gate = useTokenGate();\n const run = gate.run;\n const controls = useSessionControls(disabled ? null : sessionId, running, run);\n const followUps = useFollowUpQueue(disabled ? null : sessionId, running, run);\n const attachments = useAttachmentDrafts(disabled ? null : sessionId, run);\n\n // A draft survives ⌘K, a reload and a crash (Composer is keyed by session id).\n const [draft, setDraft] = useState(() => loadDraft(sessionId));\n const [interim, setInterim] = useState('');\n const [sending, setSending] = useState(false);\n const [listening, setListening] = useState(false);\n const [sentHere, setSentHere] = useState(false);\n const [note, setNote] = useState<{ text: string; error: boolean } | null>(null);\n const [historyAt, setHistoryAt] = useState(-1);\n const [initialHistory] = useState(() => loadHistory(sessionId));\n const history = useRef<string[]>(initialHistory);\n const stashed = useRef('');\n const textarea = useRef<HTMLTextAreaElement>(null);\n const form = useRef<HTMLFormElement>(null);\n const fileInput = useRef<HTMLInputElement>(null);\n const moreButton = useRef<HTMLButtonElement>(null);\n /** The ⋯ sheet was closed by the operator: focus goes back to ⋯ — or to the box, if ⋯ has folded away. */\n const sheetReturnFocus = useRef(false);\n const helpId = useId();\n const suggestId = useId();\n const [caret, setCaret] = useState<number | null>(null);\n const [dismissedTrigger, setDismissedTrigger] = useState<number | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const [fileMatches, setFileMatches] = useState<{ query: string; files: VerseFileMatch[]; primaryRoot: string | null } | null>(null);\n const [fileSearchError, setFileSearchError] = useState<string | null>(null);\n const [openRequest, setOpenRequest] = useState<Record<PickerId, number>>({ permission: 0, model: 0, effort: 0 });\n const [openMenus, setOpenMenus] = useState<Record<PickerId, boolean>>({ permission: false, model: false, effort: false });\n const [sheetOpen, setSheetOpen] = useState(false);\n const [bypassAsk, setBypassAsk] = useState(false);\n const [dragging, setDragging] = useState(false);\n\n const text = interim ? `${draft}${draft && !draft.endsWith(' ') ? ' ' : ''}${interim}` : draft;\n const tooLong = useMemo(() => new TextEncoder().encode(text).length > MAX_TEXT_BYTES, [text]);\n const queue = followUps.queue;\n const queueAvailable = queue !== null;\n const queueFull = (queue?.items.length ?? 0) >= VERSE_QUEUE_MAX;\n const hasText = text.trim().length > 0;\n const blockedByUpload = attachments.uploading;\n const canSend = !disabled && !sending && hasText && !tooLong && !blockedByUpload && (!running || (queueAvailable && !queueFull));\n const showHint = !hintSeen && !sentHere;\n const showHandoffHelp = handoffDraft && !sentHere && hasText;\n const cost = useMemo(\n () => costHint(text, { contextTokens, contextWindow, autoCompactAt, exact: contextExact }),\n [text, contextTokens, contextWindow, autoCompactAt, contextExact],\n );\n const view = controls.view;\n const effectiveEngine = engine ?? seats.find((s) => s.id === seat.seatId)?.engine ?? 'claude';\n const chipLabel = seatPillLabel(seats, { seatId: seat.seatId, engine: effectiveEngine, model: view?.controls.model ?? seat.model });\n // The chip names the account; a local seat's label IS its model's name, so it says \"Local\".\n const seatName = effectiveEngine === 'local'\n ? ENGINE_LABEL.local\n : seats.find((s) => s.id === seat.seatId)?.label ?? ENGINE_LABEL[effectiveEngine];\n const modelName = view ? labelOf(view.options.models, view.controls.model) : modelLabel(seats, seat);\n const effortOn = view?.options.efforts.some((o) => o.available) === true;\n const effortName = view?.controls.effort ? labelOf(view.options.efforts, view.controls.effort) : 'Default';\n const footerRow = useRef<HTMLDivElement>(null);\n // The seat chip's capacity ring (14px + a gap) arrives with a later roster\n // poll and takes room, so its presence is part of what the fold measures;\n // so is `phone`, which swaps the whole row for the compact one.\n const ringShown = capacityRingShown(seats.find((s) => s.id === seat.seatId), effectiveEngine);\n const pickerMenuOpen = Object.values(openMenus).some(Boolean);\n const fold = useFooterFold(footerRow, [seatName, modelName, effortOn && effortName, view?.controls.permissionMode,\n running, queueAvailable, queueFull, locked, disabled, ringShown, phone].join('|'), {\n // Nothing folds under an open overlay: a re-measure could unmount ⋯ or\n // the picker the overlay belongs to. What changed is measured on close.\n hold: sheetOpen || pickerMenuOpen,\n onSettle: (settled, focusBefore) => {\n // A fold took away the control that had focus (the pickers left for ⋯,\n // ⋯ left for the pickers, or new words passed the row through fold 0),\n // or the operator closed the sheet: put focus on the replacement.\n const active = document.activeElement;\n const lost = active === null || active === document.body;\n const returning = sheetReturnFocus.current;\n sheetReturnFocus.current = false;\n if (!lost || (!returning && (focusBefore === null || focusBefore.isConnected))) return;\n const more = phone || settled >= FOOTER_FOLD_SHEET ? moreButton.current : null;\n (more ?? textarea.current)?.focus();\n },\n });\n const compact = phone || fold >= FOOTER_FOLD_SHEET;\n const attachBlocked = effectiveEngine === 'grok'\n ? 'Grok can only open files inside the project folder, so it can’t read attachments.'\n : null;\n\n // ---- drafts ---------------------------------------------------------------\n const persistedDraft = useRef(draft);\n const latestDraft = useRef(draft);\n latestDraft.current = draft;\n\n const flushDraft = useCallback(() => {\n if (persistedDraft.current === latestDraft.current) return;\n persistedDraft.current = latestDraft.current;\n saveDraft(sessionId, latestDraft.current);\n }, [sessionId]);\n\n useEffect(() => {\n if (persistedDraft.current === draft) return undefined;\n const id = setTimeout(flushDraft, DRAFT_WRITE_DEBOUNCE_MS);\n return () => clearTimeout(id);\n }, [draft, flushDraft]);\n\n useEffect(() => {\n window.addEventListener('beforeunload', flushDraft);\n return () => {\n window.removeEventListener('beforeunload', flushDraft);\n flushDraft();\n };\n }, [flushDraft]);\n\n useEffect(() => {\n if (autoFocus) textarea.current?.focus();\n }, [autoFocus]);\n\n // Text drafted in from another pane: appended once per nonce, never sent.\n const seenInsert = useRef<number | null>(insertRequest?.nonce ?? null);\n useEffect(() => {\n if (!insertRequest || insertRequest.nonce === seenInsert.current) return;\n seenInsert.current = insertRequest.nonce;\n const addition = insertRequest.text.replace(/\\s+$/, '');\n if (!addition) return;\n setDraft((current) => (current.trim() ? `${current.replace(/\\s+$/, '')}\\n\\n${addition}` : addition));\n placeCaret(Number.MAX_SAFE_INTEGER);\n }, [insertRequest]);\n\n // ⌘. stops the running turn from anywhere in the app (armed only while one runs).\n //\n // ⌘. is ALSO the shell's Resources toggle (3.11 C6), and both listened on\n // document in the bubble phase: one press cancelled the turn AND opened the\n // drawer. While a turn runs, stopping wins (the macOS cancel chord, and the\n // urgent one) — so this listens in the CAPTURE phase and claims the event;\n // the shell skips default-prevented keys. Inside a modal (the palette, a\n // confirmation, the floating Resources drawer) the keyboard belongs to that\n // modal and ⌘. keeps its drawer meaning, so a press meant to close the\n // drawer never cancels work.\n useEffect(() => {\n if (!running) return undefined;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key !== '.' || (!event.metaKey && !event.ctrlKey) || event.shiftKey || event.altKey) return;\n if (event.defaultPrevented) return;\n if (document.querySelector('[aria-modal=\"true\"]')) return;\n event.preventDefault();\n onStop();\n };\n document.addEventListener('keydown', onKey, true);\n return () => document.removeEventListener('keydown', onKey, true);\n }, [running, onStop]);\n\n // When the reply finishes, hand focus back to the box unless it went somewhere deliberate.\n useEffect(() => {\n if (running || !autoFocus) return;\n const active = document.activeElement;\n const ours = active !== null && active !== textarea.current && form.current?.contains(active) === true;\n if (active === null || active === document.body || !active.isConnected || ours) textarea.current?.focus();\n }, [running, autoFocus]);\n\n // Grow with content up to 40% of the viewport; shrink back when cleared.\n useEffect(() => {\n const node = textarea.current;\n if (!node) return;\n const viewport = typeof window === 'undefined' ? 0 : window.innerHeight;\n const max = viewport > 0 ? Math.round(viewport * MAX_HEIGHT_RATIO) : MAX_HEIGHT_FALLBACK_PX;\n node.style.height = 'auto';\n node.style.height = `${Math.min(node.scrollHeight, max)}px`;\n }, [text]);\n\n // ---- `@` and `/` ------------------------------------------------------------\n const trigger: ComposerTrigger | null = useMemo(() => {\n if (disabled || interim || caret === null) return null;\n const found = activeTrigger(draft, caret);\n if (!found || found.start === dismissedTrigger) return null;\n if (found.kind === 'mention' && !sessionId) return null;\n return found;\n }, [draft, caret, disabled, interim, dismissedTrigger, sessionId]);\n\n useEffect(() => {\n if (trigger?.kind !== 'mention' || !sessionId) {\n setFileSearchError(null);\n return undefined;\n }\n const abort = new AbortController();\n const timer = setTimeout(() => {\n searchSessionFiles(sessionId, trigger.query, abort.signal)\n .then((result) => {\n setFileMatches({ query: result.query, files: result.files, primaryRoot: result.primaryRoot ?? null });\n setFileSearchError(null);\n })\n .catch(() => { if (!abort.signal.aborted) setFileSearchError('Files could not be listed.'); });\n }, MENTION_DEBOUNCE_MS);\n return () => {\n clearTimeout(timer);\n abort.abort();\n };\n }, [trigger?.kind, trigger?.query, sessionId]);\n\n const commands: SlashCommand[] = trigger?.kind === 'command' ? matchSlashCommands(trigger.query) : [];\n const commandDisabled = useCallback((id: SlashCommand['id']): string | null => {\n if (id === 'handoff' && !onHandoff) return 'Use Hand off in the chat header’s Chat actions menu'; // named, not drawn as the U+22EF glyph, which is outside the font subset (c18)\n if (id === 'handoff' && handoffDisabledReason) return handoffDisabledReason;\n if ((id === 'plan' || id === 'effort' || id === 'model') && !view) return 'This server has no session controls yet';\n if (id === 'plan' && view && !view.options.permissionModes.find((o) => o.id === 'plan')?.available) return 'This seat has no plan mode';\n return null;\n }, [onHandoff, handoffDisabledReason, view]);\n\n const suggestItems: SuggestItem[] = trigger?.kind === 'command'\n ? commands.map((c) => ({ id: c.id, primary: c.title, secondary: c.description, disabledReason: commandDisabled(c.id) }))\n : trigger?.kind === 'mention'\n ? (fileMatches?.files ?? []).slice(0, 12).map((f) => ({ id: `${f.root}:${f.path}`, primary: f.path, secondary: fileMatches?.primaryRoot === f.root ? null : f.root }))\n : [];\n const suggestOpen = trigger !== null;\n const suggestStatus = trigger?.kind === 'mention'\n ? fileSearchError ?? (fileMatches === null || fileMatches.query !== trigger.query ? 'Searching…' : suggestItems.length === 0 ? 'No files match' : null)\n : null;\n const boundedActive = suggestItems.length === 0 ? 0 : Math.min(activeIndex, suggestItems.length - 1);\n\n useEffect(() => { setActiveIndex(0); }, [trigger?.kind, trigger?.query]);\n\n function syncCaret() {\n const node = textarea.current;\n if (node) setCaret(node.selectionStart === node.selectionEnd ? node.selectionStart : null);\n }\n\n function placeCaret(position: number) {\n requestAnimationFrame(() => {\n const node = textarea.current;\n if (!node) return;\n const at = Math.min(position, node.value.length);\n node.focus();\n node.setSelectionRange(at, at);\n setCaret(at);\n });\n }\n\n const openPicker = useCallback((id: PickerId) => {\n if (compact) {\n setSheetOpen(true);\n return;\n }\n setOpenRequest((current) => ({ ...current, [id]: current[id] + 1 }));\n }, [compact]);\n\n function runSlash(command: SlashCommand) {\n if (!trigger) return;\n const reason = commandDisabled(command.id);\n if (reason) {\n setNote({ text: reason, error: false });\n return;\n }\n // Every command but /compact consumes its own text.\n const without = `${draft.slice(0, trigger.start)}${draft.slice(trigger.end)}`.replace(/^\\s+/, '');\n switch (command.id) {\n case 'compact': {\n const next = applyCompletion(draft, trigger, '/compact');\n setDraft(next.text);\n placeCaret(next.caret);\n return;\n }\n case 'new':\n setDraft(without);\n onSeatChange({ seatId: seat.seatId, model: view?.controls.model ?? seat.model });\n return;\n case 'handoff':\n setDraft(without);\n onHandoff?.();\n return;\n case 'plan':\n setDraft(without);\n placeCaret(0);\n void changePermission('plan');\n return;\n case 'effort':\n case 'model':\n setDraft(without);\n openPicker(command.id);\n return;\n default:\n }\n }\n\n function acceptSuggestion(index: number) {\n if (!trigger) return;\n if (trigger.kind === 'command') {\n const command = commands[index];\n if (command) runSlash(command);\n return;\n }\n const match = fileMatches?.files[index];\n if (!match) return;\n const next = applyCompletion(draft, trigger, mentionFor(match, fileMatches?.primaryRoot ?? null));\n setDraft(next.text);\n placeCaret(next.caret);\n }\n\n // ---- controls ---------------------------------------------------------------\n const changePermission = useCallback(async (mode: VersePermissionMode) => {\n if (mode === 'bypass') {\n setBypassAsk(true);\n return;\n }\n const ok = await controls.update({ permissionMode: mode }, 'Change this chat’s permission mode.');\n if (ok) setNote({ text: running ? 'Permission mode changes from the next turn.' : `Permission mode: ${labelOf(view?.options.permissionModes, mode)}.`, error: false });\n }, [controls, running, view]);\n\n const changeModel = useCallback(async (model: string) => {\n const ok = await controls.update({ model }, 'Change this chat’s model.');\n if (ok) setNote({ text: running ? 'The new model takes over from the next turn.' : `Model: ${labelOf(view?.options.models, model)}.`, error: false });\n }, [controls, running, view]);\n\n const changeEffort = useCallback(async (effort: VerseEffort | null) => {\n const ok = await controls.update({ effort }, 'Change this chat’s reasoning effort.');\n if (ok) setNote({ text: running ? 'Effort changes from the next turn.' : `Effort: ${effort ? labelOf(view?.options.efforts, effort) : 'the CLI default'}.`, error: false });\n }, [controls, running, view]);\n\n async function confirmBypass() {\n setBypassAsk(false);\n const ok = await controls.update({ permissionMode: 'bypass', confirmBypass: true }, 'Bypass permissions for this chat.');\n if (ok) setNote({ text: 'Bypass is on for this chat — every check is skipped.', error: false });\n textarea.current?.focus();\n }\n\n // ---- attachments ------------------------------------------------------------\n const addFiles = useCallback((files: readonly File[]) => {\n if (files.length === 0 || disabled) return;\n if (attachBlocked) {\n setNote({ text: attachBlocked, error: true });\n return;\n }\n attachments.add(files, (ref) => {\n setDraft((current) => insertAttachmentRef(current, ref).text);\n });\n }, [attachments, attachBlocked, disabled]);\n\n async function removeChip(key: string) {\n const ref = await attachments.remove(key);\n if (ref) setDraft((current) => removeAttachmentRef(current, ref));\n }\n\n function onPaste(event: ClipboardEvent<HTMLTextAreaElement>) {\n const files = Array.from(event.clipboardData?.files ?? []);\n if (files.length === 0) return;\n event.preventDefault();\n addFiles(files);\n }\n\n function onDrop(event: DragEvent<HTMLFormElement>) {\n setDragging(false);\n const files = Array.from(event.dataTransfer?.files ?? []);\n if (files.length === 0) return;\n event.preventDefault();\n addFiles(files);\n }\n\n const openFilePicker = useCallback(() => {\n if (disabled) return;\n if (attachBlocked) {\n setNote({ text: attachBlocked, error: true });\n return;\n }\n fileInput.current?.click();\n }, [disabled, attachBlocked]);\n\n // ---- send / queue -------------------------------------------------------------\n const clearAfterSend = useCallback((value: string) => {\n const last = history.current[history.current.length - 1];\n if (last !== value) history.current = [...history.current, value];\n pushHistory(sessionId, value);\n setHistoryAt(-1);\n stashed.current = '';\n setDraft('');\n persistedDraft.current = '';\n latestDraft.current = '';\n saveDraft(sessionId, '');\n setInterim('');\n setSentHere(true);\n attachments.reset();\n }, [sessionId, attachments]);\n\n const submit = useCallback(async (mode: 'send' | 'stop-and-send' = 'send') => {\n if (disabled || sending || tooLong) return;\n const value = text.trim();\n if (!value) return;\n if (blockedByUpload) {\n setNote({ text: 'Wait for the attachments to finish uploading.', error: false });\n return;\n }\n setSending(true);\n setNote(null);\n try {\n if (running && queueAvailable) {\n if (mode !== 'stop-and-send' && queueFull) {\n setNote({ text: `Up to ${VERSE_QUEUE_MAX} follow-ups can wait — send or remove one first.`, error: true });\n return;\n }\n const result = await followUps.enqueue(value, { sendNow: mode === 'stop-and-send' });\n if (result) {\n clearAfterSend(value);\n if (mode === 'stop-and-send') setNote({ text: 'Stopping the turn — this message goes next.', error: false });\n }\n return;\n }\n if (running) return; // older server: no queue, the box just keeps the draft\n const ok = await onSend(value);\n if (ok) clearAfterSend(value);\n } finally {\n setSending(false);\n textarea.current?.focus();\n }\n }, [disabled, sending, tooLong, text, blockedByUpload, running, queueAvailable, queueFull, followUps, clearAfterSend, onSend]);\n\n async function editQueued(queueId: string, queuedText: string) {\n const ok = await followUps.remove(queueId);\n if (!ok) return;\n setDraft((current) => (current.trim() ? `${current.replace(/\\s+$/, '')}\\n\\n${queuedText}` : queuedText));\n placeCaret(Number.MAX_SAFE_INTEGER);\n }\n\n // ---- keys ----------------------------------------------------------------------\n // What is actually on screen: the sheet renders whenever it is open (and\n // there are pickers to show), and a picker menu reports itself closed when\n // a fold unmounts it — so Esc-to-stop is never disarmed by an overlay that is gone.\n const sheetShown = sheetOpen && view !== null;\n const anyOverlayOpen = suggestOpen || pickerMenuOpen || sheetShown || bypassAsk || gate.dialog.open;\n\n const runCommand = useCallback((id: ComposerCommandId) => {\n switch (id) {\n case 'composer.permission': openPicker('permission'); return;\n case 'composer.model': openPicker('model'); return;\n case 'composer.effort': openPicker('effort'); return;\n case 'composer.attach': openFilePicker(); return;\n // \"Run in cloud\" lives in the Chat settings sheet (ControlsSheet), with\n // its own disabled reasons and token gate; the palette only opens it.\n case 'composer.cloud':\n if (view) setSheetOpen(true);\n else setNote({ text: 'Run in cloud opens from a chat whose controls have loaded — open or start a chat first.', error: false });\n return;\n case 'composer.send': void submit('send'); return;\n case 'composer.stop-and-send': void submit('stop-and-send'); return;\n case 'composer.stop': if (running) onStop(); return;\n default:\n }\n }, [openPicker, openFilePicker, submit, running, onStop, view]);\n\n // Chat-scope keys (⇧⌘M/I/E, ⌘U) are live while the chat is on screen, not\n // only while the box has focus; a modal dialog anywhere owns the keys.\n useEffect(() => {\n if (disabled) return undefined;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.defaultPrevented || document.querySelector('[role=\"dialog\"][aria-modal=\"true\"]')) return;\n for (const id of ['composer.permission', 'composer.model', 'composer.effort', 'composer.attach'] as const) {\n if (pressesCommand(event, id)) {\n event.preventDefault();\n runCommand(id);\n return;\n }\n }\n };\n const onCommand = (event: Event) => {\n const id = (event as CustomEvent<{ id?: unknown }>).detail?.id;\n if (typeof id === 'string' && (COMPOSER_COMMAND_IDS as readonly string[]).includes(id)) runCommand(id as ComposerCommandId);\n };\n document.addEventListener('keydown', onKey);\n window.addEventListener(WORKBENCH_COMMAND_EVENT, onCommand);\n return () => {\n document.removeEventListener('keydown', onKey);\n window.removeEventListener(WORKBENCH_COMMAND_EVENT, onCommand);\n };\n }, [disabled, runCommand]);\n\n function recall(delta: -1 | 1): boolean {\n const list = history.current;\n if (list.length === 0) return false;\n if (historyAt === -1) {\n if (delta > 0) return false;\n stashed.current = draft;\n const next = list.length - 1;\n setHistoryAt(next);\n setDraft(list[next]!);\n setInterim('');\n return true;\n }\n const next = historyAt + delta;\n if (next < 0) return true;\n if (next >= list.length) {\n setHistoryAt(-1);\n setDraft(stashed.current);\n return true;\n }\n setHistoryAt(next);\n setDraft(list[next]!);\n return true;\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n const node = event.currentTarget;\n const collapsed = node.selectionStart === node.selectionEnd;\n const composing = event.nativeEvent.isComposing;\n\n if (suggestOpen && !composing) {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n if (suggestItems.length > 0) {\n event.preventDefault();\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex((boundedActive + step + suggestItems.length) % suggestItems.length);\n }\n return;\n }\n if ((event.key === 'Enter' && !event.shiftKey && !event.metaKey && !event.ctrlKey) || event.key === 'Tab') {\n if (suggestItems.length > 0) {\n event.preventDefault();\n acceptSuggestion(boundedActive);\n return;\n }\n if (event.key === 'Tab') return;\n }\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n setDismissedTrigger(trigger?.start ?? null);\n return;\n }\n }\n\n if (!composing && pressesCommand(event, 'composer.stop-and-send')) {\n event.preventDefault();\n void submit('stop-and-send');\n return;\n }\n if (event.key === 'Enter' && !event.shiftKey && !composing) {\n event.preventDefault();\n void submit('send');\n return;\n }\n if (!composing && event.key === 'Escape' && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) {\n // Esc stops the turn ONLY from an empty box with nothing open over it,\n // so it can never discard a draft or fight a menu for the key.\n if (running && !hasText && !anyOverlayOpen) {\n event.preventDefault();\n onStop();\n }\n return;\n }\n if (event.key === 'ArrowUp' && collapsed && (historyAt !== -1 || (node.selectionStart === 0 && !interim))) {\n if (recall(-1)) event.preventDefault();\n return;\n }\n if (event.key === 'ArrowDown' && collapsed && historyAt !== -1) {\n if (recall(1)) event.preventDefault();\n }\n }\n\n // ---- render ----------------------------------------------------------------------\n const placeholder = disabled\n ? disabledReason ?? 'Sending is disabled'\n : running\n ? (queueAvailable ? 'Queue a follow-up — it sends when this turn ends…' : 'Draft your next message…')\n : 'Ask anything — @ to add files, / for commands';\n\n const permissionValue = view?.controls.permissionMode ?? null;\n const bypassOn = permissionValue === 'bypass';\n const appliesNote = running ? 'Changes apply from the next turn.' : null;\n const pickers = view ? (variant: 'menu' | 'list') => ({\n permission: (\n <ControlMenu<VersePermissionMode> key=\"permission\" label=\"Permission mode\" variant={variant}\n valueLabel={PERMISSION_SHORT[view.controls.permissionMode]}\n valueTitle={labelOf(view.options.permissionModes, view.controls.permissionMode)} iconOnly={fold >= 3}\n options={view.options.permissionModes} value={view.controls.permissionMode}\n onChange={(mode) => { void changePermission(mode); }} disabled={controls.pending}\n shortcut={shortcutLabel('composer.permission')} openRequest={openRequest.permission}\n icon={PERMISSION_GLYPH[view.controls.permissionMode]} danger={bypassOn} note={appliesNote}\n onOpenChange={(open) => setOpenMenus((m) => ({ ...m, permission: open }))} />\n ),\n model: (\n <ControlMenu<string> key=\"model\" label=\"Model\" variant={variant}\n valueLabel={modelName}\n options={view.options.models} value={view.controls.model}\n onChange={(model) => { void changeModel(model); }} disabled={controls.pending}\n shortcut={shortcutLabel('composer.model')} openRequest={openRequest.model} note={appliesNote}\n onOpenChange={(open) => setOpenMenus((m) => ({ ...m, model: open }))} />\n ),\n // No control at all where the seat cannot set effort (local models, an old CLI).\n effort: effortOn ? (\n <ControlMenu<VerseEffort> key=\"effort\" label=\"Effort\" variant={variant}\n valueLabel={fold >= 1 ? effortName : `Effort: ${effortName}`} valueTitle={effortName}\n icon={fold >= 1 ? EFFORT_GLYPH : undefined}\n options={view.options.efforts} value={view.controls.effort}\n defaultOption={{ label: 'Default', description: 'The CLI decides', onSelect: () => { void changeEffort(null); } }}\n onChange={(effort) => { void changeEffort(effort); }} disabled={controls.pending}\n shortcut={shortcutLabel('composer.effort')} openRequest={openRequest.effort} note={appliesNote}\n onOpenChange={(open) => setOpenMenus((m) => ({ ...m, effort: open }))} />\n ) : null,\n }) : null;\n const wide = pickers?.('menu') ?? null;\n\n // No \"Sending…\": a label that changes width on Enter would shift the row. The button is disabled +\n // aria-busy instead, which Composer.module.css draws as the accent, pulsing, with a progress cursor —\n // never the quiet grey of an empty box (the text stays in the box until the send is accepted).\n const sendLabel = running ? (queueFull ? 'Queue full' : 'Queue') : locked ? 'Unlock & send' : 'Send';\n const returnKey = <span className={cstyles.sendKey} aria-hidden=\"true\">⏎</span>;\n\n return (\n <form ref={form} className={`${styles.composer} ${compact ? cstyles.composerCompact : ''}`}\n onSubmit={(event) => { event.preventDefault(); void submit('send'); }} aria-describedby={helpId}\n onDragOver={(event) => {\n if (disabled || !Array.from(event.dataTransfer?.types ?? []).includes('Files')) return;\n event.preventDefault();\n setDragging(true);\n }}\n onDragLeave={(event) => { if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDragging(false); }}\n onDrop={onDrop}>\n {disabled ? null : <ComposerSeatBlock seats={seats} seatId={seat.seatId} onSeatChange={onSeatChange} />}\n {portalInto(queueSlot, (\n <QueueRow queue={queue} running={running} disabled={disabled}\n onEdit={(id, queued) => { void editQueued(id, queued); }}\n onSendNow={(id) => { void followUps.sendNow(id); }}\n onRemove={(id) => { void followUps.remove(id); }} />\n ))}\n <div className={`${styles.box} ${listening ? styles.boxListening : ''} ${dragging ? cstyles.boxDragging : ''} ${bypassOn ? cstyles.boxBypass : ''}`}>\n {suggestOpen ? (\n <SuggestMenu id={suggestId} label={trigger?.kind === 'command' ? 'Commands' : 'Files in this chat’s folders'}\n items={suggestItems} activeIndex={boundedActive} status={suggestStatus}\n onPick={acceptSuggestion} onHover={setActiveIndex} />\n ) : null}\n <AttachmentChips drafts={attachments.drafts} onRemove={(key) => { void removeChip(key); }} disabled={disabled} />\n <textarea ref={textarea} className={styles.textarea} value={text} rows={1} placeholder={placeholder}\n aria-label=\"Message\" disabled={disabled}\n aria-autocomplete={suggestOpen ? 'list' : undefined}\n aria-controls={suggestOpen && suggestItems.length > 0 ? suggestId : undefined}\n aria-expanded={suggestOpen ? true : undefined}\n aria-activedescendant={suggestOpen && suggestItems.length > 0 ? suggestOptionId(suggestId, boundedActive) : undefined}\n onChange={(event) => {\n setInterim('');\n setHistoryAt(-1);\n setDraft(event.target.value);\n setDismissedTrigger(null);\n setCaret(event.target.selectionStart === event.target.selectionEnd ? event.target.selectionStart : null);\n }}\n onSelect={syncCaret} onClick={syncCaret} onKeyUp={(event) => { if (event.key.startsWith('Arrow') || event.key === 'Home' || event.key === 'End') syncCaret(); }}\n onBlur={() => setCaret(null)} onFocus={syncCaret}\n onPaste={onPaste} onKeyDown={onKeyDown} />\n <input ref={fileInput} type=\"file\" multiple hidden tabIndex={-1} aria-hidden=\"true\"\n onChange={(event) => { addFiles(Array.from(event.target.files ?? [])); event.target.value = ''; }} />\n <div ref={footerRow} className={`${styles.row} ${cstyles.footer}`}>\n <div className={cstyles.footerLeft}>\n <Tooltip label={attachBlocked ?? 'Attach files — or paste, or drop'} shortcut={attachBlocked ? undefined : shortcutLabel('composer.attach')}>\n <button type=\"button\" className={cstyles.iconButton} aria-label=\"Attach files\" aria-disabled={attachBlocked ? true : undefined}\n disabled={disabled} onClick={openFilePicker}>\n <IconPlus width={16} height={16} aria-hidden=\"true\" />\n </button>\n </Tooltip>\n <DictationButton disabled={disabled}\n onInterim={setInterim}\n onFinal={(chunk) => setDraft((current) => (current && !current.endsWith(' ') ? `${current} ${chunk}` : `${current}${chunk}`))}\n onListeningChange={setListening} />\n {!compact && wide ? wide.permission : null}\n </div>\n <div className={cstyles.footerRight}>\n <SeatChip seats={seats} seat={{ seatId: seat.seatId, model: view?.controls.model ?? seat.model }} engine={effectiveEngine}\n label={chipLabel} name={seatName} disabled={disabled} compact={compact || fold >= 2}\n {...(onContinueOn ? { onContinueOn } : {})} onNewChat={onSeatChange} />\n {compact ? null : wide ? wide.model : (\n // No session controls (an older or read-only server, or still loading): the model, stated once, not a picker.\n <span className={cstyles.controlStatic} title={`Model: ${modelName}`}>{modelName}</span>\n )}\n {!compact && wide ? wide.effort : null}\n {!compact ? <ContextRing contextTokens={contextTokens} contextWindow={contextWindow} autoCompactAt={autoCompactAt} exact={contextExact} engine={effectiveEngine} /> : null}\n {compact && view ? (\n <button ref={moreButton} type=\"button\" className={`${cstyles.iconButton} ${bypassOn ? cstyles.iconButtonDanger : ''}`}\n aria-haspopup=\"dialog\" aria-label={`Chat settings: ${labelOf(view.options.permissionModes, view.controls.permissionMode)}, ${modelName}`}\n title=\"Chat settings\" onClick={() => setSheetOpen(true)} disabled={disabled}>\n {MORE_DOTS}\n </button>\n ) : null}\n {running ? (\n <Tooltip label=\"Stop the running turn — or Esc from an empty box\" shortcut=\"⌘.\">\n <button key=\"stop\" type=\"button\" className={`${cstyles.iconButton} ${styles.stop}`} onClick={onStop} aria-label=\"Stop the running turn\">\n <span className={styles.stopIcon} aria-hidden=\"true\" />\n </button>\n </Tooltip>\n ) : null}\n {/* While a turn runs, Queue stays put — disabled until there is text — so typing never adds a button. */}\n {running && !queueAvailable ? null : (\n <button key={running ? 'queue' : 'send'} type=\"submit\" className={styles.send} disabled={!canSend} aria-busy={sending || undefined}\n aria-label={running ? (queueFull ? 'Queue is full' : 'Queue this message — it sends when the turn ends') : locked ? 'Send (unlocks first)' : 'Send message'}>\n {sendLabel}{returnKey}\n </button>\n )}\n </div>\n </div>\n </div>\n {cost ? (\n <p className={styles.cost} data-tone={cost.tone} role=\"status\">\n <span className={styles.costFigure}>≈{formatTokens(cost.draftTokens)}</span> tokens for this message —\n sending would reach <span className={styles.costFigure}>{cost.projectedPercent}%</span> of the context window.\n {costConsequence(cost)}\n <span className=\"visually-hidden\"> This is an estimate; the provider counts the real total.</span>\n </p>\n ) : null}\n <p id={helpId} className={`${styles.help} ${showHint || tooLong || listening || running || disabled || historyAt !== -1 || showHandoffHelp || note || controls.error || followUps.error ? '' : styles.helpQuiet}`}>\n {tooLong ? <span role=\"alert\" className={styles.helpError}>Message is over 64 KB — trim it before sending.</span>\n : followUps.error ? <span role=\"alert\" className={styles.helpError}>{followUps.error}</span>\n : controls.error && view ? <span role=\"alert\" className={styles.helpError}>{controls.error}</span>\n : note ? <span role={note.error ? 'alert' : 'status'} className={note.error ? styles.helpError : undefined}>{note.text}</span>\n : listening ? 'Listening… Esc stops dictation.'\n : disabled && disabledReason ? disabledReason\n : running ? (queueAvailable\n ? <><kbd>Enter</kbd> queues (sends when this turn ends) · <kbd>{shortcutLabel('composer.stop-and-send')}</kbd> stops and sends · <kbd>Esc</kbd> stops</>\n : <>Reply in progress — your draft stays here · <kbd>⌘.</kbd> or Stop interrupts the turn</>)\n : showHandoffHelp\n ? <>Handoff note drafted from the previous chat — review or edit it; nothing is spent until you press Send.</>\n : historyAt !== -1 ? <>Recalled message {historyAt + 1} of {history.current.length} · <kbd>↓</kbd> returns to your draft</>\n : showHint\n ? <><kbd>Enter</kbd> sends · <kbd>Shift</kbd>+<kbd>Enter</kbd> new line · <kbd>@</kbd> files · <kbd>/</kbd> commands · <kbd>↑</kbd> recalls</>\n : null}\n </p>\n {/* Mounted whenever there are pickers, not only while the row is\n compact: an open sheet must never be unmounted by a fold (it would\n drop focus to <body> and leave `sheetOpen` set with nothing on screen). */}\n {pickers ? (\n <ControlsSheet open={sheetOpen} onClose={() => { sheetReturnFocus.current = true; setSheetOpen(false); }}>\n {(() => {\n const list = pickers('list');\n return <>{list.permission}{list.model}{list.effort}</>;\n })()}\n </ControlsSheet>\n ) : null}\n <BypassConfirmDialog open={bypassAsk} chatLabel={chipLabel} running={running}\n onCancel={() => { setBypassAsk(false); textarea.current?.focus(); }} onConfirm={() => { void confirmBypass(); }} />\n <MutationTokenDialog {...gate.dialog} tokenLabel=\"Mutation token\" tokenHelp=\"the mutation token ashlr verse printed\" />\n </form>\n );\n}\n\n/** Render `node` into `host` when one is given (the queue's place above the live row), else in place. */\nfunction portalInto(host: HTMLElement | null, node: ReactNode) {\n return host ? createPortal(node, host) : node;\n}\n\nfunction labelOf<T extends string>(options: ReadonlyArray<{ id: T; label: string }> | undefined, id: T): string {\n return options?.find((o) => o.id === id)?.label ?? String(id);\n}\n\n/**\n * The sentence after the percentage: what actually happens if this is sent.\n * Worded from the compaction point when it is known, because that — not the\n * window — is where the agent starts losing detail.\n */\nexport function costConsequence(cost: CostHint): string {\n const bound = cost.exact ? '' : ' (The current size is an upper bound, so this may overstate it.)';\n if (cost.tone === 'over') return ` That is past the whole window — the CLI must compact first, or the turn fails. Continue in a fresh chat.${bound}`;\n if (cost.pastCompaction) return ` That reaches the auto-compaction point, so the CLI will summarise earlier turns during this reply. Continue in a fresh chat to keep full detail.${bound}`;\n if (cost.autoCompactAt !== null) {\n const left = Math.max(0, cost.autoCompactAt - cost.projectedTokens);\n return ` About ${formatTokens(left)} left before the CLI auto-compacts.${cost.tone === 'danger' ? ' Start a new chat to keep the agent sharp.' : ''}${bound}`;\n }\n return `${cost.tone === 'danger' ? ' Start a new chat to keep the agent sharp.' : ''}${bound}`;\n}\n","/**\n * routes/verse/chat/activity-model.ts — what a folded run of tool calls DID,\n * in one sentence (SPEC-310C §2 \"Activity groups\", unit C2).\n *\n * The 3.9 group line counted tool NAMES (\"6 tools · Read ×4, Edit ×2\"). A\n * name is the vendor's vocabulary, not the operator's: `Bash`, `exec_command`\n * and `run_shell` are all \"ran a command\", and nobody scanning a long session\n * wants to learn which CLI spells it how. So the group now speaks in actions,\n * using the same classification the file-activity summary uses\n * (tool-semantics `readToolFacts`). Drawn, the clauses are separated by a\n * middle dot; spoken, by commas:\n *\n * Ran 12 commands · read 8 files · edited 3 files (the row)\n * Ran 12 commands, read 8 files, edited 3 files; 1 failed, 1 running (its name)\n *\n * 3.10.1: every clause carries its noun. \"read 1\" after \"Ran 1 command\"\n * left the reader to work out what was read.\n *\n * and decides which member rows deserve to be on screen without a click: a\n * failed call is the one thing in a group the operator must see, and a\n * running call is the thing they are waiting on. Everything else stays folded\n * behind \"Show N more\".\n *\n * Pure — no React, no DOM — so the sentence and the auto-expand rule are\n * tested directly against transcript items.\n */\nimport type { ToolGroupItem, ToolGroupMember } from '../verse-store.js';\nimport { formatDuration } from '../verse-model.js';\nimport { actionForName, type ToolAction, type ToolFacts } from './tool-semantics.js';\n\n/** Per-action counts for one group. Thinking members are not counted: they are not work. */\nexport type ActionCounts = Record<ToolAction, number>;\n\nexport interface ActivitySummary {\n counts: ActionCounts;\n /** Tool calls only. */\n toolCount: number;\n failed: number;\n running: number;\n /** Wall time first→last member, when both are stamped; null = unknown (never 0). */\n spanMs: number | null;\n /** \"Ran 12 commands · read 8 files · edited 3 files\" — the WHAT, as drawn. */\n work: string;\n /** \"1 failed, 1 running\" — the STATE; empty when neither. */\n state: string;\n /** The whole line as a screen reader should hear it. */\n sentence: string;\n}\n\n/** Display order and wording; every clause names its noun. */\nconst CLAUSES: ReadonlyArray<{ action: ToolAction; verb: string; noun: string; plural: string }> = [\n { action: 'command', verb: 'ran', noun: 'command', plural: 'commands' },\n { action: 'read', verb: 'read', noun: 'file', plural: 'files' },\n { action: 'edit', verb: 'edited', noun: 'file', plural: 'files' },\n { action: 'create', verb: 'created', noun: 'file', plural: 'files' },\n { action: 'delete', verb: 'deleted', noun: 'file', plural: 'files' },\n { action: 'search', verb: 'searched', noun: 'time', plural: 'times' },\n { action: 'web', verb: 'fetched', noun: 'page', plural: 'pages' },\n { action: 'task', verb: 'ran', noun: 'subagent', plural: 'subagents' },\n { action: 'other', verb: 'used', noun: 'other tool', plural: 'other tools' },\n];\n\n/** The row's clause separator; the accessible sentence uses commas. */\nexport const WORK_SEPARATOR = ' · ';\n\nfunction emptyCounts(): ActionCounts {\n return { read: 0, edit: 0, create: 0, delete: 0, command: 0, search: 0, task: 0, web: 0, other: 0 };\n}\n\nfunction capitalize(text: string): string {\n return text.length === 0 ? text : `${text[0]!.toUpperCase()}${text.slice(1)}`;\n}\n\n/** The action a member performed: from its derived facts when present, else from its name. */\nfunction memberAction(member: Extract<ToolGroupMember, { kind: 'tool' }>, facts: ReadonlyMap<string, ToolFacts> | null): ToolAction {\n return facts?.get(member.toolUseId)?.action ?? actionForName(member.name);\n}\n\n/** A member failed: the tool said so, or its facts read a non-zero exit. */\nexport function memberFailed(member: ToolGroupMember, facts: ReadonlyMap<string, ToolFacts> | null = null): boolean {\n if (member.kind !== 'tool' || member.result === null) return false;\n return member.result.isError || facts?.get(member.toolUseId)?.failed === true;\n}\n\nexport function memberRunning(member: ToolGroupMember): boolean {\n return member.kind === 'tool' && member.result === null;\n}\n\n/** \"Ran 12 commands · read 8 files\" from counts (`separator` between clauses); \"\" when there is nothing. */\nexport function describeWork(counts: ActionCounts, separator: string = WORK_SEPARATOR): string {\n const parts: string[] = [];\n for (const clause of CLAUSES) {\n const n = counts[clause.action];\n if (n <= 0) continue;\n parts.push(`${clause.verb} ${n} ${n === 1 ? clause.noun : clause.plural}`);\n }\n return capitalize(parts.join(separator));\n}\n\nexport function summarizeActivity(group: Pick<ToolGroupItem, 'items' | 'spanMs'>, facts: ReadonlyMap<string, ToolFacts> | null = null): ActivitySummary {\n const counts = emptyCounts();\n let failed = 0;\n let running = 0;\n let toolCount = 0;\n for (const member of group.items) {\n if (member.kind !== 'tool') continue;\n toolCount += 1;\n counts[memberAction(member, facts)] += 1;\n if (memberRunning(member)) running += 1;\n else if (memberFailed(member, facts)) failed += 1;\n }\n const work = describeWork(counts);\n const stateParts: string[] = [];\n if (failed > 0) stateParts.push(`${failed} failed`);\n if (running > 0) stateParts.push(`${running} running`);\n const state = stateParts.join(', ');\n const spanMs = group.spanMs !== null && group.spanMs > 0 ? group.spanMs : null;\n const sentence = [describeWork(counts, ', '), state, spanMs !== null && running === 0 ? formatDuration(spanMs) : '']\n .filter((part) => part.length > 0)\n .join('; ');\n return { counts, toolCount, failed, running, spanMs, work, state, sentence };\n}\n\nexport type ActivityView = 'collapsed' | 'focus' | 'all';\n\n/**\n * How a group opens when the operator has not chosen: on its FOCUS rows\n * (failed or running members) when it has any, folded otherwise. A group\n * that finishes clean folds itself again — the operator's own toggle, once\n * made, wins over both (the component tracks that).\n */\nexport function defaultActivityView(summary: Pick<ActivitySummary, 'failed' | 'running'>): ActivityView {\n return summary.failed > 0 || summary.running > 0 ? 'focus' : 'collapsed';\n}\n\n/** Members shown in `focus` view, in order, and how many that leaves behind \"Show N more\". */\nexport function focusMembers(\n group: Pick<ToolGroupItem, 'items'>,\n facts: ReadonlyMap<string, ToolFacts> | null = null,\n): { shown: ToolGroupMember[]; hidden: number } {\n const shown = group.items.filter((m) => memberRunning(m) || memberFailed(m, facts));\n return { shown, hidden: group.items.length - shown.length };\n}\n","/**\n * routes/verse/chat/ActivityGroup.tsx — a run of tool calls as ONE row\n * (SPEC-310C §2, unit C2):\n *\n * ▸ Ran 12 commands · read 8 files · edited 3 files · 1 failed, 1 running · 2m 14s\n *\n * Three views, not two, because \"folded\" and \"everything\" both fail the\n * common case:\n *\n * collapsed the line alone — a clean, finished run is one line of prose;\n * focus the line plus ONLY the rows that matter right now — the calls\n * that failed and the ones still running (each with a live\n * timer) — and \"Show 11 more\" for the rest;\n * all every member.\n *\n * Until the operator clicks, the view follows the run: it opens on focus the\n * moment a call fails or while one runs, and folds itself when the run\n * finishes clean. Once they click, their choice wins for the life of the row.\n *\n * Failure is said in words and with the left rule, never by colour alone\n * (DESIGN §6). The line is a real button with aria-expanded, so the whole\n * disclosure works from the keyboard and reads as one sentence.\n */\nimport { memo, useEffect, useId, useMemo, useState, type ReactNode } from 'react';\nimport type { ToolGroupItem, ToolGroupMember } from '../verse-store.js';\nimport { formatDuration } from '../verse-model.js';\nimport { defaultActivityView, focusMembers, summarizeActivity, type ActivityView } from './activity-model.js';\nimport { LiveTimer } from './LiveTimer.js';\nimport { toolAnchorId, type ToolFacts } from './tool-semantics.js';\nimport styles from './ActivityGroup.module.css';\n\n// ---------------------------------------------------------------------------\n// Reveal: a jump to a call folded inside a group\n// ---------------------------------------------------------------------------\n//\n// The file-activity rows and \"jump to the first failure\" scroll to a tool\n// call's DOM anchor. A folded group does not render its members (a long\n// session would otherwise carry thousands of hidden cards), so the anchor\n// does not exist until the group opens. The transcript asks here first; the\n// group that holds the call opens itself to \"all\", and the transcript retries\n// the scroll on the next frame.\n\nconst revealListeners = new Set<(anchorId: string) => boolean>();\n\n/** Ask every mounted group to open if it holds `anchorId`. True when one did. */\nexport function revealAnchorInGroups(anchorId: string): boolean {\n let claimed = false;\n for (const listener of [...revealListeners]) if (listener(anchorId)) claimed = true;\n return claimed;\n}\n\nexport interface ActivityGroupProps {\n item: ToolGroupItem;\n facts: ReadonlyMap<string, ToolFacts>;\n /** Renders one member (a tool card or a reasoning block) — the transcript owns those components and their memoization. */\n renderMember: (member: ToolGroupMember) => ReactNode;\n}\n\nexport function ActivityGroupView({ item, facts, renderMember }: ActivityGroupProps) {\n const summary = useMemo(() => summarizeActivity(item, facts), [item, facts]);\n const [chosen, setChosen] = useState<ActivityView | null>(null);\n const view: ActivityView = chosen ?? defaultActivityView(summary);\n const bodyId = useId();\n\n const focus = useMemo(() => focusMembers(item, facts), [item, facts]);\n // \"focus\" with nothing left to focus on (the run finished clean) folds.\n const effective: ActivityView = view === 'focus' && focus.shown.length === 0 ? 'collapsed' : view;\n const members = effective === 'all' ? item.items : effective === 'focus' ? focus.shown : [];\n\n function toggle() {\n setChosen(effective === 'collapsed' ? (focus.shown.length > 0 ? 'focus' : 'all') : 'collapsed');\n }\n\n useEffect(() => {\n const listener = (anchorId: string) => {\n const holds = item.items.some((m) => m.kind === 'tool' && toolAnchorId(m.toolUseId) === anchorId);\n if (holds) setChosen('all');\n return holds;\n };\n revealListeners.add(listener);\n return () => { revealListeners.delete(listener); };\n }, [item]);\n\n const running = summary.running > 0;\n const firstAt = item.items[0]?.at ?? null;\n const spoken = summary.sentence || `${summary.toolCount} tool calls`;\n\n return (\n <div className={styles.group} data-view={effective} data-failed={summary.failed > 0 || undefined}\n data-running={running || undefined} data-state-key={`activity:${item.key}`}>\n <button type=\"button\" className={styles.line} aria-expanded={effective !== 'collapsed'}\n aria-controls={bodyId} aria-label={spoken} onClick={toggle}>\n <span className={styles.chevron} aria-hidden=\"true\" />\n <span className={styles.work} aria-hidden=\"true\">{summary.work || `${summary.toolCount} tool calls`}</span>\n {summary.state ? (\n <span className={styles.state} aria-hidden=\"true\">\n {summary.failed > 0 ? <span className={styles.failed}>{summary.failed} failed</span> : null}\n {summary.failed > 0 && running ? <span className={styles.sep}>, </span> : null}\n {running ? <span className={styles.running}><span className={styles.pulse} />{summary.running} running</span> : null}\n </span>\n ) : null}\n <span className={styles.time} aria-hidden=\"true\">\n {running\n ? <LiveTimer since={firstAt} />\n : summary.spanMs !== null ? formatDuration(summary.spanMs) : null}\n </span>\n </button>\n <div id={bodyId} className={styles.body} hidden={effective === 'collapsed'}>\n {effective === 'collapsed' ? null : (\n <>\n <ol className={styles.list}>\n {members.map((member) => (\n <li key={member.key} data-member={member.kind}>{renderMember(member)}</li>\n ))}\n </ol>\n {effective === 'focus' && focus.hidden > 0 ? (\n <button type=\"button\" className={styles.more} onClick={() => setChosen('all')}>\n Show {focus.hidden} more\n </button>\n ) : null}\n {effective === 'all' && focus.shown.length > 0 && focus.hidden > 0 ? (\n <button type=\"button\" className={styles.more} onClick={() => setChosen('focus')}>\n Show only {summary.failed > 0 ? 'failures' : 'running'}\n </button>\n ) : null}\n </>\n )}\n </div>\n </div>\n );\n}\n\n/** Memoized on identity; the transcript wraps ActivityGroupView with a content comparator instead. */\nexport const ActivityGroup = memo(ActivityGroupView);\n","/**\n * routes/verse/chat/turn-model.ts — a session as a list of TURNS.\n *\n * `verse-transcript.groupTranscriptItems()` already folds runs of tool calls into\n * one summary row; what it does not do is say where one exchange ends and\n * the next begins. Everything this file exists for needs that boundary: the\n * per-turn file-activity summary (the blast radius of one ask), the outline\n * that makes a fifty-turn session navigable, keyboard movement between\n * turns, and \"jump to the first error in this turn\".\n *\n * Pure over `groupTranscriptItems()`'s output — no React, no DOM, no\n * fetching — so the numbers in the summary can be tested directly against an\n * event log.\n */\nimport { parseUnifiedDiff } from '../../inbox/diff-parser.js';\nimport type { ToolGroupItem, TranscriptItem, TranscriptRenderItem } from '../verse-store.js';\nimport { readToolFacts, type ToolAction, type ToolFacts } from './tool-semantics.js';\n\nexport type TurnStatus = 'running' | 'ok' | 'error' | 'stopped';\n\n/**\n * Work that survives a transcript rebuild.\n *\n * `buildTurns` runs on EVERY streamed token: `useVerseSession` rebuilds\n * `state.events` per event, `buildTranscript` allocates fresh item objects from\n * it, and both of Transcript's memos therefore miss. Without this, every delta\n * re-derived the semantics of every tool call in the session — including a full\n * LCS diff per Edit payload, which allocates an `Int32Array` up to the 1200-line\n * cap. Fifty moderate edits cost hundreds of milliseconds of main-thread work\n * per frame, on the one surface whose frame budget is already spoken for.\n *\n * Caching is sound because `toolUseId` is a stable identity and a tool call's\n * `{name, input, result}` is immutable ONCE ITS RESULT HAS LANDED\n * (`verse-transcript.buildTranscript` replaces the item object exactly once, when the\n * `tool-result` event arrives). A cached entry derived while the call was still\n * pending is therefore the only one that can go stale, and it is the only one\n * recomputed.\n */\nexport interface TurnCache {\n facts: Map<string, ToolFacts>;\n /** `countDiffLines` over the same immutable diff text. */\n counts: Map<string, { additions: number; deletions: number }>;\n /** Flattened searchable text per tool call — the expensive half of the index. */\n toolText: Map<string, string>;\n}\n\nexport function createTurnCache(): TurnCache {\n return { facts: new Map(), counts: new Map(), toolText: new Map() };\n}\n\n/**\n * Facts for one tool call, reusing the cached instance whenever it is still\n * valid. Exported so a test can assert the second build recomputes nothing.\n */\nexport function cachedToolFacts(\n tool: { toolUseId: string; name: string; input: unknown; result: { output: string; isError: boolean } | null },\n cache: TurnCache | null,\n): ToolFacts {\n const hit = cache?.facts.get(tool.toolUseId);\n // A pending entry must be recomputed once the result lands; anything else is\n // derived from data that can no longer change.\n if (hit && !(hit.pending && tool.result !== null)) return hit;\n const fact = readToolFacts({ name: tool.name, input: tool.input, result: tool.result });\n if (cache) {\n cache.facts.set(tool.toolUseId, fact);\n cache.counts.delete(tool.toolUseId);\n cache.toolText.delete(tool.toolUseId);\n }\n return fact;\n}\n\nexport interface TurnFileEntry {\n path: string;\n reads: number;\n edits: number;\n creates: number;\n deletes: number;\n /** The strongest thing that happened to it: delete > create > edit > read. */\n action: Extract<ToolAction, 'read' | 'edit' | 'create' | 'delete'>;\n /** Tool call to scroll to when the row is clicked. */\n anchorToolUseId: string;\n /** Summed from the diffs the calls carried; 0 when none did. */\n additions: number;\n deletions: number;\n /** At least one call touching this file failed. */\n failed: boolean;\n}\n\nexport interface TurnBlock {\n key: string;\n turnId: string | null;\n items: TranscriptRenderItem[];\n /** The ask that opened the turn, for the outline. */\n prompt: string | null;\n /** ISO stamp of the first item. */\n at: string;\n files: TurnFileEntry[];\n toolCount: number;\n commandCount: number;\n /** Failing tool calls plus logged errors. */\n errorCount: number;\n /** Id of the first failing thing in the turn, for jump-to-error. */\n firstErrorAnchor: string | null;\n status: TurnStatus;\n durationMs: number | null;\n}\n\nexport interface TurnModel {\n turns: TurnBlock[];\n /** Facts keyed by toolUseId, so the cards do not re-derive them. */\n facts: Map<string, ToolFacts>;\n /** Every error anchor in document order — the transcript-level jump list. */\n errorAnchors: string[];\n}\n\n/** DOM id for a note (error / stopped / turn-done) so it can be scrolled to. */\nexport function noteAnchorId(key: string): string {\n return `verse-note-${key.replace(/[^A-Za-z0-9_-]/g, '')}`;\n}\n\n/** DOM id for one turn section. */\nexport function turnAnchorId(key: string): string {\n return `verse-turn-${key.replace(/[^A-Za-z0-9_-]/g, '')}`;\n}\n\nconst STRENGTH: Record<TurnFileEntry['action'], number> = { read: 0, edit: 1, create: 2, delete: 3 };\n\n/**\n * Additions/deletions for a unified diff, summed across every file in it.\n *\n * This was a flat scan that skipped any line starting with `---` or `+++` as a\n * file header. Inside a hunk those prefixes are CONTENT, not structure: a\n * deleted line reading `-- a/x` is emitted as `--- a/x`, a Markdown `---` rule\n * deleted is `----`, and a diff of a diff — what editing a `.patch` fixture or\n * running `git diff` over one produces — is made almost entirely of them. Every\n * such line was dropped, so the `+N −M` on the collapsed tool card undercounted\n * exactly the changes that are hardest to eyeball.\n *\n * Delegating to the shared parser (`routes/inbox/diff-parser.ts`) fixes that\n * and retires the second diff dialect: `DiffBlock` renders `file.additions` /\n * `file.deletions` out of this same parse, so the badge on the summary line and\n * the stats in the expanded block are now the same numbers by construction\n * rather than by two implementations happening to agree.\n *\n * Cost is real but bounded and paid once: `cachedDiffCounts` memoizes by\n * `toolUseId`, and the text is capped at `MAX_FACT_TEXT_CHARS` upstream.\n */\nexport function countDiffLines(text: string): { additions: number; deletions: number } {\n let additions = 0;\n let deletions = 0;\n for (const file of parseUnifiedDiff(text).files) {\n additions += file.additions;\n deletions += file.deletions;\n }\n return { additions, deletions };\n}\n\n/** `countDiffLines` over text that cannot change once cached. */\nfunction cachedDiffCounts(\n toolUseId: string,\n text: string,\n cache: TurnCache | null,\n): { additions: number; deletions: number } {\n const hit = cache?.counts.get(toolUseId);\n if (hit) return hit;\n const counts = countDiffLines(text);\n cache?.counts.set(toolUseId, counts);\n return counts;\n}\n\nfunction toolMembers(item: TranscriptRenderItem): Array<Extract<TranscriptItem, { kind: 'tool' }>> {\n if (item.kind === 'tool') return [item];\n if (item.kind === 'toolGroup') {\n return (item as ToolGroupItem).items.filter((m): m is Extract<TranscriptItem, { kind: 'tool' }> => m.kind === 'tool');\n }\n return [];\n}\n\n/**\n * A new turn opens on a `user` item, or on any item whose turnId differs\n * from the one being accumulated. Items that arrive before the first user\n * message (a resumed session's tail, a session-level error with a null\n * turnId) collect into a leading block rather than being dropped.\n */\nexport function buildTurns(\n items: readonly TranscriptRenderItem[],\n cache: TurnCache | null = null,\n): TurnModel {\n const facts = new Map<string, ToolFacts>();\n const errorAnchors: string[] = [];\n const turns: TurnBlock[] = [];\n\n let current: TranscriptRenderItem[] = [];\n const flush = () => {\n if (current.length === 0) return;\n turns.push(makeTurn(current, facts, errorAnchors, cache));\n current = [];\n };\n\n let turnId: string | null | undefined;\n for (const item of items) {\n const itemTurn = 'turnId' in item ? item.turnId : null;\n const boundary = item.kind === 'user' || (turnId !== undefined && itemTurn !== null && itemTurn !== turnId);\n if (boundary) flush();\n if (item.kind === 'user' || turnId === undefined || itemTurn !== null) turnId = itemTurn;\n current.push(item);\n }\n flush();\n\n return { turns, facts, errorAnchors };\n}\n\nfunction makeTurn(\n items: TranscriptRenderItem[],\n facts: Map<string, ToolFacts>,\n errorAnchors: string[],\n cache: TurnCache | null,\n): TurnBlock {\n const first = items[0]!;\n const byPath = new Map<string, TurnFileEntry>();\n let toolCount = 0;\n let commandCount = 0;\n let errorCount = 0;\n let firstErrorAnchor: string | null = null;\n let status: TurnStatus = 'running';\n let durationMs: number | null = null;\n let prompt: string | null = null;\n\n const noteError = (anchor: string) => {\n errorCount++;\n if (!firstErrorAnchor) firstErrorAnchor = anchor;\n errorAnchors.push(anchor);\n };\n\n for (const item of items) {\n if (item.kind === 'user' && prompt === null) prompt = item.text;\n\n for (const tool of toolMembers(item)) {\n toolCount++;\n const fact = cachedToolFacts(tool, cache);\n facts.set(tool.toolUseId, fact);\n if (fact.action === 'command') commandCount++;\n if (fact.failed) noteError(`verse-tool-${tool.toolUseId.replace(/[^A-Za-z0-9_-]/g, '')}`);\n\n const action = fact.action;\n if (action !== 'read' && action !== 'edit' && action !== 'create' && action !== 'delete') continue;\n const counts = fact.diff ? cachedDiffCounts(tool.toolUseId, fact.diff.text, cache) : { additions: 0, deletions: 0 };\n for (const path of fact.paths) {\n const entry = byPath.get(path) ?? {\n path,\n reads: 0, edits: 0, creates: 0, deletes: 0,\n action,\n anchorToolUseId: tool.toolUseId,\n additions: 0, deletions: 0,\n failed: false,\n };\n if (action === 'read') entry.reads++;\n else if (action === 'edit') entry.edits++;\n else if (action === 'create') entry.creates++;\n else entry.deletes++;\n if (STRENGTH[action] > STRENGTH[entry.action]) {\n entry.action = action;\n // The row should land on the call that CHANGED the file, not on the\n // read that happened to come first.\n entry.anchorToolUseId = tool.toolUseId;\n }\n if (action !== 'read') {\n entry.additions += counts.additions;\n entry.deletions += counts.deletions;\n }\n if (fact.failed) entry.failed = true;\n byPath.set(path, entry);\n }\n }\n\n switch (item.kind) {\n case 'error':\n noteError(noteAnchorId(item.key));\n status = 'error';\n break;\n case 'cancelled':\n status = 'stopped';\n break;\n case 'turn-done':\n durationMs = item.durationMs > 0 ? item.durationMs : durationMs;\n // A stop already explained itself; `ok:false` after it is not a\n // second failure (the same rule Transcript's `explained` set uses).\n if (item.ok) status = status === 'running' ? 'ok' : status;\n else if (status === 'running') {\n status = 'error';\n noteError(noteAnchorId(item.key));\n }\n break;\n default:\n break;\n }\n }\n\n return {\n key: first.key,\n turnId: 'turnId' in first ? first.turnId : null,\n items,\n prompt,\n at: first.at,\n // Strongest action first. The map is in first-touch order, and an agentic\n // turn reads widely before editing narrowly — so in read order the three\n // edits in a 40-file turn sit behind \"Show 35 more files\", hiding exactly\n // the rows whose `+N −M` justified the summary in the first place.\n files: [...byPath.values()].sort(\n (a, b) =>\n STRENGTH[b.action] - STRENGTH[a.action] ||\n b.additions + b.deletions - (a.additions + a.deletions) ||\n a.path.localeCompare(b.path),\n ),\n toolCount,\n commandCount,\n errorCount,\n firstErrorAnchor,\n status,\n durationMs,\n };\n}\n\n/** `12 files · 4 edited, 8 read` — the one-line form for a collapsed summary. */\nexport function describeFiles(files: readonly TurnFileEntry[]): string {\n if (files.length === 0) return '';\n const counts = { edited: 0, created: 0, deleted: 0, read: 0 };\n for (const file of files) {\n if (file.action === 'edit') counts.edited++;\n else if (file.action === 'create') counts.created++;\n else if (file.action === 'delete') counts.deleted++;\n else counts.read++;\n }\n const parts: string[] = [];\n if (counts.created) parts.push(`${counts.created} created`);\n if (counts.edited) parts.push(`${counts.edited} edited`);\n if (counts.deleted) parts.push(`${counts.deleted} deleted`);\n if (counts.read) parts.push(`${counts.read} read`);\n return parts.join(', ');\n}\n\n// ---------------------------------------------------------------------------\n// In-session search\n// ---------------------------------------------------------------------------\n\n/**\n * Everything in a turn that is worth searching, flattened once and cached on\n * the turn object. A rebuilt transcript produces new objects, so the WeakMap\n * lets the old strings go with them.\n */\nconst SEARCH_TEXT = new WeakMap<TurnBlock, string>();\n\n/** Per tool call — a build log should not make one turn megabytes of index. */\nconst TOOL_TEXT_LIMIT = 4000;\n\n/**\n * One tool call flattened for search. Cached by `toolUseId` under the same\n * immutability rule as the facts: `JSON.stringify` of a 4000-char payload per\n * call per keystroke is what made an active query cost the whole session.\n */\nfunction toolSearchText(\n item: Extract<TranscriptItem, { kind: 'tool' }>,\n cache: TurnCache | null,\n): string {\n const hit = cache?.toolText.get(item.toolUseId);\n if (hit !== undefined) return hit;\n const parts: string[] = [item.name];\n if (typeof item.input === 'string') parts.push(item.input.slice(0, TOOL_TEXT_LIMIT));\n else if (item.input) {\n try {\n parts.push(JSON.stringify(item.input).slice(0, TOOL_TEXT_LIMIT));\n } catch {\n /* a payload that will not stringify simply is not searchable */\n }\n }\n if (item.result) parts.push(item.result.output.slice(0, TOOL_TEXT_LIMIT));\n const text = parts.join('\\n');\n // Only cache a finished call: a pending one's output is still to come.\n if (cache && item.result) cache.toolText.set(item.toolUseId, text);\n return text;\n}\n\nexport function turnSearchText(turn: TurnBlock, cache: TurnCache | null = null): string {\n const cached = SEARCH_TEXT.get(turn);\n if (cached !== undefined) return cached;\n const parts: string[] = [];\n const visit = (item: TranscriptRenderItem | TranscriptItem): void => {\n switch (item.kind) {\n case 'user':\n case 'assistant':\n case 'thinking':\n parts.push(item.text);\n break;\n case 'error':\n parts.push(item.message);\n break;\n case 'tool':\n parts.push(toolSearchText(item, cache));\n break;\n case 'toolGroup':\n for (const member of (item as ToolGroupItem).items) visit(member);\n break;\n default:\n break;\n }\n };\n for (const item of turn.items) visit(item);\n const text = parts.join('\\n');\n SEARCH_TEXT.set(turn, text);\n return text;\n}\n\nexport interface TurnMatch {\n turnKey: string;\n /** Text around the first hit, for the outline row. */\n snippet: string;\n}\n\nconst SNIPPET_PAD = 48;\n\n/** Case-insensitive substring search across every turn, in document order. */\nexport function searchTurns(\n turns: readonly TurnBlock[],\n query: string,\n cache: TurnCache | null = null,\n): TurnMatch[] {\n const needle = query.trim().toLowerCase();\n if (needle.length === 0) return [];\n const out: TurnMatch[] = [];\n for (const turn of turns) {\n const haystack = turnSearchText(turn, cache);\n const at = haystack.toLowerCase().indexOf(needle);\n if (at === -1) continue;\n const from = Math.max(0, at - SNIPPET_PAD);\n const to = Math.min(haystack.length, at + needle.length + SNIPPET_PAD);\n const snippet = `${from > 0 ? '…' : ''}${haystack.slice(from, to).replace(/\\s+/g, ' ').trim()}${to < haystack.length ? '…' : ''}`;\n out.push({ turnKey: turn.key, snippet });\n }\n return out;\n}\n\n/** Turn title for the outline: the ask, trimmed to one line. */\nexport function turnTitle(turn: TurnBlock, max = 72): string {\n const source = turn.prompt?.replace(/\\s+/g, ' ').trim();\n if (source) return source.length > max ? `${source.slice(0, max - 1)}…` : source;\n if (turn.toolCount > 0) return `${turn.toolCount} tool call${turn.toolCount === 1 ? '' : 's'}`;\n return 'Session note';\n}\n","/**\n * routes/verse/chat/chapter-model.ts — the ChapterRail's data: one tick per\n * turn the operator started, plus the moments the conversation changed shape\n * (SPEC-310C §2 \"ChapterRail\", unit C2).\n *\n * A fifty-turn chat is a long scroll with no landmarks; the outline\n * (TranscriptNav) lists turns but takes the width of the transcript to do\n * it. The rail is the glanceable version: a minimap down the right edge\n * where each ask is a tick — red if its turn failed, amber while it runs —\n * and where the context was compacted, the chat was recovered, or it began\n * as a handoff, a marker says so. Position is by ORDER (turn i of n), not by\n * pixel: the rail answers \"where in the conversation\", and a turn with a\n * 2,000-line build log should not own half of it.\n *\n * Pure over the transcript's TurnBlocks.\n */\nimport type { TurnBlock, TurnStatus } from './turn-model.js';\nimport { turnTitle } from './turn-model.js';\n\nexport type ChapterMarkerKind = 'compaction' | 'recovered' | 'handoff';\n\nexport interface ChapterTick {\n /** The turn's key — what the transcript jumps to. */\n turnKey: string;\n /** 0-based ordinal among ticks. */\n index: number;\n status: TurnStatus;\n /** The ask, one line, for the hover card. */\n title: string;\n /** 0–1 down the rail. */\n position: number;\n /** Markers that fall inside this turn, in order. */\n markers: ChapterMarkerKind[];\n}\n\nexport interface ChapterModel {\n ticks: ChapterTick[];\n /** The chat began as a handoff: a marker above the first tick. */\n handoff: boolean;\n counts: { failed: number; running: number; compactions: number; recoveries: number };\n}\n\n/** Markers carried by a turn's items. Compaction/recovered items can sit in any turn. */\nfunction markersOf(turn: TurnBlock): ChapterMarkerKind[] {\n const out: ChapterMarkerKind[] = [];\n for (const item of turn.items) {\n if (item.kind === 'compaction') out.push('compaction');\n else if (item.kind === 'recovered') out.push('recovered');\n }\n return out;\n}\n\n/**\n * Ticks for turns with an ask. A turn with no prompt (the notes before the\n * first ask, a truncated head) carries no tick of its own; its markers ride\n * on the next tick so a compaction is never dropped from the rail.\n */\nexport function buildChapters(turns: readonly TurnBlock[], opts: { handoff?: boolean } = {}): ChapterModel {\n const ticks: ChapterTick[] = [];\n let carried: ChapterMarkerKind[] = [];\n const counts = { failed: 0, running: 0, compactions: 0, recoveries: 0 };\n for (const turn of turns) {\n const markers = [...carried, ...markersOf(turn)];\n for (const marker of markersOf(turn)) {\n if (marker === 'compaction') counts.compactions += 1;\n else if (marker === 'recovered') counts.recoveries += 1;\n }\n if (turn.prompt === null) {\n carried = markers;\n continue;\n }\n carried = [];\n if (turn.status === 'error') counts.failed += 1;\n if (turn.status === 'running') counts.running += 1;\n ticks.push({ turnKey: turn.key, index: ticks.length, status: turn.status, title: turnTitle(turn, 120), position: 0, markers });\n }\n // Markers after the last ask (a compaction that closed the log) attach to it.\n if (carried.length > 0 && ticks.length > 0) ticks[ticks.length - 1]!.markers.push(...carried);\n const n = ticks.length;\n for (const tick of ticks) tick.position = n <= 1 ? 0 : tick.index / (n - 1);\n return { ticks, handoff: opts.handoff === true, counts };\n}\n\nexport const CHAPTER_STATUS_WORD: Readonly<Record<TurnStatus, string>> = {\n running: 'running',\n ok: 'done',\n error: 'failed',\n stopped: 'stopped',\n};\n\nexport const CHAPTER_MARKER_WORD: Readonly<Record<ChapterMarkerKind, string>> = {\n compaction: 'context compacted',\n recovered: 'recovered',\n handoff: 'continued from another chat',\n};\n\n/** The accessible name of one tick: \"Turn 4 of 12, failed: Fix the login test (context compacted)\". */\nexport function describeTick(tick: ChapterTick, total: number): string {\n const extra = tick.markers.length > 0 ? ` (${[...new Set(tick.markers)].map((m) => CHAPTER_MARKER_WORD[m]).join(', ')})` : '';\n return `Turn ${tick.index + 1} of ${total}, ${CHAPTER_STATUS_WORD[tick.status]}: ${tick.title}${extra}`;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming cost: the rail must not re-render per token\n// ---------------------------------------------------------------------------\n\nconst settledSignatures = new WeakMap<TurnBlock, string>();\n\nfunction turnSignature(turn: TurnBlock): string {\n const cached = settledSignatures.get(turn);\n if (cached !== undefined) return cached;\n let markers = 0;\n for (const item of turn.items) if (item.kind === 'compaction' || item.kind === 'recovered') markers += 1;\n const sig = `${turn.key}:${turn.status}:${markers}:${turn.prompt === null ? 0 : turn.prompt.length}`;\n // A settled turn's TurnBlock never changes (the segment cache hands back the\n // same object), so its signature is computed once; the live turn's each time.\n if (turn.status !== 'running') settledSignatures.set(turn, sig);\n return sig;\n}\n\n/**\n * What the rail draws depends on each turn's key, status, markers and ask —\n * none of which a streamed token changes. The transcript rebuilds its turn\n * list on every token, so it compares this signature (cheap: cached per\n * settled turn) and rebuilds the rail's model only when it moves.\n */\nexport function chaptersSignature(turns: readonly TurnBlock[], handoff: boolean): string {\n let out = handoff ? 'h' : '-';\n for (const turn of turns) out += `|${turnSignature(turn)}`;\n return out;\n}\n","/**\n * routes/verse/chat/ChapterRail.tsx — the transcript's minimap down its\n * right edge (SPEC-310C §2, unit C2). Model: chapter-model.ts.\n *\n * ─ a turn ━ red: it failed ━ amber: running\n * ◆ context compacted ↺ recovered ⤷ continued from another chat\n * (the three markers are inline SVG — see MARKER_PATH)\n *\n * Hover (or keyboard focus) shows the ask; click jumps to it.\n *\n * ONE tab stop, not one per turn: a 60-turn chat must not put 60 stops\n * between the transcript and the composer. The rail is a toolbar-style\n * group with a roving tabindex — ↑/↓ (and Home/End) move between ticks,\n * Enter or Space jumps. Every tick has a full accessible name (\"Turn 4 of\n * 12, failed: …\"), so the colour is never the only signal (DESIGN §6).\n */\nimport { memo, useCallback, useId, useRef, useState, type KeyboardEvent, type ReactElement } from 'react';\nimport { describeTick, type ChapterMarkerKind, type ChapterModel } from './chapter-model.js';\nimport styles from './ChapterRail.module.css';\n\nexport interface ChapterRailProps {\n /**\n * From chapter-model `buildChapters`. The transcript rebuilds it only when\n * `chaptersSignature` moves, so a streamed token never re-renders the rail.\n */\n model: ChapterModel;\n onJumpTurn: (turnKey: string) => void;\n}\n\n/**\n * The markers are drawn, not typed. WHY (review 3.10 c18, SPEC-310A §1 fonts\n * ≤ 150 KB): as text, ◆ ⤷ are in neither UI font and ↺ is only in the 230 KB\n * full Plex face, so a chat with one compaction or recovery made the browser\n * fetch that face on first paint just to draw a 6px mark. A 1em SVG in\n * currentColor keeps the size and the per-kind colour the CSS already sets.\n */\nconst MARKER_PATH: Readonly<Record<ChapterMarkerKind, ReactElement>> = {\n // ◆ a filled diamond: the context was compacted here.\n compaction: <path d=\"M8 2.5 13.5 8 8 13.5 2.5 8Z\" fill=\"currentColor\" />,\n // ↺ an open circle with its arrowhead: recovered.\n recovered: <path d=\"M4.2 5.2A5 5 0 1 1 3 8.5M4.2 1.8v3.4h3.4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />,\n // ⤷ down, then right: continued from another chat.\n handoff: <path d=\"M4 2.5V9a2 2 0 0 0 2 2h7M10.5 8.5 13 11l-2.5 2.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />,\n};\n\nfunction MarkerIcon({ kind }: { kind: ChapterMarkerKind }) {\n return <svg className={styles.markerIcon} viewBox=\"0 0 16 16\" width=\"1em\" height=\"1em\" aria-hidden=\"true\" focusable=\"false\">{MARKER_PATH[kind]}</svg>;\n}\n\nexport const ChapterRail = memo(function ChapterRail({ model, onJumpTurn }: ChapterRailProps) {\n const [active, setActive] = useState(-1);\n const [peek, setPeek] = useState<number | null>(null);\n const tickRefs = useRef(new Map<number, HTMLButtonElement>());\n const cardId = useId();\n const total = model.ticks.length;\n // The roving stop: the operator's last tick, else the newest turn.\n const stop = active >= 0 && active < total ? active : total - 1;\n\n const focusTick = useCallback((index: number) => {\n const clamped = Math.max(0, Math.min(total - 1, index));\n setActive(clamped);\n setPeek(clamped);\n tickRefs.current.get(clamped)?.focus();\n }, [total]);\n\n function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n const current = stop;\n let next: number | null = null;\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') next = current + 1;\n else if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') next = current - 1;\n else if (event.key === 'Home') next = 0;\n else if (event.key === 'End') next = total - 1;\n if (next === null) return;\n event.preventDefault();\n focusTick(next);\n }\n\n if (total === 0) return null;\n const peeked = peek !== null ? model.ticks[peek] ?? null : null;\n\n return (\n <div className={styles.rail} role=\"toolbar\" aria-orientation=\"vertical\" aria-label=\"Chapters\"\n onKeyDown={onKeyDown} onMouseLeave={() => setPeek(null)}>\n {model.handoff ? (\n <span className={styles.handoff} role=\"img\" aria-label=\"Continued from another chat\" title=\"Continued from another chat\">\n <MarkerIcon kind=\"handoff\" />\n </span>\n ) : null}\n <div className={styles.track}>\n {model.ticks.map((tick) => {\n const name = describeTick(tick, total);\n const markers = [...new Set(tick.markers)];\n return (\n <button\n key={tick.turnKey}\n ref={(node) => { if (node) tickRefs.current.set(tick.index, node); else tickRefs.current.delete(tick.index); }}\n type=\"button\"\n className={styles.tick}\n data-status={tick.status}\n style={{ top: `${tick.position * 100}%` }}\n tabIndex={tick.index === stop ? 0 : -1}\n aria-label={name}\n aria-describedby={peek === tick.index ? cardId : undefined}\n onMouseEnter={() => setPeek(tick.index)}\n onFocus={() => { setActive(tick.index); setPeek(tick.index); }}\n onBlur={() => setPeek((p) => (p === tick.index ? null : p))}\n onClick={() => { setActive(tick.index); onJumpTurn(tick.turnKey); }}\n >\n <span className={styles.bar} aria-hidden=\"true\" />\n {markers.length > 0 ? (\n <span className={styles.markers} aria-hidden=\"true\">\n {markers.map((m) => <span key={m} className={styles.marker} data-marker={m}><MarkerIcon kind={m} /></span>)}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n {peeked ? (\n <div id={cardId} className={styles.card} role=\"tooltip\" style={{ top: `${peeked.position * 100}%` }}>\n <span className={styles.cardMeta}>Turn {peeked.index + 1} of {total}{peeked.status === 'error' ? ' · failed' : peeked.status === 'running' ? ' · running' : ''}</span>\n <span className={styles.cardTitle}>{peeked.title}</span>\n </div>\n ) : null}\n </div>\n );\n});\n","/**\n * routes/verse/chat/FileActivity.tsx — the blast radius of one turn.\n *\n * The question this answers is the first one asked of any agentic turn:\n * *what did it touch?* Today that costs expanding every tool card and\n * reading JSON. Here it is one compact list — which files were read,\n * edited, created or deleted, with counts and line deltas — and clicking a\n * row scrolls to the call that changed it.\n *\n * DESIGN §6: the action is a WORD, never only the tint on the rule. The\n * 2px left rule repeats the same information for scanning, and the row's\n * accessible name spells it out in full.\n */\nimport { useState } from 'react';\nimport { Tooltip } from '../../../components/primitives/Tooltip.js';\nimport { useDisplayPath } from './path-display.js';\nimport { fileBasename, fileDirname } from './tool-semantics.js';\nimport { describeFiles, type TurnFileEntry } from './turn-model.js';\nimport styles from './chat.module.css';\n\n/** Beyond this the list collapses; a big refactor should not be a wall. */\nconst COLLAPSE_AFTER = 8;\n\nconst VERB: Record<TurnFileEntry['action'], string> = {\n read: 'read',\n edit: 'edited',\n create: 'created',\n delete: 'deleted',\n};\n\nexport interface FileActivityProps {\n files: readonly TurnFileEntry[];\n /** Scroll to the tool call that did the work. */\n onJump: (toolUseId: string) => void;\n}\n\nexport function FileActivity({ files, onJump }: FileActivityProps) {\n const [expanded, setExpanded] = useState(false);\n const show = useDisplayPath();\n if (files.length === 0) return null;\n const visible = expanded || files.length <= COLLAPSE_AFTER ? files : files.slice(0, COLLAPSE_AFTER);\n const hidden = files.length - visible.length;\n\n return (\n <section className={styles.activity} aria-label=\"Files this turn touched\">\n <h3 className={styles.activityHead}>\n <span className={styles.activityLabel}>{files.length} file{files.length === 1 ? '' : 's'}</span>\n <span className={styles.activityBreakdown}>{describeFiles(files)}</span>\n </h3>\n <ul className={styles.activityList}>\n {visible.map((file) => {\n const times = file.reads + file.edits + file.creates + file.deletes;\n // Relative to the chat's roots (chat/path-display.ts): the raw path\n // is usually absolute, and its directory half is what the row clips.\n const shown = show(file.path);\n const name = `${VERB[file.action]} ${shown}${times > 1 ? `, ${times} calls` : ''}${\n file.additions || file.deletions ? `, +${file.additions} −${file.deletions}` : ''\n }${file.failed ? ', a call on this file failed' : ''}`;\n return (\n <li key={file.path}>\n {/* The row draws the basename in full and lets the DIRECTORY\n clip, which is the half that tells two same-named files\n apart. The full, absolute path is the tooltip, on focus as\n well as hover. */}\n <Tooltip label={file.path} placement=\"right\">\n <button type=\"button\" className={styles.activityRow} data-action={file.action}\n data-failed={file.failed ? 'true' : undefined}\n onClick={() => onJump(file.anchorToolUseId)} aria-label={name}>\n <span className={styles.activityRule} aria-hidden=\"true\" />\n <span className={styles.activityVerb}>{VERB[file.action]}</span>\n <span className={styles.activityPath}>\n <span className={styles.activityName}>{fileBasename(shown)}</span>\n <span className={styles.activityDir}>{fileDirname(shown)}</span>\n </span>\n {file.failed ? (\n // DESIGN §6: the danger tint on the 2px rule was the ONLY\n // difference between a failed row and a successful one, and\n // it is the same rule once hue is removed. The word is what\n // actually carries it.\n <span className={styles.activityFailed} aria-hidden=\"true\">! failed</span>\n ) : null}\n {times > 1 ? <span className={styles.activityTimes}>×{times}</span> : null}\n <span className={styles.activityDelta}>\n {file.additions > 0 ? <span className={styles.activityAdd}>+{file.additions}</span> : null}\n {file.deletions > 0 ? <span className={styles.activityDel}>−{file.deletions}</span> : null}\n </span>\n </button>\n </Tooltip>\n </li>\n );\n })}\n </ul>\n {hidden > 0 || expanded ? (\n <button type=\"button\" className={styles.activityMore} aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}>\n {expanded ? 'Show fewer files' : `Show ${hidden} more file${hidden === 1 ? '' : 's'}`}\n </button>\n ) : null}\n </section>\n );\n}\n","/**\n * routes/verse/chat/ThinkingBlock.tsx — one block of model reasoning (V3.10).\n *\n * Before 3.10 reasoning arrived only when a block closed, behind a static\n * \"Thinking\" disclosure, usually two collapses deep inside a tool group — and\n * for Claude Opus it was almost always EMPTY (the CLI omitted the text), so\n * nothing showed at all. Now the adapters stream vendor summaries\n * (`thinking-delta`), and this block:\n *\n * - streams them while the model thinks, open, with a live\n * \"Thinking · 12s · ~1.8k tok\" line;\n * - settles to a collapsed \"Thought 12s · ~1.8k tok\" once the block ends,\n * the operator's own open/closed choice winning over the default;\n * - says honestly when the provider withheld the text (a redacted,\n * signature-only block): the duration is real, the words are not shown,\n * and there is nothing to expand.\n *\n * 3.10 (SPEC-310C §2, unit C2): while it streams, the body is a THREE-LINE\n * window that follows the tail — enough to see what the model is working\n * on, not so much that a long think shoves the answer off screen. Clicking\n * the label (or the window) shows the whole stream. Once the block ends it\n * folds to \"Thought 12s · ~1.8k tok ▸\" — or stays open when Settings ▸ Chat\n * says Expanded (chat/reasoning-pref.ts; Hidden is handled by the\n * transcript, which then renders no reasoning at all).\n *\n * Honesty rule: an unknown duration or token count is omitted, never shown\n * as 0. A token count the CLI did not report is estimated from the text\n * (~4 characters per token) and always carries the \"~\".\n *\n * 3.10.1: the row opens with the same ▸ every foldable transcript row uses\n * (tool cards, activity groups), at the same x; the breathing dot appears\n * only while the block streams — a settled block has no stray bullet.\n */\nimport { memo, useEffect, useLayoutEffect, useRef, useState, type MouseEvent, type SyntheticEvent } from 'react';\nimport type { VerseThinkingKind } from '../../../../core/verse/types.js';\nimport styles from './ThinkingBlock.module.css';\n\nexport interface ThinkingBlockProps {\n text: string;\n /** Still streaming: the label counts up and the body follows new text. */\n streaming?: boolean;\n /** Client clock (ms) the block started — the live label's origin while streaming. */\n startedAt?: number | null;\n /** The provider sent the block without its text. */\n redacted?: boolean;\n durationMs?: number | null;\n /** The CLI's own estimate; null → estimated from the text when there is any. */\n estimatedTokens?: number | null;\n kind?: VerseThinkingKind | null;\n /** Initial open state; the operator's toggle wins afterwards. */\n defaultOpen?: boolean;\n /** Survives scroll restore (useScrollRestore) like every other disclosure. */\n stateKey?: string;\n}\n\n/** \"~1.8k tok\" — one decimal under 10k, so the figure moves visibly while it streams. */\nexport function formatThinkingTokens(n: number): string {\n const rounded = Math.max(0, Math.round(n));\n if (rounded < 1000) return `~${rounded} tok`;\n if (rounded < 10_000) return `~${(rounded / 1000).toFixed(1).replace(/\\.0$/, '')}k tok`;\n if (rounded < 1_000_000) return `~${Math.round(rounded / 1000)}k tok`;\n return `~${(rounded / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M tok`;\n}\n\n/** \"12s\" / \"1m 5s\" / \"<1s\". */\nexport function formatThinkingDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '';\n if (ms < 1000) return '<1s';\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${s % 60}s`;\n return `${Math.floor(m / 60)}h ${m % 60}m`;\n}\n\n/** Characters per token for the fallback estimate (the common English ratio). */\nconst CHARS_PER_TOKEN = 4;\n\nexport function thinkingTokenFigure(text: string, estimatedTokens: number | null | undefined): number | null {\n if (typeof estimatedTokens === 'number' && Number.isFinite(estimatedTokens) && estimatedTokens > 0) return estimatedTokens;\n const chars = text.trim().length;\n return chars > 0 ? Math.ceil(chars / CHARS_PER_TOKEN) : null;\n}\n\n/** The one-line label — exported so the transcript search and tests agree with the UI. */\nexport function thinkingLabel(opts: {\n streaming: boolean;\n elapsedMs: number | null;\n tokens: number | null;\n}): string {\n const parts = [opts.streaming ? 'Thinking' : 'Thought'];\n const duration = opts.elapsedMs === null ? '' : formatThinkingDuration(opts.elapsedMs);\n if (opts.streaming) {\n if (duration) parts.push(duration);\n } else if (duration) {\n parts[0] = `Thought ${duration}`;\n }\n if (opts.tokens !== null) parts.push(formatThinkingTokens(opts.tokens));\n return parts.join(' · ');\n}\n\n/** Re-render once a second while `active` — the label's clock, not a network poll. */\nfunction useSecondTick(active: boolean): number {\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n if (!active) return undefined;\n setNow(Date.now());\n const id = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(id);\n }, [active]);\n return now;\n}\n\n/** How close to the bottom the body must be for new text to keep it pinned there. */\nconst FOLLOW_PX = 24;\n\nexport const ThinkingBlock = memo(function ThinkingBlock({\n text,\n streaming = false,\n startedAt = null,\n redacted = false,\n durationMs = null,\n estimatedTokens = null,\n kind = null,\n defaultOpen = false,\n stateKey,\n}: ThinkingBlockProps) {\n const [open, setOpen] = useState(defaultOpen);\n /** The operator opened or closed it by hand; from then on their choice wins over `defaultOpen`. */\n const chosen = useRef(false);\n /** The operator asked for the whole stream instead of the three-line window. */\n const [full, setFull] = useState(false);\n const body = useRef<HTMLDivElement>(null);\n const following = useRef(true);\n const now = useSecondTick(streaming && startedAt !== null);\n\n const elapsed = streaming\n ? (startedAt !== null ? Math.max(0, now - startedAt) : null)\n : durationMs;\n const tokens = thinkingTokenFigure(text, estimatedTokens);\n const label = thinkingLabel({ streaming, elapsedMs: elapsed, tokens });\n const hasText = text.trim().length > 0;\n\n // Follow the stream inside the body unless the operator scrolled up in it.\n const windowed = streaming && open && !full;\n useLayoutEffect(() => {\n const node = body.current;\n if (!node) return;\n // Fade the top line only when something is actually cut off above it.\n if (windowed && node.scrollHeight > node.clientHeight + 1) node.setAttribute('data-clipped', '');\n else node.removeAttribute('data-clipped');\n if (!streaming || (!following.current && !windowed)) return;\n node.scrollTop = node.scrollHeight;\n }, [text, streaming, open, windowed]);\n\n function onScroll() {\n const node = body.current;\n if (!node) return;\n following.current = node.scrollHeight - node.scrollTop - node.clientHeight <= FOLLOW_PX;\n }\n\n // Open while its turn runs, folded once it finishes — unless the operator\n // already decided for this block.\n useEffect(() => {\n if (!chosen.current) setOpen(defaultOpen);\n }, [defaultOpen]);\n\n // While windowed, the first click on the label means \"show me all of it\",\n // not \"hide it\": folding the one thing the operator is trying to read\n // would be the opposite of the gesture.\n function onSummaryClick(event: MouseEvent<HTMLElement>) {\n if (!windowed) return;\n event.preventDefault();\n chosen.current = true;\n setFull(true);\n }\n\n function onToggle(event: SyntheticEvent<HTMLDetailsElement>) {\n const next = event.currentTarget.open;\n // A toggle event also follows OUR prop change; only a mismatch is a click.\n if (next !== open) chosen.current = true;\n setOpen(next);\n }\n\n if (!hasText) {\n // Nothing to expand: a redacted block, or the CLI's token count before\n // any summary text arrived. One quiet line that still says it happened.\n return (\n <div className={styles.thinking} data-streaming={streaming || undefined} data-redacted={redacted || undefined}\n data-thinking-kind={kind ?? undefined}>\n <span className={styles.line}>\n {streaming ? <span className={styles.glyph} aria-hidden=\"true\" /> : null}\n <span className={styles.label}>{label}</span>\n {redacted ? (\n <span className={styles.hidden} title=\"The provider returned this reasoning without its text; only how long it took is known.\">\n reasoning hidden by the provider\n </span>\n ) : null}\n </span>\n </div>\n );\n }\n\n return (\n <details className={styles.thinking} open={open} onToggle={onToggle} data-state-key={stateKey}\n data-streaming={streaming || undefined} data-thinking-kind={kind ?? undefined}>\n <summary className={styles.summary} onClick={onSummaryClick} aria-label={windowed ? `${label} — show all` : undefined}>\n <span className={styles.chevron} aria-hidden=\"true\" />\n {streaming ? <span className={styles.glyph} aria-hidden=\"true\" /> : null}\n <span className={styles.label}>{label}</span>\n {kind === 'summary' ? <span className={styles.kind} title=\"A summary the provider wrote of the model's reasoning, not its raw chain of thought.\">summary</span> : null}\n </summary>\n <div ref={body} className={styles.body} onScroll={onScroll} data-window={windowed || undefined}\n onClick={windowed ? () => { chosen.current = true; setFull(true); } : undefined}>{text}</div>\n </details>\n );\n});\n","/**\n * routes/verse/chat/TranscriptNav.tsx — making a fifty-turn session\n * navigable.\n *\n * A long agentic session scrolls past the point where scrolling is a way to\n * find anything. This strip gives it three handles: an outline of the turns\n * (with what each one touched), a search over everything in the transcript —\n * prose, tool arguments and tool output alike — and a jump straight to the\n * first failure.\n *\n * It is chrome, so it obeys DESIGN §4: one hairline-bottomed row at\n * `--density-row`, ghost controls only, no filled buttons. It renders only\n * when the session is long enough to need it, so a two-turn chat stays as\n * bare as it is today.\n */\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { ChevronIcon, SearchIcon } from '../verse-icons.js';\nimport { turnTitle, type TurnBlock, type TurnMatch } from './turn-model.js';\nimport styles from './chat.module.css';\n\nexport interface TranscriptNavProps {\n turns: readonly TurnBlock[];\n query: string;\n onQuery: (value: string) => void;\n matches: readonly TurnMatch[];\n /** Index into `matches`, or -1 when nothing is selected yet. */\n matchIndex: number;\n onStepMatch: (delta: number) => void;\n errorCount: number;\n onJumpError: () => void;\n onJumpTurn: (turnKey: string) => void;\n /** Focus hand-off: the transcript raises this when ⌘F is pressed. */\n focusToken: number;\n}\n\nconst STATUS_GLYPH: Record<TurnBlock['status'], string> = {\n running: '•',\n ok: '',\n error: '!',\n stopped: '×',\n};\n\nconst STATUS_WORD: Record<TurnBlock['status'], string> = {\n running: 'running',\n ok: 'completed',\n error: 'failed',\n stopped: 'stopped',\n};\n\nexport function TranscriptNav(props: TranscriptNavProps) {\n const { turns, query, onQuery, matches, matchIndex, onStepMatch, errorCount, onJumpError, onJumpTurn, focusToken } = props;\n const [outlineOpen, setOutlineOpen] = useState(false);\n const search = useRef<HTMLInputElement>(null);\n const outlineId = useId();\n const searchId = useId();\n const searching = query.trim().length > 0;\n\n useEffect(() => {\n if (focusToken > 0) {\n search.current?.focus();\n search.current?.select();\n }\n }, [focusToken]);\n\n // Close the outline on Escape from anywhere inside it.\n useEffect(() => {\n if (!outlineOpen) return;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') setOutlineOpen(false);\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [outlineOpen]);\n\n const listed = searching\n ? turns.filter((turn) => matches.some((m) => m.turnKey === turn.key))\n : turns;\n const snippetFor = (key: string) => matches.find((m) => m.turnKey === key)?.snippet ?? null;\n\n return (\n <div className={styles.nav}>\n <button type=\"button\" className={styles.navOutlineButton} aria-expanded={outlineOpen}\n aria-controls={outlineOpen ? outlineId : undefined} onClick={() => setOutlineOpen((v) => !v)}>\n <span className={styles.navChevron} data-open={outlineOpen ? 'true' : undefined} aria-hidden=\"true\">\n <ChevronIcon size={12} />\n </span>\n <span className={styles.navCount}>{turns.length}</span>\n <span className={styles.navWord}>turn{turns.length === 1 ? '' : 's'}</span>\n </button>\n\n <label className={styles.navSearch} htmlFor={searchId}>\n <span className={styles.navSearchIcon} aria-hidden=\"true\"><SearchIcon size={12} /></span>\n <span className=\"visually-hidden\">Search this chat</span>\n <input ref={search} id={searchId} type=\"search\" value={query} placeholder=\"Find in chat\" autoComplete=\"off\"\n onChange={(event) => onQuery(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n onStepMatch(event.shiftKey ? -1 : 1);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n onQuery('');\n }\n }} />\n </label>\n\n {searching ? (\n <span className={styles.navMatches} role=\"status\">\n {matches.length === 0\n ? 'no matches'\n : `${matchIndex < 0 ? 1 : matchIndex + 1} of ${matches.length}`}\n </span>\n ) : null}\n {searching && matches.length > 0 ? (\n <span className={styles.navStep}>\n <button type=\"button\" onClick={() => onStepMatch(-1)} aria-label=\"Previous match\" title=\"Previous match (Shift+Enter)\">↑</button>\n <button type=\"button\" onClick={() => onStepMatch(1)} aria-label=\"Next match\" title=\"Next match (Enter)\">↓</button>\n </span>\n ) : null}\n\n <span className={styles.navSpacer} />\n\n {errorCount > 0 ? (\n <button type=\"button\" className={styles.navError} onClick={onJumpError}>\n <span className={styles.navErrorGlyph} aria-hidden=\"true\">!</span>\n {errorCount} error{errorCount === 1 ? '' : 's'}\n <span className=\"visually-hidden\"> — jump to the first one</span>\n </button>\n ) : null}\n\n {outlineOpen ? (\n <div id={outlineId} className={styles.outline} role=\"group\" aria-label=\"Turns in this chat\">\n {listed.length === 0 ? (\n <p className={styles.outlineEmpty}>No turn matches “{query.trim()}”.</p>\n ) : (\n <ol className={styles.outlineList}>\n {listed.map((turn) => {\n const index = turns.indexOf(turn) + 1;\n const snippet = snippetFor(turn.key);\n return (\n <li key={turn.key}>\n <button type=\"button\" className={styles.outlineRow} data-status={turn.status}\n onClick={() => { setOutlineOpen(false); onJumpTurn(turn.key); }}>\n <span className={styles.outlineIndex}>{index}</span>\n <span className={styles.outlineBody}>\n <span className={styles.outlineTitle}>{turnTitle(turn)}</span>\n {snippet ? <span className={styles.outlineSnippet}>{snippet}</span> : null}\n <span className={styles.outlineMeta}>\n {turn.toolCount > 0 ? `${turn.toolCount} tool${turn.toolCount === 1 ? '' : 's'}` : 'no tools'}\n {turn.files.length > 0 ? ` · ${turn.files.length} file${turn.files.length === 1 ? '' : 's'}` : ''}\n {turn.status !== 'ok' ? ` · ${STATUS_WORD[turn.status]}` : ''}\n </span>\n </span>\n {STATUS_GLYPH[turn.status] ? (\n <span className={styles.outlineStatus} aria-hidden=\"true\">{STATUS_GLYPH[turn.status]}</span>\n ) : null}\n </button>\n </li>\n );\n })}\n </ol>\n )}\n </div>\n ) : null}\n </div>\n );\n}\n","/**\n * routes/verse/chat/TurnAnnouncer.tsx — what a screen reader hears from the\n * transcript (SPEC-310C §2 \"Screen readers\", unit C2).\n *\n * The 3.9 log was `aria-live=\"polite\"` with `aria-relevant=\"additions text\"`:\n * every streamed token was a text change, so a screen reader either read the\n * reply one fragment at a time or queued minutes of speech behind it. The\n * log is now `aria-live=\"off\"` and this polite region says only the moments\n * that change what the operator should do:\n *\n * \"Turn started.\" · \"Turn finished.\" · \"Turn failed.\" · \"Turn stopped.\"\n *\n * Nothing is announced on mount or when switching chats (history is not\n * news) — only a transition observed while the same chat stays open.\n */\nimport { useEffect, useRef, useState } from 'react';\nimport type { TurnStatus } from './turn-model.js';\n\nexport interface TurnAnnouncerProps {\n /** A turn is in flight. */\n running: boolean;\n /** The last turn's status (null when there is none). */\n lastStatus: TurnStatus | null;\n /** The last turn's key — a new key with the same status is still a new turn. */\n lastKey: string | null;\n}\n\n/** A trailing no-break space makes a repeated sentence a CHANGE, so it is spoken again. */\nconst NBSP = '\\u00a0';\n\nconst SETTLED: Readonly<Record<Exclude<TurnStatus, 'running'>, string>> = {\n ok: 'Turn finished.',\n error: 'Turn failed.',\n stopped: 'Turn stopped.',\n};\n\n/** Pure: what to say for a transition, or null. Exported for tests. */\nexport function turnAnnouncement(\n prev: { running: boolean; lastKey: string | null } | null,\n next: { running: boolean; lastStatus: TurnStatus | null; lastKey: string | null },\n): string | null {\n if (prev === null) return null;\n if (!prev.running && next.running) return 'Turn started.';\n if (prev.running && !next.running) {\n if (next.lastStatus === null || next.lastStatus === 'running') return 'Turn finished.';\n return SETTLED[next.lastStatus];\n }\n return null;\n}\n\nexport function TurnAnnouncer({ running, lastStatus, lastKey }: TurnAnnouncerProps) {\n const [message, setMessage] = useState('');\n const prev = useRef<{ running: boolean; lastKey: string | null } | null>(null);\n\n useEffect(() => {\n const said = turnAnnouncement(prev.current, { running, lastStatus, lastKey });\n prev.current = { running, lastKey };\n // Re-set even an identical sentence: two failures in a row are two events.\n if (said !== null) setMessage((current) => (current === said ? `${said}${NBSP}` : said));\n }, [running, lastStatus, lastKey]);\n\n return (\n <div className=\"visually-hidden\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" data-testid=\"turn-announcer\">\n {message}\n </div>\n );\n}\n","/**\n * routes/verse/chat/ansi.ts — turning raw process output into something a\n * human can read.\n *\n * An agentic turn's Bash results arrive exactly as the child process wrote\n * them: SGR colour runs, cursor moves, OSC title sets, and carriage returns\n * from progress bars that expected a terminal to overwrite the line. Dumped\n * into a <pre> those print as `[0;32m` litter and a hundred duplicate\n * progress lines, which is why command output in the transcript is currently\n * the hardest thing in it to read.\n *\n * Pure string functions, no DOM: the colour information is DROPPED rather\n * than translated into spans. DESIGN §1 is monochrome-first — a tool's own\n * palette is not one of the meanings this interface spends colour on.\n */\n\n/* eslint-disable no-control-regex --\n Matching control bytes IS this module's entire purpose: the four patterns\n below are the escape grammar a terminal emits, and there is no way to\n express them without naming the control characters they match. They are\n written as \\uXXXX escapes, never as literal bytes in the source. */\n\n/** CSI … final byte @-~ — SGR colour, cursor moves, erase, everything. */\nconst CSI = /\\u001B\\[[0-9;:?]*[ -/]*[@-~]/g;\n/** OSC … terminated by BEL or ST — window titles, hyperlinks, clipboard. */\nconst OSC = /\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\\\)/g;\n/** Two-byte escapes (ESC 7 / ESC M / charset selects). */\nconst SHORT = /\\u001B[@-Z\\\\-_]/g;\n/** Control bytes that carry no meaning inside a <pre>: tab/newline/CR stay. */\nconst CONTROL = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n\n/* eslint-enable no-control-regex */\n\n/** True when the text contains at least one escape sequence. */\nexport function hasAnsi(text: string): boolean {\n return text.includes('\\u001B');\n}\n\n/** Remove every escape sequence and stray control byte; keep tabs/newlines. */\nexport function stripAnsi(text: string): string {\n return text.replace(OSC, '').replace(CSI, '').replace(SHORT, '').replace(CONTROL, '');\n}\n\n/**\n * Collapse carriage-return rewrites the way a terminal would: within one\n * line each CR returns the cursor to column 0 and what follows overwrites\n * what was there. A shorter overwrite leaves the tail of the longer line\n * visible, which is why this overlays rather than simply taking the last\n * segment — `100%\\rdone` is `done%` on a real terminal, not `done`.\n */\nexport function applyCarriageReturns(text: string): string {\n if (!text.includes('\\r')) return text;\n return text\n .split('\\n')\n .map((line) => {\n if (!line.includes('\\r')) return line;\n let out = '';\n for (const segment of line.split('\\r')) {\n out = segment.length >= out.length ? segment : segment + out.slice(segment.length);\n }\n return out;\n })\n .join('\\n');\n}\n\n/**\n * The whole treatment: escapes out, carriage returns applied, trailing blank\n * lines trimmed. Leading whitespace is preserved — indentation is content in\n * compiler output, stack traces and test reports.\n */\nexport function cleanTerminalOutput(text: string): string {\n return applyCarriageReturns(stripAnsi(text))\n .replace(/[ \\t]+$/gm, '')\n .replace(/\\n+$/, '');\n}\n\n/** Line count without allocating a split array. */\nexport function countLines(text: string): number {\n if (text.length === 0) return 0;\n let n = 1;\n for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) n++;\n return n;\n}\n\n/**\n * First `limit` lines plus how many were withheld. Backs the collapsed view\n * of a long result — the full text stays one click away, never discarded.\n */\nexport function headLines(text: string, limit: number): { head: string; hidden: number } {\n const lines = text.split('\\n');\n if (lines.length <= limit) return { head: text, hidden: 0 };\n return { head: lines.slice(0, limit).join('\\n'), hidden: lines.length - limit };\n}\n","/**\n * routes/verse/chat/CommandOutput.tsx — a shell run, readable.\n *\n * Three things were wrong with dumping `tool-result.output` into a <pre>:\n * escape sequences printed as `[0;32m` litter, progress bars repeated every\n * carriage-return frame, and a thousand-line build log pushed the rest of\n * the turn off the screen. All three are fixed here without losing anything:\n * `ansi.ts` cleans the text, long output collapses to a head with an exact\n * count of what is hidden, and \"Show all\" restores it.\n *\n * The status line is honest (DESIGN §6). A stated exit code is printed as a\n * number; when the tool never stated one, the line says whether the call\n * failed and does not invent a code.\n *\n * 3.10 (SPEC-310C §2, unit C2): \"Run in terminal\" opens the dock's Terminal\n * with the command PASTED at the prompt — never executed. The operator reads\n * it there and presses Enter; an agent's command must not re-run because\n * someone clicked near it. Offered only once the Terminal pane exists in\n * this build (C4's slot).\n */\nimport { useMemo, useState, type ReactNode } from 'react';\nimport { isSlotAvailable } from '../shell/slots.js';\nimport { requestTerminal } from '../dock/dock-store.js';\nimport { cleanTerminalOutput, countLines, hasAnsi, headLines } from './ansi.js';\nimport type { CommandFacts } from './tool-semantics.js';\nimport styles from './chat.module.css';\n\nconst PREVIEW_LINES = 20;\n/** Hard ceiling: past this, the DOM cost stops being worth it. */\nconst MAX_RENDER_CHARS = 200_000;\n\nexport interface CommandOutputProps {\n command: CommandFacts | null;\n output: string;\n /** The tool reported failure for this call. */\n isError: boolean;\n pending: boolean;\n /**\n * Replaces the raw output block. A command whose output IS a diff\n * (`git diff`, `git show`) keeps its command line and exit status here and\n * renders the diff properly instead of as terminal text.\n */\n body?: ReactNode;\n /** Whether \"Run in terminal\" is offered; defaults to whether C4's Terminal pane is in this build (tests inject it). */\n terminalAvailable?: boolean;\n}\n\nexport function CommandOutput({ command, output, isError, pending, body, terminalAvailable = isSlotAvailable('terminal-pane') }: CommandOutputProps) {\n const [expanded, setExpanded] = useState(false);\n const cleaned = useMemo(() => {\n const text = cleanTerminalOutput(output);\n return text.length > MAX_RENDER_CHARS\n ? `${text.slice(0, MAX_RENDER_CHARS)}\\n… (${text.length - MAX_RENDER_CHARS} more characters not shown)`\n : text;\n }, [output]);\n const { head, hidden } = useMemo(() => headLines(cleaned, PREVIEW_LINES), [cleaned]);\n const shown = expanded ? cleaned : head;\n const exit = command?.exitCode ?? null;\n\n return (\n <div className={styles.command} data-failed={isError ? 'true' : undefined}>\n {command ? (\n <div className={styles.commandLine}>\n <span className={styles.commandPrompt} aria-hidden=\"true\">$</span>\n <code className={styles.commandText}>{command.command}</code>\n {terminalAvailable && command.command.trim() ? (\n <button type=\"button\" className={styles.commandRun}\n title=\"Opens the Terminal with this command typed at the prompt. It does not run until you press Enter.\"\n onClick={() => requestTerminal({ paste: command.command })}>\n Run in terminal\n </button>\n ) : null}\n </div>\n ) : null}\n <div className={styles.commandStatus}>\n <span className={styles.commandStatusText} data-tone={pending ? 'pending' : isError ? 'bad' : 'ok'}>\n {pending\n ? 'running…'\n : exit !== null\n ? `exit ${exit}`\n : isError\n ? 'failed — no exit code reported'\n : 'completed — no exit code reported'}\n </span>\n {!pending && !body && cleaned.length === 0 ? <span className={styles.commandMeta}>no output</span> : null}\n {!pending && !body && cleaned.length > 0 ? (\n <span className={styles.commandMeta}>\n {countLines(cleaned)} line{countLines(cleaned) === 1 ? '' : 's'}\n {hasAnsi(output) ? ' · colour codes stripped' : ''}\n </span>\n ) : null}\n </div>\n {pending ? (\n <p className={styles.commandPending}>Waiting for the command to finish…</p>\n ) : body ? (\n body\n ) : cleaned.length > 0 ? (\n <>\n <pre className={styles.commandPre}>{shown}</pre>\n {hidden > 0 ? (\n <button type=\"button\" className={styles.commandMore} aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}>\n {expanded ? 'Collapse output' : `Show ${hidden} more line${hidden === 1 ? '' : 's'}`}\n </button>\n ) : null}\n </>\n ) : null}\n </div>\n );\n}\n","/**\n * routes/verse/chat/DiffBlock.tsx — a file change rendered as a change.\n *\n * The transcript's job here is narrow: ONE file, inside a 720px reading\n * column, inline in a tool card. The inbox `DiffViewer` is the right tool\n * for the other shape — a multi-file patch that wants a file tree and a\n * split toggle — so that component is imported and used verbatim for\n * multi-file diffs rather than reimplemented. The parser\n * (`routes/inbox/diff-parser.ts`) and the highlighter\n * (`routes/inbox/highlight.ts`) are shared by both paths; nothing about\n * either is rewritten here.\n *\n * Honesty (DESIGN §6): an `Edit` payload knows what was replaced but not\n * where it sat, so when `diff.anchored` is false the line-number gutter is\n * hidden and the header says the offsets are not the file's.\n */\nimport { useMemo, useState } from 'react';\nimport { DiffViewer } from '../../inbox/DiffViewer.js';\nimport { parseUnifiedDiff, type DiffFile, type DiffHunk } from '../../inbox/diff-parser.js';\nimport { languageForPath, tokenizeLine, type LangFamily } from '../../inbox/highlight.js';\nimport { useDisplayPath } from './path-display.js';\nimport type { ToolDiff } from './tool-semantics.js';\nimport styles from './chat.module.css';\n\n/** Beyond this many rendered lines the block collapses to a preview. */\nconst COLLAPSE_AFTER_LINES = 28;\nconst PREVIEW_LINES = 18;\n/**\n * Hard ceiling on rows materialised from one file's hunks. The text itself is\n * already capped in `readToolFacts`, but a capped diff can still be tens of\n * thousands of lines, and every row here becomes DOM. Past this the block says\n * how many it dropped rather than locking the tab building rows nobody asked to\n * see.\n */\nconst MAX_ROWS = 4000;\n\nexport interface DiffBlockProps {\n diff: ToolDiff;\n /** Falls back to the diff's own path when the payload named none. */\n path?: string | null;\n}\n\nexport function DiffBlock({ diff, path = null }: DiffBlockProps) {\n const parsed = useMemo(() => parseUnifiedDiff(diff.text), [diff.text]);\n\n if (parsed.files.length === 0) return null;\n if (diff.multiFile || parsed.files.length > 1) {\n return (\n <div className={styles.diffMulti}>\n <DiffViewer diff={diff.text} proposalKind=\"tool call\" />\n </div>\n );\n }\n\n const file = parsed.files[0]!;\n return <SingleFileDiff file={file} anchored={diff.anchored} path={path ?? file.displayPath} />;\n}\n\n/**\n * What happened to the file, when that is not simply \"it was edited\".\n *\n * A rename with no hunks is the case that made this necessary: it reached the\n * empty branch below and printed \"No line-level changes in this call.\" under\n * the NEW path alone, telling the operator nothing happened while silently\n * dropping the old location — the one fact that makes a rename legible.\n */\nfunction statusNote(file: DiffFile): string | null {\n switch (file.status) {\n case 'renamed':\n return 'Renamed. The contents did not change, so there are no lines to show.';\n case 'added':\n return 'New file. It was added with no contents to diff against.';\n case 'deleted':\n return 'Deleted. The file is gone, so there are no lines to show.';\n default:\n return null;\n }\n}\n\nfunction SingleFileDiff({ file, anchored, path }: { file: DiffFile; anchored: boolean; path: string }) {\n const [expanded, setExpanded] = useState(false);\n const lang = languageForPath(path);\n const rows = useMemo(() => flatten(file.hunks), [file.hunks]);\n const long = rows.length > COLLAPSE_AFTER_LINES;\n const visible = long && !expanded ? rows.slice(0, PREVIEW_LINES) : rows;\n const hidden = rows.length - visible.length;\n const renamedFrom = file.status === 'renamed' ? file.oldPath : null;\n const note = statusNote(file);\n // Drawn relative to the chat's roots; the full path is the tooltip.\n const show = useDisplayPath();\n\n return (\n <div className={styles.diff} data-anchored={anchored ? 'true' : 'false'} data-status={file.status}>\n <div className={styles.diffHead}>\n {renamedFrom ? (\n <span className={styles.diffOldPath} title={renamedFrom}>\n {show(renamedFrom)}\n <span className={styles.visuallyHidden}> renamed to</span>\n </span>\n ) : null}\n <span className={styles.diffPath} title={path}>{show(path)}</span>\n {file.status !== 'modified' ? (\n <span className={styles.diffStatus}>{file.status}</span>\n ) : null}\n <span className={styles.diffStats}>\n {file.additions > 0 ? <span className={styles.diffAdd}>+{file.additions}</span> : null}\n {file.deletions > 0 ? <span className={styles.diffDel}>−{file.deletions}</span> : null}\n </span>\n </div>\n {file.unparsedNotice ? <pre className={styles.diffNotice}>{file.unparsedNotice}</pre> : null}\n {rows.length === 0 ? (\n <p className={styles.diffEmpty}>\n {note ?? 'No line-level changes in this call.'}\n </p>\n ) : (\n <>\n <table className={styles.diffTable}>\n <tbody>\n {visible.map((row) =>\n row.kind === 'hunk' ? (\n <tr key={row.key} className={styles.diffHunkRow}>\n {anchored ? <td className={styles.diffNo} aria-hidden=\"true\" /> : null}\n <td className={styles.diffMarker} aria-hidden=\"true\" />\n <td className={styles.diffHunkHead}>{row.label}</td>\n </tr>\n ) : (\n <tr key={row.key} className={styles[`diffRow_${row.line.kind}`]}>\n {anchored ? (\n <td className={styles.diffNo}>{row.line.newLineNo ?? row.line.oldLineNo ?? ''}</td>\n ) : null}\n <td className={styles.diffMarker} aria-hidden=\"true\">\n {row.line.kind === 'add' ? '+' : row.line.kind === 'del' ? '−' : ''}\n </td>\n <td className={styles.diffText}>\n <CodeLine text={row.line.text} lang={lang} />\n </td>\n </tr>\n ),\n )}\n </tbody>\n </table>\n {long ? (\n <button type=\"button\" className={styles.diffMore} onClick={() => setExpanded((v) => !v)}\n aria-expanded={expanded}>\n {expanded ? 'Collapse diff' : `Show ${hidden} more line${hidden === 1 ? '' : 's'}`}\n </button>\n ) : null}\n </>\n )}\n {!anchored ? (\n <p className={styles.diffUnanchored}>\n No line numbers — the tool didn&apos;t report where in the file.\n </p>\n ) : null}\n </div>\n );\n}\n\ntype Row =\n | { kind: 'hunk'; key: string; label: string }\n | { kind: 'line'; key: string; line: DiffHunk['lines'][number] };\n\n/** Hunks into one flat row list, with a separator row between hunks. */\nfunction flatten(hunks: readonly DiffHunk[]): Row[] {\n const rows: Row[] = [];\n let truncated = false;\n hunks.forEach((hunk, h) => {\n if (truncated) return;\n // The `@@ -a,b +c,d @@` arithmetic is noise in a chat column; the free\n // text after it (`edit 2 of 3`, a function name) is the only part worth\n // a row, and a multi-hunk diff needs SOME separator regardless.\n const section = hunk.header.replace(/^@@[^@]*@@/, '').trim();\n if (section || hunks.length > 1) {\n rows.push({ kind: 'hunk', key: `h${h}`, label: section || `hunk ${h + 1}` });\n }\n for (let i = 0; i < hunk.lines.length; i++) {\n if (rows.length >= MAX_ROWS) {\n truncated = true;\n rows.push({ kind: 'hunk', key: 'truncated', label: `diff truncated at ${MAX_ROWS} lines` });\n return;\n }\n rows.push({ kind: 'line', key: `h${h}l${i}`, line: hunk.lines[i]! });\n }\n });\n return rows;\n}\n\nfunction CodeLine({ text, lang }: { text: string; lang: LangFamily }) {\n const tokens = tokenizeLine(text, lang);\n return (\n <code className={styles.diffCode}>\n {tokens.map((token, i) => (\n <span key={i} className={styles[`tok_${token.kind}`]}>{token.text}</span>\n ))}\n </code>\n );\n}\n","/**\n * routes/verse/ToolUseCard.tsx — one tool call.\n *\n * Collapsed it is a single dense line (DESIGN §5): a 12px glyph, the tool\n * name in mono, a truncated argument, and a right-aligned duration. A call\n * that changed a file also carries its `+12 −3` there, in the display font,\n * because the size of a change is the thing most worth knowing before\n * deciding to open it.\n *\n * Expanded, the body is chosen by what the call actually DID\n * (`chat/tool-semantics.ts`) rather than by dumping JSON:\n *\n * - an edit / create / patch → a real unified diff (`chat/DiffBlock`,\n * which reuses the inbox parser, highlighter and multi-file viewer)\n * - a shell run → the command, an honest exit status, and\n * output with ANSI stripped and long logs collapsed (`chat/CommandOutput`)\n * - anything else → the input and result as mono blocks, as before\n *\n * A failure tints the left rule and nothing else — the word \"error\" carries\n * the state (DESIGN §6).\n *\n * Paths read relative to the chat's roots (chat/path-display.ts): a Read of\n * `/private/tmp/…/e2e-proj-39/math.ts` is drawn as `math.ts`, with the full\n * path in the tooltip. The disclosure glyph is the same ▸ the activity row\n * and the reasoning row use, so every foldable line opens the same way.\n */\nimport { useMemo } from 'react';\nimport { CommandOutput } from './chat/CommandOutput.js';\nimport { LiveTimer } from './chat/LiveTimer.js';\nimport { DiffBlock } from './chat/DiffBlock.js';\nimport { useDisplayPath } from './chat/path-display.js';\nimport { countDiffLines } from './chat/turn-model.js';\nimport { readToolFacts, toolAnchorId, type ToolFacts } from './chat/tool-semantics.js';\nimport { formatDuration, prettyJson, summarizeToolInput } from './verse-model.js';\nimport styles from './Transcript.module.css';\n\nexport interface ToolUseCardProps {\n name: string;\n input: unknown;\n result: { output: string; isError: boolean } | null;\n /** Stable id so open/closed state can survive re-renders (data-state-key). */\n toolUseId: string;\n /** tool-use → tool-result wall time, when both frames were stamped. */\n durationMs?: number | null;\n /**\n * Derived once per transcript by `chat/turn-model.buildTurns` and handed\n * down, so a card never re-parses a diff the file-activity summary already\n * parsed. Omitted (tests, one-off renders) it is derived here.\n */\n facts?: ToolFacts;\n /** 3.10: ISO start of the call — a pending card counts up from it (\"running 12s\"). */\n startedAt?: string | null;\n}\n\nconst MAX_OUTPUT_CHARS = 20_000;\n\nexport function ToolUseCard({ name, input, result, toolUseId, durationMs = null, facts, startedAt = null }: ToolUseCardProps) {\n const derived = useMemo(\n () => facts ?? readToolFacts({ name, input, result }),\n [facts, name, input, result],\n );\n const show = useDisplayPath();\n // A file call names its file, relative to the chat's roots — never the\n // 96-character head of an absolute path whose filename the cut dropped. A\n // shell run keeps its command line verbatim.\n const target = derived.command ? null : derived.paths[0] ?? null;\n const summary = target === null\n ? summarizeToolInput(input)\n : `${show(target)}${derived.paths.length > 1 ? ` +${derived.paths.length - 1}` : ''}`;\n const isError = derived.failed || result?.isError === true;\n const pending = result === null;\n const state = pending\n ? 'running'\n : isError\n ? 'error'\n : durationMs !== null && durationMs > 0\n ? formatDuration(durationMs)\n : 'done';\n // Re-splitting the whole diff on every render of every card is pure waste:\n // the text is immutable once the result has landed.\n const delta = useMemo(\n () => (derived.diff ? countDiffLines(derived.diff.text) : null),\n [derived.diff],\n );\n const output = result\n ? result.output.length > MAX_OUTPUT_CHARS\n ? `${result.output.slice(0, MAX_OUTPUT_CHARS)}\\n… (${result.output.length - MAX_OUTPUT_CHARS} more characters)`\n : result.output\n : '';\n\n return (\n <details id={toolAnchorId(toolUseId)} className={`${styles.tool} ${isError ? styles.toolFailed : ''}`}\n data-state-key={`tool:${toolUseId}`} data-action={derived.action}>\n <summary className={styles.toolLine} aria-label={`${name}${summary ? `: ${summary}` : ''} (${state})`}>\n <span className={styles.toolGlyph} aria-hidden=\"true\" />\n <span className={styles.toolName}>{name}</span>\n {summary ? <span className={styles.toolArg} title={target ?? summary}>{summary}</span> : null}\n {delta && (delta.additions > 0 || delta.deletions > 0) ? (\n <span className={styles.toolDelta} aria-hidden=\"true\">\n {delta.additions > 0 ? <span className={styles.toolAdd}>+{delta.additions}</span> : null}\n {delta.deletions > 0 ? <span className={styles.toolDel}>−{delta.deletions}</span> : null}\n </span>\n ) : null}\n <span className={styles.toolState}>\n {pending && startedAt ? <>running <LiveTimer since={startedAt} /></> : state}\n </span>\n </summary>\n <div className={styles.toolBody}>\n <ToolBody input={input} output={output} facts={derived} pending={pending} isError={isError} />\n </div>\n </details>\n );\n}\n\ninterface ToolBodyProps {\n input: unknown;\n output: string;\n facts: ToolFacts;\n pending: boolean;\n isError: boolean;\n}\n\nfunction ToolBody({ input, output, facts, pending, isError }: ToolBodyProps) {\n const show = useDisplayPath();\n // A shell run: the command line and exit status always, with the diff in\n // place of the log when the command's own output is one.\n if (facts.command) {\n return (\n <CommandOutput command={facts.command} output={output} isError={isError} pending={pending}\n body={facts.diff ? <DiffBlock diff={facts.diff} path={facts.paths[0] ?? null} /> : undefined} />\n );\n }\n\n if (facts.diff) {\n return (\n <>\n <DiffBlock diff={facts.diff} path={facts.paths[0] ?? null} />\n {pending ? <p className={styles.toolPending}>Waiting for the tool to finish…</p> : null}\n {isError && output ? (\n <section>\n <h4 className={styles.toolLabel}>Error</h4>\n <pre className={styles.toolPre}>{output}</pre>\n </section>\n ) : null}\n </>\n );\n }\n\n if (facts.written) {\n return (\n <>\n <section>\n <h4 className={styles.toolLabel}>\n Contents written to <span className={styles.toolLabelPath} title={facts.written.path}>{show(facts.written.path)}</span>\n </h4>\n <pre className={styles.toolPre}>{facts.written.text}</pre>\n <p className={styles.toolNote}>\n The previous contents were not in this tool call, so there is nothing to diff against.\n </p>\n </section>\n <section>\n <h4 className={styles.toolLabel}>{isError ? 'Error' : 'Result'}</h4>\n {pending ? <p className={styles.toolPending}>Waiting for the tool to finish…</p>\n : <pre className={styles.toolPre}>{output || '(empty)'}</pre>}\n </section>\n </>\n );\n }\n\n return (\n <>\n {input !== null && input !== undefined ? (\n <section>\n <h4 className={styles.toolLabel}>Input</h4>\n <pre className={styles.toolPre}>{prettyJson(input)}</pre>\n </section>\n ) : null}\n <section>\n <h4 className={styles.toolLabel}>{isError ? 'Error' : 'Result'}</h4>\n {pending ? <p className={styles.toolPending}>Waiting for the tool to finish…</p>\n : <pre className={styles.toolPre}>{output || '(empty)'}</pre>}\n </section>\n </>\n );\n}\n","/**\n * routes/verse/Transcript.tsx — the conversation, in one 720px measure.\n *\n * 3.10.1: the user turn is a quiet rounded block on the right (the hover\n * ground, primary text), the assistant turn plain primary text at full\n * measure — role by placement and shape, never by colour alone. A turn ends\n * in a muted footer (how long it took, the jump to its first failure) instead\n * of a lone duration line between turns, and every path a tool touched reads\n * relative to the chat's roots (chat/path-display.ts).\n *\n * Runs of tool calls fold (verse-store groupTranscriptItems) into one\n * activity row — \"Ran 12 commands · read 8 files · edited 3 files · 1 failed · 2m 14s\"\n * (chat/ActivityGroup) — that opens on its failed and running calls by\n * itself, so an agentic turn reads as a summary line between two pieces of\n * prose instead of a wall of cards.\n *\n * 3.10 also adds the ChapterRail (a minimap of turns down the right edge)\n * and a screen-reader announcer that speaks turn boundaries instead of\n * every streamed token.\n *\n * On top of that the items are grouped into TURNS (`chat/turn-model.ts`),\n * which is what makes a long agentic session usable:\n *\n * - each turn ends with a file-activity summary — the blast radius of one\n * ask, with a click-through to the call that changed each file;\n * - the nav strip gives the session an outline, a search over prose AND\n * tool payloads, and a jump to the first failure;\n * - Alt+↑/↓ (and plain ↑/↓ once a turn has focus) step between turns.\n *\n * V3.9: a native compaction renders as a divider that says what the CLI\n * reported (\"Auto-compacted 812k → 41k in 1m 58s\" / \"Codex compacted its\n * context\"), and a chat started as a handoff opens with \"Continued from\n * <source>\", which links back to the chat it continues.\n *\n * V3.10 — live reasoning and a transcript that costs one turn per frame:\n *\n * - the model's reasoning streams in a ThinkingBlock (\"Thinking · 12s ·\n * ~1.8k tok\"), in a three-line window that folds when the block ends\n * (Settings ▸ Chat: Collapsed / Expanded / Hidden);\n * - the live status line (\"Running · 14s · npm test\") and the engine's\n * retry / watchdog notices sit ABOVE THE COMPOSER (Workspace), not in the\n * log: they describe the turn you are waiting on, not its history;\n * - the store hands the transcript over in turn SEGMENTS that keep their\n * identity while unchanged; each segment's turn model is derived once\n * and every finished turn is a memoized TurnView, so a streamed token\n * re-renders the one live turn — not the session. Finished turns also\n * get `content-visibility: auto`, so the browser skips laying out the\n * ones scrolled out of view;\n * - a restored conversation (`recovered`) and a trimmed log\n * (`history-truncated`) say so in the transcript.\n *\n * Follows the newest message unless the operator scrolled up, in which case\n * a \"Jump to latest\" control appears.\n */\nimport { memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react';\nimport type { VerseEngine, VerseSession } from '../../data/api-types.js';\nimport { SkeletonLine } from '../../components/primitives/Skeleton.js';\nimport { ActivityGroupView, revealAnchorInGroups } from './chat/ActivityGroup.js';\nimport { ChapterRail } from './chat/ChapterRail.js';\nimport { PathRootsContext } from './chat/path-display.js';\nimport { buildChapters, chaptersSignature } from './chat/chapter-model.js';\nimport { FileActivity } from './chat/FileActivity.js';\nimport { useReasoningDisplay, type ReasoningDisplay } from './chat/reasoning-pref.js';\nimport { ThinkingBlock } from './chat/ThinkingBlock.js';\nimport { onTranscriptFind, onTranscriptJump, onTranscriptStep } from './chat/transcript-jump.js';\nimport { TranscriptNav } from './chat/TranscriptNav.js';\nimport { TurnAnnouncer } from './chat/TurnAnnouncer.js';\nimport { toolAnchorId, type ToolFacts } from './chat/tool-semantics.js';\nimport { buildTurns, createTurnCache, noteAnchorId, searchTurns, turnAnchorId, type TurnBlock, type TurnCache } from './chat/turn-model.js';\nimport { MessageMarkdown } from './MessageMarkdown.js';\nimport { ToolUseCard, type ToolUseCardProps } from './ToolUseCard.js';\nimport { ArrowDownIcon } from './verse-icons.js';\nimport { ENGINE_LABEL, formatDuration } from './verse-model.js';\nimport { formatTokens } from './verse-readouts.js';\nimport type { VerseLiveState } from './verse-store.js';\nimport {\n groupTranscriptItems,\n type ToolGroupItem,\n type ToolGroupMember,\n type Transcript as TranscriptModel,\n type TranscriptItem,\n type TranscriptRenderItem,\n type TranscriptSegment,\n} from './verse-transcript.js';\nimport styles from './Transcript.module.css';\n\nexport interface TranscriptProps {\n transcript: TranscriptModel;\n loaded: boolean;\n loadError: string | null;\n onRetry?: () => void;\n emptyHint?: string;\n /** Names the CLI in compaction dividers whose counts are unknown (\"Codex compacted its context\"). */\n engine?: VerseEngine;\n /** V3.9: this chat was started as a handoff; the header links back to the source. */\n handoffFrom?: VerseSession['handoffFrom'] | null;\n /** Opens another chat (the handoff source). Absent → the source is named but not a link. */\n onOpenSession?: (sessionId: string) => void;\n /** V3.10: transient signals of the running turn (the streaming reasoning block). */\n live?: VerseLiveState | null;\n /**\n * 3.10.1: the chat's roots, primary first. Tool paths under one of them are\n * drawn relative to it (the full path stays in the tooltip). Absent → the\n * roots already in context (Workspace provides them). Keep the array\n * identity stable — every card showing a path re-renders when it changes.\n */\n projectRoots?: readonly string[];\n}\n\ntype CompactionItem = Extract<TranscriptItem, { kind: 'compaction' }>;\n\n/**\n * One line for a native compaction, from exactly what the CLI reported:\n * \"Auto-compacted 812k → 41k in 1m 58s\" when claude/grok sent the counts,\n * \"Codex compacted its context\" when the CLI records only that it happened.\n * Never a number the CLI did not give.\n */\nexport function describeCompaction(item: Pick<CompactionItem, 'trigger' | 'preTokens' | 'postTokens' | 'durationMs'>, engine?: VerseEngine): string {\n const verb = item.trigger === 'manual' ? 'Compacted on request' : 'Auto-compacted';\n const took = item.durationMs !== null && item.durationMs > 0 ? ` in ${formatDuration(item.durationMs)}` : '';\n if (item.preTokens !== null && item.postTokens !== null) {\n return `${verb} ${formatTokens(item.preTokens)} → ${formatTokens(item.postTokens)}${took}`;\n }\n if (item.preTokens !== null) return `${verb} at ${formatTokens(item.preTokens)}${took}`;\n const who = engine ? ENGINE_LABEL[engine] : 'The CLI';\n return `${who} compacted its context${took}`;\n}\n\n/**\n * What an error `code` (V3.10, VERSE_ERROR_CODES) means for the operator.\n * The message itself is the CLI's own words; this is the \"so what now\".\n */\nexport function errorCodeHint(code: string | null): string | null {\n switch (code) {\n case 'native-thread-missing':\n return 'The CLI no longer has this chat’s saved conversation (it may have expired). Continue in a fresh chat — the transcript here is intact.';\n case 'session-in-use':\n return 'The CLI says this chat’s native session is still held by another process. Wait for it to finish, or continue in a fresh chat.';\n default:\n return null;\n }\n}\n\nconst FOLLOW_THRESHOLD_PX = 48;\n/** Below this the session is short enough to scroll; chrome would be noise. */\nconst NAV_MIN_TURNS = 3;\n/** How long a jumped-to element keeps its locator tint. */\nconst FLASH_MS = 1400;\n\n/** One segment's derived turns — computed once per segment object. */\ninterface SegmentModel {\n turns: TurnBlock[];\n facts: Map<string, ToolFacts>;\n errorAnchors: string[];\n /** turn-done items already explained by a preceding Stop / error in the same turn. */\n explained: ReadonlySet<string>;\n}\n\nfunction explainedKeys(items: readonly TranscriptItem[]): Set<string> {\n // A `turn-done ok:false` that follows the same turn's `cancelled`/`error`\n // note is already explained; a second red \"ended without a result\" line\n // would make every Stop look like a failure.\n const out = new Set<string>();\n let last: { turnId: string | null; kind: string } | null = null;\n for (const item of items) {\n // A compaction between a Stop and its turn-done explains nothing and\n // hides nothing; it must not break the pairing.\n if (item.kind === 'compaction') continue;\n if (item.kind === 'turn-done' && !item.ok && last && last.turnId === item.turnId && (last.kind === 'cancelled' || last.kind === 'error')) {\n out.add(item.key);\n }\n last = { turnId: item.turnId, kind: item.kind };\n }\n return out;\n}\n\nfunction deriveSegment(segment: TranscriptSegment, cache: TurnCache): SegmentModel {\n const model = buildTurns(groupTranscriptItems(segment.items), cache);\n return { turns: model.turns, facts: model.facts, errorAnchors: model.errorAnchors, explained: explainedKeys(segment.items) };\n}\n\nexport function Transcript({ transcript, loaded, loadError, onRetry, emptyHint, engine, handoffFrom = null, onOpenSession, live = null,\n projectRoots }: TranscriptProps) {\n const reasoning = useReasoningDisplay();\n const inheritedRoots = useContext(PathRootsContext);\n const scroller = useRef<HTMLDivElement>(null);\n const turnNodes = useRef(new Map<string, HTMLElement>());\n const flashTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [following, setFollowing] = useState(true);\n const [unseen, setUnseen] = useState(false);\n const [query, setQuery] = useState('');\n const [matchIndex, setMatchIndex] = useState(-1);\n const [focusToken, setFocusToken] = useState(0);\n const lastVersion = useRef('');\n\n // Per-segment derivation. The store returns an unchanged segment as the\n // SAME object, so only the live turn's segment misses this cache while a\n // reply streams; the tool-facts cache below additionally keeps a rebuilt\n // segment from re-deriving tool calls whose results already landed.\n const turnCache = useRef(createTurnCache());\n const segmentModels = useRef(new WeakMap<TranscriptSegment, SegmentModel>());\n const segments = useMemo<TranscriptSegment[]>(\n () => transcript.segments ?? [{ key: 'all', items: transcript.items }],\n [transcript],\n );\n const perSegment = useMemo(() => segments.map((segment) => {\n let model = segmentModels.current.get(segment);\n if (!model) {\n model = deriveSegment(segment, turnCache.current);\n segmentModels.current.set(segment, model);\n }\n return model;\n }), [segments]);\n const turns = useMemo(() => perSegment.flatMap((m) => m.turns), [perSegment]);\n /** Index of each segment's first turn in `turns` (for the per-turn ordinal). */\n const turnOffsets = useMemo(() => {\n const out: number[] = [];\n let n = 0;\n for (const m of perSegment) {\n out.push(n);\n n += m.turns.length;\n }\n return out;\n }, [perSegment]);\n const errorAnchors = useMemo(() => perSegment.flatMap((m) => m.errorAnchors), [perSegment]);\n // The rail's model, rebuilt only when what it draws changes (never per token).\n const turnsForRail = useRef(turns);\n turnsForRail.current = turns;\n const railSignature = chaptersSignature(turns, handoffFrom !== null);\n const chapters = useMemo(\n () => buildChapters(turnsForRail.current, { handoff: handoffFrom !== null }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the signature IS the dependency\n [railSignature],\n );\n const matches = useMemo(\n () => searchTurns(turns, query, turnCache.current),\n [turns, query],\n );\n const matchKeys = useMemo(() => new Set(matches.map((m) => m.turnKey)), [matches]);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = 'auto') => {\n const node = scroller.current;\n if (!node) return;\n // jsdom has no Element.scrollTo; fall back to the property for tests.\n if (typeof node.scrollTo === 'function') node.scrollTo({ top: node.scrollHeight, behavior });\n else node.scrollTop = node.scrollHeight;\n }, []);\n\n function onScroll() {\n const node = scroller.current;\n if (!node) return;\n const distance = node.scrollHeight - node.scrollTop - node.clientHeight;\n const atBottom = distance <= FOLLOW_THRESHOLD_PX;\n setFollowing(atBottom);\n if (atBottom) setUnseen(false);\n }\n\n // ---- jumping -------------------------------------------------------------\n /** Scroll an element into view and tint it briefly so the eye can find it. */\n const reveal = useCallback((node: HTMLElement | null) => {\n if (!node) return;\n setFollowing(false);\n if (typeof node.scrollIntoView === 'function') node.scrollIntoView({ block: 'center', behavior: 'smooth' });\n if (typeof node.focus === 'function') node.focus({ preventScroll: true });\n node.setAttribute('data-flash', 'true');\n if (flashTimer.current) clearTimeout(flashTimer.current);\n flashTimer.current = setTimeout(() => node.removeAttribute('data-flash'), FLASH_MS);\n }, []);\n\n useEffect(() => () => {\n if (flashTimer.current) clearTimeout(flashTimer.current);\n }, []);\n\n const jumpToTurn = useCallback((turnKey: string) => {\n reveal(turnNodes.current.get(turnKey) ?? null);\n }, [reveal]);\n\n /**\n * Open the disclosure chain around a tool call before revealing it. A call\n * folded inside a collapsed activity group is not in the DOM at all: the\n * group is asked to open (revealAnchorInGroups) and the jump retried on\n * the next frame, once it has rendered its members.\n */\n const jumpToAnchor = useCallback((id: string, retried = false): boolean => {\n const node = scroller.current?.ownerDocument?.getElementById(id) ?? null;\n if (!node) {\n if (!retried && revealAnchorInGroups(id)) {\n requestAnimationFrame(() => { jumpToAnchor(id, true); });\n return true;\n }\n return false;\n }\n for (let el: Element | null = node; el; el = el.parentElement) {\n if (el instanceof HTMLDetailsElement) el.open = true;\n if (el === scroller.current) break;\n }\n reveal(node);\n return true;\n }, [reveal]);\n\n // The dock's Tasks pane (and anything else outside this subtree) jumps\n // through the bus; ⌘K \"Find in chat\" focuses the search the same way ⌘F does.\n useEffect(() => onTranscriptJump((id) => jumpToAnchor(id)), [jumpToAnchor]);\n const findable = turns.length >= NAV_MIN_TURNS;\n useEffect(() => onTranscriptFind(() => {\n if (!findable) return false;\n setFocusToken((n) => n + 1);\n return true;\n }), [findable]);\n\n const jumpToTool = useCallback((toolUseId: string) => jumpToAnchor(toolAnchorId(toolUseId)), [jumpToAnchor]);\n\n const jumpToFirstError = useCallback(() => {\n const anchor = errorAnchors[0];\n if (anchor) jumpToAnchor(anchor);\n }, [errorAnchors, jumpToAnchor]);\n\n const registerTurn = useCallback((key: string, node: HTMLElement | null) => {\n if (node) turnNodes.current.set(key, node);\n else turnNodes.current.delete(key);\n }, []);\n\n const stepMatch = useCallback((delta: number) => {\n if (matches.length === 0) return;\n const next = matchIndex < 0\n ? (delta > 0 ? 0 : matches.length - 1)\n : (matchIndex + delta + matches.length) % matches.length;\n setMatchIndex(next);\n jumpToTurn(matches[next]!.turnKey);\n }, [matches, matchIndex, jumpToTurn]);\n\n useEffect(() => { setMatchIndex(-1); }, [query]);\n\n // Read through a ref so the handlers below stay stable while a reply\n // streams (`turns` is a new array every frame); otherwise the document\n // keydown listener would be torn down and re-added per token.\n const turnsRef = useRef(turns);\n turnsRef.current = turns;\n\n /** Move focus n turns from whichever turn currently holds it. */\n const stepTurn = useCallback((delta: number) => {\n const list = turnsRef.current;\n if (list.length === 0) return;\n const active = scroller.current?.ownerDocument?.activeElement;\n const currentKey = active instanceof HTMLElement ? active.getAttribute('data-turn-key') : null;\n const index = currentKey ? list.findIndex((t) => t.key === currentKey) : -1;\n const next = index < 0\n ? (delta > 0 ? 0 : list.length - 1)\n : Math.max(0, Math.min(list.length - 1, index + delta));\n jumpToTurn(list[next]!.key);\n }, [jumpToTurn]);\n\n useEffect(() => onTranscriptStep((delta) => {\n if (turnsRef.current.length === 0) return false;\n stepTurn(delta);\n return true;\n }), [stepTurn]);\n\n // ⌘F focuses the in-chat search; Alt+↑/↓ steps turns from anywhere EXCEPT a\n // text field, where the OS already owns those chords for word/paragraph\n // movement and the composer must keep them.\n useEffect(() => {\n if (turns.length === 0) return;\n const onKey = (event: globalThis.KeyboardEvent) => {\n // The shell's key handler routes these through the command bus (and\n // back here via onTranscriptStep / onTranscriptFind); an event it\n // already took must not step twice.\n if (event.defaultPrevented) return;\n const target = event.target as HTMLElement | null;\n const typing = target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement\n || target?.isContentEditable === true;\n if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === 'f') {\n if (turns.length < NAV_MIN_TURNS) return;\n event.preventDefault();\n setFocusToken((n) => n + 1);\n return;\n }\n if (event.altKey && !event.metaKey && !event.ctrlKey && !typing) {\n if (event.key === 'ArrowDown') { event.preventDefault(); stepTurn(1); }\n else if (event.key === 'ArrowUp') { event.preventDefault(); stepTurn(-1); }\n }\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [turns.length, stepTurn]);\n\n /** Plain ↑/↓ once a turn container itself has focus. */\n function onListKeyDown(event: KeyboardEvent<HTMLOListElement>) {\n const target = event.target as HTMLElement | null;\n if (!target?.hasAttribute('data-turn-key')) return;\n if (event.key === 'ArrowDown') { event.preventDefault(); stepTurn(1); }\n else if (event.key === 'ArrowUp') { event.preventDefault(); stepTurn(-1); }\n else if (event.key === 'Home') { event.preventDefault(); jumpToTurn(turns[0]!.key); }\n else if (event.key === 'End') { event.preventDefault(); jumpToTurn(turns[turns.length - 1]!.key); }\n }\n\n // ---- live turn -----------------------------------------------------------\n // The live STATUS line moved above the composer in 3.10 (Workspace\n // LiveRow); what stays here is the reasoning streaming into the turn.\n const running = transcript.live;\n const liveThinking = running && reasoning !== 'hidden' && live?.thinking && live.thinking.turnId === live.turnId ? live.thinking : null;\n const lastTurn = turns.length > 0 ? turns[turns.length - 1]! : null;\n\n // Auto-follow new content only while pinned to the bottom. \"New content\"\n // includes the live parts: streamed reasoning and a notice appearing.\n const lastItem = transcript.items[transcript.items.length - 1];\n const version = `${transcript.items.length}:${lastItem?.kind === 'assistant' ? lastItem.text.length : 0}:${liveThinking?.text.length ?? -1}:${running ? 1 : 0}`;\n useLayoutEffect(() => {\n if (version === lastVersion.current) return;\n lastVersion.current = version;\n if (following) scrollToBottom();\n else setUnseen(true);\n }, [version, following, scrollToBottom]);\n\n useEffect(() => {\n if (loaded) scrollToBottom();\n }, [loaded, scrollToBottom]);\n\n if (!loaded && !loadError) {\n return (\n <div className={styles.transcript} aria-busy=\"true\">\n <div className={styles.skeleton}>\n <div className={styles.skeletonUser}><SkeletonLine width=\"42%\" /></div>\n <SkeletonLine width=\"88%\" /><SkeletonLine width=\"72%\" /><SkeletonLine width=\"80%\" />\n <div className={styles.skeletonUser}><SkeletonLine width=\"30%\" /></div>\n <SkeletonLine width=\"64%\" />\n </div>\n </div>\n );\n }\n\n if (loadError) {\n return (\n <div className={styles.transcript}>\n <div role=\"alert\" className={styles.loadError}>\n <p><span className={styles.noteLead}>Couldn’t load this chat.</span> {loadError}</p>\n {onRetry ? <button type=\"button\" onClick={onRetry}>Retry</button> : null}\n </div>\n </div>\n );\n }\n\n const activeMatchKey = matchIndex >= 0 ? matches[matchIndex]?.turnKey ?? null : null;\n\n return (\n <PathRootsContext.Provider value={projectRoots ?? inheritedRoots}>\n <div className={styles.transcriptWrap}>\n {turns.length >= NAV_MIN_TURNS ? (\n <TranscriptNav turns={turns} query={query} onQuery={setQuery} matches={matches} matchIndex={matchIndex}\n onStepMatch={stepMatch} errorCount={errorAnchors.length} onJumpError={jumpToFirstError}\n onJumpTurn={jumpToTurn} focusToken={focusToken} />\n ) : null}\n {/* The log is NOT a live region (3.10): streamed tokens announced one by\n one made a screen reader unusable mid-turn. TurnAnnouncer says only\n when a turn starts, finishes, fails or is stopped. */}\n <TurnAnnouncer running={running} lastStatus={lastTurn?.status ?? null} lastKey={lastTurn?.key ?? null} />\n <div className={styles.scrollArea}>\n <div ref={scroller} className={styles.transcript} onScroll={onScroll} role=\"log\" aria-live=\"off\"\n aria-label=\"Conversation\" data-has-rail={turns.length >= NAV_MIN_TURNS || undefined}>\n {handoffFrom ? (\n <p className={styles.continued} data-kind=\"handoff-from\">\n <span className={styles.continuedLabel}>Continued from</span>{' '}\n {onOpenSession ? (\n <button type=\"button\" className={styles.continuedLink} onClick={() => onOpenSession(handoffFrom.sessionId)}\n title=\"Open the chat this one continues\">\n {handoffFrom.title || 'Untitled chat'}\n </button>\n ) : <span className={styles.continuedTitle}>{handoffFrom.title || 'Untitled chat'}</span>}\n </p>\n ) : null}\n {transcript.items.length === 0 && !running ? (\n <div className={styles.empty}>\n <p className={styles.emptyTitle}>{handoffFrom ? 'Review the handoff, then send it.' : 'Say something to begin.'}</p>\n <p className={styles.emptyBody}>{emptyHint ?? (handoffFrom\n ? 'The note in the message box was drafted from the previous chat\\'s log. Edit it freely — nothing is sent until you press Send.'\n : 'The agent can read and edit this project. Ask for a change, a review, or an explanation.')}</p>\n </div>\n ) : null}\n <ol className={styles.list} onKeyDown={onListKeyDown}>\n {perSegment.map((segment, s) => segment.turns.map((turn, t) => (\n <TurnView key={turn.key} turn={turn} index={turnOffsets[s]! + t} facts={segment.facts} explained={segment.explained}\n engine={engine} match={matchKeys.has(turn.key) ? (turn.key === activeMatchKey ? 'active' : 'true') : undefined}\n reasoning={reasoning} onJumpTool={jumpToTool} onJumpAnchor={jumpToAnchor} registerNode={registerTurn} />\n )))}\n {/* The reasoning streaming right now — it becomes an ordinary\n thinking item the moment its persisted block lands. */}\n {liveThinking ? (\n <li className={styles.item} data-kind=\"thinking-live\">\n <ThinkingBlock key={`live-${liveThinking.startedAt}`} text={liveThinking.text} streaming startedAt={liveThinking.startedAt}\n estimatedTokens={liveThinking.estimatedTokens} defaultOpen />\n </li>\n ) : null}\n </ol>\n </div>\n {turns.length >= NAV_MIN_TURNS ? (\n <ChapterRail model={chapters} onJumpTurn={jumpToTurn} />\n ) : null}\n </div>\n {!following ? (\n <button type=\"button\" className={`${styles.jump} ${unseen ? styles.jumpUnseen : ''}`}\n onClick={() => { setFollowing(true); setUnseen(false); scrollToBottom('smooth'); }}>\n <ArrowDownIcon size={12} /> Jump to latest\n </button>\n ) : null}\n </div>\n </PathRootsContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// One turn\n// ---------------------------------------------------------------------------\n\ninterface TurnViewProps {\n turn: TurnBlock;\n index: number;\n facts: Map<string, ToolFacts>;\n explained: ReadonlySet<string>;\n engine?: VerseEngine;\n match: 'active' | 'true' | undefined;\n reasoning: ReasoningDisplay;\n onJumpTool: (toolUseId: string) => void;\n onJumpAnchor: (id: string) => boolean;\n registerNode: (key: string, node: HTMLElement | null) => void;\n}\n\n/**\n * Memoized on its props, all of which are stable for a finished turn: the\n * TurnBlock comes from a cached segment model, the callbacks are stable, and\n * `match` only changes for turns a search touches. So a streamed token\n * re-renders exactly one TurnView.\n */\nconst TurnView = memo(function TurnView({ turn, index, facts, explained, engine, match, reasoning, onJumpTool, onJumpAnchor, registerNode }: TurnViewProps) {\n const running = turn.status === 'running';\n // How long a settled turn took (turn-model: from its turn-done; unknown →\n // null, never 0). The turn's footer, not a line of its own between turns.\n const tookMs = running ? null : turn.durationMs;\n const errorJump = turn.errorCount > 0 && turn.firstErrorAnchor ? turn.firstErrorAnchor : null;\n return (\n <li\n id={turnAnchorId(turn.key)}\n ref={(node) => registerNode(turn.key, node)}\n // A settled turn no longer changes: let the browser skip its layout\n // and paint while it is off screen (Transcript.module.css .turnSettled).\n className={`${styles.turn} ${running ? '' : styles.turnSettled}`}\n data-turn-key={turn.key}\n data-status={turn.status}\n data-match={match}\n tabIndex={-1}\n aria-label={`Turn ${index + 1}`}\n >\n <ol className={styles.turnItems}>\n {turn.items.map((item) => renderItem(item, facts, explained, reasoning, engine))}\n </ol>\n {turn.files.length > 0 ? <FileActivity files={turn.files} onJump={onJumpTool} /> : null}\n {tookMs !== null || errorJump ? (\n <footer className={styles.turnFoot} data-kind=\"turn-meta\">\n {tookMs !== null ? (\n <span><span className=\"visually-hidden\">Turn took </span><span>{formatDuration(tookMs)}</span></span>\n ) : null}\n {errorJump ? (\n <button type=\"button\" className={styles.turnErrorJump} onClick={() => onJumpAnchor(errorJump)}>\n {turn.errorCount} failure{turn.errorCount === 1 ? '' : 's'} in this turn — jump to the first\n </button>\n ) : null}\n </footer>\n ) : null}\n </li>\n );\n});\n\n/**\n * A tool card re-renders only when what it shows changed. `result` is a\n * fresh object on every rebuild of the live turn, so identity is not enough;\n * `input` and `facts` are stable (the event's own object, the facts cache).\n */\nconst MemoToolUseCard = memo(ToolUseCard, (a: ToolUseCardProps, b: ToolUseCardProps) =>\n a.name === b.name && a.input === b.input && a.toolUseId === b.toolUseId && a.durationMs === b.durationMs &&\n a.facts === b.facts && a.result?.output === b.result?.output && a.result?.isError === b.result?.isError &&\n (a.result === null) === (b.result === null));\n\nfunction renderItem(item: TranscriptRenderItem, facts: Map<string, ToolFacts>, explained: ReadonlySet<string>, reasoning: ReasoningDisplay, engine?: VerseEngine) {\n switch (item.kind) {\n case 'user':\n return (\n <li key={item.key} className={`${styles.item} ${styles.user}`} data-kind=\"user\">\n <div className={styles.userText}>{item.text}</div>\n </li>\n );\n case 'assistant':\n return (\n <li key={item.key} className={`${styles.item} ${styles.assistant}`} data-kind=\"assistant\" data-streaming={item.streaming || undefined}>\n <MessageMarkdown text={item.text} streaming={item.streaming} />\n </li>\n );\n case 'thinking':\n if (reasoning === 'hidden') return null;\n return (\n <li key={item.key} className={styles.item} data-kind=\"thinking\">\n <ThinkingBlock text={item.text} redacted={item.redacted} durationMs={item.durationMs}\n estimatedTokens={item.estimatedTokens} kind={item.thinkingKind}\n // 3.10: a settled block folds (it streamed in its three-line\n // window while it ran) unless Settings ▸ Chat says Expanded.\n defaultOpen={reasoning === 'expanded'} stateKey={`thinking:${item.key}`} />\n </li>\n );\n case 'tool':\n return (\n <li key={item.key} className={styles.item} data-kind=\"tool\">\n <MemoToolUseCard name={item.name} input={item.input} result={item.result} toolUseId={item.toolUseId}\n durationMs={item.durationMs} facts={facts.get(item.toolUseId)} startedAt={item.at} />\n </li>\n );\n case 'toolGroup':\n return (\n <li key={item.key} className={styles.item} data-kind=\"tool-group\">\n <ActivityGroup item={item} facts={facts} reasoning={reasoning} />\n </li>\n );\n case 'error': {\n const hint = errorCodeHint(item.code);\n return (\n <li key={item.key} className={styles.item} data-kind=\"error\" data-code={item.code ?? undefined}>\n <div id={noteAnchorId(item.key)} role=\"alert\" className={`${styles.note} ${styles.noteError}`}>\n {/* The state in words — unless the CLI's own message already opens with it. */}\n {/^error\\b/i.test(item.message) ? null : <><span className={styles.noteLead}>Error</span>{' '}</>}{item.message}\n {hint ? <span className={styles.noteHint}>{hint}</span> : null}\n </div>\n </li>\n );\n }\n case 'compaction':\n // A divider, not a message: the conversation continues on both sides of\n // it, but the agent sees everything above it only as a summary.\n return (\n <li key={item.key} className={`${styles.item} ${styles.compaction}`} data-kind=\"compaction\">\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n <span className={styles.compactionText}>\n <span className={styles.compactionTitle}>{describeCompaction(item, engine)}</span>\n <span className={styles.compactionNote}>Earlier turns now reach the agent only as a summary; the full transcript stays here.</span>\n </span>\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n </li>\n );\n case 'truncated':\n return (\n <li key={item.key} className={`${styles.item} ${styles.compaction}`} data-kind=\"history-truncated\">\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n <span className={styles.compactionText}>\n <span className={styles.compactionTitle}>Older history trimmed</span>\n <span className={styles.compactionNote}>This chat’s log reached its size cap, so its oldest turns were dropped here. The agent’s own conversation is unaffected.</span>\n </span>\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n </li>\n );\n case 'recovered':\n return (\n <li key={item.key} className={styles.item} data-kind=\"recovered\">\n <div className={`${styles.note} ${styles.noteRecovered}`} role=\"status\">\n <span className={styles.noteLead}>{item.how === 'handoff' ? 'Recovered from the handoff note' : 'Recovered in a new session'}</span>\n {' — '}{item.message}\n </div>\n </li>\n );\n case 'cancelled':\n return (\n <li key={item.key} className={styles.item} data-kind=\"cancelled\">\n <div className={styles.note}>Stopped.</div>\n </li>\n );\n case 'turn-done':\n // A clean (or already-explained) end is not a line of its own, and how\n // long ANY turn took is its footer (TurnView), never this item.\n if (item.ok || explained.has(item.key)) return null;\n return (\n <li key={item.key} className={styles.item} data-kind=\"turn-done\">\n <div id={noteAnchorId(item.key)} className={`${styles.note} ${styles.noteError}`}>\n <span className={styles.noteLead}>Failed</span> Turn ended without a result.\n </div>\n </li>\n );\n default:\n return null;\n }\n}\n\nfunction sameMembers(a: ToolGroupItem, b: ToolGroupItem, fa: Map<string, ToolFacts>, fb: Map<string, ToolFacts>): boolean {\n if (a === b) return fa === fb || a.items.every((m) => m.kind !== 'tool' || fa.get(m.toolUseId) === fb.get(m.toolUseId));\n if (a.key !== b.key || a.items.length !== b.items.length || a.errorCount !== b.errorCount || a.pending !== b.pending ||\n a.spanMs !== b.spanMs || a.summary !== b.summary) return false;\n for (let i = 0; i < a.items.length; i += 1) {\n const x = a.items[i]!;\n const y = b.items[i]!;\n if (x.key !== y.key || x.kind !== y.kind) return false;\n if (x.kind === 'tool' && y.kind === 'tool') {\n if (x.result?.output !== y.result?.output || x.result?.isError !== y.result?.isError || x.durationMs !== y.durationMs) return false;\n if (fa.get(x.toolUseId) !== fb.get(y.toolUseId)) return false;\n } else if (x.kind === 'thinking' && y.kind === 'thinking') {\n if (x.text !== y.text || x.durationMs !== y.durationMs || x.estimatedTokens !== y.estimatedTokens) return false;\n }\n }\n return true;\n}\n\n/**\n * One member of a folded run: a tool card, or the reasoning that explained\n * the next call (hidden when Settings ▸ Chat says so).\n */\nfunction renderMember(member: ToolGroupMember, facts: Map<string, ToolFacts>, reasoning: ReasoningDisplay) {\n if (member.kind === 'tool') {\n return (\n <MemoToolUseCard name={member.name} input={member.input} result={member.result}\n toolUseId={member.toolUseId} durationMs={member.durationMs} facts={facts.get(member.toolUseId)} startedAt={member.at} />\n );\n }\n if (reasoning === 'hidden') return null;\n return (\n <ThinkingBlock text={member.text} redacted={member.redacted} durationMs={member.durationMs}\n estimatedTokens={member.estimatedTokens} kind={member.thinkingKind} defaultOpen={reasoning === 'expanded'}\n stateKey={`thinking:${member.key}`} />\n );\n}\n\n/**\n * The folded run (3.10: chat/ActivityGroup — \"Ran 12 commands · read 8 files ·\n * edited 3 files · 1 failed\"). Re-rendered only when a member's visible facts\n * changed: the live turn rebuilds its item objects on every token, and\n * `sameMembers` compares what the row shows rather than object identity.\n */\nconst ActivityGroup = memo(function ActivityGroup({ item, facts, reasoning }: { item: ToolGroupItem; facts: Map<string, ToolFacts>; reasoning: ReasoningDisplay }) {\n const render = useCallback((member: ToolGroupMember) => renderMember(member, facts, reasoning), [facts, reasoning]);\n return <ActivityGroupView item={item} facts={facts} renderMember={render} />;\n}, (a, b) => a.reasoning === b.reasoning && sameMembers(a.item, b.item, a.facts, b.facts));\n","/**\n * routes/verse/SeatCapacity.tsx — the shared rendering of a seat's\n * subscription capacity, so the resources panel, the new-chat dialog and the\n * chat header cannot describe the same seat three different ways.\n *\n * Three pieces, each one a rule from docs/VERSE-DESIGN-V2.md §6 and\n * docs/VERSE-TELEMETRY-V2.md made concrete:\n *\n * <CapacityChip> the four-word verdict. Always the WORD, never colour\n * alone; the dot is decoration on top of the word.\n * <SeatWindowMeter> one quota window. A measured window gets a real\n * <Meter>; a flagged limit gets a full danger meter\n * labelled \"limit reached\" and NO percentage, because\n * the sentinel 100 upstream is a flag rather than a\n * reading; an unread window gets a dashed rule and a\n * sentence, never a bar — an empty bar reads \"plenty\n * left\" and a full one reads \"exhausted\", and both are\n * lies when nothing was measured.\n * <SeatCapacityNote> plan · verdict · binding meter · reset · credits, for\n * the surfaces that show one seat at a time.\n *\n * Reset text is rendered VERBATIM. Claude publishes a sentence\n * (\"resets Sep 25 at 7pm (America/New_York)\") and `resetsAt` is structurally\n * null for it, so nothing here parses a reset or turns one into a countdown.\n */\nimport type { ReactNode } from 'react';\nimport type { VerseSeat } from '../../data/api-types.js';\nimport { Meter } from '../../components/primitives/Meter.js';\nimport { percentText } from './autonomy/format.js';\nimport {\n seatSubscription,\n type SeatCapacityClass,\n type SeatSubscriptionView,\n type SeatWindowView,\n} from './seat-subscription.js';\nimport styles from './SeatCapacity.module.css';\n\n/**\n * A glyph per class so the state survives greyscale, a colour-blind reader\n * and a screenshot: check (usable), half (tight), bar (blocked), query\n * (no reading). Paired with the word, never standing in for it.\n */\nconst CLASS_GLYPH: Record<SeatCapacityClass, string> = {\n ready: '✓',\n tight: '◑',\n blocked: '▮',\n unread: '?',\n};\n\nexport function CapacityChip({\n view,\n title,\n}: {\n view: SeatSubscriptionView;\n title?: string;\n}): ReactNode {\n return (\n <span className={styles.chip} data-capacity={view.cls} title={title ?? view.summary}>\n <span className={styles.chipGlyph} aria-hidden=\"true\">{CLASS_GLYPH[view.cls]}</span>\n {view.word}\n </span>\n );\n}\n\nexport function SeatWindowMeter({\n windowView,\n ariaPrefix,\n prominent = false,\n}: {\n windowView: SeatWindowView;\n /** Seat label, so every meter on the page has a unique accessible name. */\n ariaPrefix: string;\n /** The binding window — the one that actually constrains work. */\n prominent?: boolean;\n}): ReactNode {\n const name = `${ariaPrefix} ${windowView.label}`;\n\n if (windowView.limitReached) {\n return (\n <div className={prominent ? styles.windowLead : styles.window}>\n <Meter\n value={100}\n max={100}\n tone=\"danger\"\n label={<span className={styles.windowLabel}>{windowView.label}</span>}\n valueText={<span className={styles.flag}>limit reached</span>}\n aria-label={`${name}: the provider flagged this window as rate-limited, so no percentage was measured`}\n />\n {windowView.resetText === null ? null : <p className={styles.reset}>{windowView.resetText}</p>}\n </div>\n );\n }\n\n if (windowView.usedPercent === null) {\n return (\n <div className={prominent ? styles.windowLead : styles.window}>\n <div className={styles.unknownHead}>\n <span className={styles.windowLabel}>{windowView.label}</span>\n <span className={styles.unknownValue}>no reading</span>\n </div>\n {/* Absence, drawn as absence. Never a 0% bar. */}\n <hr className={styles.unknownRule} aria-hidden=\"true\" />\n {windowView.resetText === null ? null : <p className={styles.reset}>{windowView.resetText}</p>}\n </div>\n );\n }\n\n return (\n <div className={prominent ? styles.windowLead : styles.window}>\n <Meter\n value={Math.max(0, Math.min(100, windowView.usedPercent))}\n max={100}\n label={<span className={styles.windowLabel}>{windowView.label}</span>}\n valueText={<span className={styles.pct}>{percentText(windowView.usedPercent)}</span>}\n aria-label={`${name} used`}\n />\n {windowView.resetText === null ? null : <p className={styles.reset}>{windowView.resetText}</p>}\n </div>\n );\n}\n\n/**\n * One seat's capacity in a few lines, for the surfaces that show a single\n * seat: the new-chat dialog under the picker, and anywhere else a choice is\n * about to be made. Local seats say so instead of being given meters.\n */\nexport function SeatCapacityNote({ seat, className }: { seat: VerseSeat; className?: string }): ReactNode {\n const view = seatSubscription(seat);\n return (\n <div className={`${styles.note} ${className ?? ''}`} data-capacity={view.cls}>\n <div className={styles.noteHead}>\n <CapacityChip view={view} />\n {view.plan === null ? null : <span className={styles.plan}>{view.plan}</span>}\n <span className={styles.noteSummary}>{view.summary}</span>\n </div>\n {view.binding === null ? null : (\n <SeatWindowMeter windowView={view.binding} ariaPrefix={seat.label} prominent />\n )}\n {view.credits === null ? null : (\n <p className={styles.credits} title={view.creditsTitle ?? undefined}>\n {view.credits} — credits are independent of the window\n </p>\n )}\n </div>\n );\n}\n","/**\n * routes/verse/health/SeatHealthBanner.tsx — \"which of my seats can't work,\n * why, and what fixes it\", in one strip (V3.10, unit A2).\n *\n * Renders NOTHING while every seat is fine: a banner that is always there is\n * a banner nobody reads. When something is wrong it names the seat, the fact\n * (signed out / out of usage / sign-in expiring / older CLI pinned), the\n * reset time when the provider gave one, and the one action that fixes it:\n * - Reconnect (signed out, expiring) — the server opens the seat's OWN\n * sign-in in Terminal. The app cannot and does not sign in for you.\n * - the repin command, copyable (older CLI pinned) — repinning changes\n * which models the seat runs, so it stays the operator's decision.\n * - nothing but the reset time (out of usage).\n * \"Check again\" runs a sweep now (status commands only, zero cost).\n *\n * Two exports: `SeatHealthBanner` fetches and polls on its own;\n * `SeatHealthBannerView` renders reports it is handed (tests, or a parent\n * that already holds them).\n */\nimport { useState } from 'react';\nimport type { SeatHealthReport } from '../../../../core/verse/health-types.js';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { IconAlert, IconCopy, IconExternalLink, IconRefresh } from '../../../components/primitives/icons.js';\nimport type { VerseSeat } from '../../../data/api-types.js';\nimport { reconnectSeat, refreshSeatHealth } from './health-queries.js';\nimport { issuesHeadline, seatHealthIssues, shellCommandText, type SeatHealthIssue } from './health-model.js';\nimport { useSeatHealth } from './useSeatHealth.js';\nimport styles from './SeatHealth.module.css';\n\nexport interface SeatHealthBannerViewProps {\n reports: readonly SeatHealthReport[];\n /** For labels; a seat missing here is named by its id. */\n seats?: readonly VerseSeat[];\n onReconnect?: (seatId: string) => Promise<void>;\n onRefresh?: () => Promise<unknown>;\n /** Injected clock for tests. */\n now?: number;\n}\n\ntype RowStatus = { text: string; error: boolean } | null;\n\nfunction errorText(error: unknown, fallback: string): string {\n return error instanceof Error && error.message ? error.message : fallback;\n}\n\nfunction IssueRow({ issue, onReconnect }: { issue: SeatHealthIssue; onReconnect: (seatId: string) => Promise<void> }) {\n const [busy, setBusy] = useState(false);\n const [status, setStatus] = useState<RowStatus>(null);\n const { report } = issue;\n const command = report.fix.command && report.fix.command.length > 0 ? shellCommandText(report.fix.command) : null;\n\n const reconnect = async (): Promise<void> => {\n setBusy(true);\n setStatus(null);\n try {\n await onReconnect(report.seatId);\n setStatus({ text: 'Sign-in opened in Terminal. Finish there, then choose Check again.', error: false });\n } catch (error) {\n setStatus({ text: errorText(error, 'The sign-in window could not be opened.'), error: true });\n } finally {\n setBusy(false);\n }\n };\n\n const copy = async (): Promise<void> => {\n if (command === null) return;\n try {\n await navigator.clipboard.writeText(command);\n setStatus({ text: 'Command copied.', error: false });\n } catch {\n setStatus({ text: 'Copy failed — select the command and copy it by hand.', error: true });\n }\n };\n\n return (\n <li className={styles.row} data-tone={issue.tone} data-seat={report.seatId}>\n <div className={styles.rowHead}>\n <span className={styles.dot} aria-hidden=\"true\" />\n <span className={styles.label}>{issue.label}</span>\n <span className={styles.word}>{issue.word}</span>\n {issue.reset === null ? null : (\n <span className={styles.reset}>\n {issue.reset}\n {issue.usableAgain === null ? null : ` · ${issue.usableAgain}`}\n </span>\n )}\n </div>\n {issue.detail === null ? null : <p className={styles.detail}>{issue.detail}</p>}\n {report.fix.kind === 'reauth' || command !== null ? (\n <div className={styles.actions}>\n {report.fix.kind === 'reauth' ? (\n <Button size=\"sm\" variant=\"primary\" icon={<IconExternalLink />} busy={busy} onClick={() => { void reconnect(); }}\n aria-label={`Reconnect ${issue.label}`}>\n Reconnect\n </Button>\n ) : null}\n {command !== null ? (\n <>\n <code className={styles.command}>{command}</code>\n <Button size=\"sm\" variant=\"subtle\" icon={<IconCopy />} onClick={() => { void copy(); }}\n aria-label={`Copy the repin command for ${issue.label}`}>\n Copy\n </Button>\n </>\n ) : null}\n </div>\n ) : null}\n <p className={styles.status} aria-live=\"polite\" data-error={status?.error ? 'true' : undefined}>\n {status?.text ?? ''}\n </p>\n </li>\n );\n}\n\nexport function SeatHealthBannerView({\n reports,\n seats = [],\n onReconnect = reconnectSeat,\n onRefresh = refreshSeatHealth,\n now,\n}: SeatHealthBannerViewProps) {\n const [checking, setChecking] = useState(false);\n const [checkError, setCheckError] = useState<string | null>(null);\n const issues = seatHealthIssues(reports, seats, now);\n if (issues.length === 0) return null;\n const tone = issues[0]!.tone;\n\n const check = async (): Promise<void> => {\n setChecking(true);\n setCheckError(null);\n try {\n await onRefresh();\n } catch (error) {\n setCheckError(errorText(error, 'The check could not run.'));\n } finally {\n setChecking(false);\n }\n };\n\n return (\n <section className={styles.banner} data-tone={tone} aria-label=\"Seat health\">\n <div className={styles.head}>\n <IconAlert />\n <span className={styles.headline}>{issuesHeadline(issues)}</span>\n <Button size=\"sm\" variant=\"ghost\" icon={<IconRefresh />} busy={checking} onClick={() => { void check(); }}>\n Check again\n </Button>\n </div>\n <ul className={styles.list}>\n {issues.map((issue) => <IssueRow key={issue.report.seatId} issue={issue} onReconnect={onReconnect} />)}\n </ul>\n {checkError === null ? null : <p className={styles.status} data-error=\"true\" role=\"status\">{checkError}</p>}\n </section>\n );\n}\n\nexport interface SeatHealthBannerProps {\n seats?: readonly VerseSeat[];\n /** False stops polling (the last reading still renders). Default true. */\n active?: boolean;\n}\n\n/** Self-fetching banner: polls `/api/verse/health` every 30 s while visible. */\nexport function SeatHealthBanner({ seats = [], active = true }: SeatHealthBannerProps) {\n const health = useSeatHealth(active);\n const reports = health.data?.seats ?? [];\n return <SeatHealthBannerView reports={reports} seats={seats} />;\n}\n","/**\n * routes/verse/Workspace.tsx — the chat pane: the header strip, the 720px\n * transcript, and — stacked above the composer — everything about the turn\n * you are about to send or are waiting on.\n *\n * THE HEADER (3.10, SPEC-310C §2) reads left to right, in the order a person\n * asks the questions:\n *\n * [ hub › v310-foundation ] [C Ready] [insight] …… [◔ 42%] [⋯] │ [>_][▭][±] │ [⫿][▯]\n * [ Chat title ]\n *\n * 1. WHERE AM I — the chat title under a `repo › branch` breadcrumb (the\n * branch from the chat's shared roots read). The only thing in the strip\n * allowed to shrink; both lines ellipsise.\n * 2. WHAT STATE IS IT IN — a compact status: an engine tick and its monogram\n * (C/X/G/L, never a vendor logo), then Ready / Running / Last turn failed /\n * Read-only, and a capacity chip only when the seat is tight or spent.\n * 3.10.1: the seat and model are NOT repeated here — the composer's seat\n * chip names them, one step below; the chip's tooltip still says both,\n * and the chip is a tab stop so that tooltip opens on keyboard focus.\n * 3. WHAT DO I KNOW — C7's insight chip when an A7 insight cites this chat.\n * 4. WHAT CAN I DO — the context ring (its tooltip is the whole context\n * story), then ⋯: context mode, compact, hand off, copy id, delete.\n * 5. WHICH PANES — Terminal, Preview and Review toggles (each only once its\n * unit's pane exists in this build), then the chat list and the dock.\n * Every icon-only control has an accessible name AND a tooltip (the\n * Tooltip primitive, so it shows on keyboard focus too) with its key.\n *\n * NOTHING stacks under the header any more. Seat health, the engine's\n * retry / watchdog notices, context advice and the compact panel share ONE\n * notice slot above the composer (chat/NoticeSlot), in order of consequence.\n * Under it, in this order and each only when it has something to say:\n *\n * notice one at a time, \"+N more\"\n * queued C3's \"Queued, sends when this turn ends [Edit] [Send now] [×]\",\n * portalled out of the Composer into a host here (its state\n * lives in the Composer; its PLACE is this order)\n * live row ● Running npm test · 1m 02s · 38 tok/s Stop ◌ 3 running tasks\n * branch bar C5's slot: repo branch +35,079 −1,074 [Create PR ▾]\n * composer C3's — wired to the handoff (`/handoff`, Continue on ‹seat›)\n * and to the composer bridge (text the dock drafts in)\n *\n * With nothing selected the strip is not empty: it says \"Chat / No chat\n * selected\", and the seat-health banner still shows (a signed-out seat is\n * news before you pick a chat).\n *\n * This component owns the seat poll (useSeatsRefresh) because it is mounted\n * for the whole life of the Chat section — see that hook for why a\n * mount-time snapshot of seats is guaranteed to be the cold one.\n */\nimport { lazy, Suspense, useCallback, useEffect, useId, useMemo, useRef, useState, type ComponentProps, type KeyboardEvent, type ReactNode } from 'react';\nimport type { VerseContextMode, VerseProject, VerseSeat, VerseSession } from '../../data/api-types.js';\nimport { engineSupportsModes, hasExpansiveMode } from '../../../core/verse/context-math.js';\nimport { MutationTokenDialog } from '../../components/auth/MutationTokenDialog.js';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport { ActionMenu, anchorBelow, type ActionMenuItem } from './chat/ActionMenu.js';\nimport { appendParagraph, registerComposerInserter } from './chat/composer-bridge.js';\nimport { derivePhaseFromTranscript, LiveStatus } from './chat/LiveStatus.js';\nimport { NoticeSlot, type NoticeCandidate } from './chat/NoticeSlot.js';\nimport { PathRootsContext } from './chat/path-display.js';\nimport { TasksTray } from './chat/TasksTray.js';\nimport { countTasks, currentTurnTasks, type ChatTask } from './chat/tasks-model.js';\nimport { useSessionRoots } from './chat/use-session-roots.js';\nimport { Composer } from './Composer.js';\nimport { setSessionContextMode } from './context/context-queries.js';\nimport { describeContextError, useTokenGate } from './context/use-token-gate.js';\nimport {\n canCompactNow,\n CompactPanel,\n ContextAdvice,\n contextAdviceVisible,\n ContextMeter,\n CONTEXT_MODE_LABEL,\n expansiveCostCopy,\n standardSwitchCompacts,\n} from './ContextMeter.js';\nimport { CopyGlyph, HandoffGlyph, MoreGlyph, RenameGlyph, TrashGlyph, DOCK_PANE_GLYPH } from './dock/dock-icons.js';\nimport { openDockPane, requestDiff, toggleDock, toggleDockPane, useDock } from './dock/dock-store.js';\nimport { DOCK_PANE_LABEL, isPaneAvailable } from './dock/dock-panes.js';\nimport { saveDraft } from './chat/composer-state.js';\nimport type { SeatChoice } from './SeatSelector.js';\nimport { BranchBarSlot, SessionInsightChipSlot } from './shell/slots.js';\nimport type { DockPaneId } from './shell/dock-catalog.js';\nimport { findCommand, formatChord } from './shell/command-catalog.js';\nimport { Transcript } from './Transcript.js';\nimport { PanelIcon, SidebarIcon, VerseMark } from './verse-icons.js';\nimport { CapacityChip } from './SeatCapacity.js';\nimport { seatHealthIssues } from './health/health-model.js';\nimport { SeatHealthBannerView } from './health/SeatHealthBanner.js';\nimport { useSeatHealth } from './health/useSeatHealth.js';\nimport { seatSubscription, seatSubscriptionSentence, worthFlagging } from './seat-subscription.js';\nimport { projectName, seatById, seatPillLabel, sessionContextBudget, type SessionContextBudget } from './verse-model.js';\nimport { invalidateVerseLists } from './verse-queries.js';\nimport { formatTokens, lastTurnActivityAt } from './verse-readouts.js';\nimport { setVerseSession, type VerseLiveNotice } from './verse-store.js';\nimport { rememberVerseSeat } from './verse-ui-store.js';\nimport { useSeatsRefresh } from './useSeatsRefresh.js';\nimport { useVerseLive, type VerseSessionView } from './useVerseSession.js';\nimport { useVerseTranscript } from './useVerseTranscript.js';\nimport styles from './Workspace.module.css';\nimport { ProviderLogo } from '../../components/primitives/ProviderLogo.js';\n\n// Opened on request only: kept out of the chat's first-paint chunk.\nconst HandoffDialog = lazy(() => import('./context/HandoffDialog.js').then((m) => ({ default: m.HandoffDialog })));\n\nexport interface WorkspaceProps {\n view: VerseSessionView;\n seats: readonly VerseSeat[];\n projects: readonly VerseProject[];\n dispatchEnabled: boolean;\n locked: boolean;\n hasAnySessions: boolean;\n onSend: (text: string) => Promise<boolean>;\n onStop: () => void;\n onRename: (title: string) => Promise<boolean>;\n /** Asks for confirmation (ChatSection's dialog), then deletes. */\n onRequestDelete: () => void;\n onSeatChange: (choice: SeatChoice) => void;\n onNew: () => void;\n onRetry: () => void;\n sidebarCollapsed: boolean;\n onToggleSidebar: () => void;\n /**\n * V3.9. Switch the Chat section to another chat — the handoff flow lands\n * on the chat it just created, and \"Continued from …\" links back. Absent →\n * the new chat is announced and left for the operator to open.\n */\n onOpenSession?: (sessionId: string) => void;\n /** 3.10: the handoff dialog is lifted (the dock's Context pane and a sidebar row open it too). */\n handoffOpen: boolean;\n onHandoffOpenChange: (open: boolean) => void;\n /** 3.10: other chats running right now, for the \"◌ N running tasks\" chip. */\n otherRunning: readonly ChatTask[];\n}\n\nexport function Workspace(props: WorkspaceProps) {\n const { view, seats, projects, dispatchEnabled, locked, hasAnySessions, onSend, onStop, onRename, onRequestDelete,\n onSeatChange, onNew, onRetry, sidebarCollapsed, onToggleSidebar, onOpenSession, handoffOpen, onHandoffOpenChange,\n otherRunning } = props;\n const session = view.session;\n const [editing, setEditing] = useState(false);\n const [draftTitle, setDraftTitle] = useState('');\n const [busy, setBusy] = useState(false);\n const [modeState, setModeState] = useState<{ busy: boolean; error: string | null }>({ busy: false, error: null });\n const [compactOpen, setCompactOpen] = useState(false);\n /** The notice the operator just asked for — shown ahead of the priority order. */\n const [pinnedNotice, setPinnedNotice] = useState<string | null>(null);\n /** Set when a handoff chat was created but there is no host callback to switch to it. */\n const [handoffCreated, setHandoffCreated] = useState<{ title: string } | null>(null);\n const [menu, setMenu] = useState<{ anchor: { x: number; y: number }; from: HTMLElement } | null>(null);\n const [copied, setCopied] = useState(false);\n /** Text another pane drafted in (Review \"Add to message\", Terminal \"Send selection to chat\"). */\n const [insertRequest, setInsertRequest] = useState<{ nonce: number; text: string } | null>(null);\n /** \"Continue on ‹seat›\": the seat the handoff should open on. */\n const [handoffTarget, setHandoffTarget] = useState<SeatChoice | null>(null);\n /** Where the composer's \"Queued turns\" row renders — between the notice slot and the live row. */\n const [queueHost, setQueueHost] = useState<HTMLDivElement | null>(null);\n const titleInput = useRef<HTMLInputElement>(null);\n const headingId = useId();\n const newChatShortcut = shortcutFor('chat.new');\n\n // Mounted for the whole life of the section, so the roster stays live.\n useSeatsRefresh();\n const roots = useSessionRoots(session);\n\n useEffect(() => {\n setEditing(false);\n setModeState({ busy: false, error: null });\n setCompactOpen(false);\n setHandoffCreated(null);\n setPinnedNotice(null);\n setMenu(null);\n setInsertRequest(null);\n setHandoffTarget(null);\n }, [view.sessionId]);\n\n // The composer bridge (chat/composer-bridge.ts): while THIS chat's composer\n // is on screen and can take text, the dock's \"Send selection to chat\" and\n // Review's \"Add to message\" land in its draft through `insertRequest` —\n // never sent, one append per nonce. Registered only when a composer is\n // mounted and enabled: otherwise the bridge's caller says \"Open a chat…\"\n // instead of dropping the text into a box that is not there.\n const composerSessionId = view.session?.id ?? null;\n // The last request the composer has rendered. Child effects run before\n // this parent's, so by the time this effect runs the Composer has taken it.\n const deliveredInsert = useRef(0);\n useEffect(() => {\n if (insertRequest) deliveredInsert.current = insertRequest.nonce;\n }, [insertRequest]);\n useEffect(() => {\n if (!composerSessionId || !dispatchEnabled) return undefined;\n return registerComposerInserter(composerSessionId, (text) => {\n setInsertRequest((prev) => {\n const nonce = (prev?.nonce ?? 0) + 1;\n // Two inserts inside one render (batched) would otherwise show the\n // composer only the second: an undelivered one is merged, not lost.\n const undelivered = prev !== null && prev.nonce > deliveredInsert.current;\n return { nonce, text: undelivered ? appendParagraph(prev.text, text) : text };\n });\n });\n }, [composerSessionId, dispatchEnabled]);\n\n // The mode switch is a write: without a held token it parks, asks for the\n // token with a reason, and runs once unlocked.\n const gate = useTokenGate();\n const gateRun = gate.run;\n\n const sessionId = view.session?.id ?? null;\n /**\n * Standard ⇄ expansive. Explicit only, and it applies from the next turn.\n * The server answers with the updated record (budget fields recomputed),\n * which replaces the store's copy so the ring moves at once.\n */\n const changeMode = useCallback(async (mode: VerseContextMode) => {\n if (!sessionId) return;\n setModeState({ busy: true, error: null });\n try {\n const updated = await gateRun('Changing the context mode changes the compaction flag this chat runs with from its next turn.',\n () => setSessionContextMode(sessionId, mode));\n if (updated) setVerseSession(updated.id, updated);\n setModeState({ busy: false, error: null });\n } catch (err) {\n setModeState({ busy: false, error: describeContextError(err) });\n }\n }, [sessionId, gateRun]);\n\n /**\n * The handoff dialog created a chat. Its first message is NOT sent: the\n * note goes into the new chat's composer draft, and the operator reads it\n * and presses Send — that press is the first spend.\n */\n const onHandoffCreated = useCallback((created: VerseSession, text: string) => {\n saveDraft(created.id, text);\n rememberVerseSeat(created.projectPath, { seatId: created.seatId, model: created.model });\n setVerseSession(created.id, created);\n invalidateVerseLists();\n onHandoffOpenChange(false);\n if (onOpenSession) onOpenSession(created.id);\n else {\n setHandoffCreated({ title: created.title });\n setPinnedNotice('handoff-created');\n }\n }, [onOpenSession, onHandoffOpenChange]);\n\n // Focus, then select: select() alone does not move focus everywhere, and\n // the ⋯ menu hands focus back to its trigger as it closes.\n useEffect(() => {\n if (!editing) return;\n titleInput.current?.focus();\n titleInput.current?.select();\n }, [editing]);\n\n const lastTurnAtMemo = useMemo(() => lastTurnActivityAt(view.events), [view.events]);\n\n if (!view.sessionId) {\n return (\n <section className={styles.workspace} aria-label=\"Chat\">\n <header className={styles.header} data-app-region=\"drag\">\n <Lockup\n eyebrow={<span className={styles.eyebrow}>Chat</span>}\n title={\n <span className={styles.titleStatic} data-state=\"empty\">\n {hasAnySessions ? 'No chat selected' : 'No chats yet'}\n </span>\n }\n />\n <div className={styles.headerSpacer} />\n <div className={styles.actions}><PaneToggles sidebarCollapsed={sidebarCollapsed} onToggleSidebar={onToggleSidebar} hasSession={false} /></div>\n </header>\n <WorkspaceSeatHealth seats={seats} />\n <div className={styles.emptyState}>\n <span className={styles.emptyMark} aria-hidden=\"true\"><VerseMark size={36} /></span>\n <h1 className={styles.emptyTitle}>{hasAnySessions ? 'Pick a chat, or start a new one' : `No chats yet${newChatShortcut ? ` — ${newChatShortcut}` : ''}`}</h1>\n <p className={styles.emptyBody}>\n Open a project, choose a seat — a Claude or Codex account, Grok, or a local Ollama model — and talk to an agent that can edit that project.\n </p>\n <button type=\"button\" className={styles.emptyButton} onClick={onNew}>New chat {newChatShortcut ? <kbd>{newChatShortcut}</kbd> : null}</button>\n {!dispatchEnabled ? (\n <p className={styles.emptyWarn} role=\"status\">\n This server was started without dispatch, so chats are read-only here. Run <code>ashlr verse</code> to enable sending.\n </p>\n ) : null}\n </div>\n </section>\n );\n }\n\n async function commitTitle() {\n const next = draftTitle.trim();\n setEditing(false);\n if (!session || !next || next === session.title) return;\n setBusy(true);\n try {\n await onRename(next);\n } finally {\n setBusy(false);\n }\n }\n\n function onTitleKey(event: KeyboardEvent<HTMLInputElement>) {\n if (event.key === 'Enter') {\n event.preventDefault();\n void commitTitle();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n setEditing(false);\n }\n }\n\n const running = session?.status === 'running';\n const budget = session ? sessionContextBudget(seats, session) : null;\n const mode: VerseContextMode = session?.contextMode ?? 'standard';\n // Only what the CLI can do: a model with a real expansive budget on an\n // engine that takes a per-invocation budget — or a session already in\n // expansive, which must always be able to switch back.\n const modesAvailable = session !== null && budget !== null && engineSupportsModes(session.engine) &&\n (hasExpansiveMode(budget.option) || mode === 'expansive');\n const activeSeat = session ? seatById(seats, session.seatId) ?? null : null;\n const capacity = activeSeat === null ? null : seatSubscription(activeSeat);\n /** The capacity worth a chip in the header: tight or spent, else none. */\n const flagged = capacity !== null && worthFlagging(capacity.cls) ? capacity : null;\n // The status chip's tooltip: what the chat runs on (seat · model), then the\n // seat's capacity sentence without its repeated label. The project path is\n // the breadcrumb's tooltip, not this one's.\n const capacityLine = activeSeat === null || capacity === null\n ? null\n : seatSubscriptionSentence(activeSeat, capacity).replace(`${activeSeat.label} · `, '');\n const seatTitle = session === null\n ? ''\n : `Runs on ${seatPillLabel(seats, session)}${capacityLine ? ` — ${capacityLine}` : ''}`;\n const disabledReason = !dispatchEnabled ? 'Sending is disabled: this server was started without dispatch.' : null;\n const runningReason = 'Available when the current turn finishes.';\n const compactable = session !== null && canCompactNow(session.engine);\n const openCompact = compactable ? () => { setHandoffCreated(null); setCompactOpen(true); setPinnedNotice('compact'); } : undefined;\n const compactUnavailableReason = session !== null && session.turnCount === 0 ? 'Nothing to compact yet — this chat has no turns.' : null;\n const handoffReason = disabledReason ?? (running ? runningReason : null);\n const openHandoff = () => { setHandoffCreated(null); setHandoffTarget(null); onHandoffOpenChange(true); };\n /**\n * \"Continue on ‹seat›\" from the composer's seat chip: the handoff (note\n * drafted from this chat's log, spends nothing) opened for that seat. While\n * the handoff is unavailable (a turn running, dispatch off) it falls back\n * to a plain new chat on that seat, which the seat chip already offered.\n */\n const continueOn = (choice: SeatChoice) => {\n if (handoffReason !== null) {\n onSeatChange(choice);\n return;\n }\n setHandoffCreated(null);\n setHandoffTarget(choice);\n onHandoffOpenChange(true);\n };\n const breadcrumb = session ? projectName(session.projectPath, projects) : '';\n // The header's compact status: the chat's state in a word — never the seat\n // or model (the composer says those). data-status is the session's own.\n const statusWord = session === null ? '' : session.status === 'running' ? 'Running'\n : session.status === 'error' ? 'Last turn failed' : dispatchEnabled ? 'Ready' : 'Read-only';\n\n // ---- ⋯ menu -------------------------------------------------------------\n const menuItems: ActionMenuItem[] = [];\n if (session) {\n if (modesAvailable && budget) {\n const other: VerseContextMode = mode === 'standard' ? 'expansive' : 'standard';\n const downgrade = other === 'standard' ? standardSwitchCompacts(budget.option, mode, budget) : null;\n // Switching DOWN past Standard's compaction point is not a free flag\n // flip: the CLI compacts on the next turn. Said in the item, not after.\n const downgradeCopy = downgrade === null ? null : downgrade.definite\n ? `This chat holds ≈${formatTokens(downgrade.tokens)}, past Standard's ≈${formatTokens(downgrade.point)} compaction point — it compacts on the next turn.`\n : `This chat holds up to ≈${formatTokens(downgrade.tokens)}; past ≈${formatTokens(downgrade.point)} it compacts on the next turn.`;\n const reason = !dispatchEnabled ? disabledReason : running ? 'The mode cannot change mid-turn. ' + runningReason : modeState.busy ? 'Switching…' : null;\n menuItems.push({\n id: 'mode',\n label: `Context: switch to ${CONTEXT_MODE_LABEL[other]}`,\n icon: <span aria-hidden=\"true\">◔</span>,\n description: other === 'expansive'\n ? expansiveCostCopy(budget.option, session.engine)\n : downgradeCopy ?? `Now ${CONTEXT_MODE_LABEL[mode]}. Applies from the next turn.`,\n disabled: reason !== null,\n reason,\n onSelect: () => { void changeMode(other); },\n });\n }\n if (compactable) {\n const reason = disabledReason ?? (running ? runningReason : compactUnavailableReason);\n menuItems.push({ id: 'compact', label: 'Compact now…', disabled: reason !== null, reason, onSelect: () => openCompact?.() });\n }\n menuItems.push({\n id: 'handoff',\n label: 'Continue in a fresh chat…',\n icon: <HandoffGlyph size={14} />,\n disabled: handoffReason !== null,\n reason: handoffReason,\n onSelect: openHandoff,\n });\n menuItems.push({\n id: 'rename',\n label: 'Rename',\n icon: <RenameGlyph size={14} />,\n disabled: !dispatchEnabled,\n reason: disabledReason,\n onSelect: () => { setDraftTitle(session.title); setEditing(true); },\n });\n menuItems.push({\n id: 'copy-id',\n label: copied ? 'Copied' : 'Copy chat id',\n icon: <CopyGlyph size={14} />,\n onSelect: () => {\n void navigator.clipboard?.writeText(session.id).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n }, () => undefined);\n },\n });\n menuItems.push({\n id: 'delete',\n label: 'Delete chat…',\n icon: <TrashGlyph size={14} />,\n danger: true,\n separated: true,\n disabled: !dispatchEnabled || running,\n reason: running ? 'Stop the turn before deleting.' : disabledReason,\n onSelect: onRequestDelete,\n });\n }\n\n return (\n // Paths the chat draws (the transcript, the live row) read relative to its roots.\n <PathRootsContext.Provider value={roots.roots}>\n <section className={styles.workspace} aria-labelledby={headingId}>\n <header className={styles.header} data-app-region=\"drag\">\n <Lockup\n eyebrow={session ? (\n // The full path is the tooltip; the strip shows `repo › branch`.\n <span className={styles.eyebrow} title={session.projectPath} data-testid=\"chat-breadcrumb\">\n {breadcrumb}\n {roots.branch ? <><span className={styles.crumbSep} aria-hidden=\"true\"> › </span><span className=\"visually-hidden\"> on branch </span><span className={styles.crumbBranch}>{roots.branch}</span></> : null}\n </span>\n ) : (\n <span className={`${styles.eyebrow} skeleton ${styles.eyebrowSkeleton}`} aria-hidden=\"true\" />\n )}\n title={session ? (\n editing ? (\n <input ref={titleInput} className={styles.titleInput} value={draftTitle} aria-label=\"Chat title\"\n onChange={(event) => setDraftTitle(event.target.value)} onBlur={() => { void commitTitle(); }}\n onKeyDown={onTitleKey} maxLength={120} />\n ) : (\n <h1 id={headingId} className={styles.title}>\n <button type=\"button\" className={styles.titleButton} title=\"Rename chat\" disabled={busy || !dispatchEnabled}\n onClick={() => { setDraftTitle(session.title); setEditing(true); }}>\n {session.title || 'Untitled chat'}\n </button>\n </h1>\n )\n ) : (\n <h1 id={headingId} className={`${styles.titleStatic} skeleton ${styles.titleSkeleton}`} aria-label=\"Loading chat\" />\n )}\n />\n\n {session ? (\n // The chat's STATE, not its seat: the composer's seat chip names\n // seat and model; this chip's tooltip keeps them one hover away.\n // That tooltip is now the header's only copy of seat, model and the\n // capacity sentence, so the chip takes focus (a tab stop, named for\n // its state): the Tooltip primitive opens on focus and makes the\n // sentence the chip's aria-describedby, as for every icon control.\n <Tooltip label={seatTitle} placement=\"bottom\">\n <span className={`${styles.seatPill} ${styles[`engine-${session.engine}`] ?? ''}`} data-engine={session.engine}\n tabIndex={0} role=\"group\" aria-label={`Chat status: ${statusWord}${flagged ? `, ${flagged.word}` : ''}`}\n data-status={session.status} data-testid=\"chat-status\"\n data-seat-capacity={flagged?.cls}>\n <span className={styles.engineTick} aria-hidden=\"true\" />\n <span className={styles.monogram} aria-hidden=\"true\"><ProviderLogo engine={session.engine} size={13} /></span>\n <span className={styles.seatText}>{statusWord}</span>\n {flagged ? <CapacityChip view={flagged} /> : null}\n </span>\n </Tooltip>\n ) : null}\n {session ? <SessionInsightChipSlot sessionId={session.id} /> : null}\n\n <div className={styles.headerSpacer} />\n\n {/* One cluster, and it never shrinks: the lockup truncates instead. */}\n <div className={styles.actions}>\n {view.stream === 'reconnecting' ? <span className={styles.streamState} role=\"status\">reconnecting…</span> : null}\n {session && budget ? (\n <ContextMeter variant=\"ring\" contextTokens={budget.contextTokens} contextWindow={budget.contextWindow} autoCompactAt={budget.autoCompactAt}\n exact={budget.exact} source={budget.source} mode={modesAvailable ? mode : null} engine={session.engine}\n compactionCount={session.compactionCount ?? 0} />\n ) : null}\n {modeState.error ? <span className={styles.modeError} role=\"alert\">{modeState.error}</span> : null}\n {session ? (\n <Tooltip label=\"Chat actions\" placement=\"bottom\">\n <button type=\"button\" className={styles.ghostIcon} aria-label=\"Chat actions\" aria-haspopup=\"menu\"\n aria-expanded={menu !== null}\n onClick={(event) => setMenu(menu ? null : { anchor: anchorBelow(event.currentTarget, 'end', 260), from: event.currentTarget })}>\n <MoreGlyph />\n </button>\n </Tooltip>\n ) : null}\n <span className={styles.actionDivider} aria-hidden=\"true\" />\n <PaneToggles sidebarCollapsed={sidebarCollapsed} onToggleSidebar={onToggleSidebar} hasSession={session !== null} />\n </div>\n </header>\n\n <LiveTranscript key={view.sessionId} sessionId={view.sessionId} transcriptOverride={view.transcript} loaded={view.loaded}\n loadError={view.loadError} onRetry={onRetry} engine={session?.engine} handoffFrom={session?.handoffFrom ?? null}\n onOpenSession={onOpenSession} />\n\n {session && budget ? (\n <ChatNotices session={session} seats={seats} budget={budget} modesAvailable={modesAvailable}\n dispatchEnabled={dispatchEnabled} modeBusy={modeState.busy} lastTurnAt={lastTurnAtMemo}\n compactOpen={compactable && compactOpen} running={running} onSend={onSend}\n onCloseCompact={() => { setCompactOpen(false); setPinnedNotice(null); }}\n onHandoff={openHandoff} onSwitchExpansive={() => { void changeMode('expansive'); }} onCompact={openCompact}\n handoffCreated={handoffCreated} onDismissHandoffCreated={() => setHandoffCreated(null)} pinnedId={pinnedNotice} />\n ) : null}\n\n {/* The composer's \"Queued turns\" row is portalled here, so the rows above\n the composer read notice → queued → live → branch bar (SPEC-310C §2).\n An empty host is not laid out (`:empty`). */}\n {session ? <div ref={setQueueHost} className={styles.queueHost} data-testid=\"queue-slot\" /> : null}\n\n {session ? <LiveRow sessionId={session.id} running={running} otherRunning={otherRunning} onStop={onStop} /> : null}\n\n {session ? (\n <BranchBarSlot sessionId={session.id} roots={roots.roots} onOpenDiff={requestDiff} />\n ) : null}\n\n {session ? (\n <Composer key={session.id} sessionId={session.id} seats={seats} seat={{ seatId: session.seatId, model: session.model }}\n engine={session.engine} running={running} disabled={!dispatchEnabled} disabledReason={disabledReason} locked={locked}\n hintSeen={session.turnCount > 0} contextTokens={budget?.contextTokens ?? null} contextWindow={budget?.contextWindow ?? null}\n autoCompactAt={budget?.autoCompactAt ?? null} contextExact={budget?.exact ?? true}\n handoffDraft={session.handoffFrom !== undefined && session.turnCount === 0}\n onSend={onSend} onStop={onStop} onSeatChange={onSeatChange}\n onHandoff={openHandoff} handoffDisabledReason={handoffReason} onContinueOn={continueOn}\n insertRequest={insertRequest} queueSlot={queueHost} autoFocus />\n ) : null}\n\n {menu && session ? (\n <ActionMenu label=\"Chat actions\" items={menuItems} anchor={menu.anchor} returnFocus={menu.from} onClose={() => setMenu(null)} />\n ) : null}\n\n {/* Mounted only while open: the dialog fetches a preview (git diff\n --stat on the server) and nothing should run for a closed one. */}\n {session && handoffOpen ? (\n <Suspense fallback={null}>\n {/* `initialTarget` preselects the seat \"Continue on ‹seat›\" named;\n undefined (the plain Hand off… entry) keeps the dialog's own\n default. HandoffDialog reads it once per open, so a pick made\n inside the dialog wins, and a seat that has since gone falls\n back to the default rather than preselecting nothing. */}\n <HandoffDialog session={session} seats={seats} open onClose={() => { setHandoffTarget(null); onHandoffOpenChange(false); }}\n onCreated={onHandoffCreated} initialTarget={handoffTarget ?? undefined} />\n </Suspense>\n ) : null}\n <MutationTokenDialog {...gate.dialog} tokenLabel=\"Mutation token\" tokenHelp=\"the mutation token ashlr verse printed\" />\n </section>\n </PathRootsContext.Provider>\n );\n}\n\n/**\n * The transcript, subscribed on its own (V3.10). Everything above re-renders\n * when the session HEAD changes; only this subtree re-renders as tokens and\n * reasoning stream in — at most once per animation frame. Keyed by chat, so\n * search, scroll and the turn announcer start fresh on every switch.\n */\nfunction LiveTranscript({ sessionId, transcriptOverride, ...props }: {\n sessionId: string;\n transcriptOverride: VerseSessionView['transcript'];\n} & Omit<ComponentProps<typeof Transcript>, 'transcript' | 'live'>) {\n const transcript = useVerseTranscript(sessionId);\n const live = useVerseLive(sessionId);\n return <Transcript {...props} transcript={transcriptOverride ?? transcript} live={live} />;\n}\n\n/**\n * \"● Running npm test · 1m 02s · 38 tok/s Stop\" on a 1px amber hairline,\n * with \"◌ 3 running tasks\" beside it. Its own subscription (transcript +\n * live signals) so the header above never re-renders per token. When this\n * chat is idle but others run, the tasks chip stands alone.\n */\nfunction LiveRow({ sessionId, running, otherRunning, onStop }: {\n sessionId: string;\n running: boolean;\n otherRunning: readonly ChatTask[];\n onStop: () => void;\n}) {\n const transcript = useVerseTranscript(sessionId);\n const live = useVerseLive(sessionId);\n const dock = useDock();\n const segments = transcript.segments;\n const lastItems = segments && segments.length > 0 ? segments[segments.length - 1]!.items : transcript.items;\n const liveThinking = live.thinking && live.thinking.turnId === live.turnId ? live.thinking : null;\n const derived = useMemo(() => derivePhaseFromTranscript(lastItems, { thinking: liveThinking }), [lastItems, liveThinking]);\n const tasks = useMemo(() => currentTurnTasks(lastItems), [lastItems]);\n const counts = useMemo(() => countTasks(running ? tasks : [], otherRunning), [running, tasks, otherRunning]);\n const showLive = running && transcript.live;\n if (!showLive && counts.total === 0) return null;\n const tasksActive = dock.state.open && (dock.state.active === 'tasks' || dock.state.splitWith === 'tasks');\n return (\n <div className={styles.liveRow} data-running={showLive || undefined}>\n {showLive ? <LiveStatus live={live} derived={derived} onStop={onStop} showNotice={false} /> : <span className={styles.liveSpacer} />}\n <TasksTray counts={counts} active={tasksActive} onOpen={() => openDockPane('tasks')} />\n </div>\n );\n}\n\nconst ENGINE_NOTICE_WORD: Readonly<Record<VerseLiveNotice['kind'], string>> = {\n retry: 'Retrying',\n preflight: 'Preflight',\n watchdog: 'Still running',\n};\n\n/** The advice notes re-check their evidence on this clock (idle-cache advice appears on time, not on the next render). */\nconst NOTICE_TICK_MS = 60_000;\n\n/**\n * Every notice candidate for this chat, handed to the ONE slot above the\n * composer (chat/NoticeSlot decides which shows). Subscribes to the live\n * signals itself (for the engine's retry / watchdog notice), so a streamed\n * token re-renders this small tree, not the header.\n */\nfunction ChatNotices(props: {\n session: VerseSession;\n seats: readonly VerseSeat[];\n budget: SessionContextBudget;\n modesAvailable: boolean;\n dispatchEnabled: boolean;\n modeBusy: boolean;\n lastTurnAt: string | null;\n compactOpen: boolean;\n running: boolean;\n onSend: (text: string) => Promise<boolean>;\n onCloseCompact: () => void;\n onHandoff: () => void;\n onSwitchExpansive: () => void;\n onCompact?: () => void;\n handoffCreated: { title: string } | null;\n onDismissHandoffCreated: () => void;\n pinnedId: string | null;\n}) {\n const { session, seats, budget, modesAvailable, dispatchEnabled, modeBusy, lastTurnAt, compactOpen, running, onSend,\n onCloseCompact, onHandoff, onSwitchExpansive, onCompact, handoffCreated, onDismissHandoffCreated, pinnedId } = props;\n const health = useSeatHealth();\n const live = useVerseLive(session.id);\n const [now, setNow] = useState(() => Date.now());\n const [, bump] = useState(0);\n useEffect(() => {\n const id = setInterval(() => setNow(Date.now()), NOTICE_TICK_MS);\n return () => clearInterval(id);\n }, []);\n\n const reports = health.data?.seats ?? [];\n const candidates: NoticeCandidate[] = [];\n if (seatHealthIssues(reports, seats).length > 0) {\n candidates.push({ id: 'seat-health', kind: 'seat-health', label: 'Seat health', render: () => <SeatHealthBannerView reports={reports} seats={seats} /> });\n }\n const notice = running && live.notice ? live.notice : null;\n if (notice) {\n candidates.push({\n id: `engine:${notice.kind}`,\n kind: 'engine',\n label: ENGINE_NOTICE_WORD[notice.kind],\n render: () => (\n <p className={styles.engineNotice} data-kind={notice.kind} role=\"status\">\n <span className={styles.engineNoticeWord}>{ENGINE_NOTICE_WORD[notice.kind]}</span>\n <span>{notice.message}</span>\n </p>\n ),\n });\n }\n if (contextAdviceVisible(session, budget, modesAvailable, Math.max(now, Date.now()), lastTurnAt)) {\n candidates.push({\n id: 'context-advice',\n kind: 'context',\n label: 'Context advice',\n render: () => (\n <ContextAdvice embedded session={session} budget={budget} modesAvailable={modesAvailable} dispatchEnabled={dispatchEnabled}\n modeBusy={modeBusy} onHandoff={onHandoff} onSwitchExpansive={onSwitchExpansive} onCompact={onCompact} lastTurnAt={lastTurnAt}\n onDismiss={() => bump((n) => n + 1)} />\n ),\n });\n }\n if (handoffCreated) {\n candidates.push({\n id: 'handoff-created',\n kind: 'context',\n label: 'Handoff started',\n render: () => (\n <p className={styles.adviceStatus} role=\"status\">\n Started “{handoffCreated.title}” — open it from the chat list; the handoff note is waiting in its message box.\n <button type=\"button\" className={styles.ghost} onClick={onDismissHandoffCreated}>Dismiss</button>\n </p>\n ),\n });\n }\n if (compactOpen) {\n candidates.push({\n id: 'compact',\n kind: 'compact',\n label: 'Compact now',\n render: () => (\n <CompactPanel engine={session.engine} budget={budget} running={running} dispatchEnabled={dispatchEnabled}\n empty={session.turnCount === 0} onSend={onSend} onClose={onCloseCompact} />\n ),\n });\n }\n return <NoticeSlot notices={candidates} pinnedId={pinnedId} />;\n}\n\n/**\n * The seat health banner with no chat open: it sits in the reading column\n * (there is no composer to sit above). Rendered only when there is something\n * to say, so a healthy roster leaves no padding behind.\n */\nfunction WorkspaceSeatHealth({ seats }: { seats: readonly VerseSeat[] }) {\n const health = useSeatHealth();\n const reports = health.data?.seats ?? [];\n if (seatHealthIssues(reports, seats).length === 0) return null;\n return (\n <div className={styles.advice}>\n <SeatHealthBannerView reports={reports} seats={seats} />\n </div>\n );\n}\n\n/** The identity lockup — breadcrumb over title, and the only flex item allowed to give up width. */\nfunction Lockup({ eyebrow, title }: { eyebrow: ReactNode; title: ReactNode }) {\n return (\n <div className={styles.identity}>\n {eyebrow}\n {title}\n </div>\n );\n}\n\nfunction shortcutFor(commandId: string): string | null {\n const chord = findCommand(commandId)?.keys[0];\n return chord ? formatChord(chord) : null;\n}\n\nconst PANE_COMMAND: Readonly<Record<'terminal' | 'preview' | 'diff', string>> = {\n terminal: 'dock.terminal',\n preview: 'dock.preview',\n diff: 'dock.diff',\n};\n\n/**\n * The pane toggles, as one group: Terminal, Preview and Review (each only\n * once its unit's pane is in this build), then the chat list and the dock.\n * All are aria-pressed buttons, so open/closed is spoken, not implied by a\n * name that changes under you. The action and its key are in the tooltip.\n *\n * The chat-list toggle keeps the 3.9 names — \"Show chat list\" collapsed,\n * \"Chat list\" expanded (the sidebar owns a \"Hide chat list\" button, and two\n * controls sharing one accessible name is an ambiguity, not a pair).\n */\nfunction PaneToggles({ sidebarCollapsed, onToggleSidebar, hasSession }: { sidebarCollapsed: boolean; onToggleSidebar: () => void; hasSession: boolean }) {\n const { state } = useDock();\n const shows = (pane: DockPaneId) => state.open && (state.active === pane || state.splitWith === pane);\n const panes = (['terminal', 'preview', 'diff'] as const).filter((pane) => hasSession && isPaneAvailable(pane));\n const dockKey = shortcutFor('dock.toggle');\n const listKey = shortcutFor('chat.sidebar');\n return (\n <div className={styles.toggles} role=\"group\" aria-label=\"Panels\">\n {panes.map((pane) => {\n const Icon = DOCK_PANE_GLYPH[pane];\n const key = shortcutFor(PANE_COMMAND[pane]);\n const on = shows(pane);\n return (\n <Tooltip key={pane} label={`${on ? 'Hide' : 'Show'} ${DOCK_PANE_LABEL[pane]}`} shortcut={key ?? undefined} placement=\"bottom\">\n <button type=\"button\" className={`${styles.ghostIcon} ${styles.paneToggle}`} aria-pressed={on} aria-label={DOCK_PANE_LABEL[pane]}\n onClick={() => toggleDockPane(pane)}>\n <Icon />\n </button>\n </Tooltip>\n );\n })}\n {panes.length > 0 ? <span className={`${styles.actionDivider} ${styles.paneToggle}`} aria-hidden=\"true\" /> : null}\n <Tooltip label={`${sidebarCollapsed ? 'Show' : 'Hide'} chat list`} shortcut={listKey ?? undefined} placement=\"bottom\">\n <button type=\"button\" className={styles.ghostIcon} onClick={onToggleSidebar} aria-pressed={!sidebarCollapsed}\n aria-label={sidebarCollapsed ? 'Show chat list' : 'Chat list'}>\n <SidebarIcon />\n </button>\n </Tooltip>\n <Tooltip label={`${state.open ? 'Hide' : 'Show'} the dock`} shortcut={dockKey ?? undefined} placement=\"bottom\">\n <button type=\"button\" className={styles.ghostIcon} onClick={toggleDock} aria-pressed={state.open} aria-label=\"Dock\">\n <PanelIcon />\n </button>\n </Tooltip>\n </div>\n );\n}\n"],"file":"Workspace-BQSZFSeO.js"}
1
+ {"version":3,"mappings":"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","names":[],"ignoreList":[],"sources":["../../../../../../src/web-ui/routes/verse/chat/NoticeSlot.tsx","../../../../../../src/web-ui/routes/verse/chat/TasksTray.tsx","../../../../../../src/web-ui/routes/verse/chat/composer-state.ts","../../../../../../src/web-ui/routes/verse/composer/composer-text.ts","../../../../../../src/web-ui/routes/verse/composer/AttachmentChips.tsx","../../../../../../src/web-ui/routes/verse/composer/BypassConfirmDialog.tsx","../../../../../../src/web-ui/routes/verse/ContextMeter.tsx","../../../../../../src/web-ui/routes/verse/composer/ContextRing.tsx","../../../../../../src/web-ui/routes/verse/composer/ControlMenu.tsx","../../../../../../src/web-ui/routes/verse/composer/ControlsSheet.tsx","../../../../../../src/web-ui/routes/verse/composer/QueueRow.tsx","../../../../../../src/web-ui/routes/verse/composer/SeatChip.tsx","../../../../../../src/web-ui/routes/verse/composer/SuggestMenu.tsx","../../../../../../src/web-ui/routes/verse/composer/useComposerData.ts","../../../../../../src/web-ui/routes/verse/composer/useFooterFold.ts","../../../../../../src/web-ui/routes/verse/DictationButton.tsx","../../../../../../src/web-ui/routes/verse/health/ComposerSeatBlock.tsx","../../../../../../src/web-ui/routes/verse/Composer.tsx","../../../../../../src/web-ui/routes/verse/chat/activity-model.ts","../../../../../../src/web-ui/routes/verse/chat/ActivityGroup.tsx","../../../../../../src/web-ui/routes/verse/chat/turn-model.ts","../../../../../../src/web-ui/routes/verse/chat/chapter-model.ts","../../../../../../src/web-ui/routes/verse/chat/ChapterRail.tsx","../../../../../../src/web-ui/routes/verse/chat/FileActivity.tsx","../../../../../../src/web-ui/routes/verse/chat/ThinkingBlock.tsx","../../../../../../src/web-ui/routes/verse/chat/TranscriptNav.tsx","../../../../../../src/web-ui/routes/verse/chat/TurnAnnouncer.tsx","../../../../../../src/web-ui/routes/verse/chat/ansi.ts","../../../../../../src/web-ui/routes/verse/chat/CommandOutput.tsx","../../../../../../src/web-ui/routes/verse/chat/DiffBlock.tsx","../../../../../../src/web-ui/routes/verse/ToolUseCard.tsx","../../../../../../src/web-ui/routes/verse/Transcript.tsx","../../../../../../src/web-ui/routes/verse/SeatCapacity.tsx","../../../../../../src/web-ui/routes/verse/health/SeatHealthBanner.tsx","../../../../../../src/web-ui/routes/verse/Workspace.tsx"],"sourcesContent":["/**\n * routes/verse/chat/NoticeSlot.tsx — ONE notice above the composer, not a\n * stack of banners above the transcript (SPEC-310C §2, unit C2).\n *\n * Before 3.10 every advisory had its own row under the header: seat health,\n * the handoff note, the expansive suggestion, the compact panel, a \"Started\n * …\" status — on a bad day four of them pushed the transcript half a screen\n * down, and the one that mattered was wherever it happened to render. Now\n * they share one slot, in a fixed order of consequence:\n *\n * 1. seat health (A2) — the account this chat runs on is broken;\n * 2. engine — the running turn is retrying, was recovered,\n * or has gone quiet (watchdog);\n * 3. context advice — continue in a fresh chat / go expansive;\n * 4. compact — the \"Compact now\" confirm panel.\n *\n * The highest one shows; \"+N more\" opens the rest in place. One exception\n * to the order, deliberately: a notice the operator JUST asked for (they\n * pressed \"Compact now…\") is shown first — hiding the panel they opened\n * behind a seat warning they have already read would make the click look\n * broken. The caller passes that id as `pinnedId`.\n *\n * Each candidate is only passed in when it has something to say, so the\n * slot takes no room at all on a quiet chat.\n */\nimport { useEffect, useId, useState, type ReactNode } from 'react';\nimport styles from './NoticeSlot.module.css';\n\nexport const NOTICE_KINDS = ['seat-health', 'engine', 'context', 'compact'] as const;\nexport type NoticeKind = (typeof NOTICE_KINDS)[number];\n\nconst PRIORITY: Readonly<Record<NoticeKind, number>> = { 'seat-health': 0, engine: 1, context: 2, compact: 3 };\n\nexport interface NoticeCandidate {\n /** Stable per notice (the pinned one and the list keys use it). */\n id: string;\n kind: NoticeKind;\n /** One short phrase for the \"+N more\" list (\"Seat signed out\"). */\n label: string;\n render: () => ReactNode;\n}\n\n/** Priority order; the pinned notice (when present) first. Stable within a kind. */\nexport function orderNotices(candidates: readonly NoticeCandidate[], pinnedId: string | null = null): NoticeCandidate[] {\n return candidates\n .map((c, i) => ({ c, i }))\n .sort((a, b) => {\n if (pinnedId !== null) {\n if (a.c.id === pinnedId && b.c.id !== pinnedId) return -1;\n if (b.c.id === pinnedId && a.c.id !== pinnedId) return 1;\n }\n return PRIORITY[a.c.kind] - PRIORITY[b.c.kind] || a.i - b.i;\n })\n .map(({ c }) => c);\n}\n\nexport interface NoticeSlotProps {\n notices: readonly NoticeCandidate[];\n pinnedId?: string | null;\n}\n\nexport function NoticeSlot({ notices, pinnedId = null }: NoticeSlotProps) {\n const ordered = orderNotices(notices, pinnedId);\n const [expanded, setExpanded] = useState(false);\n const listId = useId();\n const extra = ordered.length - 1;\n // Collapse again once only one is left: \"Show fewer\" with nothing to hide is noise.\n useEffect(() => {\n if (extra <= 0 && expanded) setExpanded(false);\n }, [extra, expanded]);\n\n if (ordered.length === 0) return null;\n const shown = expanded ? ordered : ordered.slice(0, 1);\n const hiddenLabels = ordered.slice(1).map((n) => n.label).join(', ');\n\n return (\n <section className={styles.slot} aria-label=\"Notices\" data-count={ordered.length}>\n <div id={listId} className={styles.list}>\n {shown.map((notice) => (\n <div key={notice.id} className={styles.notice} data-kind={notice.kind}>{notice.render()}</div>\n ))}\n </div>\n {extra > 0 ? (\n <button type=\"button\" className={styles.more} aria-expanded={expanded} aria-controls={listId}\n // The hidden ones are named before they are opened.\n aria-label={expanded ? undefined : `+${extra} more: ${hiddenLabels}`}\n title={expanded ? undefined : hiddenLabels} onClick={() => setExpanded((v) => !v)}>\n {expanded ? 'Show fewer' : `+${extra} more`}\n </button>\n ) : null}\n </section>\n );\n}\n","/**\n * routes/verse/chat/TasksTray.tsx — \"◌ 3 running tasks\", beside the live\n * status line above the composer (SPEC-310C §2, unit C2).\n *\n * The count is this turn's running tool calls and subagents PLUS every\n * other chat with a turn in flight (tasks-model.ts): the operator is waiting\n * on all of them, and until 3.10 the others were only visible by scrolling\n * the sidebar. One click opens the dock's Tasks pane, which lists them and\n * jumps to each. Nothing renders when nothing runs.\n */\nimport { describeTaskCounts, type TaskCounts } from './tasks-model.js';\nimport styles from './TasksTray.module.css';\n\nexport interface TasksTrayProps {\n counts: TaskCounts;\n onOpen: () => void;\n /** The Tasks pane is the dock's visible pane right now. */\n active?: boolean;\n}\n\nexport function TasksTray({ counts, onOpen, active = false }: TasksTrayProps) {\n if (counts.total === 0) return null;\n return (\n <button type=\"button\" className={styles.tray} onClick={onOpen} aria-pressed={active}\n aria-label={`${describeTaskCounts(counts)}. Open the Tasks pane.`} title=\"Open the Tasks pane\">\n <span className={styles.ring} aria-hidden=\"true\" />\n <span className={styles.count}>{counts.total}</span>\n <span className={styles.word}>running task{counts.total === 1 ? '' : 's'}</span>\n </button>\n );\n}\n","/**\n * routes/verse/chat/composer-state.ts — the composer's memory, and an\n * honest estimate of what a message will cost.\n *\n * The memory (per-chat drafts and sent messages in `localStorage`) lives in\n * composer-memory.ts and is re-exported here unchanged; see that file.\n */\n\nimport { CHARS_PER_TOKEN, occupancy } from '../../../../core/verse/context-math.js';\n\nexport {\n clearComposerMemory,\n forgetComposerMemory,\n loadDraft,\n loadHistory,\n pushHistory,\n saveDraft,\n VERSE_DRAFT_STORAGE_KEY,\n VERSE_SENT_STORAGE_KEY,\n} from './composer-memory.js';\n\n// ---------------------------------------------------------------------------\n// Cost\n// ---------------------------------------------------------------------------\n\n/**\n * Characters per token — context-math's CHARS_PER_TOKEN, the one estimator\n * every surface uses (handoff size, fit badges, this hint). A rough\n * English/code average, and rough is the point: the real count is the\n * provider's, which this app does not have before the turn runs. Every\n * surface that shows this figure marks it `≈` and never presents it as\n * measured (DESIGN §6, VERSE-TELEMETRY-V2).\n */\nexport function estimateTokens(text: string): number {\n const trimmed = text.trim();\n if (trimmed.length === 0) return 0;\n return Math.max(1, Math.ceil(trimmed.length / CHARS_PER_TOKEN));\n}\n\n/** `over` = the projection is past the whole window, not just the compaction point. */\nexport type CostTone = 'warn' | 'danger' | 'over';\n\nexport interface CostHint {\n /** Estimated tokens for the draft itself. */\n draftTokens: number;\n /** Occupancy this message would push the session to (current + draft), in tokens. */\n projectedTokens: number;\n /** …as a percent of the context WINDOW (the meter's track), unclamped. */\n projectedPercent: number;\n /** Where the CLI auto-compacts, when known. */\n autoCompactAt: number | null;\n /** True when the projection reaches the compaction point: the CLI compacts before or during this turn. */\n pastCompaction: boolean;\n tone: CostTone;\n /** False when the current occupancy is itself an upper bound (codex before its rollout is read). */\n exact: boolean;\n}\n\n/** The session's context budget as the composer sees it (verse-model `sessionContextBudget`). */\nexport interface CostBudget {\n contextTokens: number | null | undefined;\n contextWindow: number | null | undefined;\n autoCompactAt?: number | null;\n exact?: boolean;\n}\n\n/**\n * What sending this draft would do to the context — or null when the window\n * is unknown, the box is empty, or the session stays comfortable.\n *\n * The tones are context-math `occupancy()`'s — measured against the\n * COMPACTION point when it is known (warn from 80%, danger from 95%), else\n * against the window — which is exactly what colours the ContextMeter, so the\n * hint and the meter can never disagree about when things are getting tight.\n */\nexport function costHint(text: string, budget: CostBudget): CostHint | null {\n const draftTokens = estimateTokens(text);\n if (draftTokens === 0) return null;\n const window = typeof budget.contextWindow === 'number' && budget.contextWindow > 0 ? budget.contextWindow : null;\n if (window === null) return null;\n const used = typeof budget.contextTokens === 'number' && Number.isFinite(budget.contextTokens) ? Math.max(0, budget.contextTokens) : 0;\n const projected = occupancy({\n contextTokens: used + draftTokens,\n contextWindow: window,\n autoCompactAt: budget.autoCompactAt ?? null,\n contextTokensExact: budget.exact,\n });\n if (projected.tone === 'ok' || projected.tone === 'unknown') return null;\n return {\n draftTokens,\n projectedTokens: projected.tokens,\n projectedPercent: Math.min(999, Math.round((projected.ofWindow ?? 0) * 100)),\n autoCompactAt: projected.autoCompactAt,\n pastCompaction: projected.ofCompaction !== null && projected.ofCompaction >= 1,\n tone: projected.tone,\n exact: projected.exact,\n };\n}\n","/**\n * routes/verse/composer/composer-text.ts — the pure text rules behind the\n * composer's `@` finder, `/` commands and attachment tokens (unit C3).\n *\n * Kept free of React and the DOM so every rule is a unit test: what counts as\n * an active trigger at the caret, what a completion inserts and where the\n * caret lands, and how an attachment token is added and removed.\n */\nimport type { VerseFileMatch } from '../../../../core/verse/workbench-types.js';\n\nexport type ComposerTrigger =\n | { kind: 'mention'; query: string; start: number; end: number }\n | { kind: 'command'; query: string; start: number; end: number };\n\n/** Longest `@` query the finder is asked for (the route caps at 200). */\nconst MENTION_QUERY_MAX = 120;\n\n/**\n * The trigger the caret is inside, or null.\n * - `@query` — an `@` at the start of the text or after whitespace, then\n * anything but whitespace up to the caret. An e-mail address (`a@b`) is\n * not a trigger.\n * - `/query` — only as the FIRST thing in the message (a slash command),\n * letters and dashes only, caret still inside it.\n */\nexport function activeTrigger(text: string, caret: number): ComposerTrigger | null {\n if (caret < 0 || caret > text.length) return null;\n const before = text.slice(0, caret);\n const command = /^\\/([a-z-]*)$/i.exec(before);\n if (command) {\n // The rest of the word after the caret belongs to the token too.\n const after = /^[a-z-]*/i.exec(text.slice(caret))?.[0] ?? '';\n return { kind: 'command', query: command[1]!.toLowerCase(), start: 0, end: caret + after.length };\n }\n const mention = /(^|\\s)@([^\\s@]*)$/.exec(before);\n if (mention) {\n const query = mention[2]!;\n if (query.length > MENTION_QUERY_MAX) return null;\n const start = caret - query.length - 1;\n const after = /^[^\\s@]*/.exec(text.slice(caret))?.[0] ?? '';\n return { kind: 'mention', query, start, end: caret + after.length };\n }\n return null;\n}\n\n/** Replace the trigger's span with `insert` (plus one space) and put the caret after it. */\nexport function applyCompletion(text: string, trigger: ComposerTrigger, insert: string): { text: string; caret: number } {\n const tail = text.slice(trigger.end);\n const spacer = tail.startsWith(' ') || insert.endsWith(' ') ? '' : ' ';\n const next = `${text.slice(0, trigger.start)}${insert}${spacer}${tail}`;\n return { text: next, caret: trigger.start + insert.length + spacer.length };\n}\n\n/**\n * The token a file match inserts. A file in the chat's PRIMARY root is\n * written relative (the CLI's cwd is that root); one in another root is\n * written from its root as the server showed it (`~/…`), which the engine\n * expands to an absolute path before the CLI sees it. Paths with spaces are\n * rare in repos but legal: they are quoted, the form Claude Code accepts.\n */\nexport function mentionFor(match: VerseFileMatch, primaryRoot: string | null): string {\n const path = primaryRoot !== null && match.root === primaryRoot ? match.path : `${match.root.replace(/\\/+$/, '')}/${match.path}`;\n return /\\s/.test(path) ? `@\"${path}\"` : `@${path}`;\n}\n\n// ---------------------------------------------------------------------------\n// Slash commands\n// ---------------------------------------------------------------------------\n\nexport type SlashCommandId = 'handoff' | 'compact' | 'new' | 'plan' | 'effort' | 'model';\n\nexport interface SlashCommand {\n id: SlashCommandId;\n title: string;\n description: string;\n}\n\n/** SPEC-310C §2: `/` offers exactly these, in this order. */\nexport const SLASH_COMMANDS: readonly SlashCommand[] = [\n { id: 'handoff', title: '/handoff', description: 'Continue in a fresh chat with a handoff note' },\n { id: 'compact', title: '/compact', description: 'Summarise earlier turns to free context (sent as a turn)' },\n { id: 'new', title: '/new', description: 'Start a new chat on this seat' },\n { id: 'plan', title: '/plan', description: 'Switch this chat to Plan mode — nothing is edited' },\n { id: 'effort', title: '/effort', description: 'Choose the reasoning effort' },\n { id: 'model', title: '/model', description: 'Choose the model' },\n];\n\n/** Commands whose id or title starts with (then contains) the query. */\nexport function matchSlashCommands(query: string): SlashCommand[] {\n const q = query.toLowerCase();\n if (!q) return [...SLASH_COMMANDS];\n const starts = SLASH_COMMANDS.filter((c) => c.id.startsWith(q));\n const contains = SLASH_COMMANDS.filter((c) => !c.id.startsWith(q) && (c.id.includes(q) || c.description.toLowerCase().includes(q)));\n return [...starts, ...contains];\n}\n\n// ---------------------------------------------------------------------------\n// Attachment tokens\n// ---------------------------------------------------------------------------\n\n/** Append an attachment's `@path` token to the draft, on its own if the draft is empty. */\nexport function insertAttachmentRef(text: string, ref: string, caret: number | null = null): { text: string; caret: number } {\n if (caret === null || caret < 0 || caret > text.length) {\n const sep = text.length === 0 || /\\s$/.test(text) ? '' : ' ';\n const next = `${text}${sep}${ref} `;\n return { text: next, caret: next.length };\n }\n const before = text.slice(0, caret);\n const after = text.slice(caret);\n const lead = before.length === 0 || /\\s$/.test(before) ? '' : ' ';\n const trail = after.startsWith(' ') ? '' : ' ';\n const next = `${before}${lead}${ref}${trail}${after}`;\n return { text: next, caret: before.length + lead.length + ref.length + trail.length };\n}\n\n/** Remove every occurrence of an attachment's token (and one space after it). */\nexport function removeAttachmentRef(text: string, ref: string): string {\n if (!ref) return text;\n return text.split(`${ref} `).join('').split(ref).join('').replace(/ {2,}/g, ' ');\n}\n\n/** Bytes as the operator reads them: 812 B, 14 KB, 2.3 MB. */\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '—';\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/** A File as base64 without the `data:` prefix (the route accepts either). */\nexport function readFileAsBase64(file: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error ?? new Error('the file could not be read'));\n reader.onload = () => {\n const result = typeof reader.result === 'string' ? reader.result : '';\n resolve(result.slice(result.indexOf(',') + 1));\n };\n reader.readAsDataURL(file);\n });\n}\n","/**\n * routes/verse/composer/AttachmentChips.tsx — thumbnail chips for the files\n * attached to the message being written (unit C3).\n *\n * An image shows its thumbnail (an object URL of the LOCAL file — the stored\n * copy is private and never served back to the page); anything else shows its\n * extension. Each chip says its state in words: uploading, attached (with\n * size), or why it failed. × removes the chip, the token in the text and the\n * stored copy.\n */\nimport { IconX } from '../../../components/primitives/icons.js';\nimport { formatBytes } from './composer-text.js';\nimport type { AttachmentDraft } from './useComposerData.js';\nimport styles from './composer.module.css';\n\nexport interface AttachmentChipsProps {\n drafts: readonly AttachmentDraft[];\n onRemove: (key: string) => void;\n disabled?: boolean;\n}\n\nfunction extension(name: string): string {\n const dot = name.lastIndexOf('.');\n const ext = dot > 0 ? name.slice(dot + 1) : '';\n return ext ? ext.slice(0, 4).toUpperCase() : 'FILE';\n}\n\nexport function AttachmentChips({ drafts, onRemove, disabled = false }: AttachmentChipsProps) {\n if (drafts.length === 0) return null;\n return (\n <ul className={styles.chips} aria-label=\"Attachments\">\n {drafts.map((draft) => {\n const state = draft.status === 'uploading'\n ? 'uploading'\n : draft.status === 'error'\n ? (draft.error ?? 'not attached')\n : formatBytes(draft.bytes);\n return (\n <li key={draft.key} className={styles.chip} data-status={draft.status}>\n {draft.previewUrl ? (\n <img className={styles.chipThumb} src={draft.previewUrl} alt=\"\" />\n ) : (\n <span className={styles.chipExt} aria-hidden=\"true\">{extension(draft.name)}</span>\n )}\n <span className={styles.chipText}>\n <span className={styles.chipName} title={draft.name}>{draft.name}</span>\n <span className={styles.chipMeta} role={draft.status === 'error' ? 'alert' : undefined}>\n {draft.status === 'uploading' ? <span className={styles.chipSpinner} aria-hidden=\"true\" /> : null}\n {state}\n </span>\n </span>\n <button type=\"button\" className={styles.chipRemove} disabled={disabled && draft.status !== 'error'}\n aria-label={`Remove ${draft.name}`} onClick={() => onRemove(draft.key)}>\n <IconX width={12} height={12} aria-hidden=\"true\" />\n </button>\n </li>\n );\n })}\n </ul>\n );\n}\n","/**\n * routes/verse/composer/BypassConfirmDialog.tsx — the per-chat confirmation\n * in front of Bypass permissions (SPEC-310C §0.6; unit C3).\n *\n * Bypass turns off every permission check the CLI has. It is never a\n * default, never inherited by a new chat, and never one keystroke away: this\n * dialog opens with focus on CANCEL, so Enter-Enter from the picker keeps the\n * chat as it was. Only the explicit red button sends `confirmBypass: true`\n * (the server refuses bypass without it).\n */\nimport { useId, useRef } from 'react';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { Dialog } from '../../../components/primitives/Dialog.js';\nimport { shortcutLabel } from './composer-keys.js';\nimport styles from './composer.module.css';\n\nexport interface BypassConfirmDialogProps {\n open: boolean;\n /** \"Claude Max · Opus 5.5\" — which chat this applies to. */\n chatLabel: string;\n running: boolean;\n onCancel: () => void;\n onConfirm: () => void;\n}\n\nexport function BypassConfirmDialog({ open, chatLabel, running, onCancel, onConfirm }: BypassConfirmDialogProps) {\n const titleId = useId();\n const cancel = useRef<HTMLButtonElement>(null);\n return (\n <Dialog open={open} onClose={onCancel} titleId={titleId} title=\"Bypass permissions for this chat?\" initialFocusRef={cancel}\n description={<>The agent in <strong>{chatLabel}</strong> will run every command and edit every file without asking — including outside the project.</>}>\n <ul className={styles.bypassList}>\n <li>Applies to this chat only{running ? ', from the next turn' : ''}. New chats never inherit it.</li>\n <li>Switch back any time from the Permission menu{shortcutLabel('composer.permission') ? <> (<kbd>{shortcutLabel('composer.permission')}</kbd>)</> : null}.</li>\n <li>Use it in a sandbox or a throwaway worktree, not on work you can’t lose.</li>\n </ul>\n <div className={styles.bypassActions}>\n <Button ref={cancel} variant=\"subtle\" onClick={onCancel}>Cancel</Button>\n <Button variant=\"danger\" onClick={onConfirm}>Bypass for this chat</Button>\n </div>\n </Dialog>\n );\n}\n","/**\n * routes/verse/ContextMeter.tsx — live context occupancy, the session's\n * context mode, and the two pieces of advice that follow from them.\n *\n * DESIGN §4: not a labelled progress bar. The track is a 2px full-width line\n * pinned to the bottom edge of the header strip (the component's own root\n * stays unpositioned so the header is its containing block); the numbers ride\n * alongside in the display font, the tooltip and `aria-valuetext` carry the\n * long form.\n *\n * WHAT CHANGED IN 3.9, and why (docs/VERSE-CONTEXT.md):\n *\n * - The track is the FULL window and carries a tick where the CLI will\n * auto-compact. A 1M Claude model in standard mode compacts near 367k; a\n * bar that only showed \"37% of 1M\" hid the one number that decides when\n * the agent starts forgetting.\n * - Tone comes from context-math `occupancy()`, measured against the\n * COMPACTION point (warn from 80%, danger from 95%) — before the CLI\n * compacts, not after. The old fixed 70/90% of the window turned red only\n * once codex (90%) and grok (80%) had already compacted.\n * - Nothing is clamped: a reading past the window is information, drawn as\n * an `over` state rather than pinned at 100%.\n * - An upper-bound reading (codex before its rollout is read) is prefixed\n * `≤`, never shown as a measurement.\n *\n * Every figure here is computed by context-math, the same module the server\n * uses, so \"compacts ≈367k\" cannot mean two different numbers in two places.\n */\nimport { useEffect, useId, useLayoutEffect, useRef, useState, type CSSProperties, type FormEvent, type KeyboardEvent } from 'react';\nimport type { VerseContextMode, VerseEngine, VerseModelOption, VerseSession, VerseWindowSource } from '../../data/api-types.js';\nimport {\n budgetFor,\n CACHE_IDLE_TTL_MS,\n expansiveAdvice,\n handoffAdvice,\n hasExpansiveMode,\n occupancy,\n OCCUPANCY_DANGER,\n OCCUPANCY_WARN,\n type VerseHandoffAdvice,\n type VerseOccupancyTone,\n} from '../../../core/verse/context-math.js';\nimport { CODEX_EXPANSIVE_METERING_NOTE, formatContextWindow, windowSourceText, type SessionContextBudget } from './verse-model.js';\nimport { dismissVerseAdvice, isVerseAdviceDismissed } from './verse-ui-store.js';\nimport { formatTokens } from './verse-readouts.js';\nimport styles from './Workspace.module.css';\n\n/** Percent-of-compaction thresholds, re-exported so copy can quote them. */\nexport const CONTEXT_WARN_PERCENT = Math.round(OCCUPANCY_WARN * 100);\nexport const CONTEXT_DANGER_PERCENT = Math.round(OCCUPANCY_DANGER * 100);\n\nexport const CONTEXT_MODE_LABEL: Record<VerseContextMode, string> = {\n standard: 'Standard',\n expansive: 'Expansive',\n};\n\n/** Who does the compacting, in words the operator recognises. */\nconst COMPACTOR: Record<VerseEngine, string> = {\n claude: 'Claude Code',\n local: 'Claude Code (driving the local model)',\n codex: 'Codex',\n grok: 'Grok',\n};\n\n/** Full-precision figure for the tooltip, where `367k` would hide the digits. */\nfunction exactFigure(n: number): string {\n return Math.round(n).toLocaleString('en-US');\n}\n\nexport interface ContextMeterProps {\n contextTokens: number | null | undefined;\n contextWindow: number | null | undefined;\n /** Where the CLI auto-compacts in the session's current mode; null/absent = unknown. */\n autoCompactAt?: number | null;\n /** False when `contextTokens` is an upper bound, drawn with a `≤`. Absent = exact. */\n exact?: boolean;\n source?: VerseWindowSource | null;\n mode?: VerseContextMode | null;\n engine?: VerseEngine | null;\n compactionCount?: number;\n /**\n * `inline` draws the track in flow (the Context pane) instead of pinned.\n * `ring` (3.10, the chat header) is a 16px occupancy ring plus the\n * percentage — the same figures and the same tooltip, in the room the\n * header has now that the title carries a breadcrumb.\n */\n variant?: 'header' | 'inline' | 'ring';\n}\n\n/** The meter's words, separated from its markup so the tooltip and the tests read one source. */\nexport function describeContext(props: Omit<ContextMeterProps, 'variant'>): {\n tone: VerseOccupancyTone;\n /** \"≤142k / 1M\" */\n label: string;\n /** \"compacts ≈367k\", or null when the point is unknown. */\n compactLabel: string | null;\n /** Percent of the WINDOW, unclamped; null when the window is unknown. */\n percent: number | null;\n percentLabel: string;\n /** Where the compaction tick sits on the track (0–100), or null when not drawable. */\n tickPercent: number | null;\n /** Fill width on the track (0–100). */\n fillPercent: number;\n /**\n * The reading itself — occupancy, an upper-bound or past-the-window caveat,\n * and where it compacts — without the explanation and provenance lines\n * `title` adds. The composer ring's tooltip is exactly these lines.\n */\n summary: string[];\n title: string;\n} {\n const occ = occupancy({\n contextTokens: typeof props.contextTokens === 'number' && Number.isFinite(props.contextTokens) ? props.contextTokens : 0,\n contextWindow: props.contextWindow ?? null,\n autoCompactAt: props.autoCompactAt ?? null,\n contextTokensExact: props.exact,\n });\n const bound = occ.exact ? '' : '≤';\n // An upper bound past the compaction point proves nothing about the real\n // prompt (occupancy() returns tone 'unknown' for it), so no sentence below\n // may read it as \"past\" anything.\n const boundPast = !occ.exact && occ.untilCompaction === 0;\n const label = `${bound}${formatTokens(occ.tokens)} / ${occ.window !== null ? formatContextWindow(occ.window) : 'n/a'}`;\n const compactLabel = occ.autoCompactAt !== null ? `compacts ≈${formatTokens(occ.autoCompactAt)}` : null;\n const percent = occ.ofWindow !== null ? Math.round(occ.ofWindow * 100) : null;\n const tickPercent = occ.window !== null && occ.autoCompactAt !== null && occ.autoCompactAt < occ.window\n ? (occ.autoCompactAt / occ.window) * 100\n : null;\n const fillPercent = occ.ofWindow !== null ? Math.min(100, Math.max(0, occ.ofWindow * 100)) : 0;\n\n const lines: string[] = [];\n lines.push(occ.window !== null\n ? `Context: ${bound}${exactFigure(occ.tokens)} of ${exactFigure(occ.window)} tokens (${percent}%).`\n : `Context: ${bound}${exactFigure(occ.tokens)} tokens — the window is unknown, so no percentage is shown.`);\n if (!occ.exact) {\n lines.push('Upper bound: this CLI reported only the turn total so far; the exact last-call size replaces it once its session log is read.');\n }\n if (occ.tone === 'over') {\n lines.push('Past the window: the next turn will fail or force the CLI to compact first. Continue in a fresh chat.');\n }\n const modeText = props.mode ? ` (${CONTEXT_MODE_LABEL[props.mode]} mode)` : '';\n if (occ.autoCompactAt !== null) {\n const left = boundPast\n ? 'the upper bound is past that point, which does not mean the real prompt is'\n : occ.untilCompaction === 0\n ? 'at or past that point now'\n : `${occ.exact ? '' : 'at least '}≈${exactFigure(occ.untilCompaction ?? 0)} left`;\n lines.push(`Auto-compacts at ≈${exactFigure(occ.autoCompactAt)} tokens${modeText} — ${left}.`);\n } else if (occ.window !== null) {\n lines.push(`The compaction point is unknown${modeText}; the colour is measured against the whole window.`);\n }\n const summary = [...lines];\n if (occ.autoCompactAt !== null) {\n const who = props.engine ? COMPACTOR[props.engine] : 'The CLI';\n lines.push(`When it compacts, ${who} replaces the earlier conversation with a summary and carries on; early detail then survives only as that summary.`);\n }\n if ((props.compactionCount ?? 0) > 0) {\n const n = props.compactionCount ?? 0;\n lines.push(`Compacted ${n} time${n === 1 ? '' : 's'} so far.`);\n }\n if (occ.window !== null) {\n lines.push(`Window: ${props.source ? windowSourceText(props.source, props.engine) : 'source not recorded (an older session)'}.`);\n }\n return {\n tone: occ.tone,\n label,\n compactLabel,\n percent,\n percentLabel: percent === null ? 'n/a' : `${bound}${percent}%`,\n tickPercent,\n fillPercent,\n summary,\n title: lines.join('\\n'),\n };\n}\n\nconst RING_R = 6;\nconst RING_C = 2 * Math.PI * RING_R;\n\n/**\n * The 16px occupancy ring with the compaction point as a tick — ONE drawing\n * for the chat header (`variant=\"ring\"`) and the composer footer, fed from one\n * `describeContext`, so the two rings cannot disagree about how full the chat\n * is or where it compacts. Each caller brings its own classes (colours live\n * with the surface). Decorative: the caller carries the words.\n */\nexport function ContextRingGlyph({ fillPercent, tickPercent, classes }: {\n fillPercent: number;\n tickPercent: number | null;\n classes: { svg?: string; track?: string; fill?: string; tick?: string };\n}) {\n const angle = tickPercent === null ? null : ((tickPercent / 100) * 360 - 90) * (Math.PI / 180);\n const at = (radius: number) => (angle === null ? null : { x: 8 + Math.cos(angle) * radius, y: 8 + Math.sin(angle) * radius });\n const inner = at(RING_R - 2.5);\n const outer = at(RING_R + 1.5);\n return (\n <svg className={classes.svg} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\" focusable=\"false\">\n <circle className={classes.track} cx=\"8\" cy=\"8\" r={RING_R} />\n <circle className={classes.fill} cx=\"8\" cy=\"8\" r={RING_R}\n strokeDasharray={`${(fillPercent / 100) * RING_C} ${RING_C}`} transform=\"rotate(-90 8 8)\" />\n {inner && outer ? (\n <line className={classes.tick} data-testid=\"compaction-tick\" x1={inner.x} y1={inner.y} x2={outer.x} y2={outer.y} />\n ) : null}\n </svg>\n );\n}\n\nexport function ContextMeter({ variant = 'header', ...props }: ContextMeterProps) {\n const d = describeContext(props);\n // aria-valuenow is bounded by aria-valuemax; the over state is carried by\n // the text and data-tone instead of an out-of-range value.\n const valueNow = d.percent === null ? undefined : Math.min(100, d.percent);\n const valueText = `${d.label} (${d.percentLabel})${d.compactLabel ? `, ${d.compactLabel.replace('≈', 'at about ')}` : ''}${d.tone === 'over' ? ', past the window' : ''}`;\n if (variant === 'ring') {\n return (\n <div className={`${styles.ring} ${styles[`meter-${d.tone}`] ?? ''}`} role=\"meter\" aria-label=\"Context window\"\n aria-valuemin={0} aria-valuemax={100} aria-valuenow={valueNow} aria-valuetext={valueText}\n title={d.title} data-tone={d.tone} data-exact={props.exact === false ? 'false' : undefined}>\n {/* The compaction point is a tick on the ring, like on the line. */}\n <ContextRingGlyph fillPercent={d.fillPercent} tickPercent={d.tickPercent}\n classes={{ svg: styles.ringSvg, track: styles.ringTrack, fill: styles.ringFill, tick: styles.ringTick }} />\n <span className={styles.ringText}>{d.percentLabel}</span>\n </div>\n );\n }\n return (\n <div\n className={`${styles.meter} ${styles[`meter-${d.tone}`] ?? ''} ${variant === 'inline' ? styles.meterInline : ''}`}\n role=\"meter\" aria-label=\"Context window\" aria-valuemin={0} aria-valuemax={100}\n aria-valuenow={valueNow} aria-valuetext={valueText}\n title={d.title} data-tone={d.tone} data-exact={props.exact === false ? 'false' : undefined}\n >\n <div className={styles.meterTrack} aria-hidden=\"true\">\n <div className={styles.meterFill} style={{ width: `${d.fillPercent}%` }} />\n {d.tickPercent !== null ? (\n <div className={styles.meterTick} data-testid=\"compaction-tick\" style={{ left: `${d.tickPercent}%` }} />\n ) : null}\n </div>\n <span className={styles.meterText}>\n <span className={styles.meterTokens}>{d.label}</span>\n {d.compactLabel ? <span className={styles.meterCompact}>· {d.compactLabel}</span> : null}\n <span className={styles.meterPercent}>{d.percentLabel}</span>\n </span>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Context mode — Standard / Expansive\n// ---------------------------------------------------------------------------\n\n/**\n * \"Late turns cost up to ≈2.6× the usage\": every turn re-sends the whole\n * context, so the most a turn can carry grows with the compaction point. The\n * ratio is of the two compaction points (the largest prompt each mode lets a\n * turn reach), rounded to one decimal — an honest ceiling, not a forecast.\n */\nexport function expansiveCostRatio(option: VerseModelOption | null | undefined): number | null {\n const standard = budgetFor(option, 'standard');\n const expansive = budgetFor(option, 'expansive');\n if (!standard || !expansive) return null;\n const s = standard.autoCompactAt ?? standard.contextWindow;\n const e = expansive.autoCompactAt ?? expansive.contextWindow;\n if (s <= 0 || e <= s) return null;\n return Math.round((e / s) * 10) / 10;\n}\n\nfunction modeDetail(option: VerseModelOption | null | undefined, mode: VerseContextMode): string {\n const b = budgetFor(option, mode);\n if (!b) return 'not available for this model';\n const point = b.autoCompactAt ?? b.contextWindow;\n return `compacts ≈${formatTokens(point)} of ${formatContextWindow(b.contextWindow)}`;\n}\n\n/**\n * The cost sentence both the mode menu and the suggestion chip say, word for\n * word. The ratio is SIZE alone. On codex the provider reportedly also meters\n * requests above its standard window at about 2× against plan limits, so the\n * size ratio is not the ceiling there: the copy says so, in the same\n * sentence the new-chat dialog uses (verse-model CODEX_EXPANSIVE_METERING_NOTE),\n * so the three places that price Expansive cannot disagree.\n */\nexport function expansiveCostCopy(option: VerseModelOption | null | undefined, engine?: VerseEngine | null): string {\n const ratio = expansiveCostRatio(option);\n const standard = budgetFor(option, 'standard');\n const point = standard ? formatTokens(standard.autoCompactAt ?? standard.contextWindow) : null;\n const codex = engine === 'codex';\n const base = ratio !== null && point !== null\n ? `Every turn re-sends the whole context, so once it grows past ≈${point} each turn costs more usage — up to ≈${ratio}×${codex ? ' by size alone' : ''} near the expansive limit — and recall of early detail weakens with length.`\n : 'Every turn re-sends the whole context, so a larger budget means more usage per turn as it grows.';\n return codex ? `${base} ${CODEX_EXPANSIVE_METERING_NOTE}` : base;\n}\n\n/**\n * Switching DOWN to Standard while the chat already holds more than\n * Standard's compaction point is not the free flag flip the rest of the menu\n * describes: the CLI compacts on the very next turn (Claude Code checks its\n * threshold before calling the model; codex falls back to its 244.8k limit),\n * which rewrites the conversation, starts the prompt cache over and — on a\n * paid seat — spends a summarization call. Null when switching is free.\n * `definite` is false for an upper-bound reading: the real prompt MAY be under.\n */\nexport function standardSwitchCompacts(\n option: VerseModelOption | null | undefined,\n mode: VerseContextMode,\n reading: Pick<SessionContextBudget, 'contextTokens' | 'exact'> | null | undefined,\n): { point: number; tokens: number; definite: boolean } | null {\n if (mode !== 'expansive' || !reading) return null;\n const standard = budgetFor(option, 'standard');\n if (!standard) return null;\n const point = standard.autoCompactAt ?? standard.contextWindow;\n if (reading.contextTokens < point) return null;\n return { point, tokens: reading.contextTokens, definite: reading.exact };\n}\n\n/**\n * Where the open menu goes, in VIEWPORT coordinates (position: fixed).\n *\n * WHY FIXED, and computed: the chip sits in the right-pinned action cluster,\n * ~119px from the pane's edge, and the menu is up to 380px wide — anchored\n * `right: 0` it started at x = −95 on a 375px screen, and in the app the\n * section's `overflow: hidden` cut it at the rail (x = 56), clipping every\n * label (\"rrent\", \"…of 1M\"). Fixed placement escapes that clip; the three\n * cases keep it inside the viewport with a gutter:\n *\n * 1. right-aligned to the chip (the desktop look) when there is room to its left;\n * 2. left-aligned to the chip when there is room to its right instead;\n * 3. otherwise the full width between the gutters (phone widths).\n *\n * Null when there is no layout to measure (a zero-size anchor — jsdom, a\n * hidden header): the stylesheet's absolute placement stands.\n */\nexport const MODE_MENU_GUTTER = 12;\nexport const MODE_MENU_MIN_WIDTH = 280;\nexport const MODE_MENU_MAX_WIDTH = 380;\nconst MODE_MENU_OFFSET = 8;\n\nexport function modeMenuPlacement(\n anchor: Pick<DOMRect, 'left' | 'right' | 'bottom'>,\n viewport: { width: number; height: number },\n): CSSProperties | null {\n if (!(viewport.width > 0) || !(anchor.right > anchor.left)) return null;\n const top = Math.round(anchor.bottom + MODE_MENU_OFFSET);\n const common: CSSProperties = {\n position: 'fixed',\n top,\n maxHeight: Math.max(160, Math.round(viewport.height - top - MODE_MENU_GUTTER)),\n overflowY: 'auto',\n };\n const roomLeft = anchor.right - MODE_MENU_GUTTER;\n if (roomLeft >= MODE_MENU_MIN_WIDTH) {\n return { ...common, left: 'auto', right: Math.round(viewport.width - anchor.right), maxWidth: Math.min(MODE_MENU_MAX_WIDTH, Math.floor(roomLeft)) };\n }\n const roomRight = viewport.width - MODE_MENU_GUTTER - anchor.left;\n if (roomRight >= MODE_MENU_MIN_WIDTH) {\n return { ...common, left: Math.round(anchor.left), right: 'auto', maxWidth: Math.min(MODE_MENU_MAX_WIDTH, Math.floor(roomRight)) };\n }\n return { ...common, left: MODE_MENU_GUTTER, right: MODE_MENU_GUTTER, width: 'auto', minWidth: 0, maxWidth: 'none' };\n}\n\nexport interface ContextModeControlProps {\n mode: VerseContextMode;\n option: VerseModelOption | null;\n disabled?: boolean;\n /** Why the control is disabled, for the tooltip. */\n disabledReason?: string | null;\n busy?: boolean;\n error?: string | null;\n onChange: (mode: VerseContextMode) => void;\n /**\n * Opens the \"Compact now\" panel. Passed only for sessions whose engine can\n * compact on request (`canCompactNow`); absent → no menu item.\n */\n onCompact?: () => void;\n /** Why \"Compact now…\" cannot run yet (no turns), shown on the disabled item. */\n compactUnavailableReason?: string | null;\n /**\n * False → this model has ONE budget (a 200k Claude model, a local seat): the\n * chip reads \"Context\" and its menu carries only \"Compact now…\", so a manual\n * compaction is reachable on demand on every engine that can do one, not\n * only from the handoff banner once it appears. Default true.\n */\n modesAvailable?: boolean;\n engine?: VerseEngine | null;\n /** The session's current reading and budget — the downgrade warning and the one-budget summary read it. */\n budget?: Pick<SessionContextBudget, 'contextTokens' | 'exact' | 'contextWindow' | 'autoCompactAt'> | null;\n}\n\n/**\n * The context chip beside the meter and its menu. With a real expansive\n * budget (or a session already in expansive, so it can always switch back)\n * it is the Standard / Expansive control; for a claude/local chat without\n * one it is a \"Context\" menu holding \"Compact now…\". Changing mode is an\n * explicit click — Verse never switches it on its own — and applies from the\n * NEXT turn: it changes CLI flags only, never prompt content, so the prompt\n * cache holds — EXCEPT a switch down to Standard while the chat is already\n * past Standard's compaction point, which compacts on the next turn and is\n * said so in the menu (standardSwitchCompacts).\n */\nexport function ContextModeControl({ mode, option, disabled = false, disabledReason = null, busy = false, error = null, onChange, onCompact,\n compactUnavailableReason = null, modesAvailable = true, engine = null, budget = null }: ContextModeControlProps) {\n const [open, setOpen] = useState(false);\n const [placement, setPlacement] = useState<CSSProperties | null>(null);\n const wrap = useRef<HTMLDivElement>(null);\n const button = useRef<HTMLButtonElement>(null);\n const menu = useRef<HTMLDivElement>(null);\n const menuId = useId();\n const expansiveAvailable = hasExpansiveMode(option);\n const downgrade = modesAvailable ? standardSwitchCompacts(option, mode, budget) : null;\n const who = engine ? COMPACTOR[engine] : 'The CLI';\n\n // An error keeps the menu open so the operator sees why nothing changed.\n useEffect(() => {\n if (error) setOpen(true);\n }, [error]);\n\n // A turn starting while the menu is open disables the chip (the engine\n // answers 409 to a mode change mid-turn); a menu left open under a disabled\n // chip would offer choices that can only fail.\n useEffect(() => {\n if (disabled) setOpen(false);\n }, [disabled]);\n\n useEffect(() => {\n if (!open) return;\n const onPointerDown = (event: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(event.target as Node)) setOpen(false);\n };\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n setOpen(false);\n button.current?.focus();\n }\n };\n document.addEventListener('pointerdown', onPointerDown);\n document.addEventListener('keydown', onKey);\n return () => {\n document.removeEventListener('pointerdown', onPointerDown);\n document.removeEventListener('keydown', onKey);\n };\n }, [open]);\n\n // Placed before paint (no flash at the clipped spot) and again on resize.\n useLayoutEffect(() => {\n if (!open) {\n setPlacement(null);\n return undefined;\n }\n const place = () => {\n const rect = button.current?.getBoundingClientRect();\n setPlacement(rect ? modeMenuPlacement(rect, { width: window.innerWidth, height: window.innerHeight }) : null);\n };\n place();\n window.addEventListener('resize', place);\n return () => window.removeEventListener('resize', place);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n // The current mode first; a one-budget menu has no radios, so its first\n // enabled item; and when even that is disabled, the menu itself, so\n // focus never falls to <body>.\n const target = wrap.current?.querySelector<HTMLButtonElement>('[role=\"menuitemradio\"][aria-checked=\"true\"]')\n ?? wrap.current?.querySelector<HTMLButtonElement>('[role=\"menuitemradio\"]:not(:disabled), [role=\"menuitem\"]:not(:disabled)')\n ?? menu.current;\n target?.focus();\n }, [open]);\n\n function onMenuKey(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;\n const items = Array.from(wrap.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitemradio\"]:not(:disabled), [role=\"menuitem\"]:not(:disabled)') ?? []);\n if (items.length === 0) return;\n event.preventDefault();\n const index = items.indexOf(document.activeElement as HTMLButtonElement);\n const next = event.key === 'ArrowDown' ? (index + 1) % items.length : (index - 1 + items.length) % items.length;\n items[next]?.focus();\n }\n\n const choose = (next: VerseContextMode) => {\n if (next === mode || busy) return;\n // Close on the choice; the chip shows \"Switching…\" while the write is in\n // flight, and a failure re-opens the menu with the reason (effect above).\n setOpen(false);\n button.current?.focus();\n onChange(next);\n };\n\n const compact = () => {\n setOpen(false);\n // Focus goes back to the chip BEFORE the panel opens: the panel takes it\n // into its own field and, on close, hands it back to whatever held it —\n // the chip — instead of both ending on <body> with the unmounted item.\n button.current?.focus();\n onCompact?.();\n };\n\n // What changes on a mode switch, per CLI: Claude Code takes one flag\n // (--autocompact); codex takes a window AND a compaction limit.\n const flagChange = engine === 'codex'\n ? \"The CLI's window and compaction settings change\"\n : \"Only the CLI's compaction flag changes\";\n const oneBudgetPoint = budget?.autoCompactAt ?? null;\n const chipLabel = modesAvailable ? CONTEXT_MODE_LABEL[mode] : 'Context';\n const ariaLabel = modesAvailable ? `Context mode: ${CONTEXT_MODE_LABEL[mode]}` : 'Context actions';\n const chipTitle = disabled && disabledReason\n ? disabledReason\n : modesAvailable\n ? `Context mode: ${CONTEXT_MODE_LABEL[mode]} — ${modeDetail(option, mode)}`\n : 'Context: this model has one budget — open to compact the chat now';\n\n return (\n <div ref={wrap} className={styles.modeWrap}>\n <button ref={button} type=\"button\" className={styles.modeChip} data-mode={modesAvailable ? mode : undefined}\n aria-haspopup=\"menu\" aria-expanded={open} aria-controls={open ? menuId : undefined}\n aria-label={ariaLabel} disabled={disabled} title={chipTitle}\n onClick={() => setOpen((v) => !v)}>\n {busy ? 'Switching…' : chipLabel}\n </button>\n {open ? (\n <div ref={menu} id={menuId} role=\"menu\" aria-label={modesAvailable ? 'Context mode' : 'Context'} className={styles.modeMenu}\n style={placement ?? undefined} data-placement={placement ? 'fixed' : undefined} tabIndex={-1} onKeyDown={onMenuKey}>\n {modesAvailable ? (\n <>\n <p className={styles.modeMenuHeading}>How much context this chat may hold before the CLI compacts</p>\n {(['standard', 'expansive'] as const).map((m) => {\n const unavailable = m === 'expansive' && !expansiveAvailable;\n const compactsNext = m === 'standard' && downgrade !== null\n ? ` · ${downgrade.definite ? 'compacts' : 'may compact'} on the next turn`\n : '';\n return (\n <button key={m} type=\"button\" role=\"menuitemradio\" aria-checked={mode === m} className={styles.modeItem}\n disabled={unavailable || busy} onClick={() => choose(m)}>\n <span className={styles.modeItemPrimary}>{CONTEXT_MODE_LABEL[m]}{mode === m ? ' · current' : ''}</span>\n <span className={styles.modeItemSecondary}>\n {m === 'standard'\n ? `${modeDetail(option, 'standard')} · lower usage per turn, sharper recall${compactsNext}`\n : unavailable ? 'not available for this model on this seat' : `${modeDetail(option, 'expansive')} · the full native window`}\n </span>\n </button>\n );\n })}\n <p className={styles.modeMenuNote}>{expansiveCostCopy(option, engine)}</p>\n {downgrade ? (\n <>\n <p className={styles.modeMenuNote}>Applies from the next turn.</p>\n <p className={styles.modeMenuWarn} data-testid=\"standard-compacts\">\n {downgrade.definite\n ? `This chat holds ≈${formatTokens(downgrade.tokens)} — past Standard's ≈${formatTokens(downgrade.point)} compaction point, so switching to Standard makes ${who} compact on the next turn`\n : `This chat holds up to ≈${formatTokens(downgrade.tokens)}. If the real prompt is past Standard's ≈${formatTokens(downgrade.point)} compaction point, switching to Standard makes ${who} compact on the next turn`}\n {engine === 'local' ? '' : ': one summarization call that spends usage on this seat'}. The conversation is replaced by a summary and its prompt cache starts over.\n </p>\n </>\n ) : (\n <p className={styles.modeMenuNote}>\n Applies from the next turn. {flagChange} — the conversation and its prompt cache are kept.\n </p>\n )}\n </>\n ) : (\n <p className={styles.modeMenuHeading}>\n {oneBudgetPoint !== null\n ? `${who} compacts this chat on its own at ≈${formatTokens(oneBudgetPoint)}${budget?.contextWindow ? ` of ${formatContextWindow(budget.contextWindow)}` : ''}. This model has one context budget.`\n : 'This model has one context budget; the CLI compacts at its own point.'}\n </p>\n )}\n {onCompact ? (\n <button type=\"button\" role=\"menuitem\" className={`${styles.modeItem} ${modesAvailable ? styles.modeItemAction : ''}`}\n disabled={busy || compactUnavailableReason !== null} onClick={compact}>\n <span className={styles.modeItemPrimary}>Compact now…</span>\n <span className={styles.modeItemSecondary}>\n {compactUnavailableReason ?? 'summarize the conversation so far instead of waiting for the CLI to'}\n </span>\n </button>\n ) : null}\n {error ? <p role=\"alert\" className={styles.modeMenuError}>{error}</p> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Advice — \"continue in a fresh chat\" and \"expansive may help\"\n// ---------------------------------------------------------------------------\n\n/**\n * Re-evaluated on a clock because one reason (\"idle past the cache lifetime\")\n * becomes true with no event at all. A minute is well inside the 1h TTL.\n */\nconst ADVICE_TICK_MS = 60_000;\n\nfunction useNow(intervalMs: number, injected?: number): number {\n const [now, setNow] = useState(() => injected ?? Date.now());\n useEffect(() => {\n if (injected !== undefined) {\n setNow(injected);\n return undefined;\n }\n const id = setInterval(() => setNow(Date.now()), intervalMs);\n return () => clearInterval(id);\n }, [intervalMs, injected]);\n return now;\n}\n\n/**\n * The handoff verdict for a session, from the SAME budget the meter draws.\n *\n * `lastTurnAt` is the last time the chat talked to its provider (verse-store\n * `lastTurnActivityAt`), NOT `session.updatedAt`: a rename or a mode switch\n * bumps `updatedAt` without warming the provider's cache, and measuring idle\n * time from it silenced \"the prompt cache has likely expired\" for another\n * hour while the next turn still re-read everything uncached.\n */\nexport function sessionHandoffAdvice(\n session: Pick<VerseSession, 'usage' | 'compactionCount' | 'status'>,\n budget: SessionContextBudget,\n now: number,\n lastTurnAt: string | null = null,\n): VerseHandoffAdvice {\n return handoffAdvice({\n usage: {\n ...session.usage,\n contextTokens: budget.contextTokens,\n contextWindow: budget.contextWindow,\n autoCompactAt: budget.autoCompactAt,\n contextTokensExact: budget.exact,\n },\n compactionCount: session.compactionCount ?? 0,\n // A running turn is activity: the cache is being refreshed right now.\n lastActivityAt: session.status === 'running' ? null : lastTurnAt,\n now,\n });\n}\n\n/**\n * Whether ContextAdvice would show anything — the SAME rules, without\n * rendering. The notice slot (chat/NoticeSlot) needs to know before it picks\n * which one notice to show.\n */\nexport function contextAdviceVisible(\n session: VerseSession,\n budget: SessionContextBudget,\n modesAvailable: boolean,\n now: number,\n lastTurnAt: string | null = null,\n): boolean {\n const handoff = sessionHandoffAdvice(session, budget, now, lastTurnAt);\n const compactions = session.compactionCount ?? 0;\n if (handoff.level !== 'none' && !isVerseAdviceDismissed(session.id, `handoff:${handoff.level}:${compactions}`)) return true;\n if (!modesAvailable) return false;\n return expansiveAdvice({ session, option: budget.option }).suggest && !isVerseAdviceDismissed(session.id, `expansive:${compactions}`);\n}\n\nexport interface ContextAdviceProps {\n session: VerseSession;\n budget: SessionContextBudget;\n /** Whether the mode control exists for this session (the chip only offers what it can do). */\n modesAvailable: boolean;\n dispatchEnabled: boolean;\n modeBusy?: boolean;\n onHandoff: () => void;\n onSwitchExpansive: () => void;\n /** Opens the \"Compact now\" panel; passed only when `canCompactNow(engine)`. */\n onCompact?: () => void;\n /**\n * When the chat last talked to its provider (verse-store\n * `lastTurnActivityAt`); null/absent → no idle-cache reason.\n */\n lastTurnAt?: string | null;\n /** Injectable clock for tests. */\n now?: number;\n /** 3.10: rendered inside the notice slot, which owns the column and spacing. */\n embedded?: boolean;\n /** Called after \"Not now\" hides a note, so a parent that decides visibility re-checks. */\n onDismiss?: () => void;\n}\n\n/**\n * The banner row under the header. Two independent, dismissible notes:\n *\n * - HANDOFF (handoffAdvice ≥ suggest) — reasons listed in plain sentences,\n * and one action that opens the handoff dialog. Building the note is free;\n * nothing is spent until the operator presses Send in the new chat.\n * - EXPANSIVE (expansiveAdvice) — a suggestion chip with the cost stated.\n * Never an automatic switch.\n *\n * \"Not now\" is remembered per session for the page's lifetime (verse-ui-store,\n * not persisted: advice is evidence, and stale evidence must not outlive a\n * reload), and a note comes back when its evidence escalates.\n */\nexport function ContextAdvice({ session, budget, modesAvailable, dispatchEnabled, modeBusy = false, onHandoff, onSwitchExpansive, onCompact,\n lastTurnAt = null, now: injectedNow, embedded = false, onDismiss }: ContextAdviceProps) {\n const now = useNow(ADVICE_TICK_MS, injectedNow);\n const [, bump] = useState(0);\n const handoff = sessionHandoffAdvice(session, budget, now, lastTurnAt);\n const expansive = modesAvailable\n ? expansiveAdvice({ session, option: budget.option })\n : { suggest: false, reason: null };\n const compactions = session.compactionCount ?? 0;\n const handoffKey = `handoff:${handoff.level}:${compactions}`;\n const expansiveKey = `expansive:${compactions}`;\n const showHandoff = handoff.level !== 'none' && !isVerseAdviceDismissed(session.id, handoffKey);\n const showExpansive = expansive.suggest && !isVerseAdviceDismissed(session.id, expansiveKey);\n if (!showHandoff && !showExpansive) return null;\n\n const running = session.status === 'running';\n const dismiss = (key: string) => {\n dismissVerseAdvice(session.id, key);\n bump((n) => n + 1);\n onDismiss?.();\n };\n const idleExpired = lastTurnAt && !running ? now - Date.parse(lastTurnAt) >= CACHE_IDLE_TTL_MS : false;\n\n return (\n <div className={embedded ? styles.adviceStack : styles.advice}>\n {showHandoff ? (\n <section className={styles.adviceNote} data-level={handoff.level} aria-label=\"Context advice\">\n <div className={styles.adviceBody}>\n <p className={styles.adviceTitle}>\n {handoff.level === 'urge' ? 'Time to continue in a fresh chat' : 'Consider continuing in a fresh chat'}\n </p>\n <ul className={styles.adviceReasons}>\n {handoff.reasons.map((reason) => <li key={reason}>{reason}</li>)}\n </ul>\n <p className={styles.adviceFine}>\n Verse drafts a handoff note from this chat&apos;s log — free. Nothing is sent until you press Send in the new chat.\n {idleExpired ? ' Continuing here instead re-reads the whole context at full cost.' : ''}\n </p>\n </div>\n <div className={styles.adviceActions}>\n <button type=\"button\" className={styles.adviceAction} onClick={onHandoff} disabled={!dispatchEnabled || running}\n title={running ? 'Available when the current turn finishes' : !dispatchEnabled ? 'Sending is disabled on this server' : undefined}>\n Continue in a fresh chat…\n </button>\n {onCompact ? (\n // The in-place alternative: keep this chat, trade its early\n // detail for a summary now. Same guards as the handoff.\n <button type=\"button\" className={styles.ghost} onClick={onCompact} disabled={!dispatchEnabled || running || session.turnCount === 0}\n title={running ? 'Available when the current turn finishes' : !dispatchEnabled ? 'Sending is disabled on this server' : 'Summarize this chat in place instead'}>\n Compact now…\n </button>\n ) : null}\n <button type=\"button\" className={styles.ghost} onClick={() => dismiss(handoffKey)}>Not now</button>\n </div>\n </section>\n ) : null}\n {showExpansive ? (\n <section className={styles.adviceNote} data-level=\"suggest\" data-kind=\"expansive\" aria-label=\"Expansive mode suggestion\">\n <div className={styles.adviceBody}>\n <p className={styles.adviceTitle}>Expansive mode could help</p>\n <p className={styles.adviceFine}>{expansive.reason} {expansiveCostCopy(budget.option, session.engine)}</p>\n </div>\n <div className={styles.adviceActions}>\n {/* Disabled mid-turn: the engine answers 409 to a mode change while a turn runs. */}\n <button type=\"button\" className={styles.adviceAction} onClick={onSwitchExpansive} disabled={!dispatchEnabled || modeBusy || running}\n title={running ? 'Available when the current turn finishes' : undefined}>\n {modeBusy ? 'Switching…' : 'Switch to expansive'}\n </button>\n <button type=\"button\" className={styles.ghost} onClick={() => dismiss(expansiveKey)}>Not now</button>\n </div>\n </section>\n ) : null}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Compact now — the CLI's own /compact, sent as an ordinary turn\n// ---------------------------------------------------------------------------\n\n/**\n * Engines whose CLI compacts on request. Claude Code (which also drives the\n * local seats) accepts `/compact` as the text of a headless turn on a resumed\n * session and answers with a `compact_boundary` (trigger \"manual\") that the\n * adapter already turns into a compaction event — verified for real on a\n * local seat (build-U2 §2). Codex `exec` has no compact verb, and Grok's\n * headless `/compact` is unverified, so neither is offered: a button that\n * sends a literal \"/compact\" to a model that treats it as prose would spend a\n * turn and compact nothing.\n */\nexport function canCompactNow(engine: VerseEngine | null | undefined): boolean {\n return engine === 'claude' || engine === 'local';\n}\n\n/** Longest focus line accepted — a steer for the summary, not a second prompt. */\nexport const COMPACT_FOCUS_MAX = 500;\n\n/**\n * The turn text. The focus is folded to one line: the CLI reads a slash\n * command's arguments from the rest of its line, so a newline would silently\n * drop everything after it.\n */\nexport function compactCommand(focus: string): string {\n const line = focus.replace(/\\s+/g, ' ').trim().slice(0, COMPACT_FOCUS_MAX).trim();\n return line ? `/compact ${line}` : '/compact';\n}\n\n/** The cost sentence, per engine — the part of this panel that must not be vague. */\nexport function compactCostCopy(engine: VerseEngine, tokensLabel: string): string {\n return engine === 'local'\n ? `Free — it runs on the local model — but summarizing ${tokensLabel} tokens runs at that model's speed and can take minutes; the chat is busy until it finishes.`\n : `Spends usage on this seat: one summarization call that reads the whole current context (${tokensLabel} tokens) and writes the summary.`;\n}\n\nexport interface CompactPanelProps {\n engine: VerseEngine;\n budget: Pick<SessionContextBudget, 'contextTokens' | 'autoCompactAt' | 'exact'>;\n running: boolean;\n dispatchEnabled: boolean;\n /** No turn has run yet, so there is no resumable conversation to compact. */\n empty: boolean;\n /** Sends the text through the chat's normal send path; true when it was accepted. */\n onSend: (text: string) => Promise<boolean>;\n onClose: () => void;\n /**\n * Take keyboard focus into the focus field on mount (default true: the\n * panel only ever opens on a click or a menu choice). On unmount focus goes\n * back to whatever held it before — the chip or the banner button — when\n * nothing else has claimed it.\n */\n autoFocus?: boolean;\n}\n\n/**\n * The confirm step for a manual compaction, under the header strip beside the\n * advice notes. It exists because the action is not free on a paid seat and\n * not quick on a local one, and a one-click menu item would hide both.\n *\n * It sends through the SAME path as the composer (Workspace `onSend` →\n * ChatSection `send` → POST …/turns): the token gate, the running state, the\n * transcript entry and the engine's local-only chokepoint all apply exactly\n * as for a typed message. Nothing here is a new way to reach a model.\n */\nexport function CompactPanel({ engine, budget, running, dispatchEnabled, empty, onSend, onClose, autoFocus = true }: CompactPanelProps) {\n const [focus, setFocus] = useState('');\n const [sending, setSending] = useState(false);\n const focusId = useId();\n const input = useRef<HTMLInputElement>(null);\n\n // Opened from the mode menu, the focused menu item unmounts with the menu;\n // without this, keyboard focus fell to <body> and a screen reader heard\n // nothing about the panel that appeared.\n useEffect(() => {\n if (!autoFocus) return undefined;\n const previous = document.activeElement instanceof HTMLElement && document.activeElement !== document.body ? document.activeElement : null;\n input.current?.focus();\n return () => {\n const active = document.activeElement;\n const stranded = active === null || active === document.body || !active.isConnected;\n if (stranded && previous?.isConnected) previous.focus();\n };\n }, [autoFocus]);\n const who = COMPACTOR[engine];\n const tokensLabel = `${budget.exact ? '≈' : '≤'}${formatTokens(budget.contextTokens)}`;\n const blocked = running ? 'Available when the current turn finishes.'\n : !dispatchEnabled ? 'Sending is disabled: this server was started without dispatch.'\n : empty ? 'Nothing to compact yet — this chat has no turns.'\n : null;\n\n async function submit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n if (blocked || sending) return;\n setSending(true);\n try {\n if (await onSend(compactCommand(focus))) onClose();\n } finally {\n setSending(false);\n }\n }\n\n return (\n <section className={styles.adviceNote} data-level=\"suggest\" data-kind=\"compact\" aria-label=\"Compact this chat\">\n <form className={styles.compactForm} onSubmit={(event) => { void submit(event); }}>\n <div className={styles.adviceBody}>\n <p className={styles.adviceTitle}>Compact this chat now</p>\n <p className={styles.adviceFine}>\n {who} replaces the conversation so far with a summary it writes, then carries on from it — what it does on its own\n {budget.autoCompactAt !== null ? ` at ≈${formatTokens(budget.autoCompactAt)}` : ' at its compaction point'}, done now instead.\n Early detail then survives only as that summary.\n </p>\n <p className={styles.adviceFine}>{compactCostCopy(engine, tokensLabel)}</p>\n <label className={styles.compactLabel} htmlFor={focusId}>Keep in focus (optional)</label>\n <input ref={input} id={focusId} className={styles.compactInput} value={focus} maxLength={COMPACT_FOCUS_MAX}\n placeholder=\"e.g. the login fix and its open TODOs\" onChange={(event) => setFocus(event.target.value)} />\n <p className={styles.adviceFine}>\n Sent as the message <code>{compactCommand(focus)}</code>, so it shows in the transcript like any turn.\n </p>\n {blocked ? <p className={styles.adviceFine} role=\"status\">{blocked}</p> : null}\n </div>\n <div className={styles.adviceActions}>\n <button type=\"submit\" className={styles.adviceAction} disabled={blocked !== null || sending}>\n {sending ? 'Sending…' : 'Compact now'}\n </button>\n <button type=\"button\" className={styles.ghost} onClick={onClose}>Cancel</button>\n </div>\n </form>\n </section>\n );\n}\n","/**\n * routes/verse/composer/ContextRing.tsx — \"◔ 31%\" in the composer footer\n * (unit C3): how full this chat's context window is.\n *\n * It IS the header ring, not a lookalike: the percentage, the tone and the\n * compaction tick come from ContextMeter's `describeContext` (context-math\n * `occupancy()`, measured against the compaction point) and are drawn by the\n * same `ContextRingGlyph`, from the SAME budget (verse-model\n * `sessionContextBudget`), so the two can never disagree. Hover or focus\n * shows the reading in full — used / limit tokens and where the CLI\n * compacts. Unknown occupancy is a dashed ring and \"—\", never a zero.\n *\n * The ring is quantity (the fixed azure ramp); it takes the warning colour\n * only as the chat nears the compaction point, and says so in words.\n */\nimport { Tooltip } from '../../../components/primitives/Tooltip.js';\nimport type { VerseEngine } from '../../../data/api-types.js';\nimport { ContextRingGlyph, describeContext } from '../ContextMeter.js';\nimport styles from './composer.module.css';\n\nexport interface ContextRingProps {\n contextTokens: number | null;\n contextWindow: number | null;\n autoCompactAt: number | null;\n exact: boolean;\n /** Who compacts (\"Claude Code\", \"Codex\") — the same words the header uses. */\n engine?: VerseEngine | null;\n}\n\nexport function ContextRing({ contextTokens, contextWindow, autoCompactAt, exact, engine = null }: ContextRingProps) {\n const measured = contextTokens !== null;\n const d = describeContext({ contextTokens, contextWindow, autoCompactAt, exact, engine });\n const [used = '', limit = 'n/a'] = d.label.split(' / ');\n // The accessible name is the short reading; the tooltip (its description) the full one.\n const name = !measured\n ? 'Context — not measured yet'\n : d.percent === null\n ? `Context ${used} tokens — the window is unknown`\n : `Context ${d.percentLabel} — ${used} of ${limit} tokens${d.compactLabel ? `, ${d.compactLabel}` : ''}`;\n return (\n <Tooltip placement=\"top\" content={(\n <span className={styles.tip}>\n {measured ? d.summary.map((line) => <span key={line}>{line}</span>) : <span>Context — not measured yet</span>}\n </span>\n )}>\n <span className={styles.contextRing} role=\"img\" aria-label={name} tabIndex={0}\n data-tone={measured ? d.tone : 'unknown'} data-exact={exact ? undefined : 'false'}>\n <ContextRingGlyph fillPercent={measured ? d.fillPercent : 0} tickPercent={measured ? d.tickPercent : null}\n classes={{ track: styles.ringTrack, fill: styles.contextFill, tick: styles.contextTick }} />\n <span className={styles.contextText} aria-hidden=\"true\">{measured ? d.percentLabel : '—'}</span>\n </span>\n </Tooltip>\n );\n}\n","/**\n * routes/verse/composer/ControlMenu.tsx — one picker in the composer footer\n * (Permission ▾, Model ▾, Effort ▾; unit C3).\n *\n * An ARIA menu button whose items are `menuitemradio`s:\n * - ↑/↓ move (wrapping), Home/End jump, a letter jumps to the next item\n * starting with it, Enter/Space choose, Esc closes and returns focus to\n * the button, Tab closes and lets focus move on;\n * - an UNAVAILABLE option is shown, focusable and announced, but cannot be\n * chosen — its reason sits under its label (never a hover-only tooltip),\n * because \"why can't I pick Max?\" is exactly the question the menu must\n * answer;\n * - a DANGER option (bypass) is painted red and says so in words.\n *\n * `openRequest` lets the owner open it from a shortcut (⌘⇧M/⌘⇧I/⌘⇧E) or a\n * slash command: every new value opens the menu with focus on the checked item.\n *\n * `variant=\"list\"` renders the same items as a plain radio list, for the 375px\n * sheet where a popover over a popover would be unusable.\n *\n * The button shows a SHORT value (\"Accept edits\", \"Effort: High\") and never\n * ellipsizes it; the full value is its accessible name and its tooltip, with\n * the shortcut. Where the footer is too narrow it folds to the icon alone\n * (`iconOnly`) — the name and the tooltip still carry the words, and the\n * tooltip shows on keyboard focus as well as hover (a native `title` never\n * shows on focus, which left a sighted keyboard user with a bare glyph).\n *\n * Unmounted while open (the footer folded it into the ⋯ sheet, or the seat\n * lost the control), it reports itself closed, so the owner's \"a menu is\n * open\" state — which decides whether Esc stops the turn — never outlives it.\n */\nimport { useCallback, useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport type { VerseControlOption } from '../../../../core/verse/workbench-types.js';\nimport { Tooltip, type TooltipPlacement } from '../../../components/primitives/Tooltip.js';\nimport styles from './composer.module.css';\n\nexport interface ControlMenuProps<T extends string> {\n /** Accessible name of the picker (\"Permission mode\"). */\n label: string;\n /** What the button shows for the current value (short: \"Accept edits\", \"Effort: High\"). */\n valueLabel: string;\n /** The full current value, for the accessible name and tooltip (\"Bypass permissions\"). Default: `valueLabel`. */\n valueTitle?: string;\n options: readonly VerseControlOption<T>[];\n /** The checked option; null = none (e.g. effort at the CLI default). */\n value: T | null;\n onChange: (id: T) => void;\n /** An extra first item that resets to the default (effort \"Default\"). */\n defaultOption?: { label: string; description?: string; onSelect: () => void } | null;\n disabled?: boolean;\n /** Why the whole picker is disabled — shown as its tooltip. */\n disabledReason?: string | null;\n /** Shortcut hint shown in the menu header (\"⇧⌘M\"). */\n shortcut?: string;\n /** Bumped to open the menu programmatically. */\n openRequest?: number;\n /** A leading glyph for the button. */\n icon?: ReactNode;\n /** Paint the button as dangerous (bypass is on). */\n danger?: boolean;\n /** One sentence under the header: when a change applies. */\n note?: string | null;\n variant?: 'menu' | 'list';\n /** Collapse the button to its icon (the compact footer). */\n iconOnly?: boolean;\n /**\n * Where the name bubble sits. Default 'left' (flipping right at the edge):\n * BESIDE the button, never above it, because the menu opens upward and a\n * bubble above would cover its last items (see the note at the button).\n */\n tooltipPlacement?: TooltipPlacement;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function ControlMenu<T extends string>({\n label, valueLabel, valueTitle = valueLabel, options, value, onChange, defaultOption = null, disabled = false, disabledReason = null,\n shortcut, openRequest, icon, danger = false, note = null, variant = 'menu', iconOnly = false, tooltipPlacement = 'left', onOpenChange,\n}: ControlMenuProps<T>) {\n const [open, setOpen] = useState(false);\n const wrap = useRef<HTMLDivElement>(null);\n const button = useRef<HTMLButtonElement>(null);\n const menuId = useId();\n const seenRequest = useRef(openRequest);\n // Read by the unmount cleanup, which must see the LAST open state and owner callback.\n const openNow = useRef(open);\n openNow.current = open;\n const reportOpen = useRef(onOpenChange);\n reportOpen.current = onOpenChange;\n\n const setOpenState = useCallback((next: boolean) => {\n setOpen(next);\n onOpenChange?.(next);\n }, [onOpenChange]);\n\n // Unmounted while open (folded into the ⋯ sheet, or the control went away):\n // the menu is gone, so say so — else the owner's Esc-to-stop stays disarmed.\n useEffect(() => () => {\n if (openNow.current) reportOpen.current?.(false);\n }, []);\n\n const close = useCallback((refocus: boolean) => {\n setOpenState(false);\n if (refocus) button.current?.focus();\n }, [setOpenState]);\n\n // Programmatic open (shortcut / slash command).\n useEffect(() => {\n if (openRequest === undefined || openRequest === seenRequest.current) return;\n seenRequest.current = openRequest;\n if (!disabled && variant === 'menu') setOpenState(true);\n }, [openRequest, disabled, variant, setOpenState]);\n\n // Focus the checked item (else the first) when the menu opens.\n useEffect(() => {\n if (!open) return;\n const items = Array.from(wrap.current?.querySelectorAll<HTMLElement>('[role=\"menuitemradio\"]') ?? []);\n const checked = items.find((item) => item.getAttribute('aria-checked') === 'true');\n (checked ?? items[0])?.focus();\n }, [open]);\n\n // Outside click closes without stealing focus back.\n useEffect(() => {\n if (!open) return undefined;\n const onPointerDown = (event: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(event.target as Node)) close(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [open, close]);\n\n function choose(option: VerseControlOption<T>) {\n if (!option.available) return;\n if (variant === 'menu') close(true);\n onChange(option.id);\n }\n\n function onMenuKey(event: KeyboardEvent<HTMLDivElement>) {\n const items = Array.from(wrap.current?.querySelectorAll<HTMLElement>('[role=\"menuitemradio\"]') ?? []);\n if (items.length === 0) return;\n const index = items.indexOf(document.activeElement as HTMLElement);\n const focusAt = (i: number) => items[(i + items.length) % items.length]?.focus();\n switch (event.key) {\n case 'ArrowDown': event.preventDefault(); focusAt(index + 1); return;\n case 'ArrowUp': event.preventDefault(); focusAt(index - 1); return;\n case 'Home': event.preventDefault(); focusAt(0); return;\n case 'End': event.preventDefault(); focusAt(items.length - 1); return;\n case 'Escape':\n if (variant === 'menu') {\n event.preventDefault();\n // Handled here: the composer's own Escape (stop the turn) must not also fire.\n event.stopPropagation();\n close(true);\n }\n return;\n case 'Tab':\n if (variant === 'menu') close(false);\n return;\n default:\n if (event.key.length === 1 && /\\S/.test(event.key) && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const letter = event.key.toLowerCase();\n for (let step = 1; step <= items.length; step++) {\n const candidate = items[(index + step) % items.length]!;\n if ((candidate.dataset['label'] ?? '').toLowerCase().startsWith(letter)) {\n event.preventDefault();\n candidate.focus();\n return;\n }\n }\n }\n }\n }\n\n // In the sheet's plain lists, ONE item per list is in the tab order (the\n // checked one, else the first) and the arrows move within it — a roving\n // tabindex, so Tab walks list → list instead of through every option.\n const checkedId: string | null = value === null ? (defaultOption ? '__default' : null) : value;\n const tabStop = variant === 'list' ? (checkedId ?? (defaultOption ? '__default' : options[0]?.id ?? null)) : null;\n const items = (\n <>\n {defaultOption ? (\n <MenuItem label={defaultOption.label} description={defaultOption.description ?? null} checked={value === null}\n available danger={false} tabbable={tabStop === '__default'}\n onSelect={() => { if (variant === 'menu') close(true); defaultOption.onSelect(); }} />\n ) : null}\n {options.map((option) => (\n <MenuItem key={option.id} label={option.label} description={option.available ? (option.danger ? 'Skips every permission check — confirmed for this chat only' : null) : (option.reason ?? 'Unavailable on this seat')}\n checked={value === option.id} available={option.available} danger={option.danger === true}\n tabbable={tabStop === option.id}\n onSelect={() => choose(option)} />\n ))}\n </>\n );\n\n if (variant === 'list') {\n return (\n <div ref={wrap} className={styles.controlList} role=\"menu\" aria-label={label} onKeyDown={onMenuKey}>\n {/* No per-list note: the sheet says once, above all three, when changes apply. */}\n <p className={styles.menuHeading}>{label}</p>\n {items}\n </div>\n );\n }\n\n return (\n <div ref={wrap} className={styles.controlWrap}>\n {/* The Tooltip primitive, not a native title: a title shows on hover\n only, and folded to its icon this button's words live nowhere else a\n sighted keyboard user can see. The bubble sits BESIDE the button\n (`tooltipPlacement`), never above it: WebKit does not focus a clicked\n button, so a bubble opened by hover sees no blur when the menu opens,\n and above the button it would cover the menu, which opens upward.\n The wrapper is permanent (only its label changes), so folding never\n remounts the button under focus. */}\n <Tooltip label={disabled && disabledReason ? disabledReason : `${label}: ${valueTitle}`}\n shortcut={disabled ? undefined : shortcut} placement={tooltipPlacement}>\n <button ref={button} type=\"button\" className={`${styles.control} ${danger ? styles.controlDanger : ''} ${iconOnly ? styles.controlIconOnly : ''}`}\n aria-haspopup=\"menu\" aria-expanded={open} aria-controls={open ? menuId : undefined}\n aria-label={`${label}: ${valueTitle}`} disabled={disabled}\n onClick={() => setOpenState(!open)}\n onKeyDown={(event) => {\n if ((event.key === 'ArrowUp' || event.key === 'ArrowDown') && !open) {\n event.preventDefault();\n setOpenState(true);\n }\n }}>\n {icon ? <span className={styles.controlIcon} aria-hidden=\"true\">{icon}</span> : null}\n {iconOnly ? null : <span className={styles.controlText}>{valueLabel}</span>}\n <span className={styles.caret} aria-hidden=\"true\" />\n </button>\n </Tooltip>\n {open ? (\n <div id={menuId} role=\"menu\" aria-label={label} className={styles.menu} onKeyDown={onMenuKey}>\n <p className={styles.menuHeading}>\n <span>{label}</span>\n {shortcut ? <kbd className={styles.menuKbd}>{shortcut}</kbd> : null}\n </p>\n {note ? <p className={styles.menuNote}>{note}</p> : null}\n {items}\n </div>\n ) : null}\n </div>\n );\n}\n\ninterface MenuItemProps {\n /** In the tab order (the list variant's roving tab stop). */\n tabbable?: boolean;\n label: string;\n description: string | null;\n checked: boolean;\n available: boolean;\n danger: boolean;\n onSelect: () => void;\n}\n\nfunction MenuItem({ tabbable = false, label, description, checked, available, danger, onSelect }: MenuItemProps) {\n const descId = useId();\n return (\n <div role=\"menuitemradio\" tabIndex={tabbable ? 0 : -1} aria-checked={checked} aria-disabled={available ? undefined : true}\n aria-describedby={description ? descId : undefined} data-label={label}\n className={`${styles.menuItem} ${danger ? styles.menuItemDanger : ''}`}\n onClick={() => { if (available) onSelect(); }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n if (available) onSelect();\n }\n }}>\n <span className={styles.menuCheck} aria-hidden=\"true\">{checked ? '✓' : ''}</span>\n <span className={styles.menuText}>\n <span className={styles.menuLabel}>{label}</span>\n {description ? <span id={descId} className={styles.menuDesc}>{description}</span> : null}\n </span>\n </div>\n );\n}\n","/**\n * routes/verse/composer/ControlsSheet.tsx — the 375px composer's \"⋯\": the\n * Permission, Model and Effort pickers in one bottom sheet (SPEC-310C §2 \"At\n * 375px\"; unit C3).\n *\n * A phone footer holds [+] [mic], the seat, ⋯ and Send. A popover per picker\n * would open over the keyboard and off the screen edge, so the three become\n * radio lists in a sheet that rises from the bottom. It traps focus while\n * open, Esc and the backdrop close it, and focus returns to ⋯.\n *\n * Below the pickers: \"Run in cloud\" (3.11 cloud lane) — launches the typed\n * message as a cloud task. It is an import() loaded the first time the sheet\n * opens, so nothing cloud-related is on the chat's first-paint path.\n */\nimport { lazy, Suspense, useId, useRef, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useFocusTrap } from '../../../components/primitives/focus-trap.js';\nimport styles from './composer.module.css';\n\nconst RunInCloudAction = lazy(() => import('../cloud/RunInCloudAction.js').then((m) => ({ default: m.RunInCloudAction })));\n\nexport interface ControlsSheetProps {\n open: boolean;\n onClose: () => void;\n children: ReactNode;\n}\n\nexport function ControlsSheet({ open, onClose, children }: ControlsSheetProps) {\n const panel = useRef<HTMLDivElement>(null);\n const close = useRef<HTMLButtonElement>(null);\n const titleId = useId();\n useFocusTrap({ open, containerRef: panel, onClose, initialFocusRef: close });\n if (!open) return null;\n return createPortal(\n <div className={styles.sheetBackdrop} onMouseDown={(event) => { if (event.target === event.currentTarget) onClose(); }}>\n <div ref={panel} className={styles.sheet} role=\"dialog\" aria-modal=\"true\" aria-labelledby={titleId} tabIndex={-1}>\n <div className={styles.sheetHeader}>\n <h2 id={titleId} className={styles.sheetTitle}>Chat settings</h2>\n <button ref={close} type=\"button\" className={styles.sheetClose} onClick={onClose}>Done</button>\n </div>\n <p className={styles.sheetNote}>Changes apply from the next turn.</p>\n <div className={styles.sheetBody}>\n {children}\n <Suspense fallback={null}>\n <RunInCloudAction />\n </Suspense>\n </div>\n </div>\n </div>,\n document.body,\n );\n}\n","/**\n * routes/verse/composer/QueueRow.tsx — \"Queued, sends when this turn ends\n * [Edit] [Send now] [×]\" above the composer (SPEC-310C §2; unit C3).\n *\n * Hidden when empty. Two states, both said in words:\n * - waiting: the follow-ups send one by one as each turn ends cleanly;\n * - HELD: the last turn failed or was stopped, so nothing is sent blind —\n * the row says why and [Send now] is the way on.\n *\n * Edit pulls the text back into the composer (and off the queue), so a\n * follow-up can be reworded after reading the reply it was waiting for.\n */\nimport { IconX } from '../../../components/primitives/icons.js';\nimport type { VerseQueueResponse } from '../../../../core/verse/workbench-types.js';\nimport styles from './composer.module.css';\n\nexport interface QueueRowProps {\n queue: VerseQueueResponse | null;\n running: boolean;\n disabled?: boolean;\n onEdit: (queueId: string, text: string) => void;\n onSendNow: (queueId: string) => void;\n onRemove: (queueId: string) => void;\n}\n\n/** One line of the queued text, trimmed for the row (the full text is its title). */\nfunction preview(text: string): string {\n const line = text.replace(/\\s+/g, ' ').trim();\n return line.length > 140 ? `${line.slice(0, 139)}…` : line;\n}\n\nexport function QueueRow({ queue, running, disabled = false, onEdit, onSendNow, onRemove }: QueueRowProps) {\n if (!queue || queue.items.length === 0) return null;\n const held = queue.held;\n const count = queue.items.length;\n const heading = held\n ? `Held — ${queue.heldReason ?? 'the last turn did not finish cleanly.'}`\n : running\n ? `Queued · sends when this turn ends`\n : `Queued · sending next`;\n return (\n <section className={`${styles.queue} ${held ? styles.queueHeld : ''}`} aria-label={`${count} queued follow-up${count === 1 ? '' : 's'}`}>\n <p className={styles.queueHeading} role=\"status\">{heading}</p>\n <ol className={styles.queueList}>\n {queue.items.map((item, index) => (\n <li key={item.id} className={styles.queueItem}>\n <span className={styles.queueIndex} aria-hidden=\"true\">{index + 1}</span>\n <span className={styles.queueText} title={item.text}>{preview(item.text)}</span>\n <span className={styles.queueActions}>\n <button type=\"button\" className={styles.queueButton} disabled={disabled}\n aria-label={`Edit queued message ${index + 1}`} onClick={() => onEdit(item.id, item.text)}>Edit</button>\n <button type=\"button\" className={`${styles.queueButton} ${styles.queueButtonPrimary}`} disabled={disabled}\n aria-label={running ? `Stop the turn and send queued message ${index + 1} now` : `Send queued message ${index + 1} now`}\n onClick={() => onSendNow(item.id)}>Send now</button>\n <button type=\"button\" className={styles.queueRemove} disabled={disabled}\n aria-label={`Remove queued message ${index + 1}`} onClick={() => onRemove(item.id)}>\n <IconX width={12} height={12} aria-hidden=\"true\" />\n </button>\n </span>\n </li>\n ))}\n </ol>\n </section>\n );\n}\n","/**\n * routes/verse/composer/SeatChip.tsx — the seat this chat runs on, in the\n * composer footer (SPEC-310C §2 \"Seat chip\"; unit C3).\n *\n * [C] Claude Max ◔ [L] Local\n *\n * - The chip names the SEAT (the account: \"Claude Max\", or \"Local\" for a\n * local model, whose seat label is the model's own name); the model sits\n * beside it in the Model picker, so the footer never says the model twice.\n * - The monogram tile (C/X/G/L, never a vendor logo) carries the engine's\n * identity tick; the ring is the SHORT window's use (5-hour where the\n * provider reports one, else the binding window), drawn in the quantity\n * ramp — never the accent — and switched to the status colours only past\n * the tight / limit points, always with words in the tooltip. A seat with\n * no reading (and every local seat: no limits) draws NO ring — an empty\n * circle said nothing and read as a stuck spinner.\n * - Hover or focus shows plan, every window with its reset, health, and what\n * autonomy may take from this seat (\"autonomy may use 60% · 40% reserved\"),\n * read from the Budget route (A9) only while the tooltip is open.\n * - Click opens the menu: \"Continue on ‹seat›\" (a prefilled handoff when the\n * owner wires it; a new chat otherwise), a new chat on this seat, and\n * Budget mode ▸ (A9's BudgetControl, loaded on demand).\n *\n * A chat is bound to its seat; nothing here mutates the chat's seat.\n */\nimport { lazy, Suspense, useEffect, useId, useRef, useState, type KeyboardEvent } from 'react';\nimport { Dialog } from '../../../components/primitives/Dialog.js';\nimport { useQuery } from '../../../data/hooks.js';\nimport type { VerseSeat } from '../../../data/api-types.js';\nimport type { VerseSeatWindow } from '../../../../core/verse/types.js';\nimport type { BudgetView } from '../../../../core/routing/policy.js';\nimport { budgetQuery } from '../budget/budget-queries.js';\nimport { useSeatHealth } from '../health/useSeatHealth.js';\nimport { capacityRowFor, windowSentence } from '../usage/capacity-strip-model.js';\nimport type { SeatChoice } from '../SeatSelector.js';\nimport { firstRunnableModel, seatCapacity, SEAT_CAPACITY_WORD, seatWindowLabel } from '../verse-model.js';\nimport styles from './composer.module.css';\nimport { usedPercentText } from '../percent-text.js';\nimport { ProviderLogo } from '../../../components/primitives/ProviderLogo.js';\n\nconst BudgetControl = lazy(async () => ({ default: (await import('../budget/BudgetControl.js')).BudgetControl }));\n\nexport interface SeatChipProps {\n seats: readonly VerseSeat[];\n seat: SeatChoice;\n engine: VerseSeat['engine'];\n /** The chat's full seat label (\"Claude Max · Opus 5.5\") — the tooltip and menu heading. */\n label: string;\n /** What the chip itself says (\"Claude Max\", \"Local\"). Default: `label`. */\n name?: string;\n disabled?: boolean;\n /** Collapse to monogram + ring (the 375 footer). */\n compact?: boolean;\n /** \"Continue on ‹seat›\": a handoff prefilled for that seat. Falls back to onNewChat. */\n onContinueOn?: (choice: SeatChoice) => void;\n onNewChat: (choice: SeatChoice) => void;\n}\n\n/**\n * Every window the seat reported: the capacity record's when the server built\n * one, else the older `health.windows` (same ids and honesty rules, fewer\n * fields). Never invented: a seat with neither has no windows.\n */\nexport function seatWindows(seat: VerseSeat | undefined): VerseSeatWindow[] {\n const fromCapacity = seat?.capacity?.windows ?? [];\n if (fromCapacity.length > 0) return fromCapacity;\n return (seat?.health.windows ?? []).map((w) => ({\n id: w.id,\n usedPercent: typeof w.usedPercent === 'number' && Number.isFinite(w.usedPercent) ? w.usedPercent : null,\n resetsAt: w.resetsAt,\n resetDescription: null,\n limitReached: false,\n measured: typeof w.usedPercent === 'number',\n }));\n}\n\n/** The window the ring draws: the 5-hour one when reported, else the fullest one. */\nexport function ringWindow(seat: VerseSeat | undefined): VerseSeatWindow | null {\n const windows = seatWindows(seat).filter((w) => w.usedPercent !== null || w.limitReached);\n const short = windows.find((w) => w.id === 'five_hour');\n if (short) return short;\n if (seat?.capacity?.binding) return seat.capacity.binding;\n return windows.reduce<VerseSeatWindow | null>((top, w) => (top === null || (w.usedPercent ?? 100) > (top.usedPercent ?? 100) ? w : top), null);\n}\n\nfunction ringTone(reading: VerseSeatWindow | null): 'unknown' | 'ok' | 'tight' | 'limit' {\n if (reading?.limitReached) return 'limit';\n if (!reading || reading.usedPercent === null) return 'unknown';\n if (reading.usedPercent >= 100) return 'limit';\n if (reading.usedPercent >= 85) return 'tight';\n return 'ok';\n}\n\n/**\n * Whether the chip draws its capacity ring: a seat with limits and a reading.\n * The ring takes room in the footer (14px + a gap) and arrives with a later\n * roster poll, so the composer folds on this as well as on the words.\n */\nexport function capacityRingShown(seat: VerseSeat | undefined, engine: VerseSeat['engine']): boolean {\n return engine !== 'local' && ringTone(ringWindow(seat)) !== 'unknown';\n}\n\nexport function CapacityRing({ window: reading, size = 14 }: { window: VerseSeatWindow | null; size?: number }) {\n const r = (size - 3) / 2;\n const c = 2 * Math.PI * r;\n // A spent window is a full ring even when the provider gave no percentage.\n const pct = reading?.limitReached ? 100 : reading?.usedPercent ?? null;\n const shown = pct === null ? 0 : Math.max(0, Math.min(100, pct));\n return (\n <svg className={styles.ring} data-tone={ringTone(reading)} width={size} height={size} viewBox={`0 0 ${size} ${size}`} aria-hidden=\"true\">\n <circle className={styles.ringTrack} cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth=\"2\" />\n {pct === null ? null : (\n <circle className={styles.ringFill} cx={size / 2} cy={size / 2} r={r} fill=\"none\" strokeWidth=\"2\"\n strokeDasharray={`${(shown / 100) * c} ${c}`} transform={`rotate(-90 ${size / 2} ${size / 2})`} strokeLinecap=\"round\" />\n )}\n </svg>\n );\n}\n\nfunction windowLine(reading: VerseSeatWindow): string {\n const label = seatWindowLabel(reading.id);\n const used = reading.limitReached ? 'limit reached' : reading.usedPercent === null ? 'no reading' : `${usedPercentText(reading.usedPercent)} used`;\n const reset = reading.resetDescription ?? (reading.resetsAt ? `resets ${new Date(reading.resetsAt).toLocaleString(undefined, { weekday: 'short', hour: 'numeric', minute: '2-digit' })}` : null);\n return `${label}: ${used}${reset ? ` · ${reset}` : ''}`;\n}\n\n/** \"Autonomy may use 60% · 40% reserved for you\", from the budget route's effective policy. */\nfunction budgetSentence(budget: BudgetView | null, seatId: string, status: string): { text: string; muted: boolean } {\n const policy = budget?.effective?.[seatId];\n if (!policy) return { text: status === 'error' ? 'Budget: no reading' : 'Budget: loading…', muted: true };\n if (!policy.enabled) return { text: 'Autonomy never uses this seat — it is all yours.', muted: false };\n const reserve = Math.max(0, Math.min(100, Math.round(policy.reservePercent)));\n return { text: `Autonomy may use ${100 - reserve}% · ${reserve}% reserved for you`, muted: false };\n}\n\n/**\n * The detail bubble. Its facts come from THE shared capacity row\n * (usage/capacity-strip-model `capacityRowFor`, C6) — the same projection\n * Apps & Accounts, Usage and the new-chat dialog draw — so the chip can never\n * describe a seat differently from them. Health (A2) and budget (A9) are the\n * app-wide cached reads, fetched only while this bubble is open.\n */\nfunction SeatTooltip({ seats, seat, label }: { seats: readonly VerseSeat[]; seat: VerseSeat | undefined; label: string }) {\n const budget = useQuery(budgetQuery);\n const health = useSeatHealth();\n const row = seat ? capacityRowFor(seats, seat.id, { health: health.data?.seats ?? null, budget: budget.data ?? null }) : null;\n // The shared row's windows (label, %, verbatim reset, the reserve on the\n // binding one); before the roster has a row, the seat record's own.\n const windows: Array<{ id: string; line: string }> = row\n ? row.windows.map((w) => ({ id: w.id, line: windowSentence({ label: '' }, w, w.binding ? row.reserve?.percent ?? null : null).trimStart() }))\n : seatWindows(seat).map((w) => ({ id: w.id, line: windowLine(w) }));\n const budgetLine = seat && seat.engine !== 'local' ? budgetSentence(budget.data ?? null, seat.id, budget.status) : null;\n return (\n <span className={styles.tip}>\n <strong>{label}</strong>\n {row?.plan ?? seat?.capacity?.planType ? <span>Plan: {row?.plan ?? seat?.capacity?.planType}</span> : null}\n {windows.length > 0\n ? windows.map((w) => <span key={w.id}>{w.line}</span>)\n : <span className={styles.tipMuted}>No capacity reading yet</span>}\n {row ? (\n <span>\n Health: {row.word}\n {row.connection && row.connection.connection !== 'connected' && row.connection.connection !== 'unknown' ? ` · ${row.connection.word}` : ''}\n {row.summary ? ` — ${row.summary}` : ''}\n </span>\n ) : null}\n {budgetLine ? <span className={budgetLine.muted ? styles.tipMuted : undefined}>{budgetLine.text}</span> : null}\n {seat && seat.engine === 'local' ? <span>Local — no usage limits</span> : null}\n </span>\n );\n}\n\nexport function SeatChip({ seats, seat, engine, label, name = label, disabled = false, compact = false, onContinueOn, onNewChat }: SeatChipProps) {\n const [open, setOpen] = useState(false);\n const [budgetOpen, setBudgetOpen] = useState(false);\n // The detail bubble is this component's own (not the Tooltip primitive):\n // it must hide while the menu is open, and toggling the primitive's\n // `disabled` swaps its wrapper and remounts the button under the pointer.\n const [tipOpen, setTipOpen] = useState(false);\n const tipTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const tipId = useId();\n const wrap = useRef<HTMLDivElement>(null);\n const button = useRef<HTMLButtonElement>(null);\n const menuId = useId();\n const budgetTitleId = useId();\n const current = seats.find((s) => s.id === seat.seatId);\n const ring = engine === 'local' ? null : ringWindow(current);\n const others = seats.filter((s) => s.id !== seat.seatId && s.health.state !== 'unavailable' && firstRunnableModel(s) !== null);\n const ringText = ring && (ring.usedPercent !== null || ring.limitReached)\n ? `${seatWindowLabel(ring.id)} ${ring.limitReached ? 'limit reached' : `${usedPercentText(ring.usedPercent ?? 0)} used`}`\n : engine === 'local' ? 'no usage limits' : 'no capacity reading';\n\n useEffect(() => () => { if (tipTimer.current) clearTimeout(tipTimer.current); }, []);\n\n function showTip(delayed: boolean) {\n if (tipTimer.current) clearTimeout(tipTimer.current);\n if (!delayed) {\n setTipOpen(true);\n return;\n }\n tipTimer.current = setTimeout(() => setTipOpen(true), 200);\n }\n\n function hideTip() {\n if (tipTimer.current) clearTimeout(tipTimer.current);\n tipTimer.current = null;\n setTipOpen(false);\n }\n\n useEffect(() => {\n if (!open) return undefined;\n const onPointerDown = (event: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(event.target as Node)) setOpen(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n wrap.current?.querySelector<HTMLElement>('[role=\"menuitem\"]')?.focus();\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [open]);\n\n function onMenuKey(event: KeyboardEvent<HTMLDivElement>) {\n const items = Array.from(wrap.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []);\n const index = items.indexOf(document.activeElement as HTMLElement);\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n setOpen(false);\n button.current?.focus();\n } else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n const next = event.key === 'ArrowDown' ? index + 1 : index - 1;\n items[(next + items.length) % items.length]?.focus();\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n (event.key === 'Home' ? items[0] : items[items.length - 1])?.focus();\n } else if (event.key === 'Tab') {\n setOpen(false);\n }\n }\n\n function pick(action: () => void) {\n setOpen(false);\n action();\n }\n\n return (\n <div ref={wrap} className={`${styles.seatChipWrap} ${styles[`engine-${engine}`] ?? ''}`}>\n <button ref={button} type=\"button\" className={`${styles.seatChip} ${compact ? styles.seatChipCompact : ''}`}\n aria-haspopup=\"menu\" aria-expanded={open} aria-controls={open ? menuId : undefined} disabled={disabled}\n aria-label={`Seat: ${name}, ${ringText}`}\n aria-describedby={tipOpen && !open ? tipId : undefined}\n onMouseEnter={() => showTip(true)} onMouseLeave={hideTip}\n onFocus={(event) => { if (event.currentTarget.matches(':focus-visible')) showTip(false); }} onBlur={hideTip}\n onKeyDown={(event) => { if (event.key === 'Escape' && tipOpen && !open) { event.stopPropagation(); hideTip(); } }}\n onClick={() => { hideTip(); setOpen((v) => !v); }}>\n <span className={styles.monogram} aria-hidden=\"true\"><ProviderLogo engine={engine} size={13} /></span>\n {compact ? null : <span className={styles.seatChipText}>{name}</span>}\n {capacityRingShown(current, engine) ? <CapacityRing window={ring} /> : null}\n </button>\n {tipOpen && !open ? (\n <span id={tipId} role=\"tooltip\" className={styles.seatTip}>\n <SeatTooltip seats={seats} seat={current} label={label} />\n </span>\n ) : null}\n {open ? (\n <div id={menuId} role=\"menu\" aria-label=\"Seat\" className={`${styles.menu} ${styles.menuWide}`} onKeyDown={onMenuKey}>\n <p className={styles.menuHeading}><span>This chat runs on <strong>{label}</strong></span></p>\n {others.map((s) => {\n const model = firstRunnableModel(s)!;\n const cap = seatCapacity(s);\n return (\n <button key={s.id} type=\"button\" role=\"menuitem\" className={`${styles.menuItem} ${styles[`engine-${s.engine}`] ?? ''}`}\n data-capacity={cap.cls}\n onClick={() => pick(() => (onContinueOn ?? onNewChat)({ seatId: s.id, model: model.id }))}>\n <span className={styles.monogram} aria-hidden=\"true\"><ProviderLogo engine={s.engine} size={13} /></span>\n <span className={styles.menuText}>\n <span className={styles.menuLabel}>{onContinueOn ? 'Continue on' : 'New chat on'} {s.label}</span>\n <span className={styles.menuDesc}>{model.label}{cap.cls === 'unread' ? '' : ` · ${SEAT_CAPACITY_WORD[cap.cls]}, ${cap.text}`}</span>\n </span>\n </button>\n );\n })}\n {others.length === 0 ? <p className={styles.menuNote}>No other seat can take this chat right now.</p> : null}\n <div className={styles.menuSeparator} role=\"separator\" />\n <button type=\"button\" role=\"menuitem\" className={styles.menuItem}\n onClick={() => pick(() => onNewChat({ seatId: seat.seatId, model: seat.model }))}>\n <span className={styles.menuCheck} aria-hidden=\"true\" />\n <span className={styles.menuText}><span className={styles.menuLabel}>New chat on this seat</span></span>\n </button>\n {engine !== 'local' ? (\n <button type=\"button\" role=\"menuitem\" className={styles.menuItem} onClick={() => pick(() => setBudgetOpen(true))}>\n <span className={styles.menuCheck} aria-hidden=\"true\" />\n <span className={styles.menuText}>\n <span className={styles.menuLabel}>Budget mode…</span>\n <span className={styles.menuDesc}>How much of this seat autonomy may use</span>\n </span>\n </button>\n ) : null}\n </div>\n ) : null}\n <Dialog open={budgetOpen} onClose={() => setBudgetOpen(false)} titleId={budgetTitleId} title=\"Budget mode\" widthClassName={styles.budgetDialog}\n description=\"What autonomy may spend on each seat, and what stays reserved for your own chats.\">\n {budgetOpen ? (\n <Suspense fallback={<p className={styles.menuNote}>Loading budget…</p>}>\n <BudgetControl />\n </Suspense>\n ) : null}\n </Dialog>\n </div>\n );\n}\n","/**\n * routes/verse/composer/SuggestMenu.tsx — the listbox the `@` file finder and\n * the `/` commands open above the message box (unit C3).\n *\n * Focus NEVER leaves the textarea: it owns the keys (↑/↓ move, Enter/Tab\n * accept, Esc closes — see Composer's onKeyDown) and points at the active\n * option with `aria-activedescendant`, the combobox pattern, so typing keeps\n * narrowing the list. A pointer can pick too; mousedown is prevented so the\n * click does not blur the textarea first.\n */\nimport type { ReactNode } from 'react';\nimport styles from './composer.module.css';\n\nexport interface SuggestItem {\n id: string;\n primary: ReactNode;\n secondary?: ReactNode;\n /** Shown disabled with this reason (a command this chat cannot run). */\n disabledReason?: string | null;\n}\n\nexport interface SuggestMenuProps {\n id: string;\n label: string;\n items: readonly SuggestItem[];\n activeIndex: number;\n /** Shown instead of the list: \"Searching…\", \"No files match\". */\n status?: string | null;\n onPick: (index: number) => void;\n onHover: (index: number) => void;\n}\n\nexport function suggestOptionId(menuId: string, index: number): string {\n return `${menuId}-opt-${index}`;\n}\n\nexport function SuggestMenu({ id, label, items, activeIndex, status = null, onPick, onHover }: SuggestMenuProps) {\n return (\n <div className={styles.suggest}>\n <p className={styles.suggestHeading}>{label}</p>\n {items.length === 0 ? (\n <p className={styles.suggestStatus} role=\"status\">{status ?? 'Nothing matches'}</p>\n ) : (\n <ul id={id} role=\"listbox\" aria-label={label} className={styles.suggestList}>\n {items.map((item, index) => (\n <li key={item.id} id={suggestOptionId(id, index)} role=\"option\" aria-selected={index === activeIndex}\n aria-disabled={item.disabledReason ? true : undefined}\n className={styles.suggestItem}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => onHover(index)}\n onClick={() => onPick(index)}>\n <span className={styles.suggestPrimary}>{item.primary}</span>\n {item.disabledReason ? <span className={styles.suggestSecondary}>{item.disabledReason}</span>\n : item.secondary ? <span className={styles.suggestSecondary}>{item.secondary}</span> : null}\n </li>\n ))}\n </ul>\n )}\n {items.length > 0 && status ? <p className={styles.suggestStatus} role=\"status\">{status}</p> : null}\n </div>\n );\n}\n","/**\n * routes/verse/composer/useComposerData.ts — the composer's three live\n * resources, one hook each (unit C3):\n *\n * useSessionControls model / effort / permission for this chat + options\n * useFollowUpQueue the server-side queue of follow-ups\n * useAttachmentDrafts files attached to the message being written\n *\n * None of them polls. Controls and the queue are re-read when the chat's\n * turn starts or ends (`running` flips) — the only moments the server changes\n * them on its own — and after each of the composer's own writes. A chat that\n * is not open costs nothing.\n *\n * Every write goes through the caller's token gate (`run`): with no mutation\n * token held, the gate asks for one and resolves null if dismissed, so a\n * refused unlock reads as \"not done\", never as an error.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type {\n VerseAttachment,\n VerseQueueResponse,\n VerseSessionControlsResponse,\n VerseSessionControlsUpdate,\n} from '../../../../core/verse/workbench-types.js';\nimport { VERSE_ATTACHMENT_MAX_BYTES } from '../../../../core/verse/workbench-types.js';\nimport { describeContextError } from '../context/use-token-gate.js';\nimport {\n deleteAttachment,\n enqueueFollowUp,\n fetchQueue,\n fetchSessionControls,\n removeFollowUp,\n sendFollowUpNow,\n updateSessionControls,\n uploadAttachment,\n type VerseQueueSendResult,\n} from './composer-queries.js';\nimport { formatBytes, readFileAsBase64 } from './composer-text.js';\n\nexport type GateRun = <T>(reason: string, action: () => Promise<T>) => Promise<T | null>;\n\n// ---------------------------------------------------------------------------\n// Controls\n// ---------------------------------------------------------------------------\n\nexport interface SessionControlsState {\n view: VerseSessionControlsResponse | null;\n /** True until the first read answered (or failed). */\n loading: boolean;\n /** Why the last read or write failed, in the server's words. */\n error: string | null;\n pending: boolean;\n update: (update: VerseSessionControlsUpdate, reason: string) => Promise<boolean>;\n}\n\nexport function useSessionControls(sessionId: string | null, running: boolean, run: GateRun): SessionControlsState {\n const [view, setView] = useState<VerseSessionControlsResponse | null>(null);\n const [loading, setLoading] = useState(sessionId !== null);\n const [error, setError] = useState<string | null>(null);\n const [pending, setPending] = useState(false);\n\n useEffect(() => {\n if (!sessionId) {\n setView(null);\n setLoading(false);\n return undefined;\n }\n const abort = new AbortController();\n fetchSessionControls(sessionId, abort.signal)\n .then((next) => { setView(next); setError(null); })\n .catch((err: unknown) => {\n if (abort.signal.aborted) return;\n // An older server (no route) leaves the pickers hidden rather than broken.\n setView(null);\n setError(describeContextError(err));\n })\n .finally(() => { if (!abort.signal.aborted) setLoading(false); });\n return () => abort.abort();\n // `running` is a dependency on purpose: `appliesNextTurn` flips with it.\n }, [sessionId, running]);\n\n const update = useCallback(async (change: VerseSessionControlsUpdate, reason: string): Promise<boolean> => {\n if (!sessionId) return false;\n setPending(true);\n setError(null);\n try {\n const next = await run(reason, () => updateSessionControls(sessionId, change));\n if (!next) return false;\n setView(next);\n return true;\n } catch (err) {\n setError(describeContextError(err));\n return false;\n } finally {\n setPending(false);\n }\n }, [sessionId, run]);\n\n return { view, loading, error, pending, update };\n}\n\n// ---------------------------------------------------------------------------\n// Queue\n// ---------------------------------------------------------------------------\n\n/**\n * The queue as the composer may trust it. The route is C3's and additive, so\n * a server mid-upgrade (or a stub answering `{}`) can send a body without\n * `items`; reading `queue.items.length` off that crashed the whole composer.\n * Anything malformed reads as \"no queue\" (the 3.9 behaviour), a partial body\n * keeps only well-formed items, and `held` is true only when it says so.\n */\nexport function normalizeQueue<T extends VerseQueueResponse>(raw: unknown): T | null {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return null;\n const body = raw as Partial<Record<keyof VerseQueueResponse | 'sentTurnId', unknown>>;\n if (!Array.isArray(body.items)) return null;\n const items = body.items.filter((item): item is VerseQueueResponse['items'][number] => {\n if (item === null || typeof item !== 'object') return false;\n const i = item as Record<string, unknown>;\n return typeof i['id'] === 'string' && typeof i['text'] === 'string';\n });\n return {\n ...(body as object),\n sessionId: typeof body.sessionId === 'string' ? body.sessionId : '',\n items,\n held: body.held === true,\n heldReason: typeof body.heldReason === 'string' ? body.heldReason : null,\n } as T;\n}\n\nexport interface FollowUpQueueState {\n queue: VerseQueueResponse | null;\n error: string | null;\n /** Queue (or, when nothing runs, send) a follow-up. Null when not done. */\n enqueue: (text: string, opts?: { sendNow?: boolean }) => Promise<VerseQueueSendResult | null>;\n remove: (queueId: string) => Promise<boolean>;\n sendNow: (queueId: string) => Promise<boolean>;\n refresh: () => void;\n clearError: () => void;\n}\n\nexport function useFollowUpQueue(sessionId: string | null, running: boolean, run: GateRun): FollowUpQueueState {\n const [queue, setQueue] = useState<VerseQueueResponse | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [nonce, setNonce] = useState(0);\n\n useEffect(() => {\n if (!sessionId) {\n setQueue(null);\n return undefined;\n }\n const abort = new AbortController();\n fetchQueue(sessionId, abort.signal)\n .then((next) => { if (!abort.signal.aborted) setQueue(normalizeQueue(next)); })\n // No route (older server) = no queue; the composer then behaves as 3.9.\n .catch(() => { if (!abort.signal.aborted) setQueue(null); });\n return () => abort.abort();\n }, [sessionId, running, nonce]);\n\n const write = useCallback(async <T extends VerseQueueResponse>(reason: string, action: () => Promise<T>): Promise<T | null> => {\n setError(null);\n try {\n const raw = await run(reason, action);\n if (raw === null) return null;\n // A write that answered with no readable queue still happened: keep\n // the queue we had rather than blanking it, and report it done.\n const result = normalizeQueue<T>(raw);\n if (result) setQueue(result);\n return result ?? raw;\n } catch (err) {\n setError(describeContextError(err));\n return null;\n }\n }, [run]);\n\n const enqueue = useCallback((text: string, opts: { sendNow?: boolean } = {}) => {\n if (!sessionId) return Promise.resolve(null);\n return write(opts.sendNow ? 'Stop the running turn and send this next.' : 'Queue this message to send when the turn ends.',\n () => enqueueFollowUp(sessionId, text, opts));\n }, [sessionId, write]);\n\n const remove = useCallback(async (queueId: string) => {\n if (!sessionId) return false;\n return (await write('Remove this queued message.', () => removeFollowUp(sessionId, queueId))) !== null;\n }, [sessionId, write]);\n\n const sendNow = useCallback(async (queueId: string) => {\n if (!sessionId) return false;\n return (await write('Send this queued message now.', () => sendFollowUpNow(sessionId, queueId))) !== null;\n }, [sessionId, write]);\n\n const refresh = useCallback(() => setNonce((n) => n + 1), []);\n const clearError = useCallback(() => setError(null), []);\n\n return { queue, error, enqueue, remove, sendNow, refresh, clearError };\n}\n\n// ---------------------------------------------------------------------------\n// Attachments\n// ---------------------------------------------------------------------------\n\nexport interface AttachmentDraft {\n /** Client id (stable across the upload). */\n key: string;\n name: string;\n mime: string;\n bytes: number;\n status: 'uploading' | 'ready' | 'error';\n /** Object URL for an image thumbnail; revoked when the chip goes. */\n previewUrl: string | null;\n error: string | null;\n attachment: VerseAttachment | null;\n}\n\nexport interface AttachmentDraftsState {\n drafts: AttachmentDraft[];\n /** Upload files; `onReady` fires once per file with its `@path` token. */\n add: (files: readonly File[], onReady: (ref: string) => void) => void;\n remove: (key: string) => Promise<string | null>;\n /** Forget the chips (after a send) without deleting the files, which the sent message names. */\n reset: () => void;\n uploading: boolean;\n}\n\nlet draftCounter = 0;\n\nexport function useAttachmentDrafts(sessionId: string | null, run: GateRun): AttachmentDraftsState {\n const [drafts, setDrafts] = useState<AttachmentDraft[]>([]);\n const urls = useRef(new Set<string>());\n\n // Every object URL is released with the component (a chat switch unmounts it).\n useEffect(() => () => {\n for (const url of urls.current) URL.revokeObjectURL(url);\n urls.current.clear();\n }, []);\n\n const patch = useCallback((key: string, change: Partial<AttachmentDraft>) => {\n setDrafts((list) => list.map((d) => (d.key === key ? { ...d, ...change } : d)));\n }, []);\n\n const add = useCallback((files: readonly File[], onReady: (ref: string) => void) => {\n if (!sessionId) return;\n for (const file of files) {\n draftCounter += 1;\n const key = `att-${draftCounter}`;\n const image = file.type.startsWith('image/');\n let previewUrl: string | null = null;\n if (image && typeof URL.createObjectURL === 'function') {\n previewUrl = URL.createObjectURL(file);\n urls.current.add(previewUrl);\n }\n const name = file.name || (image ? 'pasted-image.png' : 'attachment');\n const mime = file.type || 'application/octet-stream';\n const draft: AttachmentDraft = { key, name, mime, bytes: file.size, status: 'uploading', previewUrl, error: null, attachment: null };\n setDrafts((list) => [...list, draft]);\n if (file.size > VERSE_ATTACHMENT_MAX_BYTES) {\n patch(key, { status: 'error', error: `${formatBytes(file.size)} — each file must be ${formatBytes(VERSE_ATTACHMENT_MAX_BYTES)} or smaller` });\n continue;\n }\n if (file.size === 0) {\n patch(key, { status: 'error', error: 'The file is empty.' });\n continue;\n }\n void (async () => {\n try {\n const dataBase64 = await readFileAsBase64(file);\n const saved = await run('Attach this file to the chat.', () => uploadAttachment(sessionId, { name, mime, dataBase64 }));\n if (!saved) {\n patch(key, { status: 'error', error: 'Not attached — the chat is locked.' });\n return;\n }\n patch(key, { status: 'ready', attachment: saved, name: saved.name });\n onReady(saved.ref);\n } catch (err) {\n patch(key, { status: 'error', error: describeContextError(err) });\n }\n })();\n }\n }, [sessionId, run, patch]);\n\n const remove = useCallback(async (key: string): Promise<string | null> => {\n const draft = drafts.find((d) => d.key === key);\n if (!draft) return null;\n setDrafts((list) => list.filter((d) => d.key !== key));\n if (draft.previewUrl) {\n URL.revokeObjectURL(draft.previewUrl);\n urls.current.delete(draft.previewUrl);\n }\n if (draft.attachment && sessionId) {\n const attachment = draft.attachment;\n try {\n await run('Remove this attachment.', () => deleteAttachment(sessionId, attachment.id));\n } catch {\n // Already gone on the server: the chip's removal is what matters.\n }\n return attachment.ref;\n }\n return null;\n }, [drafts, sessionId, run]);\n\n const reset = useCallback(() => {\n setDrafts((list) => {\n for (const d of list) {\n if (d.previewUrl) {\n URL.revokeObjectURL(d.previewUrl);\n urls.current.delete(d.previewUrl);\n }\n }\n return [];\n });\n }, []);\n\n return { drafts, add, remove, reset, uploading: drafts.some((d) => d.status === 'uploading') };\n}\n","/**\n * routes/verse/composer/useFooterFold.ts — how far the composer footer folds\n * to fit its one row WITHOUT truncating a label.\n *\n * 0 every control in words\n * 1 \"Effort:\" becomes an icon (\"ıl High\")\n * 2 the seat chip drops to its monogram (+ capacity ring)\n * 3 the permission mode drops to its icon\n * 4 the pickers move into the ⋯ sheet (the phone footer)\n *\n * Measured, not a breakpoint: the labels vary too much (\"Local\" vs \"Personal\n * Codex\", \"Opus 5\" vs \"Qwen3.8 27b-ctx64k\"), so any fixed width either folds\n * a footer that fit or lets one overflow. Every folded control keeps its full\n * words as its accessible name and tooltip.\n *\n * WHEN IT MEASURES (3.10.1 review):\n * - On mount, synchronously, in a layout effect — never waiting for the\n * first ResizeObserver delivery, which lands after the browser has\n * painted. Composer is keyed by chat, so this runs on every chat switch;\n * waiting would flash the unfolded row on each one.\n * - When the WORDS change (the caller's `labels` key, or a web font\n * landing): the widths measured for the old words say nothing about the\n * new ones, so it starts again from fold 0 and climbs, one step per\n * synchronous layout effect, all before paint.\n * - When only the ROOM changes (ResizeObserver): no reset. Narrower → keep\n * climbing from the current fold. Wider → step down, in one render, onto\n * the folds already measured too wide for the old room but no wider than\n * the new one. A width that changes the fold by nothing (every frame of a\n * sidebar drag) renders nothing at all.\n * - Never while `hold` is set (the ⋯ sheet or a picker's menu is open): a\n * re-measure can unmount the very control the overlay belongs to and\n * strand focus. Whatever changed is measured once it closes.\n *\n * Typing never re-measures (it is not in the key), so nothing moves under the\n * caret. Without layout (jsdom, a hidden panel) the width is 0 and it never\n * folds until the footer gets a size.\n */\nimport { useEffect, useLayoutEffect, useRef, useState, type RefObject } from 'react';\nimport { flushSync } from 'react-dom';\n\n/** The fold at which the pickers leave the row for the ⋯ sheet. */\nexport const FOOTER_FOLD_SHEET = 4;\n\nexport interface FooterFoldOptions {\n /** An overlay on the footer is open: measure nothing until it closes. */\n hold?: boolean;\n /**\n * Called from the layout effect each time the fold comes to rest, with the\n * element that had focus inside the footer when this move began (null when\n * focus was elsewhere or nothing moved). A fold can unmount that control\n * (the pickers leave for ⋯, ⋯ leaves for the pickers, or the re-measure\n * after new words briefly passes through fold 0) — the owner puts focus on\n * its replacement.\n */\n onSettle?: (fold: number, focusBefore: Element | null) => void;\n}\n\ninterface FoldMemo {\n /** The words the cached widths belong to; null until the first measurement. */\n key: string | null;\n /** The words on screen now — ahead of `key` while the footer had no size to measure them at. */\n latest: string;\n /** scrollWidth of every fold that overflowed under `key` — its true content width. */\n widths: Array<number | undefined>;\n /** The footer width (clientWidth) the current fold was chosen for. */\n width: number;\n /** The committed fold, for the ResizeObserver callback. */\n fold: number;\n hold: boolean;\n /** A move is under way; `focusBefore` is what had focus in the footer when it began. */\n moving: boolean;\n focusBefore: Element | null;\n}\n\n/** Remember what had focus in the footer when a move starts (once per move). */\nfunction beginMove(s: FoldMemo, node: HTMLElement): void {\n if (s.moving) return;\n s.moving = true;\n const active = typeof document === 'undefined' ? null : document.activeElement;\n s.focusBefore = active && node.contains(active) ? active : null;\n}\n\n/** The lowest fold at or below `fold` whose measured width fits `width` — walking down only across measured folds. */\nfunction unfoldTo(widths: FoldMemo['widths'], fold: number, width: number): number {\n let next = fold;\n while (next > 0) {\n const below = widths[next - 1];\n if (below === undefined || below > width) break;\n next -= 1;\n }\n return next;\n}\n\nexport function useFooterFold(footer: RefObject<HTMLElement | null>, labels: string, options: FooterFoldOptions = {}): number {\n const { hold = false, onSettle } = options;\n const [fold, setFold] = useState(0);\n // Bumped to take a first look at a footer that mounted with no size.\n const [look, setLook] = useState(0);\n const [fontEpoch, setFontEpoch] = useState(0);\n const memo = useRef<FoldMemo>({ key: null, latest: '', widths: [], width: 0, fold: 0, hold: false, moving: false, focusBefore: null });\n const settle = useRef(onSettle);\n settle.current = onSettle;\n const key = `${fontEpoch}|${labels}`;\n\n useLayoutEffect(() => {\n const s = memo.current;\n s.fold = fold;\n s.hold = hold;\n s.latest = key;\n const node = footer.current;\n const width = node?.clientWidth ?? 0;\n if (node && !hold && width > 0) {\n if (s.key !== key) {\n // New words: start again from every label in words.\n s.key = key;\n s.widths = [];\n s.width = width;\n if (fold !== 0) {\n beginMove(s, node);\n setFold(0);\n return;\n }\n } else if (width !== s.width) {\n // Only the room changed while held (or before the first look): no reset.\n s.width = width;\n const lower = unfoldTo(s.widths, fold, width);\n if (lower !== fold) {\n beginMove(s, node);\n setFold(lower);\n return;\n }\n }\n // Strict: scrollWidth rounds, so even a sub-pixel overflow folds — one step early beats a clipped Send.\n const content = node.scrollWidth;\n if (content > width) {\n s.widths[fold] = content;\n if (fold < FOOTER_FOLD_SHEET) {\n beginMove(s, node);\n setFold(fold + 1);\n return;\n }\n }\n }\n const focusBefore = s.focusBefore;\n s.moving = false;\n s.focusBefore = null;\n settle.current?.(fold, focusBefore);\n }, [footer, key, hold, fold, look]);\n\n useEffect(() => {\n const node = footer.current;\n if (!node || typeof ResizeObserver === 'undefined') return undefined;\n const observer = new ResizeObserver(() => {\n const s = memo.current;\n const width = node.clientWidth;\n // Held: the layout effect measures the new room when the overlay closes.\n if (s.hold || width === 0) return;\n // flushSync: this callback runs after layout and before paint, so the\n // new fold is drawn in the same frame instead of one frame late.\n if (s.key !== s.latest) {\n // Never measured, or the words changed while the footer had no size\n // (a hidden panel): the layout effect takes a full look now.\n flushSync(() => setLook((n) => n + 1));\n return;\n }\n if (width === s.width) return;\n s.width = width;\n let next = unfoldTo(s.widths, s.fold, width);\n if (next === s.fold && node.scrollWidth > width) {\n s.widths[s.fold] = node.scrollWidth;\n next = Math.min(s.fold + 1, FOOTER_FOLD_SHEET);\n }\n if (next === s.fold) return;\n beginMove(s, node);\n flushSync(() => setFold(next));\n });\n observer.observe(node);\n return () => observer.disconnect();\n }, [footer]);\n\n // A web font landing changes every label's width without changing a word.\n useEffect(() => {\n const fonts = typeof document === 'undefined' ? undefined : (document as Document & { fonts?: FontFaceSet }).fonts;\n if (!fonts || typeof fonts.addEventListener !== 'function') return undefined;\n const onLoaded = () => setFontEpoch((n) => n + 1);\n fonts.addEventListener('loadingdone', onLoaded);\n return () => fonts.removeEventListener('loadingdone', onLoaded);\n }, []);\n\n return fold;\n}\n","/**\n * routes/verse/DictationButton.tsx — push-to-talk for the composer via the\n * Web Speech API (`SpeechRecognition` / `webkitSpeechRecognition`). Interim\n * results stream into the textarea through `onInterim`; final segments are\n * committed through `onFinal`. Escape stops listening. When the browser has\n * no recognizer (Firefox, Tauri/WKWebView today) the button is disabled and\n * a tooltip points at Wispr Flow / Superwhisper — system-level dictation\n * types straight into the textarea, so nothing is lost.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport styles from './Composer.module.css';\n\nexport const DICTATION_FALLBACK_HINT = 'Use Wispr Flow / Superwhisper — system dictation works in this box';\n\ninterface RecognitionResultLike {\n isFinal: boolean;\n 0: { transcript: string };\n length: number;\n}\ninterface RecognitionEventLike {\n resultIndex: number;\n results: ArrayLike<RecognitionResultLike>;\n}\ninterface RecognitionLike {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n onresult: ((event: RecognitionEventLike) => void) | null;\n onend: (() => void) | null;\n onerror: ((event: { error?: string }) => void) | null;\n start(): void;\n stop(): void;\n abort(): void;\n}\ntype RecognitionCtor = new () => RecognitionLike;\n\n/** Resolve the recognizer constructor, if this runtime ships one. */\nexport function getSpeechRecognition(): RecognitionCtor | null {\n const w = globalThis as unknown as { SpeechRecognition?: RecognitionCtor; webkitSpeechRecognition?: RecognitionCtor };\n return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;\n}\n\nexport interface DictationButtonProps {\n disabled?: boolean;\n onInterim: (text: string) => void;\n onFinal: (text: string) => void;\n onListeningChange?: (listening: boolean) => void;\n}\n\nexport function DictationButton({ disabled = false, onInterim, onFinal, onListeningChange }: DictationButtonProps) {\n const Recognizer = getSpeechRecognition();\n const [listening, setListening] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const recognizer = useRef<RecognitionLike | null>(null);\n const callbacks = useRef({ onInterim, onFinal, onListeningChange });\n callbacks.current = { onInterim, onFinal, onListeningChange };\n\n const stop = useCallback(() => {\n const active = recognizer.current;\n recognizer.current = null;\n if (active) {\n active.onresult = null;\n active.onend = null;\n active.onerror = null;\n try {\n active.stop();\n } catch {\n /* already stopped */\n }\n }\n setListening(false);\n callbacks.current.onInterim('');\n callbacks.current.onListeningChange?.(false);\n }, []);\n\n const start = useCallback(() => {\n if (!Recognizer || recognizer.current) return;\n let instance: RecognitionLike;\n try {\n instance = new Recognizer();\n } catch {\n setError('Dictation could not start in this browser.');\n return;\n }\n instance.lang = navigator.language || 'en-US';\n instance.continuous = true;\n instance.interimResults = true;\n instance.onresult = (event) => {\n let interim = '';\n let finalText = '';\n for (let i = event.resultIndex; i < event.results.length; i += 1) {\n const result = event.results[i];\n if (!result) continue;\n const transcript = result[0]?.transcript ?? '';\n if (result.isFinal) finalText += transcript;\n else interim += transcript;\n }\n if (finalText) callbacks.current.onFinal(finalText.trim());\n callbacks.current.onInterim(interim);\n };\n instance.onerror = (event) => {\n const code = event.error ?? 'unknown';\n if (code === 'not-allowed' || code === 'service-not-allowed') setError('Microphone access was denied.');\n else if (code !== 'aborted' && code !== 'no-speech') setError(`Dictation stopped (${code}).`);\n stop();\n };\n instance.onend = () => {\n if (recognizer.current === instance) stop();\n };\n recognizer.current = instance;\n setError(null);\n try {\n instance.start();\n } catch {\n recognizer.current = null;\n setError('Dictation could not start in this browser.');\n return;\n }\n setListening(true);\n callbacks.current.onListeningChange?.(true);\n }, [Recognizer, stop]);\n\n // Escape stops dictation from anywhere on the page; unmount stops it too.\n useEffect(() => {\n if (!listening) return;\n function onKey(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault();\n stop();\n }\n }\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [listening, stop]);\n useEffect(() => () => {\n const active = recognizer.current;\n recognizer.current = null;\n try {\n active?.abort();\n } catch {\n /* ignore */\n }\n }, []);\n\n if (!Recognizer) {\n return (\n <span className={styles.dictationWrap}>\n {/* This button previously carried the hint THREE times over: a native\n `title`, an aria-describedby, and a hand-rolled `role=\"tooltip\"`\n span that lived in the DOM permanently at opacity 0 and was\n revealed by a :hover rule on the wrapper. That span is the grey\n slab that overhangs the panel beside the composer. One Tooltip\n replaces all three and is mounted only while it is open. */}\n <Tooltip label={DICTATION_FALLBACK_HINT} placement=\"top\">\n <button type=\"button\" className={styles.dictation} disabled aria-disabled=\"true\">\n <MicIcon />\n <span className=\"visually-hidden\">Dictation unavailable</span>\n </button>\n </Tooltip>\n </span>\n );\n }\n\n return (\n <span className={styles.dictationWrap}>\n {/* Icon-only, so the aria-label stays put: the tooltip describes, it\n does not name. */}\n <Tooltip label={listening ? 'Stop dictation' : 'Dictate'} shortcut={listening ? 'Esc' : undefined} placement=\"top\">\n <button type=\"button\" className={`${styles.dictation} ${listening ? styles.dictationLive : ''}`}\n disabled={disabled && !listening} aria-pressed={listening}\n aria-label={listening ? 'Stop dictation (Esc)' : 'Start dictation'}\n onClick={() => (listening ? stop() : start())}>\n <MicIcon />\n {listening ? <span className={styles.dictationPulse} aria-hidden=\"true\" /> : null}\n </button>\n </Tooltip>\n {error ? <span role=\"alert\" className={styles.dictationError}>{error}</span> : null}\n </span>\n );\n}\n\nfunction MicIcon() {\n return (\n <svg viewBox=\"0 0 20 20\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"7\" y=\"2\" width=\"6\" height=\"10\" rx=\"3\" fill=\"currentColor\" />\n <path d=\"M4.5 9.5a5.5 5.5 0 0 0 11 0M10 15v3M7 18h6\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n </svg>\n );\n}\n","/**\n * routes/verse/health/ComposerSeatBlock.tsx — the Composer's \"this seat can't\n * run a turn\" block (V3.10, unit A2; mounted by the Composer's owner, A6).\n *\n * The session engine refuses a turn on a signed-out or exhausted seat with a\n * 409 (`getSeatReadiness`, core/verse/seats.ts). This block says so BEFORE\n * the operator types a message into a dead end: the reason, the reset time\n * when there is one, Reconnect for a signed-out seat, and the ranked\n * alternatives — computed by the SAME function the engine uses\n * (core/verse/seat-readiness.ts), so the block never offers a seat the server\n * would then refuse.\n *\n * Sessions are seat-bound, so an alternative starts a NEW chat on that seat —\n * the same action as the Composer seat menu's \"New chat on …\" (`onSeatChange`).\n *\n * Renders nothing while the seat is ready.\n */\nimport { useState } from 'react';\nimport type { SeatHealthReport } from '../../../../core/verse/health-types.js';\nimport { rankSeatAlternatives, seatBlock } from '../../../../core/verse/seat-readiness.js';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { IconExternalLink } from '../../../components/primitives/icons.js';\nimport type { VerseSeat } from '../../../data/api-types.js';\nimport type { SeatChoice } from '../SeatSelector.js';\nimport { firstRunnableModel } from '../verse-model.js';\nimport { reconnectSeat } from './health-queries.js';\nimport { useSeatHealth } from './useSeatHealth.js';\nimport styles from './SeatHealth.module.css';\n\n/** At most this many alternatives are offered inline. */\nconst INLINE_ALTERNATIVES = 3;\n\nexport interface ComposerSeatBlockViewProps {\n seats: readonly VerseSeat[];\n /** The seat this chat is bound to. */\n seatId: string;\n reports?: readonly SeatHealthReport[] | null;\n /** Start a new chat on another seat (Composer's `onSeatChange`). */\n onSeatChange: (choice: SeatChoice) => void;\n onReconnect?: (seatId: string) => Promise<void>;\n now?: number;\n}\n\nexport function ComposerSeatBlockView({\n seats,\n seatId,\n reports = null,\n onSeatChange,\n onReconnect = reconnectSeat,\n now = Date.now(),\n}: ComposerSeatBlockViewProps) {\n const [busy, setBusy] = useState(false);\n const [status, setStatus] = useState<{ text: string; error: boolean } | null>(null);\n const seat = seats.find((candidate) => candidate.id === seatId) ?? null;\n if (seat === null) return null;\n const report = reports?.find((candidate) => candidate.seatId === seatId) ?? null;\n const block = seatBlock(seat, report, now);\n if (block === null) return null;\n\n const alternatives = rankSeatAlternatives(seatId, seats, reports, now)\n .map((id) => seats.find((candidate) => candidate.id === id))\n .filter((candidate): candidate is VerseSeat => candidate !== undefined)\n .slice(0, INLINE_ALTERNATIVES);\n\n const reconnect = async (): Promise<void> => {\n setBusy(true);\n setStatus(null);\n try {\n await onReconnect(seatId);\n setStatus({ text: 'Sign-in opened in Terminal. Send again once you have finished there.', error: false });\n } catch (error) {\n setStatus({ text: error instanceof Error && error.message ? error.message : 'The sign-in window could not be opened.', error: true });\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <div className={styles.block} role=\"alert\" data-connection={block.connection}>\n <p className={styles.blockReason}>{block.reason}</p>\n <div className={styles.alternatives}>\n {block.connection === 'signed-out' ? (\n <Button size=\"sm\" variant=\"primary\" icon={<IconExternalLink />} busy={busy} onClick={() => { void reconnect(); }}>\n Reconnect {seat.label}\n </Button>\n ) : null}\n {alternatives.length > 0 ? (\n <>\n <span className={styles.alternativesLabel}>Continue in a new chat on</span>\n {alternatives.map((alternative) => {\n const model = firstRunnableModel(alternative);\n if (model === null) return null;\n return (\n <Button key={alternative.id} size=\"sm\" variant=\"subtle\"\n onClick={() => onSeatChange({ seatId: alternative.id, model: model.id })}>\n {alternative.label}\n </Button>\n );\n })}\n </>\n ) : (\n <span className={styles.alternativesLabel}>No other seat is ready right now.</span>\n )}\n </div>\n {status === null ? null : (\n <p className={styles.status} aria-live=\"polite\" data-error={status.error ? 'true' : undefined}>{status.text}</p>\n )}\n </div>\n );\n}\n\nexport type ComposerSeatBlockProps = Omit<ComposerSeatBlockViewProps, 'reports' | 'now'> & {\n /** False stops polling health (the seat's own capacity still decides). Default true. */\n active?: boolean;\n};\n\n/** Self-fetching: reads the live health reports (polled every 30 s while visible). */\nexport function ComposerSeatBlock({ active = true, ...props }: ComposerSeatBlockProps) {\n const health = useSeatHealth(active);\n return <ComposerSeatBlockView {...props} reports={health.data?.seats ?? null} />;\n}\n","/**\n * routes/verse/Composer.tsx — the message box and its controls (3.10: unit\n * C3; SPEC-310C §2 \"Composer\").\n *\n * [queued follow-ups: Edit · Send now · ×] (hidden when empty; portalled\n * to the host's `queueSlot` when given)\n * ┌──────────────────────────────────────────────────────────────────────┐\n * │ [attachment chips] │\n * │ Ask anything — @ to add files, / for commands │\n * │ [+] 🎙 ✎ Accept edits ▾ [C] Claude Max ◔ Opus 5 ▾ Effort: High ▾ ◔ 31% Send ⏎ │\n * └──────────────────────────────────────────────────────────────────────┘\n *\n * FOOTER: left = how you write (attach, dictate, permission mode); right =\n * where it runs and what it sends (seat, model, effort, context, send). The\n * seat chip names the ACCOUNT and the Model picker the model, so the model is\n * said once. Effort appears only where the seat can set it. Labels are short\n * and never ellipsized; the full words are each control's name and tooltip\n * (shown on keyboard focus too). When the row is too narrow it folds\n * (composer/useFooterFold): \"Effort:\" → an icon, the seat chip → its\n * monogram, the mode → its icon, then the pickers into the ⋯ sheet. Nothing\n * re-folds while the ⋯ sheet or a picker's menu is open, and when a fold\n * takes away the control that had focus, focus lands on its replacement (⋯,\n * or the box). Controls are 28px, 8px apart; typing never reflows the row\n * (while a turn runs, ■ and Queue sit there empty or not; a send in flight\n * keeps Send's width and says so with a busy style).\n *\n * ALWAYS EDITABLE. Enter while a turn runs QUEUES the message on the server\n * (at most 3); the queue sends each follow-up when the turn before it ends\n * cleanly, and HOLDS after a failure or a Stop (queue row says why). ⇧⌘↩\n * stops the running turn and sends this message next. Esc stops the turn —\n * only from an empty box with no menu open, so it never eats a draft.\n *\n * CONTROLS apply from the next turn, even mid-run: Permission (⇧⌘M: Plan,\n * Accept edits, Auto, Bypass — bypass is red and confirmed per chat), Model\n * (⇧⌘I) and Effort (⇧⌘E). An option the seat cannot honour is listed\n * disabled with the reason. Keys come from C0's command catalog; the palette\n * reaches the same actions through the `ashlr:command` window event.\n *\n * ATTACHMENTS: [+] / ⌘U, paste or drop. Each becomes a private copy on the\n * server and an `@path` token in the text, which the engine grants with\n * exactly that folder. `@` fuzzy-finds project files; `/` at the start offers\n * handoff, compact, new, plan, effort, model.\n *\n * At 375px (or a column too narrow for the pickers) the footer folds to\n * [+] [mic] · seat · ⋯ (the pickers in a bottom sheet) · Send.\n *\n * Kept from 3.9: per-chat drafts that survive a reload, ↑ history recall, the\n * pre-send cost hint, dictation, ⌘. to stop, and the seat-health block (A2).\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState, type ClipboardEvent, type DragEvent, type KeyboardEvent, type ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { VerseSeat } from '../../data/api-types.js';\nimport type { VerseEffort, VersePermissionMode } from '../../../core/verse/workbench-types.js';\nimport { VERSE_QUEUE_MAX } from '../../../core/verse/workbench-types.js';\nimport { MutationTokenDialog } from '../../components/auth/MutationTokenDialog.js';\nimport { IconPlus } from '../../components/primitives/icons.js';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport { costHint, loadDraft, loadHistory, pushHistory, saveDraft, type CostHint } from './chat/composer-state.js';\nimport { AttachmentChips } from './composer/AttachmentChips.js';\nimport { BypassConfirmDialog } from './composer/BypassConfirmDialog.js';\nimport { COMPOSER_COMMAND_IDS, pressesCommand, shortcutLabel, WORKBENCH_COMMAND_EVENT, type ComposerCommandId } from './composer/composer-keys.js';\nimport { searchSessionFiles } from './composer/composer-queries.js';\nimport {\n activeTrigger,\n applyCompletion,\n insertAttachmentRef,\n matchSlashCommands,\n mentionFor,\n removeAttachmentRef,\n type ComposerTrigger,\n type SlashCommand,\n} from './composer/composer-text.js';\nimport { ContextRing } from './composer/ContextRing.js';\nimport { ControlMenu } from './composer/ControlMenu.js';\nimport { ControlsSheet } from './composer/ControlsSheet.js';\nimport { QueueRow } from './composer/QueueRow.js';\nimport { capacityRingShown, SeatChip } from './composer/SeatChip.js';\nimport { SuggestMenu, suggestOptionId, type SuggestItem } from './composer/SuggestMenu.js';\nimport { useAttachmentDrafts, useFollowUpQueue, useSessionControls } from './composer/useComposerData.js';\nimport { FOOTER_FOLD_SHEET, useFooterFold } from './composer/useFooterFold.js';\nimport { useTokenGate } from './context/use-token-gate.js';\nimport { DictationButton } from './DictationButton.js';\nimport { ComposerSeatBlock } from './health/ComposerSeatBlock.js';\nimport type { SeatChoice } from './SeatSelector.js';\nimport { useViewport } from './shell/viewport.js';\nimport { ENGINE_LABEL, modelLabel, seatPillLabel } from './verse-model.js';\nimport { formatTokens } from './verse-readouts.js';\nimport type { VerseFileMatch } from '../../../core/verse/workbench-types.js';\nimport styles from './Composer.module.css';\nimport cstyles from './composer/composer.module.css';\n\nexport interface ComposerProps {\n /** Which chat this box belongs to — drafts, history, controls and the queue are per session. */\n sessionId?: string | null;\n seats: readonly VerseSeat[];\n seat: SeatChoice;\n /** Engine of the current chat (identity tick, and what the seat can do). */\n engine?: VerseSeat['engine'];\n running: boolean;\n disabled: boolean;\n disabledReason?: string | null;\n locked: boolean;\n /** True when this session already has turns — the hint row has done its job. */\n hintSeen?: boolean;\n /**\n * Live context occupancy, for the pre-send cost hint and the footer ring —\n * the SAME budget the header meter draws (verse-model `sessionContextBudget`).\n */\n contextTokens?: number | null;\n contextWindow?: number | null;\n autoCompactAt?: number | null;\n /** False when `contextTokens` is an upper bound (codex before its rollout is read). */\n contextExact?: boolean;\n /** The box was pre-filled with a handoff note that has not been sent yet. */\n handoffDraft?: boolean;\n onSend: (text: string) => Promise<boolean> | boolean;\n onStop: () => void;\n /** Start a new chat on a seat (the seat menu, `/new`). */\n onSeatChange: (choice: SeatChoice) => void;\n /** \"Continue on ‹seat›\": open the handoff prefilled for that seat. Absent → a new chat on it. */\n onContinueOn?: (choice: SeatChoice) => void;\n /** `/handoff`: open this chat's handoff. Absent → the command is listed disabled with where to find it. */\n onHandoff?: () => void;\n /**\n * Why `/handoff` cannot run right now (a turn is running, dispatch is off) —\n * the SAME reason the header's ⋯ item shows. Null/absent = available.\n */\n handoffDisabledReason?: string | null;\n /**\n * Where the \"Queued turns\" row renders. SPEC-310C §2 orders the rows above\n * the composer notice → queued → live status → branch bar; the queue's state\n * lives here, so the host (Workspace) passes the element at the queue's\n * place and the row is portalled into it. Absent/null → the row renders at\n * the top of the composer, as before.\n */\n queueSlot?: HTMLElement | null;\n /**\n * Text another pane drafts INTO the box (Review's \"Add to message\"\n * `path:line: note`, Terminal's \"Send selection to chat\"): each new `nonce`\n * appends `text` on its own paragraph and focuses the box. Never sends —\n * the operator reads it and presses Enter (or ⌘Enter).\n */\n insertRequest?: { nonce: number; text: string } | null;\n autoFocus?: boolean;\n}\n\nconst MAX_TEXT_BYTES = 64 * 1024;\n/** Typing must pause this long before the draft is written to localStorage (see saveDraft). */\nconst DRAFT_WRITE_DEBOUNCE_MS = 400;\n/** The `@` finder waits for a pause in typing before it asks the server. */\nconst MENTION_DEBOUNCE_MS = 120;\n/** DESIGN §5: the box grows to 40% of the viewport, then scrolls. */\nconst MAX_HEIGHT_RATIO = 0.4;\nconst MAX_HEIGHT_FALLBACK_PX = 320;\n\ntype PickerId = 'permission' | 'model' | 'effort';\n\n/**\n * Permission-mode icons as inline SVG, NOT text glyphs (review 3.10 c18).\n * WHY: '◇ ✎ ↻ ⚠' all sit outside the Ashlr Sans Latin subset's unicode-range\n * (design/global.css), so the browser fetched the 230 KB full IBM Plex face\n * on every chat paint just to find that three of the four are not in Plex\n * either and fall back to a system font. SVG costs no font request and draws\n * the same in every OS. Same geometry as components/primitives/icons.tsx\n * (16-unit box, 1.5 stroke, round caps, currentColor); decorative — the menu\n * button already carries the mode's name.\n */\nfunction ModeIcon({ children }: { children: ReactNode }) {\n return (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.5} strokeLinecap=\"round\" strokeLinejoin=\"round\" focusable=\"false\" aria-hidden=\"true\">\n {children}\n </svg>\n );\n}\n\nconst PERMISSION_GLYPH: Record<VersePermissionMode, ReactNode> = {\n // Plan: an outlined diamond (was '◇').\n plan: <ModeIcon><path d=\"M8 2.2 13.8 8 8 13.8 2.2 8Z\" /></ModeIcon>,\n // Accept edits: a pencil (was '✎').\n 'accept-edits': <ModeIcon><path d=\"M10.6 2.9 13.1 5.4 5.6 12.9 2.6 13.4 3.1 10.4Z\" /><path d=\"M9.2 4.3 11.7 6.8\" /></ModeIcon>,\n // Auto: a circular arrow (was '↻').\n auto: <ModeIcon><path d=\"M13.2 7.2a5.2 5.2 0 1 0-.7 3.5\" /><path d=\"M13.5 3.4v3.9h-3.9\" /></ModeIcon>,\n // Bypass: a warning triangle (was '⚠').\n bypass: <ModeIcon><path d=\"M8 2.6 14.2 13H1.8L8 2.6Z\" /><path d=\"M8 6.6v3\" /><circle cx=\"8\" cy=\"11.3\" r=\"0.75\" fill=\"currentColor\" stroke=\"none\" /></ModeIcon>,\n};\n\n/** The footer's short mode names; the option's own label (\"Bypass permissions\") is the name and title. */\nconst PERMISSION_SHORT: Record<VersePermissionMode, string> = {\n plan: 'Plan',\n 'accept-edits': 'Accept edits',\n auto: 'Auto',\n bypass: 'Bypass',\n};\n\n/** Effort, once \"Effort:\" folds away: three rising bars (same geometry rules as ModeIcon). */\nconst EFFORT_GLYPH = <ModeIcon><path d=\"M3.5 12.5v-2.5M8 12.5V7.5M12.5 12.5v-8\" /></ModeIcon>;\n\n/** The compact footer's \"more settings\" dots — SVG for the same reason ('⋯' U+22EF is outside the subset and not in Plex). */\nconst MORE_DOTS = (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"currentColor\" focusable=\"false\" aria-hidden=\"true\">\n <circle cx=\"3.5\" cy=\"8\" r=\"1.25\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.25\" />\n <circle cx=\"12.5\" cy=\"8\" r=\"1.25\" />\n </svg>\n);\n\nexport function Composer({ sessionId = null, seats, seat, engine, running, disabled, disabledReason, locked,\n hintSeen = false, contextTokens = null, contextWindow = null, autoCompactAt = null, contextExact = true, handoffDraft = false,\n onSend, onStop, onSeatChange, onContinueOn, onHandoff, handoffDisabledReason = null, queueSlot = null, insertRequest = null,\n autoFocus = false }: ComposerProps) {\n const { compact: phone } = useViewport();\n const gate = useTokenGate();\n const run = gate.run;\n const controls = useSessionControls(disabled ? null : sessionId, running, run);\n const followUps = useFollowUpQueue(disabled ? null : sessionId, running, run);\n const attachments = useAttachmentDrafts(disabled ? null : sessionId, run);\n\n // A draft survives ⌘K, a reload and a crash (Composer is keyed by session id).\n const [draft, setDraft] = useState(() => loadDraft(sessionId));\n const [interim, setInterim] = useState('');\n const [sending, setSending] = useState(false);\n const [listening, setListening] = useState(false);\n const [sentHere, setSentHere] = useState(false);\n const [note, setNote] = useState<{ text: string; error: boolean } | null>(null);\n const [historyAt, setHistoryAt] = useState(-1);\n const [initialHistory] = useState(() => loadHistory(sessionId));\n const history = useRef<string[]>(initialHistory);\n const stashed = useRef('');\n const textarea = useRef<HTMLTextAreaElement>(null);\n const form = useRef<HTMLFormElement>(null);\n const fileInput = useRef<HTMLInputElement>(null);\n const moreButton = useRef<HTMLButtonElement>(null);\n /** The ⋯ sheet was closed by the operator: focus goes back to ⋯ — or to the box, if ⋯ has folded away. */\n const sheetReturnFocus = useRef(false);\n const helpId = useId();\n const suggestId = useId();\n const [caret, setCaret] = useState<number | null>(null);\n const [dismissedTrigger, setDismissedTrigger] = useState<number | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const [fileMatches, setFileMatches] = useState<{ query: string; files: VerseFileMatch[]; primaryRoot: string | null } | null>(null);\n const [fileSearchError, setFileSearchError] = useState<string | null>(null);\n const [openRequest, setOpenRequest] = useState<Record<PickerId, number>>({ permission: 0, model: 0, effort: 0 });\n const [openMenus, setOpenMenus] = useState<Record<PickerId, boolean>>({ permission: false, model: false, effort: false });\n const [sheetOpen, setSheetOpen] = useState(false);\n const [bypassAsk, setBypassAsk] = useState(false);\n const [dragging, setDragging] = useState(false);\n\n const text = interim ? `${draft}${draft && !draft.endsWith(' ') ? ' ' : ''}${interim}` : draft;\n const tooLong = useMemo(() => new TextEncoder().encode(text).length > MAX_TEXT_BYTES, [text]);\n const queue = followUps.queue;\n const queueAvailable = queue !== null;\n const queueFull = (queue?.items.length ?? 0) >= VERSE_QUEUE_MAX;\n const hasText = text.trim().length > 0;\n const blockedByUpload = attachments.uploading;\n const canSend = !disabled && !sending && hasText && !tooLong && !blockedByUpload && (!running || (queueAvailable && !queueFull));\n const showHint = !hintSeen && !sentHere;\n const showHandoffHelp = handoffDraft && !sentHere && hasText;\n const cost = useMemo(\n () => costHint(text, { contextTokens, contextWindow, autoCompactAt, exact: contextExact }),\n [text, contextTokens, contextWindow, autoCompactAt, contextExact],\n );\n const view = controls.view;\n const effectiveEngine = engine ?? seats.find((s) => s.id === seat.seatId)?.engine ?? 'claude';\n const chipLabel = seatPillLabel(seats, { seatId: seat.seatId, engine: effectiveEngine, model: view?.controls.model ?? seat.model });\n // The chip names the account; a local seat's label IS its model's name, so it says \"Local\".\n const seatName = effectiveEngine === 'local'\n ? ENGINE_LABEL.local\n : seats.find((s) => s.id === seat.seatId)?.label ?? ENGINE_LABEL[effectiveEngine];\n const modelName = view ? labelOf(view.options.models, view.controls.model) : modelLabel(seats, seat);\n const effortOn = view?.options.efforts.some((o) => o.available) === true;\n const effortName = view?.controls.effort ? labelOf(view.options.efforts, view.controls.effort) : 'Default';\n const footerRow = useRef<HTMLDivElement>(null);\n // The seat chip's capacity ring (14px + a gap) arrives with a later roster\n // poll and takes room, so its presence is part of what the fold measures;\n // so is `phone`, which swaps the whole row for the compact one.\n const ringShown = capacityRingShown(seats.find((s) => s.id === seat.seatId), effectiveEngine);\n const pickerMenuOpen = Object.values(openMenus).some(Boolean);\n const fold = useFooterFold(footerRow, [seatName, modelName, effortOn && effortName, view?.controls.permissionMode,\n running, queueAvailable, queueFull, locked, disabled, ringShown, phone].join('|'), {\n // Nothing folds under an open overlay: a re-measure could unmount ⋯ or\n // the picker the overlay belongs to. What changed is measured on close.\n hold: sheetOpen || pickerMenuOpen,\n onSettle: (settled, focusBefore) => {\n // A fold took away the control that had focus (the pickers left for ⋯,\n // ⋯ left for the pickers, or new words passed the row through fold 0),\n // or the operator closed the sheet: put focus on the replacement.\n const active = document.activeElement;\n const lost = active === null || active === document.body;\n const returning = sheetReturnFocus.current;\n sheetReturnFocus.current = false;\n if (!lost || (!returning && (focusBefore === null || focusBefore.isConnected))) return;\n const more = phone || settled >= FOOTER_FOLD_SHEET ? moreButton.current : null;\n (more ?? textarea.current)?.focus();\n },\n });\n const compact = phone || fold >= FOOTER_FOLD_SHEET;\n const attachBlocked = effectiveEngine === 'grok'\n ? 'Grok can only open files inside the project folder, so it can’t read attachments.'\n : null;\n\n // ---- drafts ---------------------------------------------------------------\n const persistedDraft = useRef(draft);\n const latestDraft = useRef(draft);\n latestDraft.current = draft;\n\n const flushDraft = useCallback(() => {\n if (persistedDraft.current === latestDraft.current) return;\n persistedDraft.current = latestDraft.current;\n saveDraft(sessionId, latestDraft.current);\n }, [sessionId]);\n\n useEffect(() => {\n if (persistedDraft.current === draft) return undefined;\n const id = setTimeout(flushDraft, DRAFT_WRITE_DEBOUNCE_MS);\n return () => clearTimeout(id);\n }, [draft, flushDraft]);\n\n useEffect(() => {\n window.addEventListener('beforeunload', flushDraft);\n return () => {\n window.removeEventListener('beforeunload', flushDraft);\n flushDraft();\n };\n }, [flushDraft]);\n\n useEffect(() => {\n if (autoFocus) textarea.current?.focus();\n }, [autoFocus]);\n\n // Text drafted in from another pane: appended once per nonce, never sent.\n const seenInsert = useRef<number | null>(insertRequest?.nonce ?? null);\n useEffect(() => {\n if (!insertRequest || insertRequest.nonce === seenInsert.current) return;\n seenInsert.current = insertRequest.nonce;\n const addition = insertRequest.text.replace(/\\s+$/, '');\n if (!addition) return;\n setDraft((current) => (current.trim() ? `${current.replace(/\\s+$/, '')}\\n\\n${addition}` : addition));\n placeCaret(Number.MAX_SAFE_INTEGER);\n }, [insertRequest]);\n\n // ⌘. stops the running turn from anywhere in the app (armed only while one runs).\n //\n // ⌘. is ALSO the shell's Resources toggle (3.11 C6), and both listened on\n // document in the bubble phase: one press cancelled the turn AND opened the\n // drawer. While a turn runs, stopping wins (the macOS cancel chord, and the\n // urgent one) — so this listens in the CAPTURE phase and claims the event;\n // the shell skips default-prevented keys. Inside a modal (the palette, a\n // confirmation, the floating Resources drawer) the keyboard belongs to that\n // modal and ⌘. keeps its drawer meaning, so a press meant to close the\n // drawer never cancels work.\n useEffect(() => {\n if (!running) return undefined;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key !== '.' || (!event.metaKey && !event.ctrlKey) || event.shiftKey || event.altKey) return;\n if (event.defaultPrevented) return;\n if (document.querySelector('[aria-modal=\"true\"]')) return;\n event.preventDefault();\n onStop();\n };\n document.addEventListener('keydown', onKey, true);\n return () => document.removeEventListener('keydown', onKey, true);\n }, [running, onStop]);\n\n // When the reply finishes, hand focus back to the box unless it went somewhere deliberate.\n useEffect(() => {\n if (running || !autoFocus) return;\n const active = document.activeElement;\n const ours = active !== null && active !== textarea.current && form.current?.contains(active) === true;\n if (active === null || active === document.body || !active.isConnected || ours) textarea.current?.focus();\n }, [running, autoFocus]);\n\n // Grow with content up to 40% of the viewport; shrink back when cleared.\n useEffect(() => {\n const node = textarea.current;\n if (!node) return;\n const viewport = typeof window === 'undefined' ? 0 : window.innerHeight;\n const max = viewport > 0 ? Math.round(viewport * MAX_HEIGHT_RATIO) : MAX_HEIGHT_FALLBACK_PX;\n node.style.height = 'auto';\n node.style.height = `${Math.min(node.scrollHeight, max)}px`;\n }, [text]);\n\n // ---- `@` and `/` ------------------------------------------------------------\n const trigger: ComposerTrigger | null = useMemo(() => {\n if (disabled || interim || caret === null) return null;\n const found = activeTrigger(draft, caret);\n if (!found || found.start === dismissedTrigger) return null;\n if (found.kind === 'mention' && !sessionId) return null;\n return found;\n }, [draft, caret, disabled, interim, dismissedTrigger, sessionId]);\n\n useEffect(() => {\n if (trigger?.kind !== 'mention' || !sessionId) {\n setFileSearchError(null);\n return undefined;\n }\n const abort = new AbortController();\n const timer = setTimeout(() => {\n searchSessionFiles(sessionId, trigger.query, abort.signal)\n .then((result) => {\n setFileMatches({ query: result.query, files: result.files, primaryRoot: result.primaryRoot ?? null });\n setFileSearchError(null);\n })\n .catch(() => { if (!abort.signal.aborted) setFileSearchError('Files could not be listed.'); });\n }, MENTION_DEBOUNCE_MS);\n return () => {\n clearTimeout(timer);\n abort.abort();\n };\n }, [trigger?.kind, trigger?.query, sessionId]);\n\n const commands: SlashCommand[] = trigger?.kind === 'command' ? matchSlashCommands(trigger.query) : [];\n const commandDisabled = useCallback((id: SlashCommand['id']): string | null => {\n if (id === 'handoff' && !onHandoff) return 'Use Hand off in the chat header’s Chat actions menu'; // named, not drawn as the U+22EF glyph, which is outside the font subset (c18)\n if (id === 'handoff' && handoffDisabledReason) return handoffDisabledReason;\n if ((id === 'plan' || id === 'effort' || id === 'model') && !view) return 'This server has no session controls yet';\n if (id === 'plan' && view && !view.options.permissionModes.find((o) => o.id === 'plan')?.available) return 'This seat has no plan mode';\n return null;\n }, [onHandoff, handoffDisabledReason, view]);\n\n const suggestItems: SuggestItem[] = trigger?.kind === 'command'\n ? commands.map((c) => ({ id: c.id, primary: c.title, secondary: c.description, disabledReason: commandDisabled(c.id) }))\n : trigger?.kind === 'mention'\n ? (fileMatches?.files ?? []).slice(0, 12).map((f) => ({ id: `${f.root}:${f.path}`, primary: f.path, secondary: fileMatches?.primaryRoot === f.root ? null : f.root }))\n : [];\n const suggestOpen = trigger !== null;\n const suggestStatus = trigger?.kind === 'mention'\n ? fileSearchError ?? (fileMatches === null || fileMatches.query !== trigger.query ? 'Searching…' : suggestItems.length === 0 ? 'No files match' : null)\n : null;\n const boundedActive = suggestItems.length === 0 ? 0 : Math.min(activeIndex, suggestItems.length - 1);\n\n useEffect(() => { setActiveIndex(0); }, [trigger?.kind, trigger?.query]);\n\n function syncCaret() {\n const node = textarea.current;\n if (node) setCaret(node.selectionStart === node.selectionEnd ? node.selectionStart : null);\n }\n\n function placeCaret(position: number) {\n requestAnimationFrame(() => {\n const node = textarea.current;\n if (!node) return;\n const at = Math.min(position, node.value.length);\n node.focus();\n node.setSelectionRange(at, at);\n setCaret(at);\n });\n }\n\n const openPicker = useCallback((id: PickerId) => {\n if (compact) {\n setSheetOpen(true);\n return;\n }\n setOpenRequest((current) => ({ ...current, [id]: current[id] + 1 }));\n }, [compact]);\n\n function runSlash(command: SlashCommand) {\n if (!trigger) return;\n const reason = commandDisabled(command.id);\n if (reason) {\n setNote({ text: reason, error: false });\n return;\n }\n // Every command but /compact consumes its own text.\n const without = `${draft.slice(0, trigger.start)}${draft.slice(trigger.end)}`.replace(/^\\s+/, '');\n switch (command.id) {\n case 'compact': {\n const next = applyCompletion(draft, trigger, '/compact');\n setDraft(next.text);\n placeCaret(next.caret);\n return;\n }\n case 'new':\n setDraft(without);\n onSeatChange({ seatId: seat.seatId, model: view?.controls.model ?? seat.model });\n return;\n case 'handoff':\n setDraft(without);\n onHandoff?.();\n return;\n case 'plan':\n setDraft(without);\n placeCaret(0);\n void changePermission('plan');\n return;\n case 'effort':\n case 'model':\n setDraft(without);\n openPicker(command.id);\n return;\n default:\n }\n }\n\n function acceptSuggestion(index: number) {\n if (!trigger) return;\n if (trigger.kind === 'command') {\n const command = commands[index];\n if (command) runSlash(command);\n return;\n }\n const match = fileMatches?.files[index];\n if (!match) return;\n const next = applyCompletion(draft, trigger, mentionFor(match, fileMatches?.primaryRoot ?? null));\n setDraft(next.text);\n placeCaret(next.caret);\n }\n\n // ---- controls ---------------------------------------------------------------\n const changePermission = useCallback(async (mode: VersePermissionMode) => {\n if (mode === 'bypass') {\n setBypassAsk(true);\n return;\n }\n const ok = await controls.update({ permissionMode: mode }, 'Change this chat’s permission mode.');\n if (ok) setNote({ text: running ? 'Permission mode changes from the next turn.' : `Permission mode: ${labelOf(view?.options.permissionModes, mode)}.`, error: false });\n }, [controls, running, view]);\n\n const changeModel = useCallback(async (model: string) => {\n const ok = await controls.update({ model }, 'Change this chat’s model.');\n if (ok) setNote({ text: running ? 'The new model takes over from the next turn.' : `Model: ${labelOf(view?.options.models, model)}.`, error: false });\n }, [controls, running, view]);\n\n const changeEffort = useCallback(async (effort: VerseEffort | null) => {\n const ok = await controls.update({ effort }, 'Change this chat’s reasoning effort.');\n if (ok) setNote({ text: running ? 'Effort changes from the next turn.' : `Effort: ${effort ? labelOf(view?.options.efforts, effort) : 'the CLI default'}.`, error: false });\n }, [controls, running, view]);\n\n async function confirmBypass() {\n setBypassAsk(false);\n const ok = await controls.update({ permissionMode: 'bypass', confirmBypass: true }, 'Bypass permissions for this chat.');\n if (ok) setNote({ text: 'Bypass is on for this chat — every check is skipped.', error: false });\n textarea.current?.focus();\n }\n\n // ---- attachments ------------------------------------------------------------\n const addFiles = useCallback((files: readonly File[]) => {\n if (files.length === 0 || disabled) return;\n if (attachBlocked) {\n setNote({ text: attachBlocked, error: true });\n return;\n }\n attachments.add(files, (ref) => {\n setDraft((current) => insertAttachmentRef(current, ref).text);\n });\n }, [attachments, attachBlocked, disabled]);\n\n async function removeChip(key: string) {\n const ref = await attachments.remove(key);\n if (ref) setDraft((current) => removeAttachmentRef(current, ref));\n }\n\n function onPaste(event: ClipboardEvent<HTMLTextAreaElement>) {\n const files = Array.from(event.clipboardData?.files ?? []);\n if (files.length === 0) return;\n event.preventDefault();\n addFiles(files);\n }\n\n function onDrop(event: DragEvent<HTMLFormElement>) {\n setDragging(false);\n const files = Array.from(event.dataTransfer?.files ?? []);\n if (files.length === 0) return;\n event.preventDefault();\n addFiles(files);\n }\n\n const openFilePicker = useCallback(() => {\n if (disabled) return;\n if (attachBlocked) {\n setNote({ text: attachBlocked, error: true });\n return;\n }\n fileInput.current?.click();\n }, [disabled, attachBlocked]);\n\n // ---- send / queue -------------------------------------------------------------\n const clearAfterSend = useCallback((value: string) => {\n const last = history.current[history.current.length - 1];\n if (last !== value) history.current = [...history.current, value];\n pushHistory(sessionId, value);\n setHistoryAt(-1);\n stashed.current = '';\n setDraft('');\n persistedDraft.current = '';\n latestDraft.current = '';\n saveDraft(sessionId, '');\n setInterim('');\n setSentHere(true);\n attachments.reset();\n }, [sessionId, attachments]);\n\n const submit = useCallback(async (mode: 'send' | 'stop-and-send' = 'send') => {\n if (disabled || sending || tooLong) return;\n const value = text.trim();\n if (!value) return;\n if (blockedByUpload) {\n setNote({ text: 'Wait for the attachments to finish uploading.', error: false });\n return;\n }\n setSending(true);\n setNote(null);\n try {\n if (running && queueAvailable) {\n if (mode !== 'stop-and-send' && queueFull) {\n setNote({ text: `Up to ${VERSE_QUEUE_MAX} follow-ups can wait — send or remove one first.`, error: true });\n return;\n }\n const result = await followUps.enqueue(value, { sendNow: mode === 'stop-and-send' });\n if (result) {\n clearAfterSend(value);\n if (mode === 'stop-and-send') setNote({ text: 'Stopping the turn — this message goes next.', error: false });\n }\n return;\n }\n if (running) return; // older server: no queue, the box just keeps the draft\n const ok = await onSend(value);\n if (ok) clearAfterSend(value);\n } finally {\n setSending(false);\n textarea.current?.focus();\n }\n }, [disabled, sending, tooLong, text, blockedByUpload, running, queueAvailable, queueFull, followUps, clearAfterSend, onSend]);\n\n async function editQueued(queueId: string, queuedText: string) {\n const ok = await followUps.remove(queueId);\n if (!ok) return;\n setDraft((current) => (current.trim() ? `${current.replace(/\\s+$/, '')}\\n\\n${queuedText}` : queuedText));\n placeCaret(Number.MAX_SAFE_INTEGER);\n }\n\n // ---- keys ----------------------------------------------------------------------\n // What is actually on screen: the sheet renders whenever it is open (and\n // there are pickers to show), and a picker menu reports itself closed when\n // a fold unmounts it — so Esc-to-stop is never disarmed by an overlay that is gone.\n const sheetShown = sheetOpen && view !== null;\n const anyOverlayOpen = suggestOpen || pickerMenuOpen || sheetShown || bypassAsk || gate.dialog.open;\n\n const runCommand = useCallback((id: ComposerCommandId) => {\n switch (id) {\n case 'composer.permission': openPicker('permission'); return;\n case 'composer.model': openPicker('model'); return;\n case 'composer.effort': openPicker('effort'); return;\n case 'composer.attach': openFilePicker(); return;\n // \"Run in cloud\" lives in the Chat settings sheet (ControlsSheet), with\n // its own disabled reasons and token gate; the palette only opens it.\n case 'composer.cloud':\n if (view) setSheetOpen(true);\n else setNote({ text: 'Run in cloud opens from a chat whose controls have loaded — open or start a chat first.', error: false });\n return;\n case 'composer.send': void submit('send'); return;\n case 'composer.stop-and-send': void submit('stop-and-send'); return;\n case 'composer.stop': if (running) onStop(); return;\n default:\n }\n }, [openPicker, openFilePicker, submit, running, onStop, view]);\n\n // Chat-scope keys (⇧⌘M/I/E, ⌘U) are live while the chat is on screen, not\n // only while the box has focus; a modal dialog anywhere owns the keys.\n useEffect(() => {\n if (disabled) return undefined;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.defaultPrevented || document.querySelector('[role=\"dialog\"][aria-modal=\"true\"]')) return;\n for (const id of ['composer.permission', 'composer.model', 'composer.effort', 'composer.attach'] as const) {\n if (pressesCommand(event, id)) {\n event.preventDefault();\n runCommand(id);\n return;\n }\n }\n };\n const onCommand = (event: Event) => {\n const id = (event as CustomEvent<{ id?: unknown }>).detail?.id;\n if (typeof id === 'string' && (COMPOSER_COMMAND_IDS as readonly string[]).includes(id)) runCommand(id as ComposerCommandId);\n };\n document.addEventListener('keydown', onKey);\n window.addEventListener(WORKBENCH_COMMAND_EVENT, onCommand);\n return () => {\n document.removeEventListener('keydown', onKey);\n window.removeEventListener(WORKBENCH_COMMAND_EVENT, onCommand);\n };\n }, [disabled, runCommand]);\n\n function recall(delta: -1 | 1): boolean {\n const list = history.current;\n if (list.length === 0) return false;\n if (historyAt === -1) {\n if (delta > 0) return false;\n stashed.current = draft;\n const next = list.length - 1;\n setHistoryAt(next);\n setDraft(list[next]!);\n setInterim('');\n return true;\n }\n const next = historyAt + delta;\n if (next < 0) return true;\n if (next >= list.length) {\n setHistoryAt(-1);\n setDraft(stashed.current);\n return true;\n }\n setHistoryAt(next);\n setDraft(list[next]!);\n return true;\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLTextAreaElement>) {\n const node = event.currentTarget;\n const collapsed = node.selectionStart === node.selectionEnd;\n const composing = event.nativeEvent.isComposing;\n\n if (suggestOpen && !composing) {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n if (suggestItems.length > 0) {\n event.preventDefault();\n const step = event.key === 'ArrowDown' ? 1 : -1;\n setActiveIndex((boundedActive + step + suggestItems.length) % suggestItems.length);\n }\n return;\n }\n if ((event.key === 'Enter' && !event.shiftKey && !event.metaKey && !event.ctrlKey) || event.key === 'Tab') {\n if (suggestItems.length > 0) {\n event.preventDefault();\n acceptSuggestion(boundedActive);\n return;\n }\n if (event.key === 'Tab') return;\n }\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n setDismissedTrigger(trigger?.start ?? null);\n return;\n }\n }\n\n if (!composing && pressesCommand(event, 'composer.stop-and-send')) {\n event.preventDefault();\n void submit('stop-and-send');\n return;\n }\n if (event.key === 'Enter' && !event.shiftKey && !composing) {\n event.preventDefault();\n void submit('send');\n return;\n }\n if (!composing && event.key === 'Escape' && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) {\n // Esc stops the turn ONLY from an empty box with nothing open over it,\n // so it can never discard a draft or fight a menu for the key.\n if (running && !hasText && !anyOverlayOpen) {\n event.preventDefault();\n onStop();\n }\n return;\n }\n if (event.key === 'ArrowUp' && collapsed && (historyAt !== -1 || (node.selectionStart === 0 && !interim))) {\n if (recall(-1)) event.preventDefault();\n return;\n }\n if (event.key === 'ArrowDown' && collapsed && historyAt !== -1) {\n if (recall(1)) event.preventDefault();\n }\n }\n\n // ---- render ----------------------------------------------------------------------\n const placeholder = disabled\n ? disabledReason ?? 'Sending is disabled'\n : running\n ? (queueAvailable ? 'Queue a follow-up — it sends when this turn ends…' : 'Draft your next message…')\n : 'Ask anything — @ to add files, / for commands';\n\n const permissionValue = view?.controls.permissionMode ?? null;\n const bypassOn = permissionValue === 'bypass';\n const appliesNote = running ? 'Changes apply from the next turn.' : null;\n const pickers = view ? (variant: 'menu' | 'list') => ({\n permission: (\n <ControlMenu<VersePermissionMode> key=\"permission\" label=\"Permission mode\" variant={variant}\n valueLabel={PERMISSION_SHORT[view.controls.permissionMode]}\n valueTitle={labelOf(view.options.permissionModes, view.controls.permissionMode)} iconOnly={fold >= 3}\n options={view.options.permissionModes} value={view.controls.permissionMode}\n onChange={(mode) => { void changePermission(mode); }} disabled={controls.pending}\n shortcut={shortcutLabel('composer.permission')} openRequest={openRequest.permission}\n icon={PERMISSION_GLYPH[view.controls.permissionMode]} danger={bypassOn} note={appliesNote}\n onOpenChange={(open) => setOpenMenus((m) => ({ ...m, permission: open }))} />\n ),\n model: (\n <ControlMenu<string> key=\"model\" label=\"Model\" variant={variant}\n valueLabel={modelName}\n options={view.options.models} value={view.controls.model}\n onChange={(model) => { void changeModel(model); }} disabled={controls.pending}\n shortcut={shortcutLabel('composer.model')} openRequest={openRequest.model} note={appliesNote}\n onOpenChange={(open) => setOpenMenus((m) => ({ ...m, model: open }))} />\n ),\n // No control at all where the seat cannot set effort (local models, an old CLI).\n effort: effortOn ? (\n <ControlMenu<VerseEffort> key=\"effort\" label=\"Effort\" variant={variant}\n valueLabel={fold >= 1 ? effortName : `Effort: ${effortName}`} valueTitle={effortName}\n icon={fold >= 1 ? EFFORT_GLYPH : undefined}\n options={view.options.efforts} value={view.controls.effort}\n defaultOption={{ label: 'Default', description: 'The CLI decides', onSelect: () => { void changeEffort(null); } }}\n onChange={(effort) => { void changeEffort(effort); }} disabled={controls.pending}\n shortcut={shortcutLabel('composer.effort')} openRequest={openRequest.effort} note={appliesNote}\n onOpenChange={(open) => setOpenMenus((m) => ({ ...m, effort: open }))} />\n ) : null,\n }) : null;\n const wide = pickers?.('menu') ?? null;\n\n // No \"Sending…\": a label that changes width on Enter would shift the row. The button is disabled +\n // aria-busy instead, which Composer.module.css draws as the accent, pulsing, with a progress cursor —\n // never the quiet grey of an empty box (the text stays in the box until the send is accepted).\n const sendLabel = running ? (queueFull ? 'Queue full' : 'Queue') : locked ? 'Unlock & send' : 'Send';\n const returnKey = <span className={cstyles.sendKey} aria-hidden=\"true\">⏎</span>;\n\n return (\n <form ref={form} className={`${styles.composer} ${compact ? cstyles.composerCompact : ''}`}\n onSubmit={(event) => { event.preventDefault(); void submit('send'); }} aria-describedby={helpId}\n onDragOver={(event) => {\n if (disabled || !Array.from(event.dataTransfer?.types ?? []).includes('Files')) return;\n event.preventDefault();\n setDragging(true);\n }}\n onDragLeave={(event) => { if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDragging(false); }}\n onDrop={onDrop}>\n {disabled ? null : <ComposerSeatBlock seats={seats} seatId={seat.seatId} onSeatChange={onSeatChange} />}\n {portalInto(queueSlot, (\n <QueueRow queue={queue} running={running} disabled={disabled}\n onEdit={(id, queued) => { void editQueued(id, queued); }}\n onSendNow={(id) => { void followUps.sendNow(id); }}\n onRemove={(id) => { void followUps.remove(id); }} />\n ))}\n <div className={`${styles.box} ${listening ? styles.boxListening : ''} ${dragging ? cstyles.boxDragging : ''} ${bypassOn ? cstyles.boxBypass : ''}`}>\n {suggestOpen ? (\n <SuggestMenu id={suggestId} label={trigger?.kind === 'command' ? 'Commands' : 'Files in this chat’s folders'}\n items={suggestItems} activeIndex={boundedActive} status={suggestStatus}\n onPick={acceptSuggestion} onHover={setActiveIndex} />\n ) : null}\n <AttachmentChips drafts={attachments.drafts} onRemove={(key) => { void removeChip(key); }} disabled={disabled} />\n <textarea ref={textarea} className={styles.textarea} value={text} rows={1} placeholder={placeholder}\n aria-label=\"Message\" disabled={disabled}\n aria-autocomplete={suggestOpen ? 'list' : undefined}\n aria-controls={suggestOpen && suggestItems.length > 0 ? suggestId : undefined}\n aria-expanded={suggestOpen ? true : undefined}\n aria-activedescendant={suggestOpen && suggestItems.length > 0 ? suggestOptionId(suggestId, boundedActive) : undefined}\n onChange={(event) => {\n setInterim('');\n setHistoryAt(-1);\n setDraft(event.target.value);\n setDismissedTrigger(null);\n setCaret(event.target.selectionStart === event.target.selectionEnd ? event.target.selectionStart : null);\n }}\n onSelect={syncCaret} onClick={syncCaret} onKeyUp={(event) => { if (event.key.startsWith('Arrow') || event.key === 'Home' || event.key === 'End') syncCaret(); }}\n onBlur={() => setCaret(null)} onFocus={syncCaret}\n onPaste={onPaste} onKeyDown={onKeyDown} />\n <input ref={fileInput} type=\"file\" multiple hidden tabIndex={-1} aria-hidden=\"true\"\n onChange={(event) => { addFiles(Array.from(event.target.files ?? [])); event.target.value = ''; }} />\n <div ref={footerRow} className={`${styles.row} ${cstyles.footer}`}>\n <div className={cstyles.footerLeft}>\n <Tooltip label={attachBlocked ?? 'Attach files — or paste, or drop'} shortcut={attachBlocked ? undefined : shortcutLabel('composer.attach')}>\n <button type=\"button\" className={cstyles.iconButton} aria-label=\"Attach files\" aria-disabled={attachBlocked ? true : undefined}\n disabled={disabled} onClick={openFilePicker}>\n <IconPlus width={16} height={16} aria-hidden=\"true\" />\n </button>\n </Tooltip>\n <DictationButton disabled={disabled}\n onInterim={setInterim}\n onFinal={(chunk) => setDraft((current) => (current && !current.endsWith(' ') ? `${current} ${chunk}` : `${current}${chunk}`))}\n onListeningChange={setListening} />\n {!compact && wide ? wide.permission : null}\n </div>\n <div className={cstyles.footerRight}>\n <SeatChip seats={seats} seat={{ seatId: seat.seatId, model: view?.controls.model ?? seat.model }} engine={effectiveEngine}\n label={chipLabel} name={seatName} disabled={disabled} compact={compact || fold >= 2}\n {...(onContinueOn ? { onContinueOn } : {})} onNewChat={onSeatChange} />\n {compact ? null : wide ? wide.model : (\n // No session controls (an older or read-only server, or still loading): the model, stated once, not a picker.\n <span className={cstyles.controlStatic} title={`Model: ${modelName}`}>{modelName}</span>\n )}\n {!compact && wide ? wide.effort : null}\n {!compact ? <ContextRing contextTokens={contextTokens} contextWindow={contextWindow} autoCompactAt={autoCompactAt} exact={contextExact} engine={effectiveEngine} /> : null}\n {compact && view ? (\n <button ref={moreButton} type=\"button\" className={`${cstyles.iconButton} ${bypassOn ? cstyles.iconButtonDanger : ''}`}\n aria-haspopup=\"dialog\" aria-label={`Chat settings: ${labelOf(view.options.permissionModes, view.controls.permissionMode)}, ${modelName}`}\n title=\"Chat settings\" onClick={() => setSheetOpen(true)} disabled={disabled}>\n {MORE_DOTS}\n </button>\n ) : null}\n {running ? (\n <Tooltip label=\"Stop the running turn — or Esc from an empty box\" shortcut=\"⌘.\">\n <button key=\"stop\" type=\"button\" className={`${cstyles.iconButton} ${styles.stop}`} onClick={onStop} aria-label=\"Stop the running turn\">\n <span className={styles.stopIcon} aria-hidden=\"true\" />\n </button>\n </Tooltip>\n ) : null}\n {/* While a turn runs, Queue stays put — disabled until there is text — so typing never adds a button. */}\n {running && !queueAvailable ? null : (\n <button key={running ? 'queue' : 'send'} type=\"submit\" className={styles.send} disabled={!canSend} aria-busy={sending || undefined}\n aria-label={running ? (queueFull ? 'Queue is full' : 'Queue this message — it sends when the turn ends') : locked ? 'Send (unlocks first)' : 'Send message'}>\n {sendLabel}{returnKey}\n </button>\n )}\n </div>\n </div>\n </div>\n {cost ? (\n <p className={styles.cost} data-tone={cost.tone} role=\"status\">\n <span className={styles.costFigure}>≈{formatTokens(cost.draftTokens)}</span> tokens for this message —\n sending would reach <span className={styles.costFigure}>{cost.projectedPercent}%</span> of the context window.\n {costConsequence(cost)}\n <span className=\"visually-hidden\"> This is an estimate; the provider counts the real total.</span>\n </p>\n ) : null}\n <p id={helpId} className={`${styles.help} ${showHint || tooLong || listening || running || disabled || historyAt !== -1 || showHandoffHelp || note || controls.error || followUps.error ? '' : styles.helpQuiet}`}>\n {tooLong ? <span role=\"alert\" className={styles.helpError}>Message is over 64 KB — trim it before sending.</span>\n : followUps.error ? <span role=\"alert\" className={styles.helpError}>{followUps.error}</span>\n : controls.error && view ? <span role=\"alert\" className={styles.helpError}>{controls.error}</span>\n : note ? <span role={note.error ? 'alert' : 'status'} className={note.error ? styles.helpError : undefined}>{note.text}</span>\n : listening ? 'Listening… Esc stops dictation.'\n : disabled && disabledReason ? disabledReason\n : running ? (queueAvailable\n ? <><kbd>Enter</kbd> queues (sends when this turn ends) · <kbd>{shortcutLabel('composer.stop-and-send')}</kbd> stops and sends · <kbd>Esc</kbd> stops</>\n : <>Reply in progress — your draft stays here · <kbd>⌘.</kbd> or Stop interrupts the turn</>)\n : showHandoffHelp\n ? <>Handoff note drafted from the previous chat — review or edit it; nothing is spent until you press Send.</>\n : historyAt !== -1 ? <>Recalled message {historyAt + 1} of {history.current.length} · <kbd>↓</kbd> returns to your draft</>\n : showHint\n ? <><kbd>Enter</kbd> sends · <kbd>Shift</kbd>+<kbd>Enter</kbd> new line · <kbd>@</kbd> files · <kbd>/</kbd> commands · <kbd>↑</kbd> recalls</>\n : null}\n </p>\n {/* Mounted whenever there are pickers, not only while the row is\n compact: an open sheet must never be unmounted by a fold (it would\n drop focus to <body> and leave `sheetOpen` set with nothing on screen). */}\n {pickers ? (\n <ControlsSheet open={sheetOpen} onClose={() => { sheetReturnFocus.current = true; setSheetOpen(false); }}>\n {(() => {\n const list = pickers('list');\n return <>{list.permission}{list.model}{list.effort}</>;\n })()}\n </ControlsSheet>\n ) : null}\n <BypassConfirmDialog open={bypassAsk} chatLabel={chipLabel} running={running}\n onCancel={() => { setBypassAsk(false); textarea.current?.focus(); }} onConfirm={() => { void confirmBypass(); }} />\n <MutationTokenDialog {...gate.dialog} tokenLabel=\"Mutation token\" tokenHelp=\"the mutation token ashlr verse printed\" />\n </form>\n );\n}\n\n/** Render `node` into `host` when one is given (the queue's place above the live row), else in place. */\nfunction portalInto(host: HTMLElement | null, node: ReactNode) {\n return host ? createPortal(node, host) : node;\n}\n\nfunction labelOf<T extends string>(options: ReadonlyArray<{ id: T; label: string }> | undefined, id: T): string {\n return options?.find((o) => o.id === id)?.label ?? String(id);\n}\n\n/**\n * The sentence after the percentage: what actually happens if this is sent.\n * Worded from the compaction point when it is known, because that — not the\n * window — is where the agent starts losing detail.\n */\nexport function costConsequence(cost: CostHint): string {\n const bound = cost.exact ? '' : ' (The current size is an upper bound, so this may overstate it.)';\n if (cost.tone === 'over') return ` That is past the whole window — the CLI must compact first, or the turn fails. Continue in a fresh chat.${bound}`;\n if (cost.pastCompaction) return ` That reaches the auto-compaction point, so the CLI will summarise earlier turns during this reply. Continue in a fresh chat to keep full detail.${bound}`;\n if (cost.autoCompactAt !== null) {\n const left = Math.max(0, cost.autoCompactAt - cost.projectedTokens);\n return ` About ${formatTokens(left)} left before the CLI auto-compacts.${cost.tone === 'danger' ? ' Start a new chat to keep the agent sharp.' : ''}${bound}`;\n }\n return `${cost.tone === 'danger' ? ' Start a new chat to keep the agent sharp.' : ''}${bound}`;\n}\n","/**\n * routes/verse/chat/activity-model.ts — what a folded run of tool calls DID,\n * in one sentence (SPEC-310C §2 \"Activity groups\", unit C2).\n *\n * The 3.9 group line counted tool NAMES (\"6 tools · Read ×4, Edit ×2\"). A\n * name is the vendor's vocabulary, not the operator's: `Bash`, `exec_command`\n * and `run_shell` are all \"ran a command\", and nobody scanning a long session\n * wants to learn which CLI spells it how. So the group now speaks in actions,\n * using the same classification the file-activity summary uses\n * (tool-semantics `readToolFacts`). Drawn, the clauses are separated by a\n * middle dot; spoken, by commas:\n *\n * Ran 12 commands · read 8 files · edited 3 files (the row)\n * Ran 12 commands, read 8 files, edited 3 files; 1 failed, 1 running (its name)\n *\n * 3.10.1: every clause carries its noun. \"read 1\" after \"Ran 1 command\"\n * left the reader to work out what was read.\n *\n * and decides which member rows deserve to be on screen without a click: a\n * failed call is the one thing in a group the operator must see, and a\n * running call is the thing they are waiting on. Everything else stays folded\n * behind \"Show N more\".\n *\n * Pure — no React, no DOM — so the sentence and the auto-expand rule are\n * tested directly against transcript items.\n */\nimport type { ToolGroupItem, ToolGroupMember } from '../verse-store.js';\nimport { formatDuration } from '../verse-model.js';\nimport { actionForName, type ToolAction, type ToolFacts } from './tool-semantics.js';\n\n/** Per-action counts for one group. Thinking members are not counted: they are not work. */\nexport type ActionCounts = Record<ToolAction, number>;\n\nexport interface ActivitySummary {\n counts: ActionCounts;\n /** Tool calls only. */\n toolCount: number;\n failed: number;\n running: number;\n /** Wall time first→last member, when both are stamped; null = unknown (never 0). */\n spanMs: number | null;\n /** \"Ran 12 commands · read 8 files · edited 3 files\" — the WHAT, as drawn. */\n work: string;\n /** \"1 failed, 1 running\" — the STATE; empty when neither. */\n state: string;\n /** The whole line as a screen reader should hear it. */\n sentence: string;\n}\n\n/** Display order and wording; every clause names its noun. */\nconst CLAUSES: ReadonlyArray<{ action: ToolAction; verb: string; noun: string; plural: string }> = [\n { action: 'command', verb: 'ran', noun: 'command', plural: 'commands' },\n { action: 'read', verb: 'read', noun: 'file', plural: 'files' },\n { action: 'edit', verb: 'edited', noun: 'file', plural: 'files' },\n { action: 'create', verb: 'created', noun: 'file', plural: 'files' },\n { action: 'delete', verb: 'deleted', noun: 'file', plural: 'files' },\n { action: 'search', verb: 'searched', noun: 'time', plural: 'times' },\n { action: 'web', verb: 'fetched', noun: 'page', plural: 'pages' },\n { action: 'task', verb: 'ran', noun: 'subagent', plural: 'subagents' },\n { action: 'other', verb: 'used', noun: 'other tool', plural: 'other tools' },\n];\n\n/** The row's clause separator; the accessible sentence uses commas. */\nexport const WORK_SEPARATOR = ' · ';\n\nfunction emptyCounts(): ActionCounts {\n return { read: 0, edit: 0, create: 0, delete: 0, command: 0, search: 0, task: 0, web: 0, other: 0 };\n}\n\nfunction capitalize(text: string): string {\n return text.length === 0 ? text : `${text[0]!.toUpperCase()}${text.slice(1)}`;\n}\n\n/** The action a member performed: from its derived facts when present, else from its name. */\nfunction memberAction(member: Extract<ToolGroupMember, { kind: 'tool' }>, facts: ReadonlyMap<string, ToolFacts> | null): ToolAction {\n return facts?.get(member.toolUseId)?.action ?? actionForName(member.name);\n}\n\n/** A member failed: the tool said so, or its facts read a non-zero exit. */\nexport function memberFailed(member: ToolGroupMember, facts: ReadonlyMap<string, ToolFacts> | null = null): boolean {\n if (member.kind !== 'tool' || member.result === null) return false;\n return member.result.isError || facts?.get(member.toolUseId)?.failed === true;\n}\n\nexport function memberRunning(member: ToolGroupMember): boolean {\n return member.kind === 'tool' && member.result === null;\n}\n\n/** \"Ran 12 commands · read 8 files\" from counts (`separator` between clauses); \"\" when there is nothing. */\nexport function describeWork(counts: ActionCounts, separator: string = WORK_SEPARATOR): string {\n const parts: string[] = [];\n for (const clause of CLAUSES) {\n const n = counts[clause.action];\n if (n <= 0) continue;\n parts.push(`${clause.verb} ${n} ${n === 1 ? clause.noun : clause.plural}`);\n }\n return capitalize(parts.join(separator));\n}\n\nexport function summarizeActivity(group: Pick<ToolGroupItem, 'items' | 'spanMs'>, facts: ReadonlyMap<string, ToolFacts> | null = null): ActivitySummary {\n const counts = emptyCounts();\n let failed = 0;\n let running = 0;\n let toolCount = 0;\n for (const member of group.items) {\n if (member.kind !== 'tool') continue;\n toolCount += 1;\n counts[memberAction(member, facts)] += 1;\n if (memberRunning(member)) running += 1;\n else if (memberFailed(member, facts)) failed += 1;\n }\n const work = describeWork(counts);\n const stateParts: string[] = [];\n if (failed > 0) stateParts.push(`${failed} failed`);\n if (running > 0) stateParts.push(`${running} running`);\n const state = stateParts.join(', ');\n const spanMs = group.spanMs !== null && group.spanMs > 0 ? group.spanMs : null;\n const sentence = [describeWork(counts, ', '), state, spanMs !== null && running === 0 ? formatDuration(spanMs) : '']\n .filter((part) => part.length > 0)\n .join('; ');\n return { counts, toolCount, failed, running, spanMs, work, state, sentence };\n}\n\nexport type ActivityView = 'collapsed' | 'focus' | 'all';\n\n/**\n * How a group opens when the operator has not chosen: on its FOCUS rows\n * (failed or running members) when it has any, folded otherwise. A group\n * that finishes clean folds itself again — the operator's own toggle, once\n * made, wins over both (the component tracks that).\n */\nexport function defaultActivityView(summary: Pick<ActivitySummary, 'failed' | 'running'>): ActivityView {\n return summary.failed > 0 || summary.running > 0 ? 'focus' : 'collapsed';\n}\n\n/** Members shown in `focus` view, in order, and how many that leaves behind \"Show N more\". */\nexport function focusMembers(\n group: Pick<ToolGroupItem, 'items'>,\n facts: ReadonlyMap<string, ToolFacts> | null = null,\n): { shown: ToolGroupMember[]; hidden: number } {\n const shown = group.items.filter((m) => memberRunning(m) || memberFailed(m, facts));\n return { shown, hidden: group.items.length - shown.length };\n}\n","/**\n * routes/verse/chat/ActivityGroup.tsx — a run of tool calls as ONE row\n * (SPEC-310C §2, unit C2):\n *\n * ▸ Ran 12 commands · read 8 files · edited 3 files · 1 failed, 1 running · 2m 14s\n *\n * Three views, not two, because \"folded\" and \"everything\" both fail the\n * common case:\n *\n * collapsed the line alone — a clean, finished run is one line of prose;\n * focus the line plus ONLY the rows that matter right now — the calls\n * that failed and the ones still running (each with a live\n * timer) — and \"Show 11 more\" for the rest;\n * all every member.\n *\n * Until the operator clicks, the view follows the run: it opens on focus the\n * moment a call fails or while one runs, and folds itself when the run\n * finishes clean. Once they click, their choice wins for the life of the row.\n *\n * Failure is said in words and with the left rule, never by colour alone\n * (DESIGN §6). The line is a real button with aria-expanded, so the whole\n * disclosure works from the keyboard and reads as one sentence.\n */\nimport { memo, useEffect, useId, useMemo, useState, type ReactNode } from 'react';\nimport type { ToolGroupItem, ToolGroupMember } from '../verse-store.js';\nimport { formatDuration } from '../verse-model.js';\nimport { defaultActivityView, focusMembers, summarizeActivity, type ActivityView } from './activity-model.js';\nimport { LiveTimer } from './LiveTimer.js';\nimport { toolAnchorId, type ToolFacts } from './tool-semantics.js';\nimport styles from './ActivityGroup.module.css';\n\n// ---------------------------------------------------------------------------\n// Reveal: a jump to a call folded inside a group\n// ---------------------------------------------------------------------------\n//\n// The file-activity rows and \"jump to the first failure\" scroll to a tool\n// call's DOM anchor. A folded group does not render its members (a long\n// session would otherwise carry thousands of hidden cards), so the anchor\n// does not exist until the group opens. The transcript asks here first; the\n// group that holds the call opens itself to \"all\", and the transcript retries\n// the scroll on the next frame.\n\nconst revealListeners = new Set<(anchorId: string) => boolean>();\n\n/** Ask every mounted group to open if it holds `anchorId`. True when one did. */\nexport function revealAnchorInGroups(anchorId: string): boolean {\n let claimed = false;\n for (const listener of [...revealListeners]) if (listener(anchorId)) claimed = true;\n return claimed;\n}\n\nexport interface ActivityGroupProps {\n item: ToolGroupItem;\n facts: ReadonlyMap<string, ToolFacts>;\n /** Renders one member (a tool card or a reasoning block) — the transcript owns those components and their memoization. */\n renderMember: (member: ToolGroupMember) => ReactNode;\n}\n\nexport function ActivityGroupView({ item, facts, renderMember }: ActivityGroupProps) {\n const summary = useMemo(() => summarizeActivity(item, facts), [item, facts]);\n const [chosen, setChosen] = useState<ActivityView | null>(null);\n const view: ActivityView = chosen ?? defaultActivityView(summary);\n const bodyId = useId();\n\n const focus = useMemo(() => focusMembers(item, facts), [item, facts]);\n // \"focus\" with nothing left to focus on (the run finished clean) folds.\n const effective: ActivityView = view === 'focus' && focus.shown.length === 0 ? 'collapsed' : view;\n const members = effective === 'all' ? item.items : effective === 'focus' ? focus.shown : [];\n\n function toggle() {\n setChosen(effective === 'collapsed' ? (focus.shown.length > 0 ? 'focus' : 'all') : 'collapsed');\n }\n\n useEffect(() => {\n const listener = (anchorId: string) => {\n const holds = item.items.some((m) => m.kind === 'tool' && toolAnchorId(m.toolUseId) === anchorId);\n if (holds) setChosen('all');\n return holds;\n };\n revealListeners.add(listener);\n return () => { revealListeners.delete(listener); };\n }, [item]);\n\n const running = summary.running > 0;\n const firstAt = item.items[0]?.at ?? null;\n const spoken = summary.sentence || `${summary.toolCount} tool calls`;\n\n return (\n <div className={styles.group} data-view={effective} data-failed={summary.failed > 0 || undefined}\n data-running={running || undefined} data-state-key={`activity:${item.key}`}>\n <button type=\"button\" className={styles.line} aria-expanded={effective !== 'collapsed'}\n aria-controls={bodyId} aria-label={spoken} onClick={toggle}>\n <span className={styles.chevron} aria-hidden=\"true\" />\n <span className={styles.work} aria-hidden=\"true\">{summary.work || `${summary.toolCount} tool calls`}</span>\n {summary.state ? (\n <span className={styles.state} aria-hidden=\"true\">\n {summary.failed > 0 ? <span className={styles.failed}>{summary.failed} failed</span> : null}\n {summary.failed > 0 && running ? <span className={styles.sep}>, </span> : null}\n {running ? <span className={styles.running}><span className={styles.pulse} />{summary.running} running</span> : null}\n </span>\n ) : null}\n <span className={styles.time} aria-hidden=\"true\">\n {running\n ? <LiveTimer since={firstAt} />\n : summary.spanMs !== null ? formatDuration(summary.spanMs) : null}\n </span>\n </button>\n <div id={bodyId} className={styles.body} hidden={effective === 'collapsed'}>\n {effective === 'collapsed' ? null : (\n <>\n <ol className={styles.list}>\n {members.map((member) => (\n <li key={member.key} data-member={member.kind}>{renderMember(member)}</li>\n ))}\n </ol>\n {effective === 'focus' && focus.hidden > 0 ? (\n <button type=\"button\" className={styles.more} onClick={() => setChosen('all')}>\n Show {focus.hidden} more\n </button>\n ) : null}\n {effective === 'all' && focus.shown.length > 0 && focus.hidden > 0 ? (\n <button type=\"button\" className={styles.more} onClick={() => setChosen('focus')}>\n Show only {summary.failed > 0 ? 'failures' : 'running'}\n </button>\n ) : null}\n </>\n )}\n </div>\n </div>\n );\n}\n\n/** Memoized on identity; the transcript wraps ActivityGroupView with a content comparator instead. */\nexport const ActivityGroup = memo(ActivityGroupView);\n","/**\n * routes/verse/chat/turn-model.ts — a session as a list of TURNS.\n *\n * `verse-transcript.groupTranscriptItems()` already folds runs of tool calls into\n * one summary row; what it does not do is say where one exchange ends and\n * the next begins. Everything this file exists for needs that boundary: the\n * per-turn file-activity summary (the blast radius of one ask), the outline\n * that makes a fifty-turn session navigable, keyboard movement between\n * turns, and \"jump to the first error in this turn\".\n *\n * Pure over `groupTranscriptItems()`'s output — no React, no DOM, no\n * fetching — so the numbers in the summary can be tested directly against an\n * event log.\n */\nimport { parseUnifiedDiff } from '../../inbox/diff-parser.js';\nimport type { ToolGroupItem, TranscriptItem, TranscriptRenderItem } from '../verse-store.js';\nimport { readToolFacts, type ToolAction, type ToolFacts } from './tool-semantics.js';\n\nexport type TurnStatus = 'running' | 'ok' | 'error' | 'stopped';\n\n/**\n * Work that survives a transcript rebuild.\n *\n * `buildTurns` runs on EVERY streamed token: `useVerseSession` rebuilds\n * `state.events` per event, `buildTranscript` allocates fresh item objects from\n * it, and both of Transcript's memos therefore miss. Without this, every delta\n * re-derived the semantics of every tool call in the session — including a full\n * LCS diff per Edit payload, which allocates an `Int32Array` up to the 1200-line\n * cap. Fifty moderate edits cost hundreds of milliseconds of main-thread work\n * per frame, on the one surface whose frame budget is already spoken for.\n *\n * Caching is sound because `toolUseId` is a stable identity and a tool call's\n * `{name, input, result}` is immutable ONCE ITS RESULT HAS LANDED\n * (`verse-transcript.buildTranscript` replaces the item object exactly once, when the\n * `tool-result` event arrives). A cached entry derived while the call was still\n * pending is therefore the only one that can go stale, and it is the only one\n * recomputed.\n */\nexport interface TurnCache {\n facts: Map<string, ToolFacts>;\n /** `countDiffLines` over the same immutable diff text. */\n counts: Map<string, { additions: number; deletions: number }>;\n /** Flattened searchable text per tool call — the expensive half of the index. */\n toolText: Map<string, string>;\n}\n\nexport function createTurnCache(): TurnCache {\n return { facts: new Map(), counts: new Map(), toolText: new Map() };\n}\n\n/**\n * Facts for one tool call, reusing the cached instance whenever it is still\n * valid. Exported so a test can assert the second build recomputes nothing.\n */\nexport function cachedToolFacts(\n tool: { toolUseId: string; name: string; input: unknown; result: { output: string; isError: boolean } | null },\n cache: TurnCache | null,\n): ToolFacts {\n const hit = cache?.facts.get(tool.toolUseId);\n // A pending entry must be recomputed once the result lands; anything else is\n // derived from data that can no longer change.\n if (hit && !(hit.pending && tool.result !== null)) return hit;\n const fact = readToolFacts({ name: tool.name, input: tool.input, result: tool.result });\n if (cache) {\n cache.facts.set(tool.toolUseId, fact);\n cache.counts.delete(tool.toolUseId);\n cache.toolText.delete(tool.toolUseId);\n }\n return fact;\n}\n\nexport interface TurnFileEntry {\n path: string;\n reads: number;\n edits: number;\n creates: number;\n deletes: number;\n /** The strongest thing that happened to it: delete > create > edit > read. */\n action: Extract<ToolAction, 'read' | 'edit' | 'create' | 'delete'>;\n /** Tool call to scroll to when the row is clicked. */\n anchorToolUseId: string;\n /** Summed from the diffs the calls carried; 0 when none did. */\n additions: number;\n deletions: number;\n /** At least one call touching this file failed. */\n failed: boolean;\n}\n\nexport interface TurnBlock {\n key: string;\n turnId: string | null;\n items: TranscriptRenderItem[];\n /** The ask that opened the turn, for the outline. */\n prompt: string | null;\n /** ISO stamp of the first item. */\n at: string;\n files: TurnFileEntry[];\n toolCount: number;\n commandCount: number;\n /** Failing tool calls plus logged errors. */\n errorCount: number;\n /** Id of the first failing thing in the turn, for jump-to-error. */\n firstErrorAnchor: string | null;\n status: TurnStatus;\n durationMs: number | null;\n}\n\nexport interface TurnModel {\n turns: TurnBlock[];\n /** Facts keyed by toolUseId, so the cards do not re-derive them. */\n facts: Map<string, ToolFacts>;\n /** Every error anchor in document order — the transcript-level jump list. */\n errorAnchors: string[];\n}\n\n/** DOM id for a note (error / stopped / turn-done) so it can be scrolled to. */\nexport function noteAnchorId(key: string): string {\n return `verse-note-${key.replace(/[^A-Za-z0-9_-]/g, '')}`;\n}\n\n/** DOM id for one turn section. */\nexport function turnAnchorId(key: string): string {\n return `verse-turn-${key.replace(/[^A-Za-z0-9_-]/g, '')}`;\n}\n\nconst STRENGTH: Record<TurnFileEntry['action'], number> = { read: 0, edit: 1, create: 2, delete: 3 };\n\n/**\n * Additions/deletions for a unified diff, summed across every file in it.\n *\n * This was a flat scan that skipped any line starting with `---` or `+++` as a\n * file header. Inside a hunk those prefixes are CONTENT, not structure: a\n * deleted line reading `-- a/x` is emitted as `--- a/x`, a Markdown `---` rule\n * deleted is `----`, and a diff of a diff — what editing a `.patch` fixture or\n * running `git diff` over one produces — is made almost entirely of them. Every\n * such line was dropped, so the `+N −M` on the collapsed tool card undercounted\n * exactly the changes that are hardest to eyeball.\n *\n * Delegating to the shared parser (`routes/inbox/diff-parser.ts`) fixes that\n * and retires the second diff dialect: `DiffBlock` renders `file.additions` /\n * `file.deletions` out of this same parse, so the badge on the summary line and\n * the stats in the expanded block are now the same numbers by construction\n * rather than by two implementations happening to agree.\n *\n * Cost is real but bounded and paid once: `cachedDiffCounts` memoizes by\n * `toolUseId`, and the text is capped at `MAX_FACT_TEXT_CHARS` upstream.\n */\nexport function countDiffLines(text: string): { additions: number; deletions: number } {\n let additions = 0;\n let deletions = 0;\n for (const file of parseUnifiedDiff(text).files) {\n additions += file.additions;\n deletions += file.deletions;\n }\n return { additions, deletions };\n}\n\n/** `countDiffLines` over text that cannot change once cached. */\nfunction cachedDiffCounts(\n toolUseId: string,\n text: string,\n cache: TurnCache | null,\n): { additions: number; deletions: number } {\n const hit = cache?.counts.get(toolUseId);\n if (hit) return hit;\n const counts = countDiffLines(text);\n cache?.counts.set(toolUseId, counts);\n return counts;\n}\n\nfunction toolMembers(item: TranscriptRenderItem): Array<Extract<TranscriptItem, { kind: 'tool' }>> {\n if (item.kind === 'tool') return [item];\n if (item.kind === 'toolGroup') {\n return (item as ToolGroupItem).items.filter((m): m is Extract<TranscriptItem, { kind: 'tool' }> => m.kind === 'tool');\n }\n return [];\n}\n\n/**\n * A new turn opens on a `user` item, or on any item whose turnId differs\n * from the one being accumulated. Items that arrive before the first user\n * message (a resumed session's tail, a session-level error with a null\n * turnId) collect into a leading block rather than being dropped.\n */\nexport function buildTurns(\n items: readonly TranscriptRenderItem[],\n cache: TurnCache | null = null,\n): TurnModel {\n const facts = new Map<string, ToolFacts>();\n const errorAnchors: string[] = [];\n const turns: TurnBlock[] = [];\n\n let current: TranscriptRenderItem[] = [];\n const flush = () => {\n if (current.length === 0) return;\n turns.push(makeTurn(current, facts, errorAnchors, cache));\n current = [];\n };\n\n let turnId: string | null | undefined;\n for (const item of items) {\n const itemTurn = 'turnId' in item ? item.turnId : null;\n const boundary = item.kind === 'user' || (turnId !== undefined && itemTurn !== null && itemTurn !== turnId);\n if (boundary) flush();\n if (item.kind === 'user' || turnId === undefined || itemTurn !== null) turnId = itemTurn;\n current.push(item);\n }\n flush();\n\n return { turns, facts, errorAnchors };\n}\n\nfunction makeTurn(\n items: TranscriptRenderItem[],\n facts: Map<string, ToolFacts>,\n errorAnchors: string[],\n cache: TurnCache | null,\n): TurnBlock {\n const first = items[0]!;\n const byPath = new Map<string, TurnFileEntry>();\n let toolCount = 0;\n let commandCount = 0;\n let errorCount = 0;\n let firstErrorAnchor: string | null = null;\n let status: TurnStatus = 'running';\n let durationMs: number | null = null;\n let prompt: string | null = null;\n\n const noteError = (anchor: string) => {\n errorCount++;\n if (!firstErrorAnchor) firstErrorAnchor = anchor;\n errorAnchors.push(anchor);\n };\n\n for (const item of items) {\n if (item.kind === 'user' && prompt === null) prompt = item.text;\n\n for (const tool of toolMembers(item)) {\n toolCount++;\n const fact = cachedToolFacts(tool, cache);\n facts.set(tool.toolUseId, fact);\n if (fact.action === 'command') commandCount++;\n if (fact.failed) noteError(`verse-tool-${tool.toolUseId.replace(/[^A-Za-z0-9_-]/g, '')}`);\n\n const action = fact.action;\n if (action !== 'read' && action !== 'edit' && action !== 'create' && action !== 'delete') continue;\n const counts = fact.diff ? cachedDiffCounts(tool.toolUseId, fact.diff.text, cache) : { additions: 0, deletions: 0 };\n for (const path of fact.paths) {\n const entry = byPath.get(path) ?? {\n path,\n reads: 0, edits: 0, creates: 0, deletes: 0,\n action,\n anchorToolUseId: tool.toolUseId,\n additions: 0, deletions: 0,\n failed: false,\n };\n if (action === 'read') entry.reads++;\n else if (action === 'edit') entry.edits++;\n else if (action === 'create') entry.creates++;\n else entry.deletes++;\n if (STRENGTH[action] > STRENGTH[entry.action]) {\n entry.action = action;\n // The row should land on the call that CHANGED the file, not on the\n // read that happened to come first.\n entry.anchorToolUseId = tool.toolUseId;\n }\n if (action !== 'read') {\n entry.additions += counts.additions;\n entry.deletions += counts.deletions;\n }\n if (fact.failed) entry.failed = true;\n byPath.set(path, entry);\n }\n }\n\n switch (item.kind) {\n case 'error':\n noteError(noteAnchorId(item.key));\n status = 'error';\n break;\n case 'cancelled':\n status = 'stopped';\n break;\n case 'turn-done':\n durationMs = item.durationMs > 0 ? item.durationMs : durationMs;\n // A stop already explained itself; `ok:false` after it is not a\n // second failure (the same rule Transcript's `explained` set uses).\n if (item.ok) status = status === 'running' ? 'ok' : status;\n else if (status === 'running') {\n status = 'error';\n noteError(noteAnchorId(item.key));\n }\n break;\n default:\n break;\n }\n }\n\n return {\n key: first.key,\n turnId: 'turnId' in first ? first.turnId : null,\n items,\n prompt,\n at: first.at,\n // Strongest action first. The map is in first-touch order, and an agentic\n // turn reads widely before editing narrowly — so in read order the three\n // edits in a 40-file turn sit behind \"Show 35 more files\", hiding exactly\n // the rows whose `+N −M` justified the summary in the first place.\n files: [...byPath.values()].sort(\n (a, b) =>\n STRENGTH[b.action] - STRENGTH[a.action] ||\n b.additions + b.deletions - (a.additions + a.deletions) ||\n a.path.localeCompare(b.path),\n ),\n toolCount,\n commandCount,\n errorCount,\n firstErrorAnchor,\n status,\n durationMs,\n };\n}\n\n/** `12 files · 4 edited, 8 read` — the one-line form for a collapsed summary. */\nexport function describeFiles(files: readonly TurnFileEntry[]): string {\n if (files.length === 0) return '';\n const counts = { edited: 0, created: 0, deleted: 0, read: 0 };\n for (const file of files) {\n if (file.action === 'edit') counts.edited++;\n else if (file.action === 'create') counts.created++;\n else if (file.action === 'delete') counts.deleted++;\n else counts.read++;\n }\n const parts: string[] = [];\n if (counts.created) parts.push(`${counts.created} created`);\n if (counts.edited) parts.push(`${counts.edited} edited`);\n if (counts.deleted) parts.push(`${counts.deleted} deleted`);\n if (counts.read) parts.push(`${counts.read} read`);\n return parts.join(', ');\n}\n\n// ---------------------------------------------------------------------------\n// In-session search\n// ---------------------------------------------------------------------------\n\n/**\n * Everything in a turn that is worth searching, flattened once and cached on\n * the turn object. A rebuilt transcript produces new objects, so the WeakMap\n * lets the old strings go with them.\n */\nconst SEARCH_TEXT = new WeakMap<TurnBlock, string>();\n\n/** Per tool call — a build log should not make one turn megabytes of index. */\nconst TOOL_TEXT_LIMIT = 4000;\n\n/**\n * One tool call flattened for search. Cached by `toolUseId` under the same\n * immutability rule as the facts: `JSON.stringify` of a 4000-char payload per\n * call per keystroke is what made an active query cost the whole session.\n */\nfunction toolSearchText(\n item: Extract<TranscriptItem, { kind: 'tool' }>,\n cache: TurnCache | null,\n): string {\n const hit = cache?.toolText.get(item.toolUseId);\n if (hit !== undefined) return hit;\n const parts: string[] = [item.name];\n if (typeof item.input === 'string') parts.push(item.input.slice(0, TOOL_TEXT_LIMIT));\n else if (item.input) {\n try {\n parts.push(JSON.stringify(item.input).slice(0, TOOL_TEXT_LIMIT));\n } catch {\n /* a payload that will not stringify simply is not searchable */\n }\n }\n if (item.result) parts.push(item.result.output.slice(0, TOOL_TEXT_LIMIT));\n const text = parts.join('\\n');\n // Only cache a finished call: a pending one's output is still to come.\n if (cache && item.result) cache.toolText.set(item.toolUseId, text);\n return text;\n}\n\nexport function turnSearchText(turn: TurnBlock, cache: TurnCache | null = null): string {\n const cached = SEARCH_TEXT.get(turn);\n if (cached !== undefined) return cached;\n const parts: string[] = [];\n const visit = (item: TranscriptRenderItem | TranscriptItem): void => {\n switch (item.kind) {\n case 'user':\n case 'assistant':\n case 'thinking':\n parts.push(item.text);\n break;\n case 'error':\n parts.push(item.message);\n break;\n case 'tool':\n parts.push(toolSearchText(item, cache));\n break;\n case 'toolGroup':\n for (const member of (item as ToolGroupItem).items) visit(member);\n break;\n default:\n break;\n }\n };\n for (const item of turn.items) visit(item);\n const text = parts.join('\\n');\n SEARCH_TEXT.set(turn, text);\n return text;\n}\n\nexport interface TurnMatch {\n turnKey: string;\n /** Text around the first hit, for the outline row. */\n snippet: string;\n}\n\nconst SNIPPET_PAD = 48;\n\n/** Case-insensitive substring search across every turn, in document order. */\nexport function searchTurns(\n turns: readonly TurnBlock[],\n query: string,\n cache: TurnCache | null = null,\n): TurnMatch[] {\n const needle = query.trim().toLowerCase();\n if (needle.length === 0) return [];\n const out: TurnMatch[] = [];\n for (const turn of turns) {\n const haystack = turnSearchText(turn, cache);\n const at = haystack.toLowerCase().indexOf(needle);\n if (at === -1) continue;\n const from = Math.max(0, at - SNIPPET_PAD);\n const to = Math.min(haystack.length, at + needle.length + SNIPPET_PAD);\n const snippet = `${from > 0 ? '…' : ''}${haystack.slice(from, to).replace(/\\s+/g, ' ').trim()}${to < haystack.length ? '…' : ''}`;\n out.push({ turnKey: turn.key, snippet });\n }\n return out;\n}\n\n/** Turn title for the outline: the ask, trimmed to one line. */\nexport function turnTitle(turn: TurnBlock, max = 72): string {\n const source = turn.prompt?.replace(/\\s+/g, ' ').trim();\n if (source) return source.length > max ? `${source.slice(0, max - 1)}…` : source;\n if (turn.toolCount > 0) return `${turn.toolCount} tool call${turn.toolCount === 1 ? '' : 's'}`;\n return 'Session note';\n}\n","/**\n * routes/verse/chat/chapter-model.ts — the ChapterRail's data: one tick per\n * turn the operator started, plus the moments the conversation changed shape\n * (SPEC-310C §2 \"ChapterRail\", unit C2).\n *\n * A fifty-turn chat is a long scroll with no landmarks; the outline\n * (TranscriptNav) lists turns but takes the width of the transcript to do\n * it. The rail is the glanceable version: a minimap down the right edge\n * where each ask is a tick — red if its turn failed, amber while it runs —\n * and where the context was compacted, the chat was recovered, or it began\n * as a handoff, a marker says so. Position is by ORDER (turn i of n), not by\n * pixel: the rail answers \"where in the conversation\", and a turn with a\n * 2,000-line build log should not own half of it.\n *\n * Pure over the transcript's TurnBlocks.\n */\nimport type { TurnBlock, TurnStatus } from './turn-model.js';\nimport { turnTitle } from './turn-model.js';\n\nexport type ChapterMarkerKind = 'compaction' | 'recovered' | 'handoff';\n\nexport interface ChapterTick {\n /** The turn's key — what the transcript jumps to. */\n turnKey: string;\n /** 0-based ordinal among ticks. */\n index: number;\n status: TurnStatus;\n /** The ask, one line, for the hover card. */\n title: string;\n /** 0–1 down the rail. */\n position: number;\n /** Markers that fall inside this turn, in order. */\n markers: ChapterMarkerKind[];\n}\n\nexport interface ChapterModel {\n ticks: ChapterTick[];\n /** The chat began as a handoff: a marker above the first tick. */\n handoff: boolean;\n counts: { failed: number; running: number; compactions: number; recoveries: number };\n}\n\n/** Markers carried by a turn's items. Compaction/recovered items can sit in any turn. */\nfunction markersOf(turn: TurnBlock): ChapterMarkerKind[] {\n const out: ChapterMarkerKind[] = [];\n for (const item of turn.items) {\n if (item.kind === 'compaction') out.push('compaction');\n else if (item.kind === 'recovered') out.push('recovered');\n }\n return out;\n}\n\n/**\n * Ticks for turns with an ask. A turn with no prompt (the notes before the\n * first ask, a truncated head) carries no tick of its own; its markers ride\n * on the next tick so a compaction is never dropped from the rail.\n */\nexport function buildChapters(turns: readonly TurnBlock[], opts: { handoff?: boolean } = {}): ChapterModel {\n const ticks: ChapterTick[] = [];\n let carried: ChapterMarkerKind[] = [];\n const counts = { failed: 0, running: 0, compactions: 0, recoveries: 0 };\n for (const turn of turns) {\n const markers = [...carried, ...markersOf(turn)];\n for (const marker of markersOf(turn)) {\n if (marker === 'compaction') counts.compactions += 1;\n else if (marker === 'recovered') counts.recoveries += 1;\n }\n if (turn.prompt === null) {\n carried = markers;\n continue;\n }\n carried = [];\n if (turn.status === 'error') counts.failed += 1;\n if (turn.status === 'running') counts.running += 1;\n ticks.push({ turnKey: turn.key, index: ticks.length, status: turn.status, title: turnTitle(turn, 120), position: 0, markers });\n }\n // Markers after the last ask (a compaction that closed the log) attach to it.\n if (carried.length > 0 && ticks.length > 0) ticks[ticks.length - 1]!.markers.push(...carried);\n const n = ticks.length;\n for (const tick of ticks) tick.position = n <= 1 ? 0 : tick.index / (n - 1);\n return { ticks, handoff: opts.handoff === true, counts };\n}\n\nexport const CHAPTER_STATUS_WORD: Readonly<Record<TurnStatus, string>> = {\n running: 'running',\n ok: 'done',\n error: 'failed',\n stopped: 'stopped',\n};\n\nexport const CHAPTER_MARKER_WORD: Readonly<Record<ChapterMarkerKind, string>> = {\n compaction: 'context compacted',\n recovered: 'recovered',\n handoff: 'continued from another chat',\n};\n\n/** The accessible name of one tick: \"Turn 4 of 12, failed: Fix the login test (context compacted)\". */\nexport function describeTick(tick: ChapterTick, total: number): string {\n const extra = tick.markers.length > 0 ? ` (${[...new Set(tick.markers)].map((m) => CHAPTER_MARKER_WORD[m]).join(', ')})` : '';\n return `Turn ${tick.index + 1} of ${total}, ${CHAPTER_STATUS_WORD[tick.status]}: ${tick.title}${extra}`;\n}\n\n// ---------------------------------------------------------------------------\n// Streaming cost: the rail must not re-render per token\n// ---------------------------------------------------------------------------\n\nconst settledSignatures = new WeakMap<TurnBlock, string>();\n\nfunction turnSignature(turn: TurnBlock): string {\n const cached = settledSignatures.get(turn);\n if (cached !== undefined) return cached;\n let markers = 0;\n for (const item of turn.items) if (item.kind === 'compaction' || item.kind === 'recovered') markers += 1;\n const sig = `${turn.key}:${turn.status}:${markers}:${turn.prompt === null ? 0 : turn.prompt.length}`;\n // A settled turn's TurnBlock never changes (the segment cache hands back the\n // same object), so its signature is computed once; the live turn's each time.\n if (turn.status !== 'running') settledSignatures.set(turn, sig);\n return sig;\n}\n\n/**\n * What the rail draws depends on each turn's key, status, markers and ask —\n * none of which a streamed token changes. The transcript rebuilds its turn\n * list on every token, so it compares this signature (cheap: cached per\n * settled turn) and rebuilds the rail's model only when it moves.\n */\nexport function chaptersSignature(turns: readonly TurnBlock[], handoff: boolean): string {\n let out = handoff ? 'h' : '-';\n for (const turn of turns) out += `|${turnSignature(turn)}`;\n return out;\n}\n","/**\n * routes/verse/chat/ChapterRail.tsx — the transcript's minimap down its\n * right edge (SPEC-310C §2, unit C2). Model: chapter-model.ts.\n *\n * ─ a turn ━ red: it failed ━ amber: running\n * ◆ context compacted ↺ recovered ⤷ continued from another chat\n * (the three markers are inline SVG — see MARKER_PATH)\n *\n * Hover (or keyboard focus) shows the ask; click jumps to it.\n *\n * ONE tab stop, not one per turn: a 60-turn chat must not put 60 stops\n * between the transcript and the composer. The rail is a toolbar-style\n * group with a roving tabindex — ↑/↓ (and Home/End) move between ticks,\n * Enter or Space jumps. Every tick has a full accessible name (\"Turn 4 of\n * 12, failed: …\"), so the colour is never the only signal (DESIGN §6).\n */\nimport { memo, useCallback, useId, useRef, useState, type KeyboardEvent, type ReactElement } from 'react';\nimport { describeTick, type ChapterMarkerKind, type ChapterModel } from './chapter-model.js';\nimport styles from './ChapterRail.module.css';\n\nexport interface ChapterRailProps {\n /**\n * From chapter-model `buildChapters`. The transcript rebuilds it only when\n * `chaptersSignature` moves, so a streamed token never re-renders the rail.\n */\n model: ChapterModel;\n onJumpTurn: (turnKey: string) => void;\n}\n\n/**\n * The markers are drawn, not typed. WHY (review 3.10 c18, SPEC-310A §1 fonts\n * ≤ 150 KB): as text, ◆ ⤷ are in neither UI font and ↺ is only in the 230 KB\n * full Plex face, so a chat with one compaction or recovery made the browser\n * fetch that face on first paint just to draw a 6px mark. A 1em SVG in\n * currentColor keeps the size and the per-kind colour the CSS already sets.\n */\nconst MARKER_PATH: Readonly<Record<ChapterMarkerKind, ReactElement>> = {\n // ◆ a filled diamond: the context was compacted here.\n compaction: <path d=\"M8 2.5 13.5 8 8 13.5 2.5 8Z\" fill=\"currentColor\" />,\n // ↺ an open circle with its arrowhead: recovered.\n recovered: <path d=\"M4.2 5.2A5 5 0 1 1 3 8.5M4.2 1.8v3.4h3.4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />,\n // ⤷ down, then right: continued from another chat.\n handoff: <path d=\"M4 2.5V9a2 2 0 0 0 2 2h7M10.5 8.5 13 11l-2.5 2.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />,\n};\n\nfunction MarkerIcon({ kind }: { kind: ChapterMarkerKind }) {\n return <svg className={styles.markerIcon} viewBox=\"0 0 16 16\" width=\"1em\" height=\"1em\" aria-hidden=\"true\" focusable=\"false\">{MARKER_PATH[kind]}</svg>;\n}\n\nexport const ChapterRail = memo(function ChapterRail({ model, onJumpTurn }: ChapterRailProps) {\n const [active, setActive] = useState(-1);\n const [peek, setPeek] = useState<number | null>(null);\n const tickRefs = useRef(new Map<number, HTMLButtonElement>());\n const cardId = useId();\n const total = model.ticks.length;\n // The roving stop: the operator's last tick, else the newest turn.\n const stop = active >= 0 && active < total ? active : total - 1;\n\n const focusTick = useCallback((index: number) => {\n const clamped = Math.max(0, Math.min(total - 1, index));\n setActive(clamped);\n setPeek(clamped);\n tickRefs.current.get(clamped)?.focus();\n }, [total]);\n\n function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n const current = stop;\n let next: number | null = null;\n if (event.key === 'ArrowDown' || event.key === 'ArrowRight') next = current + 1;\n else if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') next = current - 1;\n else if (event.key === 'Home') next = 0;\n else if (event.key === 'End') next = total - 1;\n if (next === null) return;\n event.preventDefault();\n focusTick(next);\n }\n\n if (total === 0) return null;\n const peeked = peek !== null ? model.ticks[peek] ?? null : null;\n\n return (\n <div className={styles.rail} role=\"toolbar\" aria-orientation=\"vertical\" aria-label=\"Chapters\"\n onKeyDown={onKeyDown} onMouseLeave={() => setPeek(null)}>\n {model.handoff ? (\n <span className={styles.handoff} role=\"img\" aria-label=\"Continued from another chat\" title=\"Continued from another chat\">\n <MarkerIcon kind=\"handoff\" />\n </span>\n ) : null}\n <div className={styles.track}>\n {model.ticks.map((tick) => {\n const name = describeTick(tick, total);\n const markers = [...new Set(tick.markers)];\n return (\n <button\n key={tick.turnKey}\n ref={(node) => { if (node) tickRefs.current.set(tick.index, node); else tickRefs.current.delete(tick.index); }}\n type=\"button\"\n className={styles.tick}\n data-status={tick.status}\n style={{ top: `${tick.position * 100}%` }}\n tabIndex={tick.index === stop ? 0 : -1}\n aria-label={name}\n aria-describedby={peek === tick.index ? cardId : undefined}\n onMouseEnter={() => setPeek(tick.index)}\n onFocus={() => { setActive(tick.index); setPeek(tick.index); }}\n onBlur={() => setPeek((p) => (p === tick.index ? null : p))}\n onClick={() => { setActive(tick.index); onJumpTurn(tick.turnKey); }}\n >\n <span className={styles.bar} aria-hidden=\"true\" />\n {markers.length > 0 ? (\n <span className={styles.markers} aria-hidden=\"true\">\n {markers.map((m) => <span key={m} className={styles.marker} data-marker={m}><MarkerIcon kind={m} /></span>)}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n {peeked ? (\n <div id={cardId} className={styles.card} role=\"tooltip\" style={{ top: `${peeked.position * 100}%` }}>\n <span className={styles.cardMeta}>Turn {peeked.index + 1} of {total}{peeked.status === 'error' ? ' · failed' : peeked.status === 'running' ? ' · running' : ''}</span>\n <span className={styles.cardTitle}>{peeked.title}</span>\n </div>\n ) : null}\n </div>\n );\n});\n","/**\n * routes/verse/chat/FileActivity.tsx — the blast radius of one turn.\n *\n * The question this answers is the first one asked of any agentic turn:\n * *what did it touch?* Today that costs expanding every tool card and\n * reading JSON. Here it is one compact list — which files were read,\n * edited, created or deleted, with counts and line deltas — and clicking a\n * row scrolls to the call that changed it.\n *\n * DESIGN §6: the action is a WORD, never only the tint on the rule. The\n * 2px left rule repeats the same information for scanning, and the row's\n * accessible name spells it out in full.\n */\nimport { useState } from 'react';\nimport { Tooltip } from '../../../components/primitives/Tooltip.js';\nimport { useDisplayPath } from './path-display.js';\nimport { fileBasename, fileDirname } from './tool-semantics.js';\nimport { describeFiles, type TurnFileEntry } from './turn-model.js';\nimport styles from './chat.module.css';\n\n/** Beyond this the list collapses; a big refactor should not be a wall. */\nconst COLLAPSE_AFTER = 8;\n\nconst VERB: Record<TurnFileEntry['action'], string> = {\n read: 'read',\n edit: 'edited',\n create: 'created',\n delete: 'deleted',\n};\n\nexport interface FileActivityProps {\n files: readonly TurnFileEntry[];\n /** Scroll to the tool call that did the work. */\n onJump: (toolUseId: string) => void;\n}\n\nexport function FileActivity({ files, onJump }: FileActivityProps) {\n const [expanded, setExpanded] = useState(false);\n const show = useDisplayPath();\n if (files.length === 0) return null;\n const visible = expanded || files.length <= COLLAPSE_AFTER ? files : files.slice(0, COLLAPSE_AFTER);\n const hidden = files.length - visible.length;\n\n return (\n <section className={styles.activity} aria-label=\"Files this turn touched\">\n <h3 className={styles.activityHead}>\n <span className={styles.activityLabel}>{files.length} file{files.length === 1 ? '' : 's'}</span>\n <span className={styles.activityBreakdown}>{describeFiles(files)}</span>\n </h3>\n <ul className={styles.activityList}>\n {visible.map((file) => {\n const times = file.reads + file.edits + file.creates + file.deletes;\n // Relative to the chat's roots (chat/path-display.ts): the raw path\n // is usually absolute, and its directory half is what the row clips.\n const shown = show(file.path);\n const name = `${VERB[file.action]} ${shown}${times > 1 ? `, ${times} calls` : ''}${\n file.additions || file.deletions ? `, +${file.additions} −${file.deletions}` : ''\n }${file.failed ? ', a call on this file failed' : ''}`;\n return (\n <li key={file.path}>\n {/* The row draws the basename in full and lets the DIRECTORY\n clip, which is the half that tells two same-named files\n apart. The full, absolute path is the tooltip, on focus as\n well as hover. */}\n <Tooltip label={file.path} placement=\"right\">\n <button type=\"button\" className={styles.activityRow} data-action={file.action}\n data-failed={file.failed ? 'true' : undefined}\n onClick={() => onJump(file.anchorToolUseId)} aria-label={name}>\n <span className={styles.activityRule} aria-hidden=\"true\" />\n <span className={styles.activityVerb}>{VERB[file.action]}</span>\n <span className={styles.activityPath}>\n <span className={styles.activityName}>{fileBasename(shown)}</span>\n <span className={styles.activityDir}>{fileDirname(shown)}</span>\n </span>\n {file.failed ? (\n // DESIGN §6: the danger tint on the 2px rule was the ONLY\n // difference between a failed row and a successful one, and\n // it is the same rule once hue is removed. The word is what\n // actually carries it.\n <span className={styles.activityFailed} aria-hidden=\"true\">! failed</span>\n ) : null}\n {times > 1 ? <span className={styles.activityTimes}>×{times}</span> : null}\n <span className={styles.activityDelta}>\n {file.additions > 0 ? <span className={styles.activityAdd}>+{file.additions}</span> : null}\n {file.deletions > 0 ? <span className={styles.activityDel}>−{file.deletions}</span> : null}\n </span>\n </button>\n </Tooltip>\n </li>\n );\n })}\n </ul>\n {hidden > 0 || expanded ? (\n <button type=\"button\" className={styles.activityMore} aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}>\n {expanded ? 'Show fewer files' : `Show ${hidden} more file${hidden === 1 ? '' : 's'}`}\n </button>\n ) : null}\n </section>\n );\n}\n","/**\n * routes/verse/chat/ThinkingBlock.tsx — one block of model reasoning (V3.10).\n *\n * Before 3.10 reasoning arrived only when a block closed, behind a static\n * \"Thinking\" disclosure, usually two collapses deep inside a tool group — and\n * for Claude Opus it was almost always EMPTY (the CLI omitted the text), so\n * nothing showed at all. Now the adapters stream vendor summaries\n * (`thinking-delta`), and this block:\n *\n * - streams them while the model thinks, open, with a live\n * \"Thinking · 12s · ~1.8k tok\" line;\n * - settles to a collapsed \"Thought 12s · ~1.8k tok\" once the block ends,\n * the operator's own open/closed choice winning over the default;\n * - says honestly when the provider withheld the text (a redacted,\n * signature-only block): the duration is real, the words are not shown,\n * and there is nothing to expand.\n *\n * 3.10 (SPEC-310C §2, unit C2): while it streams, the body is a THREE-LINE\n * window that follows the tail — enough to see what the model is working\n * on, not so much that a long think shoves the answer off screen. Clicking\n * the label (or the window) shows the whole stream. Once the block ends it\n * folds to \"Thought 12s · ~1.8k tok ▸\" — or stays open when Settings ▸ Chat\n * says Expanded (chat/reasoning-pref.ts; Hidden is handled by the\n * transcript, which then renders no reasoning at all).\n *\n * Honesty rule: an unknown duration or token count is omitted, never shown\n * as 0. A token count the CLI did not report is estimated from the text\n * (~4 characters per token) and always carries the \"~\".\n *\n * 3.10.1: the row opens with the same ▸ every foldable transcript row uses\n * (tool cards, activity groups), at the same x; the breathing dot appears\n * only while the block streams — a settled block has no stray bullet.\n */\nimport { memo, useEffect, useLayoutEffect, useRef, useState, type MouseEvent, type SyntheticEvent } from 'react';\nimport type { VerseThinkingKind } from '../../../../core/verse/types.js';\nimport styles from './ThinkingBlock.module.css';\n\nexport interface ThinkingBlockProps {\n text: string;\n /** Still streaming: the label counts up and the body follows new text. */\n streaming?: boolean;\n /** Client clock (ms) the block started — the live label's origin while streaming. */\n startedAt?: number | null;\n /** The provider sent the block without its text. */\n redacted?: boolean;\n durationMs?: number | null;\n /** The CLI's own estimate; null → estimated from the text when there is any. */\n estimatedTokens?: number | null;\n kind?: VerseThinkingKind | null;\n /** Initial open state; the operator's toggle wins afterwards. */\n defaultOpen?: boolean;\n /** Survives scroll restore (useScrollRestore) like every other disclosure. */\n stateKey?: string;\n}\n\n/** \"~1.8k tok\" — one decimal under 10k, so the figure moves visibly while it streams. */\nexport function formatThinkingTokens(n: number): string {\n const rounded = Math.max(0, Math.round(n));\n if (rounded < 1000) return `~${rounded} tok`;\n if (rounded < 10_000) return `~${(rounded / 1000).toFixed(1).replace(/\\.0$/, '')}k tok`;\n if (rounded < 1_000_000) return `~${Math.round(rounded / 1000)}k tok`;\n return `~${(rounded / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M tok`;\n}\n\n/** \"12s\" / \"1m 5s\" / \"<1s\". */\nexport function formatThinkingDuration(ms: number): string {\n if (!Number.isFinite(ms) || ms < 0) return '';\n if (ms < 1000) return '<1s';\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${s % 60}s`;\n return `${Math.floor(m / 60)}h ${m % 60}m`;\n}\n\n/** Characters per token for the fallback estimate (the common English ratio). */\nconst CHARS_PER_TOKEN = 4;\n\nexport function thinkingTokenFigure(text: string, estimatedTokens: number | null | undefined): number | null {\n if (typeof estimatedTokens === 'number' && Number.isFinite(estimatedTokens) && estimatedTokens > 0) return estimatedTokens;\n const chars = text.trim().length;\n return chars > 0 ? Math.ceil(chars / CHARS_PER_TOKEN) : null;\n}\n\n/** The one-line label — exported so the transcript search and tests agree with the UI. */\nexport function thinkingLabel(opts: {\n streaming: boolean;\n elapsedMs: number | null;\n tokens: number | null;\n}): string {\n const parts = [opts.streaming ? 'Thinking' : 'Thought'];\n const duration = opts.elapsedMs === null ? '' : formatThinkingDuration(opts.elapsedMs);\n if (opts.streaming) {\n if (duration) parts.push(duration);\n } else if (duration) {\n parts[0] = `Thought ${duration}`;\n }\n if (opts.tokens !== null) parts.push(formatThinkingTokens(opts.tokens));\n return parts.join(' · ');\n}\n\n/** Re-render once a second while `active` — the label's clock, not a network poll. */\nfunction useSecondTick(active: boolean): number {\n const [now, setNow] = useState(() => Date.now());\n useEffect(() => {\n if (!active) return undefined;\n setNow(Date.now());\n const id = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(id);\n }, [active]);\n return now;\n}\n\n/** How close to the bottom the body must be for new text to keep it pinned there. */\nconst FOLLOW_PX = 24;\n\nexport const ThinkingBlock = memo(function ThinkingBlock({\n text,\n streaming = false,\n startedAt = null,\n redacted = false,\n durationMs = null,\n estimatedTokens = null,\n kind = null,\n defaultOpen = false,\n stateKey,\n}: ThinkingBlockProps) {\n const [open, setOpen] = useState(defaultOpen);\n /** The operator opened or closed it by hand; from then on their choice wins over `defaultOpen`. */\n const chosen = useRef(false);\n /** The operator asked for the whole stream instead of the three-line window. */\n const [full, setFull] = useState(false);\n const body = useRef<HTMLDivElement>(null);\n const following = useRef(true);\n const now = useSecondTick(streaming && startedAt !== null);\n\n const elapsed = streaming\n ? (startedAt !== null ? Math.max(0, now - startedAt) : null)\n : durationMs;\n const tokens = thinkingTokenFigure(text, estimatedTokens);\n const label = thinkingLabel({ streaming, elapsedMs: elapsed, tokens });\n const hasText = text.trim().length > 0;\n\n // Follow the stream inside the body unless the operator scrolled up in it.\n const windowed = streaming && open && !full;\n useLayoutEffect(() => {\n const node = body.current;\n if (!node) return;\n // Fade the top line only when something is actually cut off above it.\n if (windowed && node.scrollHeight > node.clientHeight + 1) node.setAttribute('data-clipped', '');\n else node.removeAttribute('data-clipped');\n if (!streaming || (!following.current && !windowed)) return;\n node.scrollTop = node.scrollHeight;\n }, [text, streaming, open, windowed]);\n\n function onScroll() {\n const node = body.current;\n if (!node) return;\n following.current = node.scrollHeight - node.scrollTop - node.clientHeight <= FOLLOW_PX;\n }\n\n // Open while its turn runs, folded once it finishes — unless the operator\n // already decided for this block.\n useEffect(() => {\n if (!chosen.current) setOpen(defaultOpen);\n }, [defaultOpen]);\n\n // While windowed, the first click on the label means \"show me all of it\",\n // not \"hide it\": folding the one thing the operator is trying to read\n // would be the opposite of the gesture.\n function onSummaryClick(event: MouseEvent<HTMLElement>) {\n if (!windowed) return;\n event.preventDefault();\n chosen.current = true;\n setFull(true);\n }\n\n function onToggle(event: SyntheticEvent<HTMLDetailsElement>) {\n const next = event.currentTarget.open;\n // A toggle event also follows OUR prop change; only a mismatch is a click.\n if (next !== open) chosen.current = true;\n setOpen(next);\n }\n\n if (!hasText) {\n // Nothing to expand: a redacted block, or the CLI's token count before\n // any summary text arrived. One quiet line that still says it happened.\n return (\n <div className={styles.thinking} data-streaming={streaming || undefined} data-redacted={redacted || undefined}\n data-thinking-kind={kind ?? undefined}>\n <span className={styles.line}>\n {streaming ? <span className={styles.glyph} aria-hidden=\"true\" /> : null}\n <span className={styles.label}>{label}</span>\n {redacted ? (\n <span className={styles.hidden} title=\"The provider returned this reasoning without its text; only how long it took is known.\">\n reasoning hidden by the provider\n </span>\n ) : null}\n </span>\n </div>\n );\n }\n\n return (\n <details className={styles.thinking} open={open} onToggle={onToggle} data-state-key={stateKey}\n data-streaming={streaming || undefined} data-thinking-kind={kind ?? undefined}>\n <summary className={styles.summary} onClick={onSummaryClick} aria-label={windowed ? `${label} — show all` : undefined}>\n <span className={styles.chevron} aria-hidden=\"true\" />\n {streaming ? <span className={styles.glyph} aria-hidden=\"true\" /> : null}\n <span className={styles.label}>{label}</span>\n {kind === 'summary' ? <span className={styles.kind} title=\"A summary the provider wrote of the model's reasoning, not its raw chain of thought.\">summary</span> : null}\n </summary>\n <div ref={body} className={styles.body} onScroll={onScroll} data-window={windowed || undefined}\n onClick={windowed ? () => { chosen.current = true; setFull(true); } : undefined}>{text}</div>\n </details>\n );\n});\n","/**\n * routes/verse/chat/TranscriptNav.tsx — making a fifty-turn session\n * navigable.\n *\n * A long agentic session scrolls past the point where scrolling is a way to\n * find anything. This strip gives it three handles: an outline of the turns\n * (with what each one touched), a search over everything in the transcript —\n * prose, tool arguments and tool output alike — and a jump straight to the\n * first failure.\n *\n * It is chrome, so it obeys DESIGN §4: one hairline-bottomed row at\n * `--density-row`, ghost controls only, no filled buttons. It renders only\n * when the session is long enough to need it, so a two-turn chat stays as\n * bare as it is today.\n */\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { ChevronIcon, SearchIcon } from '../verse-icons.js';\nimport { turnTitle, type TurnBlock, type TurnMatch } from './turn-model.js';\nimport styles from './chat.module.css';\n\nexport interface TranscriptNavProps {\n turns: readonly TurnBlock[];\n query: string;\n onQuery: (value: string) => void;\n matches: readonly TurnMatch[];\n /** Index into `matches`, or -1 when nothing is selected yet. */\n matchIndex: number;\n onStepMatch: (delta: number) => void;\n errorCount: number;\n onJumpError: () => void;\n onJumpTurn: (turnKey: string) => void;\n /** Focus hand-off: the transcript raises this when ⌘F is pressed. */\n focusToken: number;\n}\n\nconst STATUS_GLYPH: Record<TurnBlock['status'], string> = {\n running: '•',\n ok: '',\n error: '!',\n stopped: '×',\n};\n\nconst STATUS_WORD: Record<TurnBlock['status'], string> = {\n running: 'running',\n ok: 'completed',\n error: 'failed',\n stopped: 'stopped',\n};\n\nexport function TranscriptNav(props: TranscriptNavProps) {\n const { turns, query, onQuery, matches, matchIndex, onStepMatch, errorCount, onJumpError, onJumpTurn, focusToken } = props;\n const [outlineOpen, setOutlineOpen] = useState(false);\n const search = useRef<HTMLInputElement>(null);\n const outlineId = useId();\n const searchId = useId();\n const searching = query.trim().length > 0;\n\n useEffect(() => {\n if (focusToken > 0) {\n search.current?.focus();\n search.current?.select();\n }\n }, [focusToken]);\n\n // Close the outline on Escape from anywhere inside it.\n useEffect(() => {\n if (!outlineOpen) return;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') setOutlineOpen(false);\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [outlineOpen]);\n\n const listed = searching\n ? turns.filter((turn) => matches.some((m) => m.turnKey === turn.key))\n : turns;\n const snippetFor = (key: string) => matches.find((m) => m.turnKey === key)?.snippet ?? null;\n\n return (\n <div className={styles.nav}>\n <button type=\"button\" className={styles.navOutlineButton} aria-expanded={outlineOpen}\n aria-controls={outlineOpen ? outlineId : undefined} onClick={() => setOutlineOpen((v) => !v)}>\n <span className={styles.navChevron} data-open={outlineOpen ? 'true' : undefined} aria-hidden=\"true\">\n <ChevronIcon size={12} />\n </span>\n <span className={styles.navCount}>{turns.length}</span>\n <span className={styles.navWord}>turn{turns.length === 1 ? '' : 's'}</span>\n </button>\n\n <label className={styles.navSearch} htmlFor={searchId}>\n <span className={styles.navSearchIcon} aria-hidden=\"true\"><SearchIcon size={12} /></span>\n <span className=\"visually-hidden\">Search this chat</span>\n <input ref={search} id={searchId} type=\"search\" value={query} placeholder=\"Find in chat\" autoComplete=\"off\"\n onChange={(event) => onQuery(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n onStepMatch(event.shiftKey ? -1 : 1);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n onQuery('');\n }\n }} />\n </label>\n\n {searching ? (\n <span className={styles.navMatches} role=\"status\">\n {matches.length === 0\n ? 'no matches'\n : `${matchIndex < 0 ? 1 : matchIndex + 1} of ${matches.length}`}\n </span>\n ) : null}\n {searching && matches.length > 0 ? (\n <span className={styles.navStep}>\n <button type=\"button\" onClick={() => onStepMatch(-1)} aria-label=\"Previous match\" title=\"Previous match (Shift+Enter)\">↑</button>\n <button type=\"button\" onClick={() => onStepMatch(1)} aria-label=\"Next match\" title=\"Next match (Enter)\">↓</button>\n </span>\n ) : null}\n\n <span className={styles.navSpacer} />\n\n {errorCount > 0 ? (\n <button type=\"button\" className={styles.navError} onClick={onJumpError}>\n <span className={styles.navErrorGlyph} aria-hidden=\"true\">!</span>\n {errorCount} error{errorCount === 1 ? '' : 's'}\n <span className=\"visually-hidden\"> — jump to the first one</span>\n </button>\n ) : null}\n\n {outlineOpen ? (\n <div id={outlineId} className={styles.outline} role=\"group\" aria-label=\"Turns in this chat\">\n {listed.length === 0 ? (\n <p className={styles.outlineEmpty}>No turn matches “{query.trim()}”.</p>\n ) : (\n <ol className={styles.outlineList}>\n {listed.map((turn) => {\n const index = turns.indexOf(turn) + 1;\n const snippet = snippetFor(turn.key);\n return (\n <li key={turn.key}>\n <button type=\"button\" className={styles.outlineRow} data-status={turn.status}\n onClick={() => { setOutlineOpen(false); onJumpTurn(turn.key); }}>\n <span className={styles.outlineIndex}>{index}</span>\n <span className={styles.outlineBody}>\n <span className={styles.outlineTitle}>{turnTitle(turn)}</span>\n {snippet ? <span className={styles.outlineSnippet}>{snippet}</span> : null}\n <span className={styles.outlineMeta}>\n {turn.toolCount > 0 ? `${turn.toolCount} tool${turn.toolCount === 1 ? '' : 's'}` : 'no tools'}\n {turn.files.length > 0 ? ` · ${turn.files.length} file${turn.files.length === 1 ? '' : 's'}` : ''}\n {turn.status !== 'ok' ? ` · ${STATUS_WORD[turn.status]}` : ''}\n </span>\n </span>\n {STATUS_GLYPH[turn.status] ? (\n <span className={styles.outlineStatus} aria-hidden=\"true\">{STATUS_GLYPH[turn.status]}</span>\n ) : null}\n </button>\n </li>\n );\n })}\n </ol>\n )}\n </div>\n ) : null}\n </div>\n );\n}\n","/**\n * routes/verse/chat/TurnAnnouncer.tsx — what a screen reader hears from the\n * transcript (SPEC-310C §2 \"Screen readers\", unit C2).\n *\n * The 3.9 log was `aria-live=\"polite\"` with `aria-relevant=\"additions text\"`:\n * every streamed token was a text change, so a screen reader either read the\n * reply one fragment at a time or queued minutes of speech behind it. The\n * log is now `aria-live=\"off\"` and this polite region says only the moments\n * that change what the operator should do:\n *\n * \"Turn started.\" · \"Turn finished.\" · \"Turn failed.\" · \"Turn stopped.\"\n *\n * Nothing is announced on mount or when switching chats (history is not\n * news) — only a transition observed while the same chat stays open.\n */\nimport { useEffect, useRef, useState } from 'react';\nimport type { TurnStatus } from './turn-model.js';\n\nexport interface TurnAnnouncerProps {\n /** A turn is in flight. */\n running: boolean;\n /** The last turn's status (null when there is none). */\n lastStatus: TurnStatus | null;\n /** The last turn's key — a new key with the same status is still a new turn. */\n lastKey: string | null;\n}\n\n/** A trailing no-break space makes a repeated sentence a CHANGE, so it is spoken again. */\nconst NBSP = '\\u00a0';\n\nconst SETTLED: Readonly<Record<Exclude<TurnStatus, 'running'>, string>> = {\n ok: 'Turn finished.',\n error: 'Turn failed.',\n stopped: 'Turn stopped.',\n};\n\n/** Pure: what to say for a transition, or null. Exported for tests. */\nexport function turnAnnouncement(\n prev: { running: boolean; lastKey: string | null } | null,\n next: { running: boolean; lastStatus: TurnStatus | null; lastKey: string | null },\n): string | null {\n if (prev === null) return null;\n if (!prev.running && next.running) return 'Turn started.';\n if (prev.running && !next.running) {\n if (next.lastStatus === null || next.lastStatus === 'running') return 'Turn finished.';\n return SETTLED[next.lastStatus];\n }\n return null;\n}\n\nexport function TurnAnnouncer({ running, lastStatus, lastKey }: TurnAnnouncerProps) {\n const [message, setMessage] = useState('');\n const prev = useRef<{ running: boolean; lastKey: string | null } | null>(null);\n\n useEffect(() => {\n const said = turnAnnouncement(prev.current, { running, lastStatus, lastKey });\n prev.current = { running, lastKey };\n // Re-set even an identical sentence: two failures in a row are two events.\n if (said !== null) setMessage((current) => (current === said ? `${said}${NBSP}` : said));\n }, [running, lastStatus, lastKey]);\n\n return (\n <div className=\"visually-hidden\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" data-testid=\"turn-announcer\">\n {message}\n </div>\n );\n}\n","/**\n * routes/verse/chat/ansi.ts — turning raw process output into something a\n * human can read.\n *\n * An agentic turn's Bash results arrive exactly as the child process wrote\n * them: SGR colour runs, cursor moves, OSC title sets, and carriage returns\n * from progress bars that expected a terminal to overwrite the line. Dumped\n * into a <pre> those print as `[0;32m` litter and a hundred duplicate\n * progress lines, which is why command output in the transcript is currently\n * the hardest thing in it to read.\n *\n * Pure string functions, no DOM: the colour information is DROPPED rather\n * than translated into spans. DESIGN §1 is monochrome-first — a tool's own\n * palette is not one of the meanings this interface spends colour on.\n */\n\n/* eslint-disable no-control-regex --\n Matching control bytes IS this module's entire purpose: the four patterns\n below are the escape grammar a terminal emits, and there is no way to\n express them without naming the control characters they match. They are\n written as \\uXXXX escapes, never as literal bytes in the source. */\n\n/** CSI … final byte @-~ — SGR colour, cursor moves, erase, everything. */\nconst CSI = /\\u001B\\[[0-9;:?]*[ -/]*[@-~]/g;\n/** OSC … terminated by BEL or ST — window titles, hyperlinks, clipboard. */\nconst OSC = /\\u001B\\][\\s\\S]*?(?:\\u0007|\\u001B\\\\)/g;\n/** Two-byte escapes (ESC 7 / ESC M / charset selects). */\nconst SHORT = /\\u001B[@-Z\\\\-_]/g;\n/** Control bytes that carry no meaning inside a <pre>: tab/newline/CR stay. */\nconst CONTROL = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n\n/* eslint-enable no-control-regex */\n\n/** True when the text contains at least one escape sequence. */\nexport function hasAnsi(text: string): boolean {\n return text.includes('\\u001B');\n}\n\n/** Remove every escape sequence and stray control byte; keep tabs/newlines. */\nexport function stripAnsi(text: string): string {\n return text.replace(OSC, '').replace(CSI, '').replace(SHORT, '').replace(CONTROL, '');\n}\n\n/**\n * Collapse carriage-return rewrites the way a terminal would: within one\n * line each CR returns the cursor to column 0 and what follows overwrites\n * what was there. A shorter overwrite leaves the tail of the longer line\n * visible, which is why this overlays rather than simply taking the last\n * segment — `100%\\rdone` is `done%` on a real terminal, not `done`.\n */\nexport function applyCarriageReturns(text: string): string {\n if (!text.includes('\\r')) return text;\n return text\n .split('\\n')\n .map((line) => {\n if (!line.includes('\\r')) return line;\n let out = '';\n for (const segment of line.split('\\r')) {\n out = segment.length >= out.length ? segment : segment + out.slice(segment.length);\n }\n return out;\n })\n .join('\\n');\n}\n\n/**\n * The whole treatment: escapes out, carriage returns applied, trailing blank\n * lines trimmed. Leading whitespace is preserved — indentation is content in\n * compiler output, stack traces and test reports.\n */\nexport function cleanTerminalOutput(text: string): string {\n return applyCarriageReturns(stripAnsi(text))\n .replace(/[ \\t]+$/gm, '')\n .replace(/\\n+$/, '');\n}\n\n/** Line count without allocating a split array. */\nexport function countLines(text: string): number {\n if (text.length === 0) return 0;\n let n = 1;\n for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) n++;\n return n;\n}\n\n/**\n * First `limit` lines plus how many were withheld. Backs the collapsed view\n * of a long result — the full text stays one click away, never discarded.\n */\nexport function headLines(text: string, limit: number): { head: string; hidden: number } {\n const lines = text.split('\\n');\n if (lines.length <= limit) return { head: text, hidden: 0 };\n return { head: lines.slice(0, limit).join('\\n'), hidden: lines.length - limit };\n}\n","/**\n * routes/verse/chat/CommandOutput.tsx — a shell run, readable.\n *\n * Three things were wrong with dumping `tool-result.output` into a <pre>:\n * escape sequences printed as `[0;32m` litter, progress bars repeated every\n * carriage-return frame, and a thousand-line build log pushed the rest of\n * the turn off the screen. All three are fixed here without losing anything:\n * `ansi.ts` cleans the text, long output collapses to a head with an exact\n * count of what is hidden, and \"Show all\" restores it.\n *\n * The status line is honest (DESIGN §6). A stated exit code is printed as a\n * number; when the tool never stated one, the line says whether the call\n * failed and does not invent a code.\n *\n * 3.10 (SPEC-310C §2, unit C2): \"Run in terminal\" opens the dock's Terminal\n * with the command PASTED at the prompt — never executed. The operator reads\n * it there and presses Enter; an agent's command must not re-run because\n * someone clicked near it. Offered only once the Terminal pane exists in\n * this build (C4's slot).\n */\nimport { useMemo, useState, type ReactNode } from 'react';\nimport { isSlotAvailable } from '../shell/slots.js';\nimport { requestTerminal } from '../dock/dock-store.js';\nimport { cleanTerminalOutput, countLines, hasAnsi, headLines } from './ansi.js';\nimport type { CommandFacts } from './tool-semantics.js';\nimport styles from './chat.module.css';\n\nconst PREVIEW_LINES = 20;\n/** Hard ceiling: past this, the DOM cost stops being worth it. */\nconst MAX_RENDER_CHARS = 200_000;\n\nexport interface CommandOutputProps {\n command: CommandFacts | null;\n output: string;\n /** The tool reported failure for this call. */\n isError: boolean;\n pending: boolean;\n /**\n * Replaces the raw output block. A command whose output IS a diff\n * (`git diff`, `git show`) keeps its command line and exit status here and\n * renders the diff properly instead of as terminal text.\n */\n body?: ReactNode;\n /** Whether \"Run in terminal\" is offered; defaults to whether C4's Terminal pane is in this build (tests inject it). */\n terminalAvailable?: boolean;\n}\n\nexport function CommandOutput({ command, output, isError, pending, body, terminalAvailable = isSlotAvailable('terminal-pane') }: CommandOutputProps) {\n const [expanded, setExpanded] = useState(false);\n const cleaned = useMemo(() => {\n const text = cleanTerminalOutput(output);\n return text.length > MAX_RENDER_CHARS\n ? `${text.slice(0, MAX_RENDER_CHARS)}\\n… (${text.length - MAX_RENDER_CHARS} more characters not shown)`\n : text;\n }, [output]);\n const { head, hidden } = useMemo(() => headLines(cleaned, PREVIEW_LINES), [cleaned]);\n const shown = expanded ? cleaned : head;\n const exit = command?.exitCode ?? null;\n\n return (\n <div className={styles.command} data-failed={isError ? 'true' : undefined}>\n {command ? (\n <div className={styles.commandLine}>\n <span className={styles.commandPrompt} aria-hidden=\"true\">$</span>\n <code className={styles.commandText}>{command.command}</code>\n {terminalAvailable && command.command.trim() ? (\n <button type=\"button\" className={styles.commandRun}\n title=\"Opens the Terminal with this command typed at the prompt. It does not run until you press Enter.\"\n onClick={() => requestTerminal({ paste: command.command })}>\n Run in terminal\n </button>\n ) : null}\n </div>\n ) : null}\n <div className={styles.commandStatus}>\n <span className={styles.commandStatusText} data-tone={pending ? 'pending' : isError ? 'bad' : 'ok'}>\n {pending\n ? 'running…'\n : exit !== null\n ? `exit ${exit}`\n : isError\n ? 'failed — no exit code reported'\n : 'completed — no exit code reported'}\n </span>\n {!pending && !body && cleaned.length === 0 ? <span className={styles.commandMeta}>no output</span> : null}\n {!pending && !body && cleaned.length > 0 ? (\n <span className={styles.commandMeta}>\n {countLines(cleaned)} line{countLines(cleaned) === 1 ? '' : 's'}\n {hasAnsi(output) ? ' · colour codes stripped' : ''}\n </span>\n ) : null}\n </div>\n {pending ? (\n <p className={styles.commandPending}>Waiting for the command to finish…</p>\n ) : body ? (\n body\n ) : cleaned.length > 0 ? (\n <>\n <pre className={styles.commandPre}>{shown}</pre>\n {hidden > 0 ? (\n <button type=\"button\" className={styles.commandMore} aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}>\n {expanded ? 'Collapse output' : `Show ${hidden} more line${hidden === 1 ? '' : 's'}`}\n </button>\n ) : null}\n </>\n ) : null}\n </div>\n );\n}\n","/**\n * routes/verse/chat/DiffBlock.tsx — a file change rendered as a change.\n *\n * The transcript's job here is narrow: ONE file, inside a 720px reading\n * column, inline in a tool card. The inbox `DiffViewer` is the right tool\n * for the other shape — a multi-file patch that wants a file tree and a\n * split toggle — so that component is imported and used verbatim for\n * multi-file diffs rather than reimplemented. The parser\n * (`routes/inbox/diff-parser.ts`) and the highlighter\n * (`routes/inbox/highlight.ts`) are shared by both paths; nothing about\n * either is rewritten here.\n *\n * Honesty (DESIGN §6): an `Edit` payload knows what was replaced but not\n * where it sat, so when `diff.anchored` is false the line-number gutter is\n * hidden and the header says the offsets are not the file's.\n */\nimport { useMemo, useState } from 'react';\nimport { DiffViewer } from '../../inbox/DiffViewer.js';\nimport { parseUnifiedDiff, type DiffFile, type DiffHunk } from '../../inbox/diff-parser.js';\nimport { languageForPath, tokenizeLine, type LangFamily } from '../../inbox/highlight.js';\nimport { useDisplayPath } from './path-display.js';\nimport type { ToolDiff } from './tool-semantics.js';\nimport styles from './chat.module.css';\n\n/** Beyond this many rendered lines the block collapses to a preview. */\nconst COLLAPSE_AFTER_LINES = 28;\nconst PREVIEW_LINES = 18;\n/**\n * Hard ceiling on rows materialised from one file's hunks. The text itself is\n * already capped in `readToolFacts`, but a capped diff can still be tens of\n * thousands of lines, and every row here becomes DOM. Past this the block says\n * how many it dropped rather than locking the tab building rows nobody asked to\n * see.\n */\nconst MAX_ROWS = 4000;\n\nexport interface DiffBlockProps {\n diff: ToolDiff;\n /** Falls back to the diff's own path when the payload named none. */\n path?: string | null;\n}\n\nexport function DiffBlock({ diff, path = null }: DiffBlockProps) {\n const parsed = useMemo(() => parseUnifiedDiff(diff.text), [diff.text]);\n\n if (parsed.files.length === 0) return null;\n if (diff.multiFile || parsed.files.length > 1) {\n return (\n <div className={styles.diffMulti}>\n <DiffViewer diff={diff.text} proposalKind=\"tool call\" />\n </div>\n );\n }\n\n const file = parsed.files[0]!;\n return <SingleFileDiff file={file} anchored={diff.anchored} path={path ?? file.displayPath} />;\n}\n\n/**\n * What happened to the file, when that is not simply \"it was edited\".\n *\n * A rename with no hunks is the case that made this necessary: it reached the\n * empty branch below and printed \"No line-level changes in this call.\" under\n * the NEW path alone, telling the operator nothing happened while silently\n * dropping the old location — the one fact that makes a rename legible.\n */\nfunction statusNote(file: DiffFile): string | null {\n switch (file.status) {\n case 'renamed':\n return 'Renamed. The contents did not change, so there are no lines to show.';\n case 'added':\n return 'New file. It was added with no contents to diff against.';\n case 'deleted':\n return 'Deleted. The file is gone, so there are no lines to show.';\n default:\n return null;\n }\n}\n\nfunction SingleFileDiff({ file, anchored, path }: { file: DiffFile; anchored: boolean; path: string }) {\n const [expanded, setExpanded] = useState(false);\n const lang = languageForPath(path);\n const rows = useMemo(() => flatten(file.hunks), [file.hunks]);\n const long = rows.length > COLLAPSE_AFTER_LINES;\n const visible = long && !expanded ? rows.slice(0, PREVIEW_LINES) : rows;\n const hidden = rows.length - visible.length;\n const renamedFrom = file.status === 'renamed' ? file.oldPath : null;\n const note = statusNote(file);\n // Drawn relative to the chat's roots; the full path is the tooltip.\n const show = useDisplayPath();\n\n return (\n <div className={styles.diff} data-anchored={anchored ? 'true' : 'false'} data-status={file.status}>\n <div className={styles.diffHead}>\n {renamedFrom ? (\n <span className={styles.diffOldPath} title={renamedFrom}>\n {show(renamedFrom)}\n <span className={styles.visuallyHidden}> renamed to</span>\n </span>\n ) : null}\n <span className={styles.diffPath} title={path}>{show(path)}</span>\n {file.status !== 'modified' ? (\n <span className={styles.diffStatus}>{file.status}</span>\n ) : null}\n <span className={styles.diffStats}>\n {file.additions > 0 ? <span className={styles.diffAdd}>+{file.additions}</span> : null}\n {file.deletions > 0 ? <span className={styles.diffDel}>−{file.deletions}</span> : null}\n </span>\n </div>\n {file.unparsedNotice ? <pre className={styles.diffNotice}>{file.unparsedNotice}</pre> : null}\n {rows.length === 0 ? (\n <p className={styles.diffEmpty}>\n {note ?? 'No line-level changes in this call.'}\n </p>\n ) : (\n <>\n <table className={styles.diffTable}>\n <tbody>\n {visible.map((row) =>\n row.kind === 'hunk' ? (\n <tr key={row.key} className={styles.diffHunkRow}>\n {anchored ? <td className={styles.diffNo} aria-hidden=\"true\" /> : null}\n <td className={styles.diffMarker} aria-hidden=\"true\" />\n <td className={styles.diffHunkHead}>{row.label}</td>\n </tr>\n ) : (\n <tr key={row.key} className={styles[`diffRow_${row.line.kind}`]}>\n {anchored ? (\n <td className={styles.diffNo}>{row.line.newLineNo ?? row.line.oldLineNo ?? ''}</td>\n ) : null}\n <td className={styles.diffMarker} aria-hidden=\"true\">\n {row.line.kind === 'add' ? '+' : row.line.kind === 'del' ? '−' : ''}\n </td>\n <td className={styles.diffText}>\n <CodeLine text={row.line.text} lang={lang} />\n </td>\n </tr>\n ),\n )}\n </tbody>\n </table>\n {long ? (\n <button type=\"button\" className={styles.diffMore} onClick={() => setExpanded((v) => !v)}\n aria-expanded={expanded}>\n {expanded ? 'Collapse diff' : `Show ${hidden} more line${hidden === 1 ? '' : 's'}`}\n </button>\n ) : null}\n </>\n )}\n {!anchored ? (\n <p className={styles.diffUnanchored}>\n No line numbers — the tool didn&apos;t report where in the file.\n </p>\n ) : null}\n </div>\n );\n}\n\ntype Row =\n | { kind: 'hunk'; key: string; label: string }\n | { kind: 'line'; key: string; line: DiffHunk['lines'][number] };\n\n/** Hunks into one flat row list, with a separator row between hunks. */\nfunction flatten(hunks: readonly DiffHunk[]): Row[] {\n const rows: Row[] = [];\n let truncated = false;\n hunks.forEach((hunk, h) => {\n if (truncated) return;\n // The `@@ -a,b +c,d @@` arithmetic is noise in a chat column; the free\n // text after it (`edit 2 of 3`, a function name) is the only part worth\n // a row, and a multi-hunk diff needs SOME separator regardless.\n const section = hunk.header.replace(/^@@[^@]*@@/, '').trim();\n if (section || hunks.length > 1) {\n rows.push({ kind: 'hunk', key: `h${h}`, label: section || `hunk ${h + 1}` });\n }\n for (let i = 0; i < hunk.lines.length; i++) {\n if (rows.length >= MAX_ROWS) {\n truncated = true;\n rows.push({ kind: 'hunk', key: 'truncated', label: `diff truncated at ${MAX_ROWS} lines` });\n return;\n }\n rows.push({ kind: 'line', key: `h${h}l${i}`, line: hunk.lines[i]! });\n }\n });\n return rows;\n}\n\nfunction CodeLine({ text, lang }: { text: string; lang: LangFamily }) {\n const tokens = tokenizeLine(text, lang);\n return (\n <code className={styles.diffCode}>\n {tokens.map((token, i) => (\n <span key={i} className={styles[`tok_${token.kind}`]}>{token.text}</span>\n ))}\n </code>\n );\n}\n","/**\n * routes/verse/ToolUseCard.tsx — one tool call.\n *\n * Collapsed it is a single dense line (DESIGN §5): a 12px glyph, the tool\n * name in mono, a truncated argument, and a right-aligned duration. A call\n * that changed a file also carries its `+12 −3` there, in the display font,\n * because the size of a change is the thing most worth knowing before\n * deciding to open it.\n *\n * Expanded, the body is chosen by what the call actually DID\n * (`chat/tool-semantics.ts`) rather than by dumping JSON:\n *\n * - an edit / create / patch → a real unified diff (`chat/DiffBlock`,\n * which reuses the inbox parser, highlighter and multi-file viewer)\n * - a shell run → the command, an honest exit status, and\n * output with ANSI stripped and long logs collapsed (`chat/CommandOutput`)\n * - anything else → the input and result as mono blocks, as before\n *\n * A failure tints the left rule and nothing else — the word \"error\" carries\n * the state (DESIGN §6).\n *\n * Paths read relative to the chat's roots (chat/path-display.ts): a Read of\n * `/private/tmp/…/e2e-proj-39/math.ts` is drawn as `math.ts`, with the full\n * path in the tooltip. The disclosure glyph is the same ▸ the activity row\n * and the reasoning row use, so every foldable line opens the same way.\n */\nimport { useMemo } from 'react';\nimport { CommandOutput } from './chat/CommandOutput.js';\nimport { LiveTimer } from './chat/LiveTimer.js';\nimport { DiffBlock } from './chat/DiffBlock.js';\nimport { useDisplayPath } from './chat/path-display.js';\nimport { countDiffLines } from './chat/turn-model.js';\nimport { readToolFacts, toolAnchorId, type ToolFacts } from './chat/tool-semantics.js';\nimport { formatDuration, prettyJson, summarizeToolInput } from './verse-model.js';\nimport styles from './Transcript.module.css';\n\nexport interface ToolUseCardProps {\n name: string;\n input: unknown;\n result: { output: string; isError: boolean } | null;\n /** Stable id so open/closed state can survive re-renders (data-state-key). */\n toolUseId: string;\n /** tool-use → tool-result wall time, when both frames were stamped. */\n durationMs?: number | null;\n /**\n * Derived once per transcript by `chat/turn-model.buildTurns` and handed\n * down, so a card never re-parses a diff the file-activity summary already\n * parsed. Omitted (tests, one-off renders) it is derived here.\n */\n facts?: ToolFacts;\n /** 3.10: ISO start of the call — a pending card counts up from it (\"running 12s\"). */\n startedAt?: string | null;\n}\n\nconst MAX_OUTPUT_CHARS = 20_000;\n\nexport function ToolUseCard({ name, input, result, toolUseId, durationMs = null, facts, startedAt = null }: ToolUseCardProps) {\n const derived = useMemo(\n () => facts ?? readToolFacts({ name, input, result }),\n [facts, name, input, result],\n );\n const show = useDisplayPath();\n // A file call names its file, relative to the chat's roots — never the\n // 96-character head of an absolute path whose filename the cut dropped. A\n // shell run keeps its command line verbatim.\n const target = derived.command ? null : derived.paths[0] ?? null;\n const summary = target === null\n ? summarizeToolInput(input)\n : `${show(target)}${derived.paths.length > 1 ? ` +${derived.paths.length - 1}` : ''}`;\n const isError = derived.failed || result?.isError === true;\n const pending = result === null;\n const state = pending\n ? 'running'\n : isError\n ? 'error'\n : durationMs !== null && durationMs > 0\n ? formatDuration(durationMs)\n : 'done';\n // Re-splitting the whole diff on every render of every card is pure waste:\n // the text is immutable once the result has landed.\n const delta = useMemo(\n () => (derived.diff ? countDiffLines(derived.diff.text) : null),\n [derived.diff],\n );\n const output = result\n ? result.output.length > MAX_OUTPUT_CHARS\n ? `${result.output.slice(0, MAX_OUTPUT_CHARS)}\\n… (${result.output.length - MAX_OUTPUT_CHARS} more characters)`\n : result.output\n : '';\n\n return (\n <details id={toolAnchorId(toolUseId)} className={`${styles.tool} ${isError ? styles.toolFailed : ''}`}\n data-state-key={`tool:${toolUseId}`} data-action={derived.action}>\n <summary className={styles.toolLine} aria-label={`${name}${summary ? `: ${summary}` : ''} (${state})`}>\n <span className={styles.toolGlyph} aria-hidden=\"true\" />\n <span className={styles.toolName}>{name}</span>\n {summary ? <span className={styles.toolArg} title={target ?? summary}>{summary}</span> : null}\n {delta && (delta.additions > 0 || delta.deletions > 0) ? (\n <span className={styles.toolDelta} aria-hidden=\"true\">\n {delta.additions > 0 ? <span className={styles.toolAdd}>+{delta.additions}</span> : null}\n {delta.deletions > 0 ? <span className={styles.toolDel}>−{delta.deletions}</span> : null}\n </span>\n ) : null}\n <span className={styles.toolState}>\n {pending && startedAt ? <>running <LiveTimer since={startedAt} /></> : state}\n </span>\n </summary>\n <div className={styles.toolBody}>\n <ToolBody input={input} output={output} facts={derived} pending={pending} isError={isError} />\n </div>\n </details>\n );\n}\n\ninterface ToolBodyProps {\n input: unknown;\n output: string;\n facts: ToolFacts;\n pending: boolean;\n isError: boolean;\n}\n\nfunction ToolBody({ input, output, facts, pending, isError }: ToolBodyProps) {\n const show = useDisplayPath();\n // A shell run: the command line and exit status always, with the diff in\n // place of the log when the command's own output is one.\n if (facts.command) {\n return (\n <CommandOutput command={facts.command} output={output} isError={isError} pending={pending}\n body={facts.diff ? <DiffBlock diff={facts.diff} path={facts.paths[0] ?? null} /> : undefined} />\n );\n }\n\n if (facts.diff) {\n return (\n <>\n <DiffBlock diff={facts.diff} path={facts.paths[0] ?? null} />\n {pending ? <p className={styles.toolPending}>Waiting for the tool to finish…</p> : null}\n {isError && output ? (\n <section>\n <h4 className={styles.toolLabel}>Error</h4>\n <pre className={styles.toolPre}>{output}</pre>\n </section>\n ) : null}\n </>\n );\n }\n\n if (facts.written) {\n return (\n <>\n <section>\n <h4 className={styles.toolLabel}>\n Contents written to <span className={styles.toolLabelPath} title={facts.written.path}>{show(facts.written.path)}</span>\n </h4>\n <pre className={styles.toolPre}>{facts.written.text}</pre>\n <p className={styles.toolNote}>\n The previous contents were not in this tool call, so there is nothing to diff against.\n </p>\n </section>\n <section>\n <h4 className={styles.toolLabel}>{isError ? 'Error' : 'Result'}</h4>\n {pending ? <p className={styles.toolPending}>Waiting for the tool to finish…</p>\n : <pre className={styles.toolPre}>{output || '(empty)'}</pre>}\n </section>\n </>\n );\n }\n\n return (\n <>\n {input !== null && input !== undefined ? (\n <section>\n <h4 className={styles.toolLabel}>Input</h4>\n <pre className={styles.toolPre}>{prettyJson(input)}</pre>\n </section>\n ) : null}\n <section>\n <h4 className={styles.toolLabel}>{isError ? 'Error' : 'Result'}</h4>\n {pending ? <p className={styles.toolPending}>Waiting for the tool to finish…</p>\n : <pre className={styles.toolPre}>{output || '(empty)'}</pre>}\n </section>\n </>\n );\n}\n","/**\n * routes/verse/Transcript.tsx — the conversation, in one 720px measure.\n *\n * 3.10.1: the user turn is a quiet rounded block on the right (the hover\n * ground, primary text), the assistant turn plain primary text at full\n * measure — role by placement and shape, never by colour alone. A turn ends\n * in a muted footer (how long it took, the jump to its first failure) instead\n * of a lone duration line between turns, and every path a tool touched reads\n * relative to the chat's roots (chat/path-display.ts).\n *\n * Runs of tool calls fold (verse-store groupTranscriptItems) into one\n * activity row — \"Ran 12 commands · read 8 files · edited 3 files · 1 failed · 2m 14s\"\n * (chat/ActivityGroup) — that opens on its failed and running calls by\n * itself, so an agentic turn reads as a summary line between two pieces of\n * prose instead of a wall of cards.\n *\n * 3.10 also adds the ChapterRail (a minimap of turns down the right edge)\n * and a screen-reader announcer that speaks turn boundaries instead of\n * every streamed token.\n *\n * On top of that the items are grouped into TURNS (`chat/turn-model.ts`),\n * which is what makes a long agentic session usable:\n *\n * - each turn ends with a file-activity summary — the blast radius of one\n * ask, with a click-through to the call that changed each file;\n * - the nav strip gives the session an outline, a search over prose AND\n * tool payloads, and a jump to the first failure;\n * - Alt+↑/↓ (and plain ↑/↓ once a turn has focus) step between turns.\n *\n * V3.9: a native compaction renders as a divider that says what the CLI\n * reported (\"Auto-compacted 812k → 41k in 1m 58s\" / \"Codex compacted its\n * context\"), and a chat started as a handoff opens with \"Continued from\n * <source>\", which links back to the chat it continues.\n *\n * V3.10 — live reasoning and a transcript that costs one turn per frame:\n *\n * - the model's reasoning streams in a ThinkingBlock (\"Thinking · 12s ·\n * ~1.8k tok\"), in a three-line window that folds when the block ends\n * (Settings ▸ Chat: Collapsed / Expanded / Hidden);\n * - the live status line (\"Running · 14s · npm test\") and the engine's\n * retry / watchdog notices sit ABOVE THE COMPOSER (Workspace), not in the\n * log: they describe the turn you are waiting on, not its history;\n * - the store hands the transcript over in turn SEGMENTS that keep their\n * identity while unchanged; each segment's turn model is derived once\n * and every finished turn is a memoized TurnView, so a streamed token\n * re-renders the one live turn — not the session. Finished turns also\n * get `content-visibility: auto`, so the browser skips laying out the\n * ones scrolled out of view;\n * - a restored conversation (`recovered`) and a trimmed log\n * (`history-truncated`) say so in the transcript.\n *\n * Follows the newest message unless the operator scrolled up, in which case\n * a \"Jump to latest\" control appears.\n */\nimport { memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react';\nimport type { VerseEngine, VerseSession } from '../../data/api-types.js';\nimport { SkeletonLine } from '../../components/primitives/Skeleton.js';\nimport { ActivityGroupView, revealAnchorInGroups } from './chat/ActivityGroup.js';\nimport { ChapterRail } from './chat/ChapterRail.js';\nimport { PathRootsContext } from './chat/path-display.js';\nimport { buildChapters, chaptersSignature } from './chat/chapter-model.js';\nimport { FileActivity } from './chat/FileActivity.js';\nimport { useReasoningDisplay, type ReasoningDisplay } from './chat/reasoning-pref.js';\nimport { ThinkingBlock } from './chat/ThinkingBlock.js';\nimport { onTranscriptFind, onTranscriptJump, onTranscriptStep } from './chat/transcript-jump.js';\nimport { TranscriptNav } from './chat/TranscriptNav.js';\nimport { TurnAnnouncer } from './chat/TurnAnnouncer.js';\nimport { toolAnchorId, type ToolFacts } from './chat/tool-semantics.js';\nimport { buildTurns, createTurnCache, noteAnchorId, searchTurns, turnAnchorId, type TurnBlock, type TurnCache } from './chat/turn-model.js';\nimport { MessageMarkdown } from './MessageMarkdown.js';\nimport { ToolUseCard, type ToolUseCardProps } from './ToolUseCard.js';\nimport { ArrowDownIcon } from './verse-icons.js';\nimport { ENGINE_LABEL, formatDuration } from './verse-model.js';\nimport { formatTokens } from './verse-readouts.js';\nimport type { VerseLiveState } from './verse-store.js';\nimport {\n groupTranscriptItems,\n type ToolGroupItem,\n type ToolGroupMember,\n type Transcript as TranscriptModel,\n type TranscriptItem,\n type TranscriptRenderItem,\n type TranscriptSegment,\n} from './verse-transcript.js';\nimport styles from './Transcript.module.css';\n\nexport interface TranscriptProps {\n transcript: TranscriptModel;\n loaded: boolean;\n loadError: string | null;\n onRetry?: () => void;\n emptyHint?: string;\n /** Names the CLI in compaction dividers whose counts are unknown (\"Codex compacted its context\"). */\n engine?: VerseEngine;\n /** V3.9: this chat was started as a handoff; the header links back to the source. */\n handoffFrom?: VerseSession['handoffFrom'] | null;\n /** Opens another chat (the handoff source). Absent → the source is named but not a link. */\n onOpenSession?: (sessionId: string) => void;\n /** V3.10: transient signals of the running turn (the streaming reasoning block). */\n live?: VerseLiveState | null;\n /**\n * 3.10.1: the chat's roots, primary first. Tool paths under one of them are\n * drawn relative to it (the full path stays in the tooltip). Absent → the\n * roots already in context (Workspace provides them). Keep the array\n * identity stable — every card showing a path re-renders when it changes.\n */\n projectRoots?: readonly string[];\n}\n\ntype CompactionItem = Extract<TranscriptItem, { kind: 'compaction' }>;\n\n/**\n * One line for a native compaction, from exactly what the CLI reported:\n * \"Auto-compacted 812k → 41k in 1m 58s\" when claude/grok sent the counts,\n * \"Codex compacted its context\" when the CLI records only that it happened.\n * Never a number the CLI did not give.\n */\nexport function describeCompaction(item: Pick<CompactionItem, 'trigger' | 'preTokens' | 'postTokens' | 'durationMs'>, engine?: VerseEngine): string {\n const verb = item.trigger === 'manual' ? 'Compacted on request' : 'Auto-compacted';\n const took = item.durationMs !== null && item.durationMs > 0 ? ` in ${formatDuration(item.durationMs)}` : '';\n if (item.preTokens !== null && item.postTokens !== null) {\n return `${verb} ${formatTokens(item.preTokens)} → ${formatTokens(item.postTokens)}${took}`;\n }\n if (item.preTokens !== null) return `${verb} at ${formatTokens(item.preTokens)}${took}`;\n const who = engine ? ENGINE_LABEL[engine] : 'The CLI';\n return `${who} compacted its context${took}`;\n}\n\n/**\n * What an error `code` (V3.10, VERSE_ERROR_CODES) means for the operator.\n * The message itself is the CLI's own words; this is the \"so what now\".\n */\nexport function errorCodeHint(code: string | null): string | null {\n switch (code) {\n case 'native-thread-missing':\n return 'The CLI no longer has this chat’s saved conversation (it may have expired). Continue in a fresh chat — the transcript here is intact.';\n case 'session-in-use':\n return 'The CLI says this chat’s native session is still held by another process. Wait for it to finish, or continue in a fresh chat.';\n default:\n return null;\n }\n}\n\nconst FOLLOW_THRESHOLD_PX = 48;\n/** Below this the session is short enough to scroll; chrome would be noise. */\nconst NAV_MIN_TURNS = 3;\n/** How long a jumped-to element keeps its locator tint. */\nconst FLASH_MS = 1400;\n\n/** One segment's derived turns — computed once per segment object. */\ninterface SegmentModel {\n turns: TurnBlock[];\n facts: Map<string, ToolFacts>;\n errorAnchors: string[];\n /** turn-done items already explained by a preceding Stop / error in the same turn. */\n explained: ReadonlySet<string>;\n}\n\nfunction explainedKeys(items: readonly TranscriptItem[]): Set<string> {\n // A `turn-done ok:false` that follows the same turn's `cancelled`/`error`\n // note is already explained; a second red \"ended without a result\" line\n // would make every Stop look like a failure.\n const out = new Set<string>();\n let last: { turnId: string | null; kind: string } | null = null;\n for (const item of items) {\n // A compaction between a Stop and its turn-done explains nothing and\n // hides nothing; it must not break the pairing.\n if (item.kind === 'compaction') continue;\n if (item.kind === 'turn-done' && !item.ok && last && last.turnId === item.turnId && (last.kind === 'cancelled' || last.kind === 'error')) {\n out.add(item.key);\n }\n last = { turnId: item.turnId, kind: item.kind };\n }\n return out;\n}\n\nfunction deriveSegment(segment: TranscriptSegment, cache: TurnCache): SegmentModel {\n const model = buildTurns(groupTranscriptItems(segment.items), cache);\n return { turns: model.turns, facts: model.facts, errorAnchors: model.errorAnchors, explained: explainedKeys(segment.items) };\n}\n\nexport function Transcript({ transcript, loaded, loadError, onRetry, emptyHint, engine, handoffFrom = null, onOpenSession, live = null,\n projectRoots }: TranscriptProps) {\n const reasoning = useReasoningDisplay();\n const inheritedRoots = useContext(PathRootsContext);\n const scroller = useRef<HTMLDivElement>(null);\n const turnNodes = useRef(new Map<string, HTMLElement>());\n const flashTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [following, setFollowing] = useState(true);\n const [unseen, setUnseen] = useState(false);\n const [query, setQuery] = useState('');\n const [matchIndex, setMatchIndex] = useState(-1);\n const [focusToken, setFocusToken] = useState(0);\n const lastVersion = useRef('');\n\n // Per-segment derivation. The store returns an unchanged segment as the\n // SAME object, so only the live turn's segment misses this cache while a\n // reply streams; the tool-facts cache below additionally keeps a rebuilt\n // segment from re-deriving tool calls whose results already landed.\n const turnCache = useRef(createTurnCache());\n const segmentModels = useRef(new WeakMap<TranscriptSegment, SegmentModel>());\n const segments = useMemo<TranscriptSegment[]>(\n () => transcript.segments ?? [{ key: 'all', items: transcript.items }],\n [transcript],\n );\n const perSegment = useMemo(() => segments.map((segment) => {\n let model = segmentModels.current.get(segment);\n if (!model) {\n model = deriveSegment(segment, turnCache.current);\n segmentModels.current.set(segment, model);\n }\n return model;\n }), [segments]);\n const turns = useMemo(() => perSegment.flatMap((m) => m.turns), [perSegment]);\n /** Index of each segment's first turn in `turns` (for the per-turn ordinal). */\n const turnOffsets = useMemo(() => {\n const out: number[] = [];\n let n = 0;\n for (const m of perSegment) {\n out.push(n);\n n += m.turns.length;\n }\n return out;\n }, [perSegment]);\n const errorAnchors = useMemo(() => perSegment.flatMap((m) => m.errorAnchors), [perSegment]);\n // The rail's model, rebuilt only when what it draws changes (never per token).\n const turnsForRail = useRef(turns);\n turnsForRail.current = turns;\n const railSignature = chaptersSignature(turns, handoffFrom !== null);\n const chapters = useMemo(\n () => buildChapters(turnsForRail.current, { handoff: handoffFrom !== null }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the signature IS the dependency\n [railSignature],\n );\n const matches = useMemo(\n () => searchTurns(turns, query, turnCache.current),\n [turns, query],\n );\n const matchKeys = useMemo(() => new Set(matches.map((m) => m.turnKey)), [matches]);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = 'auto') => {\n const node = scroller.current;\n if (!node) return;\n // jsdom has no Element.scrollTo; fall back to the property for tests.\n if (typeof node.scrollTo === 'function') node.scrollTo({ top: node.scrollHeight, behavior });\n else node.scrollTop = node.scrollHeight;\n }, []);\n\n function onScroll() {\n const node = scroller.current;\n if (!node) return;\n const distance = node.scrollHeight - node.scrollTop - node.clientHeight;\n const atBottom = distance <= FOLLOW_THRESHOLD_PX;\n setFollowing(atBottom);\n if (atBottom) setUnseen(false);\n }\n\n // ---- jumping -------------------------------------------------------------\n /** Scroll an element into view and tint it briefly so the eye can find it. */\n const reveal = useCallback((node: HTMLElement | null) => {\n if (!node) return;\n setFollowing(false);\n if (typeof node.scrollIntoView === 'function') node.scrollIntoView({ block: 'center', behavior: 'smooth' });\n if (typeof node.focus === 'function') node.focus({ preventScroll: true });\n node.setAttribute('data-flash', 'true');\n if (flashTimer.current) clearTimeout(flashTimer.current);\n flashTimer.current = setTimeout(() => node.removeAttribute('data-flash'), FLASH_MS);\n }, []);\n\n useEffect(() => () => {\n if (flashTimer.current) clearTimeout(flashTimer.current);\n }, []);\n\n const jumpToTurn = useCallback((turnKey: string) => {\n reveal(turnNodes.current.get(turnKey) ?? null);\n }, [reveal]);\n\n /**\n * Open the disclosure chain around a tool call before revealing it. A call\n * folded inside a collapsed activity group is not in the DOM at all: the\n * group is asked to open (revealAnchorInGroups) and the jump retried on\n * the next frame, once it has rendered its members.\n */\n const jumpToAnchor = useCallback((id: string, retried = false): boolean => {\n const node = scroller.current?.ownerDocument?.getElementById(id) ?? null;\n if (!node) {\n if (!retried && revealAnchorInGroups(id)) {\n requestAnimationFrame(() => { jumpToAnchor(id, true); });\n return true;\n }\n return false;\n }\n for (let el: Element | null = node; el; el = el.parentElement) {\n if (el instanceof HTMLDetailsElement) el.open = true;\n if (el === scroller.current) break;\n }\n reveal(node);\n return true;\n }, [reveal]);\n\n // The dock's Tasks pane (and anything else outside this subtree) jumps\n // through the bus; ⌘K \"Find in chat\" focuses the search the same way ⌘F does.\n useEffect(() => onTranscriptJump((id) => jumpToAnchor(id)), [jumpToAnchor]);\n const findable = turns.length >= NAV_MIN_TURNS;\n useEffect(() => onTranscriptFind(() => {\n if (!findable) return false;\n setFocusToken((n) => n + 1);\n return true;\n }), [findable]);\n\n const jumpToTool = useCallback((toolUseId: string) => jumpToAnchor(toolAnchorId(toolUseId)), [jumpToAnchor]);\n\n const jumpToFirstError = useCallback(() => {\n const anchor = errorAnchors[0];\n if (anchor) jumpToAnchor(anchor);\n }, [errorAnchors, jumpToAnchor]);\n\n const registerTurn = useCallback((key: string, node: HTMLElement | null) => {\n if (node) turnNodes.current.set(key, node);\n else turnNodes.current.delete(key);\n }, []);\n\n const stepMatch = useCallback((delta: number) => {\n if (matches.length === 0) return;\n const next = matchIndex < 0\n ? (delta > 0 ? 0 : matches.length - 1)\n : (matchIndex + delta + matches.length) % matches.length;\n setMatchIndex(next);\n jumpToTurn(matches[next]!.turnKey);\n }, [matches, matchIndex, jumpToTurn]);\n\n useEffect(() => { setMatchIndex(-1); }, [query]);\n\n // Read through a ref so the handlers below stay stable while a reply\n // streams (`turns` is a new array every frame); otherwise the document\n // keydown listener would be torn down and re-added per token.\n const turnsRef = useRef(turns);\n turnsRef.current = turns;\n\n /** Move focus n turns from whichever turn currently holds it. */\n const stepTurn = useCallback((delta: number) => {\n const list = turnsRef.current;\n if (list.length === 0) return;\n const active = scroller.current?.ownerDocument?.activeElement;\n const currentKey = active instanceof HTMLElement ? active.getAttribute('data-turn-key') : null;\n const index = currentKey ? list.findIndex((t) => t.key === currentKey) : -1;\n const next = index < 0\n ? (delta > 0 ? 0 : list.length - 1)\n : Math.max(0, Math.min(list.length - 1, index + delta));\n jumpToTurn(list[next]!.key);\n }, [jumpToTurn]);\n\n useEffect(() => onTranscriptStep((delta) => {\n if (turnsRef.current.length === 0) return false;\n stepTurn(delta);\n return true;\n }), [stepTurn]);\n\n // ⌘F focuses the in-chat search; Alt+↑/↓ steps turns from anywhere EXCEPT a\n // text field, where the OS already owns those chords for word/paragraph\n // movement and the composer must keep them.\n useEffect(() => {\n if (turns.length === 0) return;\n const onKey = (event: globalThis.KeyboardEvent) => {\n // The shell's key handler routes these through the command bus (and\n // back here via onTranscriptStep / onTranscriptFind); an event it\n // already took must not step twice.\n if (event.defaultPrevented) return;\n const target = event.target as HTMLElement | null;\n const typing = target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement\n || target?.isContentEditable === true;\n if ((event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === 'f') {\n if (turns.length < NAV_MIN_TURNS) return;\n event.preventDefault();\n setFocusToken((n) => n + 1);\n return;\n }\n if (event.altKey && !event.metaKey && !event.ctrlKey && !typing) {\n if (event.key === 'ArrowDown') { event.preventDefault(); stepTurn(1); }\n else if (event.key === 'ArrowUp') { event.preventDefault(); stepTurn(-1); }\n }\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [turns.length, stepTurn]);\n\n /** Plain ↑/↓ once a turn container itself has focus. */\n function onListKeyDown(event: KeyboardEvent<HTMLOListElement>) {\n const target = event.target as HTMLElement | null;\n if (!target?.hasAttribute('data-turn-key')) return;\n if (event.key === 'ArrowDown') { event.preventDefault(); stepTurn(1); }\n else if (event.key === 'ArrowUp') { event.preventDefault(); stepTurn(-1); }\n else if (event.key === 'Home') { event.preventDefault(); jumpToTurn(turns[0]!.key); }\n else if (event.key === 'End') { event.preventDefault(); jumpToTurn(turns[turns.length - 1]!.key); }\n }\n\n // ---- live turn -----------------------------------------------------------\n // The live STATUS line moved above the composer in 3.10 (Workspace\n // LiveRow); what stays here is the reasoning streaming into the turn.\n const running = transcript.live;\n const liveThinking = running && reasoning !== 'hidden' && live?.thinking && live.thinking.turnId === live.turnId ? live.thinking : null;\n const lastTurn = turns.length > 0 ? turns[turns.length - 1]! : null;\n\n // Auto-follow new content only while pinned to the bottom. \"New content\"\n // includes the live parts: streamed reasoning and a notice appearing.\n const lastItem = transcript.items[transcript.items.length - 1];\n const version = `${transcript.items.length}:${lastItem?.kind === 'assistant' ? lastItem.text.length : 0}:${liveThinking?.text.length ?? -1}:${running ? 1 : 0}`;\n useLayoutEffect(() => {\n if (version === lastVersion.current) return;\n lastVersion.current = version;\n if (following) scrollToBottom();\n else setUnseen(true);\n }, [version, following, scrollToBottom]);\n\n useEffect(() => {\n if (loaded) scrollToBottom();\n }, [loaded, scrollToBottom]);\n\n if (!loaded && !loadError) {\n return (\n <div className={styles.transcript} aria-busy=\"true\">\n <div className={styles.skeleton}>\n <div className={styles.skeletonUser}><SkeletonLine width=\"42%\" /></div>\n <SkeletonLine width=\"88%\" /><SkeletonLine width=\"72%\" /><SkeletonLine width=\"80%\" />\n <div className={styles.skeletonUser}><SkeletonLine width=\"30%\" /></div>\n <SkeletonLine width=\"64%\" />\n </div>\n </div>\n );\n }\n\n if (loadError) {\n return (\n <div className={styles.transcript}>\n <div role=\"alert\" className={styles.loadError}>\n <p><span className={styles.noteLead}>Couldn’t load this chat.</span> {loadError}</p>\n {onRetry ? <button type=\"button\" onClick={onRetry}>Retry</button> : null}\n </div>\n </div>\n );\n }\n\n const activeMatchKey = matchIndex >= 0 ? matches[matchIndex]?.turnKey ?? null : null;\n\n return (\n <PathRootsContext.Provider value={projectRoots ?? inheritedRoots}>\n <div className={styles.transcriptWrap}>\n {turns.length >= NAV_MIN_TURNS ? (\n <TranscriptNav turns={turns} query={query} onQuery={setQuery} matches={matches} matchIndex={matchIndex}\n onStepMatch={stepMatch} errorCount={errorAnchors.length} onJumpError={jumpToFirstError}\n onJumpTurn={jumpToTurn} focusToken={focusToken} />\n ) : null}\n {/* The log is NOT a live region (3.10): streamed tokens announced one by\n one made a screen reader unusable mid-turn. TurnAnnouncer says only\n when a turn starts, finishes, fails or is stopped. */}\n <TurnAnnouncer running={running} lastStatus={lastTurn?.status ?? null} lastKey={lastTurn?.key ?? null} />\n <div className={styles.scrollArea}>\n <div ref={scroller} className={styles.transcript} onScroll={onScroll} role=\"log\" aria-live=\"off\"\n aria-label=\"Conversation\" data-has-rail={turns.length >= NAV_MIN_TURNS || undefined}>\n {handoffFrom ? (\n <p className={styles.continued} data-kind=\"handoff-from\">\n <span className={styles.continuedLabel}>Continued from</span>{' '}\n {onOpenSession ? (\n <button type=\"button\" className={styles.continuedLink} onClick={() => onOpenSession(handoffFrom.sessionId)}\n title=\"Open the chat this one continues\">\n {handoffFrom.title || 'Untitled chat'}\n </button>\n ) : <span className={styles.continuedTitle}>{handoffFrom.title || 'Untitled chat'}</span>}\n </p>\n ) : null}\n {transcript.items.length === 0 && !running ? (\n <div className={styles.empty}>\n <p className={styles.emptyTitle}>{handoffFrom ? 'Review the handoff, then send it.' : 'Say something to begin.'}</p>\n <p className={styles.emptyBody}>{emptyHint ?? (handoffFrom\n ? 'The note in the message box was drafted from the previous chat\\'s log. Edit it freely — nothing is sent until you press Send.'\n : 'The agent can read and edit this project. Ask for a change, a review, or an explanation.')}</p>\n </div>\n ) : null}\n <ol className={styles.list} onKeyDown={onListKeyDown}>\n {perSegment.map((segment, s) => segment.turns.map((turn, t) => (\n <TurnView key={turn.key} turn={turn} index={turnOffsets[s]! + t} facts={segment.facts} explained={segment.explained}\n engine={engine} match={matchKeys.has(turn.key) ? (turn.key === activeMatchKey ? 'active' : 'true') : undefined}\n reasoning={reasoning} onJumpTool={jumpToTool} onJumpAnchor={jumpToAnchor} registerNode={registerTurn} />\n )))}\n {/* The reasoning streaming right now — it becomes an ordinary\n thinking item the moment its persisted block lands. */}\n {liveThinking ? (\n <li className={styles.item} data-kind=\"thinking-live\">\n <ThinkingBlock key={`live-${liveThinking.startedAt}`} text={liveThinking.text} streaming startedAt={liveThinking.startedAt}\n estimatedTokens={liveThinking.estimatedTokens} defaultOpen />\n </li>\n ) : null}\n </ol>\n </div>\n {turns.length >= NAV_MIN_TURNS ? (\n <ChapterRail model={chapters} onJumpTurn={jumpToTurn} />\n ) : null}\n </div>\n {!following ? (\n <button type=\"button\" className={`${styles.jump} ${unseen ? styles.jumpUnseen : ''}`}\n onClick={() => { setFollowing(true); setUnseen(false); scrollToBottom('smooth'); }}>\n <ArrowDownIcon size={12} /> Jump to latest\n </button>\n ) : null}\n </div>\n </PathRootsContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// One turn\n// ---------------------------------------------------------------------------\n\ninterface TurnViewProps {\n turn: TurnBlock;\n index: number;\n facts: Map<string, ToolFacts>;\n explained: ReadonlySet<string>;\n engine?: VerseEngine;\n match: 'active' | 'true' | undefined;\n reasoning: ReasoningDisplay;\n onJumpTool: (toolUseId: string) => void;\n onJumpAnchor: (id: string) => boolean;\n registerNode: (key: string, node: HTMLElement | null) => void;\n}\n\n/**\n * Memoized on its props, all of which are stable for a finished turn: the\n * TurnBlock comes from a cached segment model, the callbacks are stable, and\n * `match` only changes for turns a search touches. So a streamed token\n * re-renders exactly one TurnView.\n */\nconst TurnView = memo(function TurnView({ turn, index, facts, explained, engine, match, reasoning, onJumpTool, onJumpAnchor, registerNode }: TurnViewProps) {\n const running = turn.status === 'running';\n // How long a settled turn took (turn-model: from its turn-done; unknown →\n // null, never 0). The turn's footer, not a line of its own between turns.\n const tookMs = running ? null : turn.durationMs;\n const errorJump = turn.errorCount > 0 && turn.firstErrorAnchor ? turn.firstErrorAnchor : null;\n return (\n <li\n id={turnAnchorId(turn.key)}\n ref={(node) => registerNode(turn.key, node)}\n // A settled turn no longer changes: let the browser skip its layout\n // and paint while it is off screen (Transcript.module.css .turnSettled).\n className={`${styles.turn} ${running ? '' : styles.turnSettled}`}\n data-turn-key={turn.key}\n data-status={turn.status}\n data-match={match}\n tabIndex={-1}\n aria-label={`Turn ${index + 1}`}\n >\n <ol className={styles.turnItems}>\n {turn.items.map((item) => renderItem(item, facts, explained, reasoning, engine))}\n </ol>\n {turn.files.length > 0 ? <FileActivity files={turn.files} onJump={onJumpTool} /> : null}\n {tookMs !== null || errorJump ? (\n <footer className={styles.turnFoot} data-kind=\"turn-meta\">\n {tookMs !== null ? (\n <span><span className=\"visually-hidden\">Turn took </span><span>{formatDuration(tookMs)}</span></span>\n ) : null}\n {errorJump ? (\n <button type=\"button\" className={styles.turnErrorJump} onClick={() => onJumpAnchor(errorJump)}>\n {turn.errorCount} failure{turn.errorCount === 1 ? '' : 's'} in this turn — jump to the first\n </button>\n ) : null}\n </footer>\n ) : null}\n </li>\n );\n});\n\n/**\n * A tool card re-renders only when what it shows changed. `result` is a\n * fresh object on every rebuild of the live turn, so identity is not enough;\n * `input` and `facts` are stable (the event's own object, the facts cache).\n */\nconst MemoToolUseCard = memo(ToolUseCard, (a: ToolUseCardProps, b: ToolUseCardProps) =>\n a.name === b.name && a.input === b.input && a.toolUseId === b.toolUseId && a.durationMs === b.durationMs &&\n a.facts === b.facts && a.result?.output === b.result?.output && a.result?.isError === b.result?.isError &&\n (a.result === null) === (b.result === null));\n\nfunction renderItem(item: TranscriptRenderItem, facts: Map<string, ToolFacts>, explained: ReadonlySet<string>, reasoning: ReasoningDisplay, engine?: VerseEngine) {\n switch (item.kind) {\n case 'user':\n return (\n <li key={item.key} className={`${styles.item} ${styles.user}`} data-kind=\"user\">\n <div className={styles.userText}>{item.text}</div>\n </li>\n );\n case 'assistant':\n return (\n <li key={item.key} className={`${styles.item} ${styles.assistant}`} data-kind=\"assistant\" data-streaming={item.streaming || undefined}>\n <MessageMarkdown text={item.text} streaming={item.streaming} />\n </li>\n );\n case 'thinking':\n if (reasoning === 'hidden') return null;\n return (\n <li key={item.key} className={styles.item} data-kind=\"thinking\">\n <ThinkingBlock text={item.text} redacted={item.redacted} durationMs={item.durationMs}\n estimatedTokens={item.estimatedTokens} kind={item.thinkingKind}\n // 3.10: a settled block folds (it streamed in its three-line\n // window while it ran) unless Settings ▸ Chat says Expanded.\n defaultOpen={reasoning === 'expanded'} stateKey={`thinking:${item.key}`} />\n </li>\n );\n case 'tool':\n return (\n <li key={item.key} className={styles.item} data-kind=\"tool\">\n <MemoToolUseCard name={item.name} input={item.input} result={item.result} toolUseId={item.toolUseId}\n durationMs={item.durationMs} facts={facts.get(item.toolUseId)} startedAt={item.at} />\n </li>\n );\n case 'toolGroup':\n return (\n <li key={item.key} className={styles.item} data-kind=\"tool-group\">\n <ActivityGroup item={item} facts={facts} reasoning={reasoning} />\n </li>\n );\n case 'error': {\n const hint = errorCodeHint(item.code);\n return (\n <li key={item.key} className={styles.item} data-kind=\"error\" data-code={item.code ?? undefined}>\n <div id={noteAnchorId(item.key)} role=\"alert\" className={`${styles.note} ${styles.noteError}`}>\n {/* The state in words — unless the CLI's own message already opens with it. */}\n {/^error\\b/i.test(item.message) ? null : <><span className={styles.noteLead}>Error</span>{' '}</>}{item.message}\n {hint ? <span className={styles.noteHint}>{hint}</span> : null}\n </div>\n </li>\n );\n }\n case 'compaction':\n // A divider, not a message: the conversation continues on both sides of\n // it, but the agent sees everything above it only as a summary.\n return (\n <li key={item.key} className={`${styles.item} ${styles.compaction}`} data-kind=\"compaction\">\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n <span className={styles.compactionText}>\n <span className={styles.compactionTitle}>{describeCompaction(item, engine)}</span>\n <span className={styles.compactionNote}>Earlier turns now reach the agent only as a summary; the full transcript stays here.</span>\n </span>\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n </li>\n );\n case 'truncated':\n return (\n <li key={item.key} className={`${styles.item} ${styles.compaction}`} data-kind=\"history-truncated\">\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n <span className={styles.compactionText}>\n <span className={styles.compactionTitle}>Older history trimmed</span>\n <span className={styles.compactionNote}>This chat’s log reached its size cap, so its oldest turns were dropped here. The agent’s own conversation is unaffected.</span>\n </span>\n <span className={styles.compactionRule} aria-hidden=\"true\" />\n </li>\n );\n case 'recovered':\n return (\n <li key={item.key} className={styles.item} data-kind=\"recovered\">\n <div className={`${styles.note} ${styles.noteRecovered}`} role=\"status\">\n <span className={styles.noteLead}>{item.how === 'handoff' ? 'Recovered from the handoff note' : 'Recovered in a new session'}</span>\n {' — '}{item.message}\n </div>\n </li>\n );\n case 'cancelled':\n return (\n <li key={item.key} className={styles.item} data-kind=\"cancelled\">\n <div className={styles.note}>Stopped.</div>\n </li>\n );\n case 'turn-done':\n // A clean (or already-explained) end is not a line of its own, and how\n // long ANY turn took is its footer (TurnView), never this item.\n if (item.ok || explained.has(item.key)) return null;\n return (\n <li key={item.key} className={styles.item} data-kind=\"turn-done\">\n <div id={noteAnchorId(item.key)} className={`${styles.note} ${styles.noteError}`}>\n <span className={styles.noteLead}>Failed</span> Turn ended without a result.\n </div>\n </li>\n );\n default:\n return null;\n }\n}\n\nfunction sameMembers(a: ToolGroupItem, b: ToolGroupItem, fa: Map<string, ToolFacts>, fb: Map<string, ToolFacts>): boolean {\n if (a === b) return fa === fb || a.items.every((m) => m.kind !== 'tool' || fa.get(m.toolUseId) === fb.get(m.toolUseId));\n if (a.key !== b.key || a.items.length !== b.items.length || a.errorCount !== b.errorCount || a.pending !== b.pending ||\n a.spanMs !== b.spanMs || a.summary !== b.summary) return false;\n for (let i = 0; i < a.items.length; i += 1) {\n const x = a.items[i]!;\n const y = b.items[i]!;\n if (x.key !== y.key || x.kind !== y.kind) return false;\n if (x.kind === 'tool' && y.kind === 'tool') {\n if (x.result?.output !== y.result?.output || x.result?.isError !== y.result?.isError || x.durationMs !== y.durationMs) return false;\n if (fa.get(x.toolUseId) !== fb.get(y.toolUseId)) return false;\n } else if (x.kind === 'thinking' && y.kind === 'thinking') {\n if (x.text !== y.text || x.durationMs !== y.durationMs || x.estimatedTokens !== y.estimatedTokens) return false;\n }\n }\n return true;\n}\n\n/**\n * One member of a folded run: a tool card, or the reasoning that explained\n * the next call (hidden when Settings ▸ Chat says so).\n */\nfunction renderMember(member: ToolGroupMember, facts: Map<string, ToolFacts>, reasoning: ReasoningDisplay) {\n if (member.kind === 'tool') {\n return (\n <MemoToolUseCard name={member.name} input={member.input} result={member.result}\n toolUseId={member.toolUseId} durationMs={member.durationMs} facts={facts.get(member.toolUseId)} startedAt={member.at} />\n );\n }\n if (reasoning === 'hidden') return null;\n return (\n <ThinkingBlock text={member.text} redacted={member.redacted} durationMs={member.durationMs}\n estimatedTokens={member.estimatedTokens} kind={member.thinkingKind} defaultOpen={reasoning === 'expanded'}\n stateKey={`thinking:${member.key}`} />\n );\n}\n\n/**\n * The folded run (3.10: chat/ActivityGroup — \"Ran 12 commands · read 8 files ·\n * edited 3 files · 1 failed\"). Re-rendered only when a member's visible facts\n * changed: the live turn rebuilds its item objects on every token, and\n * `sameMembers` compares what the row shows rather than object identity.\n */\nconst ActivityGroup = memo(function ActivityGroup({ item, facts, reasoning }: { item: ToolGroupItem; facts: Map<string, ToolFacts>; reasoning: ReasoningDisplay }) {\n const render = useCallback((member: ToolGroupMember) => renderMember(member, facts, reasoning), [facts, reasoning]);\n return <ActivityGroupView item={item} facts={facts} renderMember={render} />;\n}, (a, b) => a.reasoning === b.reasoning && sameMembers(a.item, b.item, a.facts, b.facts));\n","/**\n * routes/verse/SeatCapacity.tsx — the shared rendering of a seat's\n * subscription capacity, so the resources panel, the new-chat dialog and the\n * chat header cannot describe the same seat three different ways.\n *\n * Three pieces, each one a rule from docs/VERSE-DESIGN-V2.md §6 and\n * docs/VERSE-TELEMETRY-V2.md made concrete:\n *\n * <CapacityChip> the four-word verdict. Always the WORD, never colour\n * alone; the dot is decoration on top of the word.\n * <SeatWindowMeter> one quota window. A measured window gets a real\n * <Meter>; a flagged limit gets a full danger meter\n * labelled \"limit reached\" and NO percentage, because\n * the sentinel 100 upstream is a flag rather than a\n * reading; an unread window gets a dashed rule and a\n * sentence, never a bar — an empty bar reads \"plenty\n * left\" and a full one reads \"exhausted\", and both are\n * lies when nothing was measured.\n * <SeatCapacityNote> plan · verdict · binding meter · reset · credits, for\n * the surfaces that show one seat at a time.\n *\n * Reset text is rendered VERBATIM. Claude publishes a sentence\n * (\"resets Sep 25 at 7pm (America/New_York)\") and `resetsAt` is structurally\n * null for it, so nothing here parses a reset or turns one into a countdown.\n */\nimport type { ReactNode } from 'react';\nimport type { VerseSeat } from '../../data/api-types.js';\nimport { Meter } from '../../components/primitives/Meter.js';\nimport { percentText } from './autonomy/format.js';\nimport {\n seatSubscription,\n type SeatCapacityClass,\n type SeatSubscriptionView,\n type SeatWindowView,\n} from './seat-subscription.js';\nimport styles from './SeatCapacity.module.css';\n\n/**\n * A glyph per class so the state survives greyscale, a colour-blind reader\n * and a screenshot: check (usable), half (tight), bar (blocked), query\n * (no reading). Paired with the word, never standing in for it.\n */\nconst CLASS_GLYPH: Record<SeatCapacityClass, string> = {\n ready: '✓',\n tight: '◑',\n blocked: '▮',\n unread: '?',\n};\n\nexport function CapacityChip({\n view,\n title,\n}: {\n view: SeatSubscriptionView;\n title?: string;\n}): ReactNode {\n return (\n <span className={styles.chip} data-capacity={view.cls} title={title ?? view.summary}>\n <span className={styles.chipGlyph} aria-hidden=\"true\">{CLASS_GLYPH[view.cls]}</span>\n {view.word}\n </span>\n );\n}\n\nexport function SeatWindowMeter({\n windowView,\n ariaPrefix,\n prominent = false,\n}: {\n windowView: SeatWindowView;\n /** Seat label, so every meter on the page has a unique accessible name. */\n ariaPrefix: string;\n /** The binding window — the one that actually constrains work. */\n prominent?: boolean;\n}): ReactNode {\n const name = `${ariaPrefix} ${windowView.label}`;\n\n if (windowView.limitReached) {\n return (\n <div className={prominent ? styles.windowLead : styles.window}>\n <Meter\n value={100}\n max={100}\n tone=\"danger\"\n label={<span className={styles.windowLabel}>{windowView.label}</span>}\n valueText={<span className={styles.flag}>limit reached</span>}\n aria-label={`${name}: the provider flagged this window as rate-limited, so no percentage was measured`}\n />\n {windowView.resetText === null ? null : <p className={styles.reset}>{windowView.resetText}</p>}\n </div>\n );\n }\n\n if (windowView.usedPercent === null) {\n return (\n <div className={prominent ? styles.windowLead : styles.window}>\n <div className={styles.unknownHead}>\n <span className={styles.windowLabel}>{windowView.label}</span>\n <span className={styles.unknownValue}>no reading</span>\n </div>\n {/* Absence, drawn as absence. Never a 0% bar. */}\n <hr className={styles.unknownRule} aria-hidden=\"true\" />\n {windowView.resetText === null ? null : <p className={styles.reset}>{windowView.resetText}</p>}\n </div>\n );\n }\n\n return (\n <div className={prominent ? styles.windowLead : styles.window}>\n <Meter\n value={Math.max(0, Math.min(100, windowView.usedPercent))}\n max={100}\n label={<span className={styles.windowLabel}>{windowView.label}</span>}\n valueText={<span className={styles.pct}>{percentText(windowView.usedPercent)}</span>}\n aria-label={`${name} used`}\n />\n {windowView.resetText === null ? null : <p className={styles.reset}>{windowView.resetText}</p>}\n </div>\n );\n}\n\n/**\n * One seat's capacity in a few lines, for the surfaces that show a single\n * seat: the new-chat dialog under the picker, and anywhere else a choice is\n * about to be made. Local seats say so instead of being given meters.\n */\nexport function SeatCapacityNote({ seat, className }: { seat: VerseSeat; className?: string }): ReactNode {\n const view = seatSubscription(seat);\n return (\n <div className={`${styles.note} ${className ?? ''}`} data-capacity={view.cls}>\n <div className={styles.noteHead}>\n <CapacityChip view={view} />\n {view.plan === null ? null : <span className={styles.plan}>{view.plan}</span>}\n <span className={styles.noteSummary}>{view.summary}</span>\n </div>\n {view.binding === null ? null : (\n <SeatWindowMeter windowView={view.binding} ariaPrefix={seat.label} prominent />\n )}\n {view.credits === null ? null : (\n <p className={styles.credits} title={view.creditsTitle ?? undefined}>\n {view.credits} — credits are independent of the window\n </p>\n )}\n </div>\n );\n}\n","/**\n * routes/verse/health/SeatHealthBanner.tsx — \"which of my seats can't work,\n * why, and what fixes it\", in one strip (V3.10, unit A2).\n *\n * Renders NOTHING while every seat is fine: a banner that is always there is\n * a banner nobody reads. When something is wrong it names the seat, the fact\n * (signed out / out of usage / sign-in expiring / older CLI pinned), the\n * reset time when the provider gave one, and the one action that fixes it:\n * - Reconnect (signed out, expiring) — the server opens the seat's OWN\n * sign-in in Terminal. The app cannot and does not sign in for you.\n * - the repin command, copyable (older CLI pinned) — repinning changes\n * which models the seat runs, so it stays the operator's decision.\n * - nothing but the reset time (out of usage).\n * \"Check again\" runs a sweep now (status commands only, zero cost).\n *\n * Two exports: `SeatHealthBanner` fetches and polls on its own;\n * `SeatHealthBannerView` renders reports it is handed (tests, or a parent\n * that already holds them).\n */\nimport { useState } from 'react';\nimport type { SeatHealthReport } from '../../../../core/verse/health-types.js';\nimport { Button } from '../../../components/primitives/Button.js';\nimport { IconAlert, IconCopy, IconExternalLink, IconRefresh } from '../../../components/primitives/icons.js';\nimport type { VerseSeat } from '../../../data/api-types.js';\nimport { reconnectSeat, refreshSeatHealth } from './health-queries.js';\nimport { issuesHeadline, seatHealthIssues, shellCommandText, type SeatHealthIssue } from './health-model.js';\nimport { useSeatHealth } from './useSeatHealth.js';\nimport styles from './SeatHealth.module.css';\n\nexport interface SeatHealthBannerViewProps {\n reports: readonly SeatHealthReport[];\n /** For labels; a seat missing here is named by its id. */\n seats?: readonly VerseSeat[];\n onReconnect?: (seatId: string) => Promise<void>;\n onRefresh?: () => Promise<unknown>;\n /** Injected clock for tests. */\n now?: number;\n}\n\ntype RowStatus = { text: string; error: boolean } | null;\n\nfunction errorText(error: unknown, fallback: string): string {\n return error instanceof Error && error.message ? error.message : fallback;\n}\n\nfunction IssueRow({ issue, onReconnect }: { issue: SeatHealthIssue; onReconnect: (seatId: string) => Promise<void> }) {\n const [busy, setBusy] = useState(false);\n const [status, setStatus] = useState<RowStatus>(null);\n const { report } = issue;\n const command = report.fix.command && report.fix.command.length > 0 ? shellCommandText(report.fix.command) : null;\n\n const reconnect = async (): Promise<void> => {\n setBusy(true);\n setStatus(null);\n try {\n await onReconnect(report.seatId);\n setStatus({ text: 'Sign-in opened in Terminal. Finish there, then choose Check again.', error: false });\n } catch (error) {\n setStatus({ text: errorText(error, 'The sign-in window could not be opened.'), error: true });\n } finally {\n setBusy(false);\n }\n };\n\n const copy = async (): Promise<void> => {\n if (command === null) return;\n try {\n await navigator.clipboard.writeText(command);\n setStatus({ text: 'Command copied.', error: false });\n } catch {\n setStatus({ text: 'Copy failed — select the command and copy it by hand.', error: true });\n }\n };\n\n return (\n <li className={styles.row} data-tone={issue.tone} data-seat={report.seatId}>\n <div className={styles.rowHead}>\n <span className={styles.dot} aria-hidden=\"true\" />\n <span className={styles.label}>{issue.label}</span>\n <span className={styles.word}>{issue.word}</span>\n {issue.reset === null ? null : (\n <span className={styles.reset}>\n {issue.reset}\n {issue.usableAgain === null ? null : ` · ${issue.usableAgain}`}\n </span>\n )}\n </div>\n {issue.detail === null ? null : <p className={styles.detail}>{issue.detail}</p>}\n {report.fix.kind === 'reauth' || command !== null ? (\n <div className={styles.actions}>\n {report.fix.kind === 'reauth' ? (\n <Button size=\"sm\" variant=\"primary\" icon={<IconExternalLink />} busy={busy} onClick={() => { void reconnect(); }}\n aria-label={`Reconnect ${issue.label}`}>\n Reconnect\n </Button>\n ) : null}\n {command !== null ? (\n <>\n <code className={styles.command}>{command}</code>\n <Button size=\"sm\" variant=\"subtle\" icon={<IconCopy />} onClick={() => { void copy(); }}\n aria-label={`Copy the repin command for ${issue.label}`}>\n Copy\n </Button>\n </>\n ) : null}\n </div>\n ) : null}\n <p className={styles.status} aria-live=\"polite\" data-error={status?.error ? 'true' : undefined}>\n {status?.text ?? ''}\n </p>\n </li>\n );\n}\n\nexport function SeatHealthBannerView({\n reports,\n seats = [],\n onReconnect = reconnectSeat,\n onRefresh = refreshSeatHealth,\n now,\n}: SeatHealthBannerViewProps) {\n const [checking, setChecking] = useState(false);\n const [checkError, setCheckError] = useState<string | null>(null);\n const issues = seatHealthIssues(reports, seats, now);\n if (issues.length === 0) return null;\n const tone = issues[0]!.tone;\n\n const check = async (): Promise<void> => {\n setChecking(true);\n setCheckError(null);\n try {\n await onRefresh();\n } catch (error) {\n setCheckError(errorText(error, 'The check could not run.'));\n } finally {\n setChecking(false);\n }\n };\n\n return (\n <section className={styles.banner} data-tone={tone} aria-label=\"Seat health\">\n <div className={styles.head}>\n <IconAlert />\n <span className={styles.headline}>{issuesHeadline(issues)}</span>\n <Button size=\"sm\" variant=\"ghost\" icon={<IconRefresh />} busy={checking} onClick={() => { void check(); }}>\n Check again\n </Button>\n </div>\n <ul className={styles.list}>\n {issues.map((issue) => <IssueRow key={issue.report.seatId} issue={issue} onReconnect={onReconnect} />)}\n </ul>\n {checkError === null ? null : <p className={styles.status} data-error=\"true\" role=\"status\">{checkError}</p>}\n </section>\n );\n}\n\nexport interface SeatHealthBannerProps {\n seats?: readonly VerseSeat[];\n /** False stops polling (the last reading still renders). Default true. */\n active?: boolean;\n}\n\n/** Self-fetching banner: polls `/api/verse/health` every 30 s while visible. */\nexport function SeatHealthBanner({ seats = [], active = true }: SeatHealthBannerProps) {\n const health = useSeatHealth(active);\n const reports = health.data?.seats ?? [];\n return <SeatHealthBannerView reports={reports} seats={seats} />;\n}\n","/**\n * routes/verse/Workspace.tsx — the chat pane: the header strip, the 720px\n * transcript, and — stacked above the composer — everything about the turn\n * you are about to send or are waiting on.\n *\n * THE HEADER (3.10, SPEC-310C §2) reads left to right, in the order a person\n * asks the questions:\n *\n * [ hub › v310-foundation ] [C Ready] [insight] …… [◔ 42%] [⋯] │ [>_][▭][±] │ [⫿][▯]\n * [ Chat title ]\n *\n * 1. WHERE AM I — the chat title under a `repo › branch` breadcrumb (the\n * branch from the chat's shared roots read). The only thing in the strip\n * allowed to shrink; both lines ellipsise.\n * 2. WHAT STATE IS IT IN — a compact status: an engine tick and its monogram\n * (C/X/G/L, never a vendor logo), then Ready / Running / Last turn failed /\n * Read-only, and a capacity chip only when the seat is tight or spent.\n * 3.10.1: the seat and model are NOT repeated here — the composer's seat\n * chip names them, one step below; the chip's tooltip still says both,\n * and the chip is a tab stop so that tooltip opens on keyboard focus.\n * 3. WHAT DO I KNOW — C7's insight chip when an A7 insight cites this chat.\n * 4. WHAT CAN I DO — the context ring (its tooltip is the whole context\n * story), then ⋯: context mode, compact, hand off, copy id, delete.\n * 5. WHICH PANES — Terminal, Preview and Review toggles (each only once its\n * unit's pane exists in this build), then the chat list and the dock.\n * Every icon-only control has an accessible name AND a tooltip (the\n * Tooltip primitive, so it shows on keyboard focus too) with its key.\n *\n * NOTHING stacks under the header any more. Seat health, the engine's\n * retry / watchdog notices, context advice and the compact panel share ONE\n * notice slot above the composer (chat/NoticeSlot), in order of consequence.\n * Under it, in this order and each only when it has something to say:\n *\n * notice one at a time, \"+N more\"\n * queued C3's \"Queued, sends when this turn ends [Edit] [Send now] [×]\",\n * portalled out of the Composer into a host here (its state\n * lives in the Composer; its PLACE is this order)\n * live row ● Running npm test · 1m 02s · 38 tok/s Stop ◌ 3 running tasks\n * branch bar C5's slot: repo branch +35,079 −1,074 [Create PR ▾]\n * composer C3's — wired to the handoff (`/handoff`, Continue on ‹seat›)\n * and to the composer bridge (text the dock drafts in)\n *\n * With nothing selected the strip is not empty: it says \"Chat / No chat\n * selected\", and the seat-health banner still shows (a signed-out seat is\n * news before you pick a chat).\n *\n * This component owns the seat poll (useSeatsRefresh) because it is mounted\n * for the whole life of the Chat section — see that hook for why a\n * mount-time snapshot of seats is guaranteed to be the cold one.\n */\nimport { lazy, Suspense, useCallback, useEffect, useId, useMemo, useRef, useState, type ComponentProps, type KeyboardEvent, type ReactNode } from 'react';\nimport type { VerseContextMode, VerseProject, VerseSeat, VerseSession } from '../../data/api-types.js';\nimport { engineSupportsModes, hasExpansiveMode } from '../../../core/verse/context-math.js';\nimport { MutationTokenDialog } from '../../components/auth/MutationTokenDialog.js';\nimport { Tooltip } from '../../components/primitives/Tooltip.js';\nimport { ActionMenu, anchorBelow, type ActionMenuItem } from './chat/ActionMenu.js';\nimport { appendParagraph, registerComposerInserter } from './chat/composer-bridge.js';\nimport { derivePhaseFromTranscript, LiveStatus } from './chat/LiveStatus.js';\nimport { NoticeSlot, type NoticeCandidate } from './chat/NoticeSlot.js';\nimport { PathRootsContext } from './chat/path-display.js';\nimport { TasksTray } from './chat/TasksTray.js';\nimport { countTasks, currentTurnTasks, type ChatTask } from './chat/tasks-model.js';\nimport { useSessionRoots } from './chat/use-session-roots.js';\nimport { Composer } from './Composer.js';\nimport { setSessionContextMode } from './context/context-queries.js';\nimport { describeContextError, useTokenGate } from './context/use-token-gate.js';\nimport {\n canCompactNow,\n CompactPanel,\n ContextAdvice,\n contextAdviceVisible,\n ContextMeter,\n CONTEXT_MODE_LABEL,\n expansiveCostCopy,\n standardSwitchCompacts,\n} from './ContextMeter.js';\nimport { CopyGlyph, HandoffGlyph, MoreGlyph, RenameGlyph, TrashGlyph, DOCK_PANE_GLYPH } from './dock/dock-icons.js';\nimport { openDockPane, requestDiff, toggleDock, toggleDockPane, useDock } from './dock/dock-store.js';\nimport { DOCK_PANE_LABEL, isPaneAvailable } from './dock/dock-panes.js';\nimport { saveDraft } from './chat/composer-state.js';\nimport type { SeatChoice } from './SeatSelector.js';\nimport { BranchBarSlot, SessionInsightChipSlot } from './shell/slots.js';\nimport type { DockPaneId } from './shell/dock-catalog.js';\nimport { findCommand, formatChord } from './shell/command-catalog.js';\nimport { Transcript } from './Transcript.js';\nimport { PanelIcon, SidebarIcon, VerseMark } from './verse-icons.js';\nimport { CapacityChip } from './SeatCapacity.js';\nimport { seatHealthIssues } from './health/health-model.js';\nimport { SeatHealthBannerView } from './health/SeatHealthBanner.js';\nimport { useSeatHealth } from './health/useSeatHealth.js';\nimport { seatSubscription, seatSubscriptionSentence, worthFlagging } from './seat-subscription.js';\nimport { projectName, seatById, seatPillLabel, sessionContextBudget, type SessionContextBudget } from './verse-model.js';\nimport { invalidateVerseLists } from './verse-queries.js';\nimport { formatTokens, lastTurnActivityAt } from './verse-readouts.js';\nimport { setVerseSession, type VerseLiveNotice } from './verse-store.js';\nimport { rememberVerseSeat } from './verse-ui-store.js';\nimport { useSeatsRefresh } from './useSeatsRefresh.js';\nimport { useVerseLive, type VerseSessionView } from './useVerseSession.js';\nimport { useVerseTranscript } from './useVerseTranscript.js';\nimport styles from './Workspace.module.css';\nimport { ProviderLogo } from '../../components/primitives/ProviderLogo.js';\n\n// Opened on request only: kept out of the chat's first-paint chunk.\nconst HandoffDialog = lazy(() => import('./context/HandoffDialog.js').then((m) => ({ default: m.HandoffDialog })));\n\nexport interface WorkspaceProps {\n view: VerseSessionView;\n seats: readonly VerseSeat[];\n projects: readonly VerseProject[];\n dispatchEnabled: boolean;\n locked: boolean;\n hasAnySessions: boolean;\n onSend: (text: string) => Promise<boolean>;\n onStop: () => void;\n onRename: (title: string) => Promise<boolean>;\n /** Asks for confirmation (ChatSection's dialog), then deletes. */\n onRequestDelete: () => void;\n onSeatChange: (choice: SeatChoice) => void;\n onNew: () => void;\n onRetry: () => void;\n sidebarCollapsed: boolean;\n onToggleSidebar: () => void;\n /**\n * V3.9. Switch the Chat section to another chat — the handoff flow lands\n * on the chat it just created, and \"Continued from …\" links back. Absent →\n * the new chat is announced and left for the operator to open.\n */\n onOpenSession?: (sessionId: string) => void;\n /** 3.10: the handoff dialog is lifted (the dock's Context pane and a sidebar row open it too). */\n handoffOpen: boolean;\n onHandoffOpenChange: (open: boolean) => void;\n /** 3.10: other chats running right now, for the \"◌ N running tasks\" chip. */\n otherRunning: readonly ChatTask[];\n}\n\nexport function Workspace(props: WorkspaceProps) {\n const { view, seats, projects, dispatchEnabled, locked, hasAnySessions, onSend, onStop, onRename, onRequestDelete,\n onSeatChange, onNew, onRetry, sidebarCollapsed, onToggleSidebar, onOpenSession, handoffOpen, onHandoffOpenChange,\n otherRunning } = props;\n const session = view.session;\n const [editing, setEditing] = useState(false);\n const [draftTitle, setDraftTitle] = useState('');\n const [busy, setBusy] = useState(false);\n const [modeState, setModeState] = useState<{ busy: boolean; error: string | null }>({ busy: false, error: null });\n const [compactOpen, setCompactOpen] = useState(false);\n /** The notice the operator just asked for — shown ahead of the priority order. */\n const [pinnedNotice, setPinnedNotice] = useState<string | null>(null);\n /** Set when a handoff chat was created but there is no host callback to switch to it. */\n const [handoffCreated, setHandoffCreated] = useState<{ title: string } | null>(null);\n const [menu, setMenu] = useState<{ anchor: { x: number; y: number }; from: HTMLElement } | null>(null);\n const [copied, setCopied] = useState(false);\n /** Text another pane drafted in (Review \"Add to message\", Terminal \"Send selection to chat\"). */\n const [insertRequest, setInsertRequest] = useState<{ nonce: number; text: string } | null>(null);\n /** \"Continue on ‹seat›\": the seat the handoff should open on. */\n const [handoffTarget, setHandoffTarget] = useState<SeatChoice | null>(null);\n /** Where the composer's \"Queued turns\" row renders — between the notice slot and the live row. */\n const [queueHost, setQueueHost] = useState<HTMLDivElement | null>(null);\n const titleInput = useRef<HTMLInputElement>(null);\n const headingId = useId();\n const newChatShortcut = shortcutFor('chat.new');\n\n // Mounted for the whole life of the section, so the roster stays live.\n useSeatsRefresh();\n const roots = useSessionRoots(session);\n\n useEffect(() => {\n setEditing(false);\n setModeState({ busy: false, error: null });\n setCompactOpen(false);\n setHandoffCreated(null);\n setPinnedNotice(null);\n setMenu(null);\n setInsertRequest(null);\n setHandoffTarget(null);\n }, [view.sessionId]);\n\n // The composer bridge (chat/composer-bridge.ts): while THIS chat's composer\n // is on screen and can take text, the dock's \"Send selection to chat\" and\n // Review's \"Add to message\" land in its draft through `insertRequest` —\n // never sent, one append per nonce. Registered only when a composer is\n // mounted and enabled: otherwise the bridge's caller says \"Open a chat…\"\n // instead of dropping the text into a box that is not there.\n const composerSessionId = view.session?.id ?? null;\n // The last request the composer has rendered. Child effects run before\n // this parent's, so by the time this effect runs the Composer has taken it.\n const deliveredInsert = useRef(0);\n useEffect(() => {\n if (insertRequest) deliveredInsert.current = insertRequest.nonce;\n }, [insertRequest]);\n useEffect(() => {\n if (!composerSessionId || !dispatchEnabled) return undefined;\n return registerComposerInserter(composerSessionId, (text) => {\n setInsertRequest((prev) => {\n const nonce = (prev?.nonce ?? 0) + 1;\n // Two inserts inside one render (batched) would otherwise show the\n // composer only the second: an undelivered one is merged, not lost.\n const undelivered = prev !== null && prev.nonce > deliveredInsert.current;\n return { nonce, text: undelivered ? appendParagraph(prev.text, text) : text };\n });\n });\n }, [composerSessionId, dispatchEnabled]);\n\n // The mode switch is a write: without a held token it parks, asks for the\n // token with a reason, and runs once unlocked.\n const gate = useTokenGate();\n const gateRun = gate.run;\n\n const sessionId = view.session?.id ?? null;\n /**\n * Standard ⇄ expansive. Explicit only, and it applies from the next turn.\n * The server answers with the updated record (budget fields recomputed),\n * which replaces the store's copy so the ring moves at once.\n */\n const changeMode = useCallback(async (mode: VerseContextMode) => {\n if (!sessionId) return;\n setModeState({ busy: true, error: null });\n try {\n const updated = await gateRun('Changing the context mode changes the compaction flag this chat runs with from its next turn.',\n () => setSessionContextMode(sessionId, mode));\n if (updated) setVerseSession(updated.id, updated);\n setModeState({ busy: false, error: null });\n } catch (err) {\n setModeState({ busy: false, error: describeContextError(err) });\n }\n }, [sessionId, gateRun]);\n\n /**\n * The handoff dialog created a chat. Its first message is NOT sent: the\n * note goes into the new chat's composer draft, and the operator reads it\n * and presses Send — that press is the first spend.\n */\n const onHandoffCreated = useCallback((created: VerseSession, text: string) => {\n saveDraft(created.id, text);\n rememberVerseSeat(created.projectPath, { seatId: created.seatId, model: created.model });\n setVerseSession(created.id, created);\n invalidateVerseLists();\n onHandoffOpenChange(false);\n if (onOpenSession) onOpenSession(created.id);\n else {\n setHandoffCreated({ title: created.title });\n setPinnedNotice('handoff-created');\n }\n }, [onOpenSession, onHandoffOpenChange]);\n\n // Focus, then select: select() alone does not move focus everywhere, and\n // the ⋯ menu hands focus back to its trigger as it closes.\n useEffect(() => {\n if (!editing) return;\n titleInput.current?.focus();\n titleInput.current?.select();\n }, [editing]);\n\n const lastTurnAtMemo = useMemo(() => lastTurnActivityAt(view.events), [view.events]);\n\n if (!view.sessionId) {\n return (\n <section className={styles.workspace} aria-label=\"Chat\">\n <header className={styles.header} data-app-region=\"drag\">\n <Lockup\n eyebrow={<span className={styles.eyebrow}>Chat</span>}\n title={\n <span className={styles.titleStatic} data-state=\"empty\">\n {hasAnySessions ? 'No chat selected' : 'No chats yet'}\n </span>\n }\n />\n <div className={styles.headerSpacer} />\n <div className={styles.actions}><PaneToggles sidebarCollapsed={sidebarCollapsed} onToggleSidebar={onToggleSidebar} hasSession={false} /></div>\n </header>\n <WorkspaceSeatHealth seats={seats} />\n <div className={styles.emptyState}>\n <span className={styles.emptyMark} aria-hidden=\"true\"><VerseMark size={36} /></span>\n <h1 className={styles.emptyTitle}>{hasAnySessions ? 'Pick a chat, or start a new one' : `No chats yet${newChatShortcut ? ` — ${newChatShortcut}` : ''}`}</h1>\n <p className={styles.emptyBody}>\n Open a project, choose a seat — a Claude or Codex account, Grok, or a local Ollama model — and talk to an agent that can edit that project.\n </p>\n <button type=\"button\" className={styles.emptyButton} onClick={onNew}>New chat {newChatShortcut ? <kbd>{newChatShortcut}</kbd> : null}</button>\n {!dispatchEnabled ? (\n <p className={styles.emptyWarn} role=\"status\">\n This server was started without dispatch, so chats are read-only here. Run <code>ashlr verse</code> to enable sending.\n </p>\n ) : null}\n </div>\n </section>\n );\n }\n\n async function commitTitle() {\n const next = draftTitle.trim();\n setEditing(false);\n if (!session || !next || next === session.title) return;\n setBusy(true);\n try {\n await onRename(next);\n } finally {\n setBusy(false);\n }\n }\n\n function onTitleKey(event: KeyboardEvent<HTMLInputElement>) {\n if (event.key === 'Enter') {\n event.preventDefault();\n void commitTitle();\n } else if (event.key === 'Escape') {\n event.preventDefault();\n setEditing(false);\n }\n }\n\n const running = session?.status === 'running';\n const budget = session ? sessionContextBudget(seats, session) : null;\n const mode: VerseContextMode = session?.contextMode ?? 'standard';\n // Only what the CLI can do: a model with a real expansive budget on an\n // engine that takes a per-invocation budget — or a session already in\n // expansive, which must always be able to switch back.\n const modesAvailable = session !== null && budget !== null && engineSupportsModes(session.engine) &&\n (hasExpansiveMode(budget.option) || mode === 'expansive');\n const activeSeat = session ? seatById(seats, session.seatId) ?? null : null;\n const capacity = activeSeat === null ? null : seatSubscription(activeSeat);\n /** The capacity worth a chip in the header: tight or spent, else none. */\n const flagged = capacity !== null && worthFlagging(capacity.cls) ? capacity : null;\n // The status chip's tooltip: what the chat runs on (seat · model), then the\n // seat's capacity sentence without its repeated label. The project path is\n // the breadcrumb's tooltip, not this one's.\n const capacityLine = activeSeat === null || capacity === null\n ? null\n : seatSubscriptionSentence(activeSeat, capacity).replace(`${activeSeat.label} · `, '');\n const seatTitle = session === null\n ? ''\n : `Runs on ${seatPillLabel(seats, session)}${capacityLine ? ` — ${capacityLine}` : ''}`;\n const disabledReason = !dispatchEnabled ? 'Sending is disabled: this server was started without dispatch.' : null;\n const runningReason = 'Available when the current turn finishes.';\n const compactable = session !== null && canCompactNow(session.engine);\n const openCompact = compactable ? () => { setHandoffCreated(null); setCompactOpen(true); setPinnedNotice('compact'); } : undefined;\n const compactUnavailableReason = session !== null && session.turnCount === 0 ? 'Nothing to compact yet — this chat has no turns.' : null;\n const handoffReason = disabledReason ?? (running ? runningReason : null);\n const openHandoff = () => { setHandoffCreated(null); setHandoffTarget(null); onHandoffOpenChange(true); };\n /**\n * \"Continue on ‹seat›\" from the composer's seat chip: the handoff (note\n * drafted from this chat's log, spends nothing) opened for that seat. While\n * the handoff is unavailable (a turn running, dispatch off) it falls back\n * to a plain new chat on that seat, which the seat chip already offered.\n */\n const continueOn = (choice: SeatChoice) => {\n if (handoffReason !== null) {\n onSeatChange(choice);\n return;\n }\n setHandoffCreated(null);\n setHandoffTarget(choice);\n onHandoffOpenChange(true);\n };\n const breadcrumb = session ? projectName(session.projectPath, projects) : '';\n // The header's compact status: the chat's state in a word — never the seat\n // or model (the composer says those). data-status is the session's own.\n const statusWord = session === null ? '' : session.status === 'running' ? 'Running'\n : session.status === 'error' ? 'Last turn failed' : dispatchEnabled ? 'Ready' : 'Read-only';\n\n // ---- ⋯ menu -------------------------------------------------------------\n const menuItems: ActionMenuItem[] = [];\n if (session) {\n if (modesAvailable && budget) {\n const other: VerseContextMode = mode === 'standard' ? 'expansive' : 'standard';\n const downgrade = other === 'standard' ? standardSwitchCompacts(budget.option, mode, budget) : null;\n // Switching DOWN past Standard's compaction point is not a free flag\n // flip: the CLI compacts on the next turn. Said in the item, not after.\n const downgradeCopy = downgrade === null ? null : downgrade.definite\n ? `This chat holds ≈${formatTokens(downgrade.tokens)}, past Standard's ≈${formatTokens(downgrade.point)} compaction point — it compacts on the next turn.`\n : `This chat holds up to ≈${formatTokens(downgrade.tokens)}; past ≈${formatTokens(downgrade.point)} it compacts on the next turn.`;\n const reason = !dispatchEnabled ? disabledReason : running ? 'The mode cannot change mid-turn. ' + runningReason : modeState.busy ? 'Switching…' : null;\n menuItems.push({\n id: 'mode',\n label: `Context: switch to ${CONTEXT_MODE_LABEL[other]}`,\n icon: <span aria-hidden=\"true\">◔</span>,\n description: other === 'expansive'\n ? expansiveCostCopy(budget.option, session.engine)\n : downgradeCopy ?? `Now ${CONTEXT_MODE_LABEL[mode]}. Applies from the next turn.`,\n disabled: reason !== null,\n reason,\n onSelect: () => { void changeMode(other); },\n });\n }\n if (compactable) {\n const reason = disabledReason ?? (running ? runningReason : compactUnavailableReason);\n menuItems.push({ id: 'compact', label: 'Compact now…', disabled: reason !== null, reason, onSelect: () => openCompact?.() });\n }\n menuItems.push({\n id: 'handoff',\n label: 'Continue in a fresh chat…',\n icon: <HandoffGlyph size={14} />,\n disabled: handoffReason !== null,\n reason: handoffReason,\n onSelect: openHandoff,\n });\n menuItems.push({\n id: 'rename',\n label: 'Rename',\n icon: <RenameGlyph size={14} />,\n disabled: !dispatchEnabled,\n reason: disabledReason,\n onSelect: () => { setDraftTitle(session.title); setEditing(true); },\n });\n menuItems.push({\n id: 'copy-id',\n label: copied ? 'Copied' : 'Copy chat id',\n icon: <CopyGlyph size={14} />,\n onSelect: () => {\n void navigator.clipboard?.writeText(session.id).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1200);\n }, () => undefined);\n },\n });\n menuItems.push({\n id: 'delete',\n label: 'Delete chat…',\n icon: <TrashGlyph size={14} />,\n danger: true,\n separated: true,\n disabled: !dispatchEnabled || running,\n reason: running ? 'Stop the turn before deleting.' : disabledReason,\n onSelect: onRequestDelete,\n });\n }\n\n return (\n // Paths the chat draws (the transcript, the live row) read relative to its roots.\n <PathRootsContext.Provider value={roots.roots}>\n <section className={styles.workspace} aria-labelledby={headingId}>\n <header className={styles.header} data-app-region=\"drag\">\n <Lockup\n eyebrow={session ? (\n // The full path is the tooltip; the strip shows `repo › branch`.\n <span className={styles.eyebrow} title={session.projectPath} data-testid=\"chat-breadcrumb\">\n {breadcrumb}\n {roots.branch ? <><span className={styles.crumbSep} aria-hidden=\"true\"> › </span><span className=\"visually-hidden\"> on branch </span><span className={styles.crumbBranch}>{roots.branch}</span></> : null}\n </span>\n ) : (\n <span className={`${styles.eyebrow} skeleton ${styles.eyebrowSkeleton}`} aria-hidden=\"true\" />\n )}\n title={session ? (\n editing ? (\n <input ref={titleInput} className={styles.titleInput} value={draftTitle} aria-label=\"Chat title\"\n onChange={(event) => setDraftTitle(event.target.value)} onBlur={() => { void commitTitle(); }}\n onKeyDown={onTitleKey} maxLength={120} />\n ) : (\n <h1 id={headingId} className={styles.title}>\n <button type=\"button\" className={styles.titleButton} title=\"Rename chat\" disabled={busy || !dispatchEnabled}\n onClick={() => { setDraftTitle(session.title); setEditing(true); }}>\n {session.title || 'Untitled chat'}\n </button>\n </h1>\n )\n ) : (\n <h1 id={headingId} className={`${styles.titleStatic} skeleton ${styles.titleSkeleton}`} aria-label=\"Loading chat\" />\n )}\n />\n\n {session ? (\n // The chat's STATE, not its seat: the composer's seat chip names\n // seat and model; this chip's tooltip keeps them one hover away.\n // That tooltip is now the header's only copy of seat, model and the\n // capacity sentence, so the chip takes focus (a tab stop, named for\n // its state): the Tooltip primitive opens on focus and makes the\n // sentence the chip's aria-describedby, as for every icon control.\n <Tooltip label={seatTitle} placement=\"bottom\">\n <span className={`${styles.seatPill} ${styles[`engine-${session.engine}`] ?? ''}`} data-engine={session.engine}\n tabIndex={0} role=\"group\" aria-label={`Chat status: ${statusWord}${flagged ? `, ${flagged.word}` : ''}`}\n data-status={session.status} data-testid=\"chat-status\"\n data-seat-capacity={flagged?.cls}>\n <span className={styles.engineTick} aria-hidden=\"true\" />\n <span className={styles.monogram} aria-hidden=\"true\"><ProviderLogo engine={session.engine} size={13} /></span>\n <span className={styles.seatText}>{statusWord}</span>\n {flagged ? <CapacityChip view={flagged} /> : null}\n </span>\n </Tooltip>\n ) : null}\n {session ? <SessionInsightChipSlot sessionId={session.id} /> : null}\n\n <div className={styles.headerSpacer} />\n\n {/* One cluster, and it never shrinks: the lockup truncates instead. */}\n <div className={styles.actions}>\n {view.stream === 'reconnecting' ? <span className={styles.streamState} role=\"status\">reconnecting…</span> : null}\n {session && budget ? (\n <ContextMeter variant=\"ring\" contextTokens={budget.contextTokens} contextWindow={budget.contextWindow} autoCompactAt={budget.autoCompactAt}\n exact={budget.exact} source={budget.source} mode={modesAvailable ? mode : null} engine={session.engine}\n compactionCount={session.compactionCount ?? 0} />\n ) : null}\n {modeState.error ? <span className={styles.modeError} role=\"alert\">{modeState.error}</span> : null}\n {session ? (\n <Tooltip label=\"Chat actions\" placement=\"bottom\">\n <button type=\"button\" className={styles.ghostIcon} aria-label=\"Chat actions\" aria-haspopup=\"menu\"\n aria-expanded={menu !== null}\n onClick={(event) => setMenu(menu ? null : { anchor: anchorBelow(event.currentTarget, 'end', 260), from: event.currentTarget })}>\n <MoreGlyph />\n </button>\n </Tooltip>\n ) : null}\n <span className={styles.actionDivider} aria-hidden=\"true\" />\n <PaneToggles sidebarCollapsed={sidebarCollapsed} onToggleSidebar={onToggleSidebar} hasSession={session !== null} />\n </div>\n </header>\n\n <LiveTranscript key={view.sessionId} sessionId={view.sessionId} transcriptOverride={view.transcript} loaded={view.loaded}\n loadError={view.loadError} onRetry={onRetry} engine={session?.engine} handoffFrom={session?.handoffFrom ?? null}\n onOpenSession={onOpenSession} />\n\n {session && budget ? (\n <ChatNotices session={session} seats={seats} budget={budget} modesAvailable={modesAvailable}\n dispatchEnabled={dispatchEnabled} modeBusy={modeState.busy} lastTurnAt={lastTurnAtMemo}\n compactOpen={compactable && compactOpen} running={running} onSend={onSend}\n onCloseCompact={() => { setCompactOpen(false); setPinnedNotice(null); }}\n onHandoff={openHandoff} onSwitchExpansive={() => { void changeMode('expansive'); }} onCompact={openCompact}\n handoffCreated={handoffCreated} onDismissHandoffCreated={() => setHandoffCreated(null)} pinnedId={pinnedNotice} />\n ) : null}\n\n {/* The composer's \"Queued turns\" row is portalled here, so the rows above\n the composer read notice → queued → live → branch bar (SPEC-310C §2).\n An empty host is not laid out (`:empty`). */}\n {session ? <div ref={setQueueHost} className={styles.queueHost} data-testid=\"queue-slot\" /> : null}\n\n {session ? <LiveRow sessionId={session.id} running={running} otherRunning={otherRunning} onStop={onStop} /> : null}\n\n {session ? (\n <BranchBarSlot sessionId={session.id} roots={roots.roots} onOpenDiff={requestDiff} />\n ) : null}\n\n {session ? (\n <Composer key={session.id} sessionId={session.id} seats={seats} seat={{ seatId: session.seatId, model: session.model }}\n engine={session.engine} running={running} disabled={!dispatchEnabled} disabledReason={disabledReason} locked={locked}\n hintSeen={session.turnCount > 0} contextTokens={budget?.contextTokens ?? null} contextWindow={budget?.contextWindow ?? null}\n autoCompactAt={budget?.autoCompactAt ?? null} contextExact={budget?.exact ?? true}\n handoffDraft={session.handoffFrom !== undefined && session.turnCount === 0}\n onSend={onSend} onStop={onStop} onSeatChange={onSeatChange}\n onHandoff={openHandoff} handoffDisabledReason={handoffReason} onContinueOn={continueOn}\n insertRequest={insertRequest} queueSlot={queueHost} autoFocus />\n ) : null}\n\n {menu && session ? (\n <ActionMenu label=\"Chat actions\" items={menuItems} anchor={menu.anchor} returnFocus={menu.from} onClose={() => setMenu(null)} />\n ) : null}\n\n {/* Mounted only while open: the dialog fetches a preview (git diff\n --stat on the server) and nothing should run for a closed one. */}\n {session && handoffOpen ? (\n <Suspense fallback={null}>\n {/* `initialTarget` preselects the seat \"Continue on ‹seat›\" named;\n undefined (the plain Hand off… entry) keeps the dialog's own\n default. HandoffDialog reads it once per open, so a pick made\n inside the dialog wins, and a seat that has since gone falls\n back to the default rather than preselecting nothing. */}\n <HandoffDialog session={session} seats={seats} open onClose={() => { setHandoffTarget(null); onHandoffOpenChange(false); }}\n onCreated={onHandoffCreated} initialTarget={handoffTarget ?? undefined} />\n </Suspense>\n ) : null}\n <MutationTokenDialog {...gate.dialog} tokenLabel=\"Mutation token\" tokenHelp=\"the mutation token ashlr verse printed\" />\n </section>\n </PathRootsContext.Provider>\n );\n}\n\n/**\n * The transcript, subscribed on its own (V3.10). Everything above re-renders\n * when the session HEAD changes; only this subtree re-renders as tokens and\n * reasoning stream in — at most once per animation frame. Keyed by chat, so\n * search, scroll and the turn announcer start fresh on every switch.\n */\nfunction LiveTranscript({ sessionId, transcriptOverride, ...props }: {\n sessionId: string;\n transcriptOverride: VerseSessionView['transcript'];\n} & Omit<ComponentProps<typeof Transcript>, 'transcript' | 'live'>) {\n const transcript = useVerseTranscript(sessionId);\n const live = useVerseLive(sessionId);\n return <Transcript {...props} transcript={transcriptOverride ?? transcript} live={live} />;\n}\n\n/**\n * \"● Running npm test · 1m 02s · 38 tok/s Stop\" on a 1px amber hairline,\n * with \"◌ 3 running tasks\" beside it. Its own subscription (transcript +\n * live signals) so the header above never re-renders per token. When this\n * chat is idle but others run, the tasks chip stands alone.\n */\nfunction LiveRow({ sessionId, running, otherRunning, onStop }: {\n sessionId: string;\n running: boolean;\n otherRunning: readonly ChatTask[];\n onStop: () => void;\n}) {\n const transcript = useVerseTranscript(sessionId);\n const live = useVerseLive(sessionId);\n const dock = useDock();\n const segments = transcript.segments;\n const lastItems = segments && segments.length > 0 ? segments[segments.length - 1]!.items : transcript.items;\n const liveThinking = live.thinking && live.thinking.turnId === live.turnId ? live.thinking : null;\n const derived = useMemo(() => derivePhaseFromTranscript(lastItems, { thinking: liveThinking }), [lastItems, liveThinking]);\n const tasks = useMemo(() => currentTurnTasks(lastItems), [lastItems]);\n const counts = useMemo(() => countTasks(running ? tasks : [], otherRunning), [running, tasks, otherRunning]);\n const showLive = running && transcript.live;\n if (!showLive && counts.total === 0) return null;\n const tasksActive = dock.state.open && (dock.state.active === 'tasks' || dock.state.splitWith === 'tasks');\n return (\n <div className={styles.liveRow} data-running={showLive || undefined}>\n {showLive ? <LiveStatus live={live} derived={derived} onStop={onStop} showNotice={false} /> : <span className={styles.liveSpacer} />}\n <TasksTray counts={counts} active={tasksActive} onOpen={() => openDockPane('tasks')} />\n </div>\n );\n}\n\nconst ENGINE_NOTICE_WORD: Readonly<Record<VerseLiveNotice['kind'], string>> = {\n retry: 'Retrying',\n preflight: 'Preflight',\n watchdog: 'Still running',\n};\n\n/** The advice notes re-check their evidence on this clock (idle-cache advice appears on time, not on the next render). */\nconst NOTICE_TICK_MS = 60_000;\n\n/**\n * Every notice candidate for this chat, handed to the ONE slot above the\n * composer (chat/NoticeSlot decides which shows). Subscribes to the live\n * signals itself (for the engine's retry / watchdog notice), so a streamed\n * token re-renders this small tree, not the header.\n */\nfunction ChatNotices(props: {\n session: VerseSession;\n seats: readonly VerseSeat[];\n budget: SessionContextBudget;\n modesAvailable: boolean;\n dispatchEnabled: boolean;\n modeBusy: boolean;\n lastTurnAt: string | null;\n compactOpen: boolean;\n running: boolean;\n onSend: (text: string) => Promise<boolean>;\n onCloseCompact: () => void;\n onHandoff: () => void;\n onSwitchExpansive: () => void;\n onCompact?: () => void;\n handoffCreated: { title: string } | null;\n onDismissHandoffCreated: () => void;\n pinnedId: string | null;\n}) {\n const { session, seats, budget, modesAvailable, dispatchEnabled, modeBusy, lastTurnAt, compactOpen, running, onSend,\n onCloseCompact, onHandoff, onSwitchExpansive, onCompact, handoffCreated, onDismissHandoffCreated, pinnedId } = props;\n const health = useSeatHealth();\n const live = useVerseLive(session.id);\n const [now, setNow] = useState(() => Date.now());\n const [, bump] = useState(0);\n useEffect(() => {\n const id = setInterval(() => setNow(Date.now()), NOTICE_TICK_MS);\n return () => clearInterval(id);\n }, []);\n\n const reports = health.data?.seats ?? [];\n const candidates: NoticeCandidate[] = [];\n if (seatHealthIssues(reports, seats).length > 0) {\n candidates.push({ id: 'seat-health', kind: 'seat-health', label: 'Seat health', render: () => <SeatHealthBannerView reports={reports} seats={seats} /> });\n }\n const notice = running && live.notice ? live.notice : null;\n if (notice) {\n candidates.push({\n id: `engine:${notice.kind}`,\n kind: 'engine',\n label: ENGINE_NOTICE_WORD[notice.kind],\n render: () => (\n <p className={styles.engineNotice} data-kind={notice.kind} role=\"status\">\n <span className={styles.engineNoticeWord}>{ENGINE_NOTICE_WORD[notice.kind]}</span>\n <span>{notice.message}</span>\n </p>\n ),\n });\n }\n if (contextAdviceVisible(session, budget, modesAvailable, Math.max(now, Date.now()), lastTurnAt)) {\n candidates.push({\n id: 'context-advice',\n kind: 'context',\n label: 'Context advice',\n render: () => (\n <ContextAdvice embedded session={session} budget={budget} modesAvailable={modesAvailable} dispatchEnabled={dispatchEnabled}\n modeBusy={modeBusy} onHandoff={onHandoff} onSwitchExpansive={onSwitchExpansive} onCompact={onCompact} lastTurnAt={lastTurnAt}\n onDismiss={() => bump((n) => n + 1)} />\n ),\n });\n }\n if (handoffCreated) {\n candidates.push({\n id: 'handoff-created',\n kind: 'context',\n label: 'Handoff started',\n render: () => (\n <p className={styles.adviceStatus} role=\"status\">\n Started “{handoffCreated.title}” — open it from the chat list; the handoff note is waiting in its message box.\n <button type=\"button\" className={styles.ghost} onClick={onDismissHandoffCreated}>Dismiss</button>\n </p>\n ),\n });\n }\n if (compactOpen) {\n candidates.push({\n id: 'compact',\n kind: 'compact',\n label: 'Compact now',\n render: () => (\n <CompactPanel engine={session.engine} budget={budget} running={running} dispatchEnabled={dispatchEnabled}\n empty={session.turnCount === 0} onSend={onSend} onClose={onCloseCompact} />\n ),\n });\n }\n return <NoticeSlot notices={candidates} pinnedId={pinnedId} />;\n}\n\n/**\n * The seat health banner with no chat open: it sits in the reading column\n * (there is no composer to sit above). Rendered only when there is something\n * to say, so a healthy roster leaves no padding behind.\n */\nfunction WorkspaceSeatHealth({ seats }: { seats: readonly VerseSeat[] }) {\n const health = useSeatHealth();\n const reports = health.data?.seats ?? [];\n if (seatHealthIssues(reports, seats).length === 0) return null;\n return (\n <div className={styles.advice}>\n <SeatHealthBannerView reports={reports} seats={seats} />\n </div>\n );\n}\n\n/** The identity lockup — breadcrumb over title, and the only flex item allowed to give up width. */\nfunction Lockup({ eyebrow, title }: { eyebrow: ReactNode; title: ReactNode }) {\n return (\n <div className={styles.identity}>\n {eyebrow}\n {title}\n </div>\n );\n}\n\nfunction shortcutFor(commandId: string): string | null {\n const chord = findCommand(commandId)?.keys[0];\n return chord ? formatChord(chord) : null;\n}\n\nconst PANE_COMMAND: Readonly<Record<'terminal' | 'preview' | 'diff', string>> = {\n terminal: 'dock.terminal',\n preview: 'dock.preview',\n diff: 'dock.diff',\n};\n\n/**\n * The pane toggles, as one group: Terminal, Preview and Review (each only\n * once its unit's pane is in this build), then the chat list and the dock.\n * All are aria-pressed buttons, so open/closed is spoken, not implied by a\n * name that changes under you. The action and its key are in the tooltip.\n *\n * The chat-list toggle keeps the 3.9 names — \"Show chat list\" collapsed,\n * \"Chat list\" expanded (the sidebar owns a \"Hide chat list\" button, and two\n * controls sharing one accessible name is an ambiguity, not a pair).\n */\nfunction PaneToggles({ sidebarCollapsed, onToggleSidebar, hasSession }: { sidebarCollapsed: boolean; onToggleSidebar: () => void; hasSession: boolean }) {\n const { state } = useDock();\n const shows = (pane: DockPaneId) => state.open && (state.active === pane || state.splitWith === pane);\n const panes = (['terminal', 'preview', 'diff'] as const).filter((pane) => hasSession && isPaneAvailable(pane));\n const dockKey = shortcutFor('dock.toggle');\n const listKey = shortcutFor('chat.sidebar');\n return (\n <div className={styles.toggles} role=\"group\" aria-label=\"Panels\">\n {panes.map((pane) => {\n const Icon = DOCK_PANE_GLYPH[pane];\n const key = shortcutFor(PANE_COMMAND[pane]);\n const on = shows(pane);\n return (\n <Tooltip key={pane} label={`${on ? 'Hide' : 'Show'} ${DOCK_PANE_LABEL[pane]}`} shortcut={key ?? undefined} placement=\"bottom\">\n <button type=\"button\" className={`${styles.ghostIcon} ${styles.paneToggle}`} aria-pressed={on} aria-label={DOCK_PANE_LABEL[pane]}\n onClick={() => toggleDockPane(pane)}>\n <Icon />\n </button>\n </Tooltip>\n );\n })}\n {panes.length > 0 ? <span className={`${styles.actionDivider} ${styles.paneToggle}`} aria-hidden=\"true\" /> : null}\n <Tooltip label={`${sidebarCollapsed ? 'Show' : 'Hide'} chat list`} shortcut={listKey ?? undefined} placement=\"bottom\">\n <button type=\"button\" className={styles.ghostIcon} onClick={onToggleSidebar} aria-pressed={!sidebarCollapsed}\n aria-label={sidebarCollapsed ? 'Show chat list' : 'Chat list'}>\n <SidebarIcon />\n </button>\n </Tooltip>\n <Tooltip label={`${state.open ? 'Hide' : 'Show'} the dock`} shortcut={dockKey ?? undefined} placement=\"bottom\">\n <button type=\"button\" className={styles.ghostIcon} onClick={toggleDock} aria-pressed={state.open} aria-label=\"Dock\">\n <PanelIcon />\n </button>\n </Tooltip>\n </div>\n );\n}\n"],"file":"Workspace-D1nP9BhD.js"}