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
@@ -23,7 +23,7 @@ import { createStickyScrollController } from './ui-scroll-follow.js';
23
23
  import { requestGeneratedSessionTitle } from './session-title.js';
24
24
  import { wireFileLinks } from './ui-file-links.js';
25
25
  import { WORKER_END_CLASS, backToAssistant, buildWorkerEndCard, endWorkerRunOnClose, forgetWorkerEnd, trackWorkerEnd, updateWorkerEndBusy } from './ui-worker-end.js';
26
- import { isDispatchedRun, isEndOfRunAbort } from './worker-end.js';
26
+ import { isDispatchedRun, isEndOfRunAbort, workerEndView } from './worker-end.js';
27
27
  import { runAwaitsAnswer } from './sidepanel-session-status.js';
28
28
  // ── cp/ feature modules (SDK-engine CLI parity: diffs, agents, bash, perms, statusline) ──
29
29
  import { setCpCtx } from './cp/cp-ctx.js';
@@ -2600,7 +2600,7 @@ function injectStyles() {
2600
2600
  border: 0.5px solid rgba(255,255,255,0.05);
2601
2601
  border-radius: 6px;
2602
2602
  font-size: 11px; color: rgba(255,255,255,0.7);
2603
- cursor: grab;
2603
+ cursor: move;
2604
2604
  transition: background 0.15s, border-color 0.15s;
2605
2605
  }
2606
2606
  .cp-queue-item:hover {
@@ -2614,7 +2614,7 @@ function injectStyles() {
2614
2614
  background: rgba(120,180,255,0.08);
2615
2615
  }
2616
2616
  .cp-queue-drag {
2617
- color: rgba(255,255,255,0.2); cursor: grab;
2617
+ color: rgba(255,255,255,0.2); cursor: move;
2618
2618
  font-size: 10px; user-select: none; flex-shrink: 0;
2619
2619
  }
2620
2620
  .cp-queue-text {
@@ -5442,7 +5442,8 @@ function _processTabMsg(tab, msg) {
5442
5442
  if (expectRunning && !(tab.sendStartedAt > tab._reattachSentAt)) {
5443
5443
  console.log('[claude-panel] No orphan to reattach — the turn ended while disconnected');
5444
5444
  finishTab(tab, true);
5445
- appendStatus(tab, 'Connection lost — this turn did not survive the disconnect.');
5445
+ // A dispatched worker's session says what became of its run (resumed, or how it ended), not only this.
5446
+ _lostTurnLine(tab).then((line) => { if (!tab.closed) appendStatus(tab, line); });
5446
5447
  }
5447
5448
  // Prompts queued behind a Compact when the socket closed: nothing says it finished.
5448
5449
  if (tab._queueWaits === 'session') _compactQueue.after(tab, 'lost');
@@ -10452,8 +10453,11 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10452
10453
  }
10453
10454
  if (!tab) return { ok: false, reason: 'tab_limit' };
10454
10455
  const running = run.status === 'starting' || run.status === 'running';
10456
+ // A dispatched worker a server restart cut off: suspended until the next start resumes it as the same run
10457
+ // (lib/assistant-dispatch.js). It still owns this tab; nothing of its cut-off turn runs any more.
10458
+ const suspended = run.status === 'suspended';
10455
10459
  tab.automationRunId = run.runId;
10456
- tab.automationActive = running;
10460
+ tab.automationActive = running || suspended;
10457
10461
  tab.automationOwnerId = nativeLoopWindowId;
10458
10462
  tab.releasedRunId = null;
10459
10463
  tab.sdkMode = true;
@@ -10467,6 +10471,12 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10467
10471
  setRunning(tab, running);
10468
10472
  updatePillLabel(tab);
10469
10473
  tab._automationIteration = run.currentIteration;
10474
+ // Resumed after a server restart: what the run did while this window was away (the continuation prompt, its first
10475
+ // steps: events sent before the page reconnected) is in its transcript, not in this tab, which reads it again.
10476
+ // That holds for a run that resumed and ended while this window was away too.
10477
+ const resumes = Number(run.resumes) || 0;
10478
+ const resumedSinceSeen = resumes > (tab._automationResumes || 0);
10479
+ if (tab._automationHydrated && resumedSinceSeen) tab._automationHydrated = false;
10470
10480
  if (!tab._automationHydrated) {
10471
10481
  tab._automationHydrated = true;
10472
10482
  if (tab.messagesEl) tab.messagesEl.innerHTML = '';
@@ -10476,6 +10486,10 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10476
10486
  return { ok: false, reason: 'dismissed' };
10477
10487
  }
10478
10488
  }
10489
+ if (running && resumedSinceSeen && tab._automationResumes !== undefined) appendStatus(tab, 'Resumed after the server restart — the run continues here.');
10490
+ tab._automationResumes = Math.max(tab._automationResumes || 0, resumes);
10491
+ if (suspended) _showRunSuspended(tab, run);
10492
+ else tab._automationSuspendedAt = null;
10479
10493
  // (A dispatched worker's failure, with its reason, is on its end card.)
10480
10494
  if (run.status === 'failed' && run.error && !isDispatchedRun(run) && tab._automationLifecycleError !== run.error) {
10481
10495
  tab._automationLifecycleError = run.error;
@@ -10527,6 +10541,64 @@ function hasLiveBackgroundWork(tab) {
10527
10541
  return false;
10528
10542
  }
10529
10543
 
10544
+ const LOST_TURN_LINE = 'Connection lost — this turn did not survive the disconnect.';
10545
+ // What a tab says when the turn it showed did not survive a lost connection. When its session is a dispatched worker's
10546
+ // (a tab opened on that session), the line says what became of the run: resumed after a server restart (in its own
10547
+ // tab), or how it ended, with its reason. A tab the user continued by hand after its run ended keeps the generic line:
10548
+ // the turn that was cut off was the user's own.
10549
+ async function _lostTurnLine(tab) {
10550
+ if (!tab?.sessionId || tab.releasedRunId) return LOST_TURN_LINE;
10551
+ try {
10552
+ const res = await fetch('/api/sidepanel-runs');
10553
+ const runs = res.ok ? ((await res.json())?.runs || []) : [];
10554
+ const run = runs.find((item) => item?.providerSessionId === tab.sessionId && isDispatchedRun(item));
10555
+ if (!run) return LOST_TURN_LINE;
10556
+ if (run.status === 'suspended') return 'SynaBun restarted — this worker run is being resumed in its own tab.';
10557
+ if (run.status === 'starting' || run.status === 'running') {
10558
+ return Number(run.resumes) > 0 ? 'SynaBun restarted — this worker run was resumed and continues in its own tab.' : LOST_TURN_LINE;
10559
+ }
10560
+ let dispatch = null;
10561
+ try {
10562
+ const reply = await fetch(`/api/assistant/runs/${encodeURIComponent(run.runId)}`);
10563
+ if (reply.ok) dispatch = (await reply.json())?.run || null;
10564
+ } catch {}
10565
+ const view = workerEndView(run, dispatch);
10566
+ return view ? `This session's worker run ended: ${view.title}${view.detail ? ` — ${view.detail}` : ''}` : LOST_TURN_LINE;
10567
+ } catch {
10568
+ return LOST_TURN_LINE;
10569
+ }
10570
+ }
10571
+
10572
+ // A run tab whose run the server suspended as it restarted (status 'suspended'): its turn was cut off, so the tab
10573
+ // stops looking busy (spinner, a tool row and its timer, background shells: all went with the CLI) and says the run
10574
+ // resumes. Once per suspension.
10575
+ function _showRunSuspended(tab, run) {
10576
+ const key = run?.suspendedAt || 'suspended';
10577
+ if (tab._automationSuspendedAt === key) return;
10578
+ tab._automationSuspendedAt = key;
10579
+ finishTab(tab, true);
10580
+ _closeCutOffTools(tab);
10581
+ appendStatus(tab, 'SynaBun restarted — resuming this run…');
10582
+ }
10583
+
10584
+ // Every tool call of the tab still waiting for its result is closed as cut off, through the path a result takes (the
10585
+ // card's state, its timer, an agent card), and the background work the tab showed is over.
10586
+ function _closeCutOffTools(tab) {
10587
+ const $msgs = tab?.messagesEl;
10588
+ if (!$msgs) return;
10589
+ for (const card of $msgs.querySelectorAll('.tool-card[data-tool-id]')) {
10590
+ if (card.classList.contains('tool-ok') || card.classList.contains('tool-error') || card.dataset.resolved === '1') continue;
10591
+ if (!card.dataset.toolId) continue;
10592
+ try { updateToolResult(tab, { type: 'tool_result', tool_use_id: card.dataset.toolId, is_error: true, content: 'Cut off by the server restart.' }); } catch {}
10593
+ }
10594
+ let shells = false;
10595
+ for (const task of tab.bgTasks?.values?.() || []) {
10596
+ if (task?.status === 'running') { task.status = 'stopped'; shells = true; }
10597
+ }
10598
+ if (shells) { try { renderBgTray(tab); } catch {} }
10599
+ _setBackgroundWork(tab, []);
10600
+ }
10601
+
10530
10602
  // The end of a worker the Assistant dispatched (ui-worker-end.js): one card,
10531
10603
  // the last row of its transcript. Its button closes this tab the way the X
10532
10604
  // does (a warm worker that finished its task is completed, one still at work
@@ -2,6 +2,7 @@
2
2
  import { emit, on } from './state.js';
3
3
  import { KEYS, COLOR_PALETTE } from './constants.js';
4
4
  import { storage } from './storage.js';
5
+ import { frameRect, clampBox } from './app-frame.js';
5
6
  import { commandLaunchPayload, selectCommandGroups, isCommandLaunchAvailable } from './command-runner-model.js';
6
7
 
7
8
  const $ = id => document.getElementById(id);
@@ -35,9 +36,11 @@ export function initCommandRunner() { return on('command-runner:open', openPanel
35
36
  function clampPanel() {
36
37
  if (!_panel) return;
37
38
  const width = _panel.offsetWidth, height = _panel.offsetHeight;
38
- const top = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--navbar-height')) || 48;
39
- _panel.style.left = Math.max(8, Math.min(parseFloat(_panel.style.left) || 8, window.innerWidth - width - 8)) + 'px';
40
- _panel.style.top = Math.max(top + 8, Math.min(parseFloat(_panel.style.top) || top + 8, window.innerHeight - height - 8)) + 'px';
39
+ // Inside the app frame (8px from the window and the bar, as before, and clear of an open explorer)
40
+ const f = frameRect();
41
+ const at = clampBox(parseFloat(_panel.style.left) || f.left, parseFloat(_panel.style.top) || f.top, width, height, f);
42
+ _panel.style.left = at.x + 'px';
43
+ _panel.style.top = at.y + 'px';
41
44
  }
42
45
  function openPanel() {
43
46
  if (_panel) { $('cr-search')?.focus(); return; }
@@ -0,0 +1,295 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — The app's right-click menu
3
+ // A right-click anywhere that has no menu of its own opens the app's menu
4
+ // instead of the browser's: page actions, then what fits what was clicked
5
+ // (selected text, a link, an image, a memory, the memory map, the 2D graph,
6
+ // the focus-mode whiteboard), then the app's panels and tools. The
7
+ // ContextMenu key and Shift+F10 open it at the focused element.
8
+ //
9
+ // The rule: the app's menu opens unless the element under the pointer shows
10
+ // a menu of its own for that very click. Such an element takes the event
11
+ // (preventDefault) only when it opens its menu: the terminal's viewport, a
12
+ // File Explorer row or its empty tree, an editor tab, a 2D graph card, an
13
+ // Automation Studio item or folder, the map's canvas (OrbitControls takes
14
+ // every right-click there for right-drag pan; the map opens this menu itself
15
+ // when the right button comes up where it went down). An area that only
16
+ // suppressed the browser's menu would leave the user with nothing, so none
17
+ // does. NATIVE_CONTEXTMENU_SELECTORS names exact elements, never a whole
18
+ // screen or panel. Fields keep the browser's menu (spelling, paste), and so
19
+ // does any right-click with Shift held.
20
+ //
21
+ // The items are context-menu-model.js; the component is context-menu.js;
22
+ // the areas registry is context-menu-areas.js. Every action is a function the
23
+ // app already has: a registered keybind action, a bus event the menubar
24
+ // emits, a panel's button, sendToPanel, the memory card's functions.
25
+ // ═══════════════════════════════════════════
26
+
27
+ import { openContextMenu, isContextMenuOpen } from './context-menu.js';
28
+ import { isEditableTarget, isMenuKey, nativeMenuReason, selectionPayload, usableImage, usableLink } from './context-menu-logic.js';
29
+ import { buildAppMenu } from './context-menu-model.js';
30
+ import { areaAt, registerContextMenuArea } from './context-menu-areas.js';
31
+ import { t } from './i18n.js';
32
+ import { state, emit } from './state.js';
33
+ import { actionForKey, getShortcutLabel, hasAction, runAction } from './ui-keybinds.js';
34
+ import { isGuest, hasPermission } from './ui-sync.js';
35
+ import { sendToPanel } from './ui-claude-panel.js';
36
+ import { closeMemoryCard, focusLinkedMemories, getOpenCardsSnapshot } from './ui-detail.js';
37
+ import { formatMemoryForCLI } from './ui-terminal.js';
38
+ import { fetchMemory } from './api.js';
39
+
40
+ /**
41
+ * Where the browser's menu stays: the exact elements with a menu of their own (also for a click their handler did
42
+ * not take, such as Shift+F10 on them or a moment before they are wired), the other menus themselves, code editors,
43
+ * and what is not SynaBun's page or sits above the menu. Never a whole screen or panel: the rest of a panel gets the
44
+ * app's menu. Add `data-native-contextmenu` to an element to keep the browser's menu there.
45
+ */
46
+ export const NATIVE_CONTEXTMENU_SELECTORS = [
47
+ '[data-native-contextmenu]',
48
+ '.term-viewport', '.xterm', '#term-context-menu', // terminals (docked and floating) and their menu
49
+ '#fe-tree', '.fe-ctx-flyout', // the File Explorer's tree (rows and its empty space) and its menus
50
+ '.cm-editor', '.fe-editor-menu', // a code editor anywhere; the editor's menus and palette
51
+ '#context-menu', // the 2D graph's card menu
52
+ '.explorer-ctx-menu', '.as-context-menu', // the Explorer's and the Automation Studio's menus
53
+ '.browser-canvas', // the SynaBun browser's view of another site (Back / Reload here would act on SynaBun)
54
+ '#loading-overlay', // the loading / restart screen, above the menu
55
+ ];
56
+ const OPT_OUT = NATIVE_CONTEXTMENU_SELECTORS.join(', ');
57
+ const SEARCH_MAX = 500; // a search for the selection takes its first 500 characters
58
+ const KEY_ECHO_MS = 800; // the contextmenu event a menu key sends after its keydown already opened the menu
59
+
60
+ let _installed = false;
61
+ let _keyboardOpenedAt = -Infinity;
62
+
63
+ // ═══════════════════════════════════════════
64
+ // AREAS (context-menu-areas.js; re-exported here for the pages that already import this module)
65
+ // ═══════════════════════════════════════════
66
+
67
+ export { registerContextMenuArea };
68
+
69
+ // A row of the memory Explorer.
70
+ const explorerArea = {
71
+ id: 'memory-explorer',
72
+ match(el) {
73
+ const row = el?.closest?.('.explorer-memory[data-id]');
74
+ return row ? { kind: 'memory', memory: { id: row.dataset.id } } : null;
75
+ },
76
+ };
77
+
78
+ // A memory card (ui-detail.js names its element after the memory's first 8 characters).
79
+ const cardArea = {
80
+ id: 'memory-card',
81
+ match(el) {
82
+ const card = el?.closest?.('.detail-card');
83
+ if (!card?.id) return null;
84
+ const open = getOpenCardsSnapshot().find((c) => card.id === `detail-card-${String(c.memoryId).slice(0, 8)}`);
85
+ return open ? { kind: 'memory', memory: { id: open.memoryId, card: true } } : null;
86
+ },
87
+ };
88
+
89
+ // ═══════════════════════════════════════════
90
+ // WHAT WAS CLICKED
91
+ // ═══════════════════════════════════════════
92
+
93
+ const elementOf = (node) => (node && node.nodeType !== 1 ? node.parentElement : node) || null;
94
+
95
+ // The selected text, when the right-click is on it (or the keyboard opened the menu while something is selected).
96
+ function selectionAt(el) {
97
+ const sel = window.getSelection?.();
98
+ if (!sel || sel.isCollapsed || !sel.rangeCount) return '';
99
+ const text = selectionPayload(sel.toString());
100
+ if (!text) return '';
101
+ if (!el || el === document.body || el === document.documentElement) return text;
102
+ for (let i = 0; i < sel.rangeCount; i++) {
103
+ try { if (sel.getRangeAt(i).intersectsNode(el)) return text; } catch { /* a node from another document */ }
104
+ }
105
+ return '';
106
+ }
107
+
108
+ function linkAt(el) {
109
+ const a = el?.closest?.('a[href]');
110
+ if (!a) return null;
111
+ const raw = a.getAttribute('href');
112
+ let resolved = null;
113
+ try { resolved = new URL(raw, document.baseURI).href; } catch { /* not an address */ }
114
+ return usableLink(raw, resolved);
115
+ }
116
+
117
+ function imageAt(el) {
118
+ const img = el?.closest?.('img');
119
+ return img ? usableImage(img.currentSrc || img.src) : null;
120
+ }
121
+
122
+ /** What the menu is about for an element (null: the page itself). */
123
+ export function contextAt(el) {
124
+ return { selection: selectionAt(el), link: linkAt(el), image: imageAt(el), area: areaAt(el) };
125
+ }
126
+
127
+ // ═══════════════════════════════════════════
128
+ // WHAT THE MENU CAN DO (the app's own functions)
129
+ // ═══════════════════════════════════════════
130
+
131
+ async function copyText(text) {
132
+ const value = String(text ?? '');
133
+ try {
134
+ if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(value); return true; }
135
+ } catch { /* not allowed here (an address that is not secure): the fallback below */ }
136
+ const area = document.createElement('textarea');
137
+ area.value = value;
138
+ area.setAttribute('readonly', '');
139
+ area.style.cssText = 'position:fixed;top:-1000px;left:0;opacity:0;';
140
+ document.body.appendChild(area);
141
+ area.select();
142
+ let ok = false;
143
+ try { ok = document.execCommand('copy'); } catch { /* nothing more to try */ }
144
+ area.remove();
145
+ return ok;
146
+ }
147
+
148
+ function toggleFullscreen() {
149
+ if (document.fullscreenElement) return document.exitFullscreen?.();
150
+ return document.documentElement.requestFullscreen?.();
151
+ }
152
+
153
+ // The search bar's own path: its input event shows "Searching…", waits its debounce and searches.
154
+ function searchMemories(text) {
155
+ const query = selectionPayload(text, SEARCH_MAX);
156
+ const input = document.getElementById('search-input');
157
+ if (!input || !query) return;
158
+ input.value = query;
159
+ input.dispatchEvent(new Event('input', { bubbles: true }));
160
+ }
161
+
162
+ const findNode = (id) => state.allNodes?.find((n) => n.id === id) || null;
163
+
164
+ // A memory the page has (the slim graph copy is enough to open it), else the one the server has.
165
+ async function withMemory(id, fn) {
166
+ const node = findNode(id);
167
+ if (node) return fn(node);
168
+ const full = await fetchMemory(id).catch(() => null);
169
+ if (full?.payload) return fn({ id, payload: full.payload });
170
+ return undefined;
171
+ }
172
+
173
+ // The AI panel gets the whole memory (the graph's copy of its content is cut at 500 characters).
174
+ async function sendMemory(id) {
175
+ const full = await fetchMemory(id).catch(() => null);
176
+ const node = full?.payload ? { id, payload: full.payload } : findNode(id);
177
+ if (node) await sendToPanel(formatMemoryForCLI(node));
178
+ }
179
+
180
+ export const ACTIONS = {
181
+ back: () => history.back(),
182
+ forward: () => history.forward(),
183
+ reload: () => location.reload(),
184
+ copyText,
185
+ toggleFullscreen,
186
+ openInNewTab: (href) => { window.open(href, '_blank', 'noopener,noreferrer'); },
187
+ searchMemories,
188
+ sendText: (text) => sendToPanel(text),
189
+ runAction: (actionId) => runAction(actionId),
190
+ press: (id) => document.getElementById(id)?.click(),
191
+ emit: (name, payload) => emit(name, payload),
192
+ openMemory: (id) => withMemory(id, (node) => emit('node-selected', node)),
193
+ showLinked: (id) => withMemory(id, (node) => { emit('node-selected', node); focusLinkedMemories(node.id); }),
194
+ sendMemory,
195
+ closeCard: (id) => closeMemoryCard(id),
196
+ };
197
+
198
+ function canGoBack() {
199
+ const nav = window.navigation;
200
+ return typeof nav?.canGoBack === 'boolean' ? nav.canGoBack : history.length > 1;
201
+ }
202
+
203
+ function canGoForward() {
204
+ const nav = window.navigation;
205
+ return typeof nav?.canGoForward === 'boolean' ? nav.canGoForward : true; // unknown without the Navigation API
206
+ }
207
+
208
+ /** The menu model's environment as the page is now (labels, keys and states are read at every open). */
209
+ export function menuEnv() {
210
+ return {
211
+ t,
212
+ shortcut: (actionId) => getShortcutLabel(actionId),
213
+ hasAction,
214
+ hasElement: (id) => !!document.getElementById(id),
215
+ isActive: (id) => !!document.getElementById(id)?.classList.contains('active'),
216
+ allowed: (perm) => !isGuest() || hasPermission(perm),
217
+ appIcon: (id) => (id && document.getElementById(id)?.querySelector('.menu-icon')?.innerHTML) || '',
218
+ page: {
219
+ url: location.href,
220
+ canGoBack: canGoBack(),
221
+ canGoForward: canGoForward(),
222
+ fullscreen: !!document.fullscreenElement,
223
+ fullscreenEnabled: document.fullscreenEnabled !== false && typeof document.documentElement.requestFullscreen === 'function',
224
+ },
225
+ act: ACTIONS,
226
+ };
227
+ }
228
+
229
+ // ═══════════════════════════════════════════
230
+ // OPENING
231
+ // ═══════════════════════════════════════════
232
+
233
+ // One menu at a time: the menubar, the dropdowns that listen for it, and the menus that only close on a click.
234
+ function closeOtherMenus() {
235
+ emit('panel:close-all-dropdowns');
236
+ document.getElementById('term-context-menu')?.classList.remove('open');
237
+ document.getElementById('context-menu')?.classList.remove('open');
238
+ document.querySelectorAll('.as-context-menu, .explorer-ctx-menu').forEach((el) => el.remove());
239
+ }
240
+
241
+ function open(ctx, placement) {
242
+ const items = buildAppMenu(ctx, menuEnv());
243
+ closeOtherMenus();
244
+ return openContextMenu({ items, label: t('contextMenu.label'), ...placement });
245
+ }
246
+
247
+ /**
248
+ * Open the app's menu from an area that detects its own right-click (the memory map, where a right-drag pans):
249
+ * what is at `target`, with `area` in place of what the registered areas would say.
250
+ * @param {{ x?: number, y?: number, target?: Element | null, area?: object | null, keyboard?: boolean,
251
+ * anchorRect?: DOMRect | null, returnFocus?: Element | null }} o
252
+ */
253
+ export function openAppContextMenu({ x, y, target = null, area = null, keyboard = false, anchorRect = null, returnFocus } = {}) {
254
+ const el = elementOf(target);
255
+ const ctx = contextAt(el);
256
+ if (area) ctx.area = area;
257
+ return open(ctx, { x, y, anchorRect, keyboard, returnFocus: returnFocus === undefined ? document.activeElement : returnFocus });
258
+ }
259
+
260
+ function onContextMenu(event) {
261
+ // The menu key's own contextmenu event, after its keydown opened the menu.
262
+ if (isContextMenuOpen() && performance.now() - _keyboardOpenedAt < KEY_ECHO_MS) { event.preventDefault(); return; }
263
+ const el = elementOf(event.target);
264
+ const reason = nativeMenuReason(event, {
265
+ inOwnMenu: !!el?.closest?.('.app-ctx-menu'),
266
+ editable: isEditableTarget(el),
267
+ optedOut: !!el?.closest?.(OPT_OUT),
268
+ });
269
+ if (reason === 'own-menu') { event.preventDefault(); return; }
270
+ if (reason) return;
271
+ event.preventDefault();
272
+ open(contextAt(el), { x: event.clientX, y: event.clientY, keyboard: false, returnFocus: document.activeElement });
273
+ }
274
+
275
+ function onKeyDown(event) {
276
+ if (event.defaultPrevented || !isMenuKey(event) || isContextMenuOpen() || actionForKey(event)) return;
277
+ const active = document.activeElement;
278
+ const el = active && active !== document.body && active !== document.documentElement ? active : null;
279
+ if (el && (isEditableTarget(el) || el.closest(OPT_OUT) || el.closest('.app-ctx-menu'))) return;
280
+ event.preventDefault();
281
+ _keyboardOpenedAt = performance.now();
282
+ open(contextAt(el), { anchorRect: el?.getBoundingClientRect() || null, keyboard: true, returnFocus: el });
283
+ }
284
+
285
+ /** Install the app's right-click menu (once per page). */
286
+ export function initAppContextMenu() {
287
+ if (_installed) return;
288
+ _installed = true;
289
+ registerContextMenuArea(explorerArea);
290
+ registerContextMenuArea(cardArea);
291
+ // Bubble phase at the window: every handler of the page has had the event first.
292
+ window.addEventListener('contextmenu', onContextMenu);
293
+ // A menu key the user bound to an action stays that action (actionForKey), whichever listener runs first.
294
+ document.addEventListener('keydown', onKeyDown);
295
+ }
@@ -9,6 +9,7 @@
9
9
  import { on, emit } from './state.js';
10
10
  import { KEYS } from './constants.js';
11
11
  import { storage } from './storage.js';
12
+ import { clampToFrame } from './app-frame.js';
12
13
 
13
14
  // ── State ──
14
15
 
@@ -39,10 +40,10 @@ export function initCostWidget() {
39
40
  if (!_docked) {
40
41
  const savedPos = _loadPos();
41
42
  if (savedPos) {
42
- const maxLeft = window.innerWidth - 270;
43
- const maxTop = window.innerHeight - 60;
44
- _widget.style.left = Math.max(0, Math.min(savedPos.left, maxLeft)) + 'px';
45
- _widget.style.top = Math.max(0, Math.min(savedPos.top, maxTop)) + 'px';
43
+ // Inside the app frame (about 270 × 60 of it on screen)
44
+ const at = clampToFrame(savedPos.left, savedPos.top, 270, 60);
45
+ _widget.style.left = at.x + 'px';
46
+ _widget.style.top = at.y + 'px';
46
47
  } else {
47
48
  _widget.style.right = '24px';
48
49
  _widget.style.bottom = '24px';
@@ -531,47 +532,47 @@ function _toggleCollapse() {
531
532
 
532
533
  // ── Drag ──
533
534
 
534
- function _makeDraggable(header) {
535
- header.style.cursor = 'grab';
536
-
537
- header.addEventListener('mousedown', (e) => {
535
+ function _makeDraggable(header) {
536
+ header.style.cursor = 'move';
537
+
538
+ header.addEventListener('mousedown', (e) => {
538
539
  if (e.target.closest('.cw-collapse') || e.target.closest('.cw-scan')) return;
539
540
  e.preventDefault();
540
541
 
541
542
  // If docked, undock on drag
542
543
  if (_docked) _undock();
543
-
544
- const rect = _widget.getBoundingClientRect();
545
- _drag = {
546
- startX: e.clientX,
547
- startY: e.clientY,
548
- origLeft: rect.left,
549
- origTop: rect.top,
550
- width: rect.width,
551
- height: rect.height,
552
- };
553
- let dragRaf = 0;
554
- let pendingPos = null;
555
- const applyDrag = () => {
556
- dragRaf = 0;
557
- if (!_widget || !pendingPos) return;
558
- _widget.style.left = pendingPos.left + 'px';
559
- _widget.style.top = pendingPos.top + 'px';
560
- };
561
- const scheduleDrag = (left, top) => {
562
- pendingPos = { left, top };
563
- if (!dragRaf) dragRaf = requestAnimationFrame(applyDrag);
564
- };
565
- const flushDrag = () => {
566
- if (dragRaf) {
567
- cancelAnimationFrame(dragRaf);
568
- dragRaf = 0;
569
- }
570
- applyDrag();
571
- };
572
- header.style.cursor = 'grabbing';
573
-
574
- // Reset right/bottom positioning and switch to left/top
544
+
545
+ const rect = _widget.getBoundingClientRect();
546
+ _drag = {
547
+ startX: e.clientX,
548
+ startY: e.clientY,
549
+ origLeft: rect.left,
550
+ origTop: rect.top,
551
+ width: rect.width,
552
+ height: rect.height,
553
+ };
554
+ let dragRaf = 0;
555
+ let pendingPos = null;
556
+ const applyDrag = () => {
557
+ dragRaf = 0;
558
+ if (!_widget || !pendingPos) return;
559
+ _widget.style.left = pendingPos.left + 'px';
560
+ _widget.style.top = pendingPos.top + 'px';
561
+ };
562
+ const scheduleDrag = (left, top) => {
563
+ pendingPos = { left, top };
564
+ if (!dragRaf) dragRaf = requestAnimationFrame(applyDrag);
565
+ };
566
+ const flushDrag = () => {
567
+ if (dragRaf) {
568
+ cancelAnimationFrame(dragRaf);
569
+ dragRaf = 0;
570
+ }
571
+ applyDrag();
572
+ };
573
+ header.style.cursor = 'move';
574
+
575
+ // Reset right/bottom positioning and switch to left/top
575
576
  _widget.style.right = 'auto';
576
577
  _widget.style.bottom = 'auto';
577
578
  _widget.style.left = _drag.origLeft + 'px';
@@ -581,25 +582,22 @@ function _makeDraggable(header) {
581
582
  if (!_drag) return;
582
583
  const dx = ev.clientX - _drag.startX;
583
584
  const dy = ev.clientY - _drag.startY;
584
- let newLeft = _drag.origLeft + dx;
585
- let newTop = _drag.origTop + dy;
586
- // Constrain to viewport
587
- const ww = _drag.width;
588
- const wh = _drag.height;
589
- newLeft = Math.max(0, Math.min(window.innerWidth - ww, newLeft));
590
- newTop = Math.max(0, Math.min(window.innerHeight - wh, newTop));
591
- scheduleDrag(newLeft, newTop);
592
- };
593
-
594
- const onUp = () => {
595
- document.removeEventListener('mousemove', onMove);
596
- document.removeEventListener('mouseup', onUp);
597
- header.style.cursor = 'grab';
598
- if (_drag) {
599
- flushDrag();
600
- _savePos(parseInt(_widget.style.left), parseInt(_widget.style.top));
601
- _drag = null;
602
- }
585
+ let newLeft = _drag.origLeft + dx;
586
+ let newTop = _drag.origTop + dy;
587
+ // Constrain to the app frame
588
+ ({ x: newLeft, y: newTop } = clampToFrame(newLeft, newTop, _drag.width, _drag.height));
589
+ scheduleDrag(newLeft, newTop);
590
+ };
591
+
592
+ const onUp = () => {
593
+ document.removeEventListener('mousemove', onMove);
594
+ document.removeEventListener('mouseup', onUp);
595
+ header.style.cursor = 'move';
596
+ if (_drag) {
597
+ flushDrag();
598
+ _savePos(parseInt(_widget.style.left), parseInt(_widget.style.top));
599
+ _drag = null;
600
+ }
603
601
  };
604
602
 
605
603
  document.addEventListener('mousemove', onMove);