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,66 @@
1
+ // The Neural Interface offers only the 3D memory map. The 2D implementation is deprecated, kept on disk.
2
+ import { KEYS } from './constants.js';
3
+
4
+ export function resolveViewVariant(_requested) { return '3d'; }
5
+
6
+ /** Preserve other URL state, including memory anchors, when retiring a 2D address. Shared with the server. */
7
+ export function normalizeViewUrl(input) {
8
+ const url = new URL(input, 'http://localhost');
9
+ if (url.pathname.toLowerCase() === '/index2d.html') url.pathname = '/';
10
+ if (url.searchParams.has('variant')) url.searchParams.set('variant', resolveViewVariant(url.searchParams.get('variant')));
11
+ const hash = url.hash.slice(1);
12
+ if (hash === '2d') url.hash = '3d';
13
+ else {
14
+ const start = hash.includes('?') ? hash.indexOf('?') + 1 : 0;
15
+ const params = new URLSearchParams(hash.slice(start));
16
+ if (params.has('variant')) {
17
+ params.set('variant', resolveViewVariant(params.get('variant')));
18
+ url.hash = hash.slice(0, start) + params.toString();
19
+ }
20
+ }
21
+ return url.pathname + url.search + url.hash;
22
+ }
23
+
24
+ /** Optional legacy config fields are normalized without changing Focus Mode, glass or accent settings. */
25
+ export function normalizeViewConfig(config) {
26
+ if (!config || typeof config !== 'object' || Array.isArray(config)) return config;
27
+ const next = { ...config };
28
+ for (const key of ['variant', 'viewVariant']) {
29
+ if (Object.hasOwn(next, key)) next[key] = resolveViewVariant(next[key]);
30
+ }
31
+ return next;
32
+ }
33
+
34
+ /** Retain archived 2D coordinates, but never reinterpret their x/y positions as 3D positions. */
35
+ export function normalizeWorkspaceVariant(workspace) {
36
+ if (workspace?.variant !== '2d') return workspace;
37
+ return {
38
+ ...workspace,
39
+ variant: '3d',
40
+ nodePositions2d: workspace.nodePositions2d || workspace.nodePositions || {},
41
+ nodePositions3d: workspace.nodePositions3d || {},
42
+ };
43
+ }
44
+
45
+ /** Rewrite persisted preferences at boot, in both the hydrated server store and any browser-local copy. */
46
+ export function migrateViewPreferences(...stores) {
47
+ for (const store of new Set(stores.filter(Boolean))) {
48
+ // A refused read/write or malformed old value must not stop the supported view from booting.
49
+ try {
50
+ if (store.getItem(KEYS.VIEW_VARIANT) !== '3d') store.setItem(KEYS.VIEW_VARIANT, '3d');
51
+ } catch {}
52
+ for (const [key, normalize] of [
53
+ [KEYS.INTERFACE_CONFIG, normalizeViewConfig],
54
+ [KEYS.WORKSPACES, (list) => Array.isArray(list) ? list.map(normalizeWorkspaceVariant) : list],
55
+ ]) {
56
+ try {
57
+ const raw = store.getItem(key);
58
+ if (!raw) continue;
59
+ const value = JSON.parse(raw);
60
+ const next = normalize(value);
61
+ if (JSON.stringify(next) !== JSON.stringify(value)) store.setItem(key, JSON.stringify(next));
62
+ } catch {}
63
+ }
64
+ }
65
+ return resolveViewVariant();
66
+ }
@@ -242,7 +242,7 @@ export const HELP = Object.freeze({
242
242
  'Stuck on "Reconnecting"? Check this computer\'s internet connection, then press Reconnect now.',
243
243
  'Voice notes not transcribed? This computer needs whisper.cpp, its speech model and FFmpeg, which you install yourself; the line under "Transcribe voice notes" says what is missing.',
244
244
  ],
245
- browserTools: 'Not the same as the WhatsApp browser tools. The whatsapp MCP profile lets agents read WhatsApp Web in SynaBun\'s browser; this tab is you chatting with SynaBun from your phone. They do not share a session.',
245
+ browserTools: 'Not the same as the WhatsApp browser tools. The browser_whatsapp tool group lets agents read WhatsApp Web in SynaBun\'s browser; this tab is you chatting with SynaBun from your phone. They do not share a session.',
246
246
  uninstall: 'Uninstalling SynaBun does not unlink WhatsApp. Use Unlink or Remove here first, or log SynaBun out under WhatsApp → Settings → Linked devices.',
247
247
  });
248
248
 
@@ -0,0 +1,172 @@
1
+ // ═══════════════════════════════════════════
2
+ // WHITEBOARD-LIST — nesting levels of a list card (DOM-free)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // A list element keeps its bullets as `items: string[]`, as every board always has, and, only while some bullet
6
+ // is nested, `levels: number[]` beside them: levels[i] is how deep items[i] sits, 0 = top level. A list without
7
+ // `levels` (every board saved before nesting existed) reads as all level 0, and a reader that does not know
8
+ // `levels` still shows every bullet, flat. Callers may also hand items over as { text, level } objects; they are
9
+ // stored as the two arrays. Every writer keeps three rules: the first item is level 0, an item sits at most one
10
+ // level deeper than the item above it, and no item is deeper than WB_LIST_MAX_LEVEL.
11
+ //
12
+ // No imports: ui-whiteboard.js (the board), server.js (whiteboard_add / whiteboard_update) and node --test load it.
13
+ // mcp-server/src/services/whiteboard-geometry.ts reads the same format for the MCP tools.
14
+
15
+ export const WB_LIST_MAX_LEVEL = 4;
16
+
17
+ /** Level of one value: a whole number from 0 to WB_LIST_MAX_LEVEL (anything else is 0, more is the maximum). */
18
+ function levelOf(value) {
19
+ const n = Math.floor(Number(value));
20
+ return Number.isFinite(n) && n > 0 ? Math.min(n, WB_LIST_MAX_LEVEL) : 0;
21
+ }
22
+
23
+ /** Text of one stored entry: a string, or the { text, level } form. */
24
+ export function listItemText(item) {
25
+ if (typeof item === 'string') return item;
26
+ if (item && typeof item === 'object') return item.text == null ? '' : String(item.text);
27
+ return item == null ? '' : String(item);
28
+ }
29
+
30
+ /** `count` levels that keep the rules, read from `levels` (missing entries are 0). */
31
+ export function fitListLevels(levels, count) {
32
+ const out = [];
33
+ let above = -1;
34
+ for (let i = 0; i < count; i++) {
35
+ const level = Math.min(levelOf(Array.isArray(levels) ? levels[i] : 0), above + 1);
36
+ out.push(level);
37
+ above = level;
38
+ }
39
+ return out;
40
+ }
41
+
42
+ /**
43
+ * A list's items in either form → { items: string[], levels: number[] } (levels always as long as items, keeping
44
+ * the rules). A plain string is level `levels[i]` (0 when there is no such entry); a { text, level } object's own
45
+ * level wins over `levels[i]`.
46
+ */
47
+ export function readListItems(items, levels) {
48
+ const list = Array.isArray(items) ? items : [];
49
+ const texts = list.map(listItemText);
50
+ const wanted = list.map((item, i) => (item && typeof item === 'object' && item.level !== undefined
51
+ ? item.level
52
+ : (Array.isArray(levels) ? levels[i] : 0)));
53
+ return { items: texts, levels: fitListLevels(wanted, texts.length) };
54
+ }
55
+
56
+ /** True when some level is above 0 (only then is `levels` stored). */
57
+ export function hasNestedLevels(levels) {
58
+ return Array.isArray(levels) && levels.some(level => level > 0);
59
+ }
60
+
61
+ /**
62
+ * Store items + levels on a list element: `items` always, `levels` only while something is nested (removed
63
+ * otherwise, so a flat list is stored exactly as before). Always new arrays, never an edit in place: copies of
64
+ * an element (undo entries, the clipboard, a duplicate) share the arrays of the original.
65
+ */
66
+ export function writeListItems(el, items, levels) {
67
+ const read = readListItems(items, levels);
68
+ el.items = read.items;
69
+ if (hasNestedLevels(read.levels)) el.levels = read.levels;
70
+ else delete el.levels;
71
+ return el;
72
+ }
73
+
74
+ /**
75
+ * The `items` / `levels` fields of an update to a list, made to keep the rules. Items sent without levels are
76
+ * read in their own form (a plain string is level 0, an object carries its level): replacing the items of a
77
+ * nested list with plain strings flattens it. Levels sent alone apply to the items the list already has.
78
+ * `levels` comes back as null when nothing is nested (the update then removes it).
79
+ */
80
+ export function normalizeListUpdate(currentItems, patch) {
81
+ if (!patch || typeof patch !== 'object') return patch;
82
+ const hasItems = Object.hasOwn(patch, 'items');
83
+ const hasLevels = Object.hasOwn(patch, 'levels');
84
+ if (!hasItems && !hasLevels) return patch;
85
+ const read = hasItems ? readListItems(patch.items, patch.levels) : readListItems(currentItems, patch.levels);
86
+ const out = { ...patch, levels: hasNestedLevels(read.levels) ? read.levels : null };
87
+ if (hasItems) out.items = read.items;
88
+ return out;
89
+ }
90
+
91
+ /** End (exclusive) of the item at `index` and its sub-items: the items right after it that sit deeper. */
92
+ export function listSubtreeEnd(levels, index) {
93
+ let end = index + 1;
94
+ while (end < levels.length && levels[end] > levels[index]) end++;
95
+ return end;
96
+ }
97
+
98
+ /** Whether the item at `index` can go one level deeper: never the first, never more than one level deeper than
99
+ * the item above it, and neither it nor a sub-item of it past the maximum. */
100
+ export function canIndentListItem(levels, index) {
101
+ if (!Array.isArray(levels) || index <= 0 || index >= levels.length) return false;
102
+ if (levels[index] > levels[index - 1]) return false;
103
+ const deepest = Math.max(...levels.slice(index, listSubtreeEnd(levels, index)));
104
+ return deepest < WB_LIST_MAX_LEVEL;
105
+ }
106
+
107
+ /** Levels with the item at `index` one level deeper, its sub-items with it (they stay its sub-items), or null
108
+ * when it cannot go deeper. */
109
+ export function indentListItem(levels, index) {
110
+ if (!canIndentListItem(levels, index)) return null;
111
+ const next = levels.slice();
112
+ for (let i = index, end = listSubtreeEnd(levels, index); i < end; i++) next[i] += 1;
113
+ return fitListLevels(next, next.length);
114
+ }
115
+
116
+ /** Levels with the item at `index` one level shallower, its sub-items with it, or null when it is at the top
117
+ * level. The items after them keep their level (one that was its sibling becomes its sub-item, as in a flat
118
+ * outline nothing is reordered). */
119
+ export function outdentListItem(levels, index) {
120
+ if (!Array.isArray(levels) || index < 0 || index >= levels.length || levels[index] <= 0) return null;
121
+ const next = levels.slice();
122
+ for (let i = index, end = listSubtreeEnd(levels, index); i < end; i++) next[i] -= 1;
123
+ return fitListLevels(next, next.length);
124
+ }
125
+
126
+ // Bullet and number styles per level, in the order browsers nest them: the level-0 style is what a flat list
127
+ // always showed (a disc, a decimal number); deeper levels cycle.
128
+ export const UNORDERED_STYLES = ['disc', 'circle', 'square'];
129
+ export const ORDERED_STYLES = ['decimal', 'lower-alpha', 'lower-roman'];
130
+ const BULLET_GLYPHS = { disc: '•', circle: '◦', square: '▪' };
131
+
132
+ /** The list-style name of a level (the CSS in styles.css uses the same cycle). */
133
+ export function listStyleForLevel(level, ordered) {
134
+ const styles = ordered ? ORDERED_STYLES : UNORDERED_STYLES;
135
+ return styles[levelOf(level) % styles.length];
136
+ }
137
+
138
+ function toRoman(n) {
139
+ const table = [[1000, 'm'], [900, 'cm'], [500, 'd'], [400, 'cd'], [100, 'c'], [90, 'xc'], [50, 'l'], [40, 'xl'], [10, 'x'], [9, 'ix'], [5, 'v'], [4, 'iv'], [1, 'i']];
140
+ let out = '';
141
+ for (const [value, digits] of table) while (n >= value) { out += digits; n -= value; }
142
+ return out;
143
+ }
144
+
145
+ function toAlpha(n) {
146
+ let out = '';
147
+ while (n > 0) { n -= 1; out = String.fromCharCode(97 + (n % 26)) + out; n = Math.floor(n / 26); }
148
+ return out;
149
+ }
150
+
151
+ /** A counter value in one of the ordered styles: 1 → "1", "a", "i". */
152
+ export function formatListCounter(n, style) {
153
+ if (style === 'lower-alpha') return toAlpha(n);
154
+ if (style === 'lower-roman') return toRoman(n);
155
+ return String(n);
156
+ }
157
+
158
+ /**
159
+ * The marker drawn before each item: a bullet glyph per level, or the item's number in its level's style followed
160
+ * by "." (numbering restarts under every item above, as nested ordered lists do).
161
+ */
162
+ export function listMarkers(levels, ordered) {
163
+ const counters = [];
164
+ return (Array.isArray(levels) ? levels : []).map((raw) => {
165
+ const level = levelOf(raw);
166
+ const style = listStyleForLevel(level, ordered);
167
+ if (!ordered) return BULLET_GLYPHS[style];
168
+ counters.length = level + 1;
169
+ counters[level] = (counters[level] || 0) + 1;
170
+ return `${formatListCounter(counters[level], style)}.`;
171
+ });
172
+ }
@@ -47,6 +47,7 @@ export const WORKER_END_COPY = Object.freeze({
47
47
  reasonMaxTurns: 'The run reached its turn limit.',
48
48
  reasonIdle: 'The worker waited for a follow-up that did not come, and closed.',
49
49
  reasonRestart: 'The server restarted while the run was open.',
50
+ reasonRestartNotResumed: 'The server restarted while the run was open, and it could not continue: {reason}',
50
51
  reasonKillAll: 'Stopped together with every other run.',
51
52
  reasonCleared: 'The run\'s state was removed.',
52
53
  reasonOther: 'Stopped ({reason}).',
@@ -61,8 +62,9 @@ export const WORKER_END_CLASS = 'worker-end-card';
61
62
  /** The run states (runtime and dispatcher alike) after which nothing more happens. */
62
63
  export const TERMINAL_STATES = new Set(['completed', 'failed', 'stopped', 'interrupted']);
63
64
  const TERMINAL = TERMINAL_STATES;
64
- // Dispatcher states in which a turn is in progress (or about to be): the card waits.
65
- const WORKING = new Set(['queued', 'awaiting_route', 'starting', 'running', 'awaiting_permission']);
65
+ // Dispatcher states in which a turn is in progress (or about to be): the card waits. A run a server restart cut off
66
+ // (suspended) and one the next start resumes (resuming) are not over either.
67
+ const WORKING = new Set(['queued', 'awaiting_route', 'starting', 'running', 'awaiting_permission', 'suspended', 'resuming']);
66
68
  const SUMMARY_CHARS = 320;
67
69
 
68
70
  /** A WORKER_END_COPY sentence with its `{params}` filled in: the translator when the page gives none. */
@@ -85,6 +87,7 @@ export function isRunWorking(run, dispatch = null) {
85
87
  if (TERMINAL.has(state) || TERMINAL.has(String(run?.status || ''))) return false;
86
88
  if (state) return WORKING.has(state);
87
89
  const status = String(run?.status || '');
90
+ if (status === 'suspended') return true;
88
91
  if (status !== 'starting' && status !== 'running') return false;
89
92
  return run?.turnState !== 'idle' && run?.turnState !== 'terminal';
90
93
  }
@@ -159,7 +162,7 @@ function stopReason(reason, t) {
159
162
  case 'budget_share': return t('reasonBudgetShare');
160
163
  case 'max_turns': return t('reasonMaxTurns');
161
164
  case 'idle_timeout': return t('reasonIdle');
162
- case 'server_restart': case 'server_shutdown': return t('reasonRestart');
165
+ case 'server_restart': case 'server_shutdown': case 'resume_failed': return t('reasonRestart');
163
166
  case 'kill_all': return t('reasonKillAll');
164
167
  case 'state_removed': case 'state_inactive': return t('reasonCleared');
165
168
  case '': return '';
@@ -226,6 +229,10 @@ export function workerEndView(run, dispatch = null, { t = fillWorkerEndCopy, bus
226
229
  } else {
227
230
  kind = endState === 'interrupted' ? 'interrupted' : 'stopped';
228
231
  detail = stopReason(dispatch?.completionReason || run?.stoppedReason || '', t);
232
+ // A Claude Code worker the server could not resume after a restart says why (lib/worker-resume.js; a resume that
233
+ // failed to start has the reason resume_failed and the error).
234
+ const notResumed = dispatch?.resumeRefused || (dispatch?.completionReason === 'resume_failed' ? (dispatch?.error || run?.error || '') : '');
235
+ if (kind === 'interrupted' && notResumed) detail = t('reasonRestartNotResumed', { reason: plain(notResumed, 200) });
229
236
  }
230
237
 
231
238
  if (kind === 'complete' || kind === 'blocked') detail = plain(result?.summary || '');
@@ -0,0 +1,12 @@
1
+ /** Shared provider-panel status. A suspension still owns the tab, but is not working. */
2
+ export function workerRestart(run) {
3
+ const suspended = run?.status === 'suspended';
4
+ const resumes = Number(run?.resumes) || 0;
5
+ return {
6
+ suspended,
7
+ active: suspended || ['starting', 'running'].includes(run?.status),
8
+ key: suspended ? 'workerRestart.suspended' : resumes ? 'workerRestart.resumed' : null,
9
+ stamp: suspended ? `suspended:${run.suspendedAt || ''}` : `resumed:${resumes}`,
10
+ resumes,
11
+ };
12
+ }
@@ -14,5 +14,5 @@
14
14
  #term-minimized-tray { position:fixed; right:8px; top:45px; z-index:20000; }
15
15
  </style>
16
16
  </head>
17
- <body><div id="term-minimized-tray"></div><script type="module" src="/shared/ui-sidepanel-session-entry.js"></script></body>
17
+ <body><div id="term-minimized-tray"></div><script type="module" src="/shared/page-session.js"></script><script type="module" src="/shared/ui-sidepanel-session-entry.js"></script></body>
18
18
  </html>
@@ -58,7 +58,8 @@ export function drawBackground(ctx, time, w, h) {
58
58
  const maxDim = Math.max(w, h);
59
59
  _bgGrad = ctx.createRadialGradient(w / 2, h / 2, 0, w / 2, h / 2, maxDim * 0.7);
60
60
  _bgGrad.addColorStop(0, '#0e1218');
61
- _bgGrad.addColorStop(1, '#070709');
61
+ // The app frame's interior (styles.css --frame-surface): the card's edge reads as one surface with the 3D map's
62
+ _bgGrad.addColorStop(1, '#07080b');
62
63
  _bgGradW = w;
63
64
  _bgGradH = h;
64
65
  }
@@ -7,6 +7,8 @@
7
7
 
8
8
  import { state, emit, on } from '../../shared/state.js';
9
9
  import { getAllCards } from './graph.js';
10
+ import { frameRect } from '../../shared/app-frame.js';
11
+ import { placeMenu } from '../../shared/context-menu-logic.js';
10
12
 
11
13
  const $ = (id) => document.getElementById(id);
12
14
 
@@ -51,9 +53,13 @@ export function showContextMenu(node, event) {
51
53
  if (!_contextMenu) return;
52
54
 
53
55
  _contextMenuNode = node;
54
- _contextMenu.style.left = event.clientX + 'px';
55
- _contextMenu.style.top = event.clientY + 'px';
56
56
  _contextMenu.classList.add('open');
57
+ // At the pointer, flipped and kept inside the app frame (the card is in it)
58
+ const box = _contextMenu.getBoundingClientRect();
59
+ const at = placeMenu({ x: event.clientX, y: event.clientY, width: box.width, height: box.height,
60
+ viewportWidth: window.innerWidth, viewportHeight: window.innerHeight, bounds: frameRect() });
61
+ _contextMenu.style.left = at.left + 'px';
62
+ _contextMenu.style.top = at.top + 'px';
57
63
 
58
64
  // Update pin text based on card pinned state
59
65
  const pinItem = _contextMenu.querySelector('[data-action="pin"]');
@@ -131,17 +131,24 @@ const _panInertia = {
131
131
  // COORDINATE TRANSFORMS
132
132
  // ═══════════════════════════════════════════
133
133
 
134
+ // The canvas fills the app frame (index2d.html #canvas-main), not the window: a pointer's client coordinates are
135
+ // turned into the canvas's own by its box (kept from the last resize: the canvas moves only when it is resized).
136
+ let _box = { left: 0, top: 0 };
137
+ let _boxMeasured = false;
138
+
139
+ /** Client (viewport) coordinates → world. */
134
140
  export function screenToWorld(sx, sy) {
135
141
  return {
136
- x: (sx * _dpr - _viewport.offsetX) / _viewport.scale,
137
- y: (sy * _dpr - _viewport.offsetY) / _viewport.scale,
142
+ x: ((sx - _box.left) * _dpr - _viewport.offsetX) / _viewport.scale,
143
+ y: ((sy - _box.top) * _dpr - _viewport.offsetY) / _viewport.scale,
138
144
  };
139
145
  }
140
146
 
147
+ /** World → client (viewport) coordinates. */
141
148
  export function worldToScreen(wx, wy) {
142
149
  return {
143
- x: (wx * _viewport.scale + _viewport.offsetX) / _dpr,
144
- y: (wy * _viewport.scale + _viewport.offsetY) / _dpr,
150
+ x: (wx * _viewport.scale + _viewport.offsetX) / _dpr + _box.left,
151
+ y: (wy * _viewport.scale + _viewport.offsetY) / _dpr + _box.top,
145
152
  };
146
153
  }
147
154
 
@@ -163,7 +170,10 @@ function _getVisibleBounds(margin = 0) {
163
170
  // PUBLIC API: Viewport accessors
164
171
  // ═══════════════════════════════════════════
165
172
 
166
- export function getViewport() { return { ..._viewport, dpr: _dpr }; }
173
+ export function getViewport() {
174
+ // width / height: the canvas buffer (device pixels, like the offsets)
175
+ return { ..._viewport, dpr: _dpr, width: _canvas?.width || 0, height: _canvas?.height || 0 };
176
+ }
167
177
  export function getAllCards() { return _cards; }
168
178
  export function getRegions() { return _regions; }
169
179
 
@@ -187,7 +197,7 @@ export function initGraph(container, callbacks = {}) {
187
197
  if (!_canvas) {
188
198
  _canvas = document.createElement('canvas');
189
199
  _canvas.id = 'canvas-main';
190
- _canvas.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:1;';
200
+ _canvas.style.cssText = 'position:fixed;top:var(--frame-top);left:var(--frame-left);width:calc(100% - var(--sidebar-total-width, 0px) - 2 * var(--frame-gap));height:calc(100dvh - var(--frame-top) - var(--frame-gap));border-radius:var(--frame-radius);z-index:1;';
191
201
  container.appendChild(_canvas);
192
202
  }
193
203
  _ctx = _canvas.getContext('2d');
@@ -210,6 +220,8 @@ export function initGraph(container, callbacks = {}) {
210
220
  _canvas.addEventListener('dblclick', _onDblClick);
211
221
  _canvas.addEventListener('contextmenu', _onContextMenu);
212
222
  window.addEventListener('resize', _scheduleCanvasResize);
223
+ // The frame changes the canvas's box without a window resize (an explorer opening, the bar's height)
224
+ if (typeof ResizeObserver === 'function') new ResizeObserver(_scheduleCanvasResize).observe(_canvas);
213
225
 
214
226
  // Hidden tab → stop the loop entirely; resume on return unless externally stopped
215
227
  document.addEventListener('visibilitychange', () => {
@@ -1397,7 +1409,7 @@ const _scheduleHover2D = rafThrottle(() => {
1397
1409
  if (newHoverId !== state.hoveredNodeId) {
1398
1410
  state.hoveredNodeId = newHoverId;
1399
1411
  }
1400
- _canvas.style.cursor = hit ? 'pointer' : 'grab';
1412
+ _canvas.style.cursor = hit ? 'pointer' : 'move';
1401
1413
  if (newHoverId) emit('node-hover', hit.node);
1402
1414
  else if (!hit) emit('node-hover', null);
1403
1415
  });
@@ -1469,7 +1481,7 @@ function _onPointerLeave() {
1469
1481
  if (_focusPaused) return;
1470
1482
  if (state.hoveredNodeId) {
1471
1483
  state.hoveredNodeId = null;
1472
- _canvas.style.cursor = 'grab';
1484
+ _canvas.style.cursor = 'move';
1473
1485
  emit('node-hover', null);
1474
1486
  }
1475
1487
  }
@@ -1483,9 +1495,9 @@ function _onWheel(e) {
1483
1495
  _viewport.minScale,
1484
1496
  Math.min(_viewport.maxScale, (_smoothZoom.active ? _smoothZoom.targetScale : _viewport.scale) * zoomFactor)
1485
1497
  );
1486
- // Update anchor to cursor position (buffer coords)
1487
- _smoothZoom.anchorX = e.clientX * _dpr;
1488
- _smoothZoom.anchorY = e.clientY * _dpr;
1498
+ // Update anchor to cursor position (buffer coords, from the canvas's own corner)
1499
+ _smoothZoom.anchorX = (e.clientX - _box.left) * _dpr;
1500
+ _smoothZoom.anchorY = (e.clientY - _box.top) * _dpr;
1489
1501
  _smoothZoom.active = true;
1490
1502
  }
1491
1503
 
@@ -1502,11 +1514,14 @@ function _onDblClick(e) {
1502
1514
  }
1503
1515
  }
1504
1516
 
1517
+ // A card has its own menu (context-menu.js): the event is taken only then. Anywhere else on the graph (its empty
1518
+ // space, a region, while focus mode pauses it) it is left to the app's right-click menu, which adds the graph's
1519
+ // own rows (main.js registers that area): taking it there would leave the user with no menu at all.
1505
1520
  function _onContextMenu(e) {
1506
- if (_focusPaused) { e.preventDefault(); return; }
1507
- e.preventDefault();
1521
+ if (_focusPaused) return;
1508
1522
  const hit = _hitTest(e.clientX, e.clientY);
1509
1523
  if (hit && hit.type === 'card') {
1524
+ e.preventDefault();
1510
1525
  emit('node-context-menu', { node: hit.node, event: e });
1511
1526
  }
1512
1527
  }
@@ -1622,14 +1637,20 @@ function _resizeCanvas() {
1622
1637
  if (!_canvas) return;
1623
1638
  _resizePending = false;
1624
1639
  _dpr = Math.min(window.devicePixelRatio || 1, MAX_DPR);
1625
- const w = window.innerWidth;
1626
- const h = window.innerHeight;
1640
+ // The CSS box is the app frame's (index2d.html); the buffer follows it. Hidden (no box): the window's size.
1641
+ const box = _canvas.getBoundingClientRect();
1642
+ const w = box.width || window.innerWidth;
1643
+ const h = box.height || window.innerHeight;
1644
+ // The world stays where it was on screen when the canvas's corner moves (an explorer opening or closing):
1645
+ // what was under the pointer is still under it, as when the canvas was the whole window.
1646
+ if (box.width && box.height && _boxMeasured) {
1647
+ _viewport.offsetX -= (box.left - _box.left) * _dpr;
1648
+ _viewport.offsetY -= (box.top - _box.top) * _dpr;
1649
+ }
1650
+ if (box.width && box.height) { _box = { left: box.left, top: box.top }; _boxMeasured = true; }
1627
1651
  // Set canvas buffer size to DPR-scaled resolution for crisp rendering
1628
1652
  _canvas.width = Math.round(w * _dpr);
1629
1653
  _canvas.height = Math.round(h * _dpr);
1630
- // CSS display size stays at viewport dimensions
1631
- _canvas.style.width = w + 'px';
1632
- _canvas.style.height = h + 'px';
1633
1654
  }
1634
1655
 
1635
1656
  // Window resizes arrive many times per frame while a window or sidepanel is
@@ -1692,12 +1713,13 @@ function _getPublicAPI() {
1692
1713
  })),
1693
1714
  links: state.allLinks,
1694
1715
  }),
1716
+ // The canvas's size is the app frame's (CSS): a value given here only measures it again.
1695
1717
  width: (w) => {
1696
- if (w != null) { _canvas.style.width = w + 'px'; _resizeCanvas(); }
1718
+ if (w != null) _resizeCanvas();
1697
1719
  return _canvas ? Math.round(_canvas.width / _dpr) : 0;
1698
1720
  },
1699
1721
  height: (h) => {
1700
- if (h != null) { _canvas.style.height = h + 'px'; _resizeCanvas(); }
1722
+ if (h != null) _resizeCanvas();
1701
1723
  return _canvas ? Math.round(_canvas.height / _dpr) : 0;
1702
1724
  },
1703
1725
  // No-ops for compatibility
@@ -31,8 +31,7 @@ export function initHulls(graphInstance) {
31
31
  if (!_hullCanvas) return;
32
32
  _hullCtx = _hullCanvas.getContext('2d');
33
33
 
34
- _hullCanvas.width = window.innerWidth;
35
- _hullCanvas.height = window.innerHeight;
34
+ _sizeHullCanvas();
36
35
 
37
36
  // Toggle button
38
37
  const toggleBtn = $('hull-toggle-btn');
@@ -68,8 +67,7 @@ export function drawHulls() {
68
67
 
69
68
  if (!_hullCanvas || !_hullCtx) return;
70
69
 
71
- _hullCanvas.width = window.innerWidth;
72
- _hullCanvas.height = window.innerHeight;
70
+ _sizeHullCanvas();
73
71
  const ctx = _hullCtx;
74
72
  ctx.clearRect(0, 0, _hullCanvas.width, _hullCanvas.height);
75
73
 
@@ -86,11 +84,12 @@ export function drawHulls() {
86
84
  catGroups[cat].push(n);
87
85
  });
88
86
 
89
- // Convert graph coords to screen coords
87
+ // Convert graph coords to the overlay's own (it covers the graph's box, centred on the same point)
88
+ const halfW = _hullCanvas.width / 2, halfH = _hullCanvas.height / 2;
90
89
  function toScreen(gx, gy) {
91
90
  return {
92
- x: (gx - center.x) * zoom + window.innerWidth / 2,
93
- y: (gy - center.y) * zoom + window.innerHeight / 2,
91
+ x: (gx - center.x) * zoom + halfW,
92
+ y: (gy - center.y) * zoom + halfH,
94
93
  };
95
94
  }
96
95
 
@@ -186,8 +185,14 @@ export function isHullsVisible() {
186
185
 
187
186
  function _onResize() {
188
187
  if (!_hullCanvas) return;
189
- _hullCanvas.width = window.innerWidth;
190
- _hullCanvas.height = window.innerHeight;
188
+ _sizeHullCanvas();
189
+ }
190
+
191
+ /** The overlay's buffer follows its CSS box (the graph's, inside the app frame); no box: the window's size. */
192
+ function _sizeHullCanvas() {
193
+ const box = _hullCanvas.getBoundingClientRect();
194
+ _hullCanvas.width = Math.round(box.width || window.innerWidth);
195
+ _hullCanvas.height = Math.round(box.height || window.innerHeight);
191
196
  }
192
197
 
193
198
  /**
@@ -18,6 +18,7 @@ let _lassoStart = null;
18
18
  let _lassoEnd = null;
19
19
  let _lassoCanvas = null;
20
20
  let _lassoCtx = null;
21
+ let _lassoBox = { left: 0, top: 0 };
21
22
 
22
23
  // ═══════════════════════════════════════════
23
24
  // INIT
@@ -32,8 +33,9 @@ export function initLasso(_graphInstance) {
32
33
  if (!_lassoCanvas) return;
33
34
  _lassoCtx = _lassoCanvas.getContext('2d');
34
35
 
35
- _lassoCanvas.width = window.innerWidth;
36
- _lassoCanvas.height = window.innerHeight;
36
+ _onResize();
37
+ // It fills the app frame like the graph: an explorer opening resizes it without a window resize.
38
+ if (typeof ResizeObserver === 'function') new ResizeObserver(_onResize).observe(_lassoCanvas);
37
39
 
38
40
  document.addEventListener('pointerdown', _onPointerDown);
39
41
  document.addEventListener('pointermove', _onPointerMove);
@@ -50,6 +52,7 @@ function _onPointerDown(e) {
50
52
  // #wb-root: Shift+drag on the focus-mode whiteboard is its own marquee selection
51
53
  if (e.target.closest('#title-bar, #category-sidebar, .detail-card, #minimap, .glass, #wb-root')) return;
52
54
  _lassoActive = true;
55
+ _lassoBox = _lassoCanvas.getBoundingClientRect();
53
56
  _lassoStart = { x: e.clientX, y: e.clientY };
54
57
  _lassoEnd = { x: e.clientX, y: e.clientY };
55
58
  e.preventDefault();
@@ -59,10 +62,10 @@ function _onPointerMove(e) {
59
62
  if (!_lassoActive) return;
60
63
  _lassoEnd = { x: e.clientX, y: e.clientY };
61
64
 
62
- // Draw selection rect
65
+ // Draw selection rect (client coordinates → the canvas's own: it sits in the app frame)
63
66
  _lassoCtx.clearRect(0, 0, _lassoCanvas.width, _lassoCanvas.height);
64
- const x = Math.min(_lassoStart.x, _lassoEnd.x);
65
- const y = Math.min(_lassoStart.y, _lassoEnd.y);
67
+ const x = Math.min(_lassoStart.x, _lassoEnd.x) - _lassoBox.left;
68
+ const y = Math.min(_lassoStart.y, _lassoEnd.y) - _lassoBox.top;
66
69
  const w = Math.abs(_lassoEnd.x - _lassoStart.x);
67
70
  const h = Math.abs(_lassoEnd.y - _lassoStart.y);
68
71
  _lassoCtx.strokeStyle = 'rgba(100, 180, 255, 0.6)';
@@ -120,8 +123,9 @@ export function clearLasso() {
120
123
 
121
124
  function _onResize() {
122
125
  if (!_lassoCanvas) return;
123
- _lassoCanvas.width = window.innerWidth;
124
- _lassoCanvas.height = window.innerHeight;
126
+ const box = _lassoCanvas.getBoundingClientRect();
127
+ _lassoCanvas.width = Math.round(box.width || window.innerWidth);
128
+ _lassoCanvas.height = Math.round(box.height || window.innerHeight);
125
129
  }
126
130
 
127
131
  /**