synabun 2.0.0 → 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (507) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/NOTICE +5 -0
  3. package/README.md +2 -1
  4. package/THIRD-PARTY-LICENSES.md +59 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +153 -18
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +26 -3
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/child-path.js +303 -0
  14. package/lib/claude-hooks.js +2 -1
  15. package/lib/data-home-migration.js +2 -0
  16. package/lib/desktop-app.js +265 -0
  17. package/lib/external-tools.js +7 -3
  18. package/lib/log-redact.js +102 -0
  19. package/lib/packaged-runtime.js +138 -11
  20. package/lib/start-launcher.js +88 -1
  21. package/mcp-server/dist/http.d.ts +3 -1
  22. package/mcp-server/dist/http.d.ts.map +1 -1
  23. package/mcp-server/dist/http.js +15 -4
  24. package/mcp-server/dist/http.js.map +1 -1
  25. package/mcp-server/dist/index.d.ts.map +1 -1
  26. package/mcp-server/dist/index.js +9 -6
  27. package/mcp-server/dist/index.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  33. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  34. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  35. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  36. package/mcp-server/dist/services/identity.d.ts +11 -0
  37. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  38. package/mcp-server/dist/services/identity.js +24 -0
  39. package/mcp-server/dist/services/identity.js.map +1 -1
  40. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  41. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  42. package/mcp-server/dist/services/neural-interface.js +34 -2
  43. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  44. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  45. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  46. package/mcp-server/dist/services/typesafe-config.js +4 -1
  47. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  48. package/mcp-server/dist/tools/agents.d.ts +10 -7
  49. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  50. package/mcp-server/dist/tools/agents.js +34 -13
  51. package/mcp-server/dist/tools/agents.js.map +1 -1
  52. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  53. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  54. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  55. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  56. package/mcp-server/dist/tui.js +2 -1
  57. package/mcp-server/dist/tui.js.map +1 -1
  58. package/mcp-server/dist/version.d.ts +5 -0
  59. package/mcp-server/dist/version.d.ts.map +1 -0
  60. package/mcp-server/dist/version.js +21 -0
  61. package/mcp-server/dist/version.js.map +1 -0
  62. package/mcp-server/package-lock.json +2 -2
  63. package/mcp-server/package.json +1 -3
  64. package/mcp-server/run.mjs +15 -9
  65. package/mcp-server/src/http.ts +18 -3
  66. package/mcp-server/src/index.ts +9 -6
  67. package/mcp-server/src/services/budget-judgments.ts +183 -0
  68. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  69. package/mcp-server/src/services/identity.ts +27 -0
  70. package/mcp-server/src/services/neural-interface.ts +34 -2
  71. package/mcp-server/src/services/typesafe-config.ts +4 -1
  72. package/mcp-server/src/tools/agents.ts +33 -14
  73. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  74. package/mcp-server/src/tui.ts +2 -1
  75. package/mcp-server/src/version.ts +22 -0
  76. package/neural-interface/i18n/en.json +2126 -209
  77. package/neural-interface/i18n/pt-BR.json +2123 -207
  78. package/neural-interface/lib/account-usage.js +163 -0
  79. package/neural-interface/lib/assistant-api.js +316 -57
  80. package/neural-interface/lib/assistant-brains/claude.js +19 -2
  81. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  82. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  83. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  84. package/neural-interface/lib/assistant-budget-split.js +408 -0
  85. package/neural-interface/lib/assistant-budget.js +94 -95
  86. package/neural-interface/lib/assistant-catalog.js +179 -27
  87. package/neural-interface/lib/assistant-config.js +91 -14
  88. package/neural-interface/lib/assistant-dispatch.js +494 -138
  89. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  90. package/neural-interface/lib/assistant-jev.js +26 -3
  91. package/neural-interface/lib/assistant-memory.js +24 -10
  92. package/neural-interface/lib/assistant-persona.js +73 -12
  93. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  94. package/neural-interface/lib/assistant-pricing.js +21 -6
  95. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  96. package/neural-interface/lib/assistant-route-gate.js +19 -3
  97. package/neural-interface/lib/assistant-router.js +373 -42
  98. package/neural-interface/lib/assistant-runtime.js +488 -59
  99. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  100. package/neural-interface/lib/assistant-test-density.js +142 -0
  101. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  102. package/neural-interface/lib/assistant-user-context.js +908 -0
  103. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  104. package/neural-interface/lib/assistant-user-learn.js +404 -0
  105. package/neural-interface/lib/assistant-user-threads.js +226 -0
  106. package/neural-interface/lib/assistant-user-writer.js +183 -0
  107. package/neural-interface/lib/augmented-path.js +317 -53
  108. package/neural-interface/lib/browser-background-tab.js +134 -0
  109. package/neural-interface/lib/browser-screenshot.js +678 -61
  110. package/neural-interface/lib/claude-accounts.js +342 -31
  111. package/neural-interface/lib/claude-agent-bridge.js +123 -9
  112. package/neural-interface/lib/claude-executable.js +6 -1
  113. package/neural-interface/lib/claude-model-catalog.js +33 -5
  114. package/neural-interface/lib/claude-panel-auth.js +221 -0
  115. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  116. package/neural-interface/lib/claude-temporary.js +46 -7
  117. package/neural-interface/lib/cli-install-api.js +137 -0
  118. package/neural-interface/lib/cli-install.js +797 -0
  119. package/neural-interface/lib/cli-path.js +130 -0
  120. package/neural-interface/lib/cli-resolver.js +744 -0
  121. package/neural-interface/lib/cli-update-planner.js +143 -7
  122. package/neural-interface/lib/cloudflared/install.js +533 -0
  123. package/neural-interface/lib/cloudflared/manager.js +545 -0
  124. package/neural-interface/lib/cloudflared/pins.js +115 -0
  125. package/neural-interface/lib/cloudflared/pins.json +63 -0
  126. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  127. package/neural-interface/lib/codex-account-auth.js +178 -0
  128. package/neural-interface/lib/codex-accounts.js +489 -0
  129. package/neural-interface/lib/codex-capabilities.js +2 -1
  130. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  131. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  132. package/neural-interface/lib/codex-panel-auth.js +62 -0
  133. package/neural-interface/lib/codex-session-settings.js +1 -1
  134. package/neural-interface/lib/codex-settings-schema.js +120 -0
  135. package/neural-interface/lib/desktop/config.js +3 -0
  136. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  137. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  138. package/neural-interface/lib/editor-completion.js +714 -0
  139. package/neural-interface/lib/editor-config.js +149 -0
  140. package/neural-interface/lib/editor-files-api.js +268 -0
  141. package/neural-interface/lib/editor-git-api.js +134 -0
  142. package/neural-interface/lib/file-explorer-api.js +1039 -0
  143. package/neural-interface/lib/file-explorer-system.js +355 -0
  144. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  145. package/neural-interface/lib/file-explorer-watch.js +239 -0
  146. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  147. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  148. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  149. package/neural-interface/lib/git-bash/install.js +529 -0
  150. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  151. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  152. package/neural-interface/lib/git-bash/manager.js +268 -0
  153. package/neural-interface/lib/git-bash/pins.js +206 -0
  154. package/neural-interface/lib/git-bash/pins.json +34 -0
  155. package/neural-interface/lib/git-bash/resolver.js +194 -0
  156. package/neural-interface/lib/git-diff-api.js +103 -0
  157. package/neural-interface/lib/git-read.js +234 -0
  158. package/neural-interface/lib/greeting/api.js +89 -0
  159. package/neural-interface/lib/greeting/budgets.js +90 -0
  160. package/neural-interface/lib/greeting/engine.js +461 -0
  161. package/neural-interface/lib/greeting/facts.js +320 -0
  162. package/neural-interface/lib/greeting/sources.js +252 -0
  163. package/neural-interface/lib/greeting/writer.js +139 -0
  164. package/neural-interface/lib/http-guards.js +189 -13
  165. package/neural-interface/lib/install-details.js +145 -0
  166. package/neural-interface/lib/machine-names.js +88 -0
  167. package/neural-interface/lib/mcp-installer.js +14 -2
  168. package/neural-interface/lib/memory-api.js +31 -7
  169. package/neural-interface/lib/native-loop-providers.js +26 -8
  170. package/neural-interface/lib/native-loop-runtime.js +9 -4
  171. package/neural-interface/lib/os-path.js +319 -0
  172. package/neural-interface/lib/plugin-installer.js +4 -2
  173. package/neural-interface/lib/project-storage.js +5 -7
  174. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  175. package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
  176. package/neural-interface/lib/pty-host/core.js +2 -1
  177. package/neural-interface/lib/pty-host/manager.js +27 -2
  178. package/neural-interface/lib/remote-policy.js +20 -11
  179. package/neural-interface/lib/request-gate.js +589 -0
  180. package/neural-interface/lib/session-title-generator.js +4 -3
  181. package/neural-interface/lib/system-about.js +340 -0
  182. package/neural-interface/lib/system-activity.js +66 -0
  183. package/neural-interface/lib/terminal-env.js +11 -3
  184. package/neural-interface/lib/terminal-path.js +55 -0
  185. package/neural-interface/lib/ui-blob-api.js +108 -0
  186. package/neural-interface/lib/ui-blob-store.js +68 -0
  187. package/neural-interface/lib/virtual-socket.js +12 -1
  188. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  189. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  190. package/neural-interface/lib/whatsapp/commands.js +13 -14
  191. package/neural-interface/lib/whatsapp/config.js +134 -20
  192. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  193. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  194. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  195. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  196. package/neural-interface/lib/whatsapp/manager.js +92 -32
  197. package/neural-interface/lib/whatsapp/paths.js +11 -1
  198. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  199. package/neural-interface/lib/whatsapp/redact.js +63 -0
  200. package/neural-interface/lib/whatsapp/service.js +343 -91
  201. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  202. package/neural-interface/package-lock.json +701 -2
  203. package/neural-interface/package.json +13 -2
  204. package/neural-interface/public/index.html +2 -0
  205. package/neural-interface/public/index2d.html +2 -0
  206. package/neural-interface/public/install-app.html +9 -6
  207. package/neural-interface/public/offline.html +19 -3
  208. package/neural-interface/public/onboarding.html +107 -2
  209. package/neural-interface/public/shared/account-i18n.js +7 -0
  210. package/neural-interface/public/shared/account-menu.js +539 -0
  211. package/neural-interface/public/shared/account-usage.js +19 -0
  212. package/neural-interface/public/shared/account-view.js +335 -0
  213. package/neural-interface/public/shared/agent-runtime-options.js +50 -14
  214. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  215. package/neural-interface/public/shared/assistant/asst-api.js +32 -2
  216. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  217. package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
  218. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  219. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  220. package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
  221. package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
  222. package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
  223. package/neural-interface/public/shared/assistant/asst-render.js +197 -23
  224. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  225. package/neural-interface/public/shared/assistant/asst-route.js +95 -343
  226. package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
  227. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  228. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  229. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  230. package/neural-interface/public/shared/assistant/asst-state.js +186 -2
  231. package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
  232. package/neural-interface/public/shared/blob-store.js +111 -22
  233. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  234. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  235. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  236. package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
  237. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  238. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  239. package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
  240. package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
  241. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  242. package/neural-interface/public/shared/claude-account-view.js +3 -0
  243. package/neural-interface/public/shared/cli-install-view.js +392 -0
  244. package/neural-interface/public/shared/cli-status.js +282 -22
  245. package/neural-interface/public/shared/constants.js +6 -1
  246. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  247. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  248. package/neural-interface/public/shared/cp/cp-styles.js +61 -0
  249. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  250. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  251. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  252. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  253. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  254. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  255. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  256. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  257. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  258. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  259. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  260. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  261. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  262. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  263. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  264. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  265. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  266. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  267. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  268. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  269. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  270. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  271. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  272. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  273. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  274. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  275. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  276. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  277. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  278. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  279. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  280. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  281. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  282. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  283. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  284. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  285. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  286. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  287. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  288. package/neural-interface/public/shared/file-links.js +339 -0
  289. package/neural-interface/public/shared/git-bash-view.js +145 -0
  290. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  291. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  292. package/neural-interface/public/shared/html-shell.js +161 -165
  293. package/neural-interface/public/shared/i18n.js +19 -1
  294. package/neural-interface/public/shared/install-details-view.js +86 -0
  295. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  296. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  297. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
  298. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  299. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
  300. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
  301. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  302. package/neural-interface/public/shared/pwa-install-page.js +32 -2
  303. package/neural-interface/public/shared/pwa-install.css +3 -3
  304. package/neural-interface/public/shared/pwa-install.js +100 -1
  305. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  306. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  307. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  308. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  309. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  310. package/neural-interface/public/shared/settings/settings-ia.js +33 -4
  311. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  312. package/neural-interface/public/shared/showcase-privacy.js +489 -0
  313. package/neural-interface/public/shared/start-bridge.js +238 -19
  314. package/neural-interface/public/shared/storage.js +30 -3
  315. package/neural-interface/public/shared/styles.css +1710 -770
  316. package/neural-interface/public/shared/synabun-mascot.js +68 -58
  317. package/neural-interface/public/shared/tunnel-view.js +167 -0
  318. package/neural-interface/public/shared/ui-claude-panel.js +369 -229
  319. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  320. package/neural-interface/public/shared/ui-detail.js +15 -0
  321. package/neural-interface/public/shared/ui-explorer.js +22 -0
  322. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  323. package/neural-interface/public/shared/ui-file-links.js +103 -0
  324. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  325. package/neural-interface/public/shared/ui-help.js +26 -0
  326. package/neural-interface/public/shared/ui-invite.js +126 -116
  327. package/neural-interface/public/shared/ui-judgments.js +10 -1
  328. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  329. package/neural-interface/public/shared/ui-loading.js +187 -19
  330. package/neural-interface/public/shared/ui-menubar.js +25 -33
  331. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  332. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  333. package/neural-interface/public/shared/ui-navbar.js +48 -22
  334. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  335. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  336. package/neural-interface/public/shared/ui-sessions.js +9 -3
  337. package/neural-interface/public/shared/ui-settings.js +509 -490
  338. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  339. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  340. package/neural-interface/public/shared/ui-sync.js +22 -0
  341. package/neural-interface/public/shared/ui-terminal.js +40 -5
  342. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  343. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  344. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  345. package/neural-interface/public/shared/ui-update.js +594 -5
  346. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  347. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  348. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  349. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  350. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  351. package/neural-interface/public/shared/worker-end.js +253 -0
  352. package/neural-interface/public/sw.js +2 -2
  353. package/neural-interface/public/variant/2d/main.js +10 -1
  354. package/neural-interface/public/variant/3d/main.js +10 -1
  355. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  356. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  357. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  358. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  359. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  360. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  361. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  362. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  363. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  364. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  365. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  366. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  367. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  368. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  369. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  370. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  371. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  372. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  373. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  374. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  375. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  376. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  377. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  378. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  379. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  380. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  381. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  382. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  383. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  384. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  385. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  386. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  387. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  388. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  389. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  390. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  391. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  392. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  393. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  394. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  395. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  396. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  397. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  398. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  399. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  400. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  401. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  402. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  403. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  404. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  405. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  406. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  407. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  408. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  409. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  410. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  411. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  412. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  413. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  414. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  415. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  416. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  417. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  418. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  419. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  420. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  421. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  422. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  423. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  424. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  425. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  426. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  427. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  428. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  429. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  430. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  431. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  432. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  433. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  434. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  435. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  436. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  437. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  438. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  439. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  440. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  441. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  442. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  443. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  444. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  445. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  446. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  447. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  448. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  449. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  450. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  451. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  452. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  453. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  454. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  455. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  456. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  457. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  458. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  459. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  460. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  461. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  462. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  463. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  464. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  465. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  466. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  467. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  468. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  469. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  470. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  471. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  472. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  473. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  474. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  475. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  476. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  477. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  478. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  479. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  480. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  481. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  482. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  483. package/neural-interface/scripts/build-vendor.mjs +78 -2
  484. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  485. package/neural-interface/server.js +2085 -1629
  486. package/neural-interface/templates/rulesets/README.md +1 -1
  487. package/package.json +5 -8
  488. package/setup.js +53 -15
  489. package/benchmarks/browser-live-results.json +0 -79
  490. package/benchmarks/browser-v2-results.json +0 -258
  491. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  492. package/neural-interface/public/category-logos/claude.svg +0 -1
  493. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  494. package/neural-interface/public/category-logos/openai.svg +0 -1
  495. package/neural-interface/public/category-logos/synabun.png +0 -0
  496. package/neural-interface/public/claude.svg +0 -1
  497. package/neural-interface/public/gallery.svg +0 -6
  498. package/neural-interface/public/leonardoai.svg +0 -1
  499. package/neural-interface/public/mcp.svg +0 -4
  500. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  501. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  502. package/neural-interface/public/sessionicon.svg +0 -8
  503. package/neural-interface/public/toolbar-album.svg +0 -8
  504. package/neural-interface/public/toolbar-question.svg +0 -6
  505. package/neural-interface/public/toolbar-sessions.svg +0 -6
  506. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  507. package/skills/synabun/synabunicon.png +0 -0
@@ -20,6 +20,7 @@
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;
@@ -226,8 +228,9 @@ export function updateRunCard(card, run, opts = {}) {
226
228
  const r = run.route;
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'));
231
+ else if (r.decidedBy === 'budget' || r.source === 'budget') routeBits.push(t('assistant.routes.budgetDecision', 'budget re-route'));
229
232
  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'));
233
+ else if (r.remembered || r.decidedBy === 'remembered') routeBits.push(t('assistant.routes.remembered', 'your saved choice'));
231
234
  else if (Number.isFinite(Number(r.confidence))) routeBits.push(`${Math.round(Number(r.confidence) <= 1 ? Number(r.confidence) * 100 : Number(r.confidence))}%`);
232
235
  const tip = r.target ? `${t('assistant.routes.routedTo', 'Routed to')} ${routeTargetLabel(r.target, { effort: true })}` : t('assistant.routes.title', 'Model routing');
233
236
  bits.push(`<span class="asst-run-route" data-tooltip="${esc(tip)}">⇄ ${esc(routeBits.join(', '))}</span>`);
@@ -434,12 +437,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
434
437
  const scopes = new Map(); // parent_tool_use_id → { container, stream, key }
435
438
  const runCards = new Map(); // runId → card element
436
439
  const routeLines = new Map(); // routeId → line handle
440
+ const autoRows = new Map(); // traceId → the turn's automation trace row
437
441
  let working = null;
438
442
  let workingFace = null; // the "Thinking" row's character: { still, rig?, el, target? } (syncWorkingFace)
439
443
  let followTail = true;
440
444
  let emptyEl = null;
441
445
  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
446
  const mcpWarned = new Set();
444
447
  let lastErrorText = ''; // a synthetic API-error message's text, so the result does not repeat it
445
448
  let turnFrames = []; // computer cards with inline thumbnails in the current turn
@@ -539,10 +542,19 @@ export function createRenderer(transcriptEl, hooks = {}) {
539
542
  function hideEmpty({ park = false } = {}) {
540
543
  if (!emptyEl) return;
541
544
  if (!(park && parkHero())) dropHero();
545
+ closeEmptyMenu();
542
546
  emptyEl.remove();
543
547
  emptyEl = null;
544
548
  }
545
549
 
550
+ /** A menu opened from the empty state's project picker does not outlive it. */
551
+ function closeEmptyMenu() {
552
+ for (const btn of emptyEl?.querySelectorAll('.asst-empty-project-btn') || []) {
553
+ if (isMenuOpen(btn)) closeMenu();
554
+ }
555
+ hooks.unmountEmptyControls?.();
556
+ }
557
+
546
558
  // ── The hero ──
547
559
  const cssEase = (name, fallback) => {
548
560
  try { return getComputedStyle($msgs).getPropertyValue(name).trim() || fallback; } catch { return fallback; }
@@ -698,13 +710,6 @@ export function createRenderer(transcriptEl, hooks = {}) {
698
710
  try { hooks.onMascotChange?.(); } catch { /* the director is gone */ }
699
711
  }
700
712
 
701
- /** Whether this panel's rigs animate (another panel may lead); still frames otherwise. */
702
- function setRigsEnabled(on) {
703
- if (rigsOn === !!on) return;
704
- rigsOn = !!on;
705
- activateRigs();
706
- }
707
-
708
713
  function prune() {
709
714
  const rows = $msgs.children;
710
715
  while (rows.length > MAX_MESSAGES && rows[0] !== working) rows[0].remove();
@@ -757,8 +762,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
757
762
  const a = el('a', 'file-link', match[0]);
758
763
  a.href = '#';
759
764
  a.title = t('assistant.copyPath', 'Click to copy path');
760
- const path = match[0].replace(/:\d+$/, '');
761
- a.addEventListener('click', (e) => { e.preventDefault(); hooks.copy?.(path); a.style.opacity = '0.5'; setTimeout(() => { a.style.opacity = ''; }, 300); });
765
+ // The click is taken on the transcript (asst-panel.js → ui-file-links.js): the code editor when it
766
+ // can open the path, else the copy a click has always made.
762
767
  frag.appendChild(a);
763
768
  last = match.index + match[0].length;
764
769
  }
@@ -1502,7 +1507,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
1502
1507
  rack.clearTimer = 0;
1503
1508
  rack.row.classList.add('is-staged');
1504
1509
  if (!rack.rig) {
1505
- try { rack.rig = mascotLib.createMascot(rack.stage.face, { width: 72, height: 36, rangeX: 10, rangeY: 7, active: rigsOn, pose: 'idle' }); } catch { rack.rig = null; }
1510
+ try { rack.rig = mascotLib.createMascot(rack.stage.face, { width: 72, height: 36, rangeX: 10, rangeY: 7, pose: 'idle' }); } catch { rack.rig = null; }
1506
1511
  rack.pose = null;
1507
1512
  rack.target = rack.rig ? stageTarget(rack) : null;
1508
1513
  if (hero?.parked) dropHero(); // the stage is the character now: a hero still on its way out goes at once
@@ -1564,12 +1569,16 @@ export function createRenderer(transcriptEl, hooks = {}) {
1564
1569
  rack.bubble = { step: null, since: 0, key: '', open: false };
1565
1570
  }
1566
1571
 
1567
- /** One rig animates: the hero, else the newest stage, else the "Thinking" row's; none while another panel leads (setRigsEnabled). */
1572
+ /**
1573
+ * One rig of this transcript animates: the hero, else the newest stage, else
1574
+ * the "Thinking" row's. The rule ends at this renderer: every other panel
1575
+ * runs its own, so N live sessions show N moving characters.
1576
+ */
1568
1577
  function activateRigs() {
1569
1578
  const newest = hero?.rig ? null : newestStage();
1570
- for (const rack of racks) if (rack.rig) rack.rig.setActive?.(rigsOn && rack === newest);
1571
- hero?.rig?.setActive?.(rigsOn);
1572
- workingFace?.rig?.setActive?.(rigsOn && !hero?.rig && !newest);
1579
+ for (const rack of racks) if (rack.rig) rack.rig.setActive?.(rack === newest);
1580
+ hero?.rig?.setActive?.(true);
1581
+ workingFace?.rig?.setActive?.(!hero?.rig && !newest);
1573
1582
  }
1574
1583
 
1575
1584
  /** The bubble's call: the awake one, else the newest; it holds 900 ms (3 s for thinking) before the next wins. */
@@ -2634,7 +2643,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
2634
2643
  return;
2635
2644
  }
2636
2645
  try {
2637
- const rig = mascotLib.createMascot(host, { width: 56, height: 28, active: rigsOn, pose: 'think' });
2646
+ const rig = mascotLib.createMascot(host, { width: 56, height: 28, pose: 'think' });
2638
2647
  const face = { still: false, rig, el: rig.el, held: false };
2639
2648
  face.target = { kind: 'working', rig, el: rig.el, hold(on) { face.held = !!on; }, restore() { if (workingFace === face) rig.setPose('think'); } };
2640
2649
  workingFace = face;
@@ -2683,7 +2692,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
2683
2692
 
2684
2693
  function upsertRunCard(run, { result = null } = {}) {
2685
2694
  if (!run?.runId) return null;
2686
- runInfo.set(run.runId, { ...runInfo.get(run.runId), title: run.title || run.task || runInfo.get(run.runId)?.title, state: run.status || runInfo.get(run.runId)?.state, provider: run.provider });
2695
+ const prev = runInfo.get(run.runId);
2696
+ const status = run.status || prev?.state;
2697
+ const tone = status ? runTone({ ...run, status }) : '';
2698
+ const routeId = run.route?.routeId || prev?.routeId || '';
2699
+ runInfo.set(run.runId, { ...prev, title: run.title || run.task || prev?.title, state: status, provider: run.provider, routeId, working: tone === 'running' || tone === 'queued' });
2700
+ if (routeId) syncRouteLive(routeId);
2687
2701
  const cardOpts = runCardOpts(result);
2688
2702
  let card = runCards.get(run.runId);
2689
2703
  if (card && card.isConnected) {
@@ -2739,21 +2753,42 @@ export function createRenderer(transcriptEl, hooks = {}) {
2739
2753
  return { phase, t: hooks.t, providerIcon: hooks.providerIcon, brainLabel: hooks.brainLabel, onChangeRoutes: hooks.onChangeRoutes };
2740
2754
  }
2741
2755
 
2742
- /** Create or update the line for `route.routeId` in place. */
2756
+ /** Create or update the line for `route.routeId` in place. Only a new line enters (never on replay). */
2743
2757
  function upsertRouteLine(route, phase = 'decided') {
2744
2758
  const routeId = route?.routeId;
2745
2759
  if (!routeId) return null;
2746
2760
  const existing = routeLines.get(routeId);
2747
- if (existing && existing.el.isConnected) { existing.update(route, phase); return existing; }
2761
+ if (existing && existing.el.isConnected) { existing.update(route, phase); syncRouteLive(routeId); return existing; }
2748
2762
  const line = createRouteLine(route, routeLineOpts(phase));
2749
2763
  routeLines.set(routeId, line);
2750
2764
  append(line.el);
2765
+ syncRouteLive(routeId);
2766
+ enterRow(line.el);
2751
2767
  return line;
2752
2768
  }
2753
2769
 
2754
2770
  /** Adopt a line created by a collapsing route card so later events update it. */
2755
2771
  function registerRouteLine(routeId, line) {
2756
- if (routeId && line) routeLines.set(routeId, line);
2772
+ if (!routeId || !line) return;
2773
+ const fresh = routeLines.get(routeId) !== line;
2774
+ routeLines.set(routeId, line);
2775
+ syncRouteLive(routeId);
2776
+ if (fresh && line.el?.isConnected) enterRow(line.el);
2777
+ }
2778
+
2779
+ /** The line is live while a run it dispatched is queued or working (run cards keep runInfo current). */
2780
+ function syncRouteLive(routeId) {
2781
+ const line = routeLines.get(routeId);
2782
+ if (!line?.setRunning) return;
2783
+ let working = false;
2784
+ for (const info of runInfo.values()) if (info.routeId === routeId && info.working) { working = true; break; }
2785
+ line.setRunning(working);
2786
+ }
2787
+
2788
+ /** A new row's entrance: the rack ledger's (4px rise and fade, --asst-dur-fast, --asst-ease-enter); none when calm. */
2789
+ function enterRow(node) {
2790
+ if (calm() || typeof node?.animate !== 'function') return;
2791
+ node.animate([{ opacity: 0, transform: 'translateY(4px)' }, { opacity: 1, transform: 'none' }], { duration: 140, easing: cssEase('--asst-ease-enter', 'cubic-bezier(0,0,.38,.9)') });
2757
2792
  }
2758
2793
 
2759
2794
  function removeRouteLine(routeId) {
@@ -2761,6 +2796,115 @@ export function createRenderer(transcriptEl, hooks = {}) {
2761
2796
  if (line) { line.el.remove(); routeLines.delete(routeId); }
2762
2797
  }
2763
2798
 
2799
+ // ── The automation trace ──
2800
+ // What the harness did for a turn (`synabun.automation`, asst-automation.js):
2801
+ // one disclosure row per turn, upserted by its traceId. It appears where the
2802
+ // turn was sent, moves under the reply when the turn ends and updates in
2803
+ // place when learning finishes. Drawn from the event alone, so the live row
2804
+ // and the replayed one are the same. Opening one keeps the next ones open.
2805
+ let autoUid = 0;
2806
+ const TRACE_OPEN_KEY = 'synabun-asst-trace-open';
2807
+ const traceOpenPref = () => { try { return localStorage.getItem(TRACE_OPEN_KEY) === '1'; } catch { return false; } };
2808
+
2809
+ function buildAutoRow() {
2810
+ const node = el('div', 'asst-route-line asst-auto');
2811
+ const detailId = `asst-auto-detail-${++autoUid}`;
2812
+ node.innerHTML = `
2813
+ <button type="button" class="asst-route-line-head asst-auto-head" aria-expanded="false" aria-controls="${detailId}">
2814
+ <span class="asst-route-glyph asst-icon" aria-hidden="true">${ICON_MEMORY}</span>
2815
+ <span class="asst-route-class asst-auto-title"></span>
2816
+ <span class="asst-route-meta asst-auto-summary"></span>
2817
+ <span class="asst-route-state asst-auto-state"></span>
2818
+ <span class="asst-route-chev" aria-hidden="true">&#x203A;</span>
2819
+ </button>
2820
+ <div class="asst-route-detail asst-auto-detail" id="${detailId}" hidden></div>`;
2821
+ const head = node.querySelector('.asst-auto-head');
2822
+ const detail = node.querySelector('.asst-auto-detail');
2823
+ const setOpen = (open) => {
2824
+ head.setAttribute('aria-expanded', open ? 'true' : 'false');
2825
+ detail.hidden = !open;
2826
+ node.classList.toggle('open', open);
2827
+ };
2828
+ head.addEventListener('click', () => {
2829
+ const open = head.getAttribute('aria-expanded') !== 'true';
2830
+ setOpen(open);
2831
+ try { localStorage.setItem(TRACE_OPEN_KEY, open ? '1' : '0'); } catch { /* no storage: this row only */ }
2832
+ });
2833
+ setOpen(traceOpenPref());
2834
+ return { el: node, detail, atEnd: false };
2835
+ }
2836
+
2837
+ function paintAutoRow(row, ev) {
2838
+ const view = automationView(ev, { t: hooks.t });
2839
+ row.el.dataset.traceId = view.traceId;
2840
+ row.el.dataset.phase = view.phase;
2841
+ row.el.classList.toggle('busy', view.tone === 'busy');
2842
+ row.el.classList.toggle('learned', view.tone === 'learned');
2843
+ row.el.querySelector('.asst-auto-title').textContent = view.title;
2844
+ row.el.querySelector('.asst-auto-summary').textContent = view.summary;
2845
+ const state = row.el.querySelector('.asst-auto-state');
2846
+ state.textContent = view.state;
2847
+ state.hidden = !view.state;
2848
+ row.detail.replaceChildren();
2849
+ for (const group of view.groups) {
2850
+ const box = el('div', 'asst-auto-group');
2851
+ box.dataset.group = group.key;
2852
+ box.appendChild(el('div', 'asst-auto-h', group.title));
2853
+ for (const item of group.rows) {
2854
+ const line = el('div', `asst-auto-row${item.tone ? ` ${item.tone}` : ''}`);
2855
+ if (item.label || item.meta) {
2856
+ const lead = el('div', 'asst-auto-row-head');
2857
+ if (item.label) lead.appendChild(el('span', 'asst-auto-tier', item.label));
2858
+ if (item.meta) lead.appendChild(el('span', 'asst-auto-meta', item.meta));
2859
+ line.appendChild(lead);
2860
+ }
2861
+ // Memory text and the user's own words: always as text, never as markup.
2862
+ if (item.text) line.appendChild(el(item.pre ? 'pre' : 'div', 'asst-auto-text', item.text));
2863
+ if (item.ids.length) {
2864
+ const ids = el('div', 'asst-auto-ids');
2865
+ for (const id of item.ids) {
2866
+ const link = el('button', 'asst-link', String(id).slice(0, 8));
2867
+ link.type = 'button';
2868
+ link.dataset.memoryId = id;
2869
+ link.title = `${t('assistant.automation.open', 'Open memory')} ${id}`;
2870
+ link.setAttribute('aria-label', link.title);
2871
+ link.addEventListener('click', () => hooks.onMemoryClick?.(id, { id }));
2872
+ ids.appendChild(link);
2873
+ }
2874
+ line.appendChild(ids);
2875
+ }
2876
+ box.appendChild(line);
2877
+ }
2878
+ row.detail.appendChild(box);
2879
+ }
2880
+ }
2881
+
2882
+ /** Create or update the trace row for `ev.traceId`. */
2883
+ function upsertAutomation(ev) {
2884
+ const id = String(ev?.traceId || '');
2885
+ if (!id) return null;
2886
+ let row = autoRows.get(id);
2887
+ const fresh = !row || !row.el.isConnected;
2888
+ if (fresh) {
2889
+ row = buildAutoRow();
2890
+ autoRows.set(id, row);
2891
+ while (autoRows.size > 200) autoRows.delete(autoRows.keys().next().value);
2892
+ }
2893
+ paintAutoRow(row, ev);
2894
+ // Under the reply once the turn has ended; a later snapshot (learning) never moves it again.
2895
+ const ended = ev.phase !== 'sent';
2896
+ if (fresh || (ended && !row.atEnd)) {
2897
+ // Placed like any row (before "Working…"), but a status line of the harness never dismisses the hero.
2898
+ if (working && working.parentNode === $msgs) $msgs.insertBefore(row.el, working);
2899
+ else $msgs.appendChild(row.el);
2900
+ if (ended) row.atEnd = true;
2901
+ prune();
2902
+ if (racks.size) syncRacks();
2903
+ scrollEnd();
2904
+ }
2905
+ return row.el;
2906
+ }
2907
+
2764
2908
  // ── Empty state ──
2765
2909
  function showEmpty() {
2766
2910
  if (emptyEl || $msgs.querySelector(':scope > :not(.asst-working)')) return;
@@ -2769,14 +2913,24 @@ export function createRenderer(transcriptEl, hooks = {}) {
2769
2913
  <div class="asst-empty-mascot" aria-hidden="true"></div>
2770
2914
  <div class="asst-empty-title" role="heading" aria-level="2">${esc(t('assistant.empty.heading', 'What should we do?'))}</div>
2771
2915
  <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>
2916
+ <div class="asst-empty-greet"></div>
2917
+ <div class="asst-empty-project" hidden></div>
2772
2918
  <div class="asst-suggestions" role="list"></div>`;
2773
2919
  const box = emptyEl.querySelector('.asst-empty-mascot');
2774
2920
  try {
2775
- const rig = mascotLib.createMascot(box, { width: 120, height: 60, active: rigsOn });
2921
+ const rig = mascotLib.createMascot(box, { width: 120, height: 60 });
2776
2922
  hero = { rig, el: rig.el, box, parked: false, leaving: false, held: false, dock: null, egg: false, leaveTimer: 0 };
2777
2923
  hero.target = heroTarget(hero);
2778
2924
  wireEgg(hero);
2779
2925
  } catch { hero = null; }
2926
+ // The project the conversation works in, under the lead: the host draws the picker (the brain
2927
+ // picker's project field), so it and the toolbar's Working project are one control.
2928
+ const projectSlot = emptyEl.querySelector('.asst-empty-project');
2929
+ let picker = null;
2930
+ try { picker = typeof hooks.mountEmptyProject === 'function' ? hooks.mountEmptyProject(projectSlot) : null; } catch { picker = null; }
2931
+ let budget = null;
2932
+ try { budget = hooks.mountEmptyBudget?.(projectSlot); } catch { /* unavailable */ }
2933
+ projectSlot.hidden = !picker && !budget;
2780
2934
  renderSuggestions();
2781
2935
  $msgs.appendChild(emptyEl);
2782
2936
  // The host's "Starting…" character flies into place (not on replay or reduced motion).
@@ -2786,6 +2940,20 @@ export function createRenderer(transcriptEl, hooks = {}) {
2786
2940
  mascotChanged();
2787
2941
  }
2788
2942
 
2943
+ /**
2944
+ * The greeting SynaBun prepared for this return (public/shared/greeting/) takes the lead's place.
2945
+ * The panel calls it once it knows the conversation is new: a restored one is never greeted.
2946
+ */
2947
+ function offerGreeting() {
2948
+ const owner = emptyEl;
2949
+ if (!owner || owner.dataset.greetingAsked === '1' || typeof hooks.greeting !== 'function') return;
2950
+ owner.dataset.greetingAsked = '1';
2951
+ const slot = owner.querySelector('.asst-empty-greet');
2952
+ Promise.resolve().then(() => hooks.greeting(slot))
2953
+ .then((greeting) => { if (greeting && emptyEl === owner) owner.classList.add('has-greeting'); })
2954
+ .catch(() => { /* no greeting: the lead stays */ });
2955
+ }
2956
+
2789
2957
  function renderSuggestions() {
2790
2958
  if (!emptyEl) return;
2791
2959
  const host = emptyEl.querySelector('.asst-suggestions');
@@ -3111,6 +3279,9 @@ export function createRenderer(transcriptEl, hooks = {}) {
3111
3279
  // The card itself arrives as a control_request; its outcome is a line (the panel's card shows it live).
3112
3280
  if (ev.phase !== 'card') appendStatus(clarifyLine(ev), 'info');
3113
3281
  return true;
3282
+ case 'synabun.automation':
3283
+ upsertAutomation(ev);
3284
+ return true;
3114
3285
  default:
3115
3286
  return false;
3116
3287
  }
@@ -3233,6 +3404,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3233
3404
  scopes.clear();
3234
3405
  runCards.clear();
3235
3406
  routeLines.clear();
3407
+ autoRows.clear();
3236
3408
  turnFrames = [];
3237
3409
  rootScope.stream = null;
3238
3410
  rootScope.kept = null;
@@ -3249,6 +3421,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3249
3421
  seen.clear();
3250
3422
  snapshot = null;
3251
3423
  waiting = null;
3424
+ closeEmptyMenu();
3252
3425
  $msgs.innerHTML = '';
3253
3426
  emptyEl = null;
3254
3427
  followTail = true;
@@ -3273,6 +3446,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3273
3446
  registerRouteLine,
3274
3447
  removeRouteLine,
3275
3448
  getRouteLine: (routeId) => routeLines.get(routeId) || null,
3449
+ upsertAutomation,
3276
3450
  getRunCard: (runId) => runCards.get(runId) || null,
3277
3451
  handleEvent,
3278
3452
  /** The live gate for an event (false: already drawn); registers what it lets through. */
@@ -3289,6 +3463,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
3289
3463
  hideWorking,
3290
3464
  isWorkingShown: () => !!working?.isConnected,
3291
3465
  showEmpty,
3466
+ offerGreeting,
3292
3467
  refreshEmpty: renderSuggestions,
3293
3468
  isEmpty: () => !!emptyEl,
3294
3469
  scrollEnd,
@@ -3296,7 +3471,6 @@ export function createRenderer(transcriptEl, hooks = {}) {
3296
3471
  md,
3297
3472
  /** The character on screen, for the panel's director: { kind, rig, el, hold(on), restore() } or null. */
3298
3473
  mascot: visibleMascot,
3299
- setRigsEnabled,
3300
3474
  /** What this turn did: real calls (not the prompt-time recall), whether the last one failed, a turn error. */
3301
3475
  turnStats: () => ({ calls: turn.calls, lastFailed: turn.last?.state === 'error', errored: turn.errored }),
3302
3476
  /** 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
+ }