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
@@ -1,20 +1,23 @@
1
+ import { OPENCODE_BRAIN_TOOLS } from '../assistant-tools.js';
1
2
  // ═══════════════════════════════════════════
2
3
  // SynaBun — Assistant brain: OpenCode (isolated serve + SDK client)
3
4
  // ═══════════════════════════════════════════
4
5
  //
5
6
  // One isolated `opencode serve` per assistant session (same helpers the
6
7
  // OpenCode sidepanel uses). The persona is installed as a custom primary agent
7
- // (`agent.assistant.prompt`) in the per-serve config so the model sees it as
8
- // its system prompt; SSE events are translated to the unified envelope.
8
+ // (`agents.assistant.system`) in the per-serve opencode.json so the model sees
9
+ // it as its system prompt; SSE events are translated to the unified envelope.
9
10
 
10
- import { chmodSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
11
+ import { chmodSync, writeFileSync } from 'node:fs';
11
12
  import { dirname, resolve } from 'node:path';
12
13
  import { fileURLToPath, pathToFileURL } from 'node:url';
13
14
  import { normalizeOpenCodeModel } from '../native-loop-providers.js';
14
- import { compactOrSummarize } from '../opencode-v2-client.js';
15
+ import { agentOf, mcpServersOf, opencodeConfigFilePath, readOpencodeConfigDocument, setAgent, setMcpServer, withPlugin, withoutPlugin } from '../opencode-config.js';
15
16
  import { createOpenCodeEnvelopeTranslator, opencodeControlResponse } from '../assistant-envelope.js';
16
17
  import { opencodePlanPermission, opencodeReadOnlyPermission, planDenyMessage, readOnlyDenyMessage } from '../assistant-plan-permissions.js';
17
18
  import { planModeInstructions } from '../assistant-persona.js';
19
+ import { waitForSynabunTools, synabunConfigured, synabunToolsTimeoutText, OPENCODE_MCP_READY_TIMEOUT_MS, OPENCODE_MCP_SETTLE_MS, OPENCODE_MCP_LIST_GRACE_MS } from '../opencode-mcp-ready.js';
20
+ export { synabunMcpState } from '../opencode-mcp-ready.js';
18
21
 
19
22
  /** The MCP terminal pin of an assistant session's OpenCode brain. */
20
23
  export function opencodeTermIdFor(sessionId) {
@@ -34,14 +37,8 @@ export function opencodeImageParts(images = []) {
34
37
  return parts;
35
38
  }
36
39
 
37
- /** The route-gate plugin (opencode-route-gate.js) as a file URL the serve can load. */
38
- export const OPENCODE_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode-route-gate.js')).href;
39
- /**
40
- * The same gate for OpenCode 2 (opencode2-route-gate/: OpenCode 2 loads a plugin
41
- * folder from the config's `plugins` list). Both entries are written: OpenCode 1
42
- * ignores `plugins`, OpenCode 2 skips the 1.x file (a warning in its log).
43
- */
44
- export const OPENCODE2_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode2-route-gate')).href;
40
+ /** The route-gate plugin (opencode2-route-gate/, a plugin folder OpenCode loads from `plugins`) as a file URL. */
41
+ export const OPENCODE_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode2-route-gate')).href;
45
42
 
46
43
  /**
47
44
  * SynaBun's plan agent. OpenCode adds its own read-only reminder ("commands may
@@ -52,71 +49,111 @@ export const OPENCODE2_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURL
52
49
  */
53
50
  export const OPENCODE_PLAN_AGENT = 'synabun-plan';
54
51
 
52
+ /**
53
+ * Tools this brain's agents must never call. Code Mode's `execute` runs arbitrary code (a `fetch`
54
+ * included) that no argument check can read, so the route gate refuses it before routing - and this
55
+ * brain's SynaBun tools are direct (codemode: false), so the only thing `execute` can do here is earn
56
+ * that refusal. Denied at the agent, the model is not offered it at all.
57
+ */
58
+ export const OPENCODE_BRAIN_DENIED_TOOLS = Object.freeze(['execute']);
59
+
55
60
  /** The brain's SynaBun MCP tool-call timeout (ms): as long as the Codex brain's tool_timeout_sec (server.js). */
56
61
  export const OPENCODE_BRAIN_MCP_TIMEOUT_MS = 180_000;
57
62
 
63
+ // Compatibility exports for the brain's existing consumers and tests.
64
+ export const OPENCODE_BRAIN_MCP_READY_TIMEOUT_MS = OPENCODE_MCP_READY_TIMEOUT_MS;
65
+ export const OPENCODE_BRAIN_MCP_SETTLE_MS = OPENCODE_MCP_SETTLE_MS;
66
+ export const OPENCODE_BRAIN_MCP_LIST_GRACE_MS = OPENCODE_MCP_LIST_GRACE_MS;
67
+ export const assistantSynabunConfigured = synabunConfigured;
68
+ export { OPENCODE_BRAIN_TOOLS } from '../assistant-tools.js';
69
+
70
+ /**
71
+ * Why the brain's serve would start with SynaBun behind Code Mode (`execute` only, which the route gate
72
+ * refuses before routing, so agent_route would be unreachable), read back from the written config; null
73
+ * when it is fine. No config file at all: the serve has no SynaBun server, the gate turns itself off.
74
+ */
75
+ export function assistantConfigProblem(xdgRoot) {
76
+ let doc;
77
+ try { doc = readOpencodeConfigDocument(opencodeConfigFilePath(resolve(xdgRoot, 'opencode'))); } catch (err) { return `its OpenCode configuration could not be read (${err?.message || err})`; }
78
+ if (!doc.exists) return null;
79
+ if (doc.error) return 'its OpenCode configuration could not be read';
80
+ const synabun = mcpServersOf(doc.config).SynaBun;
81
+ if (synabun && synabun.codemode !== false) return 'SynaBun\'s tools could not be set up for direct use (OpenCode Code Mode stayed on)';
82
+ return null;
83
+ }
84
+
58
85
  /**
59
86
  * `routeGate` ({ url, session, token }) installs the route-gate plugin in this
60
- * isolated serve's config only (assistant brains; never the shared sidepanel setup).
87
+ * isolated serve's opencode.json only (assistant brains; never the shared
88
+ * sidepanel setup). Everything is written in OpenCode 2's schema.
61
89
  */
62
90
  export function patchOpenCodeAssistantConfig(xdgRoot, { persona = '', role = 'assistant', model = null, desktopGrant = null, routeGate = null, brainCredential = null } = {}) {
63
- const configPath = resolve(xdgRoot, 'opencode', 'config.json');
64
- if (!existsSync(configPath)) return false;
65
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
66
- if (config.mcp?.SynaBun && typeof config.mcp.SynaBun === 'object') {
67
- const envKey = config.mcp.SynaBun.environment ? 'environment' : (config.mcp.SynaBun.env ? 'env' : 'environment');
68
- const env = { ...(config.mcp.SynaBun[envKey] || {}), SYNABUN_ROLE: role };
91
+ const configPath = opencodeConfigFilePath(resolve(xdgRoot, 'opencode'));
92
+ const doc = readOpencodeConfigDocument(configPath);
93
+ if (!doc.exists || doc.error) return false;
94
+ const config = doc.config;
95
+ const synabun = mcpServersOf(config).SynaBun;
96
+ if (synabun) {
97
+ const env = { ...(synabun.environment || {}), SYNABUN_ROLE: role };
69
98
  // Computer use: an unforgeable per-brain grant unlocks the `computer` group.
70
99
  if (desktopGrant) env.SYNABUN_DESKTOP_GRANT = desktopGrant;
71
100
  else delete env.SYNABUN_DESKTOP_GRANT;
72
- // A WhatsApp conversation's brain credential (whatsapp_send_file), for this serve's SynaBun MCP child only.
101
+ // The brain's launch credential (whatsapp_send_file, the Skills Service), for this serve's SynaBun MCP child only.
73
102
  // This file names the variable; the value is in the serve's own environment (ensureIsolatedServe), so
74
103
  // neither this file (it is in every backup of data/) nor OPENCODE_CONFIG_CONTENT ever holds it.
75
104
  if (brainCredential) env.SYNABUN_BRAIN_TOKEN = '{env:SYNABUN_BRAIN_TOKEN}';
76
105
  else delete env.SYNABUN_BRAIN_TOKEN;
77
- config.mcp.SynaBun[envKey] = env;
78
106
  // OpenCode gives an MCP tool call 60 s unless the server says otherwise; agent_route may wait on the
79
107
  // user's route card and whatsapp_send_file on an upload (130 s): the Codex brain's 180 s here too.
80
- config.mcp.SynaBun.timeout = OPENCODE_BRAIN_MCP_TIMEOUT_MS;
108
+ // OpenCode 2.1's MCP default is Code Mode: only `execute` reaches these tools. The route gate must
109
+ // refuse arbitrary execute code until routed, so wrapping agent_route (and exempt memory tools) deadlocks
110
+ // the brain. Expose this brain's SynaBun tools directly instead. The serve's OPENCODE_CONFIG_CONTENT
111
+ // overlay carries this entry past project config too; other servers, workers and sidepanels keep their mode.
112
+ setMcpServer(config, 'SynaBun', { ...synabun, codemode: false, environment: env, timeout: { ...(synabun.timeout || {}), execution: OPENCODE_BRAIN_MCP_TIMEOUT_MS } });
81
113
  }
82
- config.agent = { ...(config.agent || {}) };
83
- config.agent.assistant = {
84
- ...(config.agent.assistant || {}),
114
+ const rule = (action, effect, resource = '*') => ({ action, resource, effect });
115
+ // An agent's rules with ours last, in place of any it had for the same tools (as OpenCode 1's
116
+ // `permission.<tool>` replaced the user's value): every command asks whatever was allowed before, and the
117
+ // list stays as it is when the config is patched again.
118
+ const withOurRules = (agent, ours) => [...(agent.permissions || []).filter((own) => !ours.some((r) => r.action === own?.action)), ...ours];
119
+ const withoutExecute = OPENCODE_BRAIN_DENIED_TOOLS.map((name) => rule(name, 'deny'));
120
+ const assistant = agentOf(config, 'assistant');
121
+ setAgent(config, 'assistant', {
122
+ ...assistant,
85
123
  mode: 'primary',
86
124
  description: 'SynaBun central assistant',
87
- prompt: persona || undefined,
125
+ ...(persona ? { system: persona } : {}),
88
126
  ...(model ? { model } : {}),
89
- };
127
+ permissions: withOurRules(assistant, withoutExecute),
128
+ });
90
129
  // SynaBun's plan agent (desktop plan turns): no code changes, anything else as the approval mode says.
91
- config.agent[OPENCODE_PLAN_AGENT] = {
92
- ...(config.agent[OPENCODE_PLAN_AGENT] || {}),
130
+ const synabunPlan = agentOf(config, OPENCODE_PLAN_AGENT);
131
+ setAgent(config, OPENCODE_PLAN_AGENT, {
132
+ ...synabunPlan,
93
133
  mode: 'primary',
94
134
  hidden: true,
95
135
  description: 'SynaBun plan mode: explore, run commands and use every tool; no code changes',
96
- prompt: persona || undefined,
136
+ ...(persona ? { system: persona } : {}),
97
137
  ...(model ? { model } : {}),
98
- permission: { ...(config.agent[OPENCODE_PLAN_AGENT]?.permission || {}), edit: 'deny', bash: 'ask', question: 'allow', task: { general: 'deny' } },
99
- };
138
+ permissions: withOurRules(synabunPlan, [...withoutExecute, rule('edit', 'deny'), rule('shell', 'ask'), rule('question', 'allow'), rule('subagent', 'deny', 'general')]),
139
+ });
100
140
  // OpenCode's own plan agent (a WhatsApp session's read-only brain) refuses only
101
141
  // edits and runs any command without asking: here every command asks, so the
102
142
  // read-only classifier decides on the whole line.
103
- config.agent.plan = { ...(config.agent.plan || {}), permission: { ...(config.agent.plan?.permission || {}), bash: 'ask' } };
104
- const plugins = (Array.isArray(config.plugin) ? config.plugin : []).filter((entry) => (Array.isArray(entry) ? entry[0] : entry) !== OPENCODE_ROUTE_GATE_PLUGIN);
105
- const plugins2 = (Array.isArray(config.plugins) ? config.plugins : []).filter((entry) => (entry && typeof entry === 'object' ? entry.package : entry) !== OPENCODE2_ROUTE_GATE_PLUGIN);
143
+ const plan = agentOf(config, 'plan');
144
+ setAgent(config, 'plan', { ...plan, permissions: withOurRules(plan, [rule('shell', 'ask')]) });
145
+ withoutPlugin(config, OPENCODE_ROUTE_GATE_PLUGIN);
106
146
  // `remote`: a WhatsApp session's serve — the plugin refuses a call it could not check (fail closed).
107
147
  if (routeGate?.url && routeGate?.session && routeGate?.token) {
108
- const options = { url: routeGate.url, session: routeGate.session, token: routeGate.token, ...(routeGate.remote === true ? { remote: true } : {}) };
109
- plugins.push([OPENCODE_ROUTE_GATE_PLUGIN, options]);
110
- plugins2.push({ package: OPENCODE2_ROUTE_GATE_PLUGIN, options });
148
+ withPlugin(config, OPENCODE_ROUTE_GATE_PLUGIN, { url: routeGate.url, session: routeGate.session, token: routeGate.token, ...(routeGate.remote === true ? { remote: true } : {}) });
111
149
  }
112
- if (plugins.length) config.plugin = plugins; else delete config.plugin;
113
- if (plugins2.length) config.plugins = plugins2; else delete config.plugins;
114
- writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
150
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
115
151
  // It carries this brain's grants (the desktop grant): readable by this user only.
116
152
  try { chmodSync(configPath, 0o600); } catch { /* a filesystem without modes */ }
117
153
  return true;
118
154
  }
119
155
 
156
+
120
157
  /** The assistant's OpenCode approval mode: 'auto' (auto-accept) or 'default'. */
121
158
  export function opencodeApprovalMode(mode) {
122
159
  return mode === 'auto' || mode === 'acceptEdits' || mode === 'bypassPermissions' ? 'auto' : 'default';
@@ -169,8 +206,13 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
169
206
  let keepAliveTimer = null;
170
207
  let costUsd = 0;
171
208
  let startPromise = null;
172
- // The model the last turn ran on: what a compaction summarises with.
173
- let lastTurnModel = null;
209
+ let synabunExpected = false;
210
+ let stops = 0;
211
+ let waitingAbort = null;
212
+ const mcpWait = {
213
+ readyTimeoutMs: deps.mcpReadyTimeoutMs, settleMs: deps.mcpSettleMs,
214
+ listGraceMs: deps.mcpListGraceMs, pollMs: deps.mcpPollMs,
215
+ };
174
216
  // Plan mode runs SynaBun's plan agent (OPENCODE_PLAN_AGENT; OpenCode's own
175
217
  // `plan` agent, the persona riding as `system`, when the config patch failed).
176
218
  let approvalMode = opencodeApprovalMode(brain.permissionMode);
@@ -219,17 +261,29 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
219
261
  }
220
262
 
221
263
  /** The serve's MCP servers as an `mcp_status` event (the runtime reads whether SynaBun, and so agent_route, is reachable). */
264
+ function sendMcpStatus(servers) { sink.send({ type: 'event', event: { type: 'system', subtype: 'mcp_status', servers } }); }
222
265
  async function reportMcpStatus() {
223
- try { const status = await client?.mcp?.status?.({ directory: cwd }); sink.send({ type: 'event', event: { type: 'system', subtype: 'mcp_status', servers: status?.data || status || [] } }); } catch {}
266
+ try { const status = await client?.mcp?.status?.({ directory: cwd }); sendMcpStatus(status?.data || status || []); } catch {}
224
267
  }
225
268
 
226
269
  async function start() {
227
270
  if (disposed) throw new Error('OpenCode brain disposed');
228
271
  if (startPromise) return startPromise;
229
272
  startPromise = (async () => {
230
- const xdgRoot = deps.setupOpencodeSidepanelConfig(termId, brain.mcpProfile || null);
231
- try { personaInstalled = patchOpenCodeAssistantConfig(xdgRoot, { persona, model: null, desktopGrant: deps.desktopGrant || null, routeGate: deps.routeGate || null, brainCredential: deps.brainCredential || null }); } catch { personaInstalled = false; }
232
- entry = await deps.ensureIsolatedServe(termId, { xdgRoot, keepAlive: true, mcpProfile: brain.mcpProfile || null, brainCredential: deps.brainCredential || null });
273
+ const xdgRoot = deps.setupOpencodeSidepanelConfig(termId, brain.tools ?? OPENCODE_BRAIN_TOOLS, { cwd: brain.cwd, scopeExternal: false });
274
+ // A brain whose config could not be patched would start with Code Mode on: SynaBun's tools only behind
275
+ // `execute`, which the route gate refuses before routing, so agent_route is unreachable. Refuse to start.
276
+ let patchError = null;
277
+ try { personaInstalled = patchOpenCodeAssistantConfig(xdgRoot, { persona, model: null, desktopGrant: deps.desktopGrant || null, routeGate: deps.routeGate || null, brainCredential: deps.brainCredential || null }); } catch (err) { personaInstalled = false; patchError = err; }
278
+ const unready = assistantConfigProblem(xdgRoot);
279
+ if (unready) {
280
+ throw new Error(`OpenCode Assistant could not start: ${unready}${patchError?.message ? ` (${patchError.message})` : ''}. Check Settings → OpenCode, or switch the Assistant to another provider.`);
281
+ }
282
+ // Its turns go out once SynaBun's tools are on the model's list (waitForSynabunTools).
283
+ synabunExpected = assistantSynabunConfigured(xdgRoot);
284
+ // An inherited OPENCODE_CONFIG_DIR would replace this isolated configuration (route-gate plugin,
285
+ // the assistant and plan agents): the brain's serve never reads it.
286
+ entry = await deps.ensureIsolatedServe(termId, { xdgRoot, keepAlive: true, tools: brain.tools ?? OPENCODE_BRAIN_TOOLS, brainCredential: deps.brainCredential || null, extraEnv: { OPENCODE_CONFIG_DIR: null } });
233
287
  if (!entry?.client) throw new Error('OpenCode isolated serve failed to start');
234
288
  client = entry.client;
235
289
  await client.waitUntilConnected?.(20_000);
@@ -253,7 +307,9 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
253
307
  kind: 'opencode',
254
308
  start,
255
309
  async sendUserTurn({ text, images = [], model = null, effort = null, permissionMode: mode = null, planMode: plan = null } = {}) {
310
+ const stop = stops;
256
311
  await start();
312
+ if (disposed || stops !== stop) return false;
257
313
  applyModes(mode, plan);
258
314
  turnActive = true;
259
315
  // The runtime passes the effective model/effort on every turn (the record
@@ -262,7 +318,6 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
262
318
  const request = {
263
319
  sessionID: sessionId, parts, model: normalizeOpenCodeModel(model || brain.model), directory: cwd,
264
320
  };
265
- if (request.model) lastTurnModel = request.model;
266
321
  if (effort) request.variant = String(effort);
267
322
  turnPlanned = planMode && !remoteReadOnly;
268
323
  if (turnPlanned && personaInstalled) {
@@ -277,14 +332,25 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
277
332
  else if (personaInstalled) request.agent = brain.agent || 'assistant';
278
333
  else { request.agent = brain.agent || 'build'; if (persona) request.system = persona; }
279
334
  if (entry) entry.lastUsed = Date.now();
280
- // SynaBun may have connected (or failed) since the last turn: the route gate follows it.
281
- await reportMcpStatus();
335
+ // SynaBun's tools on the model's list first (a fresh serve may still be connecting them); the route gate
336
+ // follows what was seen. Stopped meanwhile (its `aborted` is out) or disposed: the turn never goes out.
337
+ const controller = new AbortController();
338
+ waitingAbort = controller;
339
+ let ready;
340
+ try {
341
+ ready = await waitForSynabunTools(client, { ...mcpWait, directory: cwd, expected: synabunExpected,
342
+ signal: controller.signal, stopped: () => disposed || stops !== stop,
343
+ onStatus: sendMcpStatus,
344
+ onTimeout: (ms) => sink.send({ type: 'event', event: { type: 'system', subtype: 'status', text: synabunToolsTimeoutText(ms, 'brain') } }),
345
+ });
346
+ } finally { if (waitingAbort === controller) waitingAbort = null; }
347
+ if (!ready || disposed || stops !== stop) { turnActive = false; return false; }
282
348
  const response = await client.session.promptAsync(request);
283
349
  const status = Number(response?.status);
284
350
  if (Number.isFinite(status) && status >= 400) { turnActive = false; throw new Error(`OpenCode prompt failed (${status})`); }
285
351
  return true;
286
352
  },
287
- async abort() { if (client && sessionId) await client.session.abort({ sessionID: sessionId, directory: cwd }).catch(() => {}); emit({ type: 'aborted' }); },
353
+ async abort() { stops += 1; waitingAbort?.abort(); if (client && sessionId) await client.session.abort({ sessionID: sessionId, directory: cwd }).catch(() => {}); emit({ type: 'aborted' }); },
288
354
  async setPermissionMode(mode, { planMode: plan = null } = {}) { applyModes(mode, plan); sink.send({ type: 'mode_changed', mode: approvalMode, planMode }); },
289
355
  respondControl(requestId, response) {
290
356
  const pending = pendingControls.get(String(requestId));
@@ -296,12 +362,11 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
296
362
  : client.permission.reply({ requestID: body.requestID, reply: body.reply, directory: cwd });
297
363
  Promise.resolve(call).catch((error) => sink.send({ type: 'error', message: `OpenCode reply failed: ${error?.message || error}` }));
298
364
  },
299
- // OpenCode 1.18.34 answers v2.session.compact with 503 "not available yet":
300
- // compactOrSummarize then summarises on the model of the last turn.
365
+ // OpenCode compacts the session on the model it runs on.
301
366
  async compact() {
302
367
  await start();
303
368
  turnPlanned = false;
304
- await compactOrSummarize(client, { sessionID: sessionId, directory: cwd, model: lastTurnModel || normalizeOpenCodeModel(brain.model) });
369
+ await client.session.compact({ sessionID: sessionId, directory: cwd });
305
370
  },
306
371
  async mcpStatus() { await reportMcpStatus(); },
307
372
  async reconcileUsage(meter) {
@@ -337,6 +402,7 @@ export function createOpenCodeBrain({ session, sink, deps = {}, persona = '' } =
337
402
  async dispose() {
338
403
  if (disposed) return;
339
404
  disposed = true;
405
+ waitingAbort?.abort();
340
406
  if (keepAliveTimer) clearInterval(keepAliveTimer);
341
407
  if (unsubscribe) { try { unsubscribe(); } catch {} }
342
408
  try { deps.stopIsolatedServe(termId); } catch {}
@@ -1,19 +1,33 @@
1
- // SynaBun — an Assistant task run's browser policy as an OpenCode 2 plugin.
2
- // The same policy as ../opencode-browser-policy.js (read that file for what it
3
- // does and why), for OpenCode 2's plugin API: a directory OpenCode loads from
4
- // the config's `plugins` entry, whose default export is `{ id, setup(context) }`
5
- // with a hook on `tool.execute.before`; a refusal is thrown. OpenCode 2 calls
6
- // the shell tool `shell` and the sub-agent tool `subagent`: the policy, written
7
- // for OpenCode 1's names, is asked with `bash` and `task`.
1
+ // SynaBun — OpenCode plugin: the browser policy of an Assistant task run.
2
+ // Loaded only by the isolated `opencode serve` of a worker the Assistant
3
+ // dispatched (its opencode.json's `plugins` entry, { package: <this folder as a
4
+ // file URL>, options }, added by withOpenCodeBrowserPolicy from server.js
5
+ // setupOpencodeLoopConfig); loops, schedules and the OpenCode sidepanel never
6
+ // load it. OpenCode 2's plugin API: the default export is `{ id, setup(context) }`,
7
+ // registering `context.tool.hook('execute.before', …)`, which sees `{ tool, sessionID, input }`;
8
+ // a refusal is thrown as a plain Error, which OpenCode records as that tool's
9
+ // error, and the loop continues. Before every tool call it asks
10
+ // lib/browser-tool-policy.js here, with no server round trip. OpenCode calls the
11
+ // shell tool `shell` and the sub-agent tool `subagent`: the policy is asked with
12
+ // the names the rest of SynaBun knows (`bash`, `task`). Script paths resolve
13
+ // against the run's cwd (the plugin options), else the serve's directory; the
14
+ // options' `roots` name the registered code projects its test runners may also
15
+ // run in (the serve is sandboxed and cannot read SynaBun's data home: server.js
16
+ // lists them as the run starts). A
17
+ // policy module that cannot load fails closed: the failure is logged once, and
18
+ // the shell, webfetch, websearch and every tool that is not OpenCode's own or
19
+ // SynaBun's (another MCP server's, another plugin's) are refused with that
20
+ // reason until the run ends.
8
21
 
9
22
  // An absolute file URL: the policy loads from SynaBun's lib/ wherever OpenCode imports this file from.
10
23
  const POLICY_URL = new URL('../../browser-tool-policy.js', import.meta.url).href;
11
24
  const V1_NAMES = { shell: 'bash', subagent: 'task' };
12
25
 
13
- // OpenCode's own tools that run no command and fetch no page (OpenCode 1's names).
26
+ // OpenCode's own tools that run no command, fetch no page and open no browser (by the names above). The shell,
27
+ // webfetch, websearch and OpenCode's browser are not here; any other name belongs to an MCP server or a plugin.
14
28
  const OWN_TOOLS = new Set([
15
- 'read', 'list', 'glob', 'grep', 'edit', 'write', 'patch', 'multiedit', 'apply_patch', 'todowrite', 'todoread',
16
- 'task', 'skill', 'lsp', 'codesearch', 'question', 'batch', 'invalid', 'plan_exit', 'plan_enter',
29
+ 'read', 'list', 'glob', 'grep', 'edit', 'write', 'patch', 'todo', 'task', 'skill', 'lsp', 'question',
30
+ 'invalid', 'plan_exit', 'plan_enter',
17
31
  ]);
18
32
 
19
33
  export default {
@@ -24,20 +38,21 @@ export default {
24
38
  let loadError = null;
25
39
  try { policy = await import(POLICY_URL); } catch (error) { loadError = error; }
26
40
  const cwd = (typeof options.cwd === 'string' && options.cwd) || context?.location?.directory || process.cwd();
41
+ const roots = Array.isArray(options.roots) ? options.roots.filter((path) => typeof path === 'string') : [];
27
42
  if (typeof policy?.browserToolDenial !== 'function') {
28
43
  const why = loadError?.message || 'browserToolDenial is missing';
29
- console.error(`[SynaBun] browser policy could not load from ${POLICY_URL} (${why}); refusing bash, webfetch, websearch and other servers' tools for this run`);
44
+ console.error(`[SynaBun] browser policy could not load from ${POLICY_URL} (${why}); refusing the shell, webfetch, websearch and other servers' tools for this run`);
30
45
  await context.tool.hook('execute.before', async (call) => {
31
46
  const raw = String(call?.tool || '');
32
47
  const tool = V1_NAMES[raw] || raw;
33
48
  if (/^synabun_/i.test(tool) || OWN_TOOLS.has(tool.toLowerCase())) return;
34
- throw new Error(`SynaBun browser policy: ${tool} was refused — SynaBun's browser policy could not load (${why}), so no command, web tool or other server's tool can be checked for a browser launch. SynaBun's own tools (SynaBun_*) and file tools still work. Stop and report this.`);
49
+ throw new Error(`SynaBun browser policy: ${raw} was refused — SynaBun's browser policy could not load (${why}), so no command, web tool or other server's tool can be checked for a browser launch. SynaBun's own tools (SynaBun_*) and file tools still work. Stop and report this.`);
35
50
  });
36
51
  return;
37
52
  }
38
53
  await context.tool.hook('execute.before', async (call) => {
39
54
  const raw = String(call?.tool || '');
40
- const reason = policy.browserToolDenial(V1_NAMES[raw] || raw, call?.input || {}, { host: 'opencode', cwd });
55
+ const reason = policy.browserToolDenial(V1_NAMES[raw] || raw, call?.input || {}, { host: 'opencode', cwd, roots });
41
56
  if (reason) throw new Error(reason);
42
57
  });
43
58
  },
@@ -1,18 +1,54 @@
1
- // SynaBun — the Assistant's route gate as an OpenCode 2 plugin.
2
- // The same gate as ../opencode-route-gate.js (read that file for what it does
3
- // and why), for OpenCode 2's plugin API: a directory OpenCode loads from the
4
- // config's `plugins` entry ({ package: <this folder as a file URL>, options }),
5
- // whose default export is `{ id, setup(context) }`; a hook on
6
- // `tool.execute.before` sees `{ tool, sessionID, input }` and refuses a call by
7
- // throwing (OpenCode records the message as that tool's error). OpenCode 2
8
- // calls two tools by other names than OpenCode 1 (`shell` for bash,
9
- // `subagent` for task): the gate is asked with OpenCode 1's names, which the
10
- // rest of SynaBun knows. OpenCode 1 never reads this folder (it loads the
11
- // `plugin` entry), and OpenCode 2 never loads the 1.x file.
1
+ // SynaBun — OpenCode plugin for the Assistant's route gate.
2
+ // Loaded only by the assistant brain's isolated `opencode serve` (its
3
+ // opencode.json's `plugins` entry, { package: <this folder as a file URL>,
4
+ // options }, written by patchOpenCodeAssistantConfig); the OpenCode sidepanel
5
+ // never loads it. OpenCode 2's plugin API: the default export is
6
+ // `{ id, setup(context) }`; the hook it registers with
7
+ // `context.tool.hook('execute.before', …)` sees `{ tool, sessionID, input }`
8
+ // and refuses a call by throwing (OpenCode records
9
+ // the message as that tool's error and the loop continues; only
10
+ // permission/question rejections stop a turn). Before every tool call it asks
11
+ // the Neural Interface whether the call may run (the route gate, and plan
12
+ // mode's read-only policy on every session of this serve). When the gate
13
+ // allows, the user's own OpenCode permission rules apply exactly as before.
14
+ // OpenCode calls two tools by other names than the rest of SynaBun knows
15
+ // (`shell` for bash, `subagent` for task): the gate is asked with those names.
16
+ // The call's whole arguments travel (the remote denylist reads paths from
17
+ // them), marked inputComplete; arguments too big to send go as their
18
+ // command/action only. No answer at all (the server unreachable or slow)
19
+ // allows the call — except with `remote` (a WhatsApp session's serve), where a
20
+ // call SynaBun could not check is refused. An answer that is not a decision
21
+ // (the check itself refused, such as 403 PEER_REFUSED, an error status, a body
22
+ // without `allow`) refuses the call in every session and says what SynaBun
23
+ // answered: a refusal never passes for an allow.
24
+ //
25
+ // Every request carries the session's pin (X-Synabun-Assistant): this plugin
26
+ // runs inside the brain's `opencode serve`, an AI agent's process tree, which
27
+ // SynaBun's owner gate (lib/owner-authority.js) refuses unless the call says
28
+ // it is an agent's own. The per-session `token` in the body is what the route
29
+ // itself checks.
30
+ //
31
+ // On load it says hello: until the Neural Interface hears it, it enforces the
32
+ // gate reactively (interrupting a turn after an unrouted tool started), so a
33
+ // plugin that never loads still leaves the brain gated. A WhatsApp session
34
+ // runs no turn at all until the hello arrives.
12
35
 
13
36
  const MAX_INPUT_BYTES = 256 * 1024;
14
37
  const UNCHECKED = 'SynaBun could not check this call, so a WhatsApp session does not run it.';
15
38
  const V1_NAMES = { shell: 'bash', subagent: 'task' };
39
+ const PIN_HEADER = 'x-synabun-assistant';
40
+
41
+ /** The session id as a header value (printable ASCII only: fetch refuses anything else before sending). */
42
+ function pinOf(session) {
43
+ return String(session).replace(/[^\x21-\x7e]/g, '').slice(0, 200);
44
+ }
45
+
46
+ /** The refusal of a call whose check got an answer that is not a decision: what SynaBun answered, in short. */
47
+ function notADecision(status, body) {
48
+ const code = body && typeof body === 'object' && typeof body.code === 'string' ? ` ${body.code.slice(0, 64)}` : '';
49
+ const said = body && typeof body === 'object' && typeof body.error === 'string' ? `: ${body.error.slice(0, 300)}` : '';
50
+ return `SynaBun could not check this call (its route gate answered ${status}${code}${said}), so it did not run.`;
51
+ }
16
52
 
17
53
  /** { input, inputComplete }: the whole arguments when small enough to send, else their command/action. */
18
54
  function callArguments(args) {
@@ -34,24 +70,28 @@ export default {
34
70
  if (!url || !session || !token) return;
35
71
  const post = (body) => fetch(url, {
36
72
  method: 'POST',
37
- headers: { 'content-type': 'application/json' },
73
+ headers: { 'content-type': 'application/json', [PIN_HEADER]: pinOf(session) },
38
74
  body: JSON.stringify({ session, token, host: 'opencode', ...body }),
39
75
  signal: AbortSignal.timeout(5000),
40
76
  });
41
- // Never blocks loading; a lost hello only keeps the reactive fallback on.
42
- post({ hello: true }).catch(() => {});
77
+ // Never blocks loading; a lost hello only keeps the reactive fallback on (a refused one is said in the serve's log).
78
+ post({ hello: true }).then((response) => {
79
+ if (!response.ok) console.error(`[SynaBun] the route gate refused this serve's hello (${response.status}); every tool call will be refused until it answers`);
80
+ }).catch(() => {});
43
81
  await context.tool.hook('execute.before', async (call) => {
44
82
  const name = String(call?.tool || '');
45
- let decision = null;
83
+ let response;
46
84
  try {
47
- const response = await post({ tool: V1_NAMES[name] || name, providerSessionId: call?.sessionID || null, ...callArguments(call?.input) });
48
- decision = await response.json();
85
+ response = await post({ tool: V1_NAMES[name] || name, providerSessionId: call?.sessionID || null, ...callArguments(call?.input) });
49
86
  } catch {
50
87
  if (remote === true) throw new Error(UNCHECKED);
51
88
  return;
52
89
  }
53
- if (decision && decision.allow === false) throw new Error(String(decision.reason || 'SynaBun route gate: call agent_route first.'));
54
- if (remote === true && !(decision && decision.allow === true)) throw new Error(UNCHECKED);
90
+ let decision = null;
91
+ try { decision = await response.json(); } catch { decision = null; }
92
+ if (response.ok && decision && decision.allow === true) return;
93
+ if (response.ok && decision && decision.allow === false) throw new Error(String(decision.reason || 'SynaBun route gate: call agent_route first.'));
94
+ throw new Error(notADecision(response.status, decision));
55
95
  });
56
96
  },
57
97
  };
@@ -27,7 +27,7 @@ import { dirname } from 'node:path';
27
27
  import { BUDGET_DEFAULT_USD, BUDGET_WARN_RATIO, roundUsd } from './assistant-budget.js';
28
28
  import {
29
29
  BUDGET_PROVIDERS, MIN_SPEND_USD, billingOf, bucketEligibility, budgetView, coverageOf, coversWork, divideLeft,
30
- dryProviders, fallbackWeights, jevWeights, listPriceOf, priceLabel, providerSpendOf, rowOf, totalSpentOf,
30
+ dryProviders, fallbackWeights, isUnpricedProvider, jevWeights, listPriceOf, priceLabel, providerSpendOf, rowOf, totalSpentOf,
31
31
  } from './assistant-budget-split.js';
32
32
 
33
33
  export const SPLIT_REASONS = Object.freeze(['first_route', 'share_used_up', 'brain_turn', 'budget_changed', 'run_ended']);
@@ -156,13 +156,16 @@ export function createBudgetPlanner({
156
156
  function jevInput(id, { value, used, cov, eligible, brainInfo, cat, needs, task, reason, tasks }) {
157
157
  const rows = safe(() => accounts(), null);
158
158
  const prefs = safe(() => routing()?.preferences, {}) || {};
159
- const savedRoutes = Object.entries(prefs).filter(([, pref]) => pref?.provider).map(([taskClass, pref]) => {
159
+ const savedRoutes = Object.entries(prefs).filter(([, pref]) => pref?.provider && !isUnpricedProvider(pref.provider)).map(([taskClass, pref]) => {
160
160
  const info = modelInfo(cat, pref.provider, pref.model || null, accountFor(pref.provider, rows, brainInfo));
161
161
  return { taskClass, provider: pref.provider, model: pref.model || null, effort: pref.effort || null, price: info.price, billing: info.billing };
162
162
  });
163
163
  const recent = safe(() => recentSpend(id), null) || {};
164
164
  const providers = {};
165
165
  for (const provider of BUDGET_PROVIDERS) {
166
+ // A provider no budget can hold (it reports no prices) is never asked about and never described: Jev's state
167
+ // stays the one its questions were tuned on (budget-judgments.ts).
168
+ if (isUnpricedProvider(provider)) continue;
166
169
  const own = providerSpendOf(used, provider);
167
170
  const account = accountFor(provider, rows, brainInfo);
168
171
  const routed = savedRoutes.filter((r) => r.provider === provider);
@@ -308,7 +311,8 @@ export function createBudgetPlanner({
308
311
  if (!id || !limited()) return true;
309
312
  let cov = coverage(id);
310
313
  if (coversWork(cov, { provider, forBrain, free })) return true;
311
- if (cov.leftUsd < MIN_SPEND_USD || free) return false;
314
+ // No split gives an unpriced provider money: never a re-split (or a Jev request) for one.
315
+ if (cov.leftUsd < MIN_SPEND_USD || free || isUnpricedProvider(provider)) return false;
312
316
  const key = `${id}:${provider}:${forBrain ? 'brain' : 'runs'}`;
313
317
  const tried = needTries.get(key);
314
318
  const value = readSetting();