synabun 2.1.0 → 2.2.1

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 (332) hide show
  1. package/CHANGELOG.md +139 -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 +504 -39
  71. package/neural-interface/i18n/pt-BR.json +504 -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-brains/codex.js +6 -1
  88. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  89. package/neural-interface/lib/assistant-budget-split.js +42 -15
  90. package/neural-interface/lib/assistant-catalog.js +76 -9
  91. package/neural-interface/lib/assistant-config.js +6 -3
  92. package/neural-interface/lib/assistant-dispatch.js +211 -50
  93. package/neural-interface/lib/assistant-envelope.js +153 -1
  94. package/neural-interface/lib/assistant-evidence.js +80 -0
  95. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  96. package/neural-interface/lib/assistant-persona.js +16 -11
  97. package/neural-interface/lib/assistant-plan-permissions.js +15 -0
  98. package/neural-interface/lib/assistant-router.js +237 -35
  99. package/neural-interface/lib/assistant-runtime.js +158 -12
  100. package/neural-interface/lib/assistant-task-prompt.js +10 -8
  101. package/neural-interface/lib/assistant-tools.js +2 -2
  102. package/neural-interface/lib/assistant-usage.js +29 -0
  103. package/neural-interface/lib/backup-service.js +6 -1
  104. package/neural-interface/lib/browser-guard-cdp.js +5 -4
  105. package/neural-interface/lib/browser-network-guard.js +274 -82
  106. package/neural-interface/lib/browser-tool-policy.js +12 -7
  107. package/neural-interface/lib/claude-agent-bridge.js +7 -0
  108. package/neural-interface/lib/claude-history-cache.js +210 -0
  109. package/neural-interface/lib/claude-model-catalog.js +5 -1
  110. package/neural-interface/lib/cli-install-api.js +1 -1
  111. package/neural-interface/lib/cli-install.js +160 -31
  112. package/neural-interface/lib/cli-resolver.js +9 -2
  113. package/neural-interface/lib/cli-update-planner.js +25 -15
  114. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  115. package/neural-interface/lib/codex-worker-app-server.js +25 -6
  116. package/neural-interface/lib/editor-git-api.js +3 -20
  117. package/neural-interface/lib/exec-loop-command.js +9 -2
  118. package/neural-interface/lib/file-explorer-api.js +74 -40
  119. package/neural-interface/lib/file-explorer-watch.js +9 -3
  120. package/neural-interface/lib/git-bash/install.js +52 -6
  121. package/neural-interface/lib/git-bash/manager.js +59 -17
  122. package/neural-interface/lib/git-bash/pins.js +12 -0
  123. package/neural-interface/lib/git-read.js +2 -2
  124. package/neural-interface/lib/git-status-runner.js +390 -0
  125. package/neural-interface/lib/git-status-watchdog.js +40 -0
  126. package/neural-interface/lib/greeting/budgets.js +1 -0
  127. package/neural-interface/lib/greeting/engine.js +2 -2
  128. package/neural-interface/lib/greeting/sources.js +10 -12
  129. package/neural-interface/lib/greeting/writer.js +1 -1
  130. package/neural-interface/lib/mcp-client-config.js +37 -6
  131. package/neural-interface/lib/mcp-external-servers.js +40 -7
  132. package/neural-interface/lib/mcp-installer.js +2 -1
  133. package/neural-interface/lib/memory-session-capture.js +17 -3
  134. package/neural-interface/lib/morelogin.js +147 -25
  135. package/neural-interface/lib/native-loop-providers.js +423 -23
  136. package/neural-interface/lib/native-loop-runtime.js +23 -2
  137. package/neural-interface/lib/opencode-native-boundary.js +165 -22
  138. package/neural-interface/lib/owner-authority.js +1 -1
  139. package/neural-interface/lib/path-confine.js +12 -9
  140. package/neural-interface/lib/peer-identity.js +80 -6
  141. package/neural-interface/lib/pty-host/core.js +28 -1
  142. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  143. package/neural-interface/lib/pty-host/manager.js +12 -1
  144. package/neural-interface/lib/remote-policy.js +1 -1
  145. package/neural-interface/lib/request-gate.js +3 -0
  146. package/neural-interface/lib/rulesets/api.js +6 -5
  147. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  148. package/neural-interface/lib/rulesets/installer.js +89 -10
  149. package/neural-interface/lib/rulesets/render.js +1 -1
  150. package/neural-interface/lib/session-cache-builder.js +64 -1
  151. package/neural-interface/lib/skills/catalog.js +5 -1
  152. package/neural-interface/lib/skills/creation-brief.js +1 -1
  153. package/neural-interface/lib/skills/creation-sources.js +19 -1
  154. package/neural-interface/lib/skills/deploy.js +9 -4
  155. package/neural-interface/lib/skills/draft-confinement.js +3 -1
  156. package/neural-interface/lib/skills/eval-confinement.js +3 -1
  157. package/neural-interface/lib/skills/eval-trace.js +16 -0
  158. package/neural-interface/lib/skills/export.js +5 -2
  159. package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
  160. package/neural-interface/lib/skills/ownership.js +1 -1
  161. package/neural-interface/lib/skills/roots.js +10 -3
  162. package/neural-interface/lib/skills/runtime-checks.js +6 -3
  163. package/neural-interface/lib/skills/service.js +15 -7
  164. package/neural-interface/lib/skills/telemetry.js +6 -0
  165. package/neural-interface/lib/synabun-version.js +1 -1
  166. package/neural-interface/lib/system-activity.js +1 -1
  167. package/neural-interface/lib/whatsapp/brain.js +7 -3
  168. package/neural-interface/lib/whatsapp/cards.js +22 -9
  169. package/neural-interface/lib/whatsapp/config.js +1 -1
  170. package/neural-interface/lib/worker-confinement.js +77 -1
  171. package/neural-interface/lib/worker-question.js +74 -0
  172. package/neural-interface/lib/worker-resume.js +13 -4
  173. package/neural-interface/lib/worker-sandbox.js +46 -16
  174. package/neural-interface/package-lock.json +18 -2
  175. package/neural-interface/package.json +2 -1
  176. package/neural-interface/public/onboarding.html +1 -1
  177. package/neural-interface/public/shared/agent-runtime-options.js +89 -11
  178. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  179. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  180. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  181. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  182. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  183. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  184. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  185. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  186. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  187. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  188. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  189. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  190. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  191. package/neural-interface/public/shared/api.js +4 -4
  192. package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
  193. package/neural-interface/public/shared/assistant/asst-api.js +15 -3
  194. package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
  195. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  196. package/neural-interface/public/shared/assistant/asst-computer.js +26 -2
  197. package/neural-interface/public/shared/assistant/asst-control.js +48 -0
  198. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  199. package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
  200. package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
  201. package/neural-interface/public/shared/assistant/asst-motion-gate.js +80 -0
  202. package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
  203. package/neural-interface/public/shared/assistant/asst-render.js +112 -18
  204. package/neural-interface/public/shared/assistant/asst-route.js +237 -57
  205. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  206. package/neural-interface/public/shared/assistant/asst-scene-styles.js +55 -23
  207. package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
  208. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
  209. package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
  210. package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
  211. package/neural-interface/public/shared/assistant/asst-state.js +76 -9
  212. package/neural-interface/public/shared/assistant/asst-style-cache.js +44 -0
  213. package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
  214. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
  215. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  216. package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
  217. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  218. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  219. package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
  220. package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
  221. package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
  222. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  223. package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
  224. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  225. package/neural-interface/public/shared/cli-install-view.js +7 -4
  226. package/neural-interface/public/shared/cli-status.js +9 -8
  227. package/neural-interface/public/shared/constants.js +3 -2
  228. package/neural-interface/public/shared/context-menu-model.js +1 -0
  229. package/neural-interface/public/shared/cp/cp-permissions.js +12 -0
  230. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  231. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  232. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  233. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  234. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  235. package/neural-interface/public/shared/desktop-update-view.js +1 -0
  236. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  237. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  238. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  239. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  240. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  241. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  242. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  243. package/neural-interface/public/shared/html-shell.js +21 -4
  244. package/neural-interface/public/shared/loading-teardown.js +34 -0
  245. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
  246. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -7
  247. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  248. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +17 -1
  249. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
  250. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
  251. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
  252. package/neural-interface/public/shared/provider-icons.js +4 -2
  253. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  254. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
  255. package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
  256. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  257. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  258. package/neural-interface/public/shared/skills/ss-creation.js +6 -3
  259. package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
  260. package/neural-interface/public/shared/skills/ss-lint.js +9 -1
  261. package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
  262. package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
  263. package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
  264. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
  265. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
  266. package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
  267. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  268. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  269. package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
  270. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
  271. package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
  272. package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
  273. package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
  274. package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
  275. package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
  276. package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
  277. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
  278. package/neural-interface/public/shared/state.js +1 -1
  279. package/neural-interface/public/shared/storage-retry.js +47 -0
  280. package/neural-interface/public/shared/storage.js +25 -4
  281. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  282. package/neural-interface/public/shared/styles.css +362 -35
  283. package/neural-interface/public/shared/synabun-mascot.js +27 -8
  284. package/neural-interface/public/shared/synabun-ticker.js +147 -12
  285. package/neural-interface/public/shared/text-cache.js +29 -0
  286. package/neural-interface/public/shared/tool-list-picker.js +2 -2
  287. package/neural-interface/public/shared/tool-list-view.js +2 -2
  288. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  289. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  290. package/neural-interface/public/shared/ui-assistant.js +2 -0
  291. package/neural-interface/public/shared/ui-automation-studio.js +20 -12
  292. package/neural-interface/public/shared/ui-claude-panel.js +374 -134
  293. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  294. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  295. package/neural-interface/public/shared/ui-file-links.js +2 -2
  296. package/neural-interface/public/shared/ui-games.js +2 -2
  297. package/neural-interface/public/shared/ui-judgments.js +1 -1
  298. package/neural-interface/public/shared/ui-keybinds.js +8 -2
  299. package/neural-interface/public/shared/ui-link.js +3 -3
  300. package/neural-interface/public/shared/ui-loading.js +5 -4
  301. package/neural-interface/public/shared/ui-menubar.js +2 -2
  302. package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
  303. package/neural-interface/public/shared/ui-navbar.js +45 -0
  304. package/neural-interface/public/shared/ui-notifications.js +1 -0
  305. package/neural-interface/public/shared/ui-resume.js +9 -7
  306. package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
  307. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  308. package/neural-interface/public/shared/ui-sessions.js +22 -12
  309. package/neural-interface/public/shared/ui-settings.js +90 -65
  310. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  311. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
  312. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  313. package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
  314. package/neural-interface/public/shared/ui-terminal.js +146 -10
  315. package/neural-interface/public/shared/ui-tool-updates.js +3 -3
  316. package/neural-interface/public/shared/ui-tooltip.js +71 -46
  317. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  318. package/neural-interface/public/shared/ui-whiteboard.js +1 -1
  319. package/neural-interface/public/shared/ui-worker-end.js +1 -1
  320. package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
  321. package/neural-interface/public/shared/worker-questions.js +34 -0
  322. package/neural-interface/public/sidepanel-session.html +1 -1
  323. package/neural-interface/server.js +731 -466
  324. package/neural-interface/templates/rulesets/README.md +1 -1
  325. package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
  326. package/neural-interface/templates/rulesets/manifest.json +1 -1
  327. package/npm-shrinkwrap.json +18 -2
  328. package/package.json +2 -1
  329. package/skills/skill-forge/SKILL.md +4 -4
  330. package/skills/synabun/SKILL.md +1 -1
  331. package/skills/synabun/modules/schedule.md +3 -3
  332. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -22,7 +22,7 @@ import { getProviderMeta } from '../provider-icons.js';
22
22
  import { showGuestToast } from '../ui-sync.js';
23
23
  import { registerSidepanel, setSidepanelVisible, syncSidepanelLayout, focusSidepanel } from '../ui-sidepanel-windows.js';
24
24
  import {
25
- assistantBlocked, assistantOwnerOf, fetchLiveAssistantSessions, noteAssistantFocus, openAssistant, registerAssistantHost,
25
+ assistantBlocked, assistantOwnerOf, noteAssistantFocus, openAssistant, readLiveAssistantSessions, registerAssistantHost,
26
26
  } from '../ui-assistant.js';
27
27
  import { mountAssistant } from './asst-panel.js';
28
28
  import { openModelsManager } from './asst-route.js';
@@ -57,6 +57,11 @@ let _renaming = false;
57
57
  const _tabs = []; // { sessionId, viewport, toolbar, ctl, pill, label, userRenamed, status, brain }
58
58
  let _activeIdx = -1;
59
59
  let _tourPresenting = false;
60
+ // The saved tabs ({ activeIdx, tabs }) while the session list could not be read (a server that is
61
+ // starting answers before its Assistant routes exist): they stay saved and are asked about again.
62
+ let _unrestored = null;
63
+ let _restoring = null; // that restore under way: a session start waits for it
64
+ const RESTORE_RETRY_MS = [500, 1000, 2000, 4000, 8000, 15_000];
60
65
 
61
66
  // ── Helpers ──────────────────────────────────────────────────────────────────
62
67
 
@@ -94,11 +99,13 @@ function conversationOf(tab) {
94
99
 
95
100
  function saveTabs() {
96
101
  try {
97
- if (!_tabs.length) { storage.removeItem(KEYS.ASSISTANT_PANEL_TABS); return; }
102
+ // Saved tabs not restored yet stay saved (their names with them), after the open ones.
103
+ const waiting = (_unrestored?.tabs || []).filter(entry => !findTab(entry.sessionId));
104
+ if (!_tabs.length) { if (!waiting.length) storage.removeItem(KEYS.ASSISTANT_PANEL_TABS); return; }
98
105
  storage.setItem(KEYS.ASSISTANT_PANEL_TABS, JSON.stringify({
99
106
  v: 1,
100
107
  activeIdx: Math.max(0, _activeIdx),
101
- tabs: _tabs.map(tab => ({ sessionId: tab.sessionId, label: tab.userRenamed ? tab.label : '', userRenamed: !!tab.userRenamed })),
108
+ tabs: [..._tabs.map(tab => ({ sessionId: tab.sessionId, label: tab.userRenamed ? tab.label : '', userRenamed: !!tab.userRenamed })), ...waiting],
102
109
  }));
103
110
  } catch { /* storage unavailable */ }
104
111
  }
@@ -416,6 +423,16 @@ async function startSession(prompt = '') {
416
423
  _startError = '';
417
424
  renderPlaceholder('starting');
418
425
  try {
426
+ // The saved tabs come back before a new session is made: the restore under way (the server is
427
+ // starting), or once more for the ones it could not ask about.
428
+ if (!_restoring && _unrestored) startRestore(_unrestored, []);
429
+ if (_restoring) await _restoring.catch(() => { /* a new session, then */ });
430
+ if (_tabs.length) {
431
+ const tab = activeTab() || _tabs[0];
432
+ switchTo(_tabs.indexOf(tab));
433
+ if (prompt) { tab.ctl?.insertText?.(prompt); tab.ctl?.focus?.(); } // a conversation's draft, never sent for them
434
+ return;
435
+ }
419
436
  const res = await openAssistant(prompt ? { host: HOST_ID, prompt } : { host: HOST_ID });
420
437
  if (!_tabs.length) renderPlaceholder(res ? null : 'failed');
421
438
  } finally {
@@ -635,7 +652,7 @@ export function beginAssistantTour({ onPreviewSend = () => {} } = {}) {
635
652
  const wasVisible = _visible;
636
653
  const previousPanel = state.lastActivePanel;
637
654
  const previousSidepanel = state.lastActiveSidepanel;
638
- const peers = ['#topright-claude-panel-btn', '#topright-codex-panel-btn', '#topright-opencode-panel-btn']
655
+ const peers = ['#topright-claude-panel-btn', '#topright-codex-panel-btn', '#topright-opencode-panel-btn', '#topright-antigravity-panel-btn']
639
656
  .map(s => document.querySelector(s)).filter(b => b?.classList.contains('active'));
640
657
  _tourPresenting = true;
641
658
  const tab = activeTab();
@@ -731,7 +748,30 @@ export async function initAssistantPanel() {
731
748
 
732
749
  const saved = readSavedTabs();
733
750
  if (!saved?.tabs.length) return;
734
- const live = await fetchLiveAssistantSessions();
751
+ await startRestore(saved, RESTORE_RETRY_MS);
752
+ }
753
+
754
+ function startRestore(saved, retries) {
755
+ _restoring = restoreSavedTabs(saved, retries).finally(() => { _restoring = null; });
756
+ return _restoring;
757
+ }
758
+
759
+ /**
760
+ * The saved tabs come back (hidden, as pills) for the sessions the server still has open. A list
761
+ * that cannot be read restores nothing and forgets nothing: the tabs stay saved, names included
762
+ * (saveTabs), the list is asked again after each wait of `retries`, then at the next session start.
763
+ */
764
+ async function restoreSavedTabs(saved, retries) {
765
+ _unrestored = saved;
766
+ let live = await readLiveAssistantSessions();
767
+ for (const wait of retries) {
768
+ if (live || assistantBlocked()) break;
769
+ await new Promise(done => setTimeout(done, wait));
770
+ live = await readLiveAssistantSessions();
771
+ }
772
+ if (!live) return;
773
+ _unrestored = null;
774
+ const hadTabs = _tabs.length > 0; // one opened meanwhile stays the one showing
735
775
  for (const entry of saved.tabs) {
736
776
  const meta = live.get(entry.sessionId);
737
777
  if (!meta) continue;
@@ -746,8 +786,12 @@ export async function initAssistantPanel() {
746
786
  restoring: true,
747
787
  });
748
788
  }
749
- if (_tabs.length) {
750
- _activeIdx = Math.min(Math.max(0, saved.activeIdx), _tabs.length - 1);
789
+ if (_tabs.length && !hadTabs) {
790
+ // The tab that was showing, by its session: a saved tab before it that did not come back (a draft,
791
+ // an ended session) must not hand the header to its neighbour.
792
+ const shownId = saved.tabs[saved.activeIdx]?.sessionId;
793
+ const shown = _tabs.findIndex(tab => tab.sessionId === shownId);
794
+ _activeIdx = shown >= 0 ? shown : Math.min(Math.max(0, saved.activeIdx), _tabs.length - 1);
751
795
  markActive();
752
796
  renderHeader();
753
797
  renderPills();
@@ -4,7 +4,7 @@
4
4
  // Pure module (no DOM, no i18n import) so node:test can exercise it. The
5
5
  // composer resolves `descKey` through i18n and falls back to `desc`.
6
6
 
7
- import { normalizeComputerMode, normalizeProvider, normalizeRouteMode } from './asst-state.js';
7
+ import { isBrainProvider, normalizeComputerMode, normalizeProvider, normalizeRouteMode } from './asst-state.js';
8
8
 
9
9
  export const SLASH_COMMANDS = [
10
10
  { name: 'dispatch', usage: '/dispatch <provider> [--model m] [--effort e] [--agent a] [--project p] [--tools a,b] [--account id] [--ask] [--title "t"] <task>', descKey: 'assistant.slash.dispatch', desc: 'Dispatch a task to a sidepanel agent' },
@@ -143,6 +143,8 @@ function parseModel(rest) {
143
143
  provider = normalizeProvider(first);
144
144
  model = null;
145
145
  }
146
+ // /model switches the brain: a provider that only runs dispatched work (none of the four today) is refused, not swapped.
147
+ if (provider && !isBrainProvider(provider)) return { name: 'model', provider, model, effort: tokens[1] || null, error: 'not_brain' };
146
148
  return { name: 'model', provider, model, effort: tokens[1] || null };
147
149
  }
148
150
 
@@ -182,7 +184,11 @@ export function parseSlashCommand(text) {
182
184
  return enabled == null ? { name, mode: null, error: 'invalid_mode', value: restTrim } : { name, mode: enabled ? 'on' : 'off' };
183
185
  }
184
186
  case 'stop': return { name, target: restTrim && !/^all$/i.test(restTrim) ? restTrim : null, all: /^all$/i.test(restTrim) };
185
- case 'new': return { name, provider: restTrim ? normalizeProvider(restTrim) : null };
187
+ case 'new': {
188
+ const provider = restTrim ? normalizeProvider(restTrim) : null;
189
+ // A new session's brain: a provider that only runs dispatched work cannot be one (Antigravity can, since it runs the brain too).
190
+ return provider && !isBrainProvider(provider) ? { name, provider, error: 'not_brain' } : { name, provider };
191
+ }
186
192
  case 'resume': return { name, sessionId: restTrim || null };
187
193
  case 'compact':
188
194
  case 'clear':
@@ -8,9 +8,23 @@
8
8
  import { KEYS } from '../constants.js';
9
9
  import { isNewerRunDescriptor } from '../ui-native-loop-router-state.js';
10
10
  import { modelEffortIds } from '../agent-runtime-options.js';
11
+ import { getProviderMeta } from '../provider-icons.js';
11
12
  import { usagePacketIsStale } from './asst-usage.js';
12
13
 
13
- export const PROVIDERS = ['claude-code', 'codex', 'opencode'];
14
+ // The providers the Assistant itself can run on (its brain): the brain picker, /model and /new.
15
+ // Antigravity runs over ACP (lib/assistant-brains/antigravity.js), like a worker.
16
+ export const BRAIN_PROVIDERS = ['claude-code', 'codex', 'opencode', 'antigravity'];
17
+ // Providers that only ever run dispatched work. None any more (Antigravity became a brain too); kept so a module
18
+ // or a test that still imports the list reads it as empty.
19
+ export const WORKER_ONLY_PROVIDERS = [];
20
+ // Every provider the Assistant lists models for and can route a task to.
21
+ export const PROVIDERS = [...BRAIN_PROVIDERS, ...WORKER_ONLY_PROVIDERS];
22
+ // Providers that report tokens and no prices (Antigravity bills through its Google sign-in): a budget gives them no
23
+ // share, so a dry share never names them (asst-route.js), and their rows read "unpriced".
24
+ export const UNPRICED_PROVIDERS = ['antigravity'];
25
+ // Providers an older server may not list in its catalog yet: a model section without rows and without an archive
26
+ // for them stays hidden rather than showing an empty provider.
27
+ export const OPTIONAL_PROVIDERS = ['antigravity'];
14
28
 
15
29
  export const PROVIDER_ALIASES = {
16
30
  'claude-code': 'claude-code',
@@ -22,24 +36,38 @@ export const PROVIDER_ALIASES = {
22
36
  openai: 'codex',
23
37
  opencode: 'opencode',
24
38
  oc: 'opencode',
39
+ antigravity: 'antigravity',
40
+ agy: 'antigravity',
41
+ 'google antigravity': 'antigravity',
42
+ // Antigravity's CLI took the place of Gemini CLI: an old name or saved route means it.
43
+ gemini: 'antigravity',
25
44
  };
26
45
 
27
46
  export const PROVIDER_SHORT_LABELS = {
28
47
  'claude-code': 'Claude',
29
48
  codex: 'Codex',
30
49
  opencode: 'OpenCode',
50
+ antigravity: 'Antigravity',
51
+ };
52
+
53
+ export const PROVIDER_LABELS = {
54
+ 'claude-code': 'Claude Code',
55
+ codex: 'Codex',
56
+ opencode: 'OpenCode',
57
+ antigravity: 'Antigravity',
31
58
  };
32
59
 
33
60
  // Providers whose brain carries an account profile picker.
34
61
  export const ACCOUNT_PROVIDERS = new Set(['claude-code', 'codex']);
35
62
 
36
63
  // Approval modes are sent as `permissionMode` (`set_permission_mode {mode}` /
37
- // `query.permissionMode`). Claude uses the SDK enum; Codex/OpenCode brains only
38
- // distinguish "ask me" from "auto-approve". Plan mode is a separate flag
39
- // (`planMode`) that combines with any approval mode, each provider in its own
40
- // way: Claude runs the SDK 'plan' mode and returns to the approval mode once
64
+ // `query.permissionMode`). Claude uses the SDK enum; Codex/OpenCode/Antigravity
65
+ // brains only distinguish "ask me" from "auto-approve". Plan mode is a separate
66
+ // flag (`planMode`) that combines with any approval mode, each provider in its
67
+ // own way: Claude runs the SDK 'plan' mode and returns to the approval mode once
41
68
  // the plan is approved; Codex runs its native plan collaboration mode (read-only
42
- // sandbox); OpenCode runs its own `plan` agent.
69
+ // sandbox); OpenCode runs its own `plan` agent; Antigravity refuses code changes
70
+ // through SynaBun's hook and proposes the plan in a <proposed_plan> block.
43
71
  export const PERMISSION_MODES = {
44
72
  'claude-code': [
45
73
  { id: 'default', label: 'Default' },
@@ -54,9 +82,13 @@ export const PERMISSION_MODES = {
54
82
  { id: 'default', label: 'Default' },
55
83
  { id: 'auto', label: 'Auto-accept' },
56
84
  ],
85
+ antigravity: [
86
+ { id: 'default', label: 'Default' },
87
+ { id: 'auto', label: 'Auto-accept' },
88
+ ],
57
89
  };
58
90
 
59
- export const PLAN_MODE_PROVIDERS = new Set(['claude-code', 'codex', 'opencode']);
91
+ export const PLAN_MODE_PROVIDERS = new Set(['claude-code', 'codex', 'opencode', 'antigravity']);
60
92
 
61
93
  export const DEFAULT_BRAIN = Object.freeze({
62
94
  provider: 'claude-code',
@@ -80,10 +112,42 @@ export function normalizeProvider(value, fallback = null) {
80
112
  return PROVIDER_ALIASES[key] || fallback;
81
113
  }
82
114
 
115
+ /** Can the Assistant itself run on `provider` (a canonical id)? */
116
+ export function isBrainProvider(provider) {
117
+ return BRAIN_PROVIDERS.includes(provider);
118
+ }
119
+
120
+ /** Does `provider` report tokens and no prices (Antigravity)? */
121
+ export function isUnpricedProvider(provider) {
122
+ return UNPRICED_PROVIDERS.includes(provider);
123
+ }
124
+
125
+ /** `value` as a brain provider, else `fallback`: an unknown name (or a worker-only provider) is never a brain. */
126
+ export function normalizeBrainProvider(value, fallback = null) {
127
+ const provider = normalizeProvider(value);
128
+ return provider && isBrainProvider(provider) ? provider : fallback;
129
+ }
130
+
83
131
  export function providerShortLabel(provider) {
84
132
  return PROVIDER_SHORT_LABELS[provider] || String(provider || '');
85
133
  }
86
134
 
135
+ /** "Claude Code", "Antigravity": a provider's full name ('' for none). */
136
+ export function providerLabel(provider) {
137
+ return PROVIDER_LABELS[provider] || String(provider || '');
138
+ }
139
+
140
+ /**
141
+ * The icon, colour and name of a provider (provider-icons.js). A provider that file does not know
142
+ * yet keeps its own name and shows no icon, never another provider's (getProviderMeta falls back to
143
+ * Claude Code's for an unknown id).
144
+ */
145
+ export function providerMeta(provider) {
146
+ const meta = getProviderMeta(provider || undefined);
147
+ if (!provider || meta?.id === provider) return meta;
148
+ return { id: provider, label: providerLabel(provider), shortLabel: providerShortLabel(provider), icon: '', color: '' };
149
+ }
150
+
87
151
  function str(value) {
88
152
  if (value == null) return '';
89
153
  return String(value).trim();
@@ -93,16 +157,19 @@ function str(value) {
93
157
  export function normalizeBrain(raw, fallback = DEFAULT_BRAIN) {
94
158
  const base = { ...DEFAULT_BRAIN, ...(fallback || {}) };
95
159
  const src = raw && typeof raw === 'object' ? raw : {};
96
- const provider = normalizeProvider(src.provider ?? src.brain, base.provider) || 'claude-code';
160
+ // A provider that cannot be the brain (an unknown name) falls back like any unknown one.
161
+ const provider = normalizeBrainProvider(src.provider ?? src.brain, normalizeBrainProvider(base.provider)) || 'claude-code';
97
162
  const modes = PERMISSION_MODES[provider] || PERMISSION_MODES['claude-code'];
98
163
  const rawMode = str(src.permissionMode);
99
164
  // 'plan' used to be one of Claude's permission modes: it is the plan flag now.
100
165
  const legacyPlan = rawMode === 'plan';
101
166
  const mode = legacyPlan ? 'default' : rawMode;
167
+ const effort = str(src.effort) || null;
102
168
  return {
103
169
  provider,
104
170
  model: str(src.model),
105
- effort: str(src.effort) || null,
171
+ // Antigravity's thinking levels have no "off": no level is the model's own default (null).
172
+ effort: provider === 'antigravity' && effort === 'off' ? null : effort,
106
173
  agent: provider === 'opencode' ? (str(src.agent) || null) : null,
107
174
  project: str(src.project ?? src.cwd) || null,
108
175
  tools: provider === 'opencode' ? (Array.isArray(src.tools) ? [...src.tools] : null) : ['all'],
@@ -0,0 +1,44 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Assistant — computed styles read once
3
+ // ═══════════════════════════════════════════
4
+ // A value the stylesheet decides and the renderer needs on a hot path (the
5
+ // rack's --rk-cut, where a long subject is cut; its bubble line's
6
+ // white-space), read once and kept until the renderer says it may have
7
+ // changed. getComputedStyle() on a frame's path forces a style recalculation
8
+ // of everything the frame dirtied (a rig's SVG, a scene); these values change
9
+ // only when a container query flips, which happens only when the stage's width
10
+ // changes, and the renderer's ResizeObserver sees every such change and calls
11
+ // invalidate().
12
+ //
13
+ // createStyleCache({ read }?) → { get(el, prop), invalidate(el?), reads() }
14
+ //
15
+ // `read(el, prop)` defaults to getComputedStyle(el).getPropertyValue(prop)
16
+ // (trimmed); a read that throws counts as ''. DOM-free: tested in Node.
17
+
18
+ const defaultRead = (el, prop) => globalThis.getComputedStyle(el).getPropertyValue(prop);
19
+
20
+ export function createStyleCache({ read = defaultRead } = {}) {
21
+ let values = new WeakMap(); // element → Map(prop → value)
22
+ let count = 0;
23
+ return {
24
+ /** The element's computed `prop` (a custom property or a longhand), read once until invalidated. */
25
+ get(el, prop) {
26
+ if (!el) return '';
27
+ let props = values.get(el);
28
+ if (!props) { props = new Map(); values.set(el, props); }
29
+ if (props.has(prop)) return props.get(prop);
30
+ count += 1;
31
+ let value = '';
32
+ try { value = String(read(el, prop) ?? '').trim(); } catch { value = ''; }
33
+ props.set(prop, value);
34
+ return value;
35
+ },
36
+ /** Forget one element's values, or every value (no argument). */
37
+ invalidate(el) {
38
+ if (el) values.delete(el);
39
+ else values = new WeakMap();
40
+ },
41
+ /** How many real reads were made (tests, the idle-CPU suite). */
42
+ reads: () => count,
43
+ };
44
+ }
@@ -252,9 +252,13 @@ export function injectAssistantStyles() {
252
252
  .asst-count.attention { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
253
253
  /* The running cameo: the mascot's two pill eyes, glancing ±1.2px every 1.4s.
254
254
  One form everywhere (the header status, the tray pill, run pills):
255
- mascotCameoSvg() from synabun-mascot.js, a 10×7 SVG. */
255
+ mascotCameoSvg() from synabun-mascot.js, a 10×7 SVG in two spans. The
256
+ glance moves the HTML box around the eyes (.syna-cameo-eyes), along the
257
+ pair's −12° lean (1.2 of the SVG's 10×7 units: 11.738% and 3.564% of the
258
+ box), never a part of the SVG: a box's transform runs on the compositor,
259
+ and a frame the page draws anyway restyles one span and lays nothing out. */
256
260
  .syna-cameo { display: inline-block; flex-shrink: 0; overflow: visible; color: var(--asst-brand); }
257
- .syna-cameo-eye { transform-box: fill-box; animation: asst-cameo 2.8s var(--asst-ease-emphasized) infinite; }
261
+ .syna-cameo-eyes { display: block; animation: asst-cameo 2.8s var(--asst-ease-emphasized) infinite; }
258
262
 
259
263
  /* ── Toolbar: flush under the terminal tabs ─────────────────────────
260
264
  Edge to edge like the tab bar above it (same hairline), no card and no
@@ -1088,6 +1092,8 @@ export function injectAssistantStyles() {
1088
1092
  border-radius: 50%;
1089
1093
  animation: asst-spin .8s linear infinite;
1090
1094
  }
1095
+ /* A spinner nobody can see never runs (a hidden holder still restyles a running loop every frame). */
1096
+ [hidden] .asst-spinner, .asst-spinner[hidden], [inert] .asst-spinner { animation: none; }
1091
1097
  .asst-computer-activity { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 7px 8px 7px 12px; border-bottom: 1px solid var(--asst-line); }
1092
1098
  .asst-live-dot { width: 6px; height: 6px; flex-shrink: 0; border-radius: 50%; background: var(--asst-live); animation: asst-live 1.4s var(--asst-ease-standard) infinite; }
1093
1099
  .asst-computer-activity[data-state="paused_user"] .asst-live-dot { background: var(--asst-warn); animation: none; }
@@ -1368,6 +1374,40 @@ export function injectAssistantStyles() {
1368
1374
  /* Route card */
1369
1375
  .asst-route-summary { color: var(--asst-text); }
1370
1376
  .asst-route-reasons { margin-top: -4px; color: var(--asst-text-3); font-size: 11.5px; }
1377
+ /* Here / Dispatch: a segmented choice of where the task runs; the list, the model menu and Run follow it.
1378
+ sm capsules in one xs-padded track; the pick takes the gold "on" pair, like the option's key. */
1379
+ .asst-route-sides {
1380
+ display: inline-flex;
1381
+ align-self: flex-start;
1382
+ gap: 2px;
1383
+ padding: 2px;
1384
+ border-radius: var(--asst-pill-r-md);
1385
+ background: var(--asst-fill-1);
1386
+ box-shadow: inset 0 0 0 1px var(--asst-line);
1387
+ }
1388
+ .asst-route-side {
1389
+ display: inline-flex;
1390
+ align-items: center;
1391
+ justify-content: center;
1392
+ min-width: 76px;
1393
+ height: var(--asst-pill-h-sm);
1394
+ padding: 0 var(--asst-pill-px-sm);
1395
+ border: 0;
1396
+ border-radius: var(--asst-pill-r-sm);
1397
+ background: transparent;
1398
+ color: var(--asst-text-2);
1399
+ font: inherit;
1400
+ font-size: 12px;
1401
+ font-weight: 500;
1402
+ white-space: nowrap;
1403
+ cursor: pointer;
1404
+ transition: background-color var(--asst-dur-fast) var(--asst-ease-standard), box-shadow var(--asst-dur-fast) var(--asst-ease-standard), color var(--asst-dur-fast) var(--asst-ease-standard);
1405
+ }
1406
+ .asst-route-side:hover { background: var(--asst-fill-2); color: var(--asst-text); }
1407
+ .asst-route-side.selected { background: var(--asst-pill-on-fill); box-shadow: inset 0 0 0 1px var(--asst-pill-on-ring); color: var(--asst-text); }
1408
+ .asst-route-side[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
1409
+ .asst-route-side[aria-disabled="true"]:not(.selected):hover { background: transparent; color: var(--asst-text-2); }
1410
+ .asst-route-side-note { margin-top: -6px; color: var(--asst-text-3); font-size: 11.5px; }
1371
1411
  .asst-route-options { display: flex; flex-direction: column; gap: 4px; }
1372
1412
  .asst-route-option {
1373
1413
  display: flex;
@@ -2029,10 +2069,24 @@ export function injectAssistantStyles() {
2029
2069
  whole Assistant holds still: the component, its menus and modals, the
2030
2070
  sidepanel frame and its header (.asp-*), and the tray pills. */
2031
2071
  @keyframes asst-live { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.8); } }
2032
- @keyframes asst-cameo { 0%, 42% { transform: translateX(-1.2px); } 50%, 92% { transform: translateX(1.2px); } 100% { transform: translateX(-1.2px); } }
2072
+ @keyframes asst-cameo { 0%, 42% { transform: translate(-11.738%, 3.564%); } 50%, 92% { transform: translate(11.738%, -3.564%); } 100% { transform: translate(-11.738%, 3.564%); } }
2033
2073
  @keyframes asst-spin { to { transform: rotate(360deg); } }
2034
2074
 
2035
- body.ui-interacting :is(.asst-live-dot, .asst-spinner, .asst-computer-dot, .syna-cameo-eye) { animation-play-state: paused !important; }
2075
+ /* A moving box gets its layer up front (only where motion is allowed; nothing is promoted under reduced motion). */
2076
+ @media (prefers-reduced-motion: no-preference) {
2077
+ .syna-cameo-eyes { will-change: transform; }
2078
+ }
2079
+
2080
+ body.ui-interacting :is(.asst-live-dot, .asst-spinner, .asst-computer-dot, .syna-cameo-eyes) { animation-play-state: paused !important; }
2081
+
2082
+ /* Off screen, nothing loops. A holder the renderer saw leave the viewport (asst-motion-gate.js marks it
2083
+ data-offscreen: a run card, a rack or a route line scrolled out of its transcript) and a closed side
2084
+ panel (it stays in the page, moved out and faded: .assistant-panel without .open) pause every loop
2085
+ under them; each picks up where it stopped when it is back. Every frame the page draws restyles each
2086
+ running animation, composited or not, so a loop nobody sees is never free. */
2087
+ [data-offscreen], [data-offscreen]::before, [data-offscreen]::after,
2088
+ [data-offscreen] *, [data-offscreen] *::before, [data-offscreen] *::after,
2089
+ .assistant-panel:not(.open) *, .assistant-panel:not(.open) *::before, .assistant-panel:not(.open) *::after { animation-play-state: paused !important; }
2036
2090
  body.ui-interacting .asst-route-line[data-live] .asst-route-icon,
2037
2091
  body.ui-interacting .asst-route-line[data-live] .asst-route-sweep::after { animation-play-state: paused !important; }
2038
2092
 
@@ -13,16 +13,43 @@
13
13
 
14
14
  export const KIND_IDS = Object.freeze(['shell', 'read', 'edit', 'search', 'web', 'subagent', 'plan', 'memory', 'runs', 'computer', 'think', 'mcp']);
15
15
 
16
- // Tool names after lowercasing and stripping `mcp__<server>__` / `<server>_`.
16
+ // Tool names after lowercasing and stripping `mcp__<server>__` / `<server>_`. Antigravity's own tools keep their
17
+ // snake_case names (run_command, read_file, write_to_file, grep_search, search_web …).
17
18
  const KIND_TOOLS = {
18
- shell: ['bash', 'bashoutput', 'killshell', 'shell', 'exec_command', 'commandexecution'],
19
- read: ['read', 'notebookread', 'view'],
20
- edit: ['edit', 'multiedit', 'write', 'notebookedit', 'patch', 'apply_patch', 'filechange'],
21
- search: ['grep', 'glob', 'ls', 'list', 'codesearch', 'toolsearch'],
22
- web: ['webfetch', 'websearch'],
23
- subagent: ['task', 'agent', 'collabtoolcall', 'taskstop'],
19
+ shell: ['bash', 'bashoutput', 'killshell', 'shell', 'exec_command', 'commandexecution', 'run_command', 'send_command_input', 'command_status', 'read_terminal', 'shell_exec'],
20
+ read: ['read', 'notebookread', 'view', 'read_file', 'view_file', 'view_file_outline', 'view_code_item', 'view_content_chunk', 'read_resource', 'read_notebook'],
21
+ edit: ['edit', 'multiedit', 'write', 'notebookedit', 'patch', 'apply_patch', 'filechange', 'write_to_file', 'write_file', 'create_file', 'replace_file_content', 'multi_replace_file_content', 'edit_file', 'edit_notebook', 'delete_file', 'move_file'],
22
+ search: ['grep', 'glob', 'ls', 'list', 'codesearch', 'toolsearch', 'grep_search', 'find_by_name', 'list_dir', 'list_directory', 'codebase_search', 'code_search'],
23
+ web: ['webfetch', 'websearch', 'search_web', 'read_url_content', 'open_browser_url', 'read_browser_page', 'browser_subagent'],
24
+ subagent: ['task', 'agent', 'collabtoolcall', 'taskstop', 'invoke_subagent'],
24
25
  plan: ['todowrite', 'todoread', 'update_plan'],
25
26
  };
27
+
28
+ // Antigravity names its arguments in its own words: each alias is filled from the first of them that is set.
29
+ const INPUT_ALIASES = [
30
+ ['command', ['CommandLine']],
31
+ ['cwd', ['Cwd']],
32
+ ['file_path', ['AbsolutePath', 'TargetFile', 'Path']],
33
+ ['path', ['DirectoryPath', 'SearchPath', 'SearchDirectory']],
34
+ ['query', ['Query']],
35
+ ['url', ['Url']],
36
+ ];
37
+
38
+ /**
39
+ * A tool call's input with the names the rack reads (command, file_path, path, query, url) filled from
40
+ * Antigravity's own (CommandLine, AbsolutePath, TargetFile, DirectoryPath, Query, Url …). A name that is set
41
+ * already is kept; anything else is left as it came. Not an object: {}.
42
+ */
43
+ export function canonicalToolInput(input) {
44
+ const i = input && typeof input === 'object' && !Array.isArray(input) ? input : {};
45
+ let out = null;
46
+ for (const [alias, sources] of INPUT_ALIASES) {
47
+ if (i[alias] !== undefined && i[alias] !== null && i[alias] !== '') continue;
48
+ const value = sources.map(key => i[key]).find(v => typeof v === 'string' && v);
49
+ if (value) { out = out || { ...i }; out[alias] = value; }
50
+ }
51
+ return out || i;
52
+ }
26
53
  // SynaBun's own tools: only when the call is SynaBun's (or carries no server).
27
54
  const MEMORY_TOOLS = ['recall', 'remember', 'reflect', 'forget', 'restore', 'memories', 'sync', 'category'];
28
55
  const COMPUTER_TOOLS = ['computer', 'computer_apps', 'computer_ax', 'computer_status'];
@@ -13,6 +13,7 @@ const REASONS = {
13
13
  'codex-no-records': ['reason.codex-no-records', 'Codex ended without final usage records.'],
14
14
  'codex-rollout-missing': ['reason.codex-rollout-missing', "Codex usage records were missing; the turn's own totals were used."],
15
15
  'opencode-not-reconciled': ['reason.opencode-not-reconciled', "OpenCode's final message check did not finish."],
16
+ 'antigravity-total-mismatch': ['reason.antigravity-total-mismatch', "Antigravity's token total did not match its token classes; the classes were counted as reported."],
16
17
  interrupted: ['reason.interrupted', 'A turn was interrupted; the call it cut off is counted from the stream.'],
17
18
  'no-result': ['reason.no-result', 'A run ended mid-turn; its last calls are counted from the stream.'],
18
19
  };
@@ -34,7 +35,7 @@ const node = (tag, cls, value) => {
34
35
  };
35
36
  const valid = (n) => Number.isSafeInteger(n) && n >= 0;
36
37
  const safe = (n) => (valid(n) ? n : 0);
37
- const providerName = (id) => ['claude-code', 'codex', 'opencode'].includes(id) ? getProviderMeta(id).label : id === 'jev' ? 'Jev' : String(id || '');
38
+ const providerName = (id) => ['claude-code', 'codex', 'opencode'].includes(id) ? getProviderMeta(id).label : id === 'antigravity' ? 'Antigravity' : id === 'jev' ? 'Jev' : String(id || '');
38
39
  const defaultTr = (_, fallback, params) => String(fallback).replace(/\{(\w+)\}/g, (_m, key) => params?.[key] ?? `{${key}}`);
39
40
  export const formatExactTokens = (n) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(valid(n) ? n : 0);
40
41
  export function formatCompactTokens(n) {
@@ -6,7 +6,7 @@ const bound = new WeakSet();
6
6
  const diffStates = new WeakMap();
7
7
  const pending = new WeakMap();
8
8
  const cleaned = new WeakMap();
9
- const watched = new WeakSet();
9
+ const watched = new WeakMap(); // transcript -> teardown (including queued mutation work)
10
10
  const observer = typeof IntersectionObserver === 'function' ? new IntersectionObserver(entries => {
11
11
  for (const entry of entries) if (entry.isIntersecting) { pending.get(entry.target)?.(); observer.unobserve(entry.target); pending.delete(entry.target); }
12
12
  }, { rootMargin: '200px' }) : null;
@@ -23,6 +23,7 @@ const measurements = new WeakMap();
23
23
  const measurementQueue = new Set();
24
24
  let measurementFrame = false, capId = 0;
25
25
  function queueMeasurement(el) {
26
+ if (!measurements.has(el)) return;
26
27
  measurementQueue.add(el);
27
28
  if (measurementFrame) return;
28
29
  measurementFrame = true;
@@ -50,6 +51,7 @@ function measureChanges(el, update) {
50
51
  }
51
52
  function releaseMeasurements(root) {
52
53
  for (const el of [root, ...(root.querySelectorAll?.('*') || [])]) {
54
+ observer?.unobserve(el); pending.delete(el);
53
55
  sizeObserver?.unobserve(el); contentObservers.get(el)?.disconnect();
54
56
  contentObservers.delete(el); measurements.delete(el); measurementQueue.delete(el);
55
57
  }
@@ -249,11 +251,13 @@ export function cleanOutputBlocks(container) {
249
251
  // Covers generic/legacy cards and request builders which assign pre.textContent
250
252
  // directly. Observe each transcript once; our own writes settle after one pass.
251
253
  export function observeOutputBlocks(container) {
252
- if (!container || watched.has(container) || typeof MutationObserver !== 'function') return;
253
- watched.add(container);
254
+ if (!container || typeof MutationObserver !== 'function') return;
255
+ if (watched.has(container)) return watched.get(container);
254
256
  let queued = false;
257
+ let disposed = false;
255
258
  const dirty = new Set();
256
259
  const watcher = new MutationObserver(records => {
260
+ if (disposed) return;
257
261
  for (const record of records) {
258
262
  for (const removed of record.removedNodes || []) if (removed.nodeType === 1 && !removed.isConnected) releaseMeasurements(removed);
259
263
  const target = record.target.nodeType === 1 ? record.target : record.target.parentElement;
@@ -265,6 +269,7 @@ export function observeOutputBlocks(container) {
265
269
  queued = true;
266
270
  queueMicrotask(() => {
267
271
  queued = false;
272
+ if (disposed) return;
268
273
  for (const root of dirty) {
269
274
  if (root.matches?.('pre, .cxp-card-pre') && !root.closest('.cxp-readable, .cxp-output-source, .cxp-diff-view') && !root.querySelector('.cxp-readable')) {
270
275
  const code = root.querySelector('code');
@@ -276,6 +281,24 @@ export function observeOutputBlocks(container) {
276
281
  });
277
282
  });
278
283
  watcher.observe(container, { childList: true, characterData: true, subtree: true });
284
+ const dispose = () => {
285
+ disposed = true;
286
+ watcher.disconnect();
287
+ dirty.clear();
288
+ watched.delete(container);
289
+ releaseMeasurements(container);
290
+ };
291
+ watched.set(container, dispose);
292
+ return dispose;
293
+ }
294
+
295
+ // Removing a whole transcript does not produce a mutation *inside* it. Both
296
+ // global observers must release its outputs and lazy diffs explicitly on close.
297
+ export function disposeOutputBlocks(container) {
298
+ if (!container) return;
299
+ const dispose = watched.get(container);
300
+ if (dispose) dispose();
301
+ else releaseMeasurements(container);
279
302
  }
280
303
 
281
304
  function diffRows(file, target, count) {
@@ -1,3 +1,5 @@
1
+ import { syncWorkerQuestions } from '../worker-questions.js';
2
+ import { handleServerRequest, resolveRequestCard } from './cdx-requests.js';
1
3
  import { cxpText, cxpSessionLabel, setCodexTranslator } from './cdx-copy.js';
2
4
  // SynaBun — Codex Panel: DOM, Events & Lifecycle
3
5
  // ═══════════════════════════════════════════
@@ -808,6 +810,7 @@ function wireEvents() {
808
810
  try {
809
811
  const statusResponse = await fetch(`/api/git/status?path=${encodeURIComponent(project)}`);
810
812
  const status = statusResponse.ok ? await statusResponse.json() : null;
813
+ if (!status?.ok || !status.isGit || status.partial || status.stale || status.timedOut) throw new Error(status?.error || 'Git status is incomplete. Refresh before switching branches.');
811
814
  if (status?.changes?.length) {
812
815
  const confirmed = window.confirm(`Switch from ${previous || 'the current branch'} to ${branch} with ${status.changes.length} uncommitted change${status.changes.length === 1 ? '' : 's'}? Git will preserve them when possible.`);
813
816
  if (!confirmed) {
@@ -927,11 +930,12 @@ function wireEvents() {
927
930
 
928
931
 
929
932
  on('codex-panel:show', async (data) => {
930
- setVisible(true);
931
933
  if (data?.tabId) {
932
934
  const idx = _tabs.findIndex((entry) => entry.id === data.tabId);
933
935
  if (idx >= 0) switchTab(idx);
934
936
  }
937
+ setVisible(true);
938
+ ensureProjectsLoaded().catch(() => {});
935
939
  });
936
940
 
937
941
  const findPlanEditorTab = ({ source, tabId, planId, accountId, threadId } = {}) => {
@@ -1108,8 +1112,8 @@ export async function toggleCodexPanel() {
1108
1112
  setVisible(false);
1109
1113
  return;
1110
1114
  }
1111
- await ensureProjectsLoaded();
1112
1115
  setVisible(true);
1116
+ await ensureProjectsLoaded();
1113
1117
  }
1114
1118
 
1115
1119
  export async function attachCodexAutomation(run, {
@@ -1188,6 +1192,14 @@ export async function attachCodexAutomation(run, {
1188
1192
  tab._automationLifecycleError = run.error;
1189
1193
  withTab(tab, () => appendSystem(run.error, 'error'));
1190
1194
  }
1195
+ withTab(tab, () => syncWorkerQuestions(tab, run, {
1196
+ open: (id, pending) => handleServerRequest({ requestId: id, method: pending.protocol?.method, params: pending.input || {}, threadId }),
1197
+ close: (id) => {
1198
+ resolveRequestCard(id, t('workerQuestion.resolved'));
1199
+ tab.heldServerRequests?.delete(String(id));
1200
+ tab.blockingServerRequests?.delete(String(id));
1201
+ },
1202
+ }));
1191
1203
  trackWorkerEnd(run, { apply: (view) => { tab._workerEnd = view; placeWorkerEnd(tab); } });
1192
1204
  saveTabs();
1193
1205
  renderPills();
@@ -0,0 +1,12 @@
1
+ // The early Codex rollout preview (server.js sendEarlyCodexRolloutPreview): DOM-free decisions.
2
+
3
+ /** A bootstrap asks for the preview only for a saved thread of the tab on screen. */
4
+ export function codexWantsRolloutPreview(tab, shownTab, threadId) {
5
+ return !!threadId && !!tab && tab === shownTab;
6
+ }
7
+
8
+ /** A preview paints only for the thread shown, and only while nothing else (a stored snapshot) is on screen. */
9
+ export function rolloutPreviewApplies(msg, shownThread, transcriptSourceType) {
10
+ const previewFor = String(msg?.threadId || msg?.thread?.id || '');
11
+ return !transcriptSourceType && !!previewFor && previewFor === String(shownThread || '');
12
+ }