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
@@ -3,6 +3,7 @@ import { on } from './state.js';
3
3
  import { initI18n, isReady, t } from './i18n.js';
4
4
  import { isGuest, hasPermission, showGuestToast } from './ui-sync.js';
5
5
  import { createPanel } from './styleguide/sg-panel.js';
6
+ import { centeredInFrame } from './app-frame.js';
6
7
  let current = null, opening = false;
7
8
  export function initStyleGuide() { on('styleguide:open', options => openPanel(options)); }
8
9
  async function openPanel(options = {}) {
@@ -25,8 +26,10 @@ async function openPanel(options = {}) {
25
26
  document.body.append(backdrop);
26
27
  current = createPanel({ projectPath: options?.projectPath, onClosed: () => { backdrop.remove(); current = null; } });
27
28
  document.body.append(current.panel);
28
- current.panel.style.left = Math.max(8, (window.innerWidth - Math.min(1180, window.innerWidth - 16)) / 2) + 'px';
29
- current.panel.style.top = Math.max(8, (window.innerHeight - Math.min(720, window.innerHeight - 32)) / 2) + 'px';
29
+ // Centred in the app frame (its CSS size: 1180 × 720 at most, the window less 16 / 32px)
30
+ const at = centeredInFrame(Math.min(1180, window.innerWidth - 16), Math.min(720, window.innerHeight - 32));
31
+ current.panel.style.left = at.left + 'px';
32
+ current.panel.style.top = at.top + 'px';
30
33
  await current.ready;
31
34
  }
32
35
  finally {
@@ -1,3 +1,4 @@
1
+ import { updateCachedToolDefault, invalidateToolCatalog } from './agent-runtime-options.js';
1
2
  // ═══════════════════════════════════════════
2
3
  // SynaBun Neural Interface — Real-time Sync
3
4
  // ═══════════════════════════════════════════
@@ -24,7 +25,7 @@ function clearOpenCodeHistoryClientState(message) {
24
25
  'opencode-v2-windows',
25
26
  'synabun-ocp-windows',
26
27
  'synabun-ocp-session-snapshots',
27
- 'synabun-opencode-session-mcp-profiles',
28
+ 'synabun-opencode-session-tools',
28
29
  ]);
29
30
  const prefixes = [
30
31
  'opencode-v2-tabs-',
@@ -138,6 +139,9 @@ const SYNC_HANDLERS = {
138
139
  'assistant:routing-changed': (msg) => emit('sync:assistant:routing-changed', msg),
139
140
  // Admin sandbox (lib/agent-sandbox-setting.js): the one global value changed in some window
140
141
  'agents:admin-sandbox-changed': (msg) => emit('sync:agents:admin-sandbox-changed', msg),
142
+ // Skills Service (lib/skills/service.js): a skill, a revision, a review or an installation changed (owner only) —
143
+ // the Studio reads its library again, Settings its chips, the Codex panel its skill hints
144
+ 'skills:changed': (msg) => emit('sync:skills:changed', msg),
141
145
 
142
146
  // WhatsApp Link — states and counters only (never a QR, code, number or text); Settings → WhatsApp re-reads /api/whatsapp/status
143
147
  'whatsapp:status': (msg) => emit('sync:whatsapp:status', msg),
@@ -154,10 +158,12 @@ const SYNC_HANDLERS = {
154
158
  // Browser auto-open (triggered by menu/keybind manual open)
155
159
  'browser:open': (msg) => emit('browser:open', msg),
156
160
 
157
- // MCP profile sync
158
- 'mcp:profile-changed': (msg) => {
159
- emit('mcp:profile-changed', msg);
160
- emit('sync:mcp:profile-changed', msg);
161
+ // Default tools sync
162
+ 'mcp:servers-changed': (msg) => { invalidateToolCatalog(msg); emit('mcp:servers-changed', msg); },
163
+ 'mcp:tools-default-changed': (msg) => {
164
+ updateCachedToolDefault(msg.tools);
165
+ emit('mcp:tools-default-changed', msg);
166
+ emit('sync:mcp:tools-default-changed', msg);
161
167
  },
162
168
 
163
169
  // Terminal link events
@@ -209,6 +215,10 @@ const SYNC_HANDLERS = {
209
215
  // restart, not only the one that asked for it (ui-loading.js).
210
216
  'server:restarting': (msg) => emit('server:restarting', msg),
211
217
 
218
+ // The notice after an automatic restart was dismissed in some window: every
219
+ // other one drops it too (ui-crash-notice.js).
220
+ 'crash-notice:changed': (msg) => emit('sync:crash-notice:changed', msg),
221
+
212
222
  // Session indexing progress
213
223
  'indexing:started': (msg) => emit('ws:message', msg),
214
224
  'indexing:session-started': (msg) => emit('ws:message', msg),
@@ -14,6 +14,7 @@ import { registerAction } from './ui-keybinds.js';
14
14
  import { isGuest, hasPermission } from './ui-sync.js';
15
15
  import { getWhiteboardElementById } from './ui-whiteboard.js';
16
16
  import { createFrameRenderer } from './utils.js';
17
+ import { frameRect, onFrameChange, clampBox, clampBoxLoose } from './app-frame.js';
17
18
  import { notify, NOTIF_TYPE } from './ui-notifications.js';
18
19
  import { getProviderMeta } from './provider-icons.js';
19
20
  import { readinessKind, typeWhenReady } from './term-ready.js';
@@ -27,7 +28,6 @@ import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf,
27
28
  const $ = (id) => document.getElementById(id);
28
29
  const CLI_PROFILES = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
29
30
  const NOTIF_TRACKED_CLI_PROFILES = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
30
- const CLI_PROFILES_NEED_PROFILER = new Set(['codex', 'gemini']); // Claude Code has tool search — no profiler needed
31
31
  const CLI_PROFILES_NEED_RECALL = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
32
32
 
33
33
  // ── Platform ──
@@ -617,79 +617,6 @@ function _cliBadgeHtml(sessionId) {
617
617
  : '';
618
618
  }
619
619
 
620
- // ── Floating terminal MCP profile dropdown ──
621
-
622
- let _floatProfileCache = null;
623
- let _floatProfileCacheTime = 0;
624
-
625
- async function _fetchFloatProfiles() {
626
- const now = Date.now();
627
- if (_floatProfileCache && now - _floatProfileCacheTime < 60000) return _floatProfileCache;
628
- try {
629
- const resp = await fetch('/api/mcp/profile');
630
- const data = await resp.json();
631
- if (data.ok) {
632
- _floatProfileCache = { current: data.profile || 'full', presets: data.presets || {} };
633
- _floatProfileCacheTime = now;
634
- }
635
- } catch {}
636
- return _floatProfileCache || { current: 'full', presets: {} };
637
- }
638
-
639
- function _initFloatProfileDropdown(dd) {
640
- const btn = dd.querySelector('.term-float-profile-btn');
641
- const menu = dd.querySelector('.term-float-profile-menu');
642
- const label = dd.querySelector('.term-float-profile-label');
643
-
644
- // Load current profile label
645
- _fetchFloatProfiles().then(data => {
646
- const p = data.presets[data.current];
647
- label.textContent = p?.label || data.current;
648
- });
649
-
650
- btn.addEventListener('click', async (e) => {
651
- e.stopPropagation();
652
- const wasOpen = menu.classList.contains('open');
653
- // Close all other open profile menus
654
- document.querySelectorAll('.term-float-profile-menu.open').forEach(m => m.classList.remove('open'));
655
- if (wasOpen) return;
656
-
657
- const data = await _fetchFloatProfiles();
658
- menu.innerHTML = '';
659
- for (const [id, preset] of Object.entries(data.presets)) {
660
- const opt = document.createElement('div');
661
- opt.className = 'term-float-profile-option' + (id === data.current ? ' selected' : '');
662
- opt.innerHTML = `<span class="tfp-name">${_escHtml(String(preset.label || id))}</span><span class="tfp-hint">${_escHtml(String(preset.tools))} tools</span>`;
663
- opt.addEventListener('click', async (ev) => {
664
- ev.stopPropagation();
665
- menu.classList.remove('open');
666
- if (id === data.current) return;
667
- label.textContent = preset.label || id;
668
- try {
669
- await fetch('/api/mcp/profile', {
670
- method: 'POST',
671
- headers: { 'Content-Type': 'application/json' },
672
- body: JSON.stringify({ profile: id }),
673
- });
674
- _floatProfileCache = null; // bust cache
675
- // Update all other floating profile dropdowns
676
- document.querySelectorAll('.term-float-profile-label').forEach(lbl => {
677
- lbl.textContent = preset.label || id;
678
- });
679
- } catch {}
680
- });
681
- menu.appendChild(opt);
682
- }
683
- menu.classList.add('open');
684
-
685
- // Close on outside click
686
- const close = (ev) => {
687
- if (!dd.contains(ev.target)) { menu.classList.remove('open'); document.removeEventListener('mousedown', close); }
688
- };
689
- setTimeout(() => document.addEventListener('mousedown', close), 0);
690
- });
691
- }
692
-
693
620
  // ── Floating terminal Recall profile dropdown ──
694
621
 
695
622
  const FLOAT_RECALL_META = [
@@ -2322,24 +2249,22 @@ function detachPanel() {
2322
2249
  // so left margin matches right margin — accounts for left sidebars and right sidepanel)
2323
2250
  let pos;
2324
2251
  try { pos = JSON.parse(storage.getItem(KEYS.TERMINAL_FLOAT_POS)); } catch {}
2252
+ const f = frameRect();
2325
2253
  if (!pos) {
2326
- const cs = getComputedStyle(document.documentElement);
2327
- const leftEdge = (parseFloat(cs.getPropertyValue('--explorer-width')) || 0)
2328
- + (parseFloat(cs.getPropertyValue('--file-explorer-width')) || 0);
2329
- const rightEdge = window.innerWidth - getRightPanelReservedWidth(cs);
2254
+ const { left: leftEdge, right: rightEdge } = _usableArea(f);
2330
2255
  const usableW = Math.max(0, rightEdge - leftEdge);
2331
2256
  const w = Math.min(800, usableW * 0.6);
2332
2257
  const h = Math.min(500, dockedH);
2333
2258
  pos = {
2334
2259
  left: leftEdge + (usableW - w) / 2,
2335
- top: Math.max(48, (window.innerHeight - h) / 2),
2260
+ top: Math.max(f.top, f.top + (f.height - h) / 2),
2336
2261
  width: w, height: h,
2337
2262
  };
2338
2263
  }
2339
2264
 
2340
2265
  _panel.classList.add('detached');
2341
- _panel.style.left = pos.left + 'px';
2342
- _panel.style.top = Math.max(48, pos.top) + 'px';
2266
+ _panel.style.left = Math.max(f.left, Math.min(pos.left, f.right - 80)) + 'px';
2267
+ _panel.style.top = Math.max(f.top, Math.min(pos.top, f.bottom - 80)) + 'px';
2343
2268
  _panel.style.width = pos.width + 'px';
2344
2269
  _panel.style.height = pos.height + 'px';
2345
2270
 
@@ -2436,7 +2361,7 @@ function initFloatDrag() {
2436
2361
  // Header drag for floating mode
2437
2362
  function _startPanelDrag(x, y) {
2438
2363
  const rect = _panel.getBoundingClientRect();
2439
- _floatDrag = { startX: x, startY: y, startL: rect.left, startT: rect.top };
2364
+ _floatDrag = { startX: x, startY: y, startL: rect.left, startT: rect.top, w: rect.width, h: rect.height, frame: frameRect() };
2440
2365
  pendingPanelDrag = null;
2441
2366
  _panel.classList.add('float-dragging');
2442
2367
  document.body.classList.add('ui-interacting');
@@ -2452,7 +2377,10 @@ function initFloatDrag() {
2452
2377
  finalL = Math.round(finalL / gs) * gs;
2453
2378
  finalT = Math.round(finalT / gs) * gs;
2454
2379
  }
2455
- finalT = Math.max(48, finalT);
2380
+ // Inside the app frame: the header stays below the bar, 80px of the window stays in reach
2381
+ const f = _floatDrag.frame || (_floatDrag.frame = frameRect());
2382
+ const w = _floatDrag.w || 0, h = _floatDrag.h || 0;
2383
+ ({ x: finalL, y: finalT } = clampBoxLoose(finalL, finalT, w, h, f, 80));
2456
2384
  // Transform during drag (composited, no layout); committed on dragend
2457
2385
  _floatDrag.lastL = finalL;
2458
2386
  _floatDrag.lastT = finalT;
@@ -2546,7 +2474,11 @@ function initFloatResize() {
2546
2474
  nl = Math.round(nl / gs) * gs; nt = Math.round(nt / gs) * gs;
2547
2475
  nw = Math.round(nw / gs) * gs; nh = Math.round(nh / gs) * gs;
2548
2476
  }
2549
- nt = Math.max(48, nt);
2477
+ const f = frameRect();
2478
+ if (nt < f.top) { if (dir.includes('n')) nh -= f.top - nt; nt = f.top; }
2479
+ if (nl < f.left) { if (dir.includes('w')) nw -= f.left - nl; nl = f.left; }
2480
+ nw = Math.max(MIN_W, Math.min(nw, f.right - nl));
2481
+ nh = Math.max(MIN_H, Math.min(nh, f.bottom - nt));
2550
2482
  _panel.style.left = nl + 'px';
2551
2483
  _panel.style.top = nt + 'px';
2552
2484
  _panel.style.width = nw + 'px';
@@ -2653,10 +2585,12 @@ function initResizeHandle() {
2653
2585
  _draggingResize = true;
2654
2586
  document.body.classList.add('ui-interacting');
2655
2587
  graphLayers = [$('graph-container'), $('static-bg')].filter(Boolean);
2588
+ // Up to 80% of the window, and never past the top of the app frame (its 12px inner padding kept)
2589
+ const dockBottom = _panel.getBoundingClientRect().bottom;
2590
+ const maxH = Math.max(MIN_HEIGHT, Math.min(window.innerHeight * 0.8, dockBottom - frameRect().top - 12));
2656
2591
 
2657
2592
  const onMove = (e) => {
2658
2593
  const dy = startY - e.clientY;
2659
- const maxH = window.innerHeight * 0.8;
2660
2594
  const newH = Math.max(MIN_HEIGHT, Math.min(maxH, startH + dy));
2661
2595
  scheduleHeight(newH);
2662
2596
  };
@@ -4180,12 +4114,13 @@ function initContextMenu(viewport, term, getWs) {
4180
4114
  menu.style.top = '0px';
4181
4115
  menu.classList.add('open');
4182
4116
  const rect = menu.getBoundingClientRect();
4117
+ const f = frameRect();
4183
4118
  let x = e.clientX;
4184
4119
  let y = e.clientY;
4185
- if (x + rect.width > window.innerWidth) x = window.innerWidth - rect.width - 4;
4186
- if (y + rect.height > window.innerHeight) y = e.clientY - rect.height;
4187
- if (x < 0) x = 4;
4188
- if (y < 0) y = 4;
4120
+ if (x + rect.width > f.right) x = f.right - rect.width - 4;
4121
+ if (y + rect.height > f.bottom) y = e.clientY - rect.height;
4122
+ if (x < f.left) x = f.left + 4;
4123
+ if (y < f.top) y = f.top + 4;
4189
4124
  menu.style.left = x + 'px';
4190
4125
  menu.style.top = y + 'px';
4191
4126
 
@@ -4415,7 +4350,8 @@ function sendMemoryDrop(node, ws) {
4415
4350
  ws.send(JSON.stringify({ type: 'memory_drop', content, title }));
4416
4351
  }
4417
4352
 
4418
- function formatMemoryForCLI(node) {
4353
+ // Also what the context menu sends to the AI panel (ui-context-menu.js).
4354
+ export function formatMemoryForCLI(node) {
4419
4355
  const p = node.payload;
4420
4356
  const parts = [];
4421
4357
 
@@ -4551,10 +4487,11 @@ function _placeFloatColorPicker(picker, colorBtn) {
4551
4487
  const inset = first ? first.left + first.width / 2 - box.left : 0;
4552
4488
  const margin = 8, gap = 6;
4553
4489
  const left = btn.left + btn.width / 2 - inset;
4490
+ const f = frameRect();
4554
4491
  let top = btn.top - gap - box.height;
4555
- if (top < margin) top = btn.bottom + gap;
4556
- picker.style.left = Math.max(margin, Math.min(left, window.innerWidth - box.width - margin)) + 'px';
4557
- picker.style.top = Math.max(margin, Math.min(top, window.innerHeight - box.height - margin)) + 'px';
4492
+ if (top < f.top + margin) top = btn.bottom + gap;
4493
+ picker.style.left = Math.max(f.left + margin, Math.min(left, f.right - box.width - margin)) + 'px';
4494
+ picker.style.top = Math.max(f.top + margin, Math.min(top, f.bottom - box.height - margin)) + 'px';
4558
4495
  }
4559
4496
 
4560
4497
  function getRightPanelReservedWidth(cs = getComputedStyle(document.documentElement)) {
@@ -4563,17 +4500,24 @@ function getRightPanelReservedWidth(cs = getComputedStyle(document.documentEleme
4563
4500
  || 0;
4564
4501
  }
4565
4502
 
4503
+ /** The app frame's area left for the floats: right of the explorers, left of a docked right sidepanel (its reserved
4504
+ * width counts from the window's right edge), below the bar. */
4505
+ function _usableArea(f = frameRect(), cs = getComputedStyle(document.documentElement)) {
4506
+ const reserved = getRightPanelReservedWidth(cs);
4507
+ return { left: f.left, top: f.top, right: Math.max(f.left, Math.min(f.right, window.innerWidth - reserved)), bottom: f.bottom };
4508
+ }
4509
+
4566
4510
  /** Update snapped-edge classes on all floating tabs based on adjacency */
4567
- /** Compute viewport/panel snap boundaries (getComputedStyle reads — cache during drag) */
4511
+ /** Compute frame/panel snap boundaries (getComputedStyle reads — cache during drag) */
4568
4512
  function _snapEnv() {
4569
- const cs = getComputedStyle(document.documentElement);
4570
- const explorerW = parseFloat(cs.getPropertyValue('--explorer-width')) || 0;
4571
- const fileExplorerW = parseFloat(cs.getPropertyValue('--file-explorer-width')) || 0;
4513
+ const f = frameRect();
4514
+ const area = _usableArea(f);
4572
4515
  return {
4573
- leftPanelEdge: explorerW + fileExplorerW,
4574
- rightPanelEdge: window.innerWidth - getRightPanelReservedWidth(cs),
4575
- topEdge: parseFloat(cs.getPropertyValue('--navbar-height')) || 48,
4576
- bottomEdge: window.innerHeight,
4516
+ leftPanelEdge: f.left,
4517
+ rightPanelEdge: area.right,
4518
+ frameRight: f.right,
4519
+ topEdge: f.top,
4520
+ bottomEdge: f.bottom,
4577
4521
  };
4578
4522
  }
4579
4523
 
@@ -4591,7 +4535,7 @@ function _updateSnappedEdges(rectsOverride, envOverride) {
4591
4535
  }
4592
4536
  }
4593
4537
 
4594
- const { leftPanelEdge, rightPanelEdge, topEdge, bottomEdge } = envOverride || _snapEnv();
4538
+ const { leftPanelEdge, rightPanelEdge, frameRight, topEdge, bottomEdge } = envOverride || _snapEnv();
4595
4539
 
4596
4540
  for (const [sid, dt] of _detachedTabs) {
4597
4541
  if (!dt.el || dt.minimized) continue;
@@ -4611,8 +4555,8 @@ function _updateSnappedEdges(rectsOverride, envOverride) {
4611
4555
  }
4612
4556
 
4613
4557
  // Check adjacency to viewport / panel edges
4614
- if (Math.abs(me.left - leftPanelEdge) < TOLERANCE || Math.abs(me.left) < TOLERANCE) edges.left = true;
4615
- if (Math.abs(me.right - rightPanelEdge) < TOLERANCE || Math.abs(me.right - window.innerWidth) < TOLERANCE) edges.right = true;
4558
+ if (Math.abs(me.left - leftPanelEdge) < TOLERANCE) edges.left = true;
4559
+ if (Math.abs(me.right - rightPanelEdge) < TOLERANCE || Math.abs(me.right - frameRight) < TOLERANCE) edges.right = true;
4616
4560
  if (Math.abs(me.top - topEdge) < TOLERANCE) edges.top = true;
4617
4561
  if (Math.abs(me.bottom - bottomEdge) < TOLERANCE) edges.bottom = true;
4618
4562
 
@@ -4668,20 +4612,16 @@ export function tileFloatingTerminals() {
4668
4612
  }
4669
4613
  if (entries.length === 0) return;
4670
4614
 
4671
- // Compute available whiteboard area (respect open panels)
4672
- const cs = getComputedStyle(document.documentElement);
4673
- const explorerW = parseFloat(cs.getPropertyValue('--explorer-width')) || 0;
4674
- const fileExplorerW = parseFloat(cs.getPropertyValue('--file-explorer-width')) || 0;
4675
- const sidebarW = explorerW + fileExplorerW;
4676
- const rightPanelW = getRightPanelReservedWidth(cs);
4615
+ // Compute the available area: the app frame, minus a docked right sidepanel and the docked terminal (which sits
4616
+ // 12px inside the frame's bottom: the tiles stop at its top)
4617
+ const area = _usableArea();
4677
4618
  const termPanel = document.getElementById('terminal-panel');
4678
- const termH = (termPanel && !termPanel.classList.contains('hidden') && !termPanel.classList.contains('detached'))
4679
- ? termPanel.getBoundingClientRect().height : 0;
4680
- const TOP_PAD = parseFloat(cs.getPropertyValue('--navbar-height')) || 48;
4681
- const areaL = sidebarW;
4682
- const areaT = TOP_PAD;
4683
- const areaW = window.innerWidth - sidebarW - rightPanelW;
4684
- const areaH = window.innerHeight - TOP_PAD - termH;
4619
+ const termTop = (termPanel && !termPanel.classList.contains('hidden') && !termPanel.classList.contains('detached'))
4620
+ ? termPanel.getBoundingClientRect().top : area.bottom;
4621
+ const areaL = area.left;
4622
+ const areaT = area.top;
4623
+ const areaW = Math.max(0, area.right - area.left);
4624
+ const areaH = Math.max(0, Math.min(area.bottom, termTop) - area.top);
4685
4625
 
4686
4626
  // Calculate grid: find best cols/rows to fill the space
4687
4627
  const n = entries.length;
@@ -4732,17 +4672,16 @@ function _defaultFloatRect(kind = {}) {
4732
4672
  const offset = _detachedTabs.size * 30;
4733
4673
  const isBrowser = kind.profile === 'browser';
4734
4674
  const isAssistant = !!kind._isAssistant || kind.profile === 'assistant';
4735
- const cs = getComputedStyle(document.documentElement);
4736
- const leftEdge = (parseFloat(cs.getPropertyValue('--explorer-width')) || 0)
4737
- + (parseFloat(cs.getPropertyValue('--file-explorer-width')) || 0);
4738
- const rightEdge = window.innerWidth - getRightPanelReservedWidth(cs);
4675
+ const f = frameRect();
4676
+ const { left: leftEdge, right: rightEdge } = _usableArea(f);
4739
4677
  const usableW = Math.max(0, rightEdge - leftEdge);
4740
4678
  // Assistant floats default to 780×560 (chat + agents dock need more room than a TUI)
4741
4679
  const w = Math.round(Math.min(isBrowser ? 960 : (isAssistant ? 780 : 700), usableW * (isBrowser ? 0.65 : (isAssistant ? 0.6 : 0.5))));
4742
- const h = Math.round(Math.min(isBrowser ? 640 : (isAssistant ? 560 : 420), window.innerHeight * (isBrowser ? 0.6 : (isAssistant ? 0.7 : 0.5))));
4680
+ const h = Math.round(Math.min(isBrowser ? 640 : (isAssistant ? 560 : 420), f.height * (isBrowser ? 0.6 : (isAssistant ? 0.7 : 0.5))));
4681
+ const at = clampBox(leftEdge + (usableW - w) / 2 + offset, f.top + (f.height - h) / 2 + offset, w, h, f);
4743
4682
  return {
4744
- left: Math.round(leftEdge + (usableW - w) / 2 + offset),
4745
- top: Math.round(Math.max(48, (window.innerHeight - h) / 2 + offset)),
4683
+ left: Math.round(at.x),
4684
+ top: Math.round(at.y),
4746
4685
  width: w,
4747
4686
  height: h,
4748
4687
  };
@@ -4792,10 +4731,6 @@ function detachTab(idx, opts = {}) {
4792
4731
  <span class="term-float-tab-icon">${prof?.svg || SVG_SHELL}</span>
4793
4732
  <button class="term-float-color-strip" data-tooltip="Change color"></button>
4794
4733
  <span class="term-float-tab-title">${_escHtml(_displayLabel(session))}</span>${session._isAssistant ? '<span class="term-float-brain-chip" data-tooltip="Brain" hidden></span>' : ''}${_cliBadgeHtml(session.id)}
4795
- ${CLI_PROFILES_NEED_PROFILER.has(session.profile) ? `<div class="term-float-profile-dd" data-tooltip="Tool profile">
4796
- <button class="term-float-profile-btn"><span class="term-float-profile-label">...</span><span class="term-float-profile-arrow">&#x25BE;</span></button>
4797
- <div class="term-float-profile-menu"></div>
4798
- </div>` : ''}
4799
4734
  ${CLI_PROFILES_NEED_RECALL.has(session.profile) ? `<div class="term-float-recall-dd" data-tooltip="Recall profile">
4800
4735
  <button class="term-float-recall-btn"><span class="term-float-recall-label">...</span><span class="term-float-recall-arrow">&#x25BE;</span></button>
4801
4736
  <div class="term-float-recall-menu"></div>
@@ -5018,11 +4953,6 @@ function detachTab(idx, opts = {}) {
5018
4953
  });
5019
4954
  }
5020
4955
 
5021
- // Wire MCP profile dropdown (CLI profiles only)
5022
- const profileDd = win.querySelector('.term-float-profile-dd');
5023
- if (profileDd) {
5024
- _initFloatProfileDropdown(profileDd);
5025
- }
5026
4956
 
5027
4957
  // Wire recall profile dropdown
5028
4958
  const recallDd = win.querySelector('.term-float-recall-dd');
@@ -5306,7 +5236,7 @@ function initTabFloatDrag(win, sessionId) {
5306
5236
 
5307
5237
  function _checkHeader(target) {
5308
5238
  const header = target.closest('.term-float-tab-header');
5309
- if (!header || target.closest('button, .term-float-profile-dd')) return false;
5239
+ if (!header || target.closest('button')) return false;
5310
5240
  const session = _sessions.find(s => s.id === sessionId);
5311
5241
  return !session?.pinned;
5312
5242
  }
@@ -5343,8 +5273,9 @@ function initTabFloatDrag(win, sessionId) {
5343
5273
  const KEEP = 80;
5344
5274
  const w = drag.startW || parseFloat(win.style.width) || 700;
5345
5275
  const h = drag.startH || parseFloat(win.style.height) || 420;
5346
- finalL = Math.max(KEEP - w, Math.min(finalL, window.innerWidth - KEEP));
5347
- finalT = Math.max(48, Math.min(finalT, window.innerHeight - KEEP));
5276
+ const env = drag.snapEnv;
5277
+ const frame = { left: env.leftPanelEdge, top: env.topEdge, right: env.frameRight, bottom: env.bottomEdge };
5278
+ ({ x: finalL, y: finalT } = clampBoxLoose(finalL, finalT, w, h, frame, KEEP));
5348
5279
 
5349
5280
  // Snap to other floating windows (rects cached at drag start)
5350
5281
  const SNAP_DIST = 12;
@@ -5375,11 +5306,12 @@ function initTabFloatDrag(win, sessionId) {
5375
5306
  else if (Math.abs(myB - or.bottom) < SNAP_DIST) { finalT = or.bottom - h; snapped = true; }
5376
5307
  }
5377
5308
  }
5378
- // Also snap to viewport edges
5379
- if (Math.abs(finalL) < SNAP_DIST) { finalL = 0; snapped = true; }
5380
- if (Math.abs(finalT - 48) < SNAP_DIST) { finalT = 48; snapped = true; }
5381
- if (Math.abs(myR - window.innerWidth) < SNAP_DIST) { finalL = window.innerWidth - w; snapped = true; }
5382
- if (Math.abs(myB - window.innerHeight) < SNAP_DIST) { finalT = window.innerHeight - h; snapped = true; }
5309
+ // Also snap to the app frame's edges (and a docked right sidepanel's)
5310
+ if (Math.abs(finalL - frame.left) < SNAP_DIST) { finalL = frame.left; snapped = true; }
5311
+ if (Math.abs(finalT - frame.top) < SNAP_DIST) { finalT = frame.top; snapped = true; }
5312
+ if (Math.abs(myR - frame.right) < SNAP_DIST) { finalL = frame.right - w; snapped = true; }
5313
+ else if (Math.abs(myR - env.rightPanelEdge) < SNAP_DIST) { finalL = env.rightPanelEdge - w; snapped = true; }
5314
+ if (Math.abs(myB - frame.bottom) < SNAP_DIST) { finalT = frame.bottom - h; snapped = true; }
5383
5315
 
5384
5316
  win.classList.toggle('float-snapping', snapped);
5385
5317
  // Transform-based positioning: left/top force layout every frame; a
@@ -5498,7 +5430,9 @@ function initTabFloatResize(win, sessionId) {
5498
5430
  nl = Math.round(nl / gs) * gs; nt = Math.round(nt / gs) * gs;
5499
5431
  nw = Math.round(nw / gs) * gs; nh = Math.round(nh / gs) * gs;
5500
5432
  }
5501
- nt = Math.max(48, nt);
5433
+ // The header never goes above the app frame's top (a top edge stops there, the bottom edge stays put)
5434
+ const frameTop = frameRect().top;
5435
+ if (nt < frameTop) { if (dir.includes('n')) nh = Math.max(MIN_H, nh - (frameTop - nt)); nt = frameTop; }
5502
5436
  win.style.left = nl + 'px';
5503
5437
  win.style.top = nt + 'px';
5504
5438
  win.style.width = nw + 'px';
@@ -5841,41 +5775,48 @@ export async function initTerminal() {
5841
5775
  }
5842
5776
  });
5843
5777
 
5844
- // ── Window resize — clamp floating tabs on screen, refit all terminals ──
5778
+ // ── Window resize, an explorer opening or closing — keep the floats in the app frame, refit all terminals ──
5779
+ const clampFloatsToFrame = () => {
5780
+ const f = frameRect();
5781
+ const KEEP = 80; // min px of tab that must remain visible
5782
+ const floats = [..._detachedTabs.values()].filter(tabState => !tabState.minimized).map(tabState => tabState.el);
5783
+ if (_detached && _panel && !_panel.classList.contains('hidden')) floats.push(_panel);
5784
+
5785
+ for (const el of floats) {
5786
+ if (!el) continue;
5787
+ let l = parseFloat(el.style.left) || 0;
5788
+ let t = parseFloat(el.style.top) || 0;
5789
+ let w = parseFloat(el.style.width) || 700;
5790
+ let h = parseFloat(el.style.height) || 420;
5791
+
5792
+ // Clamp dimensions to the frame
5793
+ w = Math.min(w, f.width);
5794
+ h = Math.min(h, f.height);
5795
+
5796
+ // Clamp position so the header stays reachable, below the title bar and right of the explorers
5797
+ ({ x: l, y: t } = clampBoxLoose(l, t, w, h, f, KEEP));
5798
+
5799
+ el.style.left = l + 'px';
5800
+ el.style.top = t + 'px';
5801
+ el.style.width = w + 'px';
5802
+ el.style.height = h + 'px';
5803
+ }
5804
+
5805
+ // Refit all terminals to new dimensions
5806
+ _sessions.forEach(s => _scheduleFit(s));
5807
+ };
5845
5808
  window.addEventListener('resize', () => {
5846
5809
  if (_windowResizeTimer) clearTimeout(_windowResizeTimer);
5847
5810
  _windowResizeTimer = setTimeout(() => {
5848
5811
  _windowResizeTimer = null;
5849
- const vw = window.innerWidth;
5850
- const vh = window.innerHeight;
5851
- const KEEP = 80; // min px of tab that must remain visible
5852
-
5853
- for (const [, tabState] of _detachedTabs) {
5854
- if (tabState.minimized) continue;
5855
- const el = tabState.el;
5856
- let l = parseFloat(el.style.left) || 0;
5857
- let t = parseFloat(el.style.top) || 0;
5858
- let w = parseFloat(el.style.width) || 700;
5859
- let h = parseFloat(el.style.height) || 420;
5860
-
5861
- // Clamp dimensions to viewport
5862
- w = Math.min(w, vw);
5863
- h = Math.min(h, vh);
5864
-
5865
- // Clamp position so header stays reachable and below title bar
5866
- l = Math.max(KEEP - w, Math.min(l, vw - KEEP));
5867
- t = Math.max(48, Math.min(t, vh - KEEP));
5868
-
5869
- el.style.left = l + 'px';
5870
- el.style.top = t + 'px';
5871
- el.style.width = w + 'px';
5872
- el.style.height = h + 'px';
5873
- }
5874
-
5875
- // Refit all terminals to new dimensions
5876
- _sessions.forEach(s => _scheduleFit(s));
5812
+ clampFloatsToFrame();
5877
5813
  }, 150);
5878
5814
  });
5815
+ onFrameChange(() => {
5816
+ if (_windowResizeTimer) return; // the resize pass runs shortly
5817
+ clampFloatsToFrame();
5818
+ _updateSnappedEdges();
5819
+ });
5879
5820
 
5880
5821
  // ── Visibility change — flow control, warm renderers, one focus target ──
5881
5822
  document.addEventListener('visibilitychange', () => {
@@ -6482,11 +6423,13 @@ async function attachDetached(terminalSessionId, profile, initialMessage, autoSu
6482
6423
  if (dt?.el) {
6483
6424
  const panelWidth = getRightPanelReservedWidth();
6484
6425
  if (panelWidth > 0) {
6485
- const availWidth = window.innerWidth - panelWidth - 16;
6486
- const w = Math.min(700, availWidth * 0.6);
6487
- const h = Math.min(500, window.innerHeight - 96);
6488
- dt.el.style.left = (window.innerWidth - panelWidth - w - 24) + 'px';
6489
- dt.el.style.top = '56px';
6426
+ // Beside the panel, 24px from its slot, at the top of the app frame
6427
+ const f = frameRect();
6428
+ const area = _usableArea(f);
6429
+ const w = Math.min(700, Math.max(0, area.right - area.left - 16) * 0.6);
6430
+ const h = Math.min(500, f.height - 40);
6431
+ dt.el.style.left = Math.max(f.left, area.right - w - 24) + 'px';
6432
+ dt.el.style.top = f.top + 'px';
6490
6433
  dt.el.style.width = w + 'px';
6491
6434
  dt.el.style.height = h + 'px';
6492
6435
  }
@@ -6669,10 +6612,11 @@ function applyTerminalLayout(snap) {
6669
6612
  const minW = 280, minH = 160;
6670
6613
  const w = Math.max(minW, dt.width || minW);
6671
6614
  const h = Math.max(minH, dt.height || minH);
6672
- const maxL = window.innerWidth - 80;
6673
- const maxT = window.innerHeight - 40;
6674
- const l = Math.max(0, Math.min(dt.left || 0, maxL));
6675
- const t = Math.max(48, Math.min(dt.top || 0, maxT));
6615
+ const f = frameRect();
6616
+ const maxL = f.right - 80;
6617
+ const maxT = f.bottom - 40;
6618
+ const l = Math.max(f.left, Math.min(dt.left || 0, maxL));
6619
+ const t = Math.max(f.top, Math.min(dt.top || 0, maxT));
6676
6620
 
6677
6621
  tabState.el.style.left = l + 'px';
6678
6622
  tabState.el.style.top = t + 'px';
@@ -239,7 +239,9 @@ function _toastEsc(s) {
239
239
  }
240
240
 
241
241
  export function showUpdateToast({ updates, errors, onClick } = {}) {
242
- document.querySelectorAll('.update-check-toast').forEach(el => el.remove());
242
+ // One update toast at a time; the notice after an automatic restart (ui-crash-notice.js) wears the same toast
243
+ // and stays until it is dismissed.
244
+ document.querySelectorAll('.update-check-toast').forEach(el => { if (!el.classList.contains('crash-notice')) el.remove(); });
243
245
 
244
246
  const toast = document.createElement('div');
245
247
  toast.className = 'update-check-toast';
@@ -3,6 +3,8 @@
3
3
  // Body-appended, overflow-proof tooltip for data-tooltip attributes
4
4
  // ═══════════════════════════════════════════
5
5
 
6
+ import { boundsForPoint, frameRect } from './app-frame.js';
7
+
6
8
  /**
7
9
  * Initialize the tooltip system.
8
10
  * Creates a body-appended tooltip element and delegates
@@ -41,13 +43,16 @@ export function initTooltip() {
41
43
  const th = tip.offsetHeight;
42
44
  const gap = 8;
43
45
  const preferred = el.getAttribute('data-tooltip-pos');
46
+ // Kept inside the app frame for a control in it; one on the bar or an explorer keeps the window.
47
+ const b = boundsForPoint(frameRect(), r.left + r.width / 2, r.top + r.height / 2)
48
+ || { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
44
49
 
45
50
  // Left-side placement (session dropdowns in sidepanels)
46
51
  // Anchor to the left edge of the containing sidepanel so tooltip always appears fully outside
47
52
  if (preferred === 'left') {
48
53
  const panel = el.closest('.claude-panel, .codex-panel, .ocp-panel, .assistant-panel, .menubar-dropdown--resume');
49
54
  const anchorLeft = panel ? panel.getBoundingClientRect().left : r.left;
50
- if (anchorLeft - gap - tw > 4) {
55
+ if (anchorLeft - gap - tw > b.left + 4) {
51
56
  setPlacement('left');
52
57
  tip.style.top = (r.top + r.height / 2 - th / 2) + 'px';
53
58
  tip.style.left = (anchorLeft - gap - tw) + 'px';
@@ -60,7 +65,7 @@ export function initTooltip() {
60
65
  const inWbToolbar = !!el.closest('#wb-toolbar');
61
66
  const rightAnchor = el.closest('.menubar-dropdown--resume');
62
67
  const anchorRight = rightAnchor ? rightAnchor.getBoundingClientRect().right : r.right;
63
- if ((preferred === 'right' || inWbToolbar) && anchorRight + gap + tw < window.innerWidth - 4) {
68
+ if ((preferred === 'right' || inWbToolbar) && anchorRight + gap + tw < b.right - 4) {
64
69
  setPlacement('right');
65
70
  tip.style.top = (r.top + r.height / 2 - th / 2) + 'px';
66
71
  tip.style.left = (anchorRight + gap) + 'px';
@@ -72,13 +77,13 @@ export function initTooltip() {
72
77
  const forceBelow = preferred === 'below' || el.closest('#title-bar');
73
78
  let top = forceBelow ? r.bottom + gap : r.top - th - gap;
74
79
  let placement = forceBelow ? 'below' : 'above';
75
- if (!forceBelow && top < 4) {
80
+ if (!forceBelow && top < b.top + 4) {
76
81
  top = r.bottom + gap;
77
82
  placement = 'below';
78
83
  }
79
84
  let left = r.left + r.width / 2 - tw / 2;
80
- if (left < 4) left = 4;
81
- if (left + tw > window.innerWidth - 4) left = window.innerWidth - 4 - tw;
85
+ if (left + tw > b.right - 4) left = b.right - 4 - tw;
86
+ if (left < b.left + 4) left = b.left + 4;
82
87
  setPlacement(placement);
83
88
  tip.style.top = top + 'px';
84
89
  tip.style.left = left + 'px';