synabun 2.0.0 → 2.0.7

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 (507) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/NOTICE +5 -0
  3. package/README.md +2 -1
  4. package/THIRD-PARTY-LICENSES.md +59 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +153 -18
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +26 -3
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/child-path.js +303 -0
  14. package/lib/claude-hooks.js +2 -1
  15. package/lib/data-home-migration.js +2 -0
  16. package/lib/desktop-app.js +265 -0
  17. package/lib/external-tools.js +7 -3
  18. package/lib/log-redact.js +102 -0
  19. package/lib/packaged-runtime.js +138 -11
  20. package/lib/start-launcher.js +88 -1
  21. package/mcp-server/dist/http.d.ts +3 -1
  22. package/mcp-server/dist/http.d.ts.map +1 -1
  23. package/mcp-server/dist/http.js +15 -4
  24. package/mcp-server/dist/http.js.map +1 -1
  25. package/mcp-server/dist/index.d.ts.map +1 -1
  26. package/mcp-server/dist/index.js +9 -6
  27. package/mcp-server/dist/index.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  33. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  34. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  35. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  36. package/mcp-server/dist/services/identity.d.ts +11 -0
  37. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  38. package/mcp-server/dist/services/identity.js +24 -0
  39. package/mcp-server/dist/services/identity.js.map +1 -1
  40. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  41. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  42. package/mcp-server/dist/services/neural-interface.js +34 -2
  43. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  44. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  45. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  46. package/mcp-server/dist/services/typesafe-config.js +4 -1
  47. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  48. package/mcp-server/dist/tools/agents.d.ts +10 -7
  49. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  50. package/mcp-server/dist/tools/agents.js +34 -13
  51. package/mcp-server/dist/tools/agents.js.map +1 -1
  52. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  53. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  54. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  55. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  56. package/mcp-server/dist/tui.js +2 -1
  57. package/mcp-server/dist/tui.js.map +1 -1
  58. package/mcp-server/dist/version.d.ts +5 -0
  59. package/mcp-server/dist/version.d.ts.map +1 -0
  60. package/mcp-server/dist/version.js +21 -0
  61. package/mcp-server/dist/version.js.map +1 -0
  62. package/mcp-server/package-lock.json +2 -2
  63. package/mcp-server/package.json +1 -3
  64. package/mcp-server/run.mjs +15 -9
  65. package/mcp-server/src/http.ts +18 -3
  66. package/mcp-server/src/index.ts +9 -6
  67. package/mcp-server/src/services/budget-judgments.ts +183 -0
  68. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  69. package/mcp-server/src/services/identity.ts +27 -0
  70. package/mcp-server/src/services/neural-interface.ts +34 -2
  71. package/mcp-server/src/services/typesafe-config.ts +4 -1
  72. package/mcp-server/src/tools/agents.ts +33 -14
  73. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  74. package/mcp-server/src/tui.ts +2 -1
  75. package/mcp-server/src/version.ts +22 -0
  76. package/neural-interface/i18n/en.json +2126 -209
  77. package/neural-interface/i18n/pt-BR.json +2123 -207
  78. package/neural-interface/lib/account-usage.js +163 -0
  79. package/neural-interface/lib/assistant-api.js +316 -57
  80. package/neural-interface/lib/assistant-brains/claude.js +19 -2
  81. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  82. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  83. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  84. package/neural-interface/lib/assistant-budget-split.js +408 -0
  85. package/neural-interface/lib/assistant-budget.js +94 -95
  86. package/neural-interface/lib/assistant-catalog.js +179 -27
  87. package/neural-interface/lib/assistant-config.js +91 -14
  88. package/neural-interface/lib/assistant-dispatch.js +494 -138
  89. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  90. package/neural-interface/lib/assistant-jev.js +26 -3
  91. package/neural-interface/lib/assistant-memory.js +24 -10
  92. package/neural-interface/lib/assistant-persona.js +73 -12
  93. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  94. package/neural-interface/lib/assistant-pricing.js +21 -6
  95. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  96. package/neural-interface/lib/assistant-route-gate.js +19 -3
  97. package/neural-interface/lib/assistant-router.js +373 -42
  98. package/neural-interface/lib/assistant-runtime.js +488 -59
  99. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  100. package/neural-interface/lib/assistant-test-density.js +142 -0
  101. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  102. package/neural-interface/lib/assistant-user-context.js +908 -0
  103. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  104. package/neural-interface/lib/assistant-user-learn.js +404 -0
  105. package/neural-interface/lib/assistant-user-threads.js +226 -0
  106. package/neural-interface/lib/assistant-user-writer.js +183 -0
  107. package/neural-interface/lib/augmented-path.js +317 -53
  108. package/neural-interface/lib/browser-background-tab.js +134 -0
  109. package/neural-interface/lib/browser-screenshot.js +678 -61
  110. package/neural-interface/lib/claude-accounts.js +342 -31
  111. package/neural-interface/lib/claude-agent-bridge.js +123 -9
  112. package/neural-interface/lib/claude-executable.js +6 -1
  113. package/neural-interface/lib/claude-model-catalog.js +33 -5
  114. package/neural-interface/lib/claude-panel-auth.js +221 -0
  115. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  116. package/neural-interface/lib/claude-temporary.js +46 -7
  117. package/neural-interface/lib/cli-install-api.js +137 -0
  118. package/neural-interface/lib/cli-install.js +797 -0
  119. package/neural-interface/lib/cli-path.js +130 -0
  120. package/neural-interface/lib/cli-resolver.js +744 -0
  121. package/neural-interface/lib/cli-update-planner.js +143 -7
  122. package/neural-interface/lib/cloudflared/install.js +533 -0
  123. package/neural-interface/lib/cloudflared/manager.js +545 -0
  124. package/neural-interface/lib/cloudflared/pins.js +115 -0
  125. package/neural-interface/lib/cloudflared/pins.json +63 -0
  126. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  127. package/neural-interface/lib/codex-account-auth.js +178 -0
  128. package/neural-interface/lib/codex-accounts.js +489 -0
  129. package/neural-interface/lib/codex-capabilities.js +2 -1
  130. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  131. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  132. package/neural-interface/lib/codex-panel-auth.js +62 -0
  133. package/neural-interface/lib/codex-session-settings.js +1 -1
  134. package/neural-interface/lib/codex-settings-schema.js +120 -0
  135. package/neural-interface/lib/desktop/config.js +3 -0
  136. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  137. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  138. package/neural-interface/lib/editor-completion.js +714 -0
  139. package/neural-interface/lib/editor-config.js +149 -0
  140. package/neural-interface/lib/editor-files-api.js +268 -0
  141. package/neural-interface/lib/editor-git-api.js +134 -0
  142. package/neural-interface/lib/file-explorer-api.js +1039 -0
  143. package/neural-interface/lib/file-explorer-system.js +355 -0
  144. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  145. package/neural-interface/lib/file-explorer-watch.js +239 -0
  146. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  147. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  148. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  149. package/neural-interface/lib/git-bash/install.js +529 -0
  150. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  151. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  152. package/neural-interface/lib/git-bash/manager.js +268 -0
  153. package/neural-interface/lib/git-bash/pins.js +206 -0
  154. package/neural-interface/lib/git-bash/pins.json +34 -0
  155. package/neural-interface/lib/git-bash/resolver.js +194 -0
  156. package/neural-interface/lib/git-diff-api.js +103 -0
  157. package/neural-interface/lib/git-read.js +234 -0
  158. package/neural-interface/lib/greeting/api.js +89 -0
  159. package/neural-interface/lib/greeting/budgets.js +90 -0
  160. package/neural-interface/lib/greeting/engine.js +461 -0
  161. package/neural-interface/lib/greeting/facts.js +320 -0
  162. package/neural-interface/lib/greeting/sources.js +252 -0
  163. package/neural-interface/lib/greeting/writer.js +139 -0
  164. package/neural-interface/lib/http-guards.js +189 -13
  165. package/neural-interface/lib/install-details.js +145 -0
  166. package/neural-interface/lib/machine-names.js +88 -0
  167. package/neural-interface/lib/mcp-installer.js +14 -2
  168. package/neural-interface/lib/memory-api.js +31 -7
  169. package/neural-interface/lib/native-loop-providers.js +26 -8
  170. package/neural-interface/lib/native-loop-runtime.js +9 -4
  171. package/neural-interface/lib/os-path.js +319 -0
  172. package/neural-interface/lib/plugin-installer.js +4 -2
  173. package/neural-interface/lib/project-storage.js +5 -7
  174. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  175. package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
  176. package/neural-interface/lib/pty-host/core.js +2 -1
  177. package/neural-interface/lib/pty-host/manager.js +27 -2
  178. package/neural-interface/lib/remote-policy.js +20 -11
  179. package/neural-interface/lib/request-gate.js +589 -0
  180. package/neural-interface/lib/session-title-generator.js +4 -3
  181. package/neural-interface/lib/system-about.js +340 -0
  182. package/neural-interface/lib/system-activity.js +66 -0
  183. package/neural-interface/lib/terminal-env.js +11 -3
  184. package/neural-interface/lib/terminal-path.js +55 -0
  185. package/neural-interface/lib/ui-blob-api.js +108 -0
  186. package/neural-interface/lib/ui-blob-store.js +68 -0
  187. package/neural-interface/lib/virtual-socket.js +12 -1
  188. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  189. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  190. package/neural-interface/lib/whatsapp/commands.js +13 -14
  191. package/neural-interface/lib/whatsapp/config.js +134 -20
  192. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  193. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  194. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  195. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  196. package/neural-interface/lib/whatsapp/manager.js +92 -32
  197. package/neural-interface/lib/whatsapp/paths.js +11 -1
  198. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  199. package/neural-interface/lib/whatsapp/redact.js +63 -0
  200. package/neural-interface/lib/whatsapp/service.js +343 -91
  201. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  202. package/neural-interface/package-lock.json +701 -2
  203. package/neural-interface/package.json +13 -2
  204. package/neural-interface/public/index.html +2 -0
  205. package/neural-interface/public/index2d.html +2 -0
  206. package/neural-interface/public/install-app.html +9 -6
  207. package/neural-interface/public/offline.html +19 -3
  208. package/neural-interface/public/onboarding.html +107 -2
  209. package/neural-interface/public/shared/account-i18n.js +7 -0
  210. package/neural-interface/public/shared/account-menu.js +539 -0
  211. package/neural-interface/public/shared/account-usage.js +19 -0
  212. package/neural-interface/public/shared/account-view.js +335 -0
  213. package/neural-interface/public/shared/agent-runtime-options.js +50 -14
  214. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  215. package/neural-interface/public/shared/assistant/asst-api.js +32 -2
  216. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  217. package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
  218. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  219. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  220. package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
  221. package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
  222. package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
  223. package/neural-interface/public/shared/assistant/asst-render.js +197 -23
  224. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  225. package/neural-interface/public/shared/assistant/asst-route.js +95 -343
  226. package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
  227. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  228. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  229. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  230. package/neural-interface/public/shared/assistant/asst-state.js +186 -2
  231. package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
  232. package/neural-interface/public/shared/blob-store.js +111 -22
  233. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  234. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  235. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  236. package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
  237. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  238. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  239. package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
  240. package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
  241. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  242. package/neural-interface/public/shared/claude-account-view.js +3 -0
  243. package/neural-interface/public/shared/cli-install-view.js +392 -0
  244. package/neural-interface/public/shared/cli-status.js +282 -22
  245. package/neural-interface/public/shared/constants.js +6 -1
  246. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  247. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  248. package/neural-interface/public/shared/cp/cp-styles.js +61 -0
  249. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  250. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  251. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  252. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  253. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  254. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  255. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  256. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  257. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  258. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  259. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  260. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  261. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  262. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  263. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  264. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  265. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  266. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  267. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  268. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  269. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  270. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  271. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  272. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  273. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  274. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  275. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  276. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  277. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  278. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  279. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  280. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  281. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  282. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  283. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  284. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  285. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  286. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  287. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  288. package/neural-interface/public/shared/file-links.js +339 -0
  289. package/neural-interface/public/shared/git-bash-view.js +145 -0
  290. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  291. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  292. package/neural-interface/public/shared/html-shell.js +161 -165
  293. package/neural-interface/public/shared/i18n.js +19 -1
  294. package/neural-interface/public/shared/install-details-view.js +86 -0
  295. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  296. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  297. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
  298. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  299. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
  300. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
  301. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  302. package/neural-interface/public/shared/pwa-install-page.js +32 -2
  303. package/neural-interface/public/shared/pwa-install.css +3 -3
  304. package/neural-interface/public/shared/pwa-install.js +100 -1
  305. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  306. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  307. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  308. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  309. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  310. package/neural-interface/public/shared/settings/settings-ia.js +33 -4
  311. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  312. package/neural-interface/public/shared/showcase-privacy.js +489 -0
  313. package/neural-interface/public/shared/start-bridge.js +238 -19
  314. package/neural-interface/public/shared/storage.js +30 -3
  315. package/neural-interface/public/shared/styles.css +1710 -770
  316. package/neural-interface/public/shared/synabun-mascot.js +68 -58
  317. package/neural-interface/public/shared/tunnel-view.js +167 -0
  318. package/neural-interface/public/shared/ui-claude-panel.js +369 -229
  319. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  320. package/neural-interface/public/shared/ui-detail.js +15 -0
  321. package/neural-interface/public/shared/ui-explorer.js +22 -0
  322. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  323. package/neural-interface/public/shared/ui-file-links.js +103 -0
  324. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  325. package/neural-interface/public/shared/ui-help.js +26 -0
  326. package/neural-interface/public/shared/ui-invite.js +126 -116
  327. package/neural-interface/public/shared/ui-judgments.js +10 -1
  328. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  329. package/neural-interface/public/shared/ui-loading.js +187 -19
  330. package/neural-interface/public/shared/ui-menubar.js +25 -33
  331. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  332. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  333. package/neural-interface/public/shared/ui-navbar.js +48 -22
  334. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  335. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  336. package/neural-interface/public/shared/ui-sessions.js +9 -3
  337. package/neural-interface/public/shared/ui-settings.js +509 -490
  338. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  339. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  340. package/neural-interface/public/shared/ui-sync.js +22 -0
  341. package/neural-interface/public/shared/ui-terminal.js +40 -5
  342. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  343. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  344. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  345. package/neural-interface/public/shared/ui-update.js +594 -5
  346. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  347. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  348. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  349. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  350. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  351. package/neural-interface/public/shared/worker-end.js +253 -0
  352. package/neural-interface/public/sw.js +2 -2
  353. package/neural-interface/public/variant/2d/main.js +10 -1
  354. package/neural-interface/public/variant/3d/main.js +10 -1
  355. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  356. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  357. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  358. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  359. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  360. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  361. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  362. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  363. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  364. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  365. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  366. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  367. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  368. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  369. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  370. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  371. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  372. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  373. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  374. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  375. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  376. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  377. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  378. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  379. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  380. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  381. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  382. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  383. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  384. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  385. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  386. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  387. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  388. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  389. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  390. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  391. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  392. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  393. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  394. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  395. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  396. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  397. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  398. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  399. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  400. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  401. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  402. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  403. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  404. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  405. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  406. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  407. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  408. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  409. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  410. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  411. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  412. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  413. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  414. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  415. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  416. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  417. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  418. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  419. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  420. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  421. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  422. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  423. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  424. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  425. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  426. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  427. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  428. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  429. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  430. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  431. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  432. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  433. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  434. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  435. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  436. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  437. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  438. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  439. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  440. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  441. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  442. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  443. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  444. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  445. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  446. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  447. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  448. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  449. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  450. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  451. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  452. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  453. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  454. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  455. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  456. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  457. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  458. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  459. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  460. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  461. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  462. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  463. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  464. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  465. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  466. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  467. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  468. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  469. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  470. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  471. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  472. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  473. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  474. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  475. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  476. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  477. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  478. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  479. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  480. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  481. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  482. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  483. package/neural-interface/scripts/build-vendor.mjs +78 -2
  484. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  485. package/neural-interface/server.js +2085 -1629
  486. package/neural-interface/templates/rulesets/README.md +1 -1
  487. package/package.json +5 -8
  488. package/setup.js +53 -15
  489. package/benchmarks/browser-live-results.json +0 -79
  490. package/benchmarks/browser-v2-results.json +0 -258
  491. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  492. package/neural-interface/public/category-logos/claude.svg +0 -1
  493. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  494. package/neural-interface/public/category-logos/openai.svg +0 -1
  495. package/neural-interface/public/category-logos/synabun.png +0 -0
  496. package/neural-interface/public/claude.svg +0 -1
  497. package/neural-interface/public/gallery.svg +0 -6
  498. package/neural-interface/public/leonardoai.svg +0 -1
  499. package/neural-interface/public/mcp.svg +0 -4
  500. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  501. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  502. package/neural-interface/public/sessionicon.svg +0 -8
  503. package/neural-interface/public/toolbar-album.svg +0 -8
  504. package/neural-interface/public/toolbar-question.svg +0 -6
  505. package/neural-interface/public/toolbar-sessions.svg +0 -6
  506. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  507. package/skills/synabun/synabunicon.png +0 -0
@@ -359,11 +359,19 @@ export function injectAssistantStyles() {
359
359
  }
360
360
  .asst-empty-title { color: var(--asst-text); font-family: var(--ff-heading, inherit); font-size: 20px; font-weight: 600; letter-spacing: -.01em; }
361
361
  .asst-empty-body { max-width: 440px; color: var(--asst-text-3); font-size: 13px; line-height: 1.55; }
362
+ /* The greeting (public/shared/greeting/) takes the lead's place when there is one. */
363
+ .asst-empty-greet { display: contents; }
364
+ .asst-empty.has-greeting .asst-empty-body { display: none; }
362
365
  .asst-suggestions { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; max-width: 560px; margin-top: 6px; }
363
366
  .asst-chip.asst-suggestion { --cap-h: var(--asst-pill-h-md); --cap-r: var(--asst-pill-r-md); --cap-px: var(--asst-pill-px-md); --cap-fs: 12px; justify-content: flex-start; text-align: left; }
364
367
  .asst-chip.asst-suggestion > .asst-icon svg { width: 20px; height: 14px; }
365
368
  .asst-chip.asst-suggestion > .asst-icon { color: var(--asst-text-3); }
366
369
  .asst-chip.asst-suggestion:is(:hover, :focus-visible) > .asst-icon { color: var(--asst-text); }
370
+ /* Project and Budget: shared md capsules under the lead, like the suggestions. */
371
+ .asst-empty-project { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; max-width: 100%; }
372
+ .asst-dd.asst-empty-control-btn { max-width: min(100%, 360px); }
373
+ .asst-empty-control-btn .asst-dd-label { max-width: 30ch; }
374
+ .asst-empty-project-key { flex-shrink: 0; color: var(--asst-text-3); font-weight: 400; }
367
375
 
368
376
  /* ── Messages ───────────────────────────────────────────────────── */
369
377
  .asst-msg { display: flex; flex-direction: column; min-width: 0; }
@@ -570,32 +578,61 @@ export function injectAssistantStyles() {
570
578
  color: var(--asst-text-2);
571
579
  }
572
580
 
573
- /* ── Route lines ────────────────────────────────────────────────── */
581
+ /* ── Route lines ────────────────────────────────────────────────────
582
+ One line at any width, in the cards' column (the rack's 560px, no
583
+ hang): the glyph, arrow, kind and mark hold; the rest (model ·
584
+ account · usage, or the state) takes only the room left (flex-basis 0)
585
+ and ellipsizes first; the class gives way once the rest is down to
586
+ its "…". The head's title carries the whole text. */
574
587
  .asst-route-line { font-size: 12.5px; }
575
588
  .asst-route-line-head {
576
- display: inline-flex;
589
+ position: relative;
590
+ display: flex;
577
591
  align-items: center;
578
592
  gap: 6px;
579
- max-width: calc(100% + 8px);
593
+ width: fit-content;
594
+ max-width: min(100%, 560px);
595
+ min-width: 0;
580
596
  min-height: 26px;
581
- margin-left: -8px;
582
597
  padding: 3px 8px;
598
+ overflow: hidden;
583
599
  border: 0;
584
600
  border-radius: var(--asst-r-ctl);
585
601
  background: transparent;
586
602
  color: var(--asst-text-2);
587
603
  font: inherit;
604
+ white-space: nowrap;
588
605
  text-align: left;
589
606
  cursor: pointer;
590
607
  }
591
608
  .asst-route-line-head:hover { background: var(--asst-fill-1); }
592
609
  .asst-route-glyph { color: var(--asst-brand); }
610
+ .asst-route-glyph svg, .asst-route-icon svg { width: 12px; height: 12px; }
611
+ .asst-route-glyph, .asst-route-arrow, .asst-route-kind, .asst-route-icon, .asst-route-chev { flex: none; }
593
612
  .asst-route-arrow, .asst-route-chev { color: var(--asst-text-4); }
594
613
  .asst-route-kind { color: var(--asst-text-3); font-size: 11.5px; }
595
- .asst-route-icon svg { width: 12px; height: 12px; }
596
- .asst-route-target { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--asst-text); font-weight: 500; }
614
+ .asst-route-class { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
615
+ .asst-route-rest { flex: 1 1 0; min-width: 1.2em; overflow: hidden; text-overflow: ellipsis; color: var(--asst-text-3); }
616
+ .asst-route-target { color: var(--asst-text); font-weight: 500; }
597
617
  .asst-route-meta, .asst-route-state { flex-shrink: 0; color: var(--asst-text-3); white-space: nowrap; }
618
+ .asst-route-meta:not([hidden]) + .asst-route-state::before { content: '· '; color: var(--asst-text-3); }
598
619
  .asst-route-line.pending .asst-route-state { color: var(--asst-warn); }
620
+ /* Live: the rack's sweep (asst-rk-sweep, asst-rack-styles.js) on the row's bottom edge, gold while
621
+ a run it dispatched works (the mark ticks like the rack's running tick), the warn tone while it
622
+ waits for your choice. Settled rows hold still. */
623
+ .asst-route-sweep { position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px; overflow: hidden; pointer-events: none; }
624
+ .asst-route-line[data-live] .asst-route-sweep::after {
625
+ content: '';
626
+ position: absolute;
627
+ top: 0;
628
+ bottom: 0;
629
+ left: 0;
630
+ width: 38%;
631
+ background: linear-gradient(90deg, transparent, var(--asst-brand), transparent);
632
+ animation: asst-rk-sweep 1.8s cubic-bezier(.45, 0, .55, 1) infinite;
633
+ }
634
+ .asst-route-line[data-live="waiting"] .asst-route-sweep::after { background: linear-gradient(90deg, transparent, var(--asst-warn), transparent); }
635
+ .asst-route-line[data-live="working"] .asst-route-icon { animation: asst-rk-tick 1.1s ease-in-out infinite; }
599
636
  .asst-route-line.muted .asst-route-glyph { color: var(--asst-text-4); }
600
637
  .asst-route-line.muted .asst-route-class { color: var(--asst-text-3); }
601
638
  .asst-route-chev { transition: transform var(--asst-dur-base) var(--asst-ease-standard); }
@@ -610,6 +647,39 @@ export function injectAssistantStyles() {
610
647
  color: var(--asst-text-3);
611
648
  font-size: 12px;
612
649
  }
650
+ /* ── Automation trace (one row per turn: asst-automation.js) ─────── */
651
+ .asst-transcript > .asst-auto + .asst-route-line,
652
+ .asst-transcript > .asst-route-line + .asst-auto,
653
+ .asst-transcript > .asst-msg.msg-user + .asst-auto,
654
+ .asst-transcript > .asst-msg.msg-assistant + .asst-auto { margin-top: 4px; }
655
+ .asst-auto .asst-auto-head { color: var(--asst-text-3); }
656
+ .asst-auto .asst-route-glyph { display: inline-flex; color: var(--asst-text-4); }
657
+ .asst-auto .asst-route-glyph svg { width: 12px; height: 12px; }
658
+ .asst-auto.learned .asst-route-glyph { color: var(--asst-brand); }
659
+ .asst-auto-title { flex-shrink: 0; color: var(--asst-text-2); white-space: nowrap; }
660
+ /* The summary is what gives way in a narrow panel: one line, ellipsized. */
661
+ .asst-auto .asst-auto-summary { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
662
+ .asst-auto.learned .asst-auto-state { color: var(--asst-brand); }
663
+ .asst-auto-detail { gap: 10px; }
664
+ .asst-auto-group { display: flex; flex-direction: column; gap: 4px; }
665
+ .asst-auto-h { color: var(--asst-text-3); font-size: 11px; font-weight: 500; letter-spacing: .02em; }
666
+ .asst-auto-row { display: flex; flex-direction: column; gap: 2px; color: var(--asst-text-2); }
667
+ .asst-auto-row.muted { color: var(--asst-text-3); }
668
+ .asst-auto-row.learned .asst-auto-text { color: var(--asst-text); }
669
+ .asst-auto-row-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; }
670
+ .asst-auto-tier { color: var(--asst-text-2); font-weight: 500; }
671
+ .asst-auto-meta { color: var(--asst-text-3); font-size: 11.5px; font-variant-numeric: tabular-nums; }
672
+ .asst-auto-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; }
673
+ pre.asst-auto-text {
674
+ padding: 6px 8px;
675
+ border-radius: var(--asst-r-ctl);
676
+ background: var(--asst-fill-1);
677
+ color: var(--asst-text-2);
678
+ font-family: var(--asst-mono);
679
+ font-size: 11.5px;
680
+ line-height: 1.45;
681
+ }
682
+ .asst-auto-ids { display: flex; flex-wrap: wrap; gap: 2px 10px; font-family: var(--asst-mono); font-size: 11px; }
613
683
  .asst-link { padding: 0; border: 0; background: transparent; color: var(--asst-focus); font: inherit; text-align: left; cursor: pointer; }
614
684
  .asst-link:hover { text-decoration: underline; }
615
685
  .asst-link:disabled { color: var(--asst-text-4); cursor: default; text-decoration: none; }
@@ -1038,6 +1108,11 @@ export function injectAssistantStyles() {
1038
1108
  /* Fields (toolbar and composer): glyph · seam · value ▾ */
1039
1109
  .asst-dd { flex: 0 1 auto; justify-content: flex-start; max-width: calc(18ch + 60px); }
1040
1110
  .asst-dd .asst-dd-label { min-width: 0; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; }
1111
+ .asst-dd-model .asst-dd-label.asst-model-account-label { display: flex; gap: 4px; max-width: 34ch; }
1112
+ .asst-model-account-label :is(.asst-model-name, .asst-model-account) { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1113
+ .asst-model-account-label .asst-model-name { flex: 1 1 auto; }
1114
+ .asst-model-account-label .asst-model-account { flex: 0 1 auto; min-width: 4ch; max-width: 12ch; }
1115
+ .asst-model-account-label .asst-model-mode { flex-shrink: 0; }
1041
1116
  /* A display name ("Sonnet 5", a project) is sans; a raw model id (opus[1m]) is mono. */
1042
1117
  .asst-dd.asst-dd-model[data-raw] .asst-dd-label { font-family: var(--asst-mono); font-weight: 400; }
1043
1118
  .asst-dd-caret {
@@ -1391,6 +1466,8 @@ export function injectAssistantStyles() {
1391
1466
  .asst-dd-item-main { display: flex; flex: 1; flex-direction: column; gap: 1px; min-width: 0; }
1392
1467
  .asst-dd-item-label, .asst-dd-item-desc { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
1393
1468
  .asst-dd-item-desc { color: var(--asst-text-3); font-size: 11px; }
1469
+ /* Test density: each level's description is a sentence, so it wraps instead of truncating. */
1470
+ .asst-tests-menu .asst-dd-item-desc { white-space: normal; }
1394
1471
  .asst-dd-item-tag { flex-shrink: 0; margin-left: auto; color: var(--asst-text-3); font-size: 11px; }
1395
1472
  .asst-dd-kbd { flex-shrink: 0; margin-left: auto; color: var(--asst-text-3); font-family: var(--asst-mono); font-size: 10.5px; }
1396
1473
  .asst-dd-sep { flex-shrink: 0; height: 1px; margin: 4px 6px; background: var(--asst-line); }
@@ -1460,11 +1537,50 @@ export function injectAssistantStyles() {
1460
1537
  .asst-account-label { color: var(--asst-text); }
1461
1538
  .asst-account-sub { color: var(--asst-text-3); font-size: 11px; }
1462
1539
  .asst-account-row input.asst-dd-input { margin: 0; }
1540
+ /* Claude accounts (claude-account-view.js): what each runs on, its credentials and their actions. */
1541
+ .asst-account-row.claude { flex-wrap: wrap; align-items: flex-start; }
1542
+ .asst-account-row.claude .asst-account-main { flex-basis: 60%; }
1543
+ .asst-account-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
1544
+ .asst-account-badge { flex-shrink: 0; padding: 1px 6px; border-radius: var(--asst-pill-r-xs); color: var(--asst-text-2); font-size: 10.5px; box-shadow: inset 0 0 0 1px var(--asst-line-2); }
1545
+ .asst-account-badge[data-tone="ok"] { color: var(--asst-text); box-shadow: inset 0 0 0 1px var(--asst-pill-on-ring); }
1546
+ .asst-account-badge[data-tone="warn"] { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
1547
+ .asst-account-creds { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; color: var(--asst-text-3); font-size: 11px; }
1548
+ .asst-account-cred { display: flex; align-items: center; gap: 6px; }
1549
+ .asst-account-cred-k { min-width: 120px; }
1550
+ .asst-account-actions { display: flex; flex-wrap: wrap; flex-basis: 100%; gap: 6px; margin-top: 6px; }
1551
+ .asst-account-form { display: flex; flex-basis: 100%; gap: 6px; margin-top: 6px; }
1552
+ .asst-account-form input { flex: 1; min-width: 0; }
1553
+ .asst-account-msg { flex-basis: 100%; margin-top: 4px; color: var(--asst-text-3); font-size: 11px; }
1554
+ .asst-dd.asst-dd-warn { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
1463
1555
  .asst-login-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-radius: var(--asst-r-field); box-shadow: inset 0 0 0 1px var(--asst-line-2); }
1464
1556
  .asst-login-code { color: var(--asst-text); font-family: var(--asst-mono); font-size: 15px; letter-spacing: .1em; }
1465
1557
 
1466
1558
  .asst-routes-modal { width: min(660px, 100%); }
1467
- .asst-budget-row { align-items: flex-start; }
1559
+ .asst-budget-modal { width: min(560px, calc(100vw - 24px)); }
1560
+ .asst-budget-controls { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; min-width: 0; }
1561
+ .asst-budget-controls > :is(.asst-modal-note, .asst-budget-field-error, .asst-budget-switch-label) { grid-column: 1 / -1; }
1562
+ .asst-budget-controls > .asst-btn { justify-self: start; }
1563
+ .asst-budget-controls .asst-budget-input { width: 96px; min-width: 0; box-sizing: border-box; }
1564
+ .asst-budget-switch-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--asst-text); cursor: pointer; }
1565
+ .asst-budget-switch { appearance: none; position: relative; width: 30px; height: 18px; margin: 0; border: 0; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-3); box-shadow: inset 0 0 0 1px var(--asst-line-2); cursor: pointer; }
1566
+ .asst-budget-switch::before { content: ''; position: absolute; width: 12px; height: 12px; top: 3px; left: 3px; border-radius: var(--asst-pill-r-xs); background: var(--asst-text-3); transition: transform var(--asst-dur-fast) var(--asst-ease-standard); }
1567
+ .asst-budget-switch:checked { background: var(--asst-brand); }
1568
+ .asst-budget-switch:checked::before { background: var(--asst-ink); transform: translateX(12px); }
1569
+ .asst-budget-controls :is(input, button):focus-visible { outline: 2px solid var(--asst-focus); outline-offset: 2px; }
1570
+ .asst-budget-controls input:disabled { opacity: .5; }
1571
+ .asst-budget-popover .asst-dd-list { padding: 8px; }
1572
+ .asst-budget-session { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--asst-line); }
1573
+ .asst-budget-total { font-family: var(--asst-mono); }
1574
+ .asst-budget-session[data-tone="warn"] .asst-budget-total, .asst-budget-dry { color: var(--asst-warn); }
1575
+ .asst-budget-session[data-tone="over"] .asst-budget-total { color: var(--asst-live); }
1576
+ .asst-budget-providers { display: flex; flex-direction: column; gap: 8px; }
1577
+ .asst-budget-provider { padding: 8px; border-radius: var(--asst-r-field); background: var(--asst-fill-1); overflow-wrap: anywhere; }
1578
+ .asst-budget-provider[data-eligible="false"] { color: var(--asst-text-3); }
1579
+ .asst-budget-provider-name { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
1580
+ .asst-budget-provider-money { margin-top: 4px; font: 12px/1.5 var(--asst-mono); }
1581
+ .asst-budget-brain { padding: 0 6px; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-2); font-size: 11.5px; }
1582
+ .asst-budget-dry { font-size: 12px; }
1583
+ .asst-budget-meter { width: 100%; accent-color: var(--asst-brand); }
1468
1584
  .asst-budget-desc, .asst-budget-meta { color: var(--asst-text-3); font-size: 11.5px; line-height: 1.4; }
1469
1585
  .asst-budget-meta { font-variant-numeric: tabular-nums; }
1470
1586
  .asst-budget-field-error { color: var(--asst-live); font-size: 11.5px; line-height: 1.4; }
@@ -1546,6 +1662,114 @@ export function injectAssistantStyles() {
1546
1662
  .asst-routes-row { flex-wrap: wrap; }
1547
1663
  }
1548
1664
 
1665
+ /* Model routes editor: Settings density on a desktop pointer. Scoped here
1666
+ so route cards, the model manager and other Assistant menus keep their UI. */
1667
+ .asst-routes-overlay { font-family: var(--stg-font, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Inter, sans-serif); }
1668
+ .asst-routes-overlay .asst-routes-modal { width: min(1040px, 100%); max-height: calc(100dvh - 32px); border-radius: var(--asst-r-card); line-height: 1.4; }
1669
+ .asst-routes-modal :is(.asst-btn, .asst-dd, .asst-iconbtn) { --cap-h: var(--asst-pill-h-sm); --cap-r: var(--asst-pill-r-xs); --cap-px: var(--asst-pill-px-sm); --cap-icon: 12px; --cap-gap: 4px; }
1670
+ .asst-routes-modal .asst-modal-head { min-height: 40px; padding: 8px 14px 8px 22px; font-size: 13px; }
1671
+ .asst-routes-modal .asst-modal-head .asst-icon { color: var(--asst-text-2); }
1672
+ .asst-routes-modal .asst-modal-close { width: var(--cap-h); height: var(--cap-h); }
1673
+ .asst-routes-modal .asst-modal-body { gap: 12px; padding: 12px 22px; scroll-padding: 28px 0; }
1674
+ .asst-routes-modal .asst-modal-foot { padding: 10px 22px; }
1675
+ .asst-routes-modal .asst-btn { height: var(--cap-h); min-height: var(--cap-h); padding: 0 10px; }
1676
+ .asst-routes-modal .asst-routes-section { flex-shrink: 0; gap: 6px; margin: 0; min-width: 0; }
1677
+ .asst-routes-modal .asst-routes-section-title { margin: 0; color: var(--asst-text); font-size: 13px; font-weight: 600; }
1678
+ .asst-routes-modal .asst-routes-modes { gap: 0; padding: 0; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-1); }
1679
+ .asst-routes-modal .asst-routes-mode { position: relative; flex-direction: row; align-items: center; justify-content: center; height: var(--asst-pill-h-sm); padding: 0 8px; gap: 0; min-width: 0; border-radius: var(--asst-pill-r-xs); box-shadow: none; color: var(--asst-text-2); }
1680
+ .asst-routes-mode input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
1681
+ .asst-routes-modal .asst-routes-mode:has(input:checked) { background: var(--asst-fill-3); color: var(--asst-text); box-shadow: inset 0 0 0 1px var(--asst-line-2); }
1682
+ .asst-routes-modal .asst-routes-mode-label { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; font-weight: 500; }
1683
+ .asst-routes-modal .asst-routes-mode-desc, .asst-routes-session-note { margin: 0; color: var(--asst-text-2); font-size: 12px; line-height: 1.45; overflow-wrap: anywhere; }
1684
+ .asst-routes-session-note { color: var(--asst-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1685
+ .asst-routes-mode:has(:focus-visible) { outline: 2px solid var(--asst-focus); outline-offset: 2px; z-index: 1; }
1686
+ .asst-routes-section-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
1687
+ .asst-routes-list-actions { display: flex; flex-wrap: wrap; gap: 8px; }
1688
+ .asst-routes-modal .asst-routes-list-actions .asst-btn { background: transparent; box-shadow: none; padding: 0 2px; color: var(--asst-text-2); font-weight: 400; }
1689
+ .asst-routes-modal .asst-routes-list-actions .asst-btn:hover:not(:disabled) { color: var(--asst-text); background: var(--asst-fill-1); }
1690
+ /* Reserve the widths the current model names, effort words and destinations
1691
+ need; give all remaining space to task descriptions instead of a share. */
1692
+ .asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { display: grid; grid-template-columns: minmax(100px, 1fr) minmax(120px, 200px) 70px 116px minmax(120px, 190px) var(--asst-pill-h-sm); column-gap: 6px; }
1693
+ .asst-routes-columns { position: sticky; top: -14px; z-index: 1; padding: 4px 8px; color: var(--asst-text-3); font-size: 11.5px; background: var(--asst-popover); }
1694
+ .asst-routes-columns span:last-child { visibility: hidden; }
1695
+ .asst-routes-modal .asst-routes-list { padding: 0 8px; border: 1px solid var(--asst-line); border-radius: var(--radius-group, 9px); background: var(--asst-field); }
1696
+ .asst-routes-modal .asst-routes-row { align-items: center; row-gap: 0; padding: 2px 0; border-top: 1px solid var(--asst-line); }
1697
+ .asst-routes-modal .asst-routes-row:first-child { border-top: 0; }
1698
+ .asst-routes-modal .asst-routes-row-main { min-width: 0; }
1699
+ .asst-routes-modal .asst-routes-row-label { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 13px; font-weight: 400; line-height: 1.3; }
1700
+ .asst-routes-modal :is(.asst-routes-row-desc, .asst-routes-row-note, .asst-routes-row-warning, .asst-routes-row-context) { margin: 0; padding: 0; border: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--asst-text-3); font-size: 11.5px; line-height: 1.3; }
1701
+ .asst-routes-modal :is(.asst-routes-row-note, .asst-routes-row-warning) { color: var(--asst-warn); }
1702
+ .asst-routes-modal .asst-routes-row > :is(.asst-routes-row-note, .asst-routes-row-warning, .asst-routes-row-context) { grid-column: 1 / -1; white-space: normal; overflow: visible; overflow-wrap: anywhere; }
1703
+ .asst-routes-inactive { margin-inline-start: 6px; color: var(--asst-text-3); font-size: 11px; }
1704
+ .asst-routes-cell { display: flex; min-width: 0; }
1705
+ .asst-routes-cell-label { display: none; }
1706
+ .asst-routes-modal .asst-dd { width: 100%; max-width: none; height: var(--cap-h); min-height: var(--cap-h); padding: 0 6px; background: var(--asst-fill-2); box-shadow: inset 0 0 0 1px var(--asst-line); color: var(--asst-text); }
1707
+ .asst-routes-modal .asst-dd .asst-dd-label { flex: 1; max-width: none; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; line-height: 1; font-size: 12px; text-align: start; }
1708
+ .asst-routes-modal .asst-dd > .asst-icon { margin: 0; padding: 0; box-shadow: none; }
1709
+ .asst-routes-modal .asst-dd:disabled { opacity: 1; cursor: default; background: transparent; box-shadow: none; color: var(--asst-text-3); }
1710
+ .asst-routes-modal .asst-routes-source { display: inline-flex; flex-shrink: 0; min-width: 0; width: 12px; height: 12px; padding: 0; border-radius: 0; background: none; box-shadow: none; color: var(--asst-text-3); }
1711
+ .asst-routes-source svg { width: 12px; height: 12px; }
1712
+ .asst-routes-modal .asst-routes-source[data-edited="true"] { color: var(--asst-text); }
1713
+ .asst-routes-modal .asst-routes-source:is([data-source="disabled"], [data-source="incapable"]) { color: var(--asst-warn); text-decoration: none; }
1714
+ .asst-routes-modal .asst-routes-reset { width: var(--cap-h); min-width: var(--cap-h); padding: 0; color: var(--asst-text-3); }
1715
+ .asst-routes-draft { flex: 1; min-width: 0; color: var(--asst-text-3); font-size: 12px; }
1716
+ .asst-routes-draft[data-dirty="true"] { color: var(--asst-brand); }
1717
+ .asst-routes-scroll-cue { display: none; flex-shrink: 0; min-height: 44px; padding: 8px 14px; border: 0; border-top: 1px solid var(--asst-line); background: transparent; color: var(--asst-text-2); font: inherit; font-size: 12px; cursor: pointer; }
1718
+ .asst-routes-confirm { display: flex; flex-shrink: 0; align-items: center; flex-wrap: wrap; gap: 8px; padding: 10px 22px; border-top: 1px solid var(--asst-line); }
1719
+ .asst-routes-confirm-copy { display: flex; flex: 1 1 240px; flex-direction: column; gap: 4px; min-width: 0; font-size: 12px; }
1720
+ .asst-routes-confirm-copy span { color: var(--asst-text-2); }
1721
+ .asst-routes-confirm-action { box-shadow: inset 0 0 0 1px var(--asst-warn); }
1722
+ .asst-routes-modal .asst-routes-notice { box-shadow: none; padding: 0; color: var(--asst-text-2); }
1723
+ .asst-routes-catalog-note { padding: 8px; color: var(--asst-warn); font-size: 12px; line-height: 1.45; background: var(--asst-fill-1); border-radius: var(--asst-pill-r-xs); }
1724
+ .asst-routes-modal .asst-routes-error { flex-wrap: wrap; }
1725
+ .asst-routes-modal .asst-routes-error span { min-width: 0; overflow-wrap: anywhere; }
1726
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) { max-width: min(440px, calc(100vw - 16px)); font-family: inherit; font-size: 12px; }
1727
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item { height: var(--asst-pill-h-sm); min-height: var(--asst-pill-h-sm); padding: 0 8px; border-radius: var(--asst-pill-r-xs); }
1728
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-main { flex-direction: row; align-items: center; gap: 8px; }
1729
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-label { flex-shrink: 0; max-width: 60%; }
1730
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-desc { flex: 1; text-align: end; }
1731
+ /* Account identities and quota windows need a second line in their menu. */
1732
+ .asst-routes-overlay .asst-route-account-menu .asst-dd-item { height: auto; min-height: var(--asst-pill-h-sm); padding: 4px 8px; }
1733
+ .asst-routes-overlay .asst-route-account-menu .asst-dd-item-main { flex-direction: column; align-items: stretch; gap: 2px; }
1734
+ .asst-routes-overlay .asst-route-account-menu :is(.asst-dd-item-label, .asst-dd-item-desc) { white-space: normal; max-width: none; text-align: start; overflow-wrap: anywhere; }
1735
+ .asst-model-menu .asst-dd-item[data-kind="radio"]:not([data-id*=":"]) .asst-dd-item-desc { white-space: normal; overflow-wrap: anywhere; }
1736
+ .asst-routes-overlay .asst-route-model-menu .asst-dd-input { height: var(--asst-pill-h-sm); min-height: var(--asst-pill-h-sm); font-size: 12px; border-radius: var(--asst-pill-r-xs); }
1737
+ .asst-routes-overlay .asst-route-model-menu .asst-dd-input:focus-visible { outline: 2px solid var(--asst-focus); outline-offset: -2px; }
1738
+ .asst-route-menu-foot { flex-shrink: 0; display: flex; flex-wrap: wrap; align-items: center; border-top: 1px solid var(--asst-line-2); padding-top: 4px; }
1739
+ .asst-route-menu-foot .asst-dd-info { flex: 1 0 100%; font-size: 12px; color: var(--asst-text-2); }
1740
+ .asst-route-menu-foot .asst-dd-item { flex: 1; width: auto; }
1741
+ /* Large targets belong to a coarse pointer or the narrow stacked layout. */
1742
+ @media (pointer: coarse), (max-width: 600px) {
1743
+ .asst-routes-modal :is(.asst-btn, .asst-dd, .asst-iconbtn) { --cap-h: 44px; }
1744
+ .asst-routes-modal .asst-routes-mode { height: auto; min-height: 44px; }
1745
+ .asst-routes-modal .asst-routes-mode-label { white-space: normal; }
1746
+ .asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, .7fr) minmax(0, .9fr) minmax(0, 1.4fr) 44px; }
1747
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) :is(.asst-dd-item, .asst-dd-input) { min-height: 44px; height: auto; padding: 8px; }
1748
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-main { flex-direction: column; align-items: stretch; gap: 2px; }
1749
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) :is(.asst-dd-item-label, .asst-dd-item-desc) { white-space: normal; max-width: none; text-align: start; overflow-wrap: anywhere; }
1750
+ }
1751
+ @media (max-width: 600px) {
1752
+ .asst-routes-overlay { padding: 8px; }
1753
+ .asst-routes-overlay .asst-routes-modal { max-height: calc(100dvh - 16px); }
1754
+ .asst-routes-modal .asst-modal-body { padding: 14px; gap: 16px; }
1755
+ .asst-routes-modal .asst-modal-head { padding-inline: 14px 8px; }
1756
+ .asst-routes-modal .asst-routes-modes { grid-template-columns: 1fr; }
1757
+ .asst-routes-modal .asst-routes-columns { display: none; }
1758
+ .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 44px; gap: 8px; padding: 12px 0; }
1759
+ .asst-routes-modal .asst-routes-row-main, .asst-routes-modal .asst-routes-model-cell, .asst-routes-modal .asst-routes-account-cell { grid-column: 1 / -1; }
1760
+ .asst-routes-cell { flex-direction: column; gap: 4px; }
1761
+ .asst-routes-cell-label { display: block; color: var(--asst-text-3); font-size: 11.5px; }
1762
+ .asst-routes-reset-cell { align-self: end; }
1763
+ .asst-routes-reset-cell:empty { display: none; }
1764
+ .asst-routes-modal .asst-modal-foot { display: grid; grid-template-columns: 1fr auto auto; padding: 10px 14px; }
1765
+ .asst-routes-draft { grid-column: 1 / -1; grid-row: 1; }
1766
+ .asst-routes-reset-all { grid-column: 1; grid-row: 2; justify-self: start; }
1767
+ .asst-routes-cancel { grid-column: 2; grid-row: 2; }
1768
+ .asst-routes-save { grid-column: 3; grid-row: 2; }
1769
+ .asst-routes-scroll-cue { display: block; }
1770
+ .asst-routes-confirm { padding: 10px 14px; }
1771
+ }
1772
+
1549
1773
  /* ── Frame lightbox ─────────────────────────────────────────────── */
1550
1774
  .asst-lightbox { position: fixed; inset: 0; z-index: 100040; display: flex; align-items: center; justify-content: center; background: var(--asst-scrim-strong); }
1551
1775
  .asst-lightbox-figure { display: flex; flex-direction: column; align-items: center; gap: 10px; margin: 0; }
@@ -1599,7 +1823,8 @@ export function injectAssistantStyles() {
1599
1823
  /* ── Narrow / short containers (the tab or float window) ──────────
1600
1824
  Toolbar priority, highest first: route mode · Computer · project ·
1601
1825
  MCP. The composer row (attach · model · effort · permission mode ·
1602
- account) keeps model; account hides ≤720px. A capsule that runs out of
1826
+ test density · account) keeps model; account hides ≤720px, where test
1827
+ density is its gauge alone. A capsule that runs out of
1603
1828
  room drops its label half first and stays a glyph (its words remain in
1604
1829
  aria-label and the tooltip): route mode and Computer ≤960px, the mode
1605
1830
  ≤540px, every composer field but the model ≤440px. Model, history, new
@@ -1620,6 +1845,9 @@ export function injectAssistantStyles() {
1620
1845
  }
1621
1846
  @container asst (max-width: 720px) {
1622
1847
  :is(.asst-dd-mcp, .asst-dd-account, .asst-dd-project, .asst-bar-group[data-group="context"]) { display: none !important; }
1848
+ .asst-composer-brain .asst-dd.asst-dd-tests { display: grid; place-content: center; place-items: center; padding: 0; }
1849
+ .asst-composer-brain .asst-dd.asst-dd-tests > :is(.asst-dd-label, .asst-dd-caret) { display: none; }
1850
+ .asst-composer-brain .asst-dd.asst-dd-tests > .asst-icon:not([hidden]) { margin: 0; padding: 0 var(--cap-glyph-pad); box-shadow: none; }
1623
1851
  .asst-route-keys { display: none; }
1624
1852
  }
1625
1853
  @container asst (max-width: 540px) {
@@ -1639,6 +1867,9 @@ export function injectAssistantStyles() {
1639
1867
  :is(.asst-cost, .asst-bar-group[data-group="context"]) { display: none !important; }
1640
1868
  .asst-suggestions { flex-direction: column; align-items: stretch; width: 100%; }
1641
1869
  .asst-chip.asst-suggestion { justify-content: flex-start; max-width: none; }
1870
+ .asst-empty-project { width: 100%; }
1871
+ .asst-dd.asst-empty-control-btn { flex: 1 1 auto; justify-content: flex-start; max-width: none; }
1872
+ .asst-empty-control-btn .asst-dd-label { flex: 1 1 auto; max-width: none; text-align: start; }
1642
1873
  .asst-msg.msg-user .asst-msg-content { max-width: 92%; }
1643
1874
  .asst-dd-model .asst-dd-label { max-width: 9ch; }
1644
1875
  .asst-route-opt-sub { display: none; }
@@ -1649,12 +1880,13 @@ export function injectAssistantStyles() {
1649
1880
  .asst-bar-group[data-group="computer"] { display: none !important; }
1650
1881
  /* The model keeps a readable name (at least 5ch): the composer row wraps instead of truncating it. */
1651
1882
  .asst-dd-model .asst-dd-label { min-width: 5ch; max-width: 14ch; }
1652
- .asst-composer-actions { flex-wrap: wrap; row-gap: 6px; }
1653
- .asst-composer-brain { flex-wrap: wrap; row-gap: 6px; }
1883
+ /* A tighter gap keeps attach · model · effort · variant · mode · tests · send on one row at 320px. */
1884
+ .asst-composer-actions { flex-wrap: wrap; gap: 6px 4px; }
1885
+ .asst-composer-brain { flex-wrap: wrap; gap: 6px 4px; }
1654
1886
  }
1655
1887
  @container asst (max-height: 260px) {
1656
1888
  /* the toolbar stays: it carries the controls */
1657
- :is(.asst-empty-body, .asst-empty-mascot, .asst-suggestions) { display: none !important; }
1889
+ :is(.asst-empty-body, .asst-empty-mascot, .asst-empty-project, .asst-suggestions, .asst-empty-greet) { display: none !important; }
1658
1890
  .asst-empty { gap: 4px; padding: 4px; }
1659
1891
  .asst-empty-title { font-size: 14px; }
1660
1892
  .asst-bottom { margin-top: 6px; }
@@ -1671,6 +1903,8 @@ export function injectAssistantStyles() {
1671
1903
  @keyframes asst-spin { to { transform: rotate(360deg); } }
1672
1904
 
1673
1905
  body.ui-interacting :is(.asst-live-dot, .asst-spinner, .asst-computer-dot, .syna-cameo-eye) { animation-play-state: paused !important; }
1906
+ body.ui-interacting .asst-route-line[data-live] .asst-route-icon,
1907
+ body.ui-interacting .asst-route-line[data-live] .asst-route-sweep::after { animation-play-state: paused !important; }
1674
1908
 
1675
1909
  @media (prefers-reduced-motion: reduce) {
1676
1910
  .asst-transcript { scroll-behavior: auto; }
@@ -1680,6 +1914,8 @@ export function injectAssistantStyles() {
1680
1914
  .term-minimized-pill,
1681
1915
  .term-minimized-pill::before,
1682
1916
  .term-minimized-pill::after { animation: none !important; transition: none !important; }
1917
+ /* A frozen sweep would be a stray stub: the route row draws none (its run card and state words remain). */
1918
+ .asst-route-sweep { display: none; }
1683
1919
  }
1684
1920
  `;
1685
1921
  document.head.appendChild(style);
@@ -10,6 +10,38 @@
10
10
  const _putTimers = new Map(); // `${ns}/${id}` -> timeout id
11
11
  const _pendingPuts = new Map(); // `${ns}/${id}` -> { ns, id, value, opts }
12
12
  const DEFAULT_DEBOUNCE_MS = 500;
13
+ // One request per entry at a time, in the order they were asked for: a delete
14
+ // (or a newer write) that overtook a write still on its way would be undone by it.
15
+ const _inFlight = new Map(); // `${ns}/${id}` -> the request under way (a write or a delete); never rejects
16
+ const _waitingDeletes = new Map(); // `${ns}/${id}` -> sends the delete that waits behind that request
17
+ const _asked = new Map(); // `${ns}/${id}` -> counts the puts and deletes asked for
18
+
19
+ // Every put and delete of this page is numbered when it is asked for, and says which page it is of. Of two
20
+ // requests of one page for an entry the server keeps the one with the higher number, whichever arrives first
21
+ // (lib/ui-blob-store.js): what is sent while the page is being left (flushBlobs) cannot be put in an order here.
22
+ // A server from before this ignores the two fields; the order kept above is then all there is, as it was.
23
+ const _writer = (() => {
24
+ try { return Array.from(crypto.getRandomValues(new Uint8Array(12)), (byte) => byte.toString(16).padStart(2, '0')).join(''); }
25
+ catch { return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`; }
26
+ })();
27
+ let _revision = 0;
28
+
29
+ /** The address of one entry, for the request numbered `rev` (and what a write asks to be evicted). */
30
+ const _url = (ns, id, rev, opts = {}) => {
31
+ const params = new URLSearchParams();
32
+ if (opts.maxEntries) params.set('maxEntries', String(opts.maxEntries));
33
+ if (opts.maxChars) params.set('maxChars', String(opts.maxChars));
34
+ params.set('rev', String(rev));
35
+ params.set('writer', _writer);
36
+ return `/api/ui-blobs/${encodeURIComponent(ns)}/${encodeURIComponent(id)}?${params.toString()}`;
37
+ };
38
+ const _ask = (key) => { const n = (_asked.get(key) || 0) + 1; _asked.set(key, n); return n; };
39
+
40
+ function _track(key, request) {
41
+ const tracked = Promise.resolve(request).then(() => {}, () => {}).then(() => { if (_inFlight.get(key) === tracked) _inFlight.delete(key); });
42
+ _inFlight.set(key, tracked);
43
+ return tracked;
44
+ }
13
45
 
14
46
  export async function fetchBlobNamespace(ns) {
15
47
  try {
@@ -22,68 +54,125 @@ export async function fetchBlobNamespace(ns) {
22
54
  }
23
55
  }
24
56
 
57
+ // One entry, or null: there is none, or the server has no such namespace.
58
+ export async function fetchBlob(ns, id) {
59
+ try {
60
+ const res = await fetch(`/api/ui-blobs/${encodeURIComponent(ns)}/${encodeURIComponent(id)}`);
61
+ if (!res.ok) return null;
62
+ return await res.json();
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+
25
68
  async function _sendPut(key) {
26
69
  const pending = _pendingPuts.get(key);
27
70
  if (!pending) return;
28
- _pendingPuts.delete(key);
29
71
  _putTimers.delete(key);
30
- const { ns, id, value, opts } = pending;
31
- const params = new URLSearchParams();
32
- if (opts.maxEntries) params.set('maxEntries', String(opts.maxEntries));
33
- if (opts.maxChars) params.set('maxChars', String(opts.maxChars));
34
- const qs = params.toString();
35
- try {
36
- const res = await fetch(`/api/ui-blobs/${encodeURIComponent(ns)}/${encodeURIComponent(id)}${qs ? '?' + qs : ''}`, {
72
+ // Something of this entry is on its way: this write goes when that is settled (still pending until then).
73
+ const busy = _inFlight.get(key);
74
+ if (busy) { busy.then(() => _sendPut(key)); return; }
75
+ _pendingPuts.delete(key);
76
+ const { ns, id, value, opts, rev } = pending;
77
+ const asked = _asked.get(key);
78
+ const request = (async () => {
79
+ const res = await fetch(_url(ns, id, rev, opts), {
37
80
  method: 'PUT',
38
81
  headers: { 'Content-Type': 'application/json' },
39
82
  body: JSON.stringify(value),
40
83
  });
41
84
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
85
+ })();
86
+ _track(key, request);
87
+ try {
88
+ await request;
89
+ _answer(pending.waiters, true);
42
90
  } catch (err) {
43
- console.warn(`[blob-store] PUT ${ns}/${id} failed:`, err.message, '— will retry');
44
- // Re-queue unless a newer value superseded this one while in flight.
45
- if (!_pendingPuts.has(key)) {
91
+ // Re-queue only if nothing was asked for this entry meanwhile: a newer value superseded this one, or the
92
+ // entry was deleted (a retry would bring back what was removed).
93
+ const retry = _asked.get(key) === asked && !_pendingPuts.has(key);
94
+ console.warn(`[blob-store] PUT ${ns}/${id} failed:`, err.message, retry ? '— will retry' : '— superseded');
95
+ if (retry) {
46
96
  _pendingPuts.set(key, pending);
47
97
  _putTimers.set(key, setTimeout(() => _sendPut(key), 2000));
98
+ } else {
99
+ // Whoever waits for this write waits for the newer one that took its place; without one (the entry was
100
+ // deleted) it was given up.
101
+ const newer = _pendingPuts.get(key);
102
+ if (newer) newer.waiters.push(...pending.waiters.splice(0));
103
+ else _answer(pending.waiters, false);
48
104
  }
49
105
  }
50
106
  }
51
107
 
52
- // Debounced per-(ns,id), latest value wins.
108
+ // What the callers of putBlob that wait for it are told: `true`, the server has the value (or a newer one of the
109
+ // entry); `false`, the write was given up.
110
+ const _answer = (waiters, stored) => { for (const done of waiters.splice(0)) done(stored); };
111
+
112
+ // Debounced per-(ns,id), latest value wins. Answers a promise, for a caller that must not go on before the value
113
+ // is in the store: `true` once the server has stored it (or a newer value of the entry, when one was asked for
114
+ // meanwhile), `false` when the write was given up (the entry was deleted, or the page is going and the write was
115
+ // handed to a beacon, which says nothing back). It never rejects; a write that fails is tried again as before, and
116
+ // the promise waits with it.
53
117
  export function putBlob(ns, id, value, opts = {}) {
54
118
  const key = `${ns}/${id}`;
55
119
  const debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
56
- _pendingPuts.set(key, { ns, id, value, opts });
120
+ _ask(key);
121
+ const waiters = _pendingPuts.get(key)?.waiters || [];
122
+ const stored = new Promise((done) => { waiters.push(done); });
123
+ _pendingPuts.set(key, { ns, id, value, opts, rev: ++_revision, waiters });
57
124
  if (_putTimers.has(key)) clearTimeout(_putTimers.get(key));
58
125
  _putTimers.set(key, setTimeout(() => _sendPut(key), debounceMs));
126
+ return stored;
59
127
  }
60
128
 
129
+ // A delete is tried once: one that failed (the server could not make it, refused it, or was not reached) is not
130
+ // sent again. This page cannot know what another page has written to the entry since, and a delete that arrived
131
+ // late would remove that.
61
132
  export function deleteBlob(ns, id) {
62
133
  const key = `${ns}/${id}`;
63
134
  if (_putTimers.has(key)) { clearTimeout(_putTimers.get(key)); _putTimers.delete(key); }
135
+ const dropped = _pendingPuts.get(key);
64
136
  _pendingPuts.delete(key);
65
- fetch(`/api/ui-blobs/${encodeURIComponent(ns)}/${encodeURIComponent(id)}`, { method: 'DELETE' })
66
- .catch(() => {});
137
+ if (dropped) _answer(dropped.waiters, false);
138
+ _ask(key);
139
+ const rev = ++_revision;
140
+ const send = (init = {}) => fetch(_url(ns, id, rev), { method: 'DELETE', ...init }).catch(() => {});
141
+ const busy = _inFlight.get(key);
142
+ if (!busy) { _track(key, send()); return; }
143
+ // After the request of this entry that is on its way (sent once, also when the page goes first: flushBlobs).
144
+ // Its turn sends it only while it is still the one that waits: a later delete has taken its place, or the
145
+ // page that was about to be left has sent it already.
146
+ _waitingDeletes.set(key, send);
147
+ _track(key, busy.then(() => {
148
+ if (_waitingDeletes.get(key) !== send) return undefined;
149
+ _waitingDeletes.delete(key);
150
+ return send();
151
+ }));
67
152
  }
68
153
 
69
154
  // Flush all pending puts immediately. With useBeacon (pagehide path), bodies
70
155
  // under ~60KB go via sendBeacon (survives unload); larger ones use a best-
71
156
  // effort fetch — the last debounced PUT was at most a debounce window ago, so
72
- // little is lost if it doesn't complete.
157
+ // little is lost if it doesn't complete. What goes out here goes out at once,
158
+ // beside a request that may still be on its way: each carries its number, and
159
+ // the server keeps what was asked for last.
73
160
  export function flushBlobs({ useBeacon = false } = {}) {
161
+ // The page is going: a delete that waits behind a request cannot wait any longer.
162
+ if (useBeacon) {
163
+ for (const [key, send] of [..._waitingDeletes.entries()]) { _waitingDeletes.delete(key); send({ keepalive: true }); }
164
+ }
74
165
  for (const [key, pending] of [..._pendingPuts.entries()]) {
75
166
  if (_putTimers.has(key)) { clearTimeout(_putTimers.get(key)); _putTimers.delete(key); }
76
167
  if (useBeacon) {
77
- const { ns, id, value } = pending;
168
+ const { ns, id, value, rev } = pending;
78
169
  const body = JSON.stringify(value);
79
170
  _pendingPuts.delete(key);
171
+ _answer(pending.waiters, false); // a beacon says nothing back: nobody is told it arrived
80
172
  if (body.length < 60_000) {
81
- navigator.sendBeacon(
82
- `/api/ui-blobs/${encodeURIComponent(ns)}/${encodeURIComponent(id)}`,
83
- new Blob([body], { type: 'application/json' })
84
- );
173
+ navigator.sendBeacon(_url(ns, id, rev), new Blob([body], { type: 'application/json' }));
85
174
  } else {
86
- fetch(`/api/ui-blobs/${encodeURIComponent(ns)}/${encodeURIComponent(id)}`, {
175
+ fetch(_url(ns, id, rev), {
87
176
  method: 'PUT',
88
177
  headers: { 'Content-Type': 'application/json' },
89
178
  body,
@@ -20,6 +20,8 @@ const definitions = [
20
20
  ['hooks', 'Inspect configured hooks', 'hooks_list'],
21
21
  ['init', 'Create or update AGENTS.md guidance', null, true],
22
22
  ['logout', 'Sign out of Codex', 'account_logout'],
23
+ ['login', 'Sign in with ChatGPT'],
24
+ ['account', 'Choose an account or sign in'],
23
25
  ['mcp', 'Search MCP servers and tools', 'mcp_status'],
24
26
  ['mention', 'Add a structured file mention', null, true],
25
27
  ['model', 'Choose model'],
@@ -146,10 +148,11 @@ export function configChanges(definitions, inputs, original) {
146
148
  export const SESSION_CONFIG_FIELDS = Object.freeze({
147
149
  model: 'model', model_reasoning_effort: 'effort', model_reasoning_summary: 'summary',
148
150
  personality: 'personality', service_tier: 'serviceTier', approval_policy: 'approvalPolicy',
151
+ sandbox_mode: 'sandboxPolicy',
149
152
  });
150
153
 
151
154
  export function sessionSettings(config) {
152
- return Object.fromEntries(Object.entries(config).filter(([key]) => SESSION_CONFIG_FIELDS[key]).map(([key, value]) => [SESSION_CONFIG_FIELDS[key], value]));
155
+ return Object.fromEntries(Object.entries(config).filter(([key]) => SESSION_CONFIG_FIELDS[key]).map(([key, value]) => [key === 'sandbox_mode' ? 'sandboxMode' : SESSION_CONFIG_FIELDS[key], value]));
153
156
  }
154
157
 
155
158
  export function modelServiceTiers(model) {