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
@@ -0,0 +1,397 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Assistant — the cards the mascot presents: the shared kit
3
+ // ═══════════════════════════════════════════
4
+ // What a finished turn offers (the follow-ups card, asst-followups-card.js)
5
+ // and what the brain asks (the question cards of asst-control.js: agent_clarify,
6
+ // AskUserQuestion, Codex's choice and requestUserInput, OpenCode's question)
7
+ // are drawn the same way: the mascot beside the card, presenting, asking and
8
+ // reacting, its words in a bubble and the answers under it. This module is the
9
+ // part they share:
10
+ //
11
+ // - createPresenter(): the mascot's slot. A painted still (paintMascot +
12
+ // stillFrame, never a rig) until the renderer makes it live (setLive): then a
13
+ // rig from synabun-mascot.js, drawn by the one shared ticker and parked off
14
+ // screen by the rig's own observer. Only the newest open card of a panel is
15
+ // live, and none while calm (replay, reduced motion, a drag): asst-render.js
16
+ // activateRigs. `target` is what the panel's director (asst-mascot.js) dresses
17
+ // while this presenter is the character on screen; `waits` marks a card that
18
+ // waits on the user itself (a question card), which the director never
19
+ // poses "blocked" at its own card.
20
+ // - askOption() / setOptionChecked(): one option row (key · label · description
21
+ // · badge), the same markup in both kinds of card.
22
+ // - optionKeyIntent() (pure) and the wiring around it: one tab stop per option
23
+ // group (arrows, Home, End), 1-9 pick an option while focus is in the card,
24
+ // Esc goes back to the composer; the mascot looks at the option pointed at
25
+ // or focused (wireGaze) and watches a text field being typed in (wireWatch).
26
+ // Nothing touches the DOM at import: the server imports asst-control.js
27
+ // (WhatsApp cards) and node:test draws cards with a stand-in document.
28
+
29
+ import * as mascotLib from '../synabun-mascot.js';
30
+ import { isReducedMotion } from '../synabun-ticker.js';
31
+ import { lookToward } from './asst-mascot.js';
32
+ import { injectAskStyles } from './asst-ask-styles.js';
33
+
34
+ export { injectAskStyles };
35
+
36
+ /** The presenter's SVG, before the sheet's container queries shrink it. */
37
+ export const PRESENTER_SIZE = Object.freeze({ width: 64, height: 32 });
38
+ const HOP_MS = 320; // the hop (300 ms) and a frame to land on
39
+ const WATCH_IDLE_MS = 1500; // a field nobody typed in for this long: back to the card's own pose
40
+ const WATCH_EVERY_MS = 120; // the caret is looked at no more often than this
41
+ // Where a still looks: toward what the card offers (the answers sit right of the face and under it).
42
+ const STILL_LOOK = Object.freeze({ present: { x: 0.5, y: 0.35 }, pick: { x: 0.5, y: 0.25 }, ask: { x: 0.35, y: -0.15 }, success: { x: 0, y: 0 }, idle: { x: 0, y: 0 } });
43
+
44
+ function el(tag, className, text) {
45
+ const node = document.createElement(tag);
46
+ if (className) node.className = className;
47
+ if (text != null) node.textContent = text;
48
+ return node;
49
+ }
50
+
51
+ function reduced() {
52
+ try { return !!isReducedMotion(); } catch { return false; }
53
+ }
54
+
55
+ const centre = (node) => {
56
+ try {
57
+ const r = node?.getBoundingClientRect?.();
58
+ return r && r.width ? { x: r.left + r.width / 2, y: r.top + r.height / 2 } : null;
59
+ } catch { return null; }
60
+ };
61
+
62
+ /** A text field: typing there never picks an option (1-9 are its characters). */
63
+ export function isTextField(node) {
64
+ const tag = String(node?.tagName || '').toUpperCase();
65
+ return tag === 'INPUT' || tag === 'TEXTAREA' || !!node?.isContentEditable;
66
+ }
67
+
68
+ /**
69
+ * createPresenter({ pose, options, waits, entrance })
70
+ * → { el, target, setLive(on), isLive(), setPose(name, opts), react(name), lookAt(node|null), setHome(node),
71
+ * watch(point), unwatch(), settle(pose, opts, { hop }), destroy() }
72
+ * `pose` / `options`: the card's own pose (present, pick, ask). `entrance`: the reaction it plays the first
73
+ * time it goes live (none on a card drawn while calm). settle(): the card is answered or left behind: the
74
+ * still it ends on ('success' is the "got it" face), after the hop when `hop` and it is live.
75
+ */
76
+ export function createPresenter({ pose = 'present', options = null, waits = false, entrance = null } = {}) {
77
+ const face = el('span', 'asst-ask-face');
78
+ face.setAttribute('aria-hidden', 'true');
79
+ let want = { pose, options: options && typeof options === 'object' ? { ...options } : {} };
80
+ let rig = null;
81
+ let held = false; // the director dresses the rig (asst-mascot.js hold / restore)
82
+ let watching = false;
83
+ let watchedAt = -Infinity;
84
+ let final = null; // { pose, opts }: settled, a still from now on
85
+ let leaving = 0; // the hop plays out before the settled still
86
+ let destroyed = false;
87
+ let home = null; // what it looks at when nothing is pointed at
88
+ let looking = null; // what it looks at now
89
+ let entered = false;
90
+
91
+ const stillOpts = (name, opts = {}) => ({ ...opts, look: opts.look || STILL_LOOK[name] || { x: 0, y: 0 } });
92
+
93
+ function dropRig() {
94
+ if (!rig) return;
95
+ try { rig.destroy(); } catch { /* gone */ }
96
+ rig = null;
97
+ }
98
+
99
+ function paintStill(name, opts) {
100
+ dropRig();
101
+ try {
102
+ face.innerHTML = mascotLib.mascotSvg({ width: PRESENTER_SIZE.width, height: PRESENTER_SIZE.height, className: 'synabun-mascot asst-still' });
103
+ const svg = face.firstElementChild;
104
+ if (svg && typeof svg.setAttribute === 'function') {
105
+ svg.setAttribute('data-pose', name);
106
+ mascotLib.paintMascot(svg, mascotLib.stillFrame(name, stillOpts(name, opts)));
107
+ }
108
+ } catch { /* no SVG here (a stand-in document): the card reads without its face */ }
109
+ }
110
+
111
+ function look(node) {
112
+ looking = node || null;
113
+ if (!rig || held || watching || final) return;
114
+ const point = centre(looking || home);
115
+ if (!point) return;
116
+ let box = null;
117
+ try { box = rig.el.getBoundingClientRect(); } catch { box = null; }
118
+ const to = lookToward(box, point);
119
+ try { rig.look(to.x, to.y); } catch { /* gone */ }
120
+ }
121
+
122
+ function applyWant() {
123
+ if (!rig || held || watching || final) return;
124
+ try { rig.setPose(want.pose, want.options); } catch { /* keeps its pose */ }
125
+ look(looking);
126
+ }
127
+
128
+ function setLive(on) {
129
+ if (destroyed || final) return;
130
+ if (on && !rig) {
131
+ try {
132
+ face.textContent = '';
133
+ rig = mascotLib.createMascot(face, { width: PRESENTER_SIZE.width, height: PRESENTER_SIZE.height, pose: want.pose, poseOptions: want.options });
134
+ } catch { rig = null; paintStill(want.pose, want.options); return; }
135
+ look(looking);
136
+ if (!entered) {
137
+ entered = true;
138
+ if (entrance && !reduced()) { try { rig.react(entrance); } catch { /* still */ } }
139
+ }
140
+ } else if (!on && rig) {
141
+ watching = false;
142
+ paintStill(want.pose, want.options);
143
+ }
144
+ }
145
+
146
+ function settle(name = 'success', opts = {}, { hop = false } = {}) {
147
+ if (destroyed) return;
148
+ final = { pose: name, opts };
149
+ watching = false;
150
+ face.classList.add('is-settled');
151
+ if (leaving) { clearTimeout(leaving); leaving = 0; }
152
+ if (hop && rig && !reduced()) {
153
+ try { rig.react('hop'); } catch { /* lands at once */ }
154
+ leaving = setTimeout(() => { leaving = 0; if (!destroyed && final) paintStill(final.pose, final.opts); }, HOP_MS);
155
+ return;
156
+ }
157
+ paintStill(name, opts);
158
+ }
159
+
160
+ const target = {
161
+ kind: 'card',
162
+ waits: !!waits,
163
+ get rig() { return rig; },
164
+ get el() { return rig ? rig.el : face; },
165
+ hold(on) { held = !!on; },
166
+ restore() { applyWant(); },
167
+ };
168
+
169
+ paintStill(want.pose, want.options);
170
+
171
+ return {
172
+ el: face,
173
+ target,
174
+ setLive,
175
+ isLive: () => !!rig && !final,
176
+ /** The card's own pose; while the director holds the rig (or a field is watched) it waits for its turn. */
177
+ setPose(name, opts = null) {
178
+ want = { pose: name, options: opts && typeof opts === 'object' ? { ...opts } : {} };
179
+ if (final || destroyed) return;
180
+ if (rig) applyWant();
181
+ else paintStill(want.pose, want.options);
182
+ },
183
+ react(name) {
184
+ if (!rig || final || destroyed || reduced()) return;
185
+ try { rig.react(name); } catch { /* still */ }
186
+ },
187
+ /** Eyes on `node` (an option, a button); null: back to what it offers first. */
188
+ lookAt: look,
189
+ setHome(node) { home = node || null; if (!looking) look(null); },
190
+ /** Someone types in the card's field: the watch pose, its eyes on the caret (`point`, client px). */
191
+ watch(point) {
192
+ if (!rig || held || final || !point) return;
193
+ if (!watching) {
194
+ watching = true;
195
+ try { rig.setPose('watch'); } catch { /* keeps its pose */ }
196
+ }
197
+ const now = Date.now();
198
+ if (now - watchedAt < WATCH_EVERY_MS) return;
199
+ watchedAt = now;
200
+ let box = null;
201
+ try { box = rig.el.getBoundingClientRect(); } catch { box = null; }
202
+ const to = lookToward(box, point, { horizontal: true });
203
+ try { rig.look(to.x, to.y); } catch { /* gone */ }
204
+ },
205
+ unwatch() {
206
+ if (!watching) return;
207
+ watching = false;
208
+ applyWant();
209
+ },
210
+ settle,
211
+ destroy() {
212
+ if (destroyed) return;
213
+ destroyed = true;
214
+ if (leaving) { clearTimeout(leaving); leaving = 0; }
215
+ dropRig();
216
+ },
217
+ };
218
+ }
219
+
220
+ // ── Options ──
221
+
222
+ /**
223
+ * One option row: [key] label · description [badge] [↵]. `role` 'radio' / 'checkbox' (or none: a plain
224
+ * action button). The key shows its number shortcut (1-9); a checked checkbox shows ✓ instead.
225
+ */
226
+ export function askOption({ label, description = '', index = 0, role = '', badge = '', go = false, title = '' } = {}) {
227
+ const btn = el('button', 'asst-ask-option');
228
+ btn.type = 'button';
229
+ btn.dataset.index = String(index);
230
+ btn.setAttribute('data-ask-option', '');
231
+ if (role) {
232
+ btn.setAttribute('role', role);
233
+ btn.setAttribute('aria-checked', 'false');
234
+ }
235
+ if (title) btn.title = title;
236
+ const key = el('span', 'asst-ask-key', index < 9 ? String(index + 1) : '');
237
+ key.setAttribute('aria-hidden', 'true');
238
+ btn.appendChild(key);
239
+ const main = el('span', 'asst-ask-main');
240
+ main.appendChild(el('span', 'asst-ask-label', label));
241
+ if (description) main.appendChild(el('span', 'asst-ask-desc', description));
242
+ btn.appendChild(main);
243
+ if (badge) btn.appendChild(el('span', 'asst-ask-badge', badge));
244
+ if (go) {
245
+ const arrow = el('span', 'asst-ask-go', '↵');
246
+ arrow.setAttribute('aria-hidden', 'true');
247
+ btn.appendChild(arrow);
248
+ }
249
+ btn._askKey = key;
250
+ return btn;
251
+ }
252
+
253
+ /** Check or clear an option row (aria-checked, the gold, a checkbox's ✓). */
254
+ export function setOptionChecked(btn, on) {
255
+ if (!btn) return;
256
+ btn.classList.toggle('selected', !!on);
257
+ if (btn.getAttribute?.('role') || btn.attributes?.role) btn.setAttribute('aria-checked', on ? 'true' : 'false');
258
+ const key = btn._askKey;
259
+ const role = btn.getAttribute?.('role') || btn.attributes?.role || '';
260
+ if (key && role === 'checkbox') {
261
+ const index = Number(btn.dataset.index);
262
+ key.textContent = on ? '✓' : (index < 9 ? String(index + 1) : '');
263
+ }
264
+ }
265
+
266
+ /**
267
+ * What a key does in a card: { composer } (Esc: back to the message box), { pick: i } (1-9, never while
268
+ * typing in a field), { move: i } (arrows, Home, End inside an option group: `index` is the focused option,
269
+ * -1 when focus is elsewhere), or null.
270
+ */
271
+ export function optionKeyIntent({ key, index = -1, count = 0, typing = false, alt = false, ctrl = false, meta = false } = {}) {
272
+ if (key === 'Escape') return { composer: true };
273
+ if (typing || alt || ctrl || meta) return null;
274
+ if (/^[1-9]$/.test(String(key))) {
275
+ const i = Number(key) - 1;
276
+ return i < count ? { pick: i } : null;
277
+ }
278
+ if (index < 0 || count < 1) return null;
279
+ switch (key) {
280
+ case 'ArrowDown':
281
+ case 'ArrowRight': return { move: (index + 1) % count };
282
+ case 'ArrowUp':
283
+ case 'ArrowLeft': return { move: (index - 1 + count) % count };
284
+ case 'Home': return { move: 0 };
285
+ case 'End': return { move: count - 1 };
286
+ default: return null;
287
+ }
288
+ }
289
+
290
+ const usable = (btn) => !!btn && !btn.disabled && !btn.hidden;
291
+
292
+ /**
293
+ * One tab stop for an option group (roving tabindex): the checked option, else the first. Arrows, Home and
294
+ * End move focus; `onMove(button, index)` follows it (a radio group checks the option, as radios do).
295
+ */
296
+ export function wireOptionGroup(group, { options = () => [], onMove = null } = {}) {
297
+ const all = () => (typeof options === 'function' ? options() : []) || [];
298
+ function sync(current = null) {
299
+ const list = all();
300
+ const checked = list.find((b) => (b.getAttribute?.('aria-checked') || b.attributes?.['aria-checked']) === 'true');
301
+ const pick = list.includes(current) ? current : (checked || list.find(usable) || list[0]);
302
+ for (const b of list) b.tabIndex = b === pick ? 0 : -1;
303
+ }
304
+ group.addEventListener('keydown', (e) => {
305
+ const list = all().filter(usable);
306
+ const index = list.indexOf(e.target);
307
+ if (index < 0) return;
308
+ const intent = optionKeyIntent({ key: e.key, index, count: list.length, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey });
309
+ if (!intent || intent.move == null) return;
310
+ e.preventDefault();
311
+ e.stopPropagation();
312
+ const next = list[intent.move];
313
+ sync(next);
314
+ try { next.focus(); } catch { /* not focusable here */ }
315
+ onMove?.(next, intent.move);
316
+ });
317
+ group.addEventListener('focusin', (e) => { if (all().includes(e.target)) sync(e.target); });
318
+ sync();
319
+ return { sync };
320
+ }
321
+
322
+ /**
323
+ * The card's own keys, wherever focus is inside it: 1-9 pick an option of `options()` (the group in play),
324
+ * Esc goes back to the composer, Enter is `onEnter(event)`'s (true: handled). A key a group or a field
325
+ * already handled (defaultPrevented) is left alone.
326
+ */
327
+ export function wireCardKeys(card, { options = () => [], onPick = null, onComposer = null, onEnter = null } = {}) {
328
+ card.addEventListener('keydown', (e) => {
329
+ if (e.defaultPrevented) return;
330
+ const list = ((typeof options === 'function' ? options() : []) || []).filter(usable);
331
+ const typing = isTextField(e.target);
332
+ const intent = optionKeyIntent({ key: e.key, count: list.length, typing, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey });
333
+ if (intent?.composer) {
334
+ e.preventDefault();
335
+ e.stopPropagation();
336
+ onComposer?.();
337
+ return;
338
+ }
339
+ if (intent?.pick != null) {
340
+ e.preventDefault();
341
+ e.stopPropagation();
342
+ onPick?.(intent.pick, list[intent.pick]);
343
+ return;
344
+ }
345
+ if (e.key === 'Enter' && typeof onEnter === 'function' && onEnter(e) === true) {
346
+ e.preventDefault();
347
+ e.stopPropagation();
348
+ }
349
+ });
350
+ }
351
+
352
+ /** The presenter looks at the option pointed at or focused inside `root`, and at what it offers first when none is. */
353
+ export function wireGaze(root, presenter, { selector = '[data-ask-option]' } = {}) {
354
+ const optionOf = (node) => {
355
+ const hit = node?.closest?.(selector);
356
+ return hit && root.contains?.(hit) ? hit : null;
357
+ };
358
+ const focusedOption = () => {
359
+ try { return optionOf(document.activeElement); } catch { return null; }
360
+ };
361
+ root.addEventListener('pointerover', (e) => { const hit = optionOf(e.target); if (hit) presenter.lookAt(hit); });
362
+ root.addEventListener('pointerleave', () => presenter.lookAt(focusedOption()));
363
+ root.addEventListener('focusin', (e) => presenter.lookAt(optionOf(e.target)));
364
+ root.addEventListener('focusout', (e) => { if (!root.contains?.(e.relatedTarget)) presenter.lookAt(null); });
365
+ }
366
+
367
+ /** Where a text field's caret is, roughly (client px): its left edge plus the typed width, mid-height. */
368
+ function caretOf(input) {
369
+ try {
370
+ const r = input.getBoundingClientRect();
371
+ const typed = String(input.value || '').slice(0, input.selectionStart ?? undefined).length;
372
+ return { x: r.left + Math.min(r.width - 12, 12 + typed * 7), y: r.top + r.height / 2 };
373
+ } catch { return null; }
374
+ }
375
+
376
+ /** The presenter watches `input` while it is typed in (its eyes on the caret), and goes back to its pose 1.5 s after. */
377
+ export function wireWatch(input, presenter) {
378
+ let timer = 0;
379
+ const stop = () => { if (timer) clearTimeout(timer); timer = 0; presenter.unwatch(); };
380
+ input.addEventListener('input', () => {
381
+ presenter.watch(caretOf(input));
382
+ if (timer) clearTimeout(timer);
383
+ timer = setTimeout(stop, WATCH_IDLE_MS);
384
+ });
385
+ input.addEventListener('blur', stop);
386
+ }
387
+
388
+ /** "1–4" for four options, "1" for one: the keys a hint names. */
389
+ export function keyRange(count) {
390
+ const n = Math.max(0, Math.min(9, Number(count) || 0));
391
+ return n > 1 ? `1–${n}` : n === 1 ? '1' : '';
392
+ }
393
+
394
+ /** The key that sends a multi-select from anywhere in its card, as the platform names it. */
395
+ export function sendModifier() {
396
+ try { return /Mac|iPhone|iPad/.test(globalThis.navigator?.platform || globalThis.navigator?.userAgent || '') ? '⌘' : 'Ctrl'; } catch { return 'Ctrl'; }
397
+ }
@@ -1,23 +1,26 @@
1
+ import { wireDropdowns, closeDropdowns, setDropdownDisabled, dropdownOf } from '../cc-dropdown.js';
2
+ import { configureToolPickerTranslation, toolPickerHTML, updateToolPicker, readToolPicker, toolWord } from '../tool-list-picker.js';
3
+ import { toolListView } from '../tool-list-view.js';
1
4
  // ═══════════════════════════════════════════
2
5
  // SynaBun Assistant — brain picker (toolbar fields)
3
6
  // ═══════════════════════════════════════════
4
7
  // Dropdown fields for the brain: model (provider icon + model; switching
5
8
  // provider happens inside the model menu, grouped by provider), effort (or the
6
9
  // OpenCode agent), permission mode and account (Claude/Codex, inline only when
7
- // there is more than one to pick) in the composer row; project and MCP profile
10
+ // there is more than one to pick) in the composer row; project and SynaBun tools
8
11
  // in the toolbar. Every field opens an asst-menu popover (it flips when there
9
- // is no room). A provider/account/profile change reports
12
+ // is no room). A provider/account/tools change reports
10
13
  // { requiresSwitch: true } so the panel sends `switch_brain`.
11
14
 
12
15
  import {
13
16
  ensureModelsForProfile,
14
17
  fetchClaudeAccounts,
15
18
  fetchCodexAccounts,
16
- fetchMcpProfilePresets,
19
+ fetchToolCatalog,
17
20
  fetchOpencodeAgents,
18
21
  getCachedClaudeAccounts,
19
22
  getCachedCodexAccounts,
20
- getCachedMcpProfilePresets,
23
+ getCachedToolCatalog,
21
24
  getCachedOpencodeAgents,
22
25
  getClaudeWorkerAccountId,
23
26
  getCodexWorkerAccountId,
@@ -445,7 +448,7 @@ on('sync:assistant:catalog-changed', modelListsChanged);
445
448
  // Launch: the menus' lists are read once the page has painted, so the first model menu opens from them.
446
449
  if (typeof document !== 'undefined') {
447
450
  registerLaunchPrefetch('assistant-model-catalog', () => loadModelCatalog(), { order: 10, owner: true });
448
- registerLaunchPrefetch('assistant-accounts', () => Promise.allSettled([fetchClaudeAccounts(), fetchCodexAccounts(), fetchMcpProfilePresets(), fetchOpencodeAgents()]), { order: 14, owner: true });
451
+ registerLaunchPrefetch('assistant-accounts', () => Promise.allSettled([fetchClaudeAccounts(), fetchCodexAccounts(), fetchToolCatalog(), fetchOpencodeAgents()]), { order: 14, owner: true });
449
452
  }
450
453
 
451
454
  function sameModel(selected, m) {
@@ -507,6 +510,7 @@ export async function openModelMenu(anchor, opts = {}) {
507
510
  try { catalog = await loadModelCatalog({ force: opts.forceReload === true }); } catch { failed = true; } finally { anchor.removeAttribute('aria-busy'); }
508
511
  if (!anchor.isConnected || (loading && currentMenu() !== loading.el)) return null;
509
512
  }
513
+ if (opts.shouldOpen && !opts.shouldOpen()) return null;
510
514
  const items = buildModelMenuItems(catalog);
511
515
  const menu = openMenu(anchor, {
512
516
  title: title || tr('assistant.brain.model', 'Model'),
@@ -635,7 +639,7 @@ function menuItemsSignature(items) {
635
639
  // Creation order is DOM order within a slot: model · effort · variant · mode · tests · account in the composer.
636
640
  // `effort` is the OpenCode agent for OpenCode; `variant` is OpenCode's effort (the model's variants).
637
641
  // `tests` is the session's Test density, not a brain field: it shows only while the host supplies a level.
638
- const FIELDS = ['model', 'effort', 'variant', 'mode', 'tests', 'account', 'project', 'mcp'];
642
+ const FIELDS = ['model', 'effort', 'variant', 'mode', 'tests', 'account', 'project'];
639
643
  // Slot of each field: the composer row (the brain and how it acts) · the toolbar (where it works).
640
644
  const FIELD_GROUP = { model: 'brain', effort: 'brain', variant: 'brain', mode: 'brain', tests: 'brain', account: 'brain', project: 'context', mcp: 'context' };
641
645
 
@@ -650,6 +654,7 @@ const FIELD_GROUP = { model: 'brain', effort: 'brain', variant: 'brain', mode: '
650
654
  * onProjects(projects) — the registered projects were (re)loaded }
651
655
  */
652
656
  export function createBrainPicker(host, hooks = {}) {
657
+ configureToolPickerTranslation(hooks.t);
653
658
  const t = tf(hooks.t);
654
659
  const placement = hooks.menuPlacement || 'below';
655
660
 
@@ -674,6 +679,31 @@ export function createBrainPicker(host, hooks = {}) {
674
679
  buttons[field] = btn;
675
680
  }
676
681
 
682
+ const toolsSlot = el('div', 'asst-tools-picker');
683
+ (single ? root : host.context || root).appendChild(toolsSlot);
684
+ const toolsLife = new AbortController();
685
+ wireDropdowns(toolsSlot, { signal: toolsLife.signal });
686
+ let toolsSessionId = null;
687
+ toolsSlot.addEventListener('cc-dropdown-open', () => { toolsSessionId = hooks.getSessionId?.() ?? null; }, { signal: toolsLife.signal });
688
+ toolsSlot.addEventListener('change', (event) => {
689
+ if (currentBrain().provider !== 'opencode' || toolsSessionId !== (hooks.getSessionId?.() ?? null)) return;
690
+ commit({ tools: readToolPicker(event.target, { inherit: false }) }, 'tools');
691
+ }, { signal: toolsLife.signal });
692
+ function renderTools(brain) {
693
+ if (toolsSessionId !== (hooks.getSessionId?.() ?? null)) closeDropdowns(toolsSlot, { cancel: true });
694
+ const catalog = getCachedToolCatalog();
695
+ toolsSlot.hidden = brain.provider !== 'opencode' || !catalog;
696
+ if (toolsSlot.hidden) { closeDropdowns(toolsSlot, { cancel: true }); toolsSlot.innerHTML = ''; return; }
697
+ let input = toolsSlot.querySelector('input');
698
+ const options = { catalog, includeServers: false, label: t('assistant.brain.mcp') };
699
+ const tools = brain.tools ?? [...new Set([...(catalog.default || []).filter(id => id !== 'git'), 'browser'])];
700
+ if (!input) {
701
+ toolsSlot.innerHTML = toolPickerHTML(`asst-tools-${globalThis.crypto.randomUUID()}`, tools, options);
702
+ input = toolsSlot.querySelector('input');
703
+ } else if (!dropdownOf(input)?.classList.contains('open')) updateToolPicker(input, tools, options);
704
+ setDropdownDisabled(input, hooks.getRunning?.() === true);
705
+ }
706
+
677
707
  let destroyed = false;
678
708
  let loading = null;
679
709
  // The empty state's "Choose a project" (mountEmptyProject): the project field's second button.
@@ -694,8 +724,7 @@ export function createBrainPicker(host, hooks = {}) {
694
724
  codex: getCachedCodexAccounts(),
695
725
  },
696
726
  agents: getCachedOpencodeAgents(),
697
- mcpProfiles: getCachedMcpProfilePresets(),
698
- defaultMcpProfile: 'full',
727
+ toolCatalog: getCachedToolCatalog(),
699
728
  projects: _projectsCache || [],
700
729
  };
701
730
  }
@@ -768,7 +797,7 @@ export function createBrainPicker(host, hooks = {}) {
768
797
  const efforts = effortsFor(brain);
769
798
  const level = efforts.find(e => e.id === brain.effort);
770
799
  const hasEfforts = efforts.some(e => e.id !== 'off');
771
- const presets = getCachedMcpProfilePresets();
800
+ const toolCatalog = getCachedToolCatalog();
772
801
  return {
773
802
  meta,
774
803
  model: modelLabel(brain),
@@ -780,7 +809,7 @@ export function createBrainPicker(host, hooks = {}) {
780
809
  variant: level ? level.label : (brain.effort || t('assistant.brain.variantDefault', 'default variant')),
781
810
  project: brain.project ? projectLabel(brain.project, _projectsCache || []) : t('assistant.brain.projectHome', 'Home'),
782
811
  mode: permissionModeLabel(brain, (id, fallback) => t(`assistant.brain.modes.${id}`, fallback)),
783
- mcp: presets[brain.mcpProfile]?.label || brain.mcpProfile || 'full',
812
+ mcp: toolListView(toolCatalog, brain.tools, { translate: hooks.t }).summary,
784
813
  account: ACCOUNT_PROVIDERS.has(brain.provider) ? accountLabel(brain) : '',
785
814
  };
786
815
  }
@@ -955,7 +984,7 @@ export function createBrainPicker(host, hooks = {}) {
955
984
  const testsLabel = tests ? testDensityLabel(tests, hooks.t) : '';
956
985
  setBtn('tests', { label: testsLabel, icon: testsIcon(testDensityBars(tests)), hidden: !tests, tooltip: `${t('assistant.tests.title', 'Test density')}: ${testsLabel}` });
957
986
  if (tests) buttons.tests.dataset.level = tests; else delete buttons.tests.dataset.level;
958
- setBtn('mcp', { label: v.mcp, icon: ICON_PLUG, tooltip: `${t('assistant.brain.mcp', 'MCP tool profile')}: ${v.mcp}` });
987
+ renderTools(brain);
959
988
  renderEmptyProject(brain);
960
989
  }
961
990
 
@@ -1022,7 +1051,6 @@ export function createBrainPicker(host, hooks = {}) {
1022
1051
  { field: 'project', label: t('assistant.brain.project', 'Working project'), value: brain.project || v.project, icon: ICON_FOLDER, hidden: false },
1023
1052
  { field: 'mode', label: t('assistant.brain.mode', 'Permission mode'), value: v.mode, icon: ICON_SHIELD, hidden: false },
1024
1053
  { field: 'tests', label: t('assistant.tests.title', 'Test density'), value: testDensity() ? testDensityLabel(testDensity(), hooks.t) : '', icon: testsIcon(testDensityBars(testDensity())), hidden: !testDensity() },
1025
- { field: 'mcp', label: t('assistant.brain.mcp', 'MCP tool profile'), value: v.mcp, icon: ICON_PLUG, hidden: false },
1026
1054
  { field: 'account', label: t('assistant.brain.accountShort', 'Account'), value: accountValue(brain), icon: ICON_ACCOUNT, hidden: !ACCOUNT_PROVIDERS.has(brain.provider) },
1027
1055
  ];
1028
1056
  }
@@ -1038,7 +1066,7 @@ export function createBrainPicker(host, hooks = {}) {
1038
1066
 
1039
1067
  async function ensureLoaded(provider, { force = false } = {}) {
1040
1068
  // The catalog too: defaults resolve against the menu's list (context variants, disabled models out).
1041
- const jobs = [ensureModelsForProfile(provider, force), fetchMcpProfilePresets(force), loadModelCatalog()];
1069
+ const jobs = [ensureModelsForProfile(provider, force), fetchToolCatalog(force), loadModelCatalog()];
1042
1070
  if (provider === 'codex') jobs.push(fetchCodexAccounts(force));
1043
1071
  if (provider === 'claude-code') jobs.push(fetchClaudeAccounts(force));
1044
1072
  if (provider === 'opencode') jobs.push(fetchOpencodeAgents(force));
@@ -1077,7 +1105,8 @@ export function createBrainPicker(host, hooks = {}) {
1077
1105
  * Open a field menu; `anchorOverride` (e.g. the toolbar "⋯") when the inline
1078
1106
  * button is hidden. `restoreFocus`: a pick sends focus back to the anchor.
1079
1107
  */
1080
- async function openField(field, anchorOverride = null, { restoreFocus = false } = {}) {
1108
+ async function openField(field, anchorOverride = null, { restoreFocus = false, shouldOpen = () => true } = {}) {
1109
+ if (!shouldOpen()) return;
1081
1110
  const anchor = anchorFor(field, anchorOverride);
1082
1111
  const brain = currentBrain();
1083
1112
  if (field === 'model') {
@@ -1088,6 +1117,7 @@ export function createBrainPicker(host, hooks = {}) {
1088
1117
  }
1089
1118
  await openModelMenu(anchor, {
1090
1119
  brain,
1120
+ shouldOpen,
1091
1121
  t: hooks.t,
1092
1122
  placement,
1093
1123
  current: { provider: brain.provider, model: brain.model },
@@ -1126,7 +1156,7 @@ export function createBrainPicker(host, hooks = {}) {
1126
1156
  if (!_warmProviders.has(brain.provider)) {
1127
1157
  anchor.setAttribute('aria-busy', 'true');
1128
1158
  try { await ensureLoaded(brain.provider); } finally { anchor.removeAttribute('aria-busy'); }
1129
- if (destroyed || !anchor.isConnected) return;
1159
+ if (destroyed || !anchor.isConnected || !shouldOpen()) return;
1130
1160
  } else {
1131
1161
  ensureLoaded(brain.provider).catch(() => {});
1132
1162
  }
@@ -1141,7 +1171,7 @@ export function createBrainPicker(host, hooks = {}) {
1141
1171
  if (!warm) {
1142
1172
  anchor.setAttribute('aria-busy', 'true');
1143
1173
  try { await ensureLoaded(brain.provider); } finally { anchor.removeAttribute('aria-busy'); }
1144
- if (destroyed || !anchor.isConnected) return;
1174
+ if (destroyed || !anchor.isConnected || !shouldOpen()) return;
1145
1175
  }
1146
1176
  let shown = null;
1147
1177
  drawField(field, brain, (title, items, extra = {}) => {
@@ -1208,21 +1238,6 @@ export function createBrainPicker(host, hooks = {}) {
1208
1238
  return;
1209
1239
  }
1210
1240
 
1211
- if (field === 'mcp') {
1212
- const presets = getCachedMcpProfilePresets();
1213
- const items = Object.entries(presets).map(([id, preset]) => ({
1214
- kind: 'radio',
1215
- id,
1216
- label: preset?.label || id,
1217
- desc: preset?.description || '',
1218
- tag: preset?.tools != null ? `${preset.tools} ${t('assistant.brain.tools', 'tools')}` : '',
1219
- selected: id === brain.mcpProfile,
1220
- onSelect: () => commit({ mcpProfile: id }, 'mcp'),
1221
- }));
1222
- menu(t('assistant.brain.mcp', 'MCP tool profile'), items);
1223
- return;
1224
- }
1225
-
1226
1241
  if (field === 'mode') {
1227
1242
  const modes = PERMISSION_MODES[brain.provider] || PERMISSION_MODES['claude-code'];
1228
1243
  // Plan mode is its own switch: it combines with every approval mode below.
@@ -1319,15 +1334,14 @@ export function createBrainPicker(host, hooks = {}) {
1319
1334
  if (provider === before.provider) return;
1320
1335
  await ensureLoaded(provider);
1321
1336
  if (destroyed) return;
1322
- const draft = { ...before, provider, model: model || '', effort: null, agent: null, accountId: null };
1337
+ const draft = { ...before, provider, model: model || '', effort: null, agent: null, accountId: null, tools: null };
1323
1338
  // resolveBrainDefaults reads the new provider's catalog.
1324
1339
  const next = resolveBrainDefaults(draft, {
1325
1340
  models: { [provider]: resolvableModels(provider, draft.model) },
1326
1341
  efforts: { [provider]: effortsFor(normalizeBrain(draft)) },
1327
1342
  accounts: { 'claude-code': getCachedClaudeAccounts(), codex: getCachedCodexAccounts() },
1328
1343
  agents: getCachedOpencodeAgents(),
1329
- mcpProfiles: getCachedMcpProfilePresets(),
1330
- defaultMcpProfile: 'full',
1344
+ toolCatalog: getCachedToolCatalog(),
1331
1345
  projects: _projectsCache || [],
1332
1346
  });
1333
1347
  if (model && !next.model) next.model = model;
@@ -1360,6 +1374,6 @@ export function createBrainPicker(host, hooks = {}) {
1360
1374
  mountEmptyProject,
1361
1375
  setBrain() { render(); },
1362
1376
  close: closeMenu,
1363
- destroy() { destroyed = true; fitObserver?.disconnect(); if (fitFrame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(fitFrame); for (const off of offAccounts) { try { off?.(); } catch {} } closeMenu(); heroProjectMascot?.destroy(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
1377
+ destroy() { toolsLife.abort(); toolsSlot.remove(); destroyed = true; fitObserver?.disconnect(); if (fitFrame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(fitFrame); for (const off of offAccounts) { try { off?.(); } catch {} } closeMenu(); heroProjectMascot?.destroy(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
1364
1378
  };
1365
1379
  }