synabun 2.0.10 → 2.2.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 (656) hide show
  1. package/CHANGELOG.md +207 -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 +13 -13
  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 +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  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 +302 -0
  24. package/lib/virustotal.mjs +203 -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/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -4,7 +4,7 @@
4
4
  // ═══════════════════════════════════════════
5
5
  //
6
6
  // Provides a docked bottom terminal panel with tabbed sessions.
7
- // Profiles: Claude Code, Codex CLI, Gemini CLI, Shell.
7
+ // Profiles: Claude Code, Codex CLI, Antigravity CLI, OpenCode, Shell.
8
8
 
9
9
  import { state, emit, on } from './state.js';
10
10
  import { KEYS } from './constants.js';
@@ -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';
@@ -22,13 +23,12 @@ import { mountAssistant } from './assistant/asst-panel.js';
22
23
  import { getAssistantSession } from './assistant/asst-api.js';
23
24
  import { mascotCameoSvg } from './synabun-mascot.js';
24
25
  import { brainLabel, normalizeBrain, readStoredBrain, restoredSessionName, sessionNameGivenUp } from './assistant/asst-state.js';
25
- import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, fetchLiveAssistantSessions, isAssistantSessionLive } from './ui-assistant.js';
26
+ import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, readLiveAssistantSessions, assistantBlocked, isAssistantSessionLive } from './ui-assistant.js';
26
27
 
27
28
  const $ = (id) => document.getElementById(id);
28
- const CLI_PROFILES = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
29
- 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
- const CLI_PROFILES_NEED_RECALL = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
29
+ const CLI_PROFILES = new Set(['claude-code', 'codex', 'antigravity', 'opencode']);
30
+ const NOTIF_TRACKED_CLI_PROFILES = new Set(['claude-code', 'codex', 'antigravity', 'opencode']);
31
+ const CLI_PROFILES_NEED_RECALL = new Set(['claude-code', 'codex', 'antigravity', 'opencode']);
32
32
 
33
33
  // ── Platform ──
34
34
  // macOS gets Terminal.app keys (Cmd shortcuts, Option word-jumps, Ctrl keys
@@ -144,7 +144,7 @@ function _injectPathIntoTerm(term, ws, path) {
144
144
 
145
145
  const SVG_CLAUDE = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"/></svg>';
146
146
  const SVG_OPENAI = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"/></svg>';
147
- const SVG_GEMINI = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"/></svg>';
147
+ const SVG_ANTIGRAVITY = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 20 12 4l7.5 16"/><path d="M8.2 20a3.8 3.8 0 0 1 7.6 0"/></svg>';
148
148
  const SVG_OPENCODE = '<svg viewBox="0 0 240 300" fill="currentColor"><path fill-rule="evenodd" d="M0 0h240v300H0V0zm60 60v180h120V60H60z"/><rect x="60" y="120" width="120" height="120" opacity=".45"/></svg>';
149
149
  const SVG_SHELL = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>';
150
150
  const SVG_GIT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>';
@@ -156,7 +156,7 @@ const PROFILES = [
156
156
  { id: 'assistant', label: 'Assistant', svg: SVG_ASSISTANT, color: '#FFD23C' }, // non-PTY: hosts assistant/asst-panel.js
157
157
  { id: 'claude-code', label: 'Claude Code', svg: SVG_CLAUDE, color: '#D4A27F' },
158
158
  { id: 'codex', label: 'Codex CLI', svg: SVG_OPENAI, color: '#74c7a5' },
159
- { id: 'gemini', label: 'Gemini CLI', svg: SVG_GEMINI, color: '#669DF6' },
159
+ { id: 'antigravity', label: 'Antigravity CLI', svg: SVG_ANTIGRAVITY, color: '#669DF6' },
160
160
  { id: 'opencode', label: 'OpenCode', svg: SVG_OPENCODE, color: '#E8E0DC' },
161
161
  { id: 'shell', label: 'Shell', svg: SVG_SHELL, color: '#aaaaaa' },
162
162
  { id: 'browser', label: 'Browser', svg: SVG_BROWSER, color: '#4fc3f7' },
@@ -248,6 +248,12 @@ let _closingIds = new Set(); // session IDs currently being closed (prevents r
248
248
  let _opening = false; // true while openSession/openBrowserSession is in progress
249
249
  let _openingAt = 0; // timestamp when _opening was set true (auto-reset after 15s)
250
250
  let _restoringLayout = false; // true during reconnect+layout restore (suppresses layout saves)
251
+ // Saved Assistant tabs whose session list could not be read yet (server still starting).
252
+ // Kept in the registry on every save until a read succeeds; never pruned on a failed read.
253
+ let _waitingAssistant = [];
254
+ let _waitingAssistantTimer = null;
255
+ const _waitingPlace = new Map(); // waiting Assistant tab id → { float, order, pane, paneActive, switches } from the saved layout
256
+ const ASSISTANT_RESTORE_RETRY_MS = [500, 1000, 2000, 4000, 8000, 15_000];
251
257
  let _windowResizeTimer = null; // debounce handle for window resize clamping
252
258
 
253
259
  // ── Split pane state ──
@@ -256,6 +262,7 @@ let _focusedPane = 0; // 0=left, 1=right — which pane has keyboard
256
262
  let _paneAssignments = new Map(); // sessionId → 0|1 (which pane a session belongs to)
257
263
  let _activePaneIdx = [-1, -1]; // per-pane active session index (into _sessions)
258
264
  let _splitRatio = 0.5; // left pane width fraction (0.3–0.7)
265
+ let _paneSwitches = [0, 0]; // per-pane count of tab switches outside a restore
259
266
 
260
267
  const DEFAULT_HEIGHT = 320;
261
268
  const MIN_HEIGHT = 120;
@@ -617,79 +624,6 @@ function _cliBadgeHtml(sessionId) {
617
624
  : '';
618
625
  }
619
626
 
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
627
  // ── Floating terminal Recall profile dropdown ──
694
628
 
695
629
  const FLOAT_RECALL_META = [
@@ -1683,6 +1617,7 @@ function saveSessionRegistry() {
1683
1617
  brain: s._isAssistant ? (s._brain || prev?.brain || null) : null,
1684
1618
  });
1685
1619
  }
1620
+ for (const w of _waitingAssistant) if (!seen.has(w.id)) seen.set(w.id, w);
1686
1621
  storage.setItem(KEYS.TERMINAL_SESSIONS, JSON.stringify([...seen.values()]));
1687
1622
  }
1688
1623
 
@@ -1719,6 +1654,14 @@ function saveTerminalLayout() {
1719
1654
  if (_restoringLayout) return; // don't overwrite saved layout during reconnect
1720
1655
  try {
1721
1656
  const snap = getTerminalSnapshot();
1657
+ // A float still waiting for its session keeps its saved place.
1658
+ for (const w of _waitingAssistant) {
1659
+ const float = _waitingPlace.get(w.id)?.float;
1660
+ if (float && !snap.detachedTabs.some(dt => dt.sessionId === w.id)) snap.detachedTabs.push(float);
1661
+ // …and a docked one its split pane.
1662
+ const pane = _waitingPlace.get(w.id)?.pane;
1663
+ if (snap.splitMode && pane != null && !(w.id in snap.paneAssignments)) snap.paneAssignments[w.id] = pane;
1664
+ }
1722
1665
  storage.setItem(KEYS.TERMINAL_SESSIONS + '-layout', JSON.stringify(snap));
1723
1666
  } catch {}
1724
1667
  }
@@ -2322,24 +2265,22 @@ function detachPanel() {
2322
2265
  // so left margin matches right margin — accounts for left sidebars and right sidepanel)
2323
2266
  let pos;
2324
2267
  try { pos = JSON.parse(storage.getItem(KEYS.TERMINAL_FLOAT_POS)); } catch {}
2268
+ const f = frameRect();
2325
2269
  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);
2270
+ const { left: leftEdge, right: rightEdge } = _usableArea(f);
2330
2271
  const usableW = Math.max(0, rightEdge - leftEdge);
2331
2272
  const w = Math.min(800, usableW * 0.6);
2332
2273
  const h = Math.min(500, dockedH);
2333
2274
  pos = {
2334
2275
  left: leftEdge + (usableW - w) / 2,
2335
- top: Math.max(48, (window.innerHeight - h) / 2),
2276
+ top: Math.max(f.top, f.top + (f.height - h) / 2),
2336
2277
  width: w, height: h,
2337
2278
  };
2338
2279
  }
2339
2280
 
2340
2281
  _panel.classList.add('detached');
2341
- _panel.style.left = pos.left + 'px';
2342
- _panel.style.top = Math.max(48, pos.top) + 'px';
2282
+ _panel.style.left = Math.max(f.left, Math.min(pos.left, f.right - 80)) + 'px';
2283
+ _panel.style.top = Math.max(f.top, Math.min(pos.top, f.bottom - 80)) + 'px';
2343
2284
  _panel.style.width = pos.width + 'px';
2344
2285
  _panel.style.height = pos.height + 'px';
2345
2286
 
@@ -2436,7 +2377,7 @@ function initFloatDrag() {
2436
2377
  // Header drag for floating mode
2437
2378
  function _startPanelDrag(x, y) {
2438
2379
  const rect = _panel.getBoundingClientRect();
2439
- _floatDrag = { startX: x, startY: y, startL: rect.left, startT: rect.top };
2380
+ _floatDrag = { startX: x, startY: y, startL: rect.left, startT: rect.top, w: rect.width, h: rect.height, frame: frameRect() };
2440
2381
  pendingPanelDrag = null;
2441
2382
  _panel.classList.add('float-dragging');
2442
2383
  document.body.classList.add('ui-interacting');
@@ -2452,7 +2393,10 @@ function initFloatDrag() {
2452
2393
  finalL = Math.round(finalL / gs) * gs;
2453
2394
  finalT = Math.round(finalT / gs) * gs;
2454
2395
  }
2455
- finalT = Math.max(48, finalT);
2396
+ // Inside the app frame: the header stays below the bar, 80px of the window stays in reach
2397
+ const f = _floatDrag.frame || (_floatDrag.frame = frameRect());
2398
+ const w = _floatDrag.w || 0, h = _floatDrag.h || 0;
2399
+ ({ x: finalL, y: finalT } = clampBoxLoose(finalL, finalT, w, h, f, 80));
2456
2400
  // Transform during drag (composited, no layout); committed on dragend
2457
2401
  _floatDrag.lastL = finalL;
2458
2402
  _floatDrag.lastT = finalT;
@@ -2546,7 +2490,11 @@ function initFloatResize() {
2546
2490
  nl = Math.round(nl / gs) * gs; nt = Math.round(nt / gs) * gs;
2547
2491
  nw = Math.round(nw / gs) * gs; nh = Math.round(nh / gs) * gs;
2548
2492
  }
2549
- nt = Math.max(48, nt);
2493
+ const f = frameRect();
2494
+ if (nt < f.top) { if (dir.includes('n')) nh -= f.top - nt; nt = f.top; }
2495
+ if (nl < f.left) { if (dir.includes('w')) nw -= f.left - nl; nl = f.left; }
2496
+ nw = Math.max(MIN_W, Math.min(nw, f.right - nl));
2497
+ nh = Math.max(MIN_H, Math.min(nh, f.bottom - nt));
2550
2498
  _panel.style.left = nl + 'px';
2551
2499
  _panel.style.top = nt + 'px';
2552
2500
  _panel.style.width = nw + 'px';
@@ -2653,10 +2601,12 @@ function initResizeHandle() {
2653
2601
  _draggingResize = true;
2654
2602
  document.body.classList.add('ui-interacting');
2655
2603
  graphLayers = [$('graph-container'), $('static-bg')].filter(Boolean);
2604
+ // Up to 80% of the window, and never past the top of the app frame (its 12px inner padding kept)
2605
+ const dockBottom = _panel.getBoundingClientRect().bottom;
2606
+ const maxH = Math.max(MIN_HEIGHT, Math.min(window.innerHeight * 0.8, dockBottom - frameRect().top - 12));
2656
2607
 
2657
2608
  const onMove = (e) => {
2658
2609
  const dy = startY - e.clientY;
2659
- const maxH = window.innerHeight * 0.8;
2660
2610
  const newH = Math.max(MIN_HEIGHT, Math.min(maxH, startH + dy));
2661
2611
  scheduleHeight(newH);
2662
2612
  };
@@ -3596,6 +3546,107 @@ function _assistantOwnedElsewhere(sessionId) {
3596
3546
  }
3597
3547
 
3598
3548
  /** Reconnect a saved assistant tab (registry / workspace) to its live server session. */
3549
+ /** Keep saved Assistant tabs while the session list cannot be read, and retry the read. */
3550
+ function _waitAssistantRestore(entries, { layout = null, order = [] } = {}) {
3551
+ const known = new Set(_waitingAssistant.map(w => w.id));
3552
+ for (const e of entries) {
3553
+ if (!known.has(e.id)) _waitingAssistant.push(e);
3554
+ // Where the tab was: its float (position, size) from the saved layout, or its docked place.
3555
+ const float = (layout?.detachedTabs || []).find(dt => dt?.sessionId === e.id) || null;
3556
+ // In split mode, its pane and whether it was that pane's active tab.
3557
+ const p = layout?.splitMode ? layout.paneAssignments?.[e.id] : null;
3558
+ const pane = p === 0 || p === 1 ? p : null;
3559
+ const paneActive = pane != null && (layout.sessions || [])[layout.activePaneIdx?.[pane]]?.id === e.id;
3560
+ _waitingPlace.set(e.id, { float, order: order.map(o => o.id), pane, paneActive, switches: null });
3561
+ }
3562
+ if (_waitingAssistantTimer) return;
3563
+ const attempt = async (n) => {
3564
+ _waitingAssistantTimer = null;
3565
+ if (!_waitingAssistant.length || assistantBlocked()) return;
3566
+ const live = await readLiveAssistantSessions();
3567
+ if (!live) {
3568
+ // Out of retries: the entries stay saved for the next page load.
3569
+ if (n < ASSISTANT_RESTORE_RETRY_MS.length) _waitingAssistantTimer = setTimeout(() => attempt(n + 1), ASSISTANT_RESTORE_RETRY_MS[n]);
3570
+ return;
3571
+ }
3572
+ const waiting = _waitingAssistant;
3573
+ _waitingAssistant = [];
3574
+ // A late restore is silent: no panel opened, the active tab kept, each tab back where it was saved.
3575
+ const active = _sessions[_activeIdx] || null;
3576
+ const restored = [];
3577
+ _restoringLayout = true;
3578
+ try {
3579
+ for (const saved of waiting) {
3580
+ if (!live.has(saved.id) || _sessions.some(s => s.id === saved.id)) continue;
3581
+ try { const s = await reconnectAssistantSession(saved.id, live.get(saved.id), saved); if (s) restored.push(s); }
3582
+ catch (err) { console.warn('[assistant] reconnect failed:', saved.id, err); }
3583
+ }
3584
+ } finally { _restoringLayout = false; }
3585
+ if (active) _activeIdx = _sessions.indexOf(active);
3586
+ for (const s of restored) _placeLateAssistant(s, _waitingPlace.get(s.id));
3587
+ _waitingPlace.clear();
3588
+ if (active) _activeIdx = _sessions.indexOf(active);
3589
+ else {
3590
+ // Nothing was active: the first docked tab is, unhidden for when the panel is opened.
3591
+ _activeIdx = _sessions.findIndex(s => !_detachedTabs.has(s.id));
3592
+ const first = _sessions[_activeIdx];
3593
+ if (first?.viewport) first.viewport.style.display = '';
3594
+ }
3595
+ renderTabBar();
3596
+ saveSessionRegistry(); // prunes the ones whose sessions are really gone
3597
+ saveTerminalLayout();
3598
+ };
3599
+ _waitingAssistantTimer = setTimeout(() => attempt(1), ASSISTANT_RESTORE_RETRY_MS[0]);
3600
+ }
3601
+
3602
+ /** The restore that started the wait is done: pane switches from now on are the user's. */
3603
+ function _markLatePaneSwitches() {
3604
+ for (const place of _waitingPlace.values()) if (!place.switches) place.switches = [..._paneSwitches];
3605
+ }
3606
+
3607
+ /** A tab a late restore mounted goes where it was saved: its float, or its docked place in the saved order (and split pane). */
3608
+ function _placeLateAssistant(session, place) {
3609
+ if (place?.float) {
3610
+ if (session.viewport) session.viewport.style.display = '';
3611
+ applyTerminalLayout({ detachedTabs: [place.float] });
3612
+ return;
3613
+ }
3614
+ if (!_splitMode) { _orderLateAssistant(session, place?.order || []); return; }
3615
+ // Split is on, so both panes exist: its saved pane, else its neighbour's. The user's layout stays as it is.
3616
+ const paneActiveIds = _activePaneIdx.map(i => _sessions[i]?.id ?? null);
3617
+ const pane = place?.pane ?? null;
3618
+ let order = place?.order || [];
3619
+ if (pane != null) order = order.filter(id => id === session.id || _paneAssignments.get(id) === pane);
3620
+ const neighbour = _orderLateAssistant(session, order);
3621
+ const target = pane ?? _paneAssignments.get(neighbour?.id) ?? _focusedPane;
3622
+ _paneAssignments.set(session.id, target);
3623
+ _panel?.querySelector(`.term-pane[data-pane="${target}"] .term-pane-body`)?.appendChild(session.viewport);
3624
+ _activePaneIdx = paneActiveIds.map(id => _sessions.findIndex(s => s.id === id));
3625
+ // It was the pane's active tab and nobody switched tabs there since: it is again.
3626
+ const untouched = place?.switches && place.switches[target] === _paneSwitches[target];
3627
+ if (pane != null && place.paneActive && untouched) switchToSessionInPane(_sessions.indexOf(session), target);
3628
+ else if (session.viewport) session.viewport.style.display = 'none';
3629
+ }
3630
+
3631
+ /** Move a late tab next to its nearest neighbour in `order`; returns that neighbour (or null). */
3632
+ function _orderLateAssistant(session, order) {
3633
+ const at = order.indexOf(session.id);
3634
+ if (at < 0) return null; // not in the saved order: it stays last
3635
+ const from = _sessions.indexOf(session);
3636
+ _sessions.splice(from, 1);
3637
+ // After the nearest tab saved before it, else before the nearest saved after it, else last.
3638
+ let to = _sessions.length;
3639
+ const before = order.slice(0, at).reverse().map(id => _sessions.findIndex(s => s.id === id)).find(i => i >= 0);
3640
+ if (before !== undefined) to = before + 1;
3641
+ else {
3642
+ const after = order.slice(at + 1).map(id => _sessions.findIndex(s => s.id === id)).find(i => i >= 0);
3643
+ if (after !== undefined) to = after;
3644
+ }
3645
+ const neighbour = _sessions[to] || _sessions[to - 1] || null;
3646
+ _sessions.splice(to, 0, session);
3647
+ return neighbour;
3648
+ }
3649
+
3599
3650
  async function reconnectAssistantSession(sessionId, live, saved) {
3600
3651
  if (_assistantOwnedElsewhere(sessionId)) return null;
3601
3652
  let meta = live || null;
@@ -4180,12 +4231,13 @@ function initContextMenu(viewport, term, getWs) {
4180
4231
  menu.style.top = '0px';
4181
4232
  menu.classList.add('open');
4182
4233
  const rect = menu.getBoundingClientRect();
4234
+ const f = frameRect();
4183
4235
  let x = e.clientX;
4184
4236
  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;
4237
+ if (x + rect.width > f.right) x = f.right - rect.width - 4;
4238
+ if (y + rect.height > f.bottom) y = e.clientY - rect.height;
4239
+ if (x < f.left) x = f.left + 4;
4240
+ if (y < f.top) y = f.top + 4;
4189
4241
  menu.style.left = x + 'px';
4190
4242
  menu.style.top = y + 'px';
4191
4243
 
@@ -4415,7 +4467,8 @@ function sendMemoryDrop(node, ws) {
4415
4467
  ws.send(JSON.stringify({ type: 'memory_drop', content, title }));
4416
4468
  }
4417
4469
 
4418
- function formatMemoryForCLI(node) {
4470
+ // Also what the context menu sends to the AI panel (ui-context-menu.js).
4471
+ export function formatMemoryForCLI(node) {
4419
4472
  const p = node.payload;
4420
4473
  const parts = [];
4421
4474
 
@@ -4551,10 +4604,11 @@ function _placeFloatColorPicker(picker, colorBtn) {
4551
4604
  const inset = first ? first.left + first.width / 2 - box.left : 0;
4552
4605
  const margin = 8, gap = 6;
4553
4606
  const left = btn.left + btn.width / 2 - inset;
4607
+ const f = frameRect();
4554
4608
  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';
4609
+ if (top < f.top + margin) top = btn.bottom + gap;
4610
+ picker.style.left = Math.max(f.left + margin, Math.min(left, f.right - box.width - margin)) + 'px';
4611
+ picker.style.top = Math.max(f.top + margin, Math.min(top, f.bottom - box.height - margin)) + 'px';
4558
4612
  }
4559
4613
 
4560
4614
  function getRightPanelReservedWidth(cs = getComputedStyle(document.documentElement)) {
@@ -4563,17 +4617,24 @@ function getRightPanelReservedWidth(cs = getComputedStyle(document.documentEleme
4563
4617
  || 0;
4564
4618
  }
4565
4619
 
4620
+ /** The app frame's area left for the floats: right of the explorers, left of a docked right sidepanel (its reserved
4621
+ * width counts from the window's right edge), below the bar. */
4622
+ function _usableArea(f = frameRect(), cs = getComputedStyle(document.documentElement)) {
4623
+ const reserved = getRightPanelReservedWidth(cs);
4624
+ return { left: f.left, top: f.top, right: Math.max(f.left, Math.min(f.right, window.innerWidth - reserved)), bottom: f.bottom };
4625
+ }
4626
+
4566
4627
  /** Update snapped-edge classes on all floating tabs based on adjacency */
4567
- /** Compute viewport/panel snap boundaries (getComputedStyle reads — cache during drag) */
4628
+ /** Compute frame/panel snap boundaries (getComputedStyle reads — cache during drag) */
4568
4629
  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;
4630
+ const f = frameRect();
4631
+ const area = _usableArea(f);
4572
4632
  return {
4573
- leftPanelEdge: explorerW + fileExplorerW,
4574
- rightPanelEdge: window.innerWidth - getRightPanelReservedWidth(cs),
4575
- topEdge: parseFloat(cs.getPropertyValue('--navbar-height')) || 48,
4576
- bottomEdge: window.innerHeight,
4633
+ leftPanelEdge: f.left,
4634
+ rightPanelEdge: area.right,
4635
+ frameRight: f.right,
4636
+ topEdge: f.top,
4637
+ bottomEdge: f.bottom,
4577
4638
  };
4578
4639
  }
4579
4640
 
@@ -4591,7 +4652,7 @@ function _updateSnappedEdges(rectsOverride, envOverride) {
4591
4652
  }
4592
4653
  }
4593
4654
 
4594
- const { leftPanelEdge, rightPanelEdge, topEdge, bottomEdge } = envOverride || _snapEnv();
4655
+ const { leftPanelEdge, rightPanelEdge, frameRight, topEdge, bottomEdge } = envOverride || _snapEnv();
4595
4656
 
4596
4657
  for (const [sid, dt] of _detachedTabs) {
4597
4658
  if (!dt.el || dt.minimized) continue;
@@ -4611,8 +4672,8 @@ function _updateSnappedEdges(rectsOverride, envOverride) {
4611
4672
  }
4612
4673
 
4613
4674
  // 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;
4675
+ if (Math.abs(me.left - leftPanelEdge) < TOLERANCE) edges.left = true;
4676
+ if (Math.abs(me.right - rightPanelEdge) < TOLERANCE || Math.abs(me.right - frameRight) < TOLERANCE) edges.right = true;
4616
4677
  if (Math.abs(me.top - topEdge) < TOLERANCE) edges.top = true;
4617
4678
  if (Math.abs(me.bottom - bottomEdge) < TOLERANCE) edges.bottom = true;
4618
4679
 
@@ -4668,20 +4729,16 @@ export function tileFloatingTerminals() {
4668
4729
  }
4669
4730
  if (entries.length === 0) return;
4670
4731
 
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);
4732
+ // Compute the available area: the app frame, minus a docked right sidepanel and the docked terminal (which sits
4733
+ // 12px inside the frame's bottom: the tiles stop at its top)
4734
+ const area = _usableArea();
4677
4735
  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;
4736
+ const termTop = (termPanel && !termPanel.classList.contains('hidden') && !termPanel.classList.contains('detached'))
4737
+ ? termPanel.getBoundingClientRect().top : area.bottom;
4738
+ const areaL = area.left;
4739
+ const areaT = area.top;
4740
+ const areaW = Math.max(0, area.right - area.left);
4741
+ const areaH = Math.max(0, Math.min(area.bottom, termTop) - area.top);
4685
4742
 
4686
4743
  // Calculate grid: find best cols/rows to fill the space
4687
4744
  const n = entries.length;
@@ -4732,17 +4789,16 @@ function _defaultFloatRect(kind = {}) {
4732
4789
  const offset = _detachedTabs.size * 30;
4733
4790
  const isBrowser = kind.profile === 'browser';
4734
4791
  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);
4792
+ const f = frameRect();
4793
+ const { left: leftEdge, right: rightEdge } = _usableArea(f);
4739
4794
  const usableW = Math.max(0, rightEdge - leftEdge);
4740
4795
  // Assistant floats default to 780×560 (chat + agents dock need more room than a TUI)
4741
4796
  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))));
4797
+ const h = Math.round(Math.min(isBrowser ? 640 : (isAssistant ? 560 : 420), f.height * (isBrowser ? 0.6 : (isAssistant ? 0.7 : 0.5))));
4798
+ const at = clampBox(leftEdge + (usableW - w) / 2 + offset, f.top + (f.height - h) / 2 + offset, w, h, f);
4743
4799
  return {
4744
- left: Math.round(leftEdge + (usableW - w) / 2 + offset),
4745
- top: Math.round(Math.max(48, (window.innerHeight - h) / 2 + offset)),
4800
+ left: Math.round(at.x),
4801
+ top: Math.round(at.y),
4746
4802
  width: w,
4747
4803
  height: h,
4748
4804
  };
@@ -4792,10 +4848,6 @@ function detachTab(idx, opts = {}) {
4792
4848
  <span class="term-float-tab-icon">${prof?.svg || SVG_SHELL}</span>
4793
4849
  <button class="term-float-color-strip" data-tooltip="Change color"></button>
4794
4850
  <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
4851
  ${CLI_PROFILES_NEED_RECALL.has(session.profile) ? `<div class="term-float-recall-dd" data-tooltip="Recall profile">
4800
4852
  <button class="term-float-recall-btn"><span class="term-float-recall-label">...</span><span class="term-float-recall-arrow">&#x25BE;</span></button>
4801
4853
  <div class="term-float-recall-menu"></div>
@@ -5018,11 +5070,6 @@ function detachTab(idx, opts = {}) {
5018
5070
  });
5019
5071
  }
5020
5072
 
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
5073
 
5027
5074
  // Wire recall profile dropdown
5028
5075
  const recallDd = win.querySelector('.term-float-recall-dd');
@@ -5306,7 +5353,7 @@ function initTabFloatDrag(win, sessionId) {
5306
5353
 
5307
5354
  function _checkHeader(target) {
5308
5355
  const header = target.closest('.term-float-tab-header');
5309
- if (!header || target.closest('button, .term-float-profile-dd')) return false;
5356
+ if (!header || target.closest('button')) return false;
5310
5357
  const session = _sessions.find(s => s.id === sessionId);
5311
5358
  return !session?.pinned;
5312
5359
  }
@@ -5343,8 +5390,9 @@ function initTabFloatDrag(win, sessionId) {
5343
5390
  const KEEP = 80;
5344
5391
  const w = drag.startW || parseFloat(win.style.width) || 700;
5345
5392
  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));
5393
+ const env = drag.snapEnv;
5394
+ const frame = { left: env.leftPanelEdge, top: env.topEdge, right: env.frameRight, bottom: env.bottomEdge };
5395
+ ({ x: finalL, y: finalT } = clampBoxLoose(finalL, finalT, w, h, frame, KEEP));
5348
5396
 
5349
5397
  // Snap to other floating windows (rects cached at drag start)
5350
5398
  const SNAP_DIST = 12;
@@ -5375,11 +5423,12 @@ function initTabFloatDrag(win, sessionId) {
5375
5423
  else if (Math.abs(myB - or.bottom) < SNAP_DIST) { finalT = or.bottom - h; snapped = true; }
5376
5424
  }
5377
5425
  }
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; }
5426
+ // Also snap to the app frame's edges (and a docked right sidepanel's)
5427
+ if (Math.abs(finalL - frame.left) < SNAP_DIST) { finalL = frame.left; snapped = true; }
5428
+ if (Math.abs(finalT - frame.top) < SNAP_DIST) { finalT = frame.top; snapped = true; }
5429
+ if (Math.abs(myR - frame.right) < SNAP_DIST) { finalL = frame.right - w; snapped = true; }
5430
+ else if (Math.abs(myR - env.rightPanelEdge) < SNAP_DIST) { finalL = env.rightPanelEdge - w; snapped = true; }
5431
+ if (Math.abs(myB - frame.bottom) < SNAP_DIST) { finalT = frame.bottom - h; snapped = true; }
5383
5432
 
5384
5433
  win.classList.toggle('float-snapping', snapped);
5385
5434
  // Transform-based positioning: left/top force layout every frame; a
@@ -5498,7 +5547,9 @@ function initTabFloatResize(win, sessionId) {
5498
5547
  nl = Math.round(nl / gs) * gs; nt = Math.round(nt / gs) * gs;
5499
5548
  nw = Math.round(nw / gs) * gs; nh = Math.round(nh / gs) * gs;
5500
5549
  }
5501
- nt = Math.max(48, nt);
5550
+ // The header never goes above the app frame's top (a top edge stops there, the bottom edge stays put)
5551
+ const frameTop = frameRect().top;
5552
+ if (nt < frameTop) { if (dir.includes('n')) nh = Math.max(MIN_H, nh - (frameTop - nt)); nt = frameTop; }
5502
5553
  win.style.left = nl + 'px';
5503
5554
  win.style.top = nt + 'px';
5504
5555
  win.style.width = nw + 'px';
@@ -5841,41 +5892,48 @@ export async function initTerminal() {
5841
5892
  }
5842
5893
  });
5843
5894
 
5844
- // ── Window resize — clamp floating tabs on screen, refit all terminals ──
5895
+ // ── Window resize, an explorer opening or closing — keep the floats in the app frame, refit all terminals ──
5896
+ const clampFloatsToFrame = () => {
5897
+ const f = frameRect();
5898
+ const KEEP = 80; // min px of tab that must remain visible
5899
+ const floats = [..._detachedTabs.values()].filter(tabState => !tabState.minimized).map(tabState => tabState.el);
5900
+ if (_detached && _panel && !_panel.classList.contains('hidden')) floats.push(_panel);
5901
+
5902
+ for (const el of floats) {
5903
+ if (!el) continue;
5904
+ let l = parseFloat(el.style.left) || 0;
5905
+ let t = parseFloat(el.style.top) || 0;
5906
+ let w = parseFloat(el.style.width) || 700;
5907
+ let h = parseFloat(el.style.height) || 420;
5908
+
5909
+ // Clamp dimensions to the frame
5910
+ w = Math.min(w, f.width);
5911
+ h = Math.min(h, f.height);
5912
+
5913
+ // Clamp position so the header stays reachable, below the title bar and right of the explorers
5914
+ ({ x: l, y: t } = clampBoxLoose(l, t, w, h, f, KEEP));
5915
+
5916
+ el.style.left = l + 'px';
5917
+ el.style.top = t + 'px';
5918
+ el.style.width = w + 'px';
5919
+ el.style.height = h + 'px';
5920
+ }
5921
+
5922
+ // Refit all terminals to new dimensions
5923
+ _sessions.forEach(s => _scheduleFit(s));
5924
+ };
5845
5925
  window.addEventListener('resize', () => {
5846
5926
  if (_windowResizeTimer) clearTimeout(_windowResizeTimer);
5847
5927
  _windowResizeTimer = setTimeout(() => {
5848
5928
  _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));
5929
+ clampFloatsToFrame();
5877
5930
  }, 150);
5878
5931
  });
5932
+ onFrameChange(() => {
5933
+ if (_windowResizeTimer) return; // the resize pass runs shortly
5934
+ clampFloatsToFrame();
5935
+ _updateSnappedEdges();
5936
+ });
5879
5937
 
5880
5938
  // ── Visibility change — flow control, warm renderers, one focus target ──
5881
5939
  document.addEventListener('visibilitychange', () => {
@@ -6055,7 +6113,7 @@ export async function initTerminal() {
6055
6113
  // Register CLI launch keybind actions (open as detached floating tab)
6056
6114
  registerAction('launch-claude', () => launchDetached('claude-code'));
6057
6115
  registerAction('launch-codex', () => launchDetached('codex'));
6058
- registerAction('launch-gemini', () => launchDetached('gemini'));
6116
+ registerAction('launch-antigravity', () => launchDetached('antigravity'));
6059
6117
  registerAction('launch-opencode', () => launchDetached('opencode'));
6060
6118
  registerAction('launch-browser', () => openBrowserSession());
6061
6119
  registerAction('launch-youtube', () => openBrowserSession('https://www.youtube.com'));
@@ -6184,7 +6242,17 @@ export async function initTerminal() {
6184
6242
  const liveBrowserMap = _streamDisabled ? new Map() : new Map(liveBrowserSessions.map(s => [s.id, s]));
6185
6243
 
6186
6244
  // Fetch live assistant sessions (only when the registry has assistant tabs)
6187
- const liveAssistantMap = registry.some(r => r.profile === 'assistant') ? await fetchLiveAssistantSessions() : new Map();
6245
+ // A failed read is not an empty list: keep those tabs waiting and retry.
6246
+ let liveAssistantMap = new Map();
6247
+ if (registry.some(r => r.profile === 'assistant')) {
6248
+ const read = await readLiveAssistantSessions();
6249
+ if (read) liveAssistantMap = read;
6250
+ else {
6251
+ let layout = null;
6252
+ try { layout = JSON.parse(storage.getItem(KEYS.TERMINAL_SESSIONS + '-layout') || 'null'); } catch {}
6253
+ _waitAssistantRestore(registry.filter(r => r.profile === 'assistant'), { layout, order: registry });
6254
+ }
6255
+ }
6188
6256
 
6189
6257
  const liveIds = new Set([...liveMap.keys(), ...liveBrowserMap.keys(), ...liveAssistantMap.keys()]);
6190
6258
 
@@ -6249,6 +6317,8 @@ export async function initTerminal() {
6249
6317
  _syncRenderers();
6250
6318
  }
6251
6319
 
6320
+ _markLatePaneSwitches();
6321
+
6252
6322
  // Clean up dead sessions from registry
6253
6323
  if (toReconnect.length !== registry.length) {
6254
6324
  saveSessionRegistry();
@@ -6482,11 +6552,13 @@ async function attachDetached(terminalSessionId, profile, initialMessage, autoSu
6482
6552
  if (dt?.el) {
6483
6553
  const panelWidth = getRightPanelReservedWidth();
6484
6554
  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';
6555
+ // Beside the panel, 24px from its slot, at the top of the app frame
6556
+ const f = frameRect();
6557
+ const area = _usableArea(f);
6558
+ const w = Math.min(700, Math.max(0, area.right - area.left - 16) * 0.6);
6559
+ const h = Math.min(500, f.height - 40);
6560
+ dt.el.style.left = Math.max(f.left, area.right - w - 24) + 'px';
6561
+ dt.el.style.top = f.top + 'px';
6490
6562
  dt.el.style.width = w + 'px';
6491
6563
  dt.el.style.height = h + 'px';
6492
6564
  }
@@ -6669,10 +6741,11 @@ function applyTerminalLayout(snap) {
6669
6741
  const minW = 280, minH = 160;
6670
6742
  const w = Math.max(minW, dt.width || minW);
6671
6743
  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));
6744
+ const f = frameRect();
6745
+ const maxL = f.right - 80;
6746
+ const maxT = f.bottom - 40;
6747
+ const l = Math.max(f.left, Math.min(dt.left || 0, maxL));
6748
+ const t = Math.max(f.top, Math.min(dt.top || 0, maxT));
6676
6749
 
6677
6750
  tabState.el.style.left = l + 'px';
6678
6751
  tabState.el.style.top = t + 'px';
@@ -6764,7 +6837,12 @@ export async function restoreTerminalSnapshot(snap) {
6764
6837
  } catch {}
6765
6838
  const liveMap = new Map(liveSessions.map(s => [s.id, s]));
6766
6839
  const liveIds = new Set(liveMap.keys());
6767
- const liveAssistantMap = snap.sessions.some(s => s.profile === 'assistant') ? await fetchLiveAssistantSessions() : new Map();
6840
+ let liveAssistantMap = new Map();
6841
+ if (snap.sessions.some(s => s.profile === 'assistant')) {
6842
+ const read = await readLiveAssistantSessions();
6843
+ if (read) liveAssistantMap = read;
6844
+ else _waitAssistantRestore(snap.sessions.filter(s => s.profile === 'assistant'), { layout: snap, order: snap.sessions });
6845
+ }
6768
6846
 
6769
6847
  // Disconnect current sessions without killing server PTY
6770
6848
  disconnectAllSessions();
@@ -6801,6 +6879,7 @@ export async function restoreTerminalSnapshot(snap) {
6801
6879
  const firstDocked = _sessions.findIndex(s => !_detachedTabs.has(s.id));
6802
6880
  if (firstDocked >= 0 && !_splitMode) switchToSession(firstDocked, { force: true });
6803
6881
  _syncRenderers();
6882
+ _markLatePaneSwitches();
6804
6883
  }
6805
6884
 
6806
6885
  // ══════════════════════════════════════════
@@ -7706,6 +7785,7 @@ function initSplitDivider() {
7706
7785
  function switchToSessionInPane(idx, pane) {
7707
7786
  if (idx < 0 || idx >= _sessions.length) return;
7708
7787
  _activePaneIdx[pane] = idx;
7788
+ if (!_restoringLayout) _paneSwitches[pane]++;
7709
7789
 
7710
7790
  // Show/hide viewports within this pane only
7711
7791
  const paneBody = _panel?.querySelector(`.term-pane[data-pane="${pane}"] .term-pane-body`);