synabun 2.0.10 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (656) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +13 -13
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +302 -0
  24. package/lib/virustotal.mjs +203 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,423 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Assistant brain: Google Antigravity (`agy acp`, in the brain's own Antigravity home)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // One `agy acp` process and one ACP session per Assistant session (lib/antigravity/acp-connection.js), started in
6
+ // the brain's own Antigravity home (lib/antigravity/home.js: `--gemini-dir`): SynaBun's MCP server (its child gets
7
+ // the assistant role, the session pin, the desktop grant and the brain's credential from the agent's environment)
8
+ // beside the user's own servers, the user's global rules and skills, and the policy hook that asks this process
9
+ // about every tool call before it runs (lib/antigravity/policy.js → deps.policyCheck: the browser policy, plan mode,
10
+ // a remote session's read-only rules and the route gate; a refusal reaches the model with its reason and the turn
11
+ // goes on). The persona rides as the session's appended system prompt (_meta.antigravity.systemPrompt.append, which
12
+ // agy advertises in initialize). ACP session/update notifications become the unified envelope
13
+ // (assistant-envelope.js createAcpEnvelopeTranslator); a permission request the approval mode does not answer
14
+ // becomes a card (control_request), answered once (never "always": Antigravity would save it for later sessions).
15
+ // The model and the thinking level are the session's config options, switched per turn; images ride as ACP image
16
+ // blocks; Stop is session/cancel; a resumed conversation is session/resume (no replay) in the same home.
17
+
18
+ import { randomUUID } from 'node:crypto';
19
+ import { fileURLToPath } from 'node:url';
20
+ import { acpErrorInfo, createAcpConnection, isAuthRequired } from '../antigravity/acp-connection.js';
21
+ import { assertSafeAntigravityProject, antigravityHomeReadPolicy, antigravityHomeSettings, homeMcpTools, policyHookCommand, prepareAntigravityHome, toAntigravityMcpEntry, userAntigravityMcpServers, userAntigravitySettings, userGeminiDir } from '../antigravity/home.js';
22
+ import { acpCall, acpReadOnlyKind, acpToolPaths, acpUiToolName } from '../antigravity/tool-call.js';
23
+ import { splitAntigravityModel } from '../antigravity/catalog.js';
24
+ import { acpTurnUsage } from '../antigravity/usage.js';
25
+ import { createAcpEnvelopeTranslator } from '../assistant-envelope.js';
26
+ import { planModeInstructions } from '../assistant-persona.js';
27
+
28
+ /** The hook script of every SynaBun Antigravity home. */
29
+ export const ANTIGRAVITY_BRAIN_POLICY_HOOK = fileURLToPath(new URL('../antigravity/policy-hook.mjs', import.meta.url));
30
+ const CANCEL_GRACE_MS = 8_000;
31
+ const MCP_WATCH_MS = 120_000;
32
+ const LEVELS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
33
+
34
+ /** The Assistant's Antigravity approval mode: 'auto' (every call the policy allows runs) or 'default' (cards). */
35
+ export function antigravityApprovalMode(mode) {
36
+ return mode === 'auto' || mode === 'acceptEdits' || mode === 'bypassPermissions' ? 'auto' : 'default';
37
+ }
38
+
39
+ /** Composer images ({ base64 | data, mediaType }) → ACP image blocks. */
40
+ export function acpImageBlocks(images = []) {
41
+ const blocks = [];
42
+ for (const image of Array.isArray(images) ? images : []) {
43
+ let data = image?.base64 || image?.data || null;
44
+ let mimeType = image?.mediaType || image?.media_type || image?.mime || image?.mimeType || 'image/png';
45
+ if (!data || typeof data !== 'string') continue;
46
+ const inline = /^data:([^;,]+);base64,(.*)$/s.exec(data);
47
+ if (inline) { mimeType = inline[1]; data = inline[2]; }
48
+ blocks.push({ type: 'image', mimeType, data });
49
+ }
50
+ return blocks;
51
+ }
52
+
53
+ /** The ACP permission option for an answer: explicit kinds only, never allow-always. */
54
+ function permissionOption(options, wanted) {
55
+ const list = Array.isArray(options) ? options.filter((option) => option && typeof option.optionId === 'string') : [];
56
+ for (const kind of wanted) {
57
+ if (kind === 'allow_always') continue;
58
+ const found = list.find((option) => option.kind === kind);
59
+ if (found) return found.optionId;
60
+ }
61
+ return null;
62
+ }
63
+
64
+ /**
65
+ * The MCP servers of a brain's home: SynaBun's own entry, then every enabled server of the user's own Antigravity
66
+ * configuration (a brain keeps the user's servers, as a Claude brain keeps the user's settings).
67
+ */
68
+ export function antigravityBrainServers({ synabunMcpEntry = null, userGemini = userGeminiDir() } = {}) {
69
+ const servers = {};
70
+ const user = userAntigravityMcpServers(userGemini);
71
+ const own = synabunMcpEntry || user.SynaBun || null;
72
+ if (own) servers.SynaBun = toAntigravityMcpEntry(own);
73
+ for (const [name, entry] of Object.entries(user)) {
74
+ if (name === 'SynaBun' || entry?.disabled === true) continue;
75
+ servers[name] = toAntigravityMcpEntry(entry);
76
+ }
77
+ return servers;
78
+ }
79
+
80
+ export function createAntigravityBrain({ session, sink, deps = {}, persona = '' } = {}) {
81
+ if (typeof deps.antigravityLaunch !== 'function') throw new Error('Antigravity brain requires deps.antigravityLaunch');
82
+ const brain = session.brain || {};
83
+ const cwd = brain.cwd || deps.PACKAGE_ROOT || process.cwd();
84
+ const connect = typeof deps.connect === 'function' ? deps.connect : createAcpConnection;
85
+ const homeDir = typeof deps.antigravityHome === 'function' ? deps.antigravityHome(session.id) : null;
86
+ const userGemini = deps.userGemini || userGeminiDir();
87
+ const remoteReadOnly = deps.remoteReadOnly === true;
88
+ let approvalMode = antigravityApprovalMode(brain.permissionMode);
89
+ let planMode = brain.planMode === true || brain.permissionMode === 'plan';
90
+ let conn = null;
91
+ let sessionId = session.providerSessionId || null;
92
+ let resumeWanted = !!sessionId;
93
+ let registration = null;
94
+ let configOptions = [];
95
+ let turn = null; // { planned, cancelled, cancelTimer, model }
96
+ let lastSessionUsage = deps.sessionUsageBase && typeof deps.sessionUsageBase === 'object' ? deps.sessionUsageBase : null;
97
+ let startPromise = null;
98
+ let disposed = false;
99
+ let mcpTimer = null;
100
+ let mcpReported = null;
101
+ let replaying = false;
102
+ const pendingControls = new Map(); // requestId → { resolve, options }
103
+
104
+ const emit = (packet) => { if (!disposed) sink.send(packet); };
105
+ const note = (text) => emit({ type: 'stderr', text });
106
+ const optionOf = (category) => configOptions.find((o) => o?.category === category || o?.id === category) || null;
107
+ const valuesOf = (option) => {
108
+ const out = [];
109
+ for (const entry of option?.options || []) for (const v of Array.isArray(entry?.options) ? entry.options : [entry]) if (v && v.value !== undefined) out.push(String(v.value));
110
+ return out;
111
+ };
112
+ const currentValue = (category) => { const option = optionOf(category); return option?.currentValue !== undefined && option?.currentValue !== null ? String(option.currentValue) : null; };
113
+
114
+ const translator = createAcpEnvelopeTranslator({
115
+ emit,
116
+ brain: { cwd, sessionId: () => sessionId, model: () => currentValue('model') || brain.model || null },
117
+ planTurn: () => turn?.planned === true,
118
+ });
119
+ emit({ type: 'engine', engine: 'antigravity', brain: 'antigravity' });
120
+
121
+ function applyModes(mode, plan) {
122
+ if (mode === 'plan') planMode = true;
123
+ else if (mode) approvalMode = antigravityApprovalMode(mode);
124
+ if (typeof plan === 'boolean') planMode = plan;
125
+ }
126
+
127
+ /**
128
+ * The policy hook's question for this brain: first the runtime's checks (deps.policyCheck: browser policy, plan mode, remote read-only, route gate); then the approval
129
+ * mode: auto lets every call run, default lets reads and SynaBun's own tools run and asks about the rest.
130
+ */
131
+ function policyCheck(call) {
132
+ let verdict = null;
133
+ try { verdict = typeof deps.policyCheck === 'function' ? deps.policyCheck(call) : null; } catch (error) { return { decision: 'deny', reason: `SynaBun could not check this call (${error?.message || error}).` }; }
134
+ if (verdict?.decision === 'deny') return verdict;
135
+ const homeRead = antigravityHomeReadPolicy(call, { homeDir, cwd });
136
+ if (homeRead) return homeRead;
137
+ if (remoteReadOnly || approvalMode === 'auto') return { decision: 'allow' };
138
+ if (acpReadOnlyKind(call.kind)) return { decision: 'allow' };
139
+ if (call.mcp?.server === 'SynaBun') return { decision: 'allow' };
140
+ return { decision: 'ask' };
141
+ }
142
+
143
+ function cancelControls(reason = 'turn_ended') {
144
+ for (const [requestId, pending] of pendingControls) {
145
+ pendingControls.delete(requestId);
146
+ emit({ type: 'control_cancelled', request_id: requestId, reason });
147
+ try { pending.resolve({ outcome: { outcome: 'cancelled' } }); } catch { /* answered */ }
148
+ }
149
+ }
150
+
151
+ async function onPermission(params, { signal } = {}) {
152
+ if (disposed) return { outcome: { outcome: 'cancelled' } };
153
+ const call = acpCall(params);
154
+ const options = params?.options || [];
155
+ const pick = (kinds) => {
156
+ const optionId = permissionOption(options, kinds);
157
+ return optionId ? { outcome: { outcome: 'selected', optionId } } : { outcome: { outcome: 'cancelled' } };
158
+ };
159
+ const verdict = policyCheck(call);
160
+ // The hook asked first; a refusal reaching here (a call the hook never saw) ends the turn, as a user's would.
161
+ if (verdict.decision === 'deny') return pick(['reject_once', 'reject_always']);
162
+ if (verdict.decision === 'allow') return pick(['allow_once']);
163
+ const requestId = `agy-${randomUUID()}`;
164
+ const packet = {
165
+ type: 'control_request',
166
+ request_id: requestId,
167
+ request: {
168
+ subtype: 'can_use_tool', provider: 'antigravity', kind: 'permission',
169
+ tool_name: acpUiToolName(call),
170
+ input: call.mcp ? { ...call.mcp.args } : { ...call.args, ...(call.command ? { command: call.command } : {}) },
171
+ title: call.title || null,
172
+ brain_native: { kind: 'permission', toolCallId: call.id, sessionId: params?.sessionId || sessionId, toolCall: params?.toolCall || null },
173
+ },
174
+ };
175
+ return new Promise((resolveAnswer) => {
176
+ pendingControls.set(requestId, { resolve: resolveAnswer, options });
177
+ signal?.addEventListener?.('abort', () => {
178
+ if (!pendingControls.delete(requestId)) return;
179
+ emit({ type: 'control_cancelled', request_id: requestId, reason: 'withdrawn' });
180
+ resolveAnswer({ outcome: { outcome: 'cancelled' } });
181
+ }, { once: true });
182
+ emit(packet);
183
+ });
184
+ }
185
+
186
+ function reportMcp() {
187
+ if (!homeDir) return null;
188
+ const tools = homeMcpTools(homeDir, 'SynaBun');
189
+ const status = tools.length ? 'connected' : null;
190
+ if (status && mcpReported !== status) {
191
+ mcpReported = status;
192
+ emit({ type: 'event', event: { type: 'system', subtype: 'mcp_status', servers: [{ name: 'SynaBun', status, tools: tools.length }] } });
193
+ }
194
+ return status;
195
+ }
196
+ function watchMcp() {
197
+ if (!homeDir || mcpTimer) return;
198
+ const until = Date.now() + MCP_WATCH_MS;
199
+ mcpTimer = setInterval(() => {
200
+ if (disposed || reportMcp() || Date.now() > until) { clearInterval(mcpTimer); mcpTimer = null; }
201
+ }, 1000);
202
+ mcpTimer.unref?.();
203
+ }
204
+
205
+ async function openSession() {
206
+ const meta = persona ? { antigravity: { systemPrompt: { append: persona } } } : undefined;
207
+ const init = await conn.initialize();
208
+ let answer = null;
209
+ if (sessionId && resumeWanted) {
210
+ const caps = init?.agentCapabilities?.sessionCapabilities || {};
211
+ // agy 1.4.12 sends the conversation's history again while it resumes (session/resume as session/load): the
212
+ // panel has it already, so nothing that arrives before the answer is shown.
213
+ replaying = true;
214
+ try {
215
+ answer = await conn.request(caps.resume ? 'session/resume' : 'session/load', { sessionId, cwd, mcpServers: [], ...(meta ? { _meta: meta } : {}) });
216
+ sessionId = String(answer?.sessionId || sessionId);
217
+ } catch (error) {
218
+ if (isAuthRequired(error)) throw error;
219
+ note(`Antigravity could not resume its conversation (${acpErrorInfo(error).message}); this brain starts a new one.`);
220
+ answer = null;
221
+ sessionId = null;
222
+ } finally {
223
+ replaying = false;
224
+ }
225
+ }
226
+ if (!answer) {
227
+ answer = await conn.request('session/new', { cwd, mcpServers: [], ...(meta ? { _meta: meta } : {}) });
228
+ sessionId = String(answer?.sessionId || '');
229
+ if (!sessionId) throw new Error('Antigravity did not return a session id');
230
+ lastSessionUsage = null;
231
+ }
232
+ resumeWanted = false;
233
+ configOptions = Array.isArray(answer?.configOptions) ? answer.configOptions : [];
234
+ try { deps.onAntigravitySession?.(sessionId); } catch { /* bookkeeping */ }
235
+ reportMcp();
236
+ watchMcp();
237
+ }
238
+
239
+ async function boot() {
240
+ if (disposed) throw new Error('Antigravity brain disposed');
241
+ const launch = deps.antigravityLaunch();
242
+ if (!launch?.command) throw new Error('The Antigravity CLI (agy) is not installed. Install it from the Antigravity side panel, sign in there, then try again — or switch the Assistant to another provider.');
243
+ assertSafeAntigravityProject(cwd);
244
+ let hookCommand = null;
245
+ if (homeDir) {
246
+ if (deps.policy?.register && deps.policyUrl) hookCommand = policyHookCommand({ script: deps.policyHookScript || ANTIGRAVITY_BRAIN_POLICY_HOOK });
247
+ prepareAntigravityHome({
248
+ dir: homeDir,
249
+ mcpServers: antigravityBrainServers({ synabunMcpEntry: typeof deps.synabunMcpEntry === 'function' ? deps.synabunMcpEntry() : deps.synabunMcpEntry, userGemini }),
250
+ hookCommand,
251
+ settings: antigravityHomeSettings({ userSettings: userAntigravitySettings(userGemini) }),
252
+ userGemini,
253
+ });
254
+ }
255
+ if (hookCommand) registration = deps.policy.register((call) => (disposed ? { decision: 'deny', reason: 'This Assistant session ended.' } : policyCheck(call)), { label: `brain ${session.id}` });
256
+ const base = typeof deps.childEnv === 'function' ? deps.childEnv() : { ...process.env };
257
+ for (const key of ['SYNABUN_TOOLS', 'SYNABUN_TOOLS_PATH', 'SYNABUN_PROFILE', 'SYNABUN_RUNTIME_PROFILE_PATH', 'SYNABUN_TOOL_CATALOG_MODE', 'SYNABUN_AGY_POLICY_URL', 'SYNABUN_AGY_POLICY_TOKEN', 'SYNABUN_BROWSER_SESSION', 'SYNABUN_BROWSER_TAB']) delete base[key];
258
+ const env = {
259
+ ...base,
260
+ // The brain's SynaBun MCP child (stdio) reads who it serves from here (mcp-server/src/services/identity.ts).
261
+ SYNABUN_ROLE: 'assistant',
262
+ SYNABUN_TERMINAL_SESSION: String(session.id),
263
+ ...(deps.desktopGrant ? { SYNABUN_DESKTOP_GRANT: String(deps.desktopGrant) } : {}),
264
+ ...(deps.brainCredential ? { SYNABUN_BRAIN_TOKEN: String(deps.brainCredential) } : {}),
265
+ ...(registration ? { SYNABUN_AGY_POLICY_URL: deps.policyUrl, SYNABUN_AGY_POLICY_TOKEN: registration.token } : {}),
266
+ };
267
+ conn = connect({
268
+ command: launch.command,
269
+ args: launch.args || [],
270
+ acpArgs: homeDir ? ['--gemini-dir', homeDir] : [],
271
+ cwd,
272
+ env,
273
+ clientInfo: { name: 'synabun', title: 'SynaBun Assistant', version: '0' },
274
+ onUpdate: (params) => {
275
+ if (disposed || replaying || !params?.update || !sessionId || params.sessionId !== sessionId) return;
276
+ // Between turns only titles and the like count: a stray message of a turn that already ended is not shown.
277
+ if (!turn && params.update.sessionUpdate !== 'session_info_update') return;
278
+ translator.handle(params.update);
279
+ },
280
+ onPermission,
281
+ onExit: (info) => {
282
+ if (disposed) return;
283
+ cancelControls('agent_exited');
284
+ const why = info?.error || (info?.signal ? `signal ${info.signal}` : `code ${info?.code}`);
285
+ if (turn) finishTurn(null, new Error(`Antigravity exited (${why})${info?.stderr ? `: ${String(info.stderr).trim().split('\n').slice(-2).join(' ')}` : ''}`));
286
+ else note(`Antigravity exited (${why}); the next message starts it again.`);
287
+ conn = null;
288
+ startPromise = null;
289
+ resumeWanted = !!sessionId;
290
+ },
291
+ });
292
+ try {
293
+ await openSession();
294
+ } catch (error) {
295
+ const auth = isAuthRequired(error);
296
+ const message = auth ? 'Antigravity is not signed in: open the Antigravity panel (or run `agy` in a terminal) and sign in, then try again.' : `Antigravity could not start: ${acpErrorInfo(error).message}`;
297
+ registration?.dispose();
298
+ registration = null;
299
+ try { await conn?.close(); } catch { /* gone */ }
300
+ conn = null;
301
+ startPromise = null;
302
+ throw Object.assign(new Error(message), { code: auth ? 'AUTH_REQUIRED' : 'START_FAILED' });
303
+ }
304
+ return true;
305
+ }
306
+ function start() {
307
+ if (!startPromise) startPromise = boot();
308
+ return startPromise;
309
+ }
310
+
311
+ async function setOption(category, value, label) {
312
+ const option = optionOf(category);
313
+ if (!value || !option) return;
314
+ if (String(option.currentValue) === String(value)) return;
315
+ const accepted = valuesOf(option);
316
+ if (accepted.length && !accepted.includes(String(value))) { note(`Antigravity offers no ${label} "${value}" here; it keeps ${option.currentValue}.`); return; }
317
+ try {
318
+ const answer = await conn.request('session/set_config_option', { sessionId, configId: option.id, value: String(value) });
319
+ if (Array.isArray(answer?.configOptions)) configOptions = answer.configOptions;
320
+ } catch (error) { note(`Antigravity kept its ${label} (${acpErrorInfo(error).message}).`); }
321
+ }
322
+ /** The turn's model and thinking level (the record may have changed since the session started; a routed turn names its own). */
323
+ async function applyModel(model, effort) {
324
+ const wanted = splitAntigravityModel(model, valuesOf(optionOf('model')));
325
+ if (wanted.model) await setOption('model', wanted.model, 'model');
326
+ const level = effort && LEVELS.has(String(effort).toLowerCase()) ? String(effort).toLowerCase() : wanted.effort;
327
+ if (level) await setOption('thought_level', level, 'thinking level');
328
+ }
329
+
330
+ function finishTurn(answer, error = null) {
331
+ const current = turn;
332
+ if (!current) return;
333
+ turn = null;
334
+ if (current.cancelTimer) clearTimeout(current.cancelTimer);
335
+ cancelControls('turn_ended');
336
+ const usage = answer?.usage ? acpTurnUsage(answer.usage, lastSessionUsage) : null;
337
+ if (answer?.usage) {
338
+ lastSessionUsage = answer.usage;
339
+ try { deps.onAntigravitySessionUsage?.(sessionId, answer.usage); } catch { /* bookkeeping */ }
340
+ }
341
+ if (usage) { try { deps.onAntigravityUsage?.({ usage, model: currentValue('model') || brain.model || null, sessionId }); } catch { /* metering never breaks a turn */ } }
342
+ const stopReason = current.cancelled ? 'cancelled' : (answer?.stopReason || 'end_turn');
343
+ translator.endTurn({ stopReason, usage, error: error && !current.cancelled ? (error.message || String(error)) : null, planned: current.planned === true });
344
+ }
345
+
346
+ async function runPrompt(blocks, { planned = false } = {}) {
347
+ translator.reset();
348
+ turn = { planned, cancelled: false, cancelTimer: null };
349
+ const mine = turn;
350
+ conn.request('session/prompt', { sessionId, prompt: blocks }, { timeoutMs: 0 })
351
+ .then((answer) => { if (turn === mine) finishTurn(answer); })
352
+ .catch((error) => { if (turn === mine) finishTurn(null, new Error(acpErrorInfo(error).message)); });
353
+ }
354
+
355
+ return {
356
+ kind: 'antigravity',
357
+ start,
358
+ async sendUserTurn({ text, images = [], model = null, effort = null, permissionMode: mode = null, planMode: plan = null } = {}) {
359
+ await start();
360
+ if (disposed || !conn) return false;
361
+ if (turn) throw new Error('Antigravity is still answering the last message');
362
+ applyModes(mode, plan);
363
+ if (resumeWanted && sessionId) await openSession();
364
+ await applyModel(model || brain.model || null, effort || brain.effort || null);
365
+ // A plan turn carries what plan mode means here (a remote session's read-only brain does not).
366
+ const planned = planMode && !remoteReadOnly;
367
+ const body = planned ? `<system-reminder>\n${planModeInstructions({ provider: 'antigravity' })}\n</system-reminder>\n\n${String(text ?? '')}` : String(text ?? '');
368
+ await runPrompt([{ type: 'text', text: body }, ...acpImageBlocks(images)], { planned });
369
+ return true;
370
+ },
371
+ async abort() {
372
+ if (!turn) { cancelControls('stopped'); return; }
373
+ const current = turn;
374
+ current.cancelled = true;
375
+ cancelControls('stopped');
376
+ try { if (conn && sessionId) await conn.notify('session/cancel', { sessionId }); } catch { /* the answer or the grace timer ends it */ }
377
+ // An agent that never answers the cancelled prompt: the turn ends here anyway.
378
+ current.cancelTimer = setTimeout(() => { if (turn === current) finishTurn(null); }, CANCEL_GRACE_MS);
379
+ current.cancelTimer.unref?.();
380
+ },
381
+ async setPermissionMode(mode, { planMode: plan = null } = {}) { applyModes(mode, plan); emit({ type: 'mode_changed', mode: approvalMode, planMode }); },
382
+ respondControl(requestId, response = {}) {
383
+ const pending = pendingControls.get(String(requestId));
384
+ if (!pending) { emit({ type: 'error', message: `No pending Antigravity request ${requestId}` }); return; }
385
+ pendingControls.delete(String(requestId));
386
+ // Once, whatever the answer says about "always": Antigravity would save it for every later session.
387
+ const allow = response?.behavior === 'allow';
388
+ const optionId = permissionOption(pending.options, allow ? ['allow_once'] : ['reject_once', 'reject_always']);
389
+ pending.resolve(optionId ? { outcome: { outcome: 'selected', optionId } } : { outcome: { outcome: 'cancelled' } });
390
+ },
391
+ // Antigravity's own /compact command, as a turn of its own (busy until it ends).
392
+ async compact() {
393
+ await start();
394
+ if (disposed || !conn || turn) return;
395
+ await runPrompt([{ type: 'text', text: '/compact' }]);
396
+ },
397
+ async mcpStatus() { if (!reportMcp()) emit({ type: 'event', event: { type: 'system', subtype: 'mcp_status', servers: [] } }); },
398
+ // The conversation to pick up: resumed before the next message (session/resume in the brain's home).
399
+ async resume(id) { if (!id) return; sessionId = String(id); resumeWanted = true; lastSessionUsage = null; },
400
+ async listSessions() { return []; },
401
+ isBusy() { return !!turn || pendingControls.size > 0; },
402
+ // The route gate: its hook checks every call before it runs; without one (no policy wired) the runtime checks
403
+ // the tool starts it sees (the reactive fallback).
404
+ get gateMode() { return registration ? 'hook' : 'reactive'; },
405
+ identity() {
406
+ return { providerSessionId: sessionId, providerThreadId: null, accountId: null, model: currentValue('model') || brain.model || null, effort: currentValue('thought_level') || brain.effort || null, cwd, busy: !!turn, home: homeDir };
407
+ },
408
+ async dispose() {
409
+ if (disposed) return;
410
+ const wasRunning = !!turn;
411
+ cancelControls('brain_disposed');
412
+ if (wasRunning && conn && sessionId) { try { await conn.notify('session/cancel', { sessionId }); } catch { /* closing anyway */ } }
413
+ disposed = true;
414
+ if (mcpTimer) { clearInterval(mcpTimer); mcpTimer = null; }
415
+ if (turn?.cancelTimer) clearTimeout(turn.cancelTimer);
416
+ turn = null;
417
+ registration?.dispose();
418
+ registration = null;
419
+ try { await conn?.close(); } catch { /* gone */ }
420
+ conn = null;
421
+ },
422
+ };
423
+ }
@@ -123,10 +123,11 @@ export function createClaudeBrain({ session, sink, deps = {}, persona = '', hook
123
123
  'X-Synabun-Role': 'assistant', 'X-Synabun-Terminal': session.id, 'X-Synabun-Memory-Session': session.id,
124
124
  ...(deps.desktopGrant ? { 'X-Synabun-Desktop-Grant': deps.desktopGrant } : {}),
125
125
  ...mcpHeaders,
126
- // A WhatsApp conversation's brain credential (whatsapp_send_file). The header names the variable and
126
+ // This brain launch's credential (assistant-runtime.js mintBrainCredential). The header names the variable and
127
127
  // the CLI expands it from its own environment (below): the MCP config is on its command line, which every
128
128
  // account of the computer can read, an environment only this user's processes. The in-process MCP server
129
- // keeps the value for the request it arrived with, and sends it on the file route alone. Last, so nothing replaces it.
129
+ // keeps the value for the request it arrived with, and sends it only to the routes that ask who the brain is
130
+ // (whatsapp_send_file, the Skills Service). Last, so nothing replaces it.
130
131
  ...(deps.brainCredential ? { 'X-Synabun-Brain-Token': '${SYNABUN_BRAIN_TOKEN}' } : {}),
131
132
  },
132
133
  // A remote (WhatsApp) session never pre-approves computer use: its PreToolUse hook refuses, asks or allows each call.
@@ -143,7 +144,7 @@ export function createClaudeBrain({ session, sink, deps = {}, persona = '', hook
143
144
  ...(typeof deps.claudeAccountEnv === 'function' ? deps.claudeAccountEnv(brain.accountId) : {}),
144
145
  SYNABUN_ASSISTANT_SESSION: String(session.id),
145
146
  ...(deps.userContextThreads === true ? { SYNABUN_ASSISTANT_THREADS: '1' } : {}),
146
- // The value of the X-Synabun-Brain-Token header above (a WhatsApp conversation's brain only).
147
+ // The value of the X-Synabun-Brain-Token header above.
147
148
  ...(deps.brainCredential ? { SYNABUN_BRAIN_TOKEN: deps.brainCredential } : {}),
148
149
  },
149
150
  maxBudgetUsd: Number(deps.maxBudgetUsd) > 0 ? Number(deps.maxBudgetUsd) : undefined,
@@ -6,7 +6,9 @@
6
6
  // PreToolUse payload from stdin and asks lib/browser-tool-policy.js here, with
7
7
  // no server round trip; a refusal becomes permissionDecision "deny" with the
8
8
  // reason. Script paths resolve against the payload's cwd, else
9
- // SYNABUN_BROWSER_POLICY_CWD (the run's project). An unreadable payload allows
9
+ // SYNABUN_BROWSER_POLICY_CWD (the run's project); SYNABUN_BROWSER_POLICY_ROOTS
10
+ // (a JSON list, written as the run starts) names the registered code projects
11
+ // its test runners may also run in. An unreadable payload allows
10
12
  // the call: the policy is a speed bump, not a sandbox. A policy module that
11
13
  // cannot load fails closed (logged on stderr): shell commands, web search and
12
14
  // every MCP tool but SynaBun's are denied with that reason.
@@ -25,6 +27,14 @@ function unloadedDenial(tool) {
25
27
  return `SynaBun browser policy: ${tool} was refused — SynaBun's browser policy could not load (${loadError?.message || 'browserToolDenial is missing'}), so no command, web search or other server's tool can be checked for a browser launch. SynaBun's own tools still work. Stop and report this`;
26
28
  }
27
29
 
30
+ /** The registered code projects from the env (a JSON list of paths); anything unreadable is none. */
31
+ function policyRoots() {
32
+ try {
33
+ const list = JSON.parse(process.env.SYNABUN_BROWSER_POLICY_ROOTS || '[]');
34
+ return Array.isArray(list) ? list.filter((path) => typeof path === 'string') : [];
35
+ } catch { return []; }
36
+ }
37
+
28
38
  let raw = '';
29
39
  try { for await (const chunk of process.stdin) raw += chunk; } catch {}
30
40
  let payload = {};
@@ -35,7 +45,7 @@ try {
35
45
  const input = payload.tool_input && typeof payload.tool_input === 'object' ? payload.tool_input : {};
36
46
  const cwd = (typeof payload.cwd === 'string' && payload.cwd) || process.env.SYNABUN_BROWSER_POLICY_CWD || process.cwd();
37
47
  if (typeof browserToolDenial !== 'function') process.stderr.write(`[SynaBun] browser policy could not load (${loadError?.message || 'browserToolDenial is missing'}); denying shell, web search and other servers' tools\n`);
38
- const reason = !tool ? null : typeof browserToolDenial === 'function' ? browserToolDenial(tool, input, { host: 'codex', cwd }) : unloadedDenial(tool);
48
+ const reason = !tool ? null : typeof browserToolDenial === 'function' ? browserToolDenial(tool, input, { host: 'codex', cwd, roots: policyRoots() }) : unloadedDenial(tool);
39
49
  // Codex prints "Command blocked by PreToolUse hook: <reason>. Command: <cmd>", so
40
50
  // the reason goes without its own final period.
41
51
  if (reason) process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason.replace(/[.\s]+$/, '') } }));
@@ -4,9 +4,19 @@
4
4
  // tool may run; a refusal becomes permissionDecision "deny" with the reason.
5
5
  // The call's whole tool_input travels (the remote denylist reads paths from
6
6
  // it), marked inputComplete; one too big to send goes as its command/action
7
- // only, marked incomplete. Any failure (no env, server unreachable, bad reply)
8
- // allows the call — except in a WhatsApp session (SYNABUN_ROUTE_GATE_REMOTE=1),
9
- // where a call SynaBun could not check is denied.
7
+ // only, marked incomplete. No env or no answer at all (the server unreachable
8
+ // or slow) allows the call — except in a WhatsApp session
9
+ // (SYNABUN_ROUTE_GATE_REMOTE=1), where a call SynaBun could not check is
10
+ // denied. An answer that is not a decision (the check itself refused, such as
11
+ // 403 PEER_REFUSED, an error status, a body without `allow`) denies the call in
12
+ // every session and says what SynaBun answered: a refusal never passes for an
13
+ // allow.
14
+ //
15
+ // The request carries the session's pin (X-Synabun-Assistant): this hook runs
16
+ // under the brain's Codex app-server, an AI agent's process tree, which
17
+ // SynaBun's owner gate (lib/owner-authority.js) refuses unless the call says
18
+ // it is an agent's own. The per-session token in the body is what the route
19
+ // itself checks.
10
20
 
11
21
  const url = process.env.SYNABUN_ROUTE_GATE_URL;
12
22
  const session = process.env.SYNABUN_ROUTE_GATE_SESSION;
@@ -14,6 +24,7 @@ const token = process.env.SYNABUN_ROUTE_GATE_TOKEN;
14
24
  const remote = process.env.SYNABUN_ROUTE_GATE_REMOTE === '1';
15
25
  const MAX_INPUT_BYTES = 256 * 1024;
16
26
  const UNCHECKED = 'SynaBun could not check this call, so a WhatsApp session does not run it.';
27
+ const PIN_HEADER = 'x-synabun-assistant';
17
28
 
18
29
  let raw = '';
19
30
  try { for await (const chunk of process.stdin) raw += chunk; } catch {}
@@ -37,20 +48,32 @@ function callArguments(toolInput) {
37
48
  function deny(reason) {
38
49
  process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: String(reason).replace(/[.\s]+$/, '') } }));
39
50
  }
51
+ /** The refusal of a call whose check got an answer that is not a decision: what SynaBun answered, in short. */
52
+ function notADecision(status, body) {
53
+ const code = body && typeof body === 'object' && typeof body.code === 'string' ? ` ${body.code.slice(0, 64)}` : '';
54
+ const said = body && typeof body === 'object' && typeof body.error === 'string' ? `: ${body.error.slice(0, 300)}` : '';
55
+ return `SynaBun could not check this call (its route gate answered ${status}${code}${said}), so it did not run.`;
56
+ }
40
57
 
41
58
  if (url && session && token) {
59
+ let response = null;
42
60
  try {
43
- const response = await fetch(url, {
61
+ response = await fetch(url, {
44
62
  method: 'POST',
45
- headers: { 'content-type': 'application/json' },
63
+ // The session id as a header value: printable ASCII only (fetch refuses anything else before sending).
64
+ headers: { 'content-type': 'application/json', [PIN_HEADER]: String(session).replace(/[^\x21-\x7e]/g, '').slice(0, 200) },
46
65
  body: JSON.stringify({ session, token, host: 'codex', tool: String(input.tool_name || input.toolName || input.tool || ''), agent: input.agent_id || null, ...callArguments(input.tool_input) }),
47
66
  signal: AbortSignal.timeout(5000),
48
67
  });
49
- const decision = await response.json();
50
- if (decision && decision.allow === false) deny(decision.reason || 'SynaBun refused this call.');
51
- else if (remote && !(decision && decision.allow === true)) deny(UNCHECKED);
52
- } catch {
68
+ } catch { response = null; }
69
+ if (!response) {
53
70
  if (remote) deny(UNCHECKED);
71
+ } else {
72
+ let decision = null;
73
+ try { decision = await response.json(); } catch { decision = null; }
74
+ if (response.ok && decision && decision.allow === true) { /* allowed: Codex runs the call */ }
75
+ else if (response.ok && decision && decision.allow === false) deny(decision.reason || 'SynaBun refused this call.');
76
+ else deny(notADecision(response.status, decision));
54
77
  }
55
78
  }
56
79
  process.exit(0);
@@ -14,6 +14,8 @@ import { createCodexEnvelopeTranslator, codexControlResponse } from '../assistan
14
14
  import { baseModelId, contextVariantOf } from '../assistant-catalog.js';
15
15
  import { codexPlanPermission, codexReadOnlyPermission, planDenyMessage, readOnlyDenyMessage } from '../assistant-plan-permissions.js';
16
16
  import { planModeInstructions } from '../assistant-persona.js';
17
+ import { shouldAutoAcceptCodexMcpToolApproval } from '../codex-mcp-approvals.js';
18
+ import { codexMcpToolApproval } from '../../public/shared/cdx/cdx-protocol.js';
17
19
 
18
20
  const READY_TIMEOUT_MS = 90_000;
19
21
  // Approvals the Codex sidepanel's Auto-accept answers on its own (cdx-requests.js).
@@ -83,7 +85,7 @@ export function createCodexBrain({ session, sink, deps = {}, persona = '' } = {}
83
85
  }
84
86
  if (planned) {
85
87
  const method = packet.request?.brain_native?.method || packet.request?.method || '';
86
- const tool = packet.request?.brain_native?.params?._meta?.tool_name || packet.request?.tool_name || 'request';
88
+ const tool = codexMcpToolApproval(packet.request?.brain_native?.params)?.fullName || packet.request?.tool_name || 'request';
87
89
  const response = planned === 'allow'
88
90
  ? { behavior: 'allow', ...(!remoteReadOnly && approvalMode === 'auto' ? { always: true } : {}) }
89
91
  : { behavior: 'deny', decline: true, message: remoteReadOnly ? readOnlyDenyMessage(tool) : planDenyMessage(tool, { host: 'codex' }) };
@@ -91,7 +93,9 @@ export function createCodexBrain({ session, sink, deps = {}, persona = '' } = {}
91
93
  sink.send({ type: 'stderr', text: `${planMode ? 'Plan mode' : 'Read-only (WhatsApp)'}: ${planned === 'allow' ? 'allowed' : 'declined'} ${String(method).split('/').slice(1, -1).join('/') || 'request'} ${tool}${planned === 'allow' ? '' : remoteReadOnly ? ' (read-only)' : ' (no code changes)'}` });
92
94
  return;
93
95
  }
94
- if (packet.type === 'control_request' && !planMode && !remoteReadOnly && approvalMode === 'auto' && AUTO_ACCEPT_METHODS.has(packet.request?.brain_native?.method || packet.request?.method)) {
96
+ const method = packet.request?.brain_native?.method || packet.request?.method;
97
+ const autoMcp = method === 'mcpServer/elicitation/request' && shouldAutoAcceptCodexMcpToolApproval(packet.request?.brain_native?.params, { autoAccept: approvalMode === 'auto', role: 'assistant' });
98
+ if (packet.type === 'control_request' && !planMode && !remoteReadOnly && (autoMcp || (approvalMode === 'auto' && AUTO_ACCEPT_METHODS.has(method)))) {
95
99
  // Auto-accept: approve at once (for the session when offered), as the Codex sidepanel does.
96
100
  send({ type: 'server_request_response', ...codexControlResponse(packet, { behavior: 'allow', always: true }) });
97
101
  sink.send({ type: 'stderr', text: `Auto-accepted: ${String(packet.request?.brain_native?.method || packet.request?.method).split('/').slice(1, -1).join('/')}` });
@@ -119,8 +123,8 @@ export function createCodexBrain({ session, sink, deps = {}, persona = '' } = {}
119
123
  });
120
124
  const socket = createVirtualSocket({
121
125
  name: `codex-brain-${session.id}`,
122
- // A WhatsApp conversation's brain credential rides on the socket object, never in a message: the skin
123
- // handler gives it to this app-server's SynaBun MCP child through its environment (whatsapp_send_file).
126
+ // The brain's launch credential rides on the socket object, never in a message: the skin handler gives it to
127
+ // this app-server's SynaBun MCP child through its environment (whatsapp_send_file, the Skills Service).
124
128
  credential: deps.brainCredential || null,
125
129
  onSend: (packet) => {
126
130
  if (disposed || !packet || typeof packet !== 'object') return;
@@ -159,7 +163,7 @@ export function createCodexBrain({ session, sink, deps = {}, persona = '' } = {}
159
163
  readyResolve = () => { clearTimeout(timer); resolve(true); };
160
164
  });
161
165
  send({
162
- type: 'bootstrap', windowId, accountId: brain.accountId || 'default', mcpProfile: brain.mcpProfile || null,
166
+ type: 'bootstrap', windowId, accountId: brain.accountId || 'default',
163
167
  cwd: brain.cwd || deps.PACKAGE_ROOT || undefined, threadId: threadId || null, role: 'assistant',
164
168
  // Unlocks the capability-gated `computer` MCP group for this brain only.
165
169
  desktopGrant: deps.desktopGrant || null,