synabun 2.0.10 → 2.1.0

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 (517) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +228 -0
  24. package/lib/virustotal.mjs +192 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -1,3 +1,4 @@
1
+ import { fetchToolCatalog } from '../agent-runtime-options.js';
1
2
  // ═══════════════════════════════════════════
2
3
  // SynaBun Assistant — panel (mount / lifecycle)
3
4
  // ═══════════════════════════════════════════
@@ -40,6 +41,7 @@ import { getWhiteboardElementById } from '../ui-whiteboard.js';
40
41
  import { focusNativeLoopRun } from '../ui-native-loop-router.js';
41
42
  import { wireFileLinks } from '../ui-file-links.js';
42
43
  import { injectAssistantStyles } from './asst-styles.js';
44
+ import { tipsSeen, tourOffered } from '../tour/tour-state.js';
43
45
  import { createAssistantSocket } from './asst-ws.js';
44
46
  import { createRenderer, ICON_ASSISTANT, mdInto } from './asst-render.js';
45
47
  import { createMascotDirector, MASCOT_TIMING, turnEndPose } from './asst-mascot.js';
@@ -47,6 +49,8 @@ import { lastInputAt, onKey, onPointer } from '../synabun-ticker.js';
47
49
  import { buildControlResponse, closedControlState, normalizeControlRequest, renderControlCard } from './asst-control.js';
48
50
  import { buildPromptText, createComposer } from './asst-composer.js';
49
51
  import { buildDispatchSpec, parseSlashCommand, slashHelpMarkdown } from './asst-slash.js';
52
+ import { resolveStopTarget } from './asst-stop.js';
53
+ import { plural } from './asst-tool-kinds.js';
50
54
  import { cachedProjects, createBrainPicker, testDensityLabel } from './asst-brain-picker.js';
51
55
  import { createRovingToolbar } from './asst-roving.js';
52
56
  import { openAccountsManager } from './asst-accounts.js';
@@ -243,6 +247,23 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
243
247
  const providerIcon = (p) => getProviderMeta(p).icon;
244
248
  const providerColor = (p) => getProviderMeta(p).color;
245
249
  const visible = () => { try { return host.isVisible ? !!host.isVisible() : true; } catch { return true; } };
250
+
251
+ // ── Just-in-time tips (../tour/tour-tips.js): once each, ever; never in a replay or while the tour runs ──
252
+ function tip(id, targetOf) {
253
+ if (destroyed || replaying || !visible() || guestBlocked() || tipsSeen(storage).has(id)) return;
254
+ requestAnimationFrame(() => {
255
+ if (destroyed) return;
256
+ let target = null;
257
+ try { target = targetOf(); } catch { target = null; }
258
+ if (!target?.isConnected) return;
259
+ import('../tour/tour-tips.js')
260
+ .then(async ({ ensureTourStyles, showTip }) => {
261
+ await ensureTourStyles();
262
+ if (!destroyed && target.isConnected) showTip(id, { storage, tr: (key) => t(key), root, target, renderer, reduced: () => matchMedia('(prefers-reduced-motion: reduce)').matches });
263
+ })
264
+ .catch(() => { /* a tip is never worth an error */ });
265
+ });
266
+ }
246
267
  const hostId = host.id || null; // routing only (assistant:new / resume / focused) — never a feature switch
247
268
 
248
269
  function persist() {
@@ -506,7 +527,18 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
506
527
  emptyBudgetChip?.destroy();
507
528
  emptyBudgetChip = null;
508
529
  },
509
- onSuggestion: (template) => composer.fillTemplate(template),
530
+ // The first-visit row asks for the Workspace tour on this panel (ui-tutorial.js); the others fill the composer.
531
+ onSuggestion: (template, s) => {
532
+ if (s?.action === 'tour') { emit('tour:open', { host: controller.tourHost(), from: 'starter' }); return; }
533
+ composer.fillTemplate(template);
534
+ },
535
+ // A follow-up under a finished message, or Retry now on a usage-limit card: sent as the user's next
536
+ // message (queued while a turn runs, like a typed one). "Something else…" is the composer.
537
+ onFollowup: (text) => { submit(clickPayload(text)); },
538
+ onFollowupOther: () => composer.focus(),
539
+ onLimitRetry: (text) => { submit(clickPayload(text)); },
540
+ limitInfo: () => st.limit,
541
+ brainProvider: () => st.brain.provider,
510
542
  heroOrigin: () => { try { return host.takeHeroOrigin?.() || null; } catch { return null; } },
511
543
  onMascotChange: () => director?.retarget(),
512
544
  onInit: (ev) => {
@@ -536,12 +568,20 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
536
568
  },
537
569
  });
538
570
 
571
+ /** What a click sends: the text alone, shaped like a typed message with nothing attached. */
572
+ function clickPayload(text) {
573
+ return { text: String(text ?? ''), images: [], files: [], uploads: [], memories: [] };
574
+ }
575
+
539
576
  function emptySuggestions() {
540
577
  const project = projectLabel(st.brain.project, cachedProjects()) || tt('assistant.empty.thisProject', 'this project');
541
578
  const list = [];
579
+ // Until the Workspace tour is completed or skipped, its starter comes first. It never opens by itself.
580
+ if (tourOffered(storage) && !guestBlocked()) list.push({ id: 'tour', action: 'tour', kind: 'ask', label: tt('tour.starter', 'Show me the workspace'), meta: tt('tour.starterTime', '2 min') });
542
581
  if (computerAvailable()) list.push({ id: 'computer', label: tt('assistant.empty.suggestComputer', 'Use my Mac to…'), template: tt('assistant.empty.templateComputer', 'Use my Mac to {caret}') });
543
582
  list.push({ id: 'web', label: tt('assistant.empty.suggestWeb', 'Look up … on the web'), template: tt('assistant.empty.templateWeb', 'Look up {caret} on the web') });
544
583
  list.push({ id: 'fix', label: tt('assistant.empty.suggestFix', 'Fix … in {project}', { project }), template: tt('assistant.empty.templateFix', 'Fix {caret} in {project}', { project }) });
584
+ list.push({ id: 'skill', label: tt('assistant.empty.suggestSkill', 'Create Skill'), template: tt('assistant.empty.templateSkill', 'Create a skill that {caret}') });
545
585
  list.push({ id: 'schedule', label: tt('assistant.empty.suggestSchedule', 'Schedule …'), template: tt('assistant.empty.templateSchedule', 'Schedule {caret}') });
546
586
  list.push({ id: 'remember', label: tt('assistant.empty.suggestRemember', 'Remember …'), template: tt('assistant.empty.templateRemember', 'Remember {caret}') });
547
587
  return list;
@@ -553,7 +593,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
553
593
  describeSlash: (cmd) => tt(cmd.descKey, cmd.desc),
554
594
  resolveMemory: (id) => appState.allNodes?.find(n => n.id === id) || null,
555
595
  resolveWhiteboardImage: (id) => { const wb = getWhiteboardElementById(id); return wb?.type === 'image' && wb.dataUrl ? { dataUrl: wb.dataUrl } : null; },
556
- onDraftChange: (text) => { st.draft = text; persist(); director?.type(!!String(text || '').trim()); },
596
+ onDraftChange: (text) => { st.draft = text; persist(); director?.type(!!String(text || '').trim()); renderer.setDrafting?.(!!String(text || '').trim()); },
557
597
  onFocusChange: (focused) => { if (focused) director?.touch(); else director?.stopTyping(); },
558
598
  onAbort: () => abort(),
559
599
  onToast: toast,
@@ -591,6 +631,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
591
631
  const accountHooks = () => ({ t, onChanged: () => picker.refresh(true), onSelect: (id) => applyBrain({ ...st.brain, accountId: id }, { requiresSwitch: true, field: 'account' }) });
592
632
  // Model · effort · permission mode · account sit in the composer row next to attach; project and MCP in the toolbar.
593
633
  const picker = createBrainPicker({ brain: composer.el.querySelector('.asst-composer-brain'), context: barGroups.context }, {
634
+ getSessionId: () => st.sessionId,
594
635
  t,
595
636
  mascot: () => director,
596
637
  getBrain: () => st.brain,
@@ -677,6 +718,17 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
677
718
  agentsBtn.setAttribute('aria-pressed', st.dockOpen ? 'true' : 'false');
678
719
 
679
720
  const usage = createUsageGauge(root.querySelector('.asst-usage-host'), { t, sessionId });
721
+ // The first usage strip on screen gets its tip (once, ever).
722
+ const usageHostEl = root.querySelector('.asst-usage-host');
723
+ if (typeof MutationObserver === 'function' && !tipsSeen(storage).has('usage')) {
724
+ const usageTipWatch = new MutationObserver(() => {
725
+ if (usageHostEl.hidden) return;
726
+ usageTipWatch.disconnect();
727
+ tip('usage', () => usageHostEl.querySelector('.asst-usage-strip') || usageHostEl);
728
+ });
729
+ usageTipWatch.observe(usageHostEl, { attributes: true, attributeFilter: ['hidden'] });
730
+ unsubs.push(() => usageTipWatch.disconnect());
731
+ }
680
732
  if (st.usageView) { usage.apply({ type: 'assistant:usage', ...st.usageView }); usage.setConnection('reconnecting'); }
681
733
  let usageFetchSeq = 0;
682
734
  let usageSocketOpened = false;
@@ -721,6 +773,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
721
773
  clearTimeout(limitTimer);
722
774
  limitTimer = null;
723
775
  const limit = st.limit;
776
+ renderer.noteLimit(limit); // the waiting card of this turn's limit error counts to the reported reset
724
777
  limitEl.hidden = !limit;
725
778
  limitEl.dataset.status = limit?.status || '';
726
779
  const text = limitNoticeText(limit, tt);
@@ -870,7 +923,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
870
923
  return;
871
924
  case 'assistant:dispatch': {
872
925
  const run = runFromPayload(packet);
873
- if (run) { dock.upsertRun(run); renderer.upsertRunCard(dock.getRun(run.runId) || run); }
926
+ if (run) { dock.upsertRun(run); renderer.upsertRunCard(dock.getRun(run.runId) || run); tip('run', () => renderer.getRunCard(run.runId)); }
874
927
  return;
875
928
  }
876
929
  case 'assistant:permission-request':
@@ -914,7 +967,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
914
967
  switch (ev.type) {
915
968
  case 'synabun.dispatch': {
916
969
  const run = runFromPayload(ev);
917
- if (run) { dock.upsertRun(run); renderer.upsertRunCard(dock.getRun(run.runId) || run); }
970
+ if (run) { dock.upsertRun(run); renderer.upsertRunCard(dock.getRun(run.runId) || run); tip('run', () => renderer.getRunCard(run.runId)); }
918
971
  return;
919
972
  }
920
973
  case 'synabun.dispatch_result': {
@@ -972,7 +1025,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
972
1025
  const next = normalizeBrain({ ...st.brain, ...meta.brain, provider: meta.brain.provider || meta.brain.brain || st.brain.provider });
973
1026
  if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
974
1027
  } else if (typeof meta.brain === 'string') {
975
- const next = normalizeBrain({ ...st.brain, provider: meta.brain, model: meta.model ?? st.brain.model, effort: meta.effort ?? st.brain.effort, accountId: meta.accountId ?? st.brain.accountId, mcpProfile: meta.mcpProfile ?? st.brain.mcpProfile, project: meta.cwd ?? st.brain.project });
1028
+ const next = normalizeBrain({ ...st.brain, provider: meta.brain, model: meta.model ?? st.brain.model, effort: meta.effort ?? st.brain.effort, accountId: meta.accountId ?? st.brain.accountId, tools: meta.tools ?? st.brain.tools, project: meta.cwd ?? st.brain.project });
976
1029
  if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
977
1030
  }
978
1031
  if (meta.budget && typeof meta.budget === 'object') budgetController.receiveSession(meta.budget);
@@ -1156,6 +1209,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1156
1209
  if (st.computerRemote) { explainRemoteComputer(); return; }
1157
1210
  const next = enabled === true;
1158
1211
  const wasActive = st.desktopActive;
1212
+ if (next && !st.computerEffective) tip('computer', () => (isRendered(computerToggle.el) ? computerToggle.el : moreBtn));
1159
1213
  st.computerUse = next;
1160
1214
  st.computerEffective = next;
1161
1215
  persist();
@@ -1181,7 +1235,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1181
1235
  elapsedMs: st.turnStartedAt ? Date.now() - st.turnStartedAt : 0,
1182
1236
  }) : null;
1183
1237
  const answer = wasRunning && !aborted ? renderer.turnAnswer() : '';
1184
- renderer.finishTurn({ holdMs: pose ? MASCOT_TIMING.oneShotMs[pose] : 0 });
1238
+ renderer.finishTurn({ holdMs: pose ? MASCOT_TIMING.oneShotMs[pose] : 0, guess: !aborted && !errored });
1185
1239
  if (wasRunning) director.turnEnded(pose);
1186
1240
  if (answer && !silent) announce(tt('assistant.announce.replied', 'SynaBun replied: {text}', { text: answer }));
1187
1241
  st.running = false;
@@ -1221,6 +1275,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1221
1275
  providerIcon,
1222
1276
  scrollEnd: () => renderer.scrollEnd(),
1223
1277
  onRespond: (n, decision) => respondControl(n, decision),
1278
+ // A question card's Esc, and its answer when focus was in it: back to the message box.
1279
+ focusComposer: () => composer.focus(),
1224
1280
  renderRoute: (_container, n) => mountRouteCard(n),
1225
1281
  });
1226
1282
  if (!entry) return;
@@ -1262,6 +1318,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1262
1318
  renderer.appendNode(card.el);
1263
1319
  routeCards.set(card.routeId, card);
1264
1320
  routeChip.render();
1321
+ tip('route', () => card.el);
1265
1322
  requestAnimationFrame(() => { if (!destroyed && !card.collapsed) card.focusIfIdle(); });
1266
1323
  return card;
1267
1324
  }
@@ -1430,8 +1487,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1430
1487
  else renames.settled().then(() => { if (!destroyed) sock.send({ type: 'list_sessions' }); }); // a rename on its way is in the list
1431
1488
  return true;
1432
1489
  case 'stop':
1490
+ // /stop all: every run of the session, at once. /stop <target>: the one run it names, or nothing.
1433
1491
  if (parsed.all) await stopAll();
1434
- else if (parsed.target) await stopRun(parsed.target);
1492
+ else if (parsed.target) await stopNamedRun(parsed.target);
1435
1493
  else abort();
1436
1494
  return true;
1437
1495
  case 'agents': {
@@ -1514,7 +1572,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1514
1572
  }
1515
1573
 
1516
1574
  async function dispatch(parsed, payload) {
1517
- const spec = buildDispatchSpec(parsed, st.brain, { assistantSessionId: sessionId });
1575
+ const spec = buildDispatchSpec(parsed, st.brain, { assistantSessionId: sessionId, toolCatalog: await fetchToolCatalog() });
1518
1576
  renderer.appendUser(payload.text);
1519
1577
  try {
1520
1578
  const res = await dispatchAssistantRun(spec);
@@ -1585,7 +1643,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1585
1643
  if (meta.silent) return;
1586
1644
  if (meta.requiresSwitch) {
1587
1645
  const b = st.brain;
1588
- if (!meta.restartBrain) sock.send({ type: 'switch_brain', brain: b.provider, model: b.model || undefined, effort: b.effort || undefined, accountId: b.accountId || undefined, mcpProfile: b.mcpProfile || undefined, cwd: b.project || undefined, agent: b.agent || undefined, permissionMode: b.permissionMode, planMode: b.planMode === true });
1646
+ if (!meta.restartBrain) sock.send({ type: 'switch_brain', brain: b.provider, model: b.model || undefined, effort: b.effort || undefined, accountId: b.accountId || undefined, tools: b.tools ?? undefined, cwd: b.project || undefined, agent: b.agent || undefined, permissionMode: b.permissionMode, planMode: b.planMode === true });
1589
1647
  renderer.appendDivider(`${tt('assistant.status.brain', 'brain')} → ${brainLabel(b)}`);
1590
1648
  renderer.forgetSeen(); // the new brain's ids start over
1591
1649
  } else if (before.permissionMode !== st.brain.permissionMode || !!before.planMode !== !!st.brain.planMode) {
@@ -1638,9 +1696,29 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1638
1696
  }
1639
1697
  }
1640
1698
 
1699
+ /**
1700
+ * /stop <target>: the run of this session it names (its id or the start of it, its title, the words
1701
+ * of its title: asst-stop.js), and only that one. A target that fits none, or more than one, stops nothing.
1702
+ */
1703
+ async function stopNamedRun(target) {
1704
+ const found = resolveStopTarget(target, dock.getRuns());
1705
+ if (found.run && !found.error) return stopRun(found.run.runId);
1706
+ if (found.passthrough) return stopRun(found.passthrough); // a full id the tray has not loaded: the server knows
1707
+ const name = (run) => `${run.title || run.runId}${run.title ? ` (${String(run.runId).slice(0, 8)})` : ''}`;
1708
+ const message = found.error === 'ambiguous'
1709
+ ? tt('assistant.slash.stopAmbiguous', '"{target}" matches {count} agents: {list}. Nothing was stopped; name one of them.', { target, count: found.matches.length, list: found.matches.map(name).join(', ') })
1710
+ : found.error === 'finished'
1711
+ ? tt('assistant.slash.stopFinished', '{name} has already finished.', { name: name(found.run) })
1712
+ : tt('assistant.slash.stopNoMatch', 'No agent of this session matches "{target}". Nothing was stopped; /agents lists them.', { target });
1713
+ renderer.appendStatus(message);
1714
+ }
1715
+
1641
1716
  async function stopAll() {
1642
- try { await killAllAssistantRuns(sessionId); toast(tt('assistant.status.stopAllRequested', 'Stopping all dispatched agents…')); }
1643
- catch (err) { renderer.appendError(`${tt('assistant.status.stopFailed', 'Stop failed')}: ${err?.message || err}`); }
1717
+ try {
1718
+ const res = await killAllAssistantRuns(sessionId);
1719
+ const count = Array.isArray(res?.stopped) ? res.stopped.length : null;
1720
+ toast(count === null ? tt('assistant.status.stopAllRequested', 'Stopping all dispatched agents…') : plural(tt, 'assistant.status.stopAllDone', count, 'Stopped {count} agent.', 'Stopped {count} agents.'));
1721
+ } catch (err) { renderer.appendError(`${tt('assistant.status.stopFailed', 'Stop failed')}: ${err?.message || err}`); }
1644
1722
  }
1645
1723
 
1646
1724
  async function escalateRun(runId, target) {
@@ -1852,6 +1930,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1852
1930
  items.push({ kind: 'info', label: tt('assistant.topbar.cost', 'Session cost'), value: fmtCost(totalCost()) || '$0.00' });
1853
1931
  if (Number(st.dispatchUsd) > 0) items.push({ kind: 'info', label: tt('assistant.topbar.agentsCost', 'Agents'), value: fmtCost(st.dispatchUsd) });
1854
1932
  openMenu(moreBtn, { label: tt('assistant.toolbar.more', 'More options'), items, role: 'menu', placement: 'below', width: 280 });
1933
+ tip('more', () => document.querySelector('.asst-dd-menu'));
1855
1934
  }
1856
1935
 
1857
1936
  // Assistant → Models: switch models off (hidden from routing, dispatch and every model menu).
@@ -2145,6 +2224,22 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
2145
2224
  resume,
2146
2225
  setRouteMode: (mode) => setRouteMode(mode),
2147
2226
  setComputerUse: (enabled) => setComputerUse(enabled),
2227
+ /**
2228
+ * What the Workspace tour presents on (public/shared/tour/): this panel's own board,
2229
+ * its controls and its director, while the empty state shows its character and nothing
2230
+ * runs. null otherwise (the tour then uses a session-free preview).
2231
+ */
2232
+ tourHost() {
2233
+ const hero = renderer.mascot();
2234
+ if (destroyed || st.running || hero?.kind !== 'hero' || hero.parked?.()) return null;
2235
+ return {
2236
+ root, transcript: transcriptEl, announcer, bar: barEl, renderer, director, composer,
2237
+ live: true,
2238
+ computer: computerAvailable(),
2239
+ refreshStarters: () => renderer.refreshEmpty(),
2240
+ focusComposer: () => composer.focus(),
2241
+ };
2242
+ },
2148
2243
  attachMemory: (node) => composer.addMemory(node),
2149
2244
  attachImageDataUrl: (dataUrl, name) => composer.addImageDataUrl(dataUrl, name),
2150
2245
  insertText: (text) => composer.insertText(text),
@@ -8,9 +8,9 @@
8
8
  // mascot stands over its task class's scene (asst-scenes.js; the motion is
9
9
  // asst-scene-styles.js): one column, as wide as the scene.
10
10
  //
11
- // Rules: gold only means live (the awake row's ring and tick, the caret, the
12
- // sweep); a rack a card holds (data-state="waiting") drops its gold: "Waiting
13
- // for you" in the warn tone with ◆, no caret, stopped clocks. Motion answers
11
+ // Rules: gold only means live (the awake row's ring and tick, the bubble's
12
+ // verb, the sweep); a rack a card holds (data-state="waiting") drops its gold:
13
+ // "Waiting for you" in the warn tone with ◆, stopped clocks. Motion answers
14
14
  // events and stays under 300 ms; infinite animations run only on the live
15
15
  // rack, pause while the UI is dragged (body.ui-interacting) and everything is
16
16
  // static under prefers-reduced-motion (the sweep is not drawn at all). No
@@ -117,6 +117,7 @@ export const RACK_CSS = `
117
117
  grid-area: caption;
118
118
  align-self: end;
119
119
  min-width: 0;
120
+ margin-left: 8px; /* flush with the bubble's body (its notch takes the first 8px) */
120
121
  margin-bottom: 6px; /* the gap over the bubble: an empty caption takes no room */
121
122
  overflow: hidden;
122
123
  display: -webkit-box;
@@ -128,95 +129,107 @@ export const RACK_CSS = `
128
129
  }
129
130
  .asst-rack-caption:empty { display: none; }
130
131
 
132
+ /* ── The bubble: one 18px line (verb · subject · clock), 32px tall at one line, the ledger row's height.
133
+ The verb is gold only while the call runs; the mascot (and the sweep) carry the motion, the bubble
134
+ carries the words and, at the end of the line in the quietest tone, the numbers. Opaque fill (the
135
+ card under the panel tint), so the notch can cover the ring where it meets the edge: no seam. */
131
136
  .asst-bubble {
137
+ --rk-bubble-bg: linear-gradient(var(--rk-fill-1), var(--rk-fill-1)), var(--rk-card);
132
138
  grid-area: bubble;
133
139
  position: relative;
134
140
  justify-self: start;
135
141
  align-self: start;
136
- display: grid;
137
- grid-template-columns: auto minmax(0, 1fr);
138
- align-items: baseline;
139
- column-gap: 7px;
140
- row-gap: 1px;
142
+ display: block; /* a block, not a flex column: a flex item's -webkit-box is blockified and loses its line clamp */
141
143
  max-width: calc(100% - 8px);
142
144
  min-width: 0;
143
145
  margin-left: 8px;
144
- padding: 6px 11px 7px;
145
- border-radius: 3px 12px 12px 12px;
146
- background: var(--rk-fill-1);
146
+ padding: 7px 10px;
147
+ border-radius: 3px 10px 10px 10px;
148
+ background: var(--rk-bubble-bg);
147
149
  box-shadow: inset 0 0 0 1px var(--rk-line);
148
150
  }
149
- /* The tail: an 8px notch on the bubble's edge pointing at the mascot (left beside it, up under it). */
151
+ /* The tail: a 7px notch pointing at the mascot (left beside it, up under it), centred on the first line and
152
+ on the mascot's eyes. An 11px square turned 45° and cut to a triangle, 1px into the bubble so it paints
153
+ over the ring: the ring stops at the notch's legs, no double edge. */
150
154
  .asst-bubble::before {
151
155
  content: '';
152
156
  position: absolute;
153
- left: -5.5px;
154
- top: 7px;
157
+ left: -4.5px;
158
+ top: 10.5px;
155
159
  width: 11px;
156
160
  height: 11px;
157
- background: var(--rk-fill-1);
161
+ background: var(--rk-bubble-bg);
158
162
  box-shadow: inset 1px -1px 0 var(--rk-line);
159
163
  clip-path: polygon(0 0, 0 100%, 100% 100%);
160
164
  transform: rotate(45deg);
161
165
  pointer-events: none;
162
166
  }
163
- .asst-bubble-verb { color: var(--rk-text-2); font-size: 12px; font-weight: 500; line-height: 1.5; white-space: nowrap; }
167
+ /* The line: one text flow, two lines at most (six opened), of the verb, the subject (cut by asst-render.js
168
+ until nothing is clamped) and the numbers, which take the end of the subject's last line and wrap
169
+ under it as a unit when they are long (a computer action and its clock). 2px of room around it for
170
+ the subject's focus ring. */
171
+ .asst-bubble-line {
172
+ display: -webkit-box;
173
+ -webkit-box-orient: vertical;
174
+ -webkit-line-clamp: 2;
175
+ min-width: 0;
176
+ margin: -2px -3px;
177
+ padding: 2px 3px;
178
+ overflow: hidden;
179
+ /* The strut sets the 18px pitch; the words inside sit at line-height 1, so no mix of sizes or fonts grows a line. */
180
+ font-size: 12px;
181
+ line-height: 18px;
182
+ overflow-wrap: anywhere;
183
+ }
184
+ .asst-bubble-line:has(> .asst-bubble-subject[aria-expanded="true"]) { -webkit-line-clamp: 6; }
185
+ .asst-bubble-verb { color: var(--rk-text-2); font-size: 12px; font-weight: 500; line-height: 1; white-space: nowrap; }
186
+ .asst-bubble-verb:not(:empty) { margin-right: 7px; }
164
187
  .asst-bubble[data-state="running"] .asst-bubble-verb { color: var(--rk-gold); }
165
- /* A card holds the call: no gold, no caret, the clock stopped (asst-render.js), "◆ Waiting for you". */
188
+ /* A card holds the call: no gold, the clock stopped (asst-render.js), "◆ Waiting for you". */
166
189
  .asst-bubble[data-state="waiting"] .asst-bubble-verb { color: var(--rk-warn); }
167
190
  .asst-bubble[data-state="waiting"] .asst-bubble-verb::before { content: '◆'; margin-right: 5px; font-size: 9px; vertical-align: 1px; }
168
191
  .asst-bubble-subject {
169
192
  --rk-cut: middle; /* where asst-render.js cuts a long subject: the middle; the head on a narrow stage */
170
193
  position: relative;
171
- min-width: 0;
172
- overflow: hidden;
173
- display: -webkit-box;
174
- -webkit-box-orient: vertical;
175
- -webkit-line-clamp: 2;
176
194
  border-radius: 4px;
177
195
  color: var(--rk-text);
178
196
  font-size: 12px;
179
- line-height: 1.5;
180
- overflow-wrap: anywhere;
197
+ line-height: 1;
181
198
  cursor: pointer;
182
199
  }
183
200
  .asst-bubble-subject.is-mono { font-family: var(--rk-mono); font-size: 11.5px; }
184
201
  .asst-bubble-subject.is-muted { color: var(--rk-text-3); }
185
- .asst-bubble-subject[aria-expanded="true"] { -webkit-line-clamp: 6; cursor: text; user-select: text; }
202
+ .asst-bubble-subject[aria-expanded="true"] { cursor: text; user-select: text; }
186
203
  .asst-bubble-subject .is-dim { color: var(--rk-text-3); }
187
204
  .asst-bubble-subject .is-strong { color: var(--rk-text); font-weight: 600; }
188
- /* Streaming thinking fades in from its cut edge. */
189
- .asst-bubble[data-kind="think"][data-state="running"] .asst-bubble-subject {
205
+ /* A URL or path: one piece per span, a <wbr> between them (asst-render.js writeSubject), so it wraps after "/"
206
+ and at its other separators, never mid-word; a piece wider than its line is cut instead (fitSubject). The
207
+ rest of the subject wraps as the line says. Opened, the whole literal shows: a piece may break again. */
208
+ .asst-bubble-piece { white-space: nowrap; }
209
+ .asst-bubble-subject[aria-expanded="true"] .asst-bubble-piece { white-space: normal; }
210
+ /* Streaming thinking cut from its head ("…the end of the thought") fades in from the cut; whole text is left alone. */
211
+ .asst-bubble[data-kind="think"][data-state="running"] .asst-bubble-subject.is-cut {
190
212
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12px);
191
213
  mask-image: linear-gradient(90deg, transparent, #000 12px);
192
214
  }
193
- .asst-bubble-caret {
194
- display: none;
195
- width: 6px;
196
- height: 12px;
197
- margin-left: 3px;
198
- vertical-align: -2px;
199
- background: var(--rk-gold);
200
- }
201
- .asst-bubble[data-state="running"] .asst-bubble-caret { display: inline-block; animation: asst-rk-caret 1.06s steps(1) infinite; }
202
215
  .asst-bubble-meta {
203
- grid-column: 2;
204
- display: flex;
205
- flex-wrap: wrap;
206
- gap: 2px 12px;
216
+ display: inline-flex;
217
+ gap: 0 8px;
218
+ margin-left: 7px;
207
219
  color: var(--rk-text-3);
208
220
  font-size: 11px;
209
- line-height: 1.5;
221
+ line-height: 1; /* inside the 18px strut: the smaller type never grows the line */
222
+ white-space: nowrap;
210
223
  font-variant-numeric: tabular-nums;
211
224
  }
212
225
  .asst-bubble-meta:empty { display: none; }
213
226
  .asst-bubble-num { white-space: nowrap; }
214
227
  .asst-bubble-err {
215
- grid-column: 1 / -1;
216
228
  display: -webkit-box;
217
229
  -webkit-box-orient: vertical;
218
230
  -webkit-line-clamp: 2;
219
231
  min-width: 0;
232
+ margin-top: 2px;
220
233
  overflow: hidden;
221
234
  color: var(--rk-red);
222
235
  font-family: var(--rk-mono);
@@ -228,8 +241,8 @@ export const RACK_CSS = `
228
241
  .asst-bubble-copy {
229
242
  position: relative;
230
243
  isolation: isolate;
231
- grid-column: 1 / -1;
232
- justify-self: start;
244
+ display: inline-flex;
245
+ align-items: center;
233
246
  height: var(--rk-pill-h);
234
247
  margin-top: 4px;
235
248
  padding: 0 var(--rk-pill-px);
@@ -242,7 +255,9 @@ export const RACK_CSS = `
242
255
  cursor: pointer;
243
256
  }
244
257
  .asst-bubble-copy:hover { color: var(--rk-text); }
245
- .asst-odo { display: inline-flex; font-variant-numeric: tabular-nums; white-space: pre; }
258
+ /* The clock: tabular digits that roll in place (asst-render.js); its cell keeps "59 s" of room, right-aligned, so
259
+ "9 s" → "10 s" moves nothing. */
260
+ .asst-odo { display: inline-flex; justify-content: flex-end; min-width: 2.4em; font-variant-numeric: tabular-nums; white-space: pre; }
246
261
  .asst-odo-d { display: inline-block; }
247
262
 
248
263
  .asst-rack-sweep { position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px; overflow: hidden; pointer-events: none; }
@@ -553,9 +568,9 @@ export const RACK_CSS = `
553
568
  .asst-rack-stage { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "face caption" "bubble bubble"; column-gap: 8px; row-gap: 6px; }
554
569
  .asst-rack-set { align-self: center; flex-direction: row; }
555
570
  .asst-rack.is-staged > .asst-rack-stage { padding: 10px 10px 10px; }
556
- .asst-rack-caption { align-self: center; margin-bottom: 0; }
557
- .asst-bubble { justify-self: stretch; max-width: 100%; margin: 4px 0 0; border-radius: 3px 12px 12px 12px; }
558
- .asst-bubble::before { left: 30px; top: -5.5px; box-shadow: inset 1px 1px 0 var(--rk-line); clip-path: polygon(0 100%, 0 0, 100% 0); }
571
+ .asst-rack-caption { align-self: center; margin-left: 0; margin-bottom: 0; }
572
+ .asst-bubble { justify-self: stretch; max-width: 100%; margin: 4px 0 0; border-radius: 3px 10px 10px 10px; }
573
+ .asst-bubble::before { left: 30px; top: -4.5px; box-shadow: inset 1px 1px 0 var(--rk-line); clip-path: polygon(0 100%, 0 0, 100% 0); }
559
574
  .asst-station-hdr { gap: 8px; padding: 0 6px; }
560
575
  .asst-station-time { display: none; }
561
576
  .asst-tick.is-old { display: none; }
@@ -565,7 +580,8 @@ export const RACK_CSS = `
565
580
  .asst-station-sub { margin-left: 12px; }
566
581
  }
567
582
  @container asst (max-width: 320px) {
568
- .asst-bubble { padding: 5px 9px 6px; }
583
+ .asst-bubble { padding: 6px 9px; }
584
+ .asst-bubble::before { top: -4.5px; }
569
585
  .asst-bubble-subject { --rk-cut: head; }
570
586
  .asst-bubble-meta { display: none; }
571
587
  }
@@ -576,6 +592,7 @@ export const RACK_CSS = `
576
592
  }
577
593
 
578
594
  /* ── Motion ─────────────────────────────────────────────────────── */
595
+ /* The caret blink belongs to the scenes' code caret (asst-scene-styles.js); the bubble has none. */
579
596
  @keyframes asst-rk-caret { 0% { opacity: 1; } 50% { opacity: 0; } }
580
597
  @keyframes asst-rk-sweep { from { transform: translateX(-100%); } to { transform: translateX(270%); } }
581
598
  @keyframes asst-rk-tick { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }