synabun 2.1.0 → 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 (317) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +1 -1
  3. package/docs/whatsapp.md +11 -11
  4. package/lib/client-config-repair.js +13 -9
  5. package/lib/external-tools.js +6 -5
  6. package/lib/update-snapshot.js +77 -3
  7. package/lib/virustotal.mjs +12 -1
  8. package/mcp-server/dist/index.js +1 -1
  9. package/mcp-server/dist/index.js.map +1 -1
  10. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  11. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  12. package/mcp-server/dist/services/budget-judgments.js +5 -3
  13. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  14. package/mcp-server/dist/services/identity.d.ts +2 -2
  15. package/mcp-server/dist/services/identity.js +2 -2
  16. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  17. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  18. package/mcp-server/dist/services/memory-sessions.js +28 -2
  19. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  20. package/mcp-server/dist/services/neural-interface.d.ts +2 -1
  21. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  22. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  23. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  24. package/mcp-server/dist/services/server-instructions.js +2 -1
  25. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  26. package/mcp-server/dist/services/tool-catalog.js +1 -1
  27. package/mcp-server/dist/services/tool-catalog.js.map +1 -1
  28. package/mcp-server/dist/services/tool-list.d.ts +1 -1
  29. package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
  30. package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
  31. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
  32. package/mcp-server/dist/services/tool-runtime.js +1 -1
  33. package/mcp-server/dist/services/tool-runtime.js.map +1 -1
  34. package/mcp-server/dist/services/typesafe-config.js +1 -1
  35. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  36. package/mcp-server/dist/tools/agents.d.ts +8 -8
  37. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  38. package/mcp-server/dist/tools/agents.js +10 -9
  39. package/mcp-server/dist/tools/agents.js.map +1 -1
  40. package/mcp-server/dist/tools/choice.d.ts +6 -0
  41. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  42. package/mcp-server/dist/tools/choice.js +18 -13
  43. package/mcp-server/dist/tools/choice.js.map +1 -1
  44. package/mcp-server/dist/tools/git-tools.js +2 -2
  45. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  46. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  47. package/mcp-server/dist/tools/morelogin.js +9 -6
  48. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  49. package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
  50. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
  51. package/mcp-server/dist/tools/skill-tools.js +11 -8
  52. package/mcp-server/dist/tools/skill-tools.js.map +1 -1
  53. package/mcp-server/package-lock.json +2 -2
  54. package/mcp-server/package.json +1 -1
  55. package/mcp-server/src/index.ts +1 -1
  56. package/mcp-server/src/services/budget-judgments.ts +5 -3
  57. package/mcp-server/src/services/identity.ts +2 -2
  58. package/mcp-server/src/services/memory-sessions.ts +24 -3
  59. package/mcp-server/src/services/neural-interface.ts +2 -1
  60. package/mcp-server/src/services/server-instructions.ts +2 -1
  61. package/mcp-server/src/services/tool-catalog.ts +1 -1
  62. package/mcp-server/src/services/tool-list.ts +1 -1
  63. package/mcp-server/src/services/tool-runtime.ts +2 -2
  64. package/mcp-server/src/services/typesafe-config.ts +1 -1
  65. package/mcp-server/src/tools/agents.ts +10 -9
  66. package/mcp-server/src/tools/choice.ts +17 -12
  67. package/mcp-server/src/tools/git-tools.ts +2 -2
  68. package/mcp-server/src/tools/morelogin.ts +6 -4
  69. package/mcp-server/src/tools/skill-tools.ts +11 -8
  70. package/neural-interface/i18n/en.json +501 -39
  71. package/neural-interface/i18n/pt-BR.json +501 -39
  72. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  73. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  74. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  75. package/neural-interface/lib/antigravity/bypass.js +100 -0
  76. package/neural-interface/lib/antigravity/catalog.js +273 -0
  77. package/neural-interface/lib/antigravity/home.js +260 -0
  78. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  79. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  80. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  81. package/neural-interface/lib/antigravity/policy.js +74 -0
  82. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  83. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  84. package/neural-interface/lib/antigravity/usage.js +43 -0
  85. package/neural-interface/lib/assistant-api.js +1 -0
  86. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  87. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  88. package/neural-interface/lib/assistant-budget-split.js +42 -15
  89. package/neural-interface/lib/assistant-catalog.js +76 -9
  90. package/neural-interface/lib/assistant-config.js +6 -3
  91. package/neural-interface/lib/assistant-dispatch.js +207 -49
  92. package/neural-interface/lib/assistant-envelope.js +153 -1
  93. package/neural-interface/lib/assistant-evidence.js +80 -0
  94. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  95. package/neural-interface/lib/assistant-persona.js +16 -11
  96. package/neural-interface/lib/assistant-plan-permissions.js +15 -0
  97. package/neural-interface/lib/assistant-router.js +237 -35
  98. package/neural-interface/lib/assistant-runtime.js +158 -12
  99. package/neural-interface/lib/assistant-task-prompt.js +10 -8
  100. package/neural-interface/lib/assistant-tools.js +2 -2
  101. package/neural-interface/lib/assistant-usage.js +29 -0
  102. package/neural-interface/lib/backup-service.js +6 -1
  103. package/neural-interface/lib/browser-guard-cdp.js +5 -4
  104. package/neural-interface/lib/browser-network-guard.js +274 -82
  105. package/neural-interface/lib/browser-tool-policy.js +12 -7
  106. package/neural-interface/lib/claude-agent-bridge.js +7 -0
  107. package/neural-interface/lib/claude-history-cache.js +210 -0
  108. package/neural-interface/lib/claude-model-catalog.js +5 -1
  109. package/neural-interface/lib/cli-install-api.js +1 -1
  110. package/neural-interface/lib/cli-install.js +160 -31
  111. package/neural-interface/lib/cli-resolver.js +9 -2
  112. package/neural-interface/lib/cli-update-planner.js +25 -15
  113. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  114. package/neural-interface/lib/codex-worker-app-server.js +25 -6
  115. package/neural-interface/lib/editor-git-api.js +3 -20
  116. package/neural-interface/lib/exec-loop-command.js +9 -2
  117. package/neural-interface/lib/file-explorer-api.js +74 -40
  118. package/neural-interface/lib/file-explorer-watch.js +9 -3
  119. package/neural-interface/lib/git-bash/install.js +52 -6
  120. package/neural-interface/lib/git-bash/manager.js +59 -17
  121. package/neural-interface/lib/git-bash/pins.js +12 -0
  122. package/neural-interface/lib/git-read.js +2 -2
  123. package/neural-interface/lib/git-status-runner.js +390 -0
  124. package/neural-interface/lib/git-status-watchdog.js +40 -0
  125. package/neural-interface/lib/greeting/budgets.js +1 -0
  126. package/neural-interface/lib/greeting/engine.js +2 -2
  127. package/neural-interface/lib/greeting/sources.js +10 -12
  128. package/neural-interface/lib/greeting/writer.js +1 -1
  129. package/neural-interface/lib/mcp-client-config.js +37 -6
  130. package/neural-interface/lib/mcp-external-servers.js +13 -4
  131. package/neural-interface/lib/mcp-installer.js +2 -1
  132. package/neural-interface/lib/memory-session-capture.js +17 -3
  133. package/neural-interface/lib/morelogin.js +147 -25
  134. package/neural-interface/lib/native-loop-providers.js +420 -21
  135. package/neural-interface/lib/native-loop-runtime.js +23 -2
  136. package/neural-interface/lib/opencode-native-boundary.js +11 -2
  137. package/neural-interface/lib/owner-authority.js +1 -1
  138. package/neural-interface/lib/path-confine.js +12 -9
  139. package/neural-interface/lib/peer-identity.js +32 -2
  140. package/neural-interface/lib/pty-host/core.js +28 -1
  141. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  142. package/neural-interface/lib/pty-host/manager.js +12 -1
  143. package/neural-interface/lib/remote-policy.js +1 -1
  144. package/neural-interface/lib/request-gate.js +3 -0
  145. package/neural-interface/lib/rulesets/api.js +6 -5
  146. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  147. package/neural-interface/lib/rulesets/installer.js +89 -10
  148. package/neural-interface/lib/rulesets/render.js +1 -1
  149. package/neural-interface/lib/session-cache-builder.js +64 -1
  150. package/neural-interface/lib/skills/catalog.js +5 -1
  151. package/neural-interface/lib/skills/creation-brief.js +1 -1
  152. package/neural-interface/lib/skills/creation-sources.js +19 -1
  153. package/neural-interface/lib/skills/deploy.js +9 -4
  154. package/neural-interface/lib/skills/draft-confinement.js +3 -1
  155. package/neural-interface/lib/skills/eval-confinement.js +3 -1
  156. package/neural-interface/lib/skills/eval-trace.js +16 -0
  157. package/neural-interface/lib/skills/export.js +5 -2
  158. package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
  159. package/neural-interface/lib/skills/ownership.js +1 -1
  160. package/neural-interface/lib/skills/roots.js +10 -3
  161. package/neural-interface/lib/skills/runtime-checks.js +6 -3
  162. package/neural-interface/lib/skills/service.js +15 -7
  163. package/neural-interface/lib/skills/telemetry.js +6 -0
  164. package/neural-interface/lib/synabun-version.js +1 -1
  165. package/neural-interface/lib/system-activity.js +1 -1
  166. package/neural-interface/lib/whatsapp/brain.js +7 -3
  167. package/neural-interface/lib/whatsapp/cards.js +22 -9
  168. package/neural-interface/lib/whatsapp/config.js +1 -1
  169. package/neural-interface/lib/worker-confinement.js +77 -1
  170. package/neural-interface/lib/worker-question.js +74 -0
  171. package/neural-interface/lib/worker-resume.js +13 -4
  172. package/neural-interface/lib/worker-sandbox.js +46 -16
  173. package/neural-interface/package-lock.json +18 -2
  174. package/neural-interface/package.json +2 -1
  175. package/neural-interface/public/onboarding.html +1 -1
  176. package/neural-interface/public/shared/agent-runtime-options.js +89 -11
  177. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  178. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  179. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  180. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  181. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  182. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  183. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  184. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  185. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  186. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  187. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  188. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  189. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  190. package/neural-interface/public/shared/api.js +4 -4
  191. package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
  192. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  193. package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
  194. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  195. package/neural-interface/public/shared/assistant/asst-control.js +48 -0
  196. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  197. package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
  198. package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
  199. package/neural-interface/public/shared/assistant/asst-panel.js +45 -8
  200. package/neural-interface/public/shared/assistant/asst-render.js +51 -12
  201. package/neural-interface/public/shared/assistant/asst-route.js +237 -57
  202. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  203. package/neural-interface/public/shared/assistant/asst-scenes.js +54 -1
  204. package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
  205. package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
  206. package/neural-interface/public/shared/assistant/asst-state.js +76 -9
  207. package/neural-interface/public/shared/assistant/asst-styles.js +34 -0
  208. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
  209. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  210. package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
  211. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  212. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  213. package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
  214. package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
  215. package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
  216. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  217. package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
  218. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  219. package/neural-interface/public/shared/cli-install-view.js +7 -4
  220. package/neural-interface/public/shared/cli-status.js +9 -8
  221. package/neural-interface/public/shared/constants.js +3 -2
  222. package/neural-interface/public/shared/context-menu-model.js +1 -0
  223. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  224. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  225. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  226. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  227. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  228. package/neural-interface/public/shared/desktop-update-view.js +1 -0
  229. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  230. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  231. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  232. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  233. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  234. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  235. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  236. package/neural-interface/public/shared/html-shell.js +21 -4
  237. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +27 -7
  238. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  239. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  240. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -0
  241. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
  242. package/neural-interface/public/shared/provider-icons.js +4 -2
  243. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  244. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
  245. package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
  246. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  247. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  248. package/neural-interface/public/shared/skills/ss-creation.js +6 -3
  249. package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
  250. package/neural-interface/public/shared/skills/ss-lint.js +9 -1
  251. package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
  252. package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
  253. package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
  254. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
  255. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
  256. package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
  257. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  258. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  259. package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
  260. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
  261. package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
  262. package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
  263. package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
  264. package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
  265. package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
  266. package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
  267. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
  268. package/neural-interface/public/shared/state.js +1 -1
  269. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  270. package/neural-interface/public/shared/styles.css +354 -34
  271. package/neural-interface/public/shared/text-cache.js +29 -0
  272. package/neural-interface/public/shared/tool-list-picker.js +2 -2
  273. package/neural-interface/public/shared/tool-list-view.js +2 -2
  274. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  275. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  276. package/neural-interface/public/shared/ui-assistant.js +2 -0
  277. package/neural-interface/public/shared/ui-automation-studio.js +20 -12
  278. package/neural-interface/public/shared/ui-claude-panel.js +299 -77
  279. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  280. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  281. package/neural-interface/public/shared/ui-file-links.js +2 -2
  282. package/neural-interface/public/shared/ui-games.js +2 -2
  283. package/neural-interface/public/shared/ui-judgments.js +1 -1
  284. package/neural-interface/public/shared/ui-keybinds.js +8 -2
  285. package/neural-interface/public/shared/ui-link.js +3 -3
  286. package/neural-interface/public/shared/ui-menubar.js +2 -2
  287. package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
  288. package/neural-interface/public/shared/ui-navbar.js +45 -0
  289. package/neural-interface/public/shared/ui-notifications.js +1 -0
  290. package/neural-interface/public/shared/ui-resume.js +9 -7
  291. package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
  292. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  293. package/neural-interface/public/shared/ui-sessions.js +22 -12
  294. package/neural-interface/public/shared/ui-settings.js +90 -65
  295. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  296. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
  297. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  298. package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
  299. package/neural-interface/public/shared/ui-terminal.js +146 -10
  300. package/neural-interface/public/shared/ui-tool-updates.js +3 -3
  301. package/neural-interface/public/shared/ui-tooltip.js +71 -46
  302. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  303. package/neural-interface/public/shared/ui-whiteboard.js +1 -1
  304. package/neural-interface/public/shared/ui-worker-end.js +1 -1
  305. package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
  306. package/neural-interface/public/shared/worker-questions.js +34 -0
  307. package/neural-interface/public/sidepanel-session.html +1 -1
  308. package/neural-interface/server.js +644 -426
  309. package/neural-interface/templates/rulesets/README.md +1 -1
  310. package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
  311. package/neural-interface/templates/rulesets/manifest.json +1 -1
  312. package/npm-shrinkwrap.json +18 -2
  313. package/package.json +2 -1
  314. package/skills/skill-forge/SKILL.md +4 -4
  315. package/skills/synabun/SKILL.md +1 -1
  316. package/skills/synabun/modules/schedule.md +3 -3
  317. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -7,7 +7,7 @@ import { FRAMED_CLASS, frameRect } from './app-frame.js';
7
7
 
8
8
  const STORAGE_PREFIX = 'synabun-sidepanel-layout-';
9
9
  const surfaces = new Map();
10
- const LABELS = { claude: 'Claude', codex: 'Codex', opencode: 'OpenCode', assistant: 'SynaBun Assistant' };
10
+ const LABELS = { claude: 'Claude', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity', assistant: 'SynaBun Assistant' };
11
11
  const DETACH_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3" y="7" width="14" height="14" rx="2"/><path d="M13 3h8v8M21 3l-9 9"/></svg>';
12
12
  const DOCK_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16M6 12h6m-3-3 3 3-3 3"/></svg>';
13
13
  const MOVE_ICON = '<svg viewBox="0 0 16 16" fill="currentColor"><circle cx="5" cy="4" r="1"/><circle cx="11" cy="4" r="1"/><circle cx="5" cy="8" r="1"/><circle cx="11" cy="8" r="1"/><circle cx="5" cy="12" r="1"/><circle cx="11" cy="12" r="1"/></svg>';
@@ -59,10 +59,10 @@ function injectStyles() {
59
59
  transform: none !important; transition: opacity .15s ease;
60
60
  }
61
61
  .sp-sidepanel.sp-floating.sp-window-focused { border-color: rgba(148,163,184,.38); }
62
- .sp-sidepanel.sp-floating > :is(.cp-resize-handle,.cxp-resize-handle,.ocpv2-resize-handle,.asp-resize-handle) { display: none; }
62
+ .sp-sidepanel.sp-floating > :is(.cp-resize-handle,.cxp-resize-handle,.ocpv2-resize-handle,.agy-resize-handle,.asp-resize-handle) { display: none; }
63
63
  .sp-window-move { display: none !important; }
64
64
  .sp-floating .sp-window-move { display: inline-flex !important; cursor: move; flex-shrink: 0; }
65
- .sp-floating > :is(.cp-header,.cxp-header,.ocpv2-header,.asp-header) { cursor: move; touch-action: none; }
65
+ .sp-floating > :is(.cp-header,.cxp-header,.ocpv2-header,.agy-header,.asp-header) { cursor: move; touch-action: none; }
66
66
  .sp-window-toggle svg, .sp-window-move svg { width: 13px; height: 13px; pointer-events: none; }
67
67
  .sp-window-resize { display: none; position: absolute; z-index: 20; touch-action: none; }
68
68
  .sp-floating > .sp-window-resize { display: block; }
@@ -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';
@@ -23,12 +23,12 @@ import { mountAssistant } from './assistant/asst-panel.js';
23
23
  import { getAssistantSession } from './assistant/asst-api.js';
24
24
  import { mascotCameoSvg } from './synabun-mascot.js';
25
25
  import { brainLabel, normalizeBrain, readStoredBrain, restoredSessionName, sessionNameGivenUp } from './assistant/asst-state.js';
26
- import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, fetchLiveAssistantSessions, isAssistantSessionLive } from './ui-assistant.js';
26
+ import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, readLiveAssistantSessions, assistantBlocked, isAssistantSessionLive } from './ui-assistant.js';
27
27
 
28
28
  const $ = (id) => document.getElementById(id);
29
- const CLI_PROFILES = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
30
- const NOTIF_TRACKED_CLI_PROFILES = new Set(['claude-code', 'codex', 'gemini', 'opencode']);
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;
@@ -1610,6 +1617,7 @@ function saveSessionRegistry() {
1610
1617
  brain: s._isAssistant ? (s._brain || prev?.brain || null) : null,
1611
1618
  });
1612
1619
  }
1620
+ for (const w of _waitingAssistant) if (!seen.has(w.id)) seen.set(w.id, w);
1613
1621
  storage.setItem(KEYS.TERMINAL_SESSIONS, JSON.stringify([...seen.values()]));
1614
1622
  }
1615
1623
 
@@ -1646,6 +1654,14 @@ function saveTerminalLayout() {
1646
1654
  if (_restoringLayout) return; // don't overwrite saved layout during reconnect
1647
1655
  try {
1648
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
+ }
1649
1665
  storage.setItem(KEYS.TERMINAL_SESSIONS + '-layout', JSON.stringify(snap));
1650
1666
  } catch {}
1651
1667
  }
@@ -3530,6 +3546,107 @@ function _assistantOwnedElsewhere(sessionId) {
3530
3546
  }
3531
3547
 
3532
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
+
3533
3650
  async function reconnectAssistantSession(sessionId, live, saved) {
3534
3651
  if (_assistantOwnedElsewhere(sessionId)) return null;
3535
3652
  let meta = live || null;
@@ -5996,7 +6113,7 @@ export async function initTerminal() {
5996
6113
  // Register CLI launch keybind actions (open as detached floating tab)
5997
6114
  registerAction('launch-claude', () => launchDetached('claude-code'));
5998
6115
  registerAction('launch-codex', () => launchDetached('codex'));
5999
- registerAction('launch-gemini', () => launchDetached('gemini'));
6116
+ registerAction('launch-antigravity', () => launchDetached('antigravity'));
6000
6117
  registerAction('launch-opencode', () => launchDetached('opencode'));
6001
6118
  registerAction('launch-browser', () => openBrowserSession());
6002
6119
  registerAction('launch-youtube', () => openBrowserSession('https://www.youtube.com'));
@@ -6125,7 +6242,17 @@ export async function initTerminal() {
6125
6242
  const liveBrowserMap = _streamDisabled ? new Map() : new Map(liveBrowserSessions.map(s => [s.id, s]));
6126
6243
 
6127
6244
  // Fetch live assistant sessions (only when the registry has assistant tabs)
6128
- 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
+ }
6129
6256
 
6130
6257
  const liveIds = new Set([...liveMap.keys(), ...liveBrowserMap.keys(), ...liveAssistantMap.keys()]);
6131
6258
 
@@ -6190,6 +6317,8 @@ export async function initTerminal() {
6190
6317
  _syncRenderers();
6191
6318
  }
6192
6319
 
6320
+ _markLatePaneSwitches();
6321
+
6193
6322
  // Clean up dead sessions from registry
6194
6323
  if (toReconnect.length !== registry.length) {
6195
6324
  saveSessionRegistry();
@@ -6708,7 +6837,12 @@ export async function restoreTerminalSnapshot(snap) {
6708
6837
  } catch {}
6709
6838
  const liveMap = new Map(liveSessions.map(s => [s.id, s]));
6710
6839
  const liveIds = new Set(liveMap.keys());
6711
- 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
+ }
6712
6846
 
6713
6847
  // Disconnect current sessions without killing server PTY
6714
6848
  disconnectAllSessions();
@@ -6745,6 +6879,7 @@ export async function restoreTerminalSnapshot(snap) {
6745
6879
  const firstDocked = _sessions.findIndex(s => !_detachedTabs.has(s.id));
6746
6880
  if (firstDocked >= 0 && !_splitMode) switchToSession(firstDocked, { force: true });
6747
6881
  _syncRenderers();
6882
+ _markLatePaneSwitches();
6748
6883
  }
6749
6884
 
6750
6885
  // ══════════════════════════════════════════
@@ -7650,6 +7785,7 @@ function initSplitDivider() {
7650
7785
  function switchToSessionInPane(idx, pane) {
7651
7786
  if (idx < 0 || idx >= _sessions.length) return;
7652
7787
  _activePaneIdx[pane] = idx;
7788
+ if (!_restoringLayout) _paneSwitches[pane]++;
7653
7789
 
7654
7790
  // Show/hide viewports within this pane only
7655
7791
  const paneBody = _panel?.querySelector(`.term-pane[data-pane="${pane}"] .term-pane-body`);
@@ -14,7 +14,7 @@ const TOOL_ELEMENTS = {
14
14
  'claude-code': { toolbar: 'claude-update-badge', menu: 'claude-menu-update-badge' },
15
15
  'codex': { toolbar: 'codex-update-badge', menu: 'codex-menu-update-badge' },
16
16
  'opencode': { toolbar: 'opencode-update-badge', menu: 'opencode-menu-update-badge' },
17
- 'gemini': { toolbar: null, menu: 'gemini-menu-update-badge' },
17
+ 'antigravity': { toolbar: 'antigravity-update-badge', menu: 'antigravity-menu-update-badge' },
18
18
  };
19
19
 
20
20
  // Parent elements whose tooltips are updated when an update badge is visible.
@@ -22,14 +22,14 @@ const PARENT_ELEMENTS = {
22
22
  'claude-code': { toolbar: 'topright-claude-panel-btn', menu: 'menu-terminal-claude' },
23
23
  'codex': { toolbar: 'topright-codex-panel-btn', menu: 'menu-terminal-codex' },
24
24
  'opencode': { toolbar: 'topright-opencode-panel-btn', menu: 'menu-terminal-opencode' },
25
- 'gemini': { toolbar: null, menu: 'menu-terminal-gemini' },
25
+ 'antigravity': { toolbar: 'topright-antigravity-panel-btn', menu: 'menu-terminal-antigravity' },
26
26
  };
27
27
 
28
28
  const TOOL_LABELS = {
29
29
  'claude-code': 'Claude Code',
30
30
  'codex': 'Codex',
31
31
  'opencode': 'OpenCode',
32
- 'gemini': 'Gemini CLI',
32
+ 'antigravity': 'Antigravity CLI',
33
33
  };
34
34
 
35
35
  // Cached server response so click handlers can read per-tool updateCommand
@@ -4,6 +4,7 @@
4
4
  // ═══════════════════════════════════════════
5
5
 
6
6
  import { boundsForPoint, frameRect } from './app-frame.js';
7
+ import { isWindowsPlatform } from './desktop-bridge.js';
7
8
 
8
9
  /**
9
10
  * Initialize the tooltip system.
@@ -11,9 +12,16 @@ import { boundsForPoint, frameRect } from './app-frame.js';
11
12
  * mouseover/mouseout on any [data-tooltip] elements.
12
13
  */
13
14
  export function initTooltip() {
15
+ if (document.documentElement?.classList) {
16
+ document.documentElement.classList.toggle('platform-win32', isWindowsPlatform(globalThis.window));
17
+ }
14
18
  const tip = document.createElement('div');
15
19
  tip.className = 'ui-tooltip';
16
- tip.innerHTML = '<span class="ui-tooltip-arrow"></span><span class="ui-tooltip-text"></span>';
20
+ const arrowEl = document.createElement('span');
21
+ arrowEl.className = 'ui-tooltip-arrow';
22
+ const textEl = document.createElement('span');
23
+ textEl.className = 'ui-tooltip-text';
24
+ tip.append(arrowEl, textEl);
17
25
  document.body.appendChild(tip);
18
26
 
19
27
  let showTimer = null;
@@ -29,7 +37,12 @@ export function initTooltip() {
29
37
  // control's tooltip over the open menu: while one is open, the control stays
30
38
  // quiet.
31
39
  const OPEN_MENU_INSIDE = '.cp-dropdown.open > .cp-dd-menu, .cxp-dropdown.open > .cxp-dd-menu, .ocpv2-dropdown.open > .ocpv2-dd-menu';
32
- function openMenuInside(el) { return !!el.querySelector(OPEN_MENU_INSIDE); }
40
+ function openMenuInside(el) {
41
+ if (el.classList?.contains('term-minimized-pill') || el.classList?.contains('term-minimized-pill-close')) return false;
42
+ return !!el.querySelector(OPEN_MENU_INSIDE);
43
+ }
44
+
45
+ const rectKey = (r) => r.left + ',' + r.top + ',' + r.width + ',' + r.height;
33
46
 
34
47
  // Swap the placement class only: a reposition (the follow loop below) must keep .visible.
35
48
  function setPlacement(placement) {
@@ -37,8 +50,8 @@ export function initTooltip() {
37
50
  tip.classList.add(placement);
38
51
  }
39
52
 
40
- function positionTip(el) {
41
- const r = el.getBoundingClientRect();
53
+ function positionTip(el, knownRect = null) {
54
+ const r = knownRect || el.getBoundingClientRect();
42
55
  const tw = tip.offsetWidth;
43
56
  const th = tip.offsetHeight;
44
57
  const gap = 8;
@@ -50,14 +63,14 @@ export function initTooltip() {
50
63
  // Left-side placement (session dropdowns in sidepanels)
51
64
  // Anchor to the left edge of the containing sidepanel so tooltip always appears fully outside
52
65
  if (preferred === 'left') {
53
- const panel = el.closest('.claude-panel, .codex-panel, .ocp-panel, .assistant-panel, .menubar-dropdown--resume');
66
+ const panel = el.closest('.claude-panel, .codex-panel, .ocp-panel, .agy-panel, .assistant-panel, .menubar-dropdown--resume');
54
67
  const anchorLeft = panel ? panel.getBoundingClientRect().left : r.left;
55
68
  if (anchorLeft - gap - tw > b.left + 4) {
56
69
  setPlacement('left');
57
70
  tip.style.top = (r.top + r.height / 2 - th / 2) + 'px';
58
71
  tip.style.left = (anchorLeft - gap - tw) + 'px';
59
72
  tip.style.setProperty('--arrow-y', (th / 2) + 'px');
60
- return;
73
+ return { rect: r, width: tw };
61
74
  }
62
75
  }
63
76
 
@@ -70,7 +83,7 @@ export function initTooltip() {
70
83
  tip.style.top = (r.top + r.height / 2 - th / 2) + 'px';
71
84
  tip.style.left = (anchorRight + gap) + 'px';
72
85
  tip.style.setProperty('--arrow-y', (th / 2) + 'px');
73
- return;
86
+ return { rect: r, width: tw };
74
87
  }
75
88
 
76
89
  // Elements near the top (title bar) → force below
@@ -90,6 +103,7 @@ export function initTooltip() {
90
103
  const arrowLeft = r.left + r.width / 2 - left;
91
104
  tip.style.setProperty('--arrow-x', arrowLeft + 'px');
92
105
  tip.style.setProperty('--arrow-y', '');
106
+ return { rect: r, width: tw };
93
107
  }
94
108
 
95
109
  function suppressTitle(el) {
@@ -98,10 +112,16 @@ export function initTooltip() {
98
112
  el._savedTitle = el.getAttribute('title');
99
113
  el.removeAttribute('title');
100
114
  }
101
- el.querySelectorAll('[title]').forEach(child => {
102
- child._savedTitle = child.getAttribute('title');
103
- child.removeAttribute('title');
104
- });
115
+ const titledChildren = [...el.querySelectorAll('[title]')];
116
+ if (titledChildren.length) {
117
+ el._savedTitleNodes = titledChildren;
118
+ for (const child of titledChildren) {
119
+ child._savedTitle = child.getAttribute('title');
120
+ child.removeAttribute('title');
121
+ }
122
+ } else {
123
+ delete el._savedTitleNodes;
124
+ }
105
125
  }
106
126
 
107
127
  function restoreTitle(el) {
@@ -109,12 +129,15 @@ export function initTooltip() {
109
129
  el.setAttribute('title', el._savedTitle);
110
130
  delete el._savedTitle;
111
131
  }
112
- el.querySelectorAll('*').forEach(child => {
113
- if (child._savedTitle !== undefined) {
114
- child.setAttribute('title', child._savedTitle);
115
- delete child._savedTitle;
132
+ if (el._savedTitleNodes) {
133
+ for (const child of el._savedTitleNodes) {
134
+ if (child._savedTitle !== undefined) {
135
+ child.setAttribute('title', child._savedTitle);
136
+ delete child._savedTitle;
137
+ }
116
138
  }
117
- });
139
+ delete el._savedTitleNodes;
140
+ }
118
141
  }
119
142
 
120
143
  function show(el) {
@@ -122,7 +145,6 @@ export function initTooltip() {
122
145
  if (!text || openMenuInside(el)) return;
123
146
  clearTimeout(hideTimer);
124
147
  suppressTitle(el);
125
- const textEl = tip.querySelector('.ui-tooltip-text');
126
148
  const nlIdx = text.indexOf('\n');
127
149
  if (nlIdx !== -1) {
128
150
  let main = text.slice(0, nlIdx);
@@ -141,37 +163,22 @@ export function initTooltip() {
141
163
  }
142
164
  currentTarget = el;
143
165
  tip.style.display = 'block';
144
- tip.offsetHeight; // force reflow so dimensions are measurable
145
- positionTip(el);
166
+ const { rect: initialRect, width: initialWidth } = positionTip(el);
146
167
  tip.classList.add('visible');
147
- // Re-position on next frame — first show after display:none can mis-measure
148
- requestAnimationFrame(() => {
149
- if (currentTarget === el) {
150
- positionTip(el);
151
- tip.classList.add('visible');
152
- }
153
- });
154
- // Some targets resize on hover (e.g. tray pill label expands max-width
155
- // 100→300px over ~300ms). Without follow-up positioning, the tooltip
156
- // stays anchored to the un-expanded rect and the pill grows leftward
157
- // over it. Drive a rAF loop for the duration of any size/position
158
- // transition so the tooltip tracks the target.
159
- const followFor = 360; // ms — upper bound, slightly longer than the longest pill transition
160
- const stopAt = performance.now() + followFor;
161
- // Self-terminate once the target stops moving: a static target (the vast
162
- // majority of tooltips) settles in ~2 frames instead of running the full
163
- // 360ms rAF/getBoundingClientRect loop, while an animating pill keeps being
164
- // followed until it settles or the cap is hit.
165
- let lastRectKey = '';
168
+
169
+ // Follow targets that actually animate their geometry on hover (via transitionrun),
170
+ // without polling static targets across multiple rAFs.
171
+ const followFor = 360;
172
+ let stopAt = 0;
173
+ let lastRectKey = rectKey(initialRect);
166
174
  let stableTicks = 0;
167
175
  const tick = () => {
168
176
  repositionRafId = 0;
169
177
  if (currentTarget !== el) return;
170
- positionTip(el);
171
- const r = el.getBoundingClientRect();
172
- const key = r.left + ',' + r.top + ',' + r.width + ',' + r.height;
178
+ const { rect: r } = positionTip(el);
179
+ const key = rectKey(r);
173
180
  if (key === lastRectKey) {
174
- if (++stableTicks >= 2) return; // target settled — stop early
181
+ if (++stableTicks >= 2) return;
175
182
  } else {
176
183
  stableTicks = 0;
177
184
  lastRectKey = key;
@@ -184,6 +191,8 @@ export function initTooltip() {
184
191
  if (currentTarget !== el) return;
185
192
  if (event.target !== el && !el.contains(event.target)) return;
186
193
  if (!/width|max-width|transform|margin|padding|left|right|top|bottom/.test(event.propertyName || '')) return;
194
+ stopAt = performance.now() + followFor;
195
+ stableTicks = 0;
187
196
  if (!repositionRafId) repositionRafId = requestAnimationFrame(tick);
188
197
  };
189
198
  repositionEndHandler = (event) => {
@@ -193,9 +202,25 @@ export function initTooltip() {
193
202
  };
194
203
  el.addEventListener('transitionrun', repositionRunHandler);
195
204
  el.addEventListener('transitionend', repositionEndHandler);
196
- // Kick off one immediate rAF in case the transition already started on
197
- // mouseenter before our listeners attached.
198
- if (!repositionRafId) repositionRafId = requestAnimationFrame(tick);
205
+
206
+ // One check on the next frame (a single rect read): metrics that settle after display:none, or a
207
+ // hover transition that began before the listeners above were attached (the 16-20 ms show delay).
208
+ if (!repositionRafId) {
209
+ repositionRafId = requestAnimationFrame(() => {
210
+ repositionRafId = 0;
211
+ if (currentTarget !== el) return;
212
+ const r = el.getBoundingClientRect();
213
+ const key = rectKey(r);
214
+ if (key !== lastRectKey || tip.offsetWidth !== initialWidth) {
215
+ lastRectKey = key;
216
+ positionTip(el, r);
217
+ if (key !== rectKey(initialRect)) {
218
+ stopAt = performance.now() + followFor;
219
+ repositionRafId = requestAnimationFrame(tick);
220
+ }
221
+ }
222
+ });
223
+ }
199
224
  }
200
225
 
201
226
  function hide() {
@@ -230,7 +255,7 @@ export function initTooltip() {
230
255
  clearTimeout(hideTimer);
231
256
  if (currentTarget) {
232
257
  hide();
233
- show(el);
258
+ showTimer = setTimeout(() => show(el), 16);
234
259
  } else {
235
260
  showTimer = setTimeout(() => show(el), 20);
236
261
  }