synabun 2.0.0-beta → 2.0.5

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 (374) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/THIRD-PARTY-LICENSES.md +51 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +2 -2
  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 +20 -0
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/launcher.mjs +40 -0
  14. package/lib/claude-hooks.js +61 -8
  15. package/lib/client-config-repair.js +4 -3
  16. package/lib/data-home-migration.js +4 -0
  17. package/lib/desktop-pwa.js +206 -0
  18. package/lib/external-tools.js +221 -0
  19. package/lib/first-launch-protection.js +5 -0
  20. package/lib/packaged-runtime.js +110 -0
  21. package/lib/start-launcher.js +1396 -0
  22. package/mcp-server/package-lock.json +2 -2
  23. package/mcp-server/package.json +1 -3
  24. package/neural-interface/i18n/en.json +1540 -154
  25. package/neural-interface/i18n/pt-BR.json +1538 -153
  26. package/neural-interface/lib/assistant-api.js +62 -6
  27. package/neural-interface/lib/assistant-brains/claude.js +4 -0
  28. package/neural-interface/lib/assistant-catalog.js +179 -27
  29. package/neural-interface/lib/assistant-config.js +31 -2
  30. package/neural-interface/lib/assistant-dispatch.js +62 -13
  31. package/neural-interface/lib/assistant-envelope.js +9 -6
  32. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  33. package/neural-interface/lib/assistant-memory.js +24 -10
  34. package/neural-interface/lib/assistant-persona.js +6 -2
  35. package/neural-interface/lib/assistant-pricing.js +21 -6
  36. package/neural-interface/lib/assistant-runtime.js +181 -12
  37. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  38. package/neural-interface/lib/assistant-test-density.js +142 -0
  39. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  40. package/neural-interface/lib/assistant-user-context.js +908 -0
  41. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  42. package/neural-interface/lib/assistant-user-learn.js +404 -0
  43. package/neural-interface/lib/assistant-user-threads.js +226 -0
  44. package/neural-interface/lib/assistant-user-writer.js +181 -0
  45. package/neural-interface/lib/augmented-path.js +23 -3
  46. package/neural-interface/lib/browser-background-tab.js +134 -0
  47. package/neural-interface/lib/browser-language.js +83 -0
  48. package/neural-interface/lib/browser-screenshot.js +678 -61
  49. package/neural-interface/lib/claude-agent-bridge.js +87 -153
  50. package/neural-interface/lib/claude-executable.js +241 -0
  51. package/neural-interface/lib/claude-model-catalog.js +82 -93
  52. package/neural-interface/lib/claude-temporary.js +46 -7
  53. package/neural-interface/lib/cloudflared/install.js +533 -0
  54. package/neural-interface/lib/cloudflared/manager.js +545 -0
  55. package/neural-interface/lib/cloudflared/pins.js +115 -0
  56. package/neural-interface/lib/cloudflared/pins.json +63 -0
  57. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  58. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  59. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  60. package/neural-interface/lib/editor-completion.js +714 -0
  61. package/neural-interface/lib/editor-config.js +149 -0
  62. package/neural-interface/lib/editor-files-api.js +268 -0
  63. package/neural-interface/lib/editor-git-api.js +134 -0
  64. package/neural-interface/lib/file-explorer-api.js +1039 -0
  65. package/neural-interface/lib/file-explorer-system.js +355 -0
  66. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  67. package/neural-interface/lib/file-explorer-watch.js +239 -0
  68. package/neural-interface/lib/git-diff-api.js +103 -0
  69. package/neural-interface/lib/git-read.js +202 -0
  70. package/neural-interface/lib/greeting/api.js +89 -0
  71. package/neural-interface/lib/greeting/budgets.js +90 -0
  72. package/neural-interface/lib/greeting/engine.js +461 -0
  73. package/neural-interface/lib/greeting/facts.js +320 -0
  74. package/neural-interface/lib/greeting/sources.js +252 -0
  75. package/neural-interface/lib/greeting/writer.js +139 -0
  76. package/neural-interface/lib/http-guards.js +189 -13
  77. package/neural-interface/lib/machine-names.js +88 -0
  78. package/neural-interface/lib/memory-api.js +31 -7
  79. package/neural-interface/lib/native-binary-runtime.js +69 -337
  80. package/neural-interface/lib/native-loop-providers.js +40 -49
  81. package/neural-interface/lib/offline-page.js +54 -0
  82. package/neural-interface/lib/plugin-installer.js +4 -2
  83. package/neural-interface/lib/project-storage.js +5 -7
  84. package/neural-interface/lib/request-gate.js +589 -0
  85. package/neural-interface/lib/session-title-generator.js +5 -0
  86. package/neural-interface/lib/synabun-update-plan.js +19 -0
  87. package/neural-interface/lib/ui-blob-api.js +108 -0
  88. package/neural-interface/lib/ui-blob-store.js +68 -0
  89. package/neural-interface/package-lock.json +701 -253
  90. package/neural-interface/package.json +13 -2
  91. package/neural-interface/public/install-app.html +34 -0
  92. package/neural-interface/public/manifest.json +2 -0
  93. package/neural-interface/public/offline.html +402 -66
  94. package/neural-interface/public/onboarding.html +264 -147
  95. package/neural-interface/public/shared/agent-runtime-options.js +22 -6
  96. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  97. package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
  98. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  99. package/neural-interface/public/shared/assistant/asst-mascot.js +26 -64
  100. package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
  101. package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
  102. package/neural-interface/public/shared/assistant/asst-rack-styles.js +0 -3
  103. package/neural-interface/public/shared/assistant/asst-render.js +219 -67
  104. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  105. package/neural-interface/public/shared/assistant/asst-route.js +23 -329
  106. package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
  107. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  108. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  109. package/neural-interface/public/shared/assistant/asst-state.js +185 -1
  110. package/neural-interface/public/shared/assistant/asst-styles.js +162 -6
  111. package/neural-interface/public/shared/blob-store.js +111 -22
  112. package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
  113. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  114. package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
  115. package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
  116. package/neural-interface/public/shared/command-runner-model.js +30 -0
  117. package/neural-interface/public/shared/constants.js +6 -1
  118. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  119. package/neural-interface/public/shared/cp/cp-styles.js +15 -0
  120. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  121. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  122. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  123. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  124. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  125. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  126. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  127. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  128. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  129. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  130. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  131. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  132. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  133. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  134. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  135. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  136. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  137. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  138. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  139. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  140. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  141. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  142. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  143. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  144. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  145. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  146. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  147. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  148. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  149. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  150. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  151. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  152. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  153. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  154. package/neural-interface/public/shared/file-links.js +339 -0
  155. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  156. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  157. package/neural-interface/public/shared/html-shell.js +169 -170
  158. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  159. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  160. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
  161. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  162. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
  163. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
  164. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  165. package/neural-interface/public/shared/pwa-install-page.js +62 -0
  166. package/neural-interface/public/shared/pwa-install.css +12 -0
  167. package/neural-interface/public/shared/pwa-install.js +127 -0
  168. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  169. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  170. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  171. package/neural-interface/public/shared/settings/settings-ia.js +18 -4
  172. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  173. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  174. package/neural-interface/public/shared/start-bridge.js +802 -0
  175. package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
  176. package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
  177. package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
  178. package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
  179. package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
  180. package/neural-interface/public/shared/styles.css +2219 -1117
  181. package/neural-interface/public/shared/synabun-mascot.js +20 -56
  182. package/neural-interface/public/shared/term-ready.js +307 -0
  183. package/neural-interface/public/shared/tunnel-view.js +167 -0
  184. package/neural-interface/public/shared/ui-claude-panel.js +160 -19
  185. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  186. package/neural-interface/public/shared/ui-detail.js +15 -0
  187. package/neural-interface/public/shared/ui-explorer.js +22 -0
  188. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  189. package/neural-interface/public/shared/ui-file-links.js +103 -0
  190. package/neural-interface/public/shared/ui-help.js +26 -0
  191. package/neural-interface/public/shared/ui-invite.js +126 -116
  192. package/neural-interface/public/shared/ui-judgments.js +1 -1
  193. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  194. package/neural-interface/public/shared/ui-loading.js +367 -12
  195. package/neural-interface/public/shared/ui-menubar.js +21 -55
  196. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  197. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  198. package/neural-interface/public/shared/ui-navbar.js +45 -21
  199. package/neural-interface/public/shared/ui-settings.js +491 -547
  200. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  201. package/neural-interface/public/shared/ui-sync.js +16 -0
  202. package/neural-interface/public/shared/ui-terminal.js +153 -149
  203. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  204. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  205. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  206. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  207. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  208. package/neural-interface/public/shared/worker-end.js +251 -0
  209. package/neural-interface/public/sw.js +3 -1
  210. package/neural-interface/public/variant/2d/main.js +2 -1
  211. package/neural-interface/public/variant/3d/main.js +2 -1
  212. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  213. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  214. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  215. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  216. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  217. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  218. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  219. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  220. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  221. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  222. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  223. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  224. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  225. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  226. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  227. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  228. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  229. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  230. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  231. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  232. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  233. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  234. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  235. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  236. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  237. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  238. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  239. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  240. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  241. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  242. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  243. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  244. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  245. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  246. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  247. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  248. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  249. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  250. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  251. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  252. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  253. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  254. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  255. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  256. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  257. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  258. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  259. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  260. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  261. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  262. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  263. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  264. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  265. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  266. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  267. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  268. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  269. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  270. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  271. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  272. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  273. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  274. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  275. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  276. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  277. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  278. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  279. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  280. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  281. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  282. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  283. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  284. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  285. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  286. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  287. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  288. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  289. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  290. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  291. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  292. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  293. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  294. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  295. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  296. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  297. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  298. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  299. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  300. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  301. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  302. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  303. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  304. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  305. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  306. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  307. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  308. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  309. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  310. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  311. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  312. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  313. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  314. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  315. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  316. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  317. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  318. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  319. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  320. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  321. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  322. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  323. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  324. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  325. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  326. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  327. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  328. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  329. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  330. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  331. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  332. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  333. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  334. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  335. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  336. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  337. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  338. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  339. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  340. package/neural-interface/scripts/build-vendor.mjs +78 -2
  341. package/neural-interface/scripts/rebuild-pty.js +15 -12
  342. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  343. package/neural-interface/server.js +1092 -1212
  344. package/neural-interface/templates/rulesets/README.md +1 -1
  345. package/package.json +13 -7
  346. package/postinstall.js +11 -81
  347. package/setup.js +111 -6
  348. package/benchmarks/browser-live-results.json +0 -79
  349. package/benchmarks/browser-v2-results.json +0 -258
  350. package/docs/browser-v2.md +0 -109
  351. package/docs/hook-integrity.md +0 -89
  352. package/docs/judgments.md +0 -182
  353. package/docs/style-guide.md +0 -246
  354. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  355. package/neural-interface/public/category-logos/claude.svg +0 -1
  356. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  357. package/neural-interface/public/category-logos/openai.svg +0 -1
  358. package/neural-interface/public/category-logos/synabun.png +0 -0
  359. package/neural-interface/public/claude.svg +0 -1
  360. package/neural-interface/public/gallery.svg +0 -6
  361. package/neural-interface/public/leonardoai.svg +0 -1
  362. package/neural-interface/public/mcp.svg +0 -4
  363. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  364. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  365. package/neural-interface/public/sessionicon.svg +0 -8
  366. package/neural-interface/public/toolbar-album.svg +0 -8
  367. package/neural-interface/public/toolbar-question.svg +0 -6
  368. package/neural-interface/public/toolbar-sessions.svg +0 -6
  369. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  370. package/scripts/audit-claude-hook-usage.mjs +0 -12
  371. package/scripts/audit-memory-duplicates.mjs +0 -44
  372. package/scripts/measure-claude-hook-overhead.mjs +0 -77
  373. package/scripts/recover-user-config.mjs +0 -254
  374. package/skills/synabun/synabunicon.png +0 -0
@@ -29,11 +29,13 @@ import { emit, on, state as appState } from '../state.js';
29
29
  import { storage } from '../storage.js';
30
30
  import { KEYS } from '../constants.js';
31
31
  import { t } from '../i18n.js';
32
+ import { forgetGreeting, mountGreeting } from '../greeting/greeting-card.js';
32
33
  import { notify, NOTIF_TYPE } from '../ui-notifications.js';
33
34
  import { getProviderMeta } from '../provider-icons.js';
34
35
  import { isGuest, hasPermission } from '../ui-sync.js';
35
36
  import { getWhiteboardElementById } from '../ui-whiteboard.js';
36
37
  import { focusNativeLoopRun } from '../ui-native-loop-router.js';
38
+ import { wireFileLinks } from '../ui-file-links.js';
37
39
  import { injectAssistantStyles } from './asst-styles.js';
38
40
  import { createAssistantSocket } from './asst-ws.js';
39
41
  import { createRenderer, ICON_ASSISTANT, mdInto } from './asst-render.js';
@@ -42,7 +44,7 @@ import { lastInputAt, onKey, onPointer } from '../synabun-ticker.js';
42
44
  import { buildControlResponse, closedControlState, normalizeControlRequest, renderControlCard } from './asst-control.js';
43
45
  import { buildPromptText, createComposer } from './asst-composer.js';
44
46
  import { buildDispatchSpec, parseSlashCommand, slashHelpMarkdown } from './asst-slash.js';
45
- import { createBrainPicker } from './asst-brain-picker.js';
47
+ import { cachedProjects, createBrainPicker, testDensityLabel } from './asst-brain-picker.js';
46
48
  import { createRovingToolbar } from './asst-roving.js';
47
49
  import { openAccountsManager } from './asst-accounts.js';
48
50
  import { createAgentsDock } from './asst-agents-dock.js';
@@ -52,9 +54,9 @@ import { createRouteModeControl, openModelsManager, openRoutesEditor, refreshMod
52
54
  import { budgetChipText, budgetTone, openBudgetEditor } from './asst-budget.js';
53
55
  import { computerRemoteView, createComputerPanel, createComputerToggle, normalizeComputerRemote, openFrameLightbox, closeFrameLightbox, requestDesktopStop } from './asst-computer.js';
54
56
  import {
55
- applyLimit, applyLimitPacket, applyUsagePacket, brainLabel, createTabState, DEFAULT_ROUTE_MODE, deserializeTabState, fmtCost, fmtRunLine, limitExpiresIn, limitNoticeText, modelShortName,
56
- brainModePatch, normalizeBrain, normalizeComputerMode, permissionModeLabel, normalizeRouteMode, providerShortLabel, readStoredBrain, runFromPayload,
57
- serializeTabState, tabStorageKey,
57
+ applyLimit, applyLimitPacket, applyUsagePacket, brainLabel, createRenameWriter, createTabState, DEFAULT_ROUTE_MODE, deserializeTabState, fmtCost, fmtRunLine, limitExpiresIn, limitNoticeText, modelShortName,
58
+ brainModePatch, normalizeBrain, normalizeComputerMode, permissionModeLabel, normalizeRouteMode, normalizeTestDensity, projectLabel, providerShortLabel, readStoredBrain, runFromPayload,
59
+ serializeTabState, tabStorageKey, userSessionTitle,
58
60
  } from './asst-state.js';
59
61
  import {
60
62
  closeAssistantSession, dispatchAssistantRun, escalateAssistantRun, focusAssistantRun, getAssistantRun,
@@ -93,10 +95,6 @@ function num(value) {
93
95
  return value != null && value !== '' && Number.isFinite(n) ? n : null;
94
96
  }
95
97
 
96
- function basename(path) {
97
- return String(path || '').split(/[\\/]/).filter(Boolean).pop() || String(path || '');
98
- }
99
-
100
98
  function relativeTime(value) {
101
99
  const ms = new Date(value || 0).getTime();
102
100
  if (!ms) return '';
@@ -109,6 +107,10 @@ function relativeTime(value) {
109
107
  return `${Math.floor(hrs / 24)}d`;
110
108
  }
111
109
 
110
+ // Renames on their way to a session record. One writer for the page, not one per tab: a write
111
+ // outlives the tab that asked for it, and every Sessions menu waits for all of them.
112
+ const renames = createRenameWriter({ patch: patchAssistantSession });
113
+
112
114
  function clipCopy(text) {
113
115
  if (navigator.clipboard?.writeText) navigator.clipboard.writeText(text).catch(() => {});
114
116
  }
@@ -116,17 +118,21 @@ function clipCopy(text) {
116
118
  /**
117
119
  * mountAssistant(viewport, { sessionId, brain, host, session })
118
120
  * host: { id: 'terminal'|'sidepanel', notifySource: 'cli'|'panel',
119
- * setLabel(text), setStatus('idle'|'working'|'action'|'done'), setBrain(brain),
121
+ * setLabel(text), setName?(text, renamed) (a rename that was not saved: the tab's name and its
122
+ * renamed flag go back, saved label included), setStatus('idle'|'working'|'action'|'done'), setBrain(brain),
120
123
  * isVisible() → bool, toolbarSlot?: Element (where the toolbar goes; default the root),
121
124
  * takeHeroOrigin?() → DOMRect | null (a character the host showed while starting: the empty state's flies in from it) }
122
125
  * The hooks may fire during mount, before this returns.
123
- * The tab label is always "SynaBun"; a user rename arrives via controller.setTitle.
126
+ * The tab label is always "SynaBun"; a user rename arrives via controller.setTitle
127
+ * and is written to the session record (saveRename), where every session list reads it.
124
128
  */
125
129
  export function mountAssistant(viewport, { sessionId, brain, host = {}, session = null } = {}) {
126
130
  injectAssistantStyles();
127
131
  const saved = deserializeTabState(storage.getItem(tabStorageKey(sessionId)));
132
+ let greetingId = null; // the greeting this conversation opened with, until its first prompt carries it
133
+ const defaultTitle = tt('assistant.defaultTitle', 'SynaBun');
128
134
  const st = createTabState(sessionId, brain || session?.brain || saved?.brain || readStoredBrain(storage), {
129
- title: tt('assistant.defaultTitle', 'SynaBun'),
135
+ title: defaultTitle,
130
136
  // The agents tray starts collapsed; it only expands on request.
131
137
  dockOpen: saved ? saved.dockOpen : storage.getItem(KEYS.ASSISTANT_DOCK_OPEN) === '1',
132
138
  dockShowAll: saved ? saved.dockShowAll : storage.getItem(KEYS.ASSISTANT_DOCK_SHOW_ALL) === '1',
@@ -135,6 +141,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
135
141
  providerSessionId: session?.providerSessionId || null,
136
142
  routeMode: saved?.routeMode ?? null,
137
143
  computerUse: saved?.computerUse ?? null,
144
+ testDensity: saved?.testDensity ?? null,
138
145
  usageView: saved?.usageView || null,
139
146
  });
140
147
  Object.assign(st, {
@@ -144,7 +151,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
144
151
  engine: '',
145
152
  sdkVersion: '',
146
153
  routing: null, // server view { mode, effectiveMode, defaultMode, askBelow }
147
- features: { routing: null, computer: null }, // null = unknown yet
154
+ features: { routing: null, computer: null, testDensity: null, rename: null }, // null = unknown yet
155
+ testDensityEffective: null, // server view: the level in effect
148
156
  brainCapabilities: null,
149
157
  computerEffective: st.computerUse === true,
150
158
  desktopSupported: null,
@@ -152,8 +160,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
152
160
  desktopActive: false,
153
161
  desktopProbe: false,
154
162
  routingProbe: false,
155
- sessionTitle: String(session?.title || ''), // the server's name (first prompt): history + notifications, never the tab
163
+ sessionTitle: String(session?.title || ''), // the server's name (the user's rename, else the first prompt): history + notifications
156
164
  });
165
+ let recordName = userSessionTitle(session); // the name the session record holds as the user's ('' = never renamed)
157
166
  const controls = new Map(); // requestId → { card, origin, kind }
158
167
  // Cards answered here (requestId → card): one the server closed before the answer
159
168
  // arrived (control_cancelled after it) says so instead of keeping "Allowed".
@@ -253,6 +262,45 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
253
262
  try { host.setLabel?.(st.title); } catch { /* ignore */ }
254
263
  }
255
264
 
265
+ /** What the session record says about its name (a session view: the rename's answer, a broadcast, an attach). */
266
+ function applyRecordName(meta) {
267
+ if (!meta || typeof meta !== 'object' || !meta.title || (meta.id && meta.id !== sessionId)) return;
268
+ st.sessionTitle = String(meta.title);
269
+ recordName = userSessionTitle(meta);
270
+ }
271
+
272
+ /**
273
+ * A rename names the session, not this tab: it goes to the session record, which the Sessions
274
+ * menu and every other list read, through the page's one writer (`renames`). A host also calls
275
+ * setTitle when it mounts a renamed tab (`restored`): the name the record already holds is not
276
+ * written again, one it lacks (a rename made before the record kept names) is, and it never
277
+ * replaces a name on its way. "SynaBun" is no name: it is written only to take a rename back.
278
+ * `from` is the name the tab had before: a write that fails puts it back (renameFailed).
279
+ */
280
+ function saveRename(from, restored = false) {
281
+ if (destroyed || (restored && renames.pending(sessionId))) return;
282
+ renames.save(sessionId, st.title, {
283
+ from, recordName, recordTitle: st.sessionTitle, keepsRenames: st.features.rename === true, defaultTitle,
284
+ onSaved: applyRecordName,
285
+ onFailed: renameFailed,
286
+ });
287
+ }
288
+
289
+ /**
290
+ * A rename that did not reach the record: the tab goes back to the name the record answers to,
291
+ * in the host and in the label it saved, and the transcript says so. A name with nothing to go
292
+ * back to (a restored one, or the name the tab already shows) stays: the next mount sends it again.
293
+ */
294
+ function renameFailed({ name, back }) {
295
+ const previous = back || defaultTitle;
296
+ if (destroyed || previous === st.title) return;
297
+ st.userRenamed = previous !== defaultTitle;
298
+ st.title = previous;
299
+ try { host.setName?.(previous, st.userRenamed); } catch { /* ignore */ }
300
+ persist();
301
+ renderer.appendStatus(tt('assistant.status.renameFailed', 'Could not save the name “{name}”. The session is still called “{previous}”.', { name, previous }), 'warn');
302
+ }
303
+
256
304
  /** Notifications say which conversation: the rename, else the session's first-prompt title. */
257
305
  function notifyLabel() {
258
306
  return st.userRenamed ? st.title : (st.sessionTitle || st.title);
@@ -378,6 +426,20 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
378
426
  return st.features.routing === true;
379
427
  }
380
428
 
429
+ // ── File links ──
430
+ // A link to a file the code editor can open opens it there (shared with the other panels): one listener
431
+ // for the transcript, whatever drew the message. A relative path is a path in the brain's project.
432
+ wireFileLinks(transcriptEl, {
433
+ cwd: () => st.brain?.project || '',
434
+ textLinks: 'a.file-link',
435
+ // A path the editor does not open keeps what a click on it did: it is copied.
436
+ copyPath: (link) => {
437
+ clipCopy((link.textContent || '').replace(/:\d+$/, ''));
438
+ link.style.opacity = '0.5';
439
+ setTimeout(() => { link.style.opacity = ''; }, 300);
440
+ },
441
+ });
442
+
381
443
  // ── Renderer ──
382
444
  const renderer = createRenderer(transcriptEl, {
383
445
  t,
@@ -389,10 +451,18 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
389
451
  onReadRun: (runId) => readRun(runId),
390
452
  onEscalateRun: (runId, target) => escalateRun(runId, target),
391
453
  onMemoryClick: (id) => { if (id) emit('memory:open', { id }); },
454
+ // The greeting this conversation opens with (lib/greeting/ decides whether there is one): its id
455
+ // goes back with the first prompt, so the brain is told once what was said.
456
+ greeting: (slot) => (guestBlocked() ? null : mountGreeting(slot, {
457
+ surface: 'assistant', session: sessionId, cwd: st.brain?.project || null, t,
458
+ onOpenMemory: (id) => { if (id) emit('memory:open', { id }); },
459
+ }).then((greeting) => { greetingId = greeting?.id || null; return greeting; })),
392
460
  brainLabel: () => brainModelLabel(),
393
461
  onChangeRoutes: () => openRoutes(),
394
462
  onRouteEvent: (ev) => handleRouteCardEvent(ev),
395
463
  emptySuggestions: () => emptySuggestions(),
464
+ // "Choose a project" in the empty state is the toolbar's Working project field (asst-brain-picker.js).
465
+ mountEmptyProject: (slot) => picker.mountEmptyProject(slot),
396
466
  onSuggestion: (template) => composer.fillTemplate(template),
397
467
  heroOrigin: () => { try { return host.takeHeroOrigin?.() || null; } catch { return null; } },
398
468
  onMascotChange: () => director?.retarget(),
@@ -424,7 +494,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
424
494
  });
425
495
 
426
496
  function emptySuggestions() {
427
- const project = st.brain.project ? basename(st.brain.project) : tt('assistant.empty.thisProject', 'this project');
497
+ const project = projectLabel(st.brain.project, cachedProjects()) || tt('assistant.empty.thisProject', 'this project');
428
498
  const list = [];
429
499
  if (computerAvailable()) list.push({ id: 'computer', label: tt('assistant.empty.suggestComputer', 'Use my Mac to…'), template: tt('assistant.empty.templateComputer', 'Use my Mac to {caret}') });
430
500
  list.push({ id: 'web', label: tt('assistant.empty.suggestWeb', 'Look up … on the web'), template: tt('assistant.empty.templateWeb', 'Look up {caret} on the web') });
@@ -455,8 +525,6 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
455
525
  director = createMascotDirector({
456
526
  getTarget: () => renderer.mascot(),
457
527
  caret: () => composer.caretPoint(),
458
- setEnabled: (on) => renderer.setRigsEnabled(on),
459
- isVisible: () => visible(),
460
528
  env: {
461
529
  input: (fn) => { const offPointer = onPointer(fn); const offKey = onKey(fn); return () => { offPointer(); offKey(); }; },
462
530
  lastInputAt,
@@ -490,6 +558,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
490
558
  getMenuAnchor: () => moreBtn,
491
559
  // Composer fields open upward, toolbar fields downward.
492
560
  menuPlacement: 'auto',
561
+ // Test density sits right after the permission mode; hidden on a server that does not know it.
562
+ getTestDensity: () => testDensityView(),
563
+ onTestDensity: (level) => setTestDensity(level),
564
+ // The registered projects arrived: their labels name the project in the fields and the "Fix … in" suggestion.
565
+ onProjects: () => { picker.render(); renderer.refreshEmpty(); },
493
566
  });
494
567
 
495
568
  const computerToggle = createComputerToggle(barGroups.computer, {
@@ -823,8 +896,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
823
896
  function applySessionMeta(meta) {
824
897
  if (!meta || typeof meta !== 'object') return;
825
898
  if (meta.id && meta.id !== sessionId) return;
826
- // The server's title names the conversation (history, notifications), never the tab.
827
- if (meta.title) st.sessionTitle = String(meta.title);
899
+ // The server's title names the conversation (history, notifications); the tab's name is the host's.
900
+ applyRecordName(meta);
828
901
  if (meta.brain && typeof meta.brain === 'object') {
829
902
  const next = normalizeBrain({ ...st.brain, ...meta.brain, provider: meta.brain.provider || meta.brain.brain || st.brain.provider });
830
903
  if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
@@ -847,7 +920,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
847
920
  if (meta.features && typeof meta.features === 'object') {
848
921
  if (typeof meta.features.routing === 'boolean') st.features.routing = meta.features.routing;
849
922
  if (typeof meta.features.computer === 'boolean') st.features.computer = meta.features.computer;
923
+ if (typeof meta.features.testDensity === 'boolean') st.features.testDensity = meta.features.testDensity;
924
+ if (typeof meta.features.rename === 'boolean') st.features.rename = meta.features.rename;
850
925
  }
926
+ if ('testDensityExplicit' in meta) st.testDensity = normalizeTestDensity(meta.testDensityExplicit, null);
927
+ if (meta.testDensity) st.testDensityEffective = normalizeTestDensity(meta.testDensity);
851
928
  if (typeof meta.computerUse === 'boolean') st.computerEffective = meta.computerUse;
852
929
  if ('computerUseExplicit' in meta) st.computerUse = normalizeComputerMode(meta.computerUseExplicit, null);
853
930
  // A WhatsApp conversation: what is in effect and why (not a per-conversation switch). null on the desktop.
@@ -872,6 +949,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
872
949
  if (destroyed) return;
873
950
  routeChip.setVisible(routingAvailable());
874
951
  routeChip.render();
952
+ picker.render();
875
953
  const showComputer = st.features.computer === true || st.desktopSupported === true;
876
954
  computerToggle.set({
877
955
  visible: showComputer,
@@ -920,7 +998,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
920
998
 
921
999
  function routeModeView() {
922
1000
  const r = st.routing || {};
923
- return { mode: normalizeRouteMode(st.routeMode, null) || r.mode || null, effectiveMode: r.effectiveMode, defaultMode: r.defaultMode };
1001
+ return { mode: normalizeRouteMode(st.routeMode, null) || r.mode || null, effectiveMode: r.effectiveMode, defaultMode: r.defaultMode, askBelow: r.askBelow };
924
1002
  }
925
1003
 
926
1004
  function setRouteMode(mode, { quiet = false } = {}) {
@@ -943,6 +1021,31 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
943
1021
  if (last && last !== st.routing.effectiveMode) setRouteMode(last, { quiet: true });
944
1022
  }
945
1023
 
1024
+ /** The level the composer shows: this session's, else the server's; null while the server does not know the setting. */
1025
+ function testDensityView() {
1026
+ if (st.features.testDensity !== true) return null;
1027
+ return normalizeTestDensity(st.testDensity || st.testDensityEffective);
1028
+ }
1029
+
1030
+ function setTestDensity(level, { quiet = false } = {}) {
1031
+ const next = normalizeTestDensity(level);
1032
+ st.testDensity = next;
1033
+ st.testDensityEffective = next;
1034
+ picker.render();
1035
+ persist();
1036
+ sock.send({ type: 'set_test_density', level: next });
1037
+ try { storage.setItem(KEYS.ASSISTANT_TEST_DENSITY, next); } catch { /* storage unavailable */ }
1038
+ if (!quiet) renderer.appendStatus(tt('assistant.status.testDensity', 'Test density: {level}', { level: testDensityLabel(next, t) }));
1039
+ }
1040
+
1041
+ /** Fresh tabs inherit the last-used test density (like the route mode). */
1042
+ function applyLastUsedTestDensity() {
1043
+ if (saved || st.features.testDensity !== true || st.testDensity != null) return;
1044
+ let last = null;
1045
+ try { last = normalizeTestDensity(storage.getItem(KEYS.ASSISTANT_TEST_DENSITY), null); } catch { last = null; }
1046
+ if (last && last !== testDensityView()) setTestDensity(last, { quiet: true });
1047
+ }
1048
+
946
1049
  /** A WhatsApp conversation's Computer switch says why it is what it is (the status line of the transcript). */
947
1050
  function explainRemoteComputer() {
948
1051
  const view = computerRemoteView(st.computerRemote, t);
@@ -1170,6 +1273,10 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1170
1273
  request_id: `q-${Date.now().toString(36)}-${(querySeq += 1)}`,
1171
1274
  };
1172
1275
  if (payload.images?.length) msg.images = payload.images.map(i => ({ base64: i.base64, mediaType: i.mediaType }));
1276
+ // The typed words apart from long pastes (asst-composer.js): what the server may learn about the user from.
1277
+ if (typeof payload.typed === 'string') msg.typed = payload.typed;
1278
+ // The greeting shown when this conversation opened: echoed once, with the first prompt.
1279
+ if (greetingId) { msg.greetingId = greetingId; greetingId = null; forgetGreeting('assistant', sessionId); }
1173
1280
  const row = renderer.appendUser(payload.display ?? payload.text, { images: payload.images, files: [...(payload.files || []), ...(payload.uploads || [])], memories: payload.memories });
1174
1281
  director.sent();
1175
1282
  sentQueries.set(msg.request_id, { payload, row });
@@ -1178,7 +1285,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1178
1285
  setRunning(true);
1179
1286
  sock.send(msg);
1180
1287
  // Name the conversation like the server will (its first prompt) — not the tab.
1181
- if (!st.sessionTitle || /^(?:SynaBun|Assistant(?: \d+)?)$/.test(st.sessionTitle)) {
1288
+ if (!st.userRenamed && (!st.sessionTitle || /^(?:SynaBun|Assistant(?: \d+)?)$/.test(st.sessionTitle))) {
1182
1289
  const guess = String(payload.text || '').split('\n')[0].trim().slice(0, 60);
1183
1290
  if (guess) st.sessionTitle = guess;
1184
1291
  }
@@ -1222,7 +1329,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1222
1329
  return true;
1223
1330
  case 'resume':
1224
1331
  if (parsed.sessionId) resume(parsed.sessionId);
1225
- else sock.send({ type: 'list_sessions' });
1332
+ else renames.settled().then(() => { if (!destroyed) sock.send({ type: 'list_sessions' }); }); // a rename on its way is in the list
1226
1333
  return true;
1227
1334
  case 'stop':
1228
1335
  if (parsed.all) await stopAll();
@@ -1357,6 +1464,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1357
1464
  picker.render();
1358
1465
  host.setBrain?.(st.brain);
1359
1466
  persist();
1467
+ if (before.project !== st.brain.project) renderer.refreshEmpty();
1360
1468
  if (meta.silent) return;
1361
1469
  if (meta.requiresSwitch) {
1362
1470
  const b = st.brain;
@@ -1366,7 +1474,6 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1366
1474
  } else if (before.permissionMode !== st.brain.permissionMode || !!before.planMode !== !!st.brain.planMode) {
1367
1475
  sock.send({ type: 'set_permission_mode', mode: st.brain.permissionMode, planMode: st.brain.planMode === true });
1368
1476
  }
1369
- if (before.project !== st.brain.project) renderer.refreshEmpty();
1370
1477
  patchAssistantSession(sessionId, { brain: st.brain }).catch((err) => {
1371
1478
  // Refused (a model disabled in Assistant → Models): undo, so the refused
1372
1479
  // brain is neither shown nor saved as the last used one — the next start
@@ -1374,6 +1481,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1374
1481
  if (err?.code !== 'MODEL_DISABLED' || destroyed || st.brain !== applied) return;
1375
1482
  st.brain = before;
1376
1483
  picker.render();
1484
+ if (applied.project !== before.project) renderer.refreshEmpty();
1377
1485
  host.setBrain?.(before);
1378
1486
  persist();
1379
1487
  if (!meta.requiresSwitch) renderer.appendError(err.message || String(err));
@@ -1384,7 +1492,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1384
1492
  async function focusRun(runId) {
1385
1493
  if (!runId) return;
1386
1494
  focusAssistantRun(runId).catch(() => {});
1387
- try { await focusNativeLoopRun(runId); } catch { /* router not ready */ }
1495
+ // The user's own Focus: a run whose tab was closed opens again.
1496
+ try { await focusNativeLoopRun(runId, { reopen: true }); } catch { /* router not ready */ }
1388
1497
  }
1389
1498
 
1390
1499
  async function stopRun(runId) {
@@ -1615,6 +1724,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1615
1724
  providerIcon,
1616
1725
  getBrain: () => st.brain,
1617
1726
  brainName: () => brainModelLabel(),
1727
+ getRouting: () => st.routing,
1618
1728
  onToast: toast,
1619
1729
  onSaved: (routing) => {
1620
1730
  if (routing?.defaultMode) {
@@ -1667,7 +1777,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1667
1777
  e.stopPropagation();
1668
1778
  if (isMenuOpen(sessionsBtn)) { closeMenu(); return; }
1669
1779
  let list = [];
1670
- try { list = await listAssistantSessions(); } catch { /* server not ready */ }
1780
+ // A rename still on its way to a record is in the list this opens: this tab's, another tab's, a closed one's.
1781
+ try { await renames.settled(); list = await listAssistantSessions(); } catch { /* server not ready */ }
1671
1782
  if (!destroyed) showSessionsMenu(list);
1672
1783
  });
1673
1784
  function toggleDock() {
@@ -1755,6 +1866,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1755
1866
  if (force) renderer.clear();
1756
1867
  if (Array.isArray(transcript) && transcript.length) renderer.renderTranscript(transcript);
1757
1868
  renderer.showEmpty(); // no-op once the transcript has rows
1869
+ // A conversation with nothing in it yet may be greeted; a restored one never is.
1870
+ if (Array.isArray(transcript) && !transcript.length && !(Number(meta?.turns) > 0)) renderer.offerGreeting();
1758
1871
  for (const run of (Array.isArray(data?.runs) ? data.runs : [])) dock.upsertRun(run);
1759
1872
  } catch (err) {
1760
1873
  console.warn('[assistant] could not restore the conversation', err);
@@ -1774,6 +1887,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1774
1887
  if (destroyed) return;
1775
1888
  probeFeatures();
1776
1889
  applyLastUsedRouteMode();
1890
+ applyLastUsedTestDensity();
1777
1891
  sock.connect();
1778
1892
  });
1779
1893
  fetchUsage();
@@ -1784,7 +1898,15 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1784
1898
  getBrain: () => st.brain,
1785
1899
  setBrain: (b, meta) => applyBrain(b, meta || { requiresSwitch: false, field: 'external' }),
1786
1900
  getTitle: () => st.title,
1787
- setTitle: (text) => { st.userRenamed = true; setTitle(text); },
1901
+ /** A rename (the host's tab, float header or ✎), or with `restored` the name a host puts back when it mounts a renamed tab. */
1902
+ setTitle: (text, { restored = false } = {}) => {
1903
+ const next = String(text || '').trim();
1904
+ if (!next) return;
1905
+ const from = restored ? next : st.title; // a restored name has none to go back to
1906
+ st.userRenamed = next !== defaultTitle; // the default name is no rename
1907
+ setTitle(next);
1908
+ saveRename(from, restored);
1909
+ },
1788
1910
  isRunning: () => st.running,
1789
1911
  isConnected: () => st.connected,
1790
1912
  send: (text, extra = {}) => submit({ text: String(text ?? ''), images: extra.images || [], files: extra.files || [], uploads: extra.uploads || [], memories: extra.memories || [] }),
@@ -1799,12 +1921,12 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1799
1921
  insertText: (text) => composer.insertText(text),
1800
1922
  /** A file path from the explorer ("send to panel") → the composer, as inline code. */
1801
1923
  attachPath: (path) => { if (path) { composer.insertText(`\`${path}\` `); composer.focus(); } },
1802
- /** The server's name for the conversation (first prompt) — tooltips, never the tab label. */
1924
+ /** The server's name for the conversation (the user's rename, else the first prompt): tooltips. */
1803
1925
  getConversationTitle: () => st.sessionTitle || '',
1804
1926
  getStatus: () => st.status || 'idle',
1805
1927
  focus: () => composer.focus(),
1806
- onShown: () => { syncTabLabel(); renderer.scrollEnd(true); if (visible()) composer.focus(); director.touch(); director.visibilityChanged(); },
1807
- onHidden: () => { closeMenu(); picker.close(); director.stopTyping(); director.visibilityChanged(); },
1928
+ onShown: () => { syncTabLabel(); renderer.scrollEnd(true); if (visible()) composer.focus(); director.touch(); },
1929
+ onHidden: () => { closeMenu(); picker.close(); director.stopTyping(); },
1808
1930
  counts: () => dock.counts(),
1809
1931
  destroy: ({ closeServer = false } = {}) => {
1810
1932
  if (destroyed) return;
@@ -98,9 +98,6 @@ export const RACK_CSS = `
98
98
  }
99
99
  .asst-rack-face { grid-area: face; align-self: center; color: var(--rk-text); line-height: 0; }
100
100
  .asst-rack-face svg { display: block; width: 72px; height: 36px; }
101
- /* The hero's hand-off (asst-render.js flipIntoStage): the face flies in from outside the stage. */
102
- .asst-rack.is-flip > .asst-rack-stage { overflow: visible; }
103
- .asst-rack.is-flip .asst-rack-face { position: relative; z-index: 3; }
104
101
  .asst-rack-caption {
105
102
  grid-area: caption;
106
103
  align-self: end;