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
@@ -12,7 +12,7 @@ import {
12
12
  buildSynaBunChoiceContent,
13
13
  codexApprovalDecisionActions,
14
14
  codexHeldServerRequests,
15
- codexMcpElicitationMeta,
15
+ codexMcpToolApproval,
16
16
  codexMcpToolApprovalActions,
17
17
  codexPermissionApprovalActions,
18
18
  codexRequestIdentityKey,
@@ -546,9 +546,9 @@ export function isElicitationValueSatisfied(value, schema) {
546
546
 
547
547
  export function renderMcpToolApprovalRequest(requestId, params) {
548
548
  if (_ctx.requestCards.has(String(requestId))) return;
549
- const metadata = codexMcpElicitationMeta(params);
549
+ const approval = codexMcpToolApproval(params);
550
550
  const serverName = String(params?.serverName || 'MCP');
551
- const toolName = String(metadata.tool_name || metadata.toolName || 'unknown tool');
551
+ const toolName = approval?.toolName || 'unknown tool';
552
552
  const entry = createInteractiveRequestCard(requestId, {
553
553
  title: `${serverName} Tool Approval`,
554
554
  subtitle: 'mcp tool approval',
@@ -0,0 +1,54 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Codex panel: native skills in the slash hints
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Codex lists its skills itself (app-server skills/list): each with its name, its SKILL.md path and its scope.
6
+ // A hint keeps that path, so picking one sends Codex's own `skill` input (name + path: the exact skill, even when
7
+ // two share a name) with the prompt, instead of only the text "/name" (SK-050). Same-named skills are told apart
8
+ // by their scope in the hint. No DOM: cdx-tabs.js draws the hints, the tests read this.
9
+
10
+ const SCOPE_LABEL = { user: 'personal', repo: 'repository', system: 'Codex', admin: 'admin' };
11
+
12
+ /**
13
+ * Hint rows of native skills: `{ cmd, desc, skill: { name, path }, scope }`, enabled ones only, sorted.
14
+ * @param {object[]} skills skills/list entries (name, description, path, scope, enabled)
15
+ * @param {Set<string>} [taken] commands already shown (built-ins win their name)
16
+ */
17
+ export function skillHintEntries(skills, taken = new Set()) {
18
+ const rows = (Array.isArray(skills) ? skills : [])
19
+ .filter((s) => s && s.enabled !== false)
20
+ .map((s) => {
21
+ const name = String(s.name || s.dirName || '').trim();
22
+ return { cmd: name ? `/${name}` : '', desc: String(s.description || '').trim(), skill: typeof s.path === 'string' && s.path ? { name, path: s.path } : null, scope: s.scope || null };
23
+ })
24
+ .filter((row) => row.cmd !== '/' && row.cmd && !taken.has(row.cmd));
25
+ const counts = rows.reduce((m, r) => m.set(r.cmd, (m.get(r.cmd) || 0) + 1), new Map());
26
+ for (const row of rows) {
27
+ if (counts.get(row.cmd) > 1) row.desc = `(${SCOPE_LABEL[row.scope] || row.scope || 'other'}) ${row.desc}`.trim();
28
+ }
29
+ return rows.sort((a, b) => a.cmd.localeCompare(b.cmd) || String(a.scope).localeCompare(String(b.scope)));
30
+ }
31
+
32
+ /**
33
+ * The native skill inputs a prompt carries: the picked skill, while the prompt still starts with its /name.
34
+ * @param {string} prompt the text being sent
35
+ * @param {{ cmd: string, name: string, path: string }|null} pending what was picked from the hints
36
+ * @returns {{ name: string, path: string }[]}
37
+ */
38
+ export function skillsForPrompt(prompt, pending) {
39
+ if (!pending || typeof pending.cmd !== 'string' || typeof pending.path !== 'string') return [];
40
+ const text = String(prompt || '').trimStart();
41
+ if (text !== pending.cmd && !text.startsWith(`${pending.cmd} `) && !text.startsWith(`${pending.cmd}\n`)) return [];
42
+ return [{ name: pending.name, path: pending.path }];
43
+ }
44
+
45
+ /**
46
+ * The skills_list request the panel sends for a tab: Codex's app-server (0.164.0, checked 2026-10-23) keeps its skill
47
+ * list until asked with forceReload, and sends no notification of its own when a skill folder appears or changes, so
48
+ * after SynaBun installed, removed or changed a skill (skills:changed) the next request forces the reload; otherwise
49
+ * the cached list is enough. The forced reload also refreshes the skills the app-server offers its threads.
50
+ * @param {{ project?: string|null }} tab @param {boolean} changed whether a skills:changed arrived since the last list
51
+ */
52
+ export function skillsListRequest(tab, changed) {
53
+ return { cwds: tab?.project ? [tab.project] : [], forceReload: changed === true };
54
+ }
@@ -352,6 +352,12 @@ export function injectStyles() {
352
352
  display: inline-flex; align-items: center; gap: 3px;
353
353
  position: relative;
354
354
  }
355
+ /* SVGs with only a viewBox have platform-dependent intrinsic sizes. */
356
+ .cxp-toolbar-toggle > svg {
357
+ width: var(--cxp-space-3);
358
+ height: var(--cxp-space-3);
359
+ flex-shrink: 0;
360
+ }
355
361
  .cxp-toolbar-toggle::after {
356
362
  content: '';
357
363
  position: absolute; bottom: 0; left: 25%; right: 25%;
@@ -774,6 +780,7 @@ export function injectStyles() {
774
780
  /* ── Project bar and dropdowns ── */
775
781
  .cxp-projectbar {
776
782
  display: flex;
783
+ flex-wrap: wrap;
777
784
  align-items: center;
778
785
  gap: 4px;
779
786
  padding: 4px 10px 0;
@@ -796,7 +803,6 @@ export function injectStyles() {
796
803
  transition: background var(--cxp-dur-fast);
797
804
  }
798
805
  .cxp-dropdown-sm { max-width: 72px; }
799
- #cxp-profile { max-width: 80px; }
800
806
  .cxp-dropdown:hover {
801
807
  background: var(--cxp-surface-1);
802
808
  }
@@ -1691,6 +1697,7 @@ export function injectStyles() {
1691
1697
  .cxp-thinking .cxp-msg-think-dots span:nth-child(2) { animation-delay: 0.2s; }
1692
1698
  .cxp-think-dots span:nth-child(3),
1693
1699
  .cxp-thinking .cxp-msg-think-dots span:nth-child(3) { animation-delay: 0.4s; }
1700
+ .cxp-reasoning[hidden] { display: none; }
1694
1701
  .cxp-reasoning.cxp-reasoning-done .cxp-think-dots { display: none; }
1695
1702
  .cxp-thinking {
1696
1703
  display: flex;
@@ -2521,7 +2528,7 @@ export function injectStyles() {
2521
2528
  }
2522
2529
  /* The wrap draws the input's edge: 1px of padding over a border-coloured fill. */
2523
2530
  .cxp-input-wrap {
2524
- flex: 1; position: relative;
2531
+ flex: 0 0 auto; position: relative;
2525
2532
  margin: 8px 8px 2px;
2526
2533
  border-radius: var(--cxp-radius-lg);
2527
2534
  padding: 1px;
@@ -2687,6 +2694,7 @@ export function injectStyles() {
2687
2694
  background: var(--cxp-surface-2);
2688
2695
  }
2689
2696
  .cxp-projectbar-actions {
2697
+ flex-shrink: 0;
2690
2698
  display: flex;
2691
2699
  align-items: center;
2692
2700
  gap: 2px;
@@ -3087,7 +3095,7 @@ export function injectStyles() {
3087
3095
  color: var(--cxp-text-2);
3088
3096
  background: var(--cxp-surface-1);
3089
3097
  margin-bottom: 2px;
3090
- cursor: grab;
3098
+ cursor: move;
3091
3099
  }
3092
3100
  .cxp-queue-item.dragging { opacity: 0.4; }
3093
3101
  .cxp-queue-item.drag-over { box-shadow: inset 0 2px 0 var(--cxp-text-2); }
@@ -1,3 +1,4 @@
1
+ import { cxpText, cxpSessionLabel } from './cdx-copy.js';
1
2
  import { mountCodexSettings } from './cdx-settings.js';
2
3
  import { t } from '../i18n.js';
3
4
  import { getCliState, isCliInstalling } from '../cli-status.js';
@@ -31,6 +32,7 @@ import {
31
32
  } from '../agent-runtime-options.js';
32
33
  import { CodexModelCatalog, codexModelAccountKey, codexContextAvailability } from './cdx-model-catalog.js';
33
34
  import { nativeLoopWindowId } from '../ui-native-window-id.js';
35
+ import { skillHintEntries, skillsForPrompt, skillsListRequest } from './cdx-skill-hints.js';
34
36
  import { syncSidepanelLayout, focusSidepanel, isHostedSessionFocused } from '../ui-sidepanel-windows.js';
35
37
  import { notify, NOTIF_TYPE } from '../ui-notifications.js';
36
38
  import { createStickyScrollController } from '../ui-scroll-follow.js';
@@ -298,7 +300,6 @@ function buildWsCallbacks(tab, options = {}) {
298
300
  userVerification: true,
299
301
  windowId: windowId,
300
302
  accountId: _accountId || 'default',
301
- mcpProfile: tab.mcpProfile || null,
302
303
  ...codexRuntimeSelection(tab),
303
304
  });
304
305
  } else {
@@ -310,7 +311,6 @@ function buildWsCallbacks(tab, options = {}) {
310
311
  threadId: _threadId || null,
311
312
  cwd: _project || null,
312
313
  accountId: _accountId || 'default',
313
- mcpProfile: tab.mcpProfile || null,
314
314
  ...codexRuntimeSelection(tab),
315
315
  });
316
316
  }
@@ -369,7 +369,6 @@ function buildWsCallbacks(tab, options = {}) {
369
369
  _accountId = msg.accountId;
370
370
  if (_boundTab) _boundTab.accountId = _accountId;
371
371
  }
372
- if (msg.mcpProfile && _boundTab) _boundTab.mcpProfile = msg.mcpProfile;
373
372
  // A reattached app-server can still have a different process-active
374
373
  // thread. Preserve the tab's displayed thread instead of adopting it.
375
374
  _threadId = _threadId || msg.threadId || null;
@@ -395,7 +394,6 @@ function buildWsCallbacks(tab, options = {}) {
395
394
  threadId: _threadId || null,
396
395
  cwd: _project || null,
397
396
  accountId: _accountId || 'default',
398
- mcpProfile: tab.mcpProfile || null,
399
397
  ...codexRuntimeSelection(tab),
400
398
  });
401
399
  } else {
@@ -408,7 +406,6 @@ function buildWsCallbacks(tab, options = {}) {
408
406
  threadId: _threadId || null,
409
407
  cwd: _project || null,
410
408
  accountId: _accountId || 'default',
411
- mcpProfile: tab.mcpProfile || null,
412
409
  ...codexRuntimeSelection(tab),
413
410
  });
414
411
  }
@@ -426,10 +423,6 @@ function buildWsCallbacks(tab, options = {}) {
426
423
  restoreMcpAppCards(_messagesEl, { tab: _boundTab, items: _items, request: requestSocketPayloadForTab, draft: setMcpAppDraft });
427
424
  if (_boundTab) _boundTab.bootstrapped = true;
428
425
  ensureCodexTerminalAuth(tab);
429
- if (msg.mcpProfile && _boundTab) {
430
- _boundTab.mcpProfile = msg.mcpProfile;
431
- if (activeTab() === _boundTab) _onActiveTabChanged?.(_boundTab);
432
- }
433
426
  if (Object.prototype.hasOwnProperty.call(msg, 'threadId')) {
434
427
  _threadId = msg.threadId || null;
435
428
  if (_boundTab) _boundTab.threadId = _threadId;
@@ -694,11 +687,6 @@ function buildWsCallbacks(tab, options = {}) {
694
687
  }
695
688
  renderStatusChrome();
696
689
  }
697
- if (msg.type === 'mcp_profile_changed' && msg.profile) {
698
- if (_boundTab) _boundTab.mcpProfile = msg.profile;
699
- if (activeTab() === _boundTab) _onActiveTabChanged?.(_boundTab);
700
- saveTabs();
701
- }
702
690
  if (msg.type === 'mcp_approval_updated' && msg.name) {
703
691
  const server = _mcpServers.get(msg.name);
704
692
  if (server) {
@@ -848,6 +836,9 @@ let _interruptRetried = false;
848
836
  let _prevInputTokens = 0;
849
837
  let _skillsCache = null;
850
838
  let _skillsPromise = null;
839
+ // A skills:changed arrived since the list was last read: the next skills_list asks the app-server to reload its skills
840
+ // (cdx-skill-hints.js skillsListRequest).
841
+ let _skillsChanged = false;
851
842
  let _blockingServerRequests = new Map();
852
843
  let _bufferedSocketMessages = [];
853
844
 
@@ -1861,14 +1852,19 @@ async function restoreCanonicalPlan(tab, thread) {
1861
1852
  }
1862
1853
 
1863
1854
  // ── Formatting & helpers (lines 1148-1421) ──
1855
+ function codexStatusText(status) {
1856
+ const known = {"pending": "pending", "completed": "completed", "complete": "complete", "inProgress": "in progress", "failed": "failed", "declined": "declined"};
1857
+ return Object.hasOwn(known, status) ? cxpText(`status_${status}`, known[status]) : status;
1858
+ }
1859
+
1864
1860
  export function formatStatus(status) {
1865
- if (!status) return 'pending';
1866
- if (typeof status === 'string') return status;
1861
+ if (!status) return codexStatusText('pending');
1862
+ if (typeof status === 'string') return codexStatusText(status);
1867
1863
  if (typeof status === 'object') {
1868
1864
  const [key, val] = Object.entries(status)[0] || [];
1869
- if (!key) return 'pending';
1870
- if (val && typeof val === 'object' && val.message) return `${key}: ${val.message}`;
1871
- return key;
1865
+ if (!key) return codexStatusText('pending');
1866
+ if (val && typeof val === 'object' && val.message) return `${codexStatusText(key)}: ${val.message}`;
1867
+ return codexStatusText(key);
1872
1868
  }
1873
1869
  return String(status);
1874
1870
  }
@@ -2457,10 +2453,10 @@ export function setSessionLabel(label, { persist = true } = {}) {
2457
2453
  }
2458
2454
  if (isActiveTab(_boundTab)) {
2459
2455
  const labelEl = panelEl('#cxp-session-label');
2460
- if (labelEl) labelEl.replaceChildren(document.createTextNode(_sessionLabel));
2456
+ if (labelEl) labelEl.replaceChildren(document.createTextNode(cxpSessionLabel(_sessionLabel)));
2461
2457
  // The whole name, for when the header shows only part of it.
2462
2458
  const sessionBtn = panelEl('#cxp-session-btn');
2463
- if (sessionBtn) sessionBtn.title = _sessionLabel;
2459
+ if (sessionBtn) sessionBtn.title = cxpSessionLabel(_sessionLabel);
2464
2460
  syncLegacyState();
2465
2461
  }
2466
2462
  updateTrayPillLabel();
@@ -2508,6 +2504,11 @@ function scheduleCodexTitleReassert(tab) {
2508
2504
 
2509
2505
  function applyCodexProviderTitle(title) {
2510
2506
  const tab = _boundTab;
2507
+ // Dispatch metadata owns the panel label; reading history must never rename the provider thread.
2508
+ if (tab?.automationRunId) {
2509
+ setSessionLabel(tab.automationTitle || tab.sessionLabel);
2510
+ return;
2511
+ }
2511
2512
  const incoming = normalizeSessionLabel(title || '');
2512
2513
  const desiredTitle = normalizeSessionLabel(tab?.pendingSessionLabel || tab?.sessionLabel || '');
2513
2514
  const authoritative = tab
@@ -2788,7 +2789,6 @@ export function createTab(saved = {}, { autoSwitch = true } = {}) {
2788
2789
  const inheritedAccountId = saved.accountId ?? activeTab()?.accountId ?? 'default';
2789
2790
  const inheritedEffort = saved.effort ?? activeTab()?.effort ?? (storage.getItem(STOR.effort) || 'off');
2790
2791
  const inheritedAutoAccept = codexNewTabAutoAccept(saved, activeTab(), storedAutoAccept());
2791
- const inheritedMcpProfile = saved.mcpProfile ?? null;
2792
2792
  const automationRunId = saved.automationRunId || null;
2793
2793
  const inheritedPlanMode = automationRunId ? false : (saved.planMode ?? activeTab()?.planMode ?? false);
2794
2794
  const hasExplicitTitle = !!title && !['new session', 'saved session', 'untitled session', 'codex'].includes(title.toLowerCase());
@@ -2816,7 +2816,6 @@ export function createTab(saved = {}, { autoSwitch = true } = {}) {
2816
2816
  extendedContextVerificationArmed: false,
2817
2817
  effort: inheritedEffort,
2818
2818
  autoAccept: inheritedAutoAccept,
2819
- mcpProfile: inheritedMcpProfile,
2820
2819
  planMode: inheritedPlanMode,
2821
2820
  sessionListRequest: null,
2822
2821
  threadStartRequest: null,
@@ -2898,6 +2897,7 @@ export function createTab(saved = {}, { autoSwitch = true } = {}) {
2898
2897
  isolationRejections: {},
2899
2898
  ownedThreadIds: new Set(),
2900
2899
  automationRunId,
2900
+ automationTitle: automationRunId ? (saved.automationTitle || title || '') : '',
2901
2901
  automationActive: !!saved.automationActive,
2902
2902
  automationAwaits: false, // the run's turn ended asking the user (runAwaitsAnswer, set by its attach): the pill needs you
2903
2903
  automationOwnerId: saved.automationOwnerId || null,
@@ -3039,7 +3039,6 @@ export function saveTabs() {
3039
3039
  contextMode: normalizeCodexContextMode(tab.contextMode),
3040
3040
  effort: tab.effort || 'off',
3041
3041
  autoAccept: !!tab.autoAccept,
3042
- mcpProfile: tab.mcpProfile || null,
3043
3042
  planMode: !!tab.planMode,
3044
3043
  planFilePath: tab.planFilePath || '',
3045
3044
  planContent: tab.planContent || '',
@@ -3062,6 +3061,7 @@ export function saveTabs() {
3062
3061
  postCompactionPending: tab.postCompactionPromptSource === 'manual' && !!tab.postCompactionPending,
3063
3062
  postCompactionPromptSource: tab.postCompactionPromptSource === 'manual' ? 'manual' : '',
3064
3063
  automationRunId: tab.automationRunId || null,
3064
+ automationTitle: tab.automationTitle || '',
3065
3065
  automationActive: !!tab.automationActive,
3066
3066
  automationOwnerId: tab.automationOwnerId || null,
3067
3067
  releasedRunId: tab.releasedRunId || null,
@@ -3520,7 +3520,7 @@ export function setThread(thread) {
3520
3520
  _freshThread = false;
3521
3521
  // History and a new thread carry the provider's name or, without one, the
3522
3522
  // first prompt. A name the user gave stays, and is sent to the provider again.
3523
- if (_boundTab?.titleByUser && hasCustomSessionLabel(_boundTab)) {
3523
+ if (_boundTab?.automationRunId || (_boundTab?.titleByUser && hasCustomSessionLabel(_boundTab))) {
3524
3524
  applyCodexProviderTitle(getThreadLabel(thread, _sessionLabel || 'New session'));
3525
3525
  } else {
3526
3526
  setSessionLabel(getThreadLabel(thread, 'New session'));
@@ -5140,6 +5140,10 @@ export function dispatchPrompt(prompt, { tab = activeTab(), images = null, paths
5140
5140
  };
5141
5141
  if (tab?.model) msg.model = tab.model;
5142
5142
  if (tab?.effort && tab.effort !== 'off') msg.effort = tab.effort;
5143
+ // A native skill picked from the hints (SK-050): sent as Codex's own skill input, once.
5144
+ const nativeSkills = skillsForPrompt(inputText, tab?.pendingSkill);
5145
+ if (nativeSkills.length) msg.skills = nativeSkills;
5146
+ if (tab) tab.pendingSkill = null;
5143
5147
  // A person's own turn: the greeting engine keeps when they were last here. The greeting this
5144
5148
  // tab opened with goes back once, with the first prompt of its new thread.
5145
5149
  msg.human = true;
@@ -5968,7 +5972,7 @@ export function syncToolbarState() {
5968
5972
  effortBtn.title = `Reasoning effort: ${selected?.label || effort}`;
5969
5973
  effortBtn.classList.toggle('active', effort !== 'off');
5970
5974
  const label = effortBtn.querySelector('.cxp-btn-label');
5971
- if (label) label.textContent = effort === 'off' ? 'Effort' : (selected?.label || effort);
5975
+ if (label) label.textContent = effort === 'off' ? cxpText('effortLabel', 'Effort') : cxpText(`effort_${effort}`, selected?.label || effort);
5972
5976
  const dots = effortBtn.querySelectorAll('.cxp-effort-dots i');
5973
5977
  const litCount = Math.max(0, options.findIndex((option) => option.id === effort));
5974
5978
  dots.forEach((d, i) => d.classList.toggle('lit', i < litCount));
@@ -5999,7 +6003,7 @@ export function syncToolbarState() {
5999
6003
  contextBtn.disabled = !!(tab?.running || tab?.startingThread || tab?.compacting)
6000
6004
  || (mode !== 'extended' && !supportsExtended);
6001
6005
  const label = contextBtn.querySelector('.cxp-btn-label');
6002
- if (label) label.textContent = mode === 'extended' ? 'Extended' : 'Default';
6006
+ if (label) label.textContent = mode === 'extended' ? cxpText('extended', 'Extended') : cxpText('default', 'Default');
6003
6007
  if (availability.reason) {
6004
6008
  contextBtn.title = availability.reason;
6005
6009
  } else if (mode !== 'extended') {
@@ -7215,6 +7219,15 @@ export function renderImageStrip() {
7215
7219
  // ── Whiteboard "Send to Panel" — receive image via event bus ──
7216
7220
  // Any window or surface changed a Codex account (signed in or out, a key, its mode, renamed, added,
7217
7221
  // removed): the menu re-reads the list, and the tabs on that account re-check sign-in.
7222
+ // A skill was installed, removed or changed (Skills Studio, Settings, an agent): the app-server is asked to reload its
7223
+ // skills at once (it does not watch the folders itself), so the hints and the thread's skills are current before the
7224
+ // next "/" is typed.
7225
+ appOn('sync:skills:changed', () => {
7226
+ _skillsCache = null; _skillsPromise = null; _skillsChanged = true;
7227
+ if (!activeTab()) return;
7228
+ _skillsPromise = loadSlashCommands().catch(() => {}).finally(() => { _skillsPromise = null; });
7229
+ });
7230
+
7218
7231
  appOn('sync:codex:accounts-changed', (msg = {}) => {
7219
7232
  refreshCodexAccounts().catch(() => {});
7220
7233
  for (const tab of _tabs) if (!msg.accountId || serverAccountId(tab.accountId) === msg.accountId) void tab.terminalAuthView?.refresh();
@@ -7491,16 +7504,12 @@ export async function loadSlashCommands() {
7491
7504
  if (!tab) return commands;
7492
7505
  const capabilities = await getCodexCapabilities(tab);
7493
7506
  commands = codexCommandHints(capabilities);
7494
- const data = await requestSocketPayloadForTab(tab, 'skills_list', { cwds: tab.project ? [tab.project] : [] });
7507
+ const reload = _skillsChanged;
7508
+ const data = await requestSocketPayloadForTab(tab, 'skills_list', skillsListRequest(tab, reload));
7509
+ if (reload) _skillsChanged = false;
7495
7510
  const seen = new Set(BUILTIN_SLASH_COMMANDS.map((entry) => entry.cmd));
7496
- const skillCommands = flattenNativeCatalog(data, 'skills')
7497
- .filter((skill) => skill.enabled !== false)
7498
- .map((skill) => ({
7499
- cmd: '/' + String(skill.name || skill.dirName || '').trim(),
7500
- desc: String(skill.description || '').trim(),
7501
- }))
7502
- .filter((skill) => skill.cmd !== '/' && !seen.has(skill.cmd))
7503
- .sort((a, b) => a.cmd.localeCompare(b.cmd));
7511
+ // Each native skill keeps its SKILL.md path: picking it sends Codex's own skill input (cdx-skill-hints.js).
7512
+ const skillCommands = skillHintEntries(flattenNativeCatalog(data, 'skills'), seen);
7504
7513
  _skillsCache = [...commands, ...skillCommands];
7505
7514
  } catch {
7506
7515
  _skillsCache = commands;
@@ -7563,15 +7572,19 @@ export function showSlashHints(text) {
7563
7572
  descSpan.className = 'cxp-slash-hint-desc';
7564
7573
  descSpan.textContent = c.desc;
7565
7574
  item.append(cmdSpan, descSpan);
7566
- item.addEventListener('click', () => selectSlashHint(c.cmd));
7575
+ item._hint = c;
7576
+ item.addEventListener('click', () => selectSlashHint(c.cmd, c));
7567
7577
  hintsEl.appendChild(item);
7568
7578
  });
7569
7579
  }
7570
7580
 
7571
- export function selectSlashHint(cmd) {
7581
+ export function selectSlashHint(cmd, hint = null) {
7572
7582
  const input = panelEl('#cxp-input');
7573
7583
  hideComposerHints();
7574
7584
  if (!input) return;
7585
+ // A native skill picked here goes as Codex's own skill input with the prompt, while the prompt starts with it.
7586
+ const pickTab = activeTab();
7587
+ if (pickTab) pickTab.pendingSkill = hint?.skill ? { cmd: hint.cmd, name: hint.skill.name, path: hint.skill.path } : null;
7575
7588
  const command = CODEX_COMMAND_BY_NAME.get(String(cmd).replace(/^\//, ''));
7576
7589
  if (command?.name === 'model') {
7577
7590
  panelEl('#cxp-model')?.classList.toggle('open');
@@ -7609,7 +7622,7 @@ export function confirmSlashHint() {
7609
7622
  return true;
7610
7623
  }
7611
7624
  const cmdEl = items[_slashActiveIdx]?.querySelector('.cxp-slash-hint-cmd');
7612
- if (cmdEl) selectSlashHint(cmdEl.textContent);
7625
+ if (cmdEl) selectSlashHint(cmdEl.textContent, items[_slashActiveIdx]._hint || null);
7613
7626
  return true;
7614
7627
  }
7615
7628
 
@@ -223,7 +223,6 @@ function handleSocketMessage(msg, tab, callbacks) {
223
223
  case 'rate_limits':
224
224
  case 'status_snapshot':
225
225
  case 'mcp_status':
226
- case 'mcp_profile_changed':
227
226
  case 'config_saved':
228
227
  case 'mcp_refreshed':
229
228
  case 'mcp_oauth_done':
@@ -34,7 +34,6 @@ export const CLI_INSTALL_COPY = Object.freeze({
34
34
  stepPath: 'Adding the CLI folder to your PATH…',
35
35
  stepPathAdded: 'Added {dir} to your PATH',
36
36
  bodyMissing: 'Install it with one click from {vendor}\'s official installer, or run:',
37
- bodyMissingArchive: 'Install it with one click from {vendor}\'s official release, or run:',
38
37
  bodyMissingManual: 'Install it with this command, then re-check:',
39
38
  bodyBroken: '{label} was found at {path}, but it could not be started:',
40
39
  bodyBrokenVersion: '{label} {version} was found at {path}, but it could not be started:',
@@ -50,8 +49,6 @@ export const CLI_INSTALL_COPY = Object.freeze({
50
49
  stepDownloadReceived: 'Downloading: {received} MB',
51
50
  stepVerify: 'Checking the download…',
52
51
  stepRun: 'Running the official installer…',
53
- stepExtract: 'Unpacking…',
54
- stepPlace: 'Putting it in place…',
55
52
  stepCheck: 'Checking that it starts…',
56
53
  errorOffline: 'The download server could not be reached. Check the connection and try again.',
57
54
  errorHttp: 'The download server answered with an error. Try again in a moment.',
@@ -60,7 +57,6 @@ export const CLI_INSTALL_COPY = Object.freeze({
60
57
  errorTimeout: 'The installer took too long and was stopped.',
61
58
  errorInstallerFailed: 'The official installer stopped with an error.',
62
59
  errorNotFound: 'The installer finished, but SynaBun does not find {label} afterwards.',
63
- errorUnpack: 'The download could not be unpacked, so it was removed.',
64
60
  errorDisk: 'The files could not be written. Check free space and permissions, then try again.',
65
61
  errorCancelled: 'The installation was stopped.',
66
62
  errorUnsupported: 'There is no one-click install for this system. Use the command below or the install guide.',
@@ -91,17 +87,17 @@ export const CLI_VENDORS = Object.freeze({
91
87
  export const INSTALLABLE_CLIS = Object.freeze(['claude-code', 'codex', 'opencode']);
92
88
 
93
89
  /** The steps an install reports (`{ type: 'step' }`), in the order they can come. */
94
- export const INSTALL_STEPS = Object.freeze(['start', 'download', 'verify', 'run', 'extract', 'place', 'check', 'path']);
90
+ export const INSTALL_STEPS = Object.freeze(['start', 'download', 'verify', 'run', 'check', 'path']);
95
91
 
96
92
  const STEP_COPY = Object.freeze({
97
93
  start: 'stepStart', download: 'stepDownload', verify: 'stepVerify', run: 'stepRun',
98
- extract: 'stepExtract', place: 'stepPlace', check: 'stepCheck', path: 'stepPath',
94
+ check: 'stepCheck', path: 'stepPath',
99
95
  });
100
96
 
101
97
  const ERROR_COPY = Object.freeze({
102
98
  offline: 'errorOffline', http: 'errorHttp', checksum: 'errorChecksum', interrupted: 'errorInterrupted',
103
99
  timeout: 'errorTimeout', 'installer-failed': 'errorInstallerFailed', 'not-found-after-install': 'errorNotFound',
104
- unpack: 'errorUnpack', disk: 'errorDisk', cancelled: 'errorCancelled', unsupported: 'errorUnsupported',
100
+ disk: 'errorDisk', cancelled: 'errorCancelled', unsupported: 'errorUnsupported',
105
101
  refused: 'errorRefused', connection: 'errorConnection',
106
102
  });
107
103
 
@@ -376,7 +372,7 @@ export function cliBannerView({ toolKey, label = '', info = null, forced = null,
376
372
  });
377
373
  }
378
374
  const text = offer
379
- ? t(offer.method === 'archive' ? 'bodyMissingArchive' : 'bodyMissing', { vendor: CLI_VENDORS[toolKey] || name, label: name })
375
+ ? t('bodyMissing', { vendor: CLI_VENDORS[toolKey] || name, label: name })
380
376
  : t('bodyMissingManual');
381
377
  return Object.assign(view, {
382
378
  show: true, blocked: true, kind: 'missing', tone: 'warn', icon: '!',
@@ -23,7 +23,7 @@ const TOOL_KEYS = ['claude-code', 'codex', 'opencode', 'gemini'];
23
23
  const CLI_DOC_URLS = {
24
24
  'claude-code': 'https://code.claude.com/docs/en/setup',
25
25
  'codex': 'https://learn.chatgpt.com/docs/codex/cli',
26
- 'opencode': 'https://opencode.ai/docs/',
26
+ 'opencode': 'https://opencode.ai/v2/docs',
27
27
  'gemini': 'https://github.com/google-gemini/gemini-cli',
28
28
  };
29
29
 
@@ -35,7 +35,7 @@ const CLI_DOC_URLS = {
35
35
  const CLI_INSTALL_CMDS = {
36
36
  'claude-code': { posix: 'curl -fsSL https://claude.ai/install.sh | bash', windows: 'irm https://claude.ai/install.ps1 | iex' },
37
37
  'codex': { posix: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh', windows: 'powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"' },
38
- 'opencode': { posix: 'curl -fsSL https://opencode.ai/install | bash', windows: 'npm install -g opencode-ai' },
38
+ 'opencode': { posix: 'curl -fsSL https://opencode.ai/v2/install | bash', windows: 'winget install --id Anomaly.OpenCode --exact' },
39
39
  'gemini': { posix: 'npm install -g @google/gemini-cli', windows: 'npm install -g @google/gemini-cli' },
40
40
  };
41
41
 
@@ -29,6 +29,7 @@ export const KEYS = {
29
29
  PANEL_PREFIX: 'neural-panel-',
30
30
  NODE_POS_3D: 'synabun-node-positions',
31
31
  NODE_POS_2D: 'synabun-node-positions-2d',
32
+ VIEW_VARIANT: 'neural-view-variant', // canonical memory map variant; legacy 2D preferences migrate at boot
32
33
  MAP_VIEW: 'neural-map-view', // 3D memory map options {neighbors, recency, grid}
33
34
  INTERFACE_CONFIG: 'neural-interface-config',
34
35
  EXPLORER_CAT_ORDER: 'neural-explorer-cat-order',
@@ -72,6 +73,7 @@ export const KEYS = {
72
73
  TUTORIAL_SKIPPED: 'neural-tutorial-skipped',
73
74
  TUTORIAL_STEP: 'neural-tutorial-step',
74
75
  TUTORIAL_STARTED: 'neural-tutorial-started',
76
+ TOUR_TIPS: 'neural-tour-tips',
75
77
  ONBOARDING_EXPLORE: 'neural-onboarding-explore',
76
78
  ONBOARDING_CLI: 'neural-onboarding-cli',
77
79
  ONBOARDING_MODEL: 'neural-onboarding-model',
@@ -120,7 +122,6 @@ export const DEFAULT_KEYBINDS = {
120
122
  'toggle-help': '?',
121
123
  'toggle-explorer': 'f',
122
124
  'toggle-focus-mode': 'v',
123
- 'toggle-minimap': 'm',
124
125
  'open-keybinds': 'n',
125
126
  'launch-assistant': 'a',
126
127
  'toggle-assistant': 'Ctrl+a',
@@ -146,7 +147,6 @@ export const KEYBIND_META = {
146
147
  'toggle-help': { label: 'Toggle Help', group: 'Navigation' },
147
148
  'toggle-explorer': { label: 'Toggle Explorer', group: 'Navigation' },
148
149
  'toggle-focus-mode': { label: 'Toggle Focus Mode', group: 'Navigation' },
149
- 'toggle-minimap': { label: 'Toggle Minimap', group: 'View' },
150
150
  'open-settings': { label: 'Open Settings', group: 'Settings' },
151
151
  'open-keybinds': { label: 'Open Keybinds', group: 'Settings' },
152
152
  'launch-assistant': { label: 'Assistant', group: 'Launch App', icon: 'assistant' },
@@ -0,0 +1,34 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — The app's context menu: the areas registry
3
+ // What a part of the app adds to the app's right-click menu for an element
4
+ // in it (a memory, the memory map, the focus-mode whiteboard). No imports,
5
+ // so a module as low as the whiteboard can register its area without
6
+ // pulling in the menu's actions; ui-context-menu.js reads it at every open.
7
+ // ═══════════════════════════════════════════
8
+
9
+ const _areas = [];
10
+
11
+ /**
12
+ * Add what a part of the app contributes to the menu. `match(element)` returns the area the element is in
13
+ * (`{ kind: 'memory' | 'map' | 'focus', …, act? }`, see context-menu-model.js) or null. The first area that
14
+ * matches wins, in the order they were registered. Returns a function that removes it again.
15
+ * @param {{ id?: string, match: (el: Element) => object | null }} area
16
+ */
17
+ export function registerContextMenuArea(area) {
18
+ _areas.push(area);
19
+ return () => { const i = _areas.indexOf(area); if (i >= 0) _areas.splice(i, 1); };
20
+ }
21
+
22
+ /** The area an element is in, or null. An area whose `match` throws is skipped (and named in the console). */
23
+ export function areaAt(el) {
24
+ if (!el) return null;
25
+ for (const area of _areas) {
26
+ try {
27
+ const found = area.match(el);
28
+ if (found) return found;
29
+ } catch (err) {
30
+ console.warn(`[context-menu] area ${area.id || '?'} failed:`, err);
31
+ }
32
+ }
33
+ return null;
34
+ }