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
@@ -208,6 +208,7 @@ export function normalizeSynaBunChoiceElicitation(params = {}) {
208
208
  if (options.length < 2) return null;
209
209
  return {
210
210
  id,
211
+ multiSelect: question.multiSelect === true,
211
212
  header: String(question?.header || id),
212
213
  question: String(question?.question || ''),
213
214
  options,
@@ -226,16 +227,16 @@ export function buildSynaBunChoiceContent(questions = [], answers = {}) {
226
227
  const content = {};
227
228
  for (const question of questions) {
228
229
  const id = question?.id;
229
- const answer = id ? answers[id]?.answers?.[0] : '';
230
- if (!id || !answer) continue;
231
- const option = (question.options || []).find((candidate) => candidate.label === answer);
232
- if (option) {
233
- content[id] = option.value || option.label;
234
- continue;
235
- }
236
- const meta = question._synabunMcp || {};
237
- content[id] = meta.otherValue || SYNABUN_OTHER_VALUE;
238
- content[meta.otherField || `${id}__other`] = answer;
230
+ const selected = id ? answers[id]?.answers || [] : [];
231
+ if (!id || !selected.length) continue;
232
+ const values = selected.map((answer) => {
233
+ const option = (question.options || []).find((candidate) => candidate.label === answer);
234
+ if (option) return option.value || option.label;
235
+ const meta = question._synabunMcp || {};
236
+ content[meta.otherField || `${id}__other`] = answer;
237
+ return meta.otherValue || SYNABUN_OTHER_VALUE;
238
+ });
239
+ content[id] = question.multiSelect ? values : values[0];
239
240
  }
240
241
  return content;
241
242
  }
@@ -19,6 +19,7 @@ import { OUTPUT_LIMITS, boundedText, jsonAnswer, prettyJsonFences, readableStruc
19
19
  import { renderReadableOutput, renderReadableBlock, cleanOutputBlocks, hydrateOutputControls, observeOutputBlocks, renderDiffView, restoreDiffViews } from './cdx-output.js';
20
20
  import { fileLinkTitle, fillFileLinkCopy, isInsideRoots, parseFileLinkTarget, readFileLink } from '../file-links.js';
21
21
  import { sanitizeHtmlString } from '../assistant/asst-sanitize.js';
22
+ import { createTextCache } from '../text-cache.js';
22
23
  export { setCardExpanded } from './cdx-cards.js';
23
24
 
24
25
  // ── Shared context (set by cdx-tabs via setRenderContext) ──
@@ -105,8 +106,9 @@ export function esc(s) {
105
106
  // `<img onerror>` in a reply would run in SynaBun's origin with no click. Every
106
107
  // Markdown sink of the panel takes md()'s output, so it goes through the
107
108
  // allowlist sanitiser the Assistant, Claude and OpenCode panels use, here, once.
108
- const _mdCache = new Map();
109
- const _MD_CACHE_LIMIT = 512;
109
+ const _mdCache = createTextCache();
110
+ export const markdownCacheStats = () => _mdCache.stats();
111
+ export const clearMarkdownCache = () => _mdCache.clear();
110
112
  let _mdRenderer = null;
111
113
  let _mdParser;
112
114
  function markdownFallback(text) {
@@ -130,7 +132,7 @@ export function md(text) {
130
132
  _mdRenderer = renderer;
131
133
  _mdParser = parser;
132
134
  }
133
- const key = text || '';
135
+ const key = String(text || '');
134
136
  if (key.length > OUTPUT_LIMITS.input) return `<pre class="cxp-card-pre cxp-output-raw"><code>${esc(boundedText(key))}</code></pre>`;
135
137
  const cached = _mdCache.get(key);
136
138
  if (cached !== undefined) return cached;
@@ -144,10 +146,6 @@ export function md(text) {
144
146
  // A page with no DOM to sanitise in, or a sanitiser that fails, gets escaped text: never raw HTML.
145
147
  try { html = sanitizeHtmlString(html); }
146
148
  catch { html = markdownFallback(key); }
147
- if (_mdCache.size >= _MD_CACHE_LIMIT) {
148
- const firstKey = _mdCache.keys().next().value;
149
- if (firstKey !== undefined) _mdCache.delete(firstKey);
150
- }
151
149
  _mdCache.set(key, html);
152
150
  return html;
153
151
  }
@@ -3264,7 +3262,10 @@ export function resolveHistoryRenderSource(thread, fallbackItems = null, storedS
3264
3262
  // `authoritative`: the thread's turns are its whole history (a revert reply),
3265
3263
  // so the saved rendering is never used in their place, even when none remain.
3266
3264
  export function renderHistory(thread, fallbackItems = null, { authoritative = false } = {}) {
3267
- _ctx.flushThreadSnapshotSave(_ctx.boundTab);
3265
+ // A save still pending for what is on screen is written before the transcript is rebuilt; with nothing
3266
+ // pending there is nothing to keep (no forced serialisation of the whole transcript on every open).
3267
+ const pendingTab = _ctx.boundTab;
3268
+ if (pendingTab?.snapshotTimer || pendingTab?.snapshotIdleHandle) _ctx.flushThreadSnapshotSave(pendingTab);
3268
3269
  const preservedTokenUsage = thread?.id && _ctx.threadId && thread.id === _ctx.threadId
3269
3270
  ? _ctx.threadTokenUsage
3270
3271
  : null;
@@ -3341,7 +3342,7 @@ export function renderHistory(thread, fallbackItems = null, { authoritative = fa
3341
3342
  });
3342
3343
  _ctx.hideEmpty();
3343
3344
  _ctx.scrollEnd(true);
3344
- _ctx.flushThreadSnapshotSave(_ctx.boundTab);
3345
+ _ctx.scheduleThreadSnapshotSave(_ctx.boundTab);
3345
3346
  renderPostPlanActions(_ctx.boundTab);
3346
3347
  renderPostCompactionActions(_ctx.boundTab);
3347
3348
  }
@@ -417,7 +417,7 @@ export function renderUserInputRequest(requestId, params, protocol = {}) {
417
417
  <span class="cxp-ask-option-desc">${esc(optionDesc)}</span>
418
418
  `;
419
419
  const retainedValue = answers[answerKey]?.answers?.[0];
420
- if (!isOtherOption && retainedValue === optionLabel) btn.classList.add('selected');
420
+ if (!isOtherOption && (answers[answerKey]?.answers || []).includes(optionLabel)) btn.classList.add('selected');
421
421
  btn.addEventListener('click', async () => {
422
422
  if (isChangelogAsk && /edit first/i.test(optionLabel)) {
423
423
  const allButtons = Array.from(optionsEl.querySelectorAll('.cxp-ask-option'));
@@ -434,6 +434,16 @@ export function renderUserInputRequest(requestId, params, protocol = {}) {
434
434
  if (!opened) _ctx.restoreChangelogButtons(allButtons);
435
435
  return;
436
436
  }
437
+ if (question.multiSelect && !isOtherOption) {
438
+ otherWrap.hidden = true;
439
+ const selected = new Set(answers[answerKey]?.answers || []);
440
+ if (selected.has(optionLabel)) selected.delete(optionLabel); else selected.add(optionLabel);
441
+ const labels = new Set(explicitOptions.map((o) => typeof o === 'string' ? o : o.label));
442
+ const values = [...selected].filter((label) => labels.has(label));
443
+ if (values.length) answers[answerKey] = { answers: values }; else delete answers[answerKey];
444
+ optionsEl.querySelectorAll('.cxp-ask-option').forEach((node) => node.classList.toggle('selected', values.includes(node.querySelector('.cxp-ask-option-label')?.textContent)));
445
+ persistEditingDraft(); syncSubmit(); return;
446
+ }
437
447
  optionsEl.querySelectorAll('.cxp-ask-option').forEach((node) => node.classList.remove('selected'));
438
448
  btn.classList.add('selected');
439
449
  if (isOtherOption) {
@@ -125,6 +125,10 @@ export function injectStyles() {
125
125
  opacity: 1;
126
126
  overflow: visible;
127
127
  }
128
+ /* Windows: the shell is 92% opaque, so the blur behind it barely shows, yet Chromium recomputes it on every
129
+ repaint inside the panel (a transcript rendering, a stream). There it goes; the colour stays. */
130
+ html.platform-win32 .codex-panel,
131
+ html.desktop-titlebar--win32 .codex-panel { backdrop-filter: none; -webkit-backdrop-filter: none; }
128
132
  /* A floating terminal is promoted above the panel when focused. Raise the
129
133
  whole stacking context while a footer menu is open so it stays visible. */
130
134
  .codex-panel:has(.cxp-dropdown.open) {
@@ -1,4 +1,6 @@
1
+ import { answerWorkerQuestion } from '../worker-questions.js';
1
2
  import { cxpText, cxpSessionLabel } from './cdx-copy.js';
3
+ import { codexWantsRolloutPreview, rolloutPreviewApplies } from './cdx-preview.js';
2
4
  import { mountCodexSettings } from './cdx-settings.js';
3
5
  import { t } from '../i18n.js';
4
6
  import { getCliState, isCliInstalling } from '../cli-status.js';
@@ -14,8 +16,8 @@ import { accountName, accountSummary, accountSwitchPlan, accountText, serverAcco
14
16
  import { mountAccountMenu } from '../account-menu.js';
15
17
  import '../account-i18n.js';
16
18
  import { restoreMcpAppCards, teardownMcpApps, retireMcpAppNode, stripMcpAppRuntime } from './cdx-mcp-app-host.js';
17
- import { renderTurnDiff, completeTurnOutputs } from './cdx-render.js';
18
- import { hydrateOutputControls } from './cdx-output.js';
19
+ import { renderTurnDiff, completeTurnOutputs, clearMarkdownCache } from './cdx-render.js';
20
+ import { hydrateOutputControls, disposeOutputBlocks } from './cdx-output.js';
19
21
  import { sanitizeStoredHtml } from '../assistant/asst-sanitize.js';
20
22
  import {
21
23
  beginPlanTurn, capturePlanItem, finishPlanTurn, getPlanMarkdown, syncPlanMirrors, recordPlanTurn,
@@ -205,6 +207,13 @@ function autosizeInput() { _onAutosizeInput?.(); }
205
207
  function emit(...args) { _onEmit?.(...args); }
206
208
 
207
209
  function sendSocket(msg) {
210
+ if (_boundTab?.automationRunId && msg?.type === 'server_request_response') {
211
+ const tab = _boundTab;
212
+ answerWorkerQuestion(tab.automationRunId, msg.requestId, { behavior: msg.error || ['cancel', 'decline'].includes(msg.result?.action) || ['cancel', 'decline'].includes(msg.result?.decision) ? 'deny' : 'allow', result: msg.result, error: msg.error })
213
+ .then((result) => withTab(tab, () => handleServerRequestResponseResult({ responseToken: msg.responseToken, ok: true, status: result.stale ? 'stale' : 'answered' })),
214
+ (error) => withTab(tab, () => handleServerRequestResponseResult({ responseToken: msg.responseToken, ok: false, status: tab._workerQuestions?.has(String(msg.requestId)) ? 'delivery_failed' : 'stale', retryable: true, error: error.message })));
215
+ return true;
216
+ }
208
217
  const packet = msg && _boundTab?.id
209
218
  ? {
210
219
  ...msg,
@@ -277,6 +286,16 @@ function closeAutomationHistorySocket(tab) {
277
286
  }
278
287
  }
279
288
 
289
+ // The rollout read before Codex's app-server is up (server.js sendEarlyCodexRolloutPreview): shown only for
290
+ // the thread this tab shows, and only while nothing else (a stored snapshot) is on screen. The real history
291
+ // replaces it.
292
+ function paintRolloutPreview(tab, msg) {
293
+ const shownThread = String(_boundTab?.threadId || _threadId || '');
294
+ if (!rolloutPreviewApplies(msg, shownThread, _transcriptSourceType)) return;
295
+ renderHistory(msg.thread, msg.fallbackItems || null);
296
+ placeWorkerEnd(tab);
297
+ }
298
+
280
299
  function buildWsCallbacks(tab, options = {}) {
281
300
  return {
282
301
  onConnecting() {
@@ -309,6 +328,8 @@ function buildWsCallbacks(tab, options = {}) {
309
328
  userVerification: true,
310
329
  windowId: windowId,
311
330
  threadId: _threadId || null,
331
+ // The early rollout preview only for the tab on screen (server.js sendEarlyCodexRolloutPreview).
332
+ preview: codexWantsRolloutPreview(tab, _boundTab, _threadId),
312
333
  cwd: _project || null,
313
334
  accountId: _accountId || 'default',
314
335
  ...codexRuntimeSelection(tab),
@@ -450,6 +471,7 @@ function buildWsCallbacks(tab, options = {}) {
450
471
  // which rebuilds the transcript: they go back onto it afterwards.
451
472
  const heldRequests = heldServerRequests();
452
473
  if (msg.thread) {
474
+ if (msg.preview) { paintRolloutPreview(tab, msg); return; }
453
475
  if (_boundTab) {
454
476
  _boundTab.expectedThreadId = null;
455
477
  _boundTab.expectedThreadRequestId = null;
@@ -757,6 +779,7 @@ let _projectThreads = parseStoredJson(STOR.projectThreads, {});
757
779
  // async; getThreadSnapshot returns null pre-hydration and renderHistory falls
758
780
  // back to SDK/JSONL items.
759
781
  let _threadSnapshots = {};
782
+ let _threadSnapshotsHydrated = false;
760
783
  const _threadSnapshotsReady = fetchBlobNamespace('codex-snapshots')
761
784
  .then((map) => {
762
785
  const valid = {};
@@ -772,7 +795,8 @@ const _threadSnapshotsReady = fetchBlobNamespace('codex-snapshots')
772
795
  _threadSnapshots = { ...valid, ..._threadSnapshots };
773
796
  try { if (storage.getItem(STOR.threadSnapshots)) storage.removeItem(STOR.threadSnapshots); } catch {}
774
797
  })
775
- .catch(() => {});
798
+ .catch(() => {})
799
+ .finally(() => { _threadSnapshotsHydrated = true; });
776
800
  let _tabs = [];
777
801
  let _activeTabIdx = -1;
778
802
  let _boundTab = null;
@@ -2631,6 +2655,9 @@ function disposeDiscardedTab(tab) {
2631
2655
  clearTimeout(tab.snapshotTimer);
2632
2656
  tab.snapshotTimer = null;
2633
2657
  }
2658
+ tab.scrollController?.destroy();
2659
+ tab.scrollController = null;
2660
+ disposeOutputBlocks(tab.messagesEl);
2634
2661
  tab.messagesEl?.remove();
2635
2662
  tab.pillEl?.remove();
2636
2663
  }
@@ -2719,9 +2746,37 @@ export function renderProjects() {
2719
2746
  ddPopulate(projectDropdown, projectItemsForValue(_project), _project);
2720
2747
  }
2721
2748
 
2749
+ function paintInitialThreadSnapshot(tab) {
2750
+ if (!tab || tab.closed || !tab.threadId || tab.transcriptSourceType || tab.automationRunId) return false;
2751
+ const expectedThreadId = String(tab.threadId);
2752
+ let painted = false;
2753
+ withTab(tab, () => {
2754
+ const snap = getThreadSnapshot(expectedThreadId);
2755
+ if (!snap) return;
2756
+ painted = renderStoredTranscript(snap, 'Continue this Codex thread or start a fresh one.');
2757
+ if (painted) {
2758
+ updateThreadTokenUsage(tab.threadTokenUsage || snap.threadTokenUsage || null);
2759
+ renderPostPlanActions(tab);
2760
+ renderPostCompactionActions(tab);
2761
+ placeWorkerEnd(tab);
2762
+ }
2763
+ });
2764
+ if (!painted && !_threadSnapshotsHydrated && !tab._pendingSnapshotPaint) {
2765
+ tab._pendingSnapshotPaint = true;
2766
+ _threadSnapshotsReady.finally(() => {
2767
+ tab._pendingSnapshotPaint = false;
2768
+ if (!tab.closed && tab.threadId === expectedThreadId && !tab.transcriptSourceType) {
2769
+ paintInitialThreadSnapshot(tab);
2770
+ }
2771
+ });
2772
+ }
2773
+ return painted;
2774
+ }
2775
+
2722
2776
  export function updateActiveTabView() {
2723
2777
  const tab = activeTab();
2724
2778
  if (!_getPanelEl() || !tab) return;
2779
+ paintInitialThreadSnapshot(tab);
2725
2780
  bindTabState(tab);
2726
2781
  _onActiveTabChanged?.(tab);
2727
2782
  _tabs.forEach((entry) => {
@@ -2945,6 +3000,8 @@ export function closeTab(idx, { detachAutomation = false } = {}) {
2945
3000
  tab.closed = true;
2946
3001
  tab.titleRequestController?.abort();
2947
3002
  tab.titleRequestController = null;
3003
+ tab.authView?.dispose();
3004
+ tab.terminalAuthView?.dispose();
2948
3005
  if (tab.titleReassertTimer) clearTimeout(tab.titleReassertTimer);
2949
3006
  flushThreadSnapshotSave(tab);
2950
3007
  withTab(tab, () => {
@@ -2967,11 +3024,16 @@ export function closeTab(idx, { detachAutomation = false } = {}) {
2967
3024
  }
2968
3025
  tab.scrollController?.destroy();
2969
3026
  tab.scrollController = null;
3027
+ disposeOutputBlocks(tab.messagesEl);
2970
3028
  if (tab.messagesEl) retireMcpAppNode(tab.messagesEl, () => tab.messagesEl.remove());
2971
3029
  tab.pillEl?.remove();
2972
3030
  _tabs.splice(idx, 1);
2973
3031
  if (!_tabs.length) {
2974
3032
  _activeTabIdx = -1;
3033
+ // The module binding otherwise keeps the last closed transcript, its item
3034
+ // and request maps, and every closure on their DOM alive indefinitely.
3035
+ bindTabState(null);
3036
+ clearMarkdownCache();
2975
3037
  saveTabs();
2976
3038
  renderPills();
2977
3039
  _setVisible(false);
@@ -3165,7 +3227,13 @@ export function restoreTabs() {
3165
3227
  if (tab && !tab.automationRunId && (saved.running || saved.startingThread || saved.needsYou)) wereWorking.push(tab);
3166
3228
  }
3167
3229
  if (!_tabs.length) throw new Error('No restorable Codex tabs');
3168
- _activeTabIdx = Math.min(Math.max(Number(data.activeIdx) || 0, 0), _tabs.length - 1);
3230
+ const pendingTabId = globalThis.__synabunPendingTab?.provider === 'codex'
3231
+ ? String(globalThis.__synabunPendingTab.tabId || '')
3232
+ : '';
3233
+ const pendingIdx = pendingTabId ? _tabs.findIndex((entry) => String(entry.id) === pendingTabId) : -1;
3234
+ _activeTabIdx = pendingIdx >= 0
3235
+ ? pendingIdx
3236
+ : Math.min(Math.max(Number(data.activeIdx) || 0, 0), _tabs.length - 1);
3169
3237
  pruneDiscardableTabs({ keepTab: activeTab(), persist: false });
3170
3238
  if (!_tabs.length) throw new Error('No restorable Codex tabs');
3171
3239
  updateActiveTabView();
@@ -4597,6 +4665,7 @@ export function promptNameNewSession() {
4597
4665
  try {
4598
4666
  const statusResponse = await fetch(`/api/git/status?path=${encodeURIComponent(chosenProject)}`);
4599
4667
  const status = statusResponse.ok ? await statusResponse.json() : null;
4668
+ if (!status?.ok || !status.isGit || status.partial || status.stale || status.timedOut) throw new Error(status?.error || 'Git status is incomplete. Refresh before switching branches.');
4600
4669
  const shouldCheckout = !status?.changes?.length || window.confirm(
4601
4670
  `Switch from ${branchOriginal} to ${chosenBranch} with ${status.changes.length} uncommitted change${status.changes.length === 1 ? '' : 's'}? Git will preserve them when possible.`,
4602
4671
  );
@@ -6551,8 +6620,9 @@ function setModelForTab(tab, model) {
6551
6620
  function setEffortForTab(tab, effort) {
6552
6621
  if (!tab) return false;
6553
6622
  const normalized = String(effort || '').trim().toLowerCase();
6554
- const nextEffort = normalized === 'default' || normalized === 'none' ? 'off' : normalized;
6555
6623
  const supported = effortOptionsForTab(tab);
6624
+ // 'none' is a real level on models that advertise it; elsewhere it means the default.
6625
+ const nextEffort = normalized === 'default' || (normalized === 'none' && !supported.some((option) => option.id === 'none')) ? 'off' : normalized;
6556
6626
  if (!supported.some((option) => option.id === nextEffort)) {
6557
6627
  withTab(tab, () => appendSystem(`Unsupported reasoning effort for ${tab.model || 'this model'}: ${effort}. Use ${supported.map((option) => option.id).join(', ')}.`, 'error'));
6558
6628
  return false;
@@ -0,0 +1,218 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — one CLI's install state, for a side panel (no DOM)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What a panel needs to show for its CLI and to act on it: the latest answer of
6
+ // /api/system/tool-versions (cli-status.js), an install this page started or
7
+ // follows (POST /api/cli/install/:tool, or one another window started), a
8
+ // "failed to start" a session reported, Add to PATH, Re-check. One controller
9
+ // per panel; `onChange(view)` hears every change, `view()` is
10
+ // cli-install-view.js cliBannerView() plus `checking` (a Re-check is under way)
11
+ // and `nudging` (a held-back send asked for attention a moment ago).
12
+ //
13
+ // ui-cli-banner.js paints it as the Claude, Codex and OpenCode panels' banner;
14
+ // the Antigravity panel paints it as its setup card (agy/agy-setup.js).
15
+
16
+ import { subscribeCliStatus, recheckCliStatus, getCliDocUrl, getCliInstallCommand, getCliLabel, installCli, followCliInstall, getCliInstallJobs, addCliToPath } from './cli-status.js';
17
+ import { cliBannerView, cliInstallText, cliStateOf, reduceInstallJob, INSTALLABLE_CLIS } from './cli-install-view.js';
18
+
19
+ /** How long the note about where an install put the CLI stays, when it has one. */
20
+ export const DONE_NOTICE_MS = 12000;
21
+ export const NUDGE_MS = 700;
22
+
23
+ /**
24
+ * @param {object} o
25
+ * @param {string} o.toolKey 'claude-code' | 'codex' | 'opencode' | 'antigravity'
26
+ * @param {(view: object) => void} [o.onChange]
27
+ * @param {number} [o.doneNoticeMs]
28
+ * @param {number} [o.nudgeMs]
29
+ */
30
+ export function createCliInstallController({ toolKey, onChange = () => {}, doneNoticeMs = DONE_NOTICE_MS, nudgeMs = NUDGE_MS } = {}) {
31
+ let info = null; // this CLI in the latest answer of /api/system/tool-versions (null: none yet)
32
+ let forced = null; // { reason }: a session could not start it, and the server was not asked again yet
33
+ let job = null; // the install under way or just ended (cli-install-view.js reduceInstallJob)
34
+ let installing = false;
35
+ let addingPath = false;
36
+ let pathError = '';
37
+ let checking = false;
38
+ let looked = false; // asked whether the server is already installing it since it went missing
39
+ let doneTimer = null;
40
+ let nudgeTimer = null;
41
+ let destroyed = false;
42
+
43
+ const view = () => ({
44
+ ...cliBannerView({
45
+ toolKey,
46
+ label: getCliLabel(toolKey),
47
+ info,
48
+ forced,
49
+ job,
50
+ addingPath, pathError,
51
+ guideUrl: getCliDocUrl(toolKey),
52
+ installCommand: getCliInstallCommand(toolKey),
53
+ }, { t: cliInstallText }),
54
+ checking,
55
+ nudging: !!nudgeTimer,
56
+ job,
57
+ info,
58
+ });
59
+
60
+ const changed = () => {
61
+ if (destroyed) return;
62
+ try { onChange(view()); } catch (error) { console.error('[cli-install] onChange:', error); }
63
+ };
64
+
65
+ function clearDone() {
66
+ clearTimeout(doneTimer);
67
+ doneTimer = null;
68
+ if (job?.phase === 'done') job = null;
69
+ changed();
70
+ }
71
+
72
+ function apply(event) {
73
+ if (!job && event?.type !== 'start') job = reduceInstallJob(null, { type: 'start' });
74
+ job = reduceInstallJob(job, event);
75
+ if (event?.type === 'done' && event.ok && event.status && typeof event.status === 'object') {
76
+ info = event.status;
77
+ forced = null;
78
+ }
79
+ changed();
80
+ }
81
+
82
+ function settle(final) {
83
+ if (job?.phase === 'running' && final) job = reduceInstallJob(job, final);
84
+ if (job?.phase === 'done') {
85
+ clearTimeout(doneTimer);
86
+ const where = job.path;
87
+ if (where && (where.onPath === false || where.added === true)) doneTimer = setTimeout(clearDone, doneNoticeMs);
88
+ else job = null;
89
+ }
90
+ changed();
91
+ }
92
+
93
+ async function install() {
94
+ if (installing || !INSTALLABLE_CLIS.includes(toolKey)) return null;
95
+ installing = true;
96
+ clearTimeout(doneTimer);
97
+ job = reduceInstallJob(null, { type: 'start' });
98
+ changed();
99
+ let final = null;
100
+ try {
101
+ final = await installCli(toolKey, { onEvent: apply });
102
+ } catch (error) {
103
+ final = { type: 'done', ok: false, code: 'internal', error: error?.message || null };
104
+ } finally {
105
+ installing = false;
106
+ settle(final);
107
+ }
108
+ return final;
109
+ }
110
+
111
+ async function addPath() {
112
+ if (addingPath) return;
113
+ addingPath = true; pathError = ''; changed();
114
+ try {
115
+ const result = await addCliToPath(toolKey);
116
+ info = { ...info, pathStatus: { dir: result.dir, onPath: true, canAdd: false } };
117
+ job = reduceInstallJob(null, { type: 'done', ok: true, status: info, path: { ...result, added: true } });
118
+ settle();
119
+ } catch (error) { pathError = error.message; }
120
+ finally { addingPath = false; changed(); }
121
+ }
122
+
123
+ // An install the server is already running (another window started it, or this page was reloaded during
124
+ // one): followed here as if this page had started it.
125
+ async function followRunning() {
126
+ if (installing) return;
127
+ const jobs = await getCliInstallJobs();
128
+ if (destroyed || installing || jobs?.[toolKey]?.state !== 'running') return;
129
+ installing = true;
130
+ job = reduceInstallJob(null, { type: 'start' });
131
+ changed();
132
+ let final = null;
133
+ try {
134
+ final = await followCliInstall(toolKey, { onEvent: apply });
135
+ } catch (error) {
136
+ final = { type: 'done', ok: false, code: 'connection', error: error?.message || null };
137
+ } finally {
138
+ installing = false;
139
+ settle(final);
140
+ }
141
+ }
142
+
143
+ async function recheck() {
144
+ if (checking) return info;
145
+ checking = true;
146
+ // Asking again starts over: an earlier failure is not repeated over a fresh answer.
147
+ if (job?.phase === 'failed') job = null;
148
+ changed();
149
+ try {
150
+ const next = await recheckCliStatus(toolKey);
151
+ info = next || info;
152
+ if (cliStateOf(info) === 'installed' || cliStateOf(info) === 'broken') forced = null;
153
+ } catch { /* the view stays as it is */ }
154
+ finally {
155
+ checking = false;
156
+ changed();
157
+ }
158
+ return info;
159
+ }
160
+
161
+ const unsubscribe = subscribeCliStatus(toolKey, (next) => {
162
+ info = next || null;
163
+ const state = cliStateOf(info);
164
+ if (state === 'installed') {
165
+ forced = null;
166
+ if (job?.phase === 'failed') job = null;
167
+ }
168
+ if (state === 'missing') {
169
+ if (!looked) { looked = true; followRunning().catch(() => {}); }
170
+ } else {
171
+ looked = false;
172
+ }
173
+ changed();
174
+ });
175
+
176
+ return {
177
+ view,
178
+ /** Tell `onChange` again (the panel was shown, or rebuilt). */
179
+ refresh: changed,
180
+ /** Sending is held back: the CLI is missing, broken, failed to install, or being installed. */
181
+ blocked: () => view().blocked,
182
+ /** A send was held back: `nudging` is true for a moment. */
183
+ nudge() {
184
+ if (!view().show) return;
185
+ clearTimeout(nudgeTimer);
186
+ nudgeTimer = setTimeout(() => { nudgeTimer = null; changed(); }, nudgeMs);
187
+ changed();
188
+ },
189
+ /**
190
+ * A session could not start the CLI ("CLI not found", a spawn error): "failed to start" with its words at
191
+ * once, then the server is asked again; its answer decides what stays.
192
+ */
193
+ flagFailure(reason = null) {
194
+ forced = { reason: reason ? String(reason).slice(0, 300) : null };
195
+ changed();
196
+ recheckCliStatus(toolKey)
197
+ .then((next) => {
198
+ info = next || info;
199
+ const state = cliStateOf(info);
200
+ if (state === 'installed' || state === 'broken') forced = null;
201
+ changed();
202
+ })
203
+ .catch(() => {});
204
+ },
205
+ install,
206
+ recheck,
207
+ addPath,
208
+ clearDone,
209
+ /** Whether an install runs from this page now. */
210
+ installing: () => installing,
211
+ destroy() {
212
+ destroyed = true;
213
+ unsubscribe();
214
+ clearTimeout(doneTimer);
215
+ clearTimeout(nudgeTimer);
216
+ },
217
+ };
218
+ }
@@ -2,7 +2,8 @@
2
2
  // SynaBun Neural Interface — the CLI banner of the side panels: what it says
3
3
  // ═══════════════════════════════════════════
4
4
  //
5
- // What the Claude Code, Codex and OpenCode panels show above their transcript
5
+ // What the Claude Code, Codex and OpenCode panels show above their transcript (and the Antigravity panel's setup
6
+ // card words its install steps with)
6
7
  // for one CLI: nothing while it is installed, "not installed" with a one-click
7
8
  // Install (the vendor's own installer, run by the server: POST
8
9
  // /api/cli/install/:tool) and the command to run by hand, the progress of an
@@ -35,6 +36,7 @@ export const CLI_INSTALL_COPY = Object.freeze({
35
36
  stepPathAdded: 'Added {dir} to your PATH',
36
37
  bodyMissing: 'Install it with one click from {vendor}\'s official installer, or run:',
37
38
  bodyMissingManual: 'Install it with this command, then re-check:',
39
+ bodyMissingGuide: 'Follow its install guide, then re-check.',
38
40
  bodyBroken: '{label} was found at {path}, but it could not be started:',
39
41
  bodyBrokenVersion: '{label} {version} was found at {path}, but it could not be started:',
40
42
  bodyBrokenLatched: '{label} is installed, but it could not be started:',
@@ -80,11 +82,11 @@ export const CLI_VENDORS = Object.freeze({
80
82
  'claude-code': 'Anthropic',
81
83
  codex: 'OpenAI',
82
84
  opencode: 'OpenCode',
83
- gemini: 'Google',
85
+ antigravity: 'Google',
84
86
  });
85
87
 
86
88
  /** The CLIs a server can install with one click (POST /api/cli/install/:tool). */
87
- export const INSTALLABLE_CLIS = Object.freeze(['claude-code', 'codex', 'opencode']);
89
+ export const INSTALLABLE_CLIS = Object.freeze(['claude-code', 'codex', 'opencode', 'antigravity']);
88
90
 
89
91
  /** The steps an install reports (`{ type: 'step' }`), in the order they can come. */
90
92
  export const INSTALL_STEPS = Object.freeze(['start', 'download', 'verify', 'run', 'check', 'path']);
@@ -371,9 +373,10 @@ export function cliBannerView({ toolKey, label = '', info = null, forced = null,
371
373
  recheck: true,
372
374
  });
373
375
  }
376
+ // No command at all (none from the server, and none on the page either): the guide only.
374
377
  const text = offer
375
378
  ? t('bodyMissing', { vendor: CLI_VENDORS[toolKey] || name, label: name })
376
- : t('bodyMissingManual');
379
+ : t(command ? 'bodyMissingManual' : 'bodyMissingGuide');
377
380
  return Object.assign(view, {
378
381
  show: true, blocked: true, kind: 'missing', tone: 'warn', icon: '!',
379
382
  title: t('titleMissing', { label: name }),
@@ -1,6 +1,6 @@
1
1
  // ═══════════════════════════════════════════
2
2
  // SynaBun — CLI Status (shared)
3
- // Detects whether Claude / Codex / OpenCode CLIs are installed by
3
+ // Detects whether Claude / Codex / OpenCode / Antigravity CLIs are installed by
4
4
  // querying /api/system/tool-versions. Caches the response, fans out
5
5
  // updates to subscribers, and auto-polls (every 30s) while at least
6
6
  // one subscribed tool is not installed (missing, or found but broken).
@@ -17,33 +17,34 @@ import { cliStateOf, cliInstallOffer, INSTALLABLE_CLIS } from './cli-install-vie
17
17
 
18
18
  export { cliStateOf, cliInstallOffer };
19
19
 
20
- const TOOL_KEYS = ['claude-code', 'codex', 'opencode', 'gemini'];
20
+ const TOOL_KEYS = ['claude-code', 'codex', 'opencode', 'antigravity'];
21
21
 
22
22
  // The vendors' install pages. The server's `guideUrl` wins when it sends one.
23
23
  const CLI_DOC_URLS = {
24
24
  'claude-code': 'https://code.claude.com/docs/en/setup',
25
25
  'codex': 'https://learn.chatgpt.com/docs/codex/cli',
26
26
  'opencode': 'https://opencode.ai/v2/docs',
27
- 'gemini': 'https://github.com/google-gemini/gemini-cli',
27
+ 'antigravity': 'https://antigravity.google/docs/getting-started',
28
28
  };
29
29
 
30
30
  // Fallback only. The server computes the command that fits the machine (the
31
31
  // vendor's recommended installer for its system, or the package manager the
32
- // other CLIs came from: buildInstallCommand in lib/cli-update-planner.js) and
33
- // returns it as `installCommand`. These apply only before the first
34
- // tool-versions response.
32
+ // other CLIs came from: buildInstallCommand in lib/cli-update-planner.js; for
33
+ // the Antigravity CLI, the command of the install its button runs there, which
34
+ // is Homebrew's cask on a Mac that has Homebrew) and returns it as
35
+ // `installCommand`. These apply only before the first tool-versions response.
35
36
  const CLI_INSTALL_CMDS = {
36
37
  'claude-code': { posix: 'curl -fsSL https://claude.ai/install.sh | bash', windows: 'irm https://claude.ai/install.ps1 | iex' },
37
38
  'codex': { posix: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh', windows: 'powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"' },
38
39
  'opencode': { posix: 'curl -fsSL https://opencode.ai/v2/install | bash', windows: 'winget install --id Anomaly.OpenCode --exact' },
39
- 'gemini': { posix: 'npm install -g @google/gemini-cli', windows: 'npm install -g @google/gemini-cli' },
40
+ 'antigravity': { posix: 'curl -fsSL https://antigravity.google/cli/install.sh | bash', windows: 'irm https://antigravity.google/cli/install.ps1 | iex' },
40
41
  };
41
42
 
42
43
  const CLI_LABELS = {
43
44
  'claude-code': 'Claude Code',
44
45
  'codex': 'Codex',
45
46
  'opencode': 'OpenCode',
46
- 'gemini': 'Gemini CLI',
47
+ 'antigravity': 'Antigravity CLI',
47
48
  };
48
49
 
49
50
  const POLL_INTERVAL_MS = 30000;
@@ -66,6 +66,7 @@ export const KEYS = {
66
66
  ACTIVE_WORKSPACE: 'neural-active-workspace', // sessionStorage
67
67
  GRID_SNAP: 'neural-grid-snap',
68
68
  TOPRIGHT_TOOLS_HIDDEN: 'neural-topright-tools-hidden', // the workspace tools toggle is off ('true')
69
+ WHITEBOARD_TOOLBAR_HIDDEN: 'neural-whiteboard-toolbar-hidden', // the whiteboard rail (#wb-toolbar) is hidden behind the logo button ('true')
69
70
  CATEGORIES_VISIBLE: 'neural-categories-visible',
70
71
  WHITEBOARD: 'neural-whiteboard',
71
72
  WHITEBOARD_RULESET_ALERT_DISMISSED: 'neural-whiteboard-ruleset-alert-dismissed',
@@ -127,7 +128,7 @@ export const DEFAULT_KEYBINDS = {
127
128
  'toggle-assistant': 'Ctrl+a',
128
129
  'launch-claude': '1',
129
130
  'launch-codex': '2',
130
- 'launch-gemini': '3',
131
+ 'launch-antigravity': '3',
131
132
  'launch-opencode': '4',
132
133
  'launch-browser': 'b',
133
134
  'launch-youtube': 'y',
@@ -154,7 +155,7 @@ export const KEYBIND_META = {
154
155
  'toggle-assistant-panel': { label: 'Toggle Assistant Sidepanel', group: 'Launch App', icon: 'assistant' },
155
156
  'launch-claude': { label: 'Claude Code', group: 'Launch App', icon: 'claude' },
156
157
  'launch-codex': { label: 'Codex CLI', group: 'Launch App', icon: 'codex' },
157
- 'launch-gemini': { label: 'Gemini CLI', group: 'Launch App', icon: 'gemini' },
158
+ 'launch-antigravity': { label: 'Antigravity CLI', group: 'Launch App', icon: 'antigravity' },
158
159
  'launch-opencode': { label: 'OpenCode', group: 'Launch App', icon: 'opencode' },
159
160
  'launch-browser': { label: 'Browser', group: 'Launch App', icon: 'browser' },
160
161
  'launch-youtube': { label: 'YouTube', group: 'Launch App', icon: 'youtube' },