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
@@ -11,15 +11,16 @@
11
11
  // Replay runs the same handleEvent path with { silent: true }: live minus
12
12
  // motion, durations from the journal's `at`. Every row is inserted before the
13
13
  // "Working…" indicator so it always stays last.
14
- // The empty state's character (the hero) outlives a send: parked where it
15
- // stood, thinking, until the first stage of the turn takes it over (the
16
- // stage's face flies in from the hero's box) or other content arrives (it
17
- // fades out). mascot() names the character on screen for the panel's
18
- // director (asst-mascot.js), which may hold its pose; stills are painted SVGs,
19
- // never rigs.
14
+ // The empty state's character (the hero) outlives a send only for its
15
+ // hand-off: the "Thinking" row's character flies in from the hero's box at
16
+ // once, so nothing sits on the prompt, and with no such row the hero fades
17
+ // out. mascot() names the character on screen for the panel's director
18
+ // (asst-mascot.js), which may hold its pose; stills are painted SVGs, never
19
+ // rigs.
20
20
 
21
21
  import { assistantEventKey, fmtCost, fmtElapsed, isRemovableRun, isRunIdle, isTerminalRunStatus, modelShortName, providerShortLabel, routeTargetLabel, runTone, summarizeMailbox } from './asst-state.js';
22
22
  import { createRouteLine } from './asst-route.js';
23
+ import { closeMenu, isMenuOpen } from './asst-menu.js';
23
24
  import { computerToolName, describeComputerAction, frameUrls, markerPercent, openFrameLightbox, parseComputerResult, swapImage } from './asst-computer.js';
24
25
  import * as mascotLib from '../synabun-mascot.js';
25
26
  import { isReducedMotion, subscribe } from '../synabun-ticker.js';
@@ -27,6 +28,7 @@ import { KIND_SPECS, callOutcome, commandOf, editDiff, editPaths, fmtDuration, g
27
28
  import { injectRackStyles } from './asst-rack-styles.js';
28
29
  import { formatRunTokens } from './asst-usage.js';
29
30
  import { sanitizeHtmlString, sanitizeInto } from './asst-sanitize.js';
31
+ import { automationView } from './asst-automation.js';
30
32
 
31
33
  // ── Markdown (lazy marked, same CDN build as the sidepanels) ───────────────
32
34
  let _marked = null;
@@ -227,7 +229,7 @@ export function updateRunCard(card, run, opts = {}) {
227
229
  const routeBits = [r.taskClassLabel || r.taskClass || t('assistant.routes.task', 'task')];
228
230
  if (r.status === 'pending') routeBits.push(t('assistant.routes.state.pending', 'waiting for your choice'));
229
231
  else if (r.decidedBy === 'user') routeBits.push(t('assistant.routes.youChose', 'you chose'));
230
- else if (r.remembered || r.decidedBy === 'remembered') routeBits.push(t('assistant.routes.remembered', 'remembered'));
232
+ else if (r.remembered || r.decidedBy === 'remembered') routeBits.push(t('assistant.routes.remembered', 'your saved choice'));
231
233
  else if (Number.isFinite(Number(r.confidence))) routeBits.push(`${Math.round(Number(r.confidence) <= 1 ? Number(r.confidence) * 100 : Number(r.confidence))}%`);
232
234
  const tip = r.target ? `${t('assistant.routes.routedTo', 'Routed to')} ${routeTargetLabel(r.target, { effort: true })}` : t('assistant.routes.title', 'Model routing');
233
235
  bits.push(`<span class="asst-run-route" data-tooltip="${esc(tip)}">⇄ ${esc(routeBits.join(', '))}</span>`);
@@ -408,8 +410,8 @@ const BURST_MS = 800; // 3+ calls started within this window read "newest ×N"
408
410
  const STATUS_GAP_MS = 1500; // screen-reader announcements at most this often
409
411
  const FOLD_MS = 180; // narration collapsing into the stage caption
410
412
  const COLLAPSE_MS = 260; // the stage folding into the receipt
411
- const FLIP_MS = 320; // the hero flying into the first stage (--asst-dur-slow)
412
- const HERO_LEAVE_MS = 140; // a parked hero nobody took over fades out
413
+ const FLIP_MS = 320; // the hero flying into the "Thinking" row (--asst-dur-slow)
414
+ const HERO_LEAVE_MS = 140; // a parked hero the "Thinking" row did not take over fades out
413
415
  const MAX_TICKS = 60;
414
416
  const NARROW_TICKS = 12;
415
417
  const PREVIEW_LINES = 5;
@@ -434,12 +436,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
434
436
  const scopes = new Map(); // parent_tool_use_id → { container, stream, key }
435
437
  const runCards = new Map(); // runId → card element
436
438
  const routeLines = new Map(); // routeId → line handle
439
+ const autoRows = new Map(); // traceId → the turn's automation trace row
437
440
  let working = null;
438
441
  let workingFace = null; // the "Thinking" row's character: { still, rig?, el, target? } (syncWorkingFace)
439
442
  let followTail = true;
440
443
  let emptyEl = null;
441
444
  let hero = null; // the empty state's character: { rig, el, box, parked, leaving, held, dock, egg, target }
442
- let rigsOn = true; // this panel's rigs may animate (asst-mascot.js lets one panel at a time)
443
445
  const mcpWarned = new Set();
444
446
  let lastErrorText = ''; // a synthetic API-error message's text, so the result does not repeat it
445
447
  let turnFrames = []; // computer cards with inline thumbnails in the current turn
@@ -535,14 +537,21 @@ export function createRenderer(transcriptEl, hooks = {}) {
535
537
  return scope;
536
538
  }
537
539
 
538
- /** The empty state goes; `park` (the user sent a prompt) keeps its character on screen, thinking. */
540
+ /** The empty state goes; `park` (a turn starts) hands its character over to the "Thinking" row. */
539
541
  function hideEmpty({ park = false } = {}) {
540
542
  if (!emptyEl) return;
541
543
  if (!(park && parkHero())) dropHero();
544
+ closeEmptyMenu();
542
545
  emptyEl.remove();
543
546
  emptyEl = null;
544
547
  }
545
548
 
549
+ /** A menu opened from the empty state's project picker does not outlive it. */
550
+ function closeEmptyMenu() {
551
+ const btn = emptyEl?.querySelector('.asst-empty-project-btn');
552
+ if (btn && isMenuOpen(btn)) closeMenu();
553
+ }
554
+
546
555
  // ── The hero ──
547
556
  const cssEase = (name, fallback) => {
548
557
  try { return getComputedStyle($msgs).getPropertyValue(name).trim() || fallback; } catch { return fallback; }
@@ -561,9 +570,11 @@ export function createRenderer(transcriptEl, hooks = {}) {
561
570
 
562
571
  /**
563
572
  * Park the hero where it stands, in an overlay of the transcript's parent,
564
- * so it outlives the empty state and thinks until the first stage of the
565
- * turn takes it over. Not on replay, under reduced motion or while the UI
566
- * is dragged, and not when it is off screen.
573
+ * so it outlives the empty state for its hand-off. It stays there only
574
+ * until the rows of this task are in (the prompt, "Thinking"): settleHero()
575
+ * then gives it to the "Thinking" row, so it is never drawn over the
576
+ * prompt that took the empty state's place. Not on replay, under reduced
577
+ * motion or while the UI is dragged, and not when it is off screen.
567
578
  */
568
579
  function parkHero() {
569
580
  const h = hero;
@@ -585,10 +596,33 @@ export function createRenderer(transcriptEl, hooks = {}) {
585
596
  h.dock = dock;
586
597
  h.parked = true;
587
598
  if (!h.held) h.rig.setPose('think');
599
+ queueMicrotask(() => settleHero(h));
588
600
  mascotChanged();
589
601
  return true;
590
602
  }
591
603
 
604
+ /**
605
+ * The parked hero's hand-off, once the task that parked it has put its rows
606
+ * in: the "Thinking" row's character flies in from the hero's box and the
607
+ * hero leaves in the same frame. With no such row to take it over (a slash
608
+ * command's echo, a rack already live) it fades out.
609
+ */
610
+ function settleHero(h) {
611
+ if (hero !== h || !h.parked || h.leaving) return;
612
+ if (!workingFree()) { dismissHero(); return; }
613
+ const from = h.el.getBoundingClientRect();
614
+ dropHero(); // the "Thinking" row wears the character now (syncWorkingFace)
615
+ const face = workingFace;
616
+ if (!face?.rig) return;
617
+ const row = working;
618
+ row.classList.add('is-flip');
619
+ const anim = flipFrom(face.el, from);
620
+ const done = () => row.classList.remove('is-flip');
621
+ if (!anim) { done(); return; }
622
+ anim.onfinish = done;
623
+ anim.oncancel = done;
624
+ }
625
+
592
626
  function dropHero() {
593
627
  const h = hero;
594
628
  if (!h) return;
@@ -600,20 +634,15 @@ export function createRenderer(transcriptEl, hooks = {}) {
600
634
  syncWorkingFace(); // the "Thinking" row may wear the character now
601
635
  }
602
636
 
603
- /** A parked hero no stage took over leaves: it fades out (at once when calm), after `delay` ms. */
604
- function dismissHero(delay = 0) {
637
+ /** A parked hero the "Thinking" row did not take over leaves: it fades out (at once when calm). */
638
+ function dismissHero() {
605
639
  const h = hero;
606
640
  if (!h?.parked || h.leaving) return;
607
641
  h.leaving = true;
608
- const go = () => {
609
- if (hero !== h) return;
610
- if (calm() || typeof h.dock.animate !== 'function') { dropHero(); return; }
611
- const anim = h.dock.animate([{ opacity: 1 }, { opacity: 0 }], { duration: HERO_LEAVE_MS, easing: cssEase('--asst-ease-exit', 'cubic-bezier(.2,0,1,.9)'), fill: 'forwards' });
612
- anim.onfinish = () => { if (hero === h) dropHero(); };
613
- h.leaveTimer = setTimeout(() => { if (hero === h) dropHero(); }, HERO_LEAVE_MS + 120);
614
- };
615
- if (delay > 0) h.leaveTimer = setTimeout(go, delay);
616
- else go();
642
+ if (calm() || typeof h.dock.animate !== 'function') { dropHero(); return; }
643
+ const anim = h.dock.animate([{ opacity: 1 }, { opacity: 0 }], { duration: HERO_LEAVE_MS, easing: cssEase('--asst-ease-exit', 'cubic-bezier(.2,0,1,.9)'), fill: 'forwards' });
644
+ anim.onfinish = () => { if (hero === h) dropHero(); };
645
+ h.leaveTimer = setTimeout(() => { if (hero === h) dropHero(); }, HERO_LEAVE_MS + 120);
617
646
  }
618
647
 
619
648
  /**
@@ -635,19 +664,6 @@ export function createRenderer(transcriptEl, hooks = {}) {
635
664
  ], { duration: FLIP_MS, easing: cssEase('--asst-ease-emphasized', 'cubic-bezier(.4,.14,.3,1)') });
636
665
  }
637
666
 
638
- /** The hand-off: the first stage's face flies in from the parked hero, which leaves in the same frame. */
639
- function flipIntoStage(rack) {
640
- const h = hero;
641
- const from = h.el.getBoundingClientRect();
642
- dropHero();
643
- rack.row.classList.add('is-flip');
644
- const anim = flipFrom(rack.stage.face, from);
645
- const done = () => rack.row.classList.remove('is-flip');
646
- if (!anim) { done(); return; }
647
- anim.onfinish = done;
648
- anim.oncancel = done;
649
- }
650
-
651
667
  /** Triple-click the character: its eyes part and re-form, once per mount. Silent. */
652
668
  function wireEgg(h) {
653
669
  const hit = (e) => {
@@ -691,13 +707,6 @@ export function createRenderer(transcriptEl, hooks = {}) {
691
707
  try { hooks.onMascotChange?.(); } catch { /* the director is gone */ }
692
708
  }
693
709
 
694
- /** Whether this panel's rigs animate (another panel may lead); still frames otherwise. */
695
- function setRigsEnabled(on) {
696
- if (rigsOn === !!on) return;
697
- rigsOn = !!on;
698
- activateRigs();
699
- }
700
-
701
710
  function prune() {
702
711
  const rows = $msgs.children;
703
712
  while (rows.length > MAX_MESSAGES && rows[0] !== working) rows[0].remove();
@@ -709,7 +718,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
709
718
  if (scope.container === $msgs && working && working.parentNode === $msgs) $msgs.insertBefore(node, working);
710
719
  else scope.container.appendChild(node);
711
720
  if (scope === rootScope) prune();
712
- // Text, a card, a notice…: the parked hero would sit on it. Only a rack (its stage takes it over) and the prompt keep it.
721
+ // Text, a card, a notice…: the parked hero would sit on it. The prompt and a rack leave it to settleHero().
713
722
  if (hero?.parked && scope === rootScope && !node._rack && !node.classList.contains('msg-user')) dismissHero();
714
723
  if (racks.size) syncRacks();
715
724
  scrollEnd();
@@ -750,8 +759,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
750
759
  const a = el('a', 'file-link', match[0]);
751
760
  a.href = '#';
752
761
  a.title = t('assistant.copyPath', 'Click to copy path');
753
- const path = match[0].replace(/:\d+$/, '');
754
- a.addEventListener('click', (e) => { e.preventDefault(); hooks.copy?.(path); a.style.opacity = '0.5'; setTimeout(() => { a.style.opacity = ''; }, 300); });
762
+ // The click is taken on the transcript (asst-panel.js → ui-file-links.js): the code editor when it
763
+ // can open the path, else the copy a click has always made.
755
764
  frag.appendChild(a);
756
765
  last = match.index + match[0].length;
757
766
  }
@@ -1495,13 +1504,10 @@ export function createRenderer(transcriptEl, hooks = {}) {
1495
1504
  rack.clearTimer = 0;
1496
1505
  rack.row.classList.add('is-staged');
1497
1506
  if (!rack.rig) {
1498
- // The parked hero hands over: the stage's character starts in its pose and flies in from its box.
1499
- const heir = hero?.parked && !hero.leaving && !calm() ? hero : null;
1500
- try { rack.rig = mascotLib.createMascot(rack.stage.face, { width: 72, height: 36, rangeX: 10, rangeY: 7, active: rigsOn, pose: heir ? heir.rig.pose : 'idle' }); } catch { rack.rig = null; }
1507
+ try { rack.rig = mascotLib.createMascot(rack.stage.face, { width: 72, height: 36, rangeX: 10, rangeY: 7, pose: 'idle' }); } catch { rack.rig = null; }
1501
1508
  rack.pose = null;
1502
1509
  rack.target = rack.rig ? stageTarget(rack) : null;
1503
- if (heir && rack.rig) flipIntoStage(rack);
1504
- else if (hero?.parked) dropHero();
1510
+ if (hero?.parked) dropHero(); // the stage is the character now: a hero still on its way out goes at once
1505
1511
  }
1506
1512
  mascotChanged();
1507
1513
  return;
@@ -1560,12 +1566,16 @@ export function createRenderer(transcriptEl, hooks = {}) {
1560
1566
  rack.bubble = { step: null, since: 0, key: '', open: false };
1561
1567
  }
1562
1568
 
1563
- /** One rig animates: the hero, else the newest stage, else the "Thinking" row's; none while another panel leads (setRigsEnabled). */
1569
+ /**
1570
+ * One rig of this transcript animates: the hero, else the newest stage, else
1571
+ * the "Thinking" row's. The rule ends at this renderer: every other panel
1572
+ * runs its own, so N live sessions show N moving characters.
1573
+ */
1564
1574
  function activateRigs() {
1565
1575
  const newest = hero?.rig ? null : newestStage();
1566
- for (const rack of racks) if (rack.rig) rack.rig.setActive?.(rigsOn && rack === newest);
1567
- hero?.rig?.setActive?.(rigsOn);
1568
- workingFace?.rig?.setActive?.(rigsOn && !hero?.rig && !newest);
1576
+ for (const rack of racks) if (rack.rig) rack.rig.setActive?.(rack === newest);
1577
+ hero?.rig?.setActive?.(true);
1578
+ workingFace?.rig?.setActive?.(!hero?.rig && !newest);
1569
1579
  }
1570
1580
 
1571
1581
  /** The bubble's call: the awake one, else the newest; it holds 900 ms (3 s for thinking) before the next wins. */
@@ -2605,18 +2615,23 @@ export function createRenderer(transcriptEl, hooks = {}) {
2605
2615
 
2606
2616
  // ── The "Thinking" row's character ──
2607
2617
  // The row wears the mascot, thinking, only when no other character is on
2608
- // screen: no parked hero, no stage (a live rack right above it hides the
2609
- // row altogether). A rig, so the director can dress it; a painted still
2610
- // when calm.
2618
+ // screen: no hero, no stage (a live rack right above it hides the row
2619
+ // altogether). A rig, so the director can dress it; a painted still when
2620
+ // calm. A send hands the hero over to it (settleHero).
2611
2621
  function workingCovered() {
2612
2622
  let prev = working?.previousElementSibling || null;
2613
2623
  while (prev && prev.classList.contains('asst-folding')) prev = prev.previousElementSibling;
2614
2624
  return !!prev?._rack?.live;
2615
2625
  }
2616
2626
 
2627
+ /** The row is up and neither a stage nor a live rack stands in for it. */
2628
+ function workingFree() {
2629
+ return !!working?.isConnected && !newestStage() && !workingCovered();
2630
+ }
2631
+
2617
2632
  function syncWorkingFace() {
2618
2633
  const host = working?.querySelector('.asst-working-face');
2619
- const wanted = !!host && working.isConnected && !hero && !newestStage() && !workingCovered();
2634
+ const wanted = !!host && !hero && workingFree();
2620
2635
  if (!wanted) { dropWorkingFace(); return; }
2621
2636
  if (workingFace && workingFace.still === calm()) return;
2622
2637
  dropWorkingFace();
@@ -2625,7 +2640,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
2625
2640
  return;
2626
2641
  }
2627
2642
  try {
2628
- const rig = mascotLib.createMascot(host, { width: 56, height: 28, active: rigsOn, pose: 'think' });
2643
+ const rig = mascotLib.createMascot(host, { width: 56, height: 28, pose: 'think' });
2629
2644
  const face = { still: false, rig, el: rig.el, held: false };
2630
2645
  face.target = { kind: 'working', rig, el: rig.el, hold(on) { face.held = !!on; }, restore() { if (workingFace === face) rig.setPose('think'); } };
2631
2646
  workingFace = face;
@@ -2638,6 +2653,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
2638
2653
  if (!face) return;
2639
2654
  workingFace = null;
2640
2655
  try { face.rig?.destroy(); } catch { /* gone */ }
2656
+ working?.classList.remove('is-flip');
2641
2657
  working?.querySelector('.asst-working-face')?.replaceChildren();
2642
2658
  if (face.rig) mascotChanged();
2643
2659
  }
@@ -2751,6 +2767,115 @@ export function createRenderer(transcriptEl, hooks = {}) {
2751
2767
  if (line) { line.el.remove(); routeLines.delete(routeId); }
2752
2768
  }
2753
2769
 
2770
+ // ── The automation trace ──
2771
+ // What the harness did for a turn (`synabun.automation`, asst-automation.js):
2772
+ // one disclosure row per turn, upserted by its traceId. It appears where the
2773
+ // turn was sent, moves under the reply when the turn ends and updates in
2774
+ // place when learning finishes. Drawn from the event alone, so the live row
2775
+ // and the replayed one are the same. Opening one keeps the next ones open.
2776
+ let autoUid = 0;
2777
+ const TRACE_OPEN_KEY = 'synabun-asst-trace-open';
2778
+ const traceOpenPref = () => { try { return localStorage.getItem(TRACE_OPEN_KEY) === '1'; } catch { return false; } };
2779
+
2780
+ function buildAutoRow() {
2781
+ const node = el('div', 'asst-route-line asst-auto');
2782
+ const detailId = `asst-auto-detail-${++autoUid}`;
2783
+ node.innerHTML = `
2784
+ <button type="button" class="asst-route-line-head asst-auto-head" aria-expanded="false" aria-controls="${detailId}">
2785
+ <span class="asst-route-glyph asst-icon" aria-hidden="true">${ICON_MEMORY}</span>
2786
+ <span class="asst-route-class asst-auto-title"></span>
2787
+ <span class="asst-route-meta asst-auto-summary"></span>
2788
+ <span class="asst-route-state asst-auto-state"></span>
2789
+ <span class="asst-route-chev" aria-hidden="true">&#x203A;</span>
2790
+ </button>
2791
+ <div class="asst-route-detail asst-auto-detail" id="${detailId}" hidden></div>`;
2792
+ const head = node.querySelector('.asst-auto-head');
2793
+ const detail = node.querySelector('.asst-auto-detail');
2794
+ const setOpen = (open) => {
2795
+ head.setAttribute('aria-expanded', open ? 'true' : 'false');
2796
+ detail.hidden = !open;
2797
+ node.classList.toggle('open', open);
2798
+ };
2799
+ head.addEventListener('click', () => {
2800
+ const open = head.getAttribute('aria-expanded') !== 'true';
2801
+ setOpen(open);
2802
+ try { localStorage.setItem(TRACE_OPEN_KEY, open ? '1' : '0'); } catch { /* no storage: this row only */ }
2803
+ });
2804
+ setOpen(traceOpenPref());
2805
+ return { el: node, detail, atEnd: false };
2806
+ }
2807
+
2808
+ function paintAutoRow(row, ev) {
2809
+ const view = automationView(ev, { t: hooks.t });
2810
+ row.el.dataset.traceId = view.traceId;
2811
+ row.el.dataset.phase = view.phase;
2812
+ row.el.classList.toggle('busy', view.tone === 'busy');
2813
+ row.el.classList.toggle('learned', view.tone === 'learned');
2814
+ row.el.querySelector('.asst-auto-title').textContent = view.title;
2815
+ row.el.querySelector('.asst-auto-summary').textContent = view.summary;
2816
+ const state = row.el.querySelector('.asst-auto-state');
2817
+ state.textContent = view.state;
2818
+ state.hidden = !view.state;
2819
+ row.detail.replaceChildren();
2820
+ for (const group of view.groups) {
2821
+ const box = el('div', 'asst-auto-group');
2822
+ box.dataset.group = group.key;
2823
+ box.appendChild(el('div', 'asst-auto-h', group.title));
2824
+ for (const item of group.rows) {
2825
+ const line = el('div', `asst-auto-row${item.tone ? ` ${item.tone}` : ''}`);
2826
+ if (item.label || item.meta) {
2827
+ const lead = el('div', 'asst-auto-row-head');
2828
+ if (item.label) lead.appendChild(el('span', 'asst-auto-tier', item.label));
2829
+ if (item.meta) lead.appendChild(el('span', 'asst-auto-meta', item.meta));
2830
+ line.appendChild(lead);
2831
+ }
2832
+ // Memory text and the user's own words: always as text, never as markup.
2833
+ if (item.text) line.appendChild(el(item.pre ? 'pre' : 'div', 'asst-auto-text', item.text));
2834
+ if (item.ids.length) {
2835
+ const ids = el('div', 'asst-auto-ids');
2836
+ for (const id of item.ids) {
2837
+ const link = el('button', 'asst-link', String(id).slice(0, 8));
2838
+ link.type = 'button';
2839
+ link.dataset.memoryId = id;
2840
+ link.title = `${t('assistant.automation.open', 'Open memory')} ${id}`;
2841
+ link.setAttribute('aria-label', link.title);
2842
+ link.addEventListener('click', () => hooks.onMemoryClick?.(id, { id }));
2843
+ ids.appendChild(link);
2844
+ }
2845
+ line.appendChild(ids);
2846
+ }
2847
+ box.appendChild(line);
2848
+ }
2849
+ row.detail.appendChild(box);
2850
+ }
2851
+ }
2852
+
2853
+ /** Create or update the trace row for `ev.traceId`. */
2854
+ function upsertAutomation(ev) {
2855
+ const id = String(ev?.traceId || '');
2856
+ if (!id) return null;
2857
+ let row = autoRows.get(id);
2858
+ const fresh = !row || !row.el.isConnected;
2859
+ if (fresh) {
2860
+ row = buildAutoRow();
2861
+ autoRows.set(id, row);
2862
+ while (autoRows.size > 200) autoRows.delete(autoRows.keys().next().value);
2863
+ }
2864
+ paintAutoRow(row, ev);
2865
+ // Under the reply once the turn has ended; a later snapshot (learning) never moves it again.
2866
+ const ended = ev.phase !== 'sent';
2867
+ if (fresh || (ended && !row.atEnd)) {
2868
+ // Placed like any row (before "Working…"), but a status line of the harness never dismisses the hero.
2869
+ if (working && working.parentNode === $msgs) $msgs.insertBefore(row.el, working);
2870
+ else $msgs.appendChild(row.el);
2871
+ if (ended) row.atEnd = true;
2872
+ prune();
2873
+ if (racks.size) syncRacks();
2874
+ scrollEnd();
2875
+ }
2876
+ return row.el;
2877
+ }
2878
+
2754
2879
  // ── Empty state ──
2755
2880
  function showEmpty() {
2756
2881
  if (emptyEl || $msgs.querySelector(':scope > :not(.asst-working)')) return;
@@ -2759,14 +2884,22 @@ export function createRenderer(transcriptEl, hooks = {}) {
2759
2884
  <div class="asst-empty-mascot" aria-hidden="true"></div>
2760
2885
  <div class="asst-empty-title" role="heading" aria-level="2">${esc(t('assistant.empty.heading', 'What should we do?'))}</div>
2761
2886
  <div class="asst-empty-body">${esc(t('assistant.empty.lead', 'I can use your Mac, look things up on the web, edit files and run commands, and remember or schedule things.'))}</div>
2887
+ <div class="asst-empty-greet"></div>
2888
+ <div class="asst-empty-project" hidden></div>
2762
2889
  <div class="asst-suggestions" role="list"></div>`;
2763
2890
  const box = emptyEl.querySelector('.asst-empty-mascot');
2764
2891
  try {
2765
- const rig = mascotLib.createMascot(box, { width: 120, height: 60, active: rigsOn });
2892
+ const rig = mascotLib.createMascot(box, { width: 120, height: 60 });
2766
2893
  hero = { rig, el: rig.el, box, parked: false, leaving: false, held: false, dock: null, egg: false, leaveTimer: 0 };
2767
2894
  hero.target = heroTarget(hero);
2768
2895
  wireEgg(hero);
2769
2896
  } catch { hero = null; }
2897
+ // The project the conversation works in, under the lead: the host draws the picker (the brain
2898
+ // picker's project field), so it and the toolbar's Working project are one control.
2899
+ const projectSlot = emptyEl.querySelector('.asst-empty-project');
2900
+ let picker = null;
2901
+ try { picker = typeof hooks.mountEmptyProject === 'function' ? hooks.mountEmptyProject(projectSlot) : null; } catch { picker = null; }
2902
+ projectSlot.hidden = !picker;
2770
2903
  renderSuggestions();
2771
2904
  $msgs.appendChild(emptyEl);
2772
2905
  // The host's "Starting…" character flies into place (not on replay or reduced motion).
@@ -2776,6 +2909,20 @@ export function createRenderer(transcriptEl, hooks = {}) {
2776
2909
  mascotChanged();
2777
2910
  }
2778
2911
 
2912
+ /**
2913
+ * The greeting SynaBun prepared for this return (public/shared/greeting/) takes the lead's place.
2914
+ * The panel calls it once it knows the conversation is new: a restored one is never greeted.
2915
+ */
2916
+ function offerGreeting() {
2917
+ const owner = emptyEl;
2918
+ if (!owner || owner.dataset.greetingAsked === '1' || typeof hooks.greeting !== 'function') return;
2919
+ owner.dataset.greetingAsked = '1';
2920
+ const slot = owner.querySelector('.asst-empty-greet');
2921
+ Promise.resolve().then(() => hooks.greeting(slot))
2922
+ .then((greeting) => { if (greeting && emptyEl === owner) owner.classList.add('has-greeting'); })
2923
+ .catch(() => { /* no greeting: the lead stays */ });
2924
+ }
2925
+
2779
2926
  function renderSuggestions() {
2780
2927
  if (!emptyEl) return;
2781
2928
  const host = emptyEl.querySelector('.asst-suggestions');
@@ -3101,6 +3248,9 @@ export function createRenderer(transcriptEl, hooks = {}) {
3101
3248
  // The card itself arrives as a control_request; its outcome is a line (the panel's card shows it live).
3102
3249
  if (ev.phase !== 'card') appendStatus(clarifyLine(ev), 'info');
3103
3250
  return true;
3251
+ case 'synabun.automation':
3252
+ upsertAutomation(ev);
3253
+ return true;
3104
3254
  default:
3105
3255
  return false;
3106
3256
  }
@@ -3131,8 +3281,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3131
3281
 
3132
3282
  /**
3133
3283
  * The turn ended. `holdMs`: the panel plays a one-shot (success, error) on
3134
- * the character on screen, so its stage stays up that long before it folds
3135
- * and a parked hero waits as long before it fades.
3284
+ * the character on screen, so its stage stays up that long before it folds.
3136
3285
  */
3137
3286
  function finishTurn({ holdMs = 0 } = {}) {
3138
3287
  flushStream(rootScope);
@@ -3154,7 +3303,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3154
3303
  rack.closed = true;
3155
3304
  if (rack.live || tail || rack === stage) renderRack(rack);
3156
3305
  }
3157
- if (hero?.parked) dismissHero(hold ? holdMs : 0);
3306
+ if (hero?.parked) dismissHero();
3158
3307
  }
3159
3308
 
3160
3309
  /**
@@ -3224,6 +3373,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3224
3373
  scopes.clear();
3225
3374
  runCards.clear();
3226
3375
  routeLines.clear();
3376
+ autoRows.clear();
3227
3377
  turnFrames = [];
3228
3378
  rootScope.stream = null;
3229
3379
  rootScope.kept = null;
@@ -3240,6 +3390,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3240
3390
  seen.clear();
3241
3391
  snapshot = null;
3242
3392
  waiting = null;
3393
+ closeEmptyMenu();
3243
3394
  $msgs.innerHTML = '';
3244
3395
  emptyEl = null;
3245
3396
  followTail = true;
@@ -3264,6 +3415,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3264
3415
  registerRouteLine,
3265
3416
  removeRouteLine,
3266
3417
  getRouteLine: (routeId) => routeLines.get(routeId) || null,
3418
+ upsertAutomation,
3267
3419
  getRunCard: (runId) => runCards.get(runId) || null,
3268
3420
  handleEvent,
3269
3421
  /** The live gate for an event (false: already drawn); registers what it lets through. */
@@ -3280,6 +3432,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3280
3432
  hideWorking,
3281
3433
  isWorkingShown: () => !!working?.isConnected,
3282
3434
  showEmpty,
3435
+ offerGreeting,
3283
3436
  refreshEmpty: renderSuggestions,
3284
3437
  isEmpty: () => !!emptyEl,
3285
3438
  scrollEnd,
@@ -3287,7 +3440,6 @@ export function createRenderer(transcriptEl, hooks = {}) {
3287
3440
  md,
3288
3441
  /** The character on screen, for the panel's director: { kind, rig, el, hold(on), restore() } or null. */
3289
3442
  mascot: visibleMascot,
3290
- setRigsEnabled,
3291
3443
  /** What this turn did: real calls (not the prompt-time recall), whether the last one failed, a turn error. */
3292
3444
  turnStats: () => ({ calls: turn.calls, lastFailed: turn.last?.state === 'error', errored: turn.errored }),
3293
3445
  /** The text of the turn's last message at the root ('' when it folded into a rack or there was none). */
@@ -0,0 +1,108 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Assistant — Model routes editor: where its model lists come from
3
+ // ═══════════════════════════════════════════
4
+ // The data side of the Routes editor (asst-route.js `openRoutesEditor`), kept
5
+ // apart from its markup. No DOM is built here.
6
+ //
7
+ // One list per provider, the one every Assistant model menu shows:
8
+ // loadModelCatalog() of asst-brain-picker.js, which is GET /api/assistant/catalog,
9
+ // itself built from the lists the Claude, Codex and OpenCode panels' own
10
+ // selectors read. Nothing here, and nothing in the editor, may keep a second
11
+ // list of models: a label, a tier, a price or an effort level is read from the
12
+ // catalog row each time it is drawn.
13
+
14
+ import { catalogModelRow, loadModelCatalog, modelCatalogStatus, onModelCatalogChange } from './asst-brain-picker.js';
15
+ import { routeTargetLabel } from './asst-state.js';
16
+
17
+ /** The catalog as the editor opens: a new build, never the copy another menu loaded a minute ago. */
18
+ export function loadRouteModels() {
19
+ return loadModelCatalog({ force: true });
20
+ }
21
+
22
+ /** Loading/partial state belongs to the same model source, without changing the catalog shape. */
23
+ export function routeModelsStatus() { return modelCatalogStatus(); }
24
+
25
+ /**
26
+ * The name shown for a saved route's model. The catalog's label for that id wins:
27
+ * a route keeps the label it was saved with, and an alias moves ("haiku" was
28
+ * Haiku 4.5, then Haiku 5.5). A model the catalog no longer lists (removed by its
29
+ * provider, hidden, or its provider is not answering) keeps the saved label,
30
+ * then its id, then the provider's name: never an empty name.
31
+ */
32
+ export function routeModelLabel(pref, catalog) {
33
+ if (!pref || typeof pref !== 'object') return '';
34
+ const row = pref.provider && pref.model ? catalogModelRow(pref.provider, pref.model, catalog) : null;
35
+ const live = row?.label ? String(row.label).trim() : '';
36
+ return live || routeTargetLabel(pref);
37
+ }
38
+
39
+ /** What the editor draws from a catalog: when this is unchanged, there is nothing to redraw. */
40
+ export function catalogSignature(catalog) {
41
+ if (!catalog || typeof catalog !== 'object') return '';
42
+ return JSON.stringify(Object.keys(catalog).sort().map(provider => [
43
+ provider,
44
+ (Array.isArray(catalog[provider]) ? catalog[provider] : []).map(m => [
45
+ m?.id, m?.label, m?.status ?? null, m?.vision ?? null, m?.outputs?.image ?? null, m?.outputs?.video ?? null,
46
+ (m?.efforts || []).join(','), m?.defaultEffort || '', m?.isDefault === true,
47
+ ]),
48
+ ]));
49
+ }
50
+
51
+ /**
52
+ * Keeps an open editor on the current lists. `apply(catalog)` is called with a
53
+ * newly loaded catalog whenever it differs from the last one applied:
54
+ * - after a change event (a model hidden or shown, a provider listing other
55
+ * models, an OpenCode provider connected or removed): a new build is asked;
56
+ * - when the window is shown again: the catalog is read again, and reloaded
57
+ * once its copy has aged out (no event tells a window that slept).
58
+ * Returns { seed(catalog), stop() }: seed with the catalog the editor loaded
59
+ * itself, stop when it closes. No timer runs while nothing changes.
60
+ */
61
+ export function watchRouteModels(apply, {
62
+ load = loadModelCatalog,
63
+ subscribe = onModelCatalogChange,
64
+ doc = typeof document !== 'undefined' ? document : null,
65
+ delayMs = 150,
66
+ setTimer = setTimeout,
67
+ clearTimer = clearTimeout,
68
+ } = {}) {
69
+ let stopped = false;
70
+ let timer = null;
71
+ let last = null;
72
+ let run = 0;
73
+
74
+ async function reload(force) {
75
+ const mine = ++run;
76
+ let catalog = null;
77
+ try { catalog = await load({ force }); } catch { return; }
78
+ if (stopped || mine !== run || !catalog) return;
79
+ const signature = catalogSignature(catalog);
80
+ if (signature === last) return;
81
+ last = signature;
82
+ try { apply(catalog); } catch { /* the editor's own failure */ }
83
+ }
84
+
85
+ // Several events for one change (a hide broadcasts twice) become one reload.
86
+ let pendingForce = false;
87
+ function schedule(force) {
88
+ if (stopped) return;
89
+ pendingForce = pendingForce || force;
90
+ if (timer != null) clearTimer(timer);
91
+ timer = setTimer(() => { timer = null; const f = pendingForce; pendingForce = false; reload(f); }, delayMs);
92
+ }
93
+
94
+ const unsubscribe = subscribe(() => schedule(true));
95
+ const onVisible = () => { if (doc?.visibilityState !== 'hidden') schedule(false); };
96
+ doc?.addEventListener?.('visibilitychange', onVisible);
97
+
98
+ return {
99
+ seed(catalog) { last = catalogSignature(catalog); },
100
+ stop() {
101
+ stopped = true;
102
+ if (timer != null) clearTimer(timer);
103
+ timer = null;
104
+ try { unsubscribe?.(); } catch { /* ignore */ }
105
+ doc?.removeEventListener?.('visibilitychange', onVisible);
106
+ },
107
+ };
108
+ }