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
@@ -8,11 +8,13 @@
8
8
  // the face, plus one prop drawn in the face's own pill geometry. Rigs draw
9
9
  // from the shared ticker (shared/synabun-ticker.js): 30 fps while a pose
10
10
  // loops, 60 fps for blends and reactions, no frames at all while still. One
11
- // pointermove listener and one IntersectionObserver serve every rig, and only
12
- // one rig at a time plays a pose loop (the one posed most recently); the
13
- // others hold their pose's still frame. Under prefers-reduced-motion every
14
- // pose is its still frame and changes are instant. A rig tears itself down
15
- // once its SVG leaves the document.
11
+ // loop, one pointermove listener and one IntersectionObserver serve every rig,
12
+ // and every rig that is active and on screen plays its own pose: N rigs move
13
+ // at once, each on its own clock. No rig waits for another; a host that wants
14
+ // one moving character among several of its own (an Assistant panel's hero,
15
+ // stage and "Thinking" row) says so with setActive(). Under
16
+ // prefers-reduced-motion every pose is its still frame and changes are
17
+ // instant. A rig tears itself down once its SVG leaves the document.
16
18
  //
17
19
  // v1 callers are unchanged: createMascot(container, opts) → { el, destroy,
18
20
  // look, blink }, idle, blinking every 3-7 s and following the cursor.
@@ -35,7 +37,8 @@
35
37
  * `opts.count` 1-3 (subagent minis), `opts.mode` 'recall' | 'remember'
36
38
  * (memory), `opts.ticks` 0-3 (plan), `opts.then` a pose to blend to once a
37
39
  * one-shot (success, error, blocked) has played and held. The same pose with
38
- * a new count or ticks keeps its loop running.
40
+ * a new count or ticks keeps its loop running. `opts.unlimited` selects the
41
+ * budget pose's relaxed infinity variant; project peeks into a folder.
39
42
  * - `react(name)` — `MASCOT_REACTIONS`: hop (spring, at most one per 600 ms),
40
43
  * wince, nod, pop (plan: pops the newest tick; ticks the next box too
41
44
  * unless setPose passed `ticks`), startle, pulse, click
@@ -79,7 +82,7 @@ export const MASCOT_VIEWBOX = '0 0 280 140';
79
82
  /** One pose per tool kind (shared with the activity rack's kind ids). */
80
83
  export const MASCOT_KINDS = Object.freeze(['shell', 'read', 'edit', 'search', 'web', 'subagent', 'plan', 'memory', 'runs', 'computer', 'think', 'mcp']);
81
84
  export const MASCOT_STATES = Object.freeze(['idle', 'sleep', 'watch', 'wait', 'success', 'error', 'blocked', 'offline']);
82
- export const MASCOT_POSES = Object.freeze([...MASCOT_KINDS, ...MASCOT_STATES]);
85
+ export const MASCOT_POSES = Object.freeze([...MASCOT_KINDS, ...MASCOT_STATES, 'project', 'budget']);
83
86
  export const MASCOT_REACTIONS = Object.freeze(['hop', 'wince', 'nod', 'pop', 'startle', 'pulse', 'click']);
84
87
 
85
88
  /** The character's SVG. Colour comes from `currentColor` (the mouth at 25 %). */
@@ -143,6 +146,8 @@ export function mascotCameoSvg({ className = '' } = {}) {
143
146
 
144
147
  /** Each prop's own box in face units, to draw it alone (a ledger row's glyph, a suggestion's). */
145
148
  const PROP_BOX = Object.freeze({
149
+ project: [204, 77, 62, 48],
150
+ budget: [212, 65, 54, 60],
146
151
  shell: [144, 116, 42, 26], // the last keycap and the caret
147
152
  read: [65, 20, 150, 88],
148
153
  edit: [188, 93, 56, 44],
@@ -307,12 +312,37 @@ const MINIS = [
307
312
  ];
308
313
 
309
314
  /**
310
- * motion: 'loop' (30 fps while it leads), 'blink' (still between slow blinks),
315
+ * motion: 'loop' (30 fps), 'blink' (still between slow blinks),
311
316
  * 'once' (plays `settle` ms, then still; `hold` before opts.then), 'still'.
312
317
  * blinks: random blinks every 3-7 s. tracks: follows the cursor.
313
318
  * look: follows look(), easing with this time constant (ms).
314
319
  */
315
320
  const POSES = {
321
+ // Empty-state controls: host-directed gaze, the same ticker and prop painter as tool kinds.
322
+ project: {
323
+ motion: 'loop', blinks: true, look: 70,
324
+ frame(t, o) {
325
+ const f = blankFrame();
326
+ const peek = o.still ? 0.5 : (1 + Math.sin(TAU * t / 2400)) / 2;
327
+ f.eyes = [eye(0, 2 + 2 * peek, 1, 0.92, -2), eye(0, 2 + 2 * peek, 1, 0.96, 2)];
328
+ f.mouth = mouth(34, 11);
329
+ gaze(f, o.look, { x: 16, y: 10 });
330
+ f.prop = { name: 'project', peek };
331
+ return f;
332
+ },
333
+ },
334
+ budget: {
335
+ motion: 'loop', blinks: true, look: 70,
336
+ frame(t, o) {
337
+ const f = blankFrame();
338
+ const lift = o.still ? 0 : Math.sin(TAU * t / 2200);
339
+ f.eyes = [eye(0, 0, 1, o.unlimited ? 0.65 : 0.93, -3), eye(0, 0, 1, o.unlimited ? 0.65 : 1, 3)];
340
+ f.mouth = mouth(o.unlimited ? 48 : 34, o.unlimited ? 20 : 11);
341
+ gaze(f, o.look, { x: 16, y: 10 });
342
+ f.prop = { name: 'budget', lift, unlimited: o.unlimited };
343
+ return f;
344
+ },
345
+ },
316
346
  // ── States ──
317
347
  idle: {
318
348
  motion: 'still', blinks: true, tracks: true,
@@ -596,6 +626,7 @@ function poseOptions(opts) {
596
626
  count: intIn(o.count, 1, 3, 1),
597
627
  mode: o.mode === 'remember' ? 'remember' : 'recall',
598
628
  ticks: intIn(o.ticks, 0, 3, 1),
629
+ unlimited: !!o.unlimited,
599
630
  still: !!o.still,
600
631
  };
601
632
  }
@@ -739,6 +770,23 @@ export function reactionFrame(frame, name, p) {
739
770
  // ── Props: small, currentColor, in the face's pill geometry ──
740
771
 
741
772
  const PROPS = {
773
+ project: {
774
+ markup: () => '<g fill="none" stroke="currentColor" stroke-opacity=".7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M208 117V84q0-4 4-4h15l7 8h25v29z"/><path class="syna-folder-front" d="M208 117l5-23h49l-5 23z"/><path class="syna-folder-paper" d="M223 93V85h22v8M229 89h10"/></g>',
775
+ refs: g => ({ front: g.querySelector('.syna-folder-front'), paper: g.querySelector('.syna-folder-paper') }),
776
+ update(r, p, put) {
777
+ put(r.front, 'transform', `translate(0,${n(2 * p.peek)})`);
778
+ put(r.paper, 'transform', `translate(0,${n(-4 * p.peek)})`);
779
+ },
780
+ },
781
+ budget: {
782
+ markup: () => '<g fill="none" stroke="currentColor" stroke-opacity=".7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><rect x="216" y="96" width="46" height="25" rx="4"/><path d="M250 103h12v10h-12z"/><g class="syna-budget-coin"><circle cx="236" cy="80" r="12"/><path d="M236 73v14M232 76h6q4 0 0 4h-4q-4 4 0 4h6"/></g><path class="syna-budget-infinity" d="M235 80c-16-19-25 15-8 7l17-14c17-8 8 26-9 7"/></g>',
783
+ refs: g => ({ coin: g.querySelector('.syna-budget-coin'), infinity: g.querySelector('.syna-budget-infinity') }),
784
+ update(r, p, put) {
785
+ put(r.coin, 'display', p.unlimited ? 'none' : null);
786
+ put(r.infinity, 'display', p.unlimited ? null : 'none');
787
+ put(p.unlimited ? r.infinity : r.coin, 'transform', `translate(0,${n(-3 * p.lift)})`);
788
+ },
789
+ },
742
790
  shell: { // three keycaps and a block caret under the chin
743
791
  markup: () => [0, 1, 2].map(i => `<rect class="syna-key" x="${100 + i * 24}" y="127" width="20" height="9" rx="4.5" fill="currentColor" fill-opacity=".4"/>`).join('')
744
792
  + '<rect class="syna-caret" x="174" y="125" width="7" height="12" rx="2" fill="currentColor" fill-opacity=".75"/>',
@@ -994,26 +1042,15 @@ export function paintMascot(svg, frame) {
994
1042
  painter.paint(frame);
995
1043
  }
996
1044
 
997
- // ── Shared runtime: one observer, one motion listener, one lead ──
1045
+ // ── Shared runtime: one observer and one motion listener for every rig ──
1046
+ // Nothing here picks a rig: each one decides from its own state (active, on
1047
+ // screen, motion allowed) whether it moves.
998
1048
 
999
1049
  const rigs = new Set();
1000
- let leadRig = null;
1001
- let promoteSeq = 0;
1002
1050
  let stopMotionWatch = null;
1003
1051
  let io = null;
1004
1052
  const ioRigs = new WeakMap();
1005
1053
 
1006
- /** Only one rig plays a pose loop: the most recently posed one that wants to. */
1007
- function elect() {
1008
- let best = null;
1009
- for (const rig of rigs) if (rig.wantsLead() && (!best || rig.promotedAt() > best.promotedAt())) best = rig;
1010
- if (best === leadRig) return;
1011
- const prev = leadRig;
1012
- leadRig = best;
1013
- prev?.leadChanged();
1014
- best?.leadChanged();
1015
- }
1016
-
1017
1054
  function observe(el, rig) {
1018
1055
  if (typeof IntersectionObserver !== 'function') return;
1019
1056
  try {
@@ -1034,13 +1071,11 @@ function register(rig) {
1034
1071
  rigs.add(rig);
1035
1072
  stopMotionWatch ||= onReducedMotionChange(() => {
1036
1073
  for (const r of Array.from(rigs)) r.motionChanged();
1037
- elect();
1038
1074
  });
1039
1075
  }
1040
1076
 
1041
1077
  function unregister(rig) {
1042
1078
  rigs.delete(rig);
1043
- if (leadRig === rig) { leadRig = null; elect(); }
1044
1079
  if (!rigs.size && stopMotionWatch) { stopMotionWatch(); stopMotionWatch = null; }
1045
1080
  }
1046
1081
 
@@ -1088,14 +1123,12 @@ export function createMascot(container, options) {
1088
1123
  let active = startActive !== false;
1089
1124
  let visible = true;
1090
1125
  let seenConnected = svg.isConnected;
1091
- let promotedAt = (promoteSeq += 1);
1092
1126
  let clock = 0; // rig time in ms: the ticker's dt while drawing, real time while still
1093
1127
  let frozenAt = tickerNow(); // while not drawing: when the clock was last brought up to date; -1 while drawing
1094
1128
  let pose = { name: 'idle', raw: {}, start: 0, then: null };
1095
1129
  let settled = false; // a one-shot's opts.then has fired
1096
1130
  let blend = null; // { from, start, dur }
1097
1131
  let lastFrame = null; // the last frame before blinks and reactions
1098
- let drawn = false; // has drawn a live frame (a never-drawn rig has nothing to blend from)
1099
1132
  let look = { x: 0, y: 0 }; // eased gaze
1100
1133
  let track = { x: 0, y: 0 }; // idle: where the cursor (or look()) points
1101
1134
  let hostLook = { x: 0, y: 0 };
@@ -1119,10 +1152,10 @@ export function createMascot(container, options) {
1119
1152
 
1120
1153
  const def = () => POSES[pose.name];
1121
1154
  const animating = () => !destroyed && active && visible && !isReducedMotion();
1122
- const isLead = () => leadRig === rig;
1123
1155
  const poseTime = () => clock - pose.start;
1124
1156
 
1125
- function wantsLead() {
1157
+ /** This pose moves on its own right now: a loop, a patient pose, a one-shot still playing. */
1158
+ function playing() {
1126
1159
  if (!animating()) return false;
1127
1160
  const { motion, settle } = def();
1128
1161
  if (motion === 'loop' || motion === 'blink') return true;
@@ -1186,7 +1219,6 @@ export function createMascot(container, options) {
1186
1219
  function nextRate() {
1187
1220
  if (!animating()) return 0;
1188
1221
  if (blend || blinkStart >= 0 || reactions.length || aim || easing() || miniPopping()) return 60;
1189
- if (!isLead()) return 0;
1190
1222
  const d = def();
1191
1223
  const t = poseTime();
1192
1224
  if (d.motion === 'loop') return 30;
@@ -1208,9 +1240,9 @@ export function createMascot(container, options) {
1208
1240
  const d = def();
1209
1241
  const at0 = tickerNow();
1210
1242
  let at = Infinity;
1211
- if (d.blinks && (d.motion === 'still' || isLead())) at = Math.min(at, nextBlinkAt);
1243
+ if (d.blinks) at = Math.min(at, nextBlinkAt);
1212
1244
  if (d.tracks) at = Math.min(at, nextGlanceAt, nextFidgetAt, glance ? glance.until : Infinity);
1213
- if (d.motion === 'blink' && isLead()) at = Math.min(at, at0 + d.idleFor(poseTime()));
1245
+ if (d.motion === 'blink') at = Math.min(at, at0 + d.idleFor(poseTime()));
1214
1246
  if (d.motion === 'once' && pose.then && !settled) at = Math.min(at, at0 + Math.max(0, d.settle + (d.hold || 0) - poseTime()));
1215
1247
  if (!Number.isFinite(at)) return;
1216
1248
  wakeTimer = setTimeout(() => {
@@ -1243,15 +1275,15 @@ export function createMascot(container, options) {
1243
1275
  function handleDue() {
1244
1276
  const d = def();
1245
1277
  const t = tickerNow();
1246
- if (d.blinks && (d.motion === 'still' || isLead()) && t >= nextBlinkAt) {
1278
+ if (d.blinks && t >= nextBlinkAt) {
1247
1279
  nextBlinkAt = t + rand(3000, 7000);
1248
1280
  startBlink();
1249
1281
  }
1250
1282
  if (d.tracks) {
1251
1283
  if (glance && t >= glance.until) glance = null;
1252
1284
  if (t >= nextGlanceAt) {
1253
- // Only while the cursor is still and no other rig is busy.
1254
- if (t - pointerAt < 2500 || leadRig) nextGlanceAt = t + 3000;
1285
+ // Only while the cursor is still.
1286
+ if (t - pointerAt < 2500) nextGlanceAt = t + 3000;
1255
1287
  else {
1256
1288
  const [x, y] = GLANCES[Math.floor(Math.random() * GLANCES.length)];
1257
1289
  glance = { x, y, until: t + rand(700, 1000) };
@@ -1261,7 +1293,6 @@ export function createMascot(container, options) {
1261
1293
  if (t >= nextFidgetAt) {
1262
1294
  const quietFor = t - Math.max(lastInputAt(), pointerAt);
1263
1295
  if (quietFor < 20000) nextFidgetAt = t + (20000 - quietFor) + rand(0, 5000);
1264
- else if (leadRig) nextFidgetAt = t + 10000;
1265
1296
  else {
1266
1297
  nextFidgetAt = t + rand(45000, 75000);
1267
1298
  startReaction(Math.random() < 0.5 ? 'hop' : 'wiggle');
@@ -1303,8 +1334,7 @@ export function createMascot(container, options) {
1303
1334
 
1304
1335
  /** The pose frame this rig would draw now, before blends and overlays. */
1305
1336
  function frameNow() {
1306
- const loopOn = isLead() && def().motion !== 'still';
1307
- return poseFrame(pose.name, poseTime(), { ...pose.raw, look, range, ticks, still: !loopOn });
1337
+ return poseFrame(pose.name, poseTime(), { ...pose.raw, look, range, ticks, still: !playing() });
1308
1338
  }
1309
1339
 
1310
1340
  /** Ease from what is on screen to what comes next, unless they already match. */
@@ -1400,10 +1430,7 @@ export function createMascot(container, options) {
1400
1430
  handleDue();
1401
1431
  if (aim) { aimAt(aim); aim = null; }
1402
1432
  easeLook(dt);
1403
- const wasLead = isLead();
1404
1433
  painter.paint(compose());
1405
- drawn = true;
1406
- if (wasLead && def().motion === 'once' && poseTime() >= def().settle) elect();
1407
1434
  const rate = nextRate();
1408
1435
  setRate(rate);
1409
1436
  if (!rate) armWake();
@@ -1497,9 +1524,7 @@ export function createMascot(container, options) {
1497
1524
  }
1498
1525
  scheduleIdle();
1499
1526
  }
1500
- promotedAt = (promoteSeq += 1);
1501
1527
  if (animating()) requestFrames(BLEND_MS);
1502
- elect();
1503
1528
  refresh();
1504
1529
  return api;
1505
1530
  }
@@ -1537,8 +1562,7 @@ export function createMascot(container, options) {
1537
1562
  const next = !!on;
1538
1563
  if (destroyed || next === active) return api;
1539
1564
  active = next;
1540
- if (active) { promotedAt = (promoteSeq += 1); revive(); }
1541
- elect();
1565
+ if (active) revive();
1542
1566
  refresh();
1543
1567
  return api;
1544
1568
  }
@@ -1560,18 +1584,6 @@ export function createMascot(container, options) {
1560
1584
  }
1561
1585
 
1562
1586
  const rig = {
1563
- promotedAt: () => promotedAt,
1564
- wantsLead,
1565
- leadChanged() {
1566
- if (destroyed) return;
1567
- // Gaining or losing the loop swaps the still frame for the live one: blend
1568
- // when they differ (a patient pose between blinks, a settled one-shot: no).
1569
- if (drawn && animating()) {
1570
- syncClock();
1571
- blendIn();
1572
- }
1573
- refresh();
1574
- },
1575
1587
  visibilityChanged(inView) {
1576
1588
  if (destroyed) return;
1577
1589
  if (svg.isConnected) seenConnected = true;
@@ -1579,7 +1591,6 @@ export function createMascot(container, options) {
1579
1591
  const was = visible;
1580
1592
  visible = inView;
1581
1593
  if (visible && !was) revive();
1582
- elect();
1583
1594
  refresh(); // also wakes a rig that was built before it was attached
1584
1595
  },
1585
1596
  motionChanged() {
@@ -1619,7 +1630,6 @@ export function createMascot(container, options) {
1619
1630
  if (p) aim = p;
1620
1631
  }
1621
1632
  }
1622
- elect();
1623
1633
  refresh();
1624
1634
  return api;
1625
1635
  }
@@ -0,0 +1,167 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — the public address: what the cards say
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What the two places that start the Cloudflare tunnel show for an answer of
6
+ // GET /api/tunnel/status: the Share menu's Connection block (ui-invite.js) and
7
+ // Settings → Automations → Online access (ui-settings.js). One reading for
8
+ // both, so they never disagree: the state as a word, one sentence about it,
9
+ // the progress of a download, the action that fits, and whether to ask again.
10
+ //
11
+ // No imports and no DOM: `node --test` loads this file as it is
12
+ // (tests/tunnel-view.test.mjs). TUNNEL_COPY is the English of every sentence;
13
+ // `tunnel.*` in i18n/en.json must equal it and i18n/pt-BR.json must have the
14
+ // same keys (the test checks both). The page side is ui-tunnel.js.
15
+ //
16
+ // A server that was started before SynaBun brought its own tunnel program
17
+ // answers without the `cloudflared` part: the cards then work as they did (a
18
+ // tunnel starts when a cloudflared is installed), and where none is, they say
19
+ // that a restart is what is missing. Nothing here ever tells the user to
20
+ // install anything, except on a system Cloudflare publishes no file for.
21
+
22
+ export const TUNNEL_COPY = Object.freeze({
23
+ stateOff: 'Off',
24
+ stateDownloading: 'Getting the tunnel program',
25
+ stateStarting: 'Starting',
26
+ stateRunning: 'On',
27
+ stateFailed: 'Did not start',
28
+ stateUnsupported: 'Not available',
29
+ stateRestart: 'Restart needed',
30
+ idle: 'A public address through Cloudflare, for invite links and remote MCP.',
31
+ idleDownload: 'A public address through Cloudflare, for invite links and remote MCP. The first start downloads the tunnel program ({size} MB).',
32
+ downloading: 'Downloading the tunnel program: {received} of {total} MB',
33
+ starting: 'Waiting for Cloudflare to give an address…',
34
+ restart: 'Restart SynaBun to use the public address: the server that is running was started before it could download the tunnel program.',
35
+ unsupported: 'Cloudflare publishes no tunnel program for this system. If you install cloudflared yourself, SynaBun uses it.',
36
+ errorOffline: 'The tunnel program could not be downloaded: the download server was not reached. Check the connection and try again.',
37
+ errorHttp: 'The tunnel program could not be downloaded: the download server answered with an error. Try again in a moment.',
38
+ errorInterrupted: 'The download stopped before it was complete. Nothing was kept. Try again.',
39
+ errorTimeout: 'The download stalled. Nothing was kept. Try again.',
40
+ errorChecksum: 'The download was not the file SynaBun expects, so it was removed and never run. Try again; if it happens again, something on the network is changing the file.',
41
+ errorUnpack: 'The downloaded file could not be unpacked, so it was removed. Try again.',
42
+ errorDisk: 'The tunnel program could not be saved in SynaBun\'s data folder. Check free space and permissions, then try again.',
43
+ errorLaunch: 'The tunnel program could not be started on this computer. Try again.',
44
+ errorExited: 'Cloudflare gave no address. The tunnel program said: {reason}',
45
+ errorExitedNoReason: 'The tunnel program stopped before Cloudflare gave an address. Try again.',
46
+ errorNoAddress: 'Cloudflare gave no address in time. Try again.',
47
+ errorEnded: 'The tunnel stopped. Start it again for a new address.',
48
+ errorRefused: 'SynaBun refused this request. Start the tunnel from SynaBun on this computer.',
49
+ errorUnknown: 'The tunnel did not start: {reason}',
50
+ errorUnknownNoReason: 'The tunnel did not start. Try again.',
51
+ start: 'Start tunnel',
52
+ stop: 'Stop',
53
+ cancel: 'Cancel',
54
+ retry: 'Try again',
55
+ copyBundled: 'cloudflared {version}, included with SynaBun',
56
+ copyDownloaded: 'cloudflared {version}, downloaded by SynaBun',
57
+ copySystem: 'cloudflared {version}, installed on this computer',
58
+ copySystemWhy: 'SynaBun could not download its own copy, so the one installed on this computer is used.',
59
+ mcpKeyNeeded: 'Remote MCP needs an access key. Create one below; the address to connect is then this one followed by /mcp/ and the key.',
60
+ });
61
+
62
+ const ERROR_COPY = Object.freeze({
63
+ offline: 'errorOffline', http: 'errorHttp', interrupted: 'errorInterrupted', timeout: 'errorTimeout', checksum: 'errorChecksum',
64
+ unpack: 'errorUnpack', disk: 'errorDisk', launch: 'errorLaunch', no_address: 'errorNoAddress', ended: 'errorEnded',
65
+ unsupported: 'unsupported', refused: 'errorRefused',
66
+ });
67
+
68
+ /** A TUNNEL_COPY sentence with its `{params}` filled in: the translator when the page gives none. */
69
+ export function fillTunnelCopy(key, params = {}) {
70
+ return String(TUNNEL_COPY[key] ?? key).replace(/\{(\w+)\}/g, (whole, name) => (params[name] != null ? params[name] : whole));
71
+ }
72
+
73
+ const megabytes = (bytes) => Math.max(0, Number(bytes) || 0) / (1024 * 1024);
74
+
75
+ /**
76
+ * What a card shows for a status.
77
+ *
78
+ * @param {object | null} status the answer of GET /api/tunnel/status (or of the start / stop that came last)
79
+ * @param {object} [o]
80
+ * @param {(key: string, params?: object) => string} [o.t] a key of TUNNEL_COPY → the sentence in the page's language
81
+ * @param {(value: number, digits: number) => string} [o.number] a number as the page's language writes it
82
+ * @returns {{
83
+ * kind: 'restart' | 'unsupported' | 'idle' | 'downloading' | 'starting' | 'running' | 'failed',
84
+ * tone: 'off' | 'busy' | 'ok' | 'warn' | 'err',
85
+ * on: boolean,
86
+ * label: string, detail: string,
87
+ * progress: { percent: number, text: string } | null,
88
+ * url: string | null,
89
+ * action: 'start' | 'stop' | 'cancel' | 'retry' | null, actionLabel: string,
90
+ * copy: string, copyNote: string,
91
+ * poll: boolean,
92
+ * }}
93
+ * `on`: the switch stands on (something runs or is on its way). `action`: what a press does ("cancel" and
94
+ * "stop" are both POST /api/tunnel/stop; "start" and "retry" both POST /api/tunnel/start). `copy`: which
95
+ * cloudflared runs, when one does. `poll`: the state will change by itself, ask again.
96
+ */
97
+ export function tunnelView(status, { t = fillTunnelCopy, number = (value, digits) => value.toFixed(digits) } = {}) {
98
+ const s = status && typeof status === 'object' ? status : {};
99
+ const tool = s.cloudflared && typeof s.cloudflared === 'object' ? s.cloudflared : null;
100
+ const view = {
101
+ kind: 'idle', tone: 'off', on: false, label: t('stateOff'), detail: t('idle'), progress: null,
102
+ url: typeof s.url === 'string' && s.url ? s.url : null,
103
+ action: 'start', actionLabel: t('start'), copy: '', copyNote: '', poll: false,
104
+ };
105
+ const as = (kind, tone, labelKey, detail, action) => Object.assign(view, {
106
+ kind, tone, label: t(labelKey), detail, action, actionLabel: action ? t(action) : '',
107
+ on: kind === 'downloading' || kind === 'starting' || kind === 'running', poll: kind === 'downloading' || kind === 'starting',
108
+ });
109
+ const running = () => {
110
+ as('running', 'ok', 'stateRunning', view.url, 'stop');
111
+ const source = { bundled: 'copyBundled', downloaded: 'copyDownloaded', system: 'copySystem' }[tool?.source];
112
+ if (source && tool.version) {
113
+ view.copy = t(source, { version: tool.version });
114
+ if (tool.source === 'system' && tool.fallback && tool.fallback.code !== 'unsupported') view.copyNote = t('copySystemWhy');
115
+ }
116
+ return view;
117
+ };
118
+
119
+ // Nothing is known yet (the answer has not arrived): the card as it stands before a first start.
120
+ if (!status || typeof status !== 'object') return view;
121
+
122
+ // A server from before the tunnel program came with SynaBun: no `cloudflared` part in its answer.
123
+ if (!tool) {
124
+ if (view.url) return running();
125
+ if (s.starting || s.running) return as('starting', 'busy', 'stateStarting', t('starting'), 'cancel');
126
+ if (s.available) return as('idle', 'off', 'stateOff', t('idle'), 'start');
127
+ return as('restart', 'warn', 'stateRestart', t('restart'), null);
128
+ }
129
+
130
+ const state = typeof s.state === 'string' ? s.state : 'idle';
131
+ if (state === 'running' && view.url) return running();
132
+ if (state === 'downloading') {
133
+ const total = Number(tool.progress?.total) || Number(tool.download?.size) || 0;
134
+ const received = Math.min(Number(tool.progress?.received) || 0, total || Infinity);
135
+ const percent = total ? Math.max(0, Math.min(100, Math.floor((received / total) * 100))) : 0;
136
+ as('downloading', 'busy', 'stateDownloading', t('downloading', { received: number(megabytes(received), 1), total: number(megabytes(total), 1) }), 'cancel');
137
+ view.progress = { percent, text: `${percent}%` };
138
+ return view;
139
+ }
140
+ if (state === 'starting' || state === 'running') return as('starting', 'busy', 'stateStarting', t('starting'), 'cancel');
141
+ if (state === 'failed') {
142
+ const error = s.error && typeof s.error === 'object' ? s.error : {};
143
+ if (error.code === 'unsupported') return as('unsupported', 'off', 'stateUnsupported', t('unsupported'), null);
144
+ const reason = typeof error.reason === 'string' && error.reason.trim() ? error.reason.trim() : '';
145
+ let detail;
146
+ if (error.code === 'exited') detail = reason ? t('errorExited', { reason }) : t('errorExitedNoReason');
147
+ else if (ERROR_COPY[error.code]) detail = t(ERROR_COPY[error.code]);
148
+ else detail = typeof error.message === 'string' && error.message.trim() ? t('errorUnknown', { reason: error.message.trim() }) : t('errorUnknownNoReason');
149
+ return as('failed', 'err', 'stateFailed', detail, 'retry');
150
+ }
151
+ // idle
152
+ if (tool.state === 'unsupported') return as('unsupported', 'off', 'stateUnsupported', t('unsupported'), null);
153
+ if (tool.state === 'ready') return as('idle', 'off', 'stateOff', t('idle'), 'start');
154
+ const size = Number(tool.download?.size) || 0;
155
+ return as('idle', 'off', 'stateOff', size ? t('idleDownload', { size: number(megabytes(size), 0) }) : t('idle'), 'start');
156
+ }
157
+
158
+ /**
159
+ * The address an assistant outside connects to, or null while there is none to give: the public address
160
+ * followed by /mcp/ and the access key. Without a key there is no such address: the server refuses /mcp from
161
+ * outside (lib/request-gate.js mcpWithoutKey), so the cards offer nothing to copy and say what is missing.
162
+ */
163
+ export function remoteMcpAddress(url, key) {
164
+ const base = typeof url === 'string' ? url.trim().replace(/\/+$/, '') : '';
165
+ const secret = typeof key === 'string' ? key.trim() : '';
166
+ return base && secret ? `${base}/mcp/${secret}` : null;
167
+ }