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,187 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — the notice after an automatic restart: the page side
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Asks GET /api/system/crash-notice when the page starts and every time its sync
6
+ // socket connects: a page that was open through the crash is not reloaded, its
7
+ // sockets reconnect (ui-sync.js `session:info`). What crash-notice-view.js says is
8
+ // drawn in the update toast's shape (bottom right, styles.css .update-check-toast,
9
+ // .crash-notice) once the loading screen is gone, and stays until it is dismissed.
10
+ // Dismiss asks the server to drop it (POST /api/system/crash-notice/dismiss), which
11
+ // tells every other window (`sync:crash-notice:changed`); this browser also keeps
12
+ // the id it dismissed, for a server that could not remove its record. Open logs
13
+ // is Settings > App's own: the desktop app's bridge, or POST
14
+ // /api/system/app/open-folder, which only a page on this computer may ask. Never
15
+ // asked by an invite guest, and never drawn for one, whatever reaches its sync
16
+ // socket: /api/system is the owner's (the server sends `crash-notice:` to the
17
+ // owner's pages only).
18
+
19
+ import { on } from './state.js';
20
+ import { getLocale, t } from './i18n.js';
21
+ import { desktopApp } from './desktop-bridge.js';
22
+ import { isGuest } from './ui-sync.js';
23
+ import { crashNoticeView, isLoopbackHost, readCrashNotice } from './crash-notice-view.js';
24
+
25
+ const NOTICE_URL = '/api/system/crash-notice';
26
+ const OPEN_FOLDER_URL = '/api/system/app/open-folder';
27
+ const WRITE = Object.freeze({ 'Content-Type': 'application/json', 'X-SynaBun-UI': '1', Accept: 'application/json' });
28
+ const DISMISSED_KEY = 'synabun-crash-notice-dismissed';
29
+ const NOTICE_ID = 'crash-notice';
30
+
31
+ const translate = (key, params) => t(`crashNotice.${key}`, params);
32
+
33
+ let _wired = false;
34
+ let _asking = null;
35
+ let _current = null; // { id, el }
36
+ let _waiting = null; // the notice that waits for the loading screen to go
37
+ let _guest = false; // what the last `session:info` said (ui-sync.js isGuest() is the socket's own word)
38
+
39
+ const guestPage = () => _guest || isGuest();
40
+
41
+ function esc(value) {
42
+ return String(value ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
43
+ }
44
+
45
+ function dismissedHere(id) {
46
+ try { return localStorage.getItem(DISMISSED_KEY) === id; } catch { return false; }
47
+ }
48
+
49
+ function rememberDismissed(id) {
50
+ try { localStorage.setItem(DISMISSED_KEY, id); } catch { /* storage refused: the server's word is enough */ }
51
+ }
52
+
53
+ function canOpenLogs() {
54
+ if (typeof desktopApp()?.openFolder === 'function') return true;
55
+ return isLoopbackHost(location.hostname);
56
+ }
57
+
58
+ function hide() {
59
+ const current = _current;
60
+ _current = null;
61
+ if (!current) return;
62
+ current.el.classList.remove('show');
63
+ setTimeout(() => current.el.remove(), 250);
64
+ }
65
+
66
+ /** The loading screen covers the page while it starts: the notice waits for it to go. */
67
+ function whenPageShown(run) {
68
+ const overlay = document.getElementById('loading-overlay');
69
+ if (!overlay || overlay.classList.contains('hidden')) { run(); return; }
70
+ const watch = new MutationObserver(() => {
71
+ if (!overlay.classList.contains('hidden')) return;
72
+ watch.disconnect();
73
+ run();
74
+ });
75
+ watch.observe(overlay, { attributes: true, attributeFilter: ['class'] });
76
+ }
77
+
78
+ function show(view) {
79
+ hide();
80
+ document.querySelectorAll(`#${NOTICE_ID}`).forEach((el) => el.remove());
81
+ const el = document.createElement('div');
82
+ el.className = 'update-check-toast crash-notice';
83
+ el.id = NOTICE_ID;
84
+ el.setAttribute('role', 'status');
85
+ el.setAttribute('aria-label', view.label);
86
+ el.dataset.noticeId = view.id;
87
+ const buttons = view.actions.map((item) => `<button class="action-btn action-btn--ghost crash-notice-btn" type="button" data-crash-action="${esc(item.id)}" id="crash-notice-${esc(item.id)}">${esc(item.label)}</button>`).join('');
88
+ el.innerHTML = `
89
+ <div class="utc-icon utc-icon--err" aria-hidden="true">!</div>
90
+ <div class="utc-body">
91
+ <div class="utc-title" id="crash-notice-title">${esc(view.title)}</div>
92
+ <div class="utc-sub" id="crash-notice-when">${esc(view.when)}</div>
93
+ ${view.how ? `<div class="utc-sub" id="crash-notice-how">${esc(view.how)}</div>` : ''}
94
+ <div class="crash-notice-lost-title" id="crash-notice-lost-title">${esc(view.lostTitle)}</div>
95
+ <ul class="utc-list crash-notice-lost" aria-labelledby="crash-notice-lost-title">${view.lost.map((line) => `<li>${esc(line)}</li>`).join('')}</ul>
96
+ <div class="utc-sub crash-notice-note" id="crash-notice-note" role="status" hidden></div>
97
+ <div class="update-notice-actions">${buttons}</div>
98
+ </div>`;
99
+ el.querySelectorAll('[data-crash-action]').forEach((btn) => {
100
+ btn.addEventListener('click', (event) => {
101
+ event.stopPropagation();
102
+ if (btn.dataset.crashAction === 'dismiss') dismiss(view.id);
103
+ else if (btn.dataset.crashAction === 'logs') openLogs(el);
104
+ });
105
+ });
106
+ document.body.appendChild(el);
107
+ requestAnimationFrame(() => el.classList.add('show'));
108
+ _current = { id: view.id, el };
109
+ }
110
+
111
+ /** Show this notice (or none): once per crash, never one this browser dismissed, never on a guest's page. */
112
+ function present(notice) {
113
+ if (!notice || guestPage()) {
114
+ _waiting = null;
115
+ hide();
116
+ return;
117
+ }
118
+ // A notice something else took off the page is drawn again: it stays until it is dismissed.
119
+ if (_current && !_current.el.isConnected) _current = null;
120
+ if (dismissedHere(notice.id) || _current?.id === notice.id || _waiting?.id === notice.id) return;
121
+ _waiting = notice;
122
+ whenPageShown(() => {
123
+ if (_waiting !== notice) return;
124
+ _waiting = null;
125
+ if (dismissedHere(notice.id) || guestPage()) return;
126
+ show(crashNoticeView(notice, { t: translate, locale: getLocale(), canOpenLogs: canOpenLogs() }));
127
+ });
128
+ }
129
+
130
+ /** Ask the server; an older server (404) or a guest (403) has nothing to say. */
131
+ export function checkCrashNotice() {
132
+ if (guestPage()) return Promise.resolve(null);
133
+ if (_asking) return _asking;
134
+ _asking = (async () => {
135
+ try {
136
+ const res = await fetch(NOTICE_URL, { headers: { Accept: 'application/json' }, cache: 'no-store' });
137
+ if (!res.ok) return null;
138
+ const notice = readCrashNotice(await res.json());
139
+ present(notice);
140
+ return notice;
141
+ } catch {
142
+ return null;
143
+ }
144
+ })().finally(() => { _asking = null; });
145
+ return _asking;
146
+ }
147
+
148
+ async function dismiss(id) {
149
+ rememberDismissed(id);
150
+ hide();
151
+ try {
152
+ const res = await fetch(`${NOTICE_URL}/dismiss`, { method: 'POST', headers: WRITE, body: JSON.stringify({ id }) });
153
+ const next = res.ok ? readCrashNotice(await res.json()) : null;
154
+ // A newer crash came while this one was shown: that one is news.
155
+ if (next && next.id !== id) present(next);
156
+ } catch { /* the server is away: this browser keeps it dismissed */ }
157
+ }
158
+
159
+ async function openLogs(el) {
160
+ const note = el.querySelector('#crash-notice-note');
161
+ const bridge = desktopApp();
162
+ let ok = false;
163
+ if (typeof bridge?.openFolder === 'function') {
164
+ try { ok = !!(await bridge.openFolder('logs'))?.ok; } catch { ok = false; }
165
+ } else {
166
+ try {
167
+ const res = await fetch(OPEN_FOLDER_URL, { method: 'POST', headers: WRITE, body: JSON.stringify({ which: 'logs' }) });
168
+ ok = res.ok;
169
+ } catch { ok = false; }
170
+ }
171
+ if (!note) return;
172
+ note.hidden = ok;
173
+ note.textContent = ok ? '' : translate('openLogsFailed');
174
+ }
175
+
176
+ /** Wire the notice once: ask now, and again every time the sync socket connects. */
177
+ export function initCrashNotice() {
178
+ if (_wired) return;
179
+ _wired = true;
180
+ on('session:info', (info) => {
181
+ _guest = !!info?.isGuest;
182
+ if (_guest) present(null);
183
+ else checkCrashNotice();
184
+ });
185
+ on('sync:crash-notice:changed', (message) => present(readCrashNotice(message)));
186
+ checkCrashNotice();
187
+ }
@@ -14,7 +14,8 @@ import { storage } from './storage.js';
14
14
  import { catColor } from './colors.js';
15
15
  import { truncate, formatMemoryContent, exportMemoryAsMarkdown, rafThrottle, escapeHtml } from './utils.js';
16
16
  import { t, getLocale } from './i18n.js';
17
- import { savePanelLayout, restorePanelLayout } from './ui-panels.js';
17
+ import { savePanelLayout, restorePanelLayout, clampPanelToFrame } from './ui-panels.js';
18
+ import { frameRect } from './app-frame.js';
18
19
  import { sendSync as _sendSync, hasPermission, isGuest } from './ui-sync.js';
19
20
 
20
21
  // Wrap sendSync — skip card events when guest lacks cards permission
@@ -546,9 +547,11 @@ function bringToFront(cardEl) {
546
547
  function getNextCardPosition() {
547
548
  const count = _openCards.size;
548
549
  const offset = count * 30;
550
+ // Cascading from 200 / 80 px of the window (192 / 24 px into the app frame), inside the frame.
551
+ const f = frameRect();
549
552
  return {
550
- left: Math.min(window.innerWidth - 540, 200 + (offset % 300)) + 'px',
551
- top: Math.min(window.innerHeight - 300, 80 + (offset % 200)) + 'px',
553
+ left: Math.max(f.left, Math.min(f.right - 540, f.left + 192 + (offset % 300))) + 'px',
554
+ top: Math.max(f.top, Math.min(f.bottom - 300, f.top + 24 + (offset % 200))) + 'px',
552
555
  };
553
556
  }
554
557
 
@@ -587,6 +590,13 @@ export function getOpenCardCount() {
587
590
  return _openCards.size;
588
591
  }
589
592
 
593
+ /** Press an open card's Linked button from outside it (the context menu), unless it is on already. */
594
+ export function focusLinkedMemories(memoryId) {
595
+ const card = _openCards.get(memoryId);
596
+ if (!card || state.focusedNodeId === memoryId) return;
597
+ q(card.el, 'focus-btn')?.click();
598
+ }
599
+
590
600
  export function restoreOpenCards() {
591
601
  const raw = storage.getItem(KEYS.OPEN_CARDS);
592
602
  if (!raw) return;
@@ -1062,6 +1072,8 @@ export function openMemoryCard(node, savedPosition) {
1062
1072
  // ── Add to DOM and state ──
1063
1073
  _openCards.set(node.id, cardState);
1064
1074
  document.body.appendChild(card);
1075
+ // A position saved in another window size, or with the explorers closed, comes back inside the app frame.
1076
+ if (savedPosition) clampPanelToFrame(card);
1065
1077
  bringToFront(card);
1066
1078
 
1067
1079
  // Trigger entrance animation on next frame
@@ -8,6 +8,7 @@ import { emit, on } from './state.js';
8
8
  import { fetchImages, toggleImageFavorite, deleteImage } from './api.js';
9
9
  import { addImageToWhiteboard } from './ui-whiteboard.js';
10
10
  import { escapeHtml } from './utils.js';
11
+ import { centeredInFrame } from './app-frame.js';
11
12
 
12
13
  const $ = (id) => document.getElementById(id);
13
14
 
@@ -23,33 +24,33 @@ let _lightboxImg = null; // filename currently in lightbox
23
24
  let isVisible = false;
24
25
 
25
26
  // ── Drag state ──
26
- let _dragging = false;
27
- let _dragOff = { x: 0, y: 0 };
28
- let _dragRaf = 0;
29
- let _pendingDrag = null;
30
-
31
- function applyDragPosition() {
32
- _dragRaf = 0;
33
- if (!_panel || !_pendingDrag) return;
34
- _panel.style.left = _pendingDrag.left + 'px';
35
- _panel.style.top = _pendingDrag.top + 'px';
36
- }
37
-
38
- function scheduleDragPosition(left, top) {
39
- _pendingDrag = { left, top };
40
- if (!_dragRaf) _dragRaf = requestAnimationFrame(applyDragPosition);
41
- }
42
-
43
- function flushDragPosition() {
44
- if (_dragRaf) {
45
- cancelAnimationFrame(_dragRaf);
46
- _dragRaf = 0;
47
- }
48
- applyDragPosition();
49
- }
50
-
51
- // ── Icons ──
52
- const ICON_CLOSE = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 4l8 8M12 4l-8 8"/></svg>';
27
+ let _dragging = false;
28
+ let _dragOff = { x: 0, y: 0 };
29
+ let _dragRaf = 0;
30
+ let _pendingDrag = null;
31
+
32
+ function applyDragPosition() {
33
+ _dragRaf = 0;
34
+ if (!_panel || !_pendingDrag) return;
35
+ _panel.style.left = _pendingDrag.left + 'px';
36
+ _panel.style.top = _pendingDrag.top + 'px';
37
+ }
38
+
39
+ function scheduleDragPosition(left, top) {
40
+ _pendingDrag = { left, top };
41
+ if (!_dragRaf) _dragRaf = requestAnimationFrame(applyDragPosition);
42
+ }
43
+
44
+ function flushDragPosition() {
45
+ if (_dragRaf) {
46
+ cancelAnimationFrame(_dragRaf);
47
+ _dragRaf = 0;
48
+ }
49
+ applyDragPosition();
50
+ }
51
+
52
+ // ── Icons ──
53
+ const ICON_CLOSE = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 4l8 8M12 4l-8 8"/></svg>';
53
54
  const ICON_REFRESH = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 8a5.5 5.5 0 019.2-4M13.5 8a5.5 5.5 0 01-9.2 4"/><path d="M11.5 2v2.5H14M4.5 14v-2.5H2"/></svg>';
54
55
  const ICON_HEART = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8 13.7C4.3 11 2 8.7 2 6.3A3.3 3.3 0 018 4.6a3.3 3.3 0 016 1.7c0 2.4-2.3 4.7-6 7.4z"/></svg>';
55
56
  const ICON_HEART_FILL = '<svg viewBox="0 0 16 16" fill="currentColor" stroke="none"><path d="M8 13.7C4.3 11 2 8.7 2 6.3A3.3 3.3 0 018 4.6a3.3 3.3 0 016 1.7c0 2.4-2.3 4.7-6 7.4z"/></svg>';
@@ -388,13 +389,13 @@ function wirePanel() {
388
389
  header.addEventListener('mousedown', (e) => {
389
390
  if (e.target.closest('button')) return;
390
391
  _dragging = true;
391
- _panel.classList.add('dragging');
392
- const rect = _panel.getBoundingClientRect();
393
- _dragOff = { x: e.clientX - rect.left, y: e.clientY - rect.top };
394
- _pendingDrag = null;
395
- e.preventDefault();
396
- });
397
- }
392
+ _panel.classList.add('dragging');
393
+ const rect = _panel.getBoundingClientRect();
394
+ _dragOff = { x: e.clientX - rect.left, y: e.clientY - rect.top };
395
+ _pendingDrag = null;
396
+ e.preventDefault();
397
+ });
398
+ }
398
399
 
399
400
  // Keyboard in lightbox
400
401
  document.addEventListener('keydown', handleKey);
@@ -425,24 +426,26 @@ function openPanel() {
425
426
  _panel.innerHTML = buildPanelHTML();
426
427
  document.body.appendChild(_panel);
427
428
 
428
- _panel.style.left = Math.max(20, (window.innerWidth - 780) / 2) + 'px';
429
- _panel.style.top = Math.max(48, (window.innerHeight - 560) / 2) + 'px';
429
+ // Centred in the app frame
430
+ const at = centeredInFrame(780, 560);
431
+ _panel.style.left = at.left + 'px';
432
+ _panel.style.top = at.top + 'px';
430
433
 
431
434
  wirePanel();
432
435
  isVisible = true;
433
436
  refreshImages();
434
437
 
435
- // Global mouse handlers for drag
436
- const onMouseMove = (e) => {
437
- if (!_dragging) return;
438
- scheduleDragPosition(e.clientX - _dragOff.x, e.clientY - _dragOff.y);
439
- };
440
- const onMouseUp = () => {
441
- if (!_dragging) return;
442
- _dragging = false;
443
- flushDragPosition();
444
- if (_panel) _panel.classList.remove('dragging');
445
- };
438
+ // Global mouse handlers for drag
439
+ const onMouseMove = (e) => {
440
+ if (!_dragging) return;
441
+ scheduleDragPosition(e.clientX - _dragOff.x, e.clientY - _dragOff.y);
442
+ };
443
+ const onMouseUp = () => {
444
+ if (!_dragging) return;
445
+ _dragging = false;
446
+ flushDragPosition();
447
+ if (_panel) _panel.classList.remove('dragging');
448
+ };
446
449
  document.addEventListener('mousemove', onMouseMove);
447
450
  document.addEventListener('mouseup', onMouseUp);
448
451
 
@@ -26,7 +26,7 @@ let _recording = null; // { actionId, originalCombo } when recording
26
26
  let _pendingBindings = null; // working copy during modal editing
27
27
  let _modalOpen = false;
28
28
  // Actions that were removed ("toggle-toolbar" was "Hide all toolbars"): a saved key for one is dropped on load.
29
- const RETIRED_ACTIONS = ['toggle-toolbar'];
29
+ const RETIRED_ACTIONS = ['toggle-toolbar', 'toggle-minimap'];
30
30
 
31
31
  // ── Combo building ──
32
32
 
@@ -94,6 +94,31 @@ export function getBinding(actionId) {
94
94
  return _bindings[actionId] || null;
95
95
  }
96
96
 
97
+ /** The key an action is bound to as a menu shows it, or '' while it has none (the context menu, read at each open). */
98
+ export function getShortcutLabel(actionId) {
99
+ const combo = _bindings[actionId];
100
+ return combo ? comboToDisplay(combo) : '';
101
+ }
102
+
103
+ /** Whether a module registered an action with this id. */
104
+ export function hasAction(actionId) {
105
+ return _actions.has(actionId);
106
+ }
107
+
108
+ /** The action a key press is bound to, or null (a key the user bound stays that action, Shift+F10 included). */
109
+ export function actionForKey(e) {
110
+ const combo = buildCombo(e);
111
+ return (combo && _reverseMap.get(combo)) || null;
112
+ }
113
+
114
+ /** Run a registered action as its key would. False when nothing registered it. */
115
+ export function runAction(actionId) {
116
+ const handler = _actions.get(actionId);
117
+ if (!handler) return false;
118
+ handler();
119
+ return true;
120
+ }
121
+
97
122
  // ── Central dispatcher ──
98
123
 
99
124
  function centralKeyHandler(e) {
@@ -12,12 +12,12 @@ import { openHelp } from './ui-help.js';
12
12
  import { armPwaInstallHandoff, initPwaInstall, installGuideUrl, isStandalone, requestPwaInstall } from './pwa-install.js';
13
13
  import { registerAction } from './ui-keybinds.js';
14
14
  import { isGuest, hasPermission } from './ui-sync.js';
15
- import { sendToPanel } from './ui-claude-panel.js';
16
15
  import { forceCheckToolUpdates, showUpdateToast, TOOL_LABELS } from './ui-tool-updates.js';
17
16
  import { forceCheckSynabunUpdate, openSynabunUpdateModal, synabunUpdateSourceLabel } from './ui-update.js';
18
17
  import { requestServerRestart } from './ui-loading.js';
19
18
  import { desktopBridge, isDesktopApp } from './desktop-bridge.js';
20
19
  import { t } from './i18n.js';
20
+ import { frameRect } from './app-frame.js';
21
21
 
22
22
  const $ = (id) => document.getElementById(id);
23
23
 
@@ -221,7 +221,7 @@ function fitDropdown(item) {
221
221
  dropdown.style.maxHeight = '';
222
222
  dropdown.classList.remove('menubar-dropdown--scroll');
223
223
  const top = item.getBoundingClientRect().bottom + 14;
224
- const room = Math.floor(window.innerHeight - top - 12);
224
+ const room = Math.floor(frameRect().bottom - top - 12); // down to the app frame's bottom edge
225
225
  if (room > 120 && dropdown.scrollHeight > room) {
226
226
  dropdown.style.maxHeight = room + 'px';
227
227
  dropdown.classList.add('menubar-dropdown--scroll');
@@ -278,12 +278,6 @@ function wireSubmenus() {
278
278
  sync();
279
279
  });
280
280
  window.addEventListener('resize', () => { if (openSub) placeSubmenu(openSub.parent, openSub.panel); });
281
- // The tour shows a submenu by name (ui-tutorial.js showStage); null closes it.
282
- on('menubar:submenu', (name) => {
283
- const row = name ? [...SUBMENUS.keys()].find(r => r.dataset.submenu === name) : null;
284
- if (row && !row.hidden) openSubmenu(row);
285
- else closeSubmenu();
286
- });
287
281
  }
288
282
 
289
283
  function openSubmenu(row, { focusFirst = false } = {}) {
@@ -377,8 +371,9 @@ function submenuEntries(panel) {
377
371
  .filter(el => !el.hidden && !el.classList.contains('disabled') && getComputedStyle(el).display !== 'none');
378
372
  }
379
373
 
380
- // Right of the dropdown with its first entry level with the row; on the left when the window has no room
381
- // on the right, and kept inside the window. The dropdown's laid-out place (offsetTop/Left) is what its
374
+ // Right of the dropdown with its first entry level with the row; on the left when the app frame has no room
375
+ // on the right, and kept inside its right and bottom edges (the menu hangs from the bar, which is outside the
376
+ // frame, so its left stays the window's). The dropdown's laid-out place (offsetTop/Left) is what its
382
377
  // drop-in animation does not move, so a submenu opened together with the menu (the tour) lines up too.
383
378
  function placeSubmenu(row, panel) {
384
379
  const dropdown = row.parentElement, host = panel.parentElement;
@@ -386,9 +381,10 @@ function placeSubmenu(row, panel) {
386
381
  const first = submenuEntries(panel)[0];
387
382
  const lead = first ? first.getBoundingClientRect().top - panelBox.top : 0;
388
383
  const top = dropdown.offsetTop + row.getBoundingClientRect().top - dropdown.getBoundingClientRect().top - lead;
384
+ const f = frameRect();
389
385
  let left = dropdown.offsetLeft + dropdown.offsetWidth + SUBMENU_GAP;
390
- if (hostBox.left + left + panelBox.width > innerWidth - VIEWPORT_MARGIN) left = dropdown.offsetLeft - SUBMENU_GAP - panelBox.width;
391
- const maxTop = innerHeight - VIEWPORT_MARGIN - panelBox.height - hostBox.top;
386
+ if (hostBox.left + left + panelBox.width > f.right - VIEWPORT_MARGIN) left = dropdown.offsetLeft - SUBMENU_GAP - panelBox.width;
387
+ const maxTop = f.bottom - VIEWPORT_MARGIN - panelBox.height - hostBox.top;
392
388
  panel.style.left = `${Math.max(VIEWPORT_MARGIN - hostBox.left, left)}px`;
393
389
  panel.style.top = `${Math.max(VIEWPORT_MARGIN - hostBox.top, Math.min(top, maxTop))}px`;
394
390
  }
@@ -611,7 +607,6 @@ function wireTerminalMenu() {
611
607
  'menu-terminal-discord': () => emit('browser:open', { url: 'https://discord.com/app' }),
612
608
  'menu-terminal-x': () => emit('browser:open', { url: 'https://x.com' }),
613
609
  'menu-terminal-whatsapp': () => emit('browser:open', { url: 'https://web.whatsapp.com' }),
614
- 'menu-app-leonardo': () => { emit('browser:open', { url: 'https://app.leonardo.ai' }); emit('leonardo:launch'); },
615
610
  'menu-open-styleguide': () => emit('styleguide:open'),
616
611
  'menu-command-runner': () => emit('command-runner:open'),
617
612
  'menu-terminal-link': () => emit('link:toggle'),
@@ -645,11 +640,6 @@ function wireTerminalMenu() {
645
640
  if (el) el.addEventListener('click', () => { closeAll(); handler(); });
646
641
  });
647
642
 
648
- // Leonardo.AI — open panel with /leonardo skill after browser launches
649
- on('leonardo:launch', () => {
650
- setTimeout(() => sendToPanel('/leonardo', { newTab: true, tabLabel: 'Leonardo.AI', autoSubmit: true }), 500);
651
- });
652
-
653
643
  // Keyboard shortcuts: toggle terminal (via central keybinds)
654
644
  registerAction('toggle-terminal', () => emit('terminal:toggle'));
655
645
  registerAction('toggle-terminal-alt', () => emit('terminal:toggle'));
@@ -3,6 +3,11 @@ export function isNewerRunDescriptor(next, current, terminalStatuses) {
3
3
  const nextTerminal = terminalStatuses.has(next.status);
4
4
  const currentTerminal = terminalStatuses.has(current.status);
5
5
  if (currentTerminal !== nextTerminal) return nextTerminal;
6
+ // A run resumed after a server restart (`resumes` only grows) is newer whatever its version: a crash can lose the
7
+ // last ledger writes, and with them versions a page already saw.
8
+ const nextResumes = Number(next.resumes) || 0;
9
+ const currentResumes = Number(current.resumes) || 0;
10
+ if (nextResumes !== currentResumes) return nextResumes > currentResumes;
6
11
  const nextVersion = Number(next.version) || 0;
7
12
  const currentVersion = Number(current.version) || 0;
8
13
  if (nextVersion !== currentVersion) return nextVersion > currentVersion;
@@ -13,7 +13,10 @@ import {
13
13
  const DISMISSED_KEY = 'synabun-native-loop-dismissed';
14
14
  const CLAIM_TTL_MS = 90_000;
15
15
  const TERMINAL_RESTORE_LIMIT = 10;
16
+ // 'suspended' is not among them: a dispatched worker a server restart cut off keeps its tab until the next start
17
+ // resumes it (or ends it as interrupted).
16
18
  const TERMINAL_STATUSES = new Set(['completed', 'failed', 'stopped', 'interrupted']);
19
+ const LIVE_STATUSES = new Set(['starting', 'running', 'suspended']);
17
20
  const _windowId = nativeLoopWindowId;
18
21
 
19
22
  const _attached = new Map();
@@ -355,7 +358,7 @@ async function reconcileRuns() {
355
358
  const newestFirst = (data.runs || [])
356
359
  .filter(hasIdentity)
357
360
  .sort((a, b) => String(b.updatedAt || b.startedAt || '').localeCompare(String(a.updatedAt || a.startedAt || '')));
358
- const activeRuns = newestFirst.filter((run) => run.status === 'starting' || run.status === 'running');
361
+ const activeRuns = newestFirst.filter((run) => LIVE_STATUSES.has(run.status));
359
362
  const terminalRuns = newestFirst.filter((run) => {
360
363
  if (!TERMINAL_STATUSES.has(run.status)) return false;
361
364
  if (new Date(run.updatedAt || run.startedAt || 0).getTime() < cutoff) return false;
@@ -403,6 +406,9 @@ export function initNativeLoopRouter() {
403
406
  on('sync:sidepanel:run-completed', route);
404
407
  on('sync:sidepanel:run-failed', route);
405
408
  on('sync:sidepanel:run-stopped', route);
409
+ // The sync socket (re)connected (ui-sync.js 'connected'): after a server restart the runs are read again at once,
410
+ // not at the next 30 s heartbeat, so a tab whose run was suspended sees it resume (or end) as soon as it can.
411
+ on('session:info', () => { scheduleReconcile(); });
406
412
  on('sync:sidepanel:provider-event', (message) => {
407
413
  // An OpenCode worker's panel streams from its own serve: the sync copy of its tool calls is the Assistant's.
408
414
  if (message?.event?.provider === 'opencode') return;
@@ -1,12 +1,11 @@
1
1
  import { initDetachedAgentSessions } from './ui-sidepanel-sessions.js';
2
2
  // ═══════════════════════════════════════════
3
3
  // SynaBun Neural Interface — Navbar
4
- // Initializes the 2D/3D view toggle, right-side icon buttons, and menubar action buttons.
4
+ // Initializes right-side icon buttons and menubar action buttons. The memory map is 3D only.
5
5
  // Menu items (View, Graph, Bookmarks, Layouts) are handled by ui-menubar.js.
6
6
  // ═══════════════════════════════════════════
7
7
 
8
8
  import { state, emit, on } from './state.js';
9
- import { getVariant } from './registry.js';
10
9
  import { KEYS } from './constants.js';
11
10
  import { storage } from './storage.js';
12
11
  import { t } from './i18n.js';
@@ -23,6 +22,7 @@ import { toggleImageGallery } from './ui-image-gallery.js';
23
22
  import { toggleScheduleQueue } from './ui-schedule-queue.js';
24
23
  import { initUpdate } from './ui-update.js';
25
24
  import { initToolUpdates } from './ui-tool-updates.js';
25
+ import { initCrashNotice } from './ui-crash-notice.js';
26
26
  import { initSidepanelTrayPlaceholders } from './ui-sidepanel-tray.js';
27
27
  import { initDesktopTitleBar } from './desktop-titlebar.js';
28
28
 
@@ -30,28 +30,6 @@ const $ = (id) => document.getElementById(id);
30
30
 
31
31
  export function initNavbar() {
32
32
  initDetachedAgentSessions();
33
- const variant = getVariant() || '3d';
34
-
35
- // ── View toggle ──
36
- const nav2d = $('nav-2d-link');
37
- const nav3d = $('nav-3d-link');
38
-
39
- const activeLink = variant === '3d' ? nav3d : nav2d;
40
- const inactiveLink = variant === '3d' ? nav2d : nav3d;
41
-
42
- if (activeLink) {
43
- activeLink.addEventListener('click', (e) => e.preventDefault());
44
- }
45
- if (inactiveLink) {
46
- inactiveLink.addEventListener('click', (e) => {
47
- e.preventDefault();
48
- if (state.selectedNodeId) {
49
- sessionStorage.setItem(KEYS.SELECTED_SWITCH, state.selectedNodeId);
50
- }
51
- window.location.href = inactiveLink.getAttribute('href');
52
- });
53
- }
54
-
55
33
  // ── Claude panel toggle (topright workspace toolbar) ──
56
34
  const assistantPanelBtn = $('topright-assistant-panel-btn');
57
35
  const claudePanelBtn = $('topright-claude-panel-btn');
@@ -380,4 +358,6 @@ export function initNavbar() {
380
358
  // ── Update alert ──
381
359
  initUpdate();
382
360
  initToolUpdates();
361
+ // ── The notice after an automatic restart (the server crashed and its supervisor started it again) ──
362
+ initCrashNotice();
383
363
  }