synabun 2.0.10 → 2.1.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 (517) hide show
  1. package/CHANGELOG.md +86 -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 +2 -2
  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 +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  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 +228 -0
  24. package/lib/virustotal.mjs +192 -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/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -9,11 +9,11 @@
9
9
  // deps (all supplied by handleOpencodeV2Ws):
10
10
  // send(data) write one JSON message to this socket
11
11
  // shared the shared-serve client (opencode-v2-client).
12
- // SDK calls added for the panel are under
12
+ // Client calls added for the panel are under
13
13
  // `client.extra.<group>.<method>`
14
14
  // bound(sessionId) the serve currently hosting the session's
15
15
  // turn, else the shared client; never spawns
16
- // turn(sessionId, mcpProfile) the session's dedicated serve; may spawn it
16
+ // turn(sessionId, tools) the session's dedicated serve; may spawn it
17
17
  // proxy(method, path, body, timeoutMs, baseUrl) raw HTTP to a serve
18
18
  // baseUrlFor(sessionId) base URL of the serve hosting the session
19
19
  // directoryQuery(cwd) `?directory=…` or ''
@@ -31,19 +31,15 @@
31
31
  // a type only when that type is listed.
32
32
  // ─────────────────────────────────────────────────────────────────────────────
33
33
 
34
- import { readFileSync } from 'node:fs';
35
- import { dirname, join } from 'node:path';
36
- import { fileURLToPath } from 'node:url';
37
- import { compactOrSummarize, lastModelOf } from './opencode-v2-client.js';
38
- import { sessionTablesFor } from './opencode-db-schema.js';
39
-
40
- export { lastModelOf };
34
+ import { OPENCODE_SESSION_TABLES } from './opencode-db-schema.js';
35
+ import { opencodeMcpServerEntry } from './opencode-config.js';
36
+ import { storedToolList } from './tool-lists.js';
41
37
 
42
38
  // Types answered by the switch in server.js.
43
39
  export const OPENCODE_V2_WS_INLINE_TYPES = Object.freeze([
44
40
  'init', 'identify',
45
41
  'session:list', 'session:create', 'session:get', 'session:update', 'session:delete', 'session:messages',
46
- 'mcp:profile:get', 'mcp:profile:set',
42
+ 'mcp:tools:get', 'mcp:tools:set',
47
43
  'message:send', 'message:abort',
48
44
  'question:list', 'question:reject',
49
45
  ]);
@@ -71,6 +67,10 @@ const SESSION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
71
67
  */
72
68
  export function canonicalizeOpencodeV2Message(msg) {
73
69
  if (!msg || typeof msg !== 'object') return failure(400, 'malformed request');
70
+ if (msg.tools !== undefined && msg.tools !== null) {
71
+ try { msg.tools = storedToolList({ tools: msg.tools }); }
72
+ catch (error) { return failure(400, error.message, { code: error.code, problems: error.problems }); }
73
+ }
74
74
  const raw = msg.sessionId;
75
75
  if (raw === undefined || raw === null) {
76
76
  msg.sessionId = undefined;
@@ -134,18 +134,11 @@ function modelOf(value) {
134
134
  return providerID && modelID ? { providerID, modelID } : null;
135
135
  }
136
136
 
137
- // session.compact with the session.summarize fallback (see compactOrSummarize).
137
+ // OpenCode compacts a session on the model it runs on.
138
138
  async function compactSession(msg, deps) {
139
- const client = await deps.turn(sessionKeyOf(msg), msg.mcpProfile);
140
- try {
141
- return await compactOrSummarize(client, {
142
- sessionID: sessionIdOf(msg), directory: directoryOf(msg), model: modelOf(msg.model),
143
- });
144
- } catch (err) {
145
- // "Pick a model first" is an answer for the user, not a server error.
146
- if (err?.status === 400) return failure(400, err.message);
147
- throw err;
148
- }
139
+ const client = await deps.turn(sessionKeyOf(msg), msg.tools);
140
+ const r = await client.session.compact({ sessionID: sessionIdOf(msg), directory: directoryOf(msg) });
141
+ return { status: r?.status, data: r?.data };
149
142
  }
150
143
 
151
144
  const PERMISSION_REPLIES = new Set(['once', 'always', 'reject']);
@@ -230,45 +223,18 @@ async function sessionStatus(msg, deps) {
230
223
  return { status: r.status, data: { status: statusForSession(r.data, sessionIdOf(msg)) } };
231
224
  }
232
225
 
233
- // What the relay forwards to panels. OpenCode 1.18 mirrors every bus event as
234
- // a `sync` envelope ({ type: 'sync', syncEvent }); the panel acts on the bus
235
- // event itself, so the mirror is dropped. Everything else passes.
236
- export function shouldRelayOpencodeV2Message(data) {
237
- return !(data?.type === 'event' && data.eventType === 'sync');
238
- }
239
-
240
- // Body of `POST /mcp` for a server SynaBun describes as { command, args, env }
241
- // or { url, headers }. OpenCode takes McpLocalConfig (`type: 'local'`, the
242
- // command as one array, `environment`) or McpRemoteConfig; `type: 'stdio'` with
243
- // a string command is answered 400.
226
+ // An MCP server as OpenCode 2 takes it (`mcp.servers.<name>`, lib/opencode-config.js), from what SynaBun
227
+ // describes as { command, args, env } or { url, headers }: type local with the command as one array and its
228
+ // `environment`, or type remote. Every word goes through as it is: an argument may be empty or carry spaces on
229
+ // purpose, and what is registered must be what was typed.
244
230
  export function buildOpencodeRuntimeMcpConfig(config = {}) {
245
- if (config.type === 'local' || config.type === 'remote') return { ...config };
246
- if (typeof config.url === 'string' && config.url) {
247
- return {
248
- type: 'remote',
249
- url: config.url,
250
- ...(config.headers && typeof config.headers === 'object' ? { headers: config.headers } : {}),
251
- enabled: config.enabled !== false,
252
- };
253
- }
254
- // Every word goes through as it is: an argument may be empty or carry
255
- // spaces on purpose, and what is registered must be what was typed.
256
- const head = Array.isArray(config.command) ? config.command : [config.command];
257
- const command = [...head, ...(Array.isArray(config.args) ? config.args : [])]
258
- .filter((part) => typeof part === 'string');
259
- const environment = config.environment || config.env;
260
- return {
261
- type: 'local',
262
- command,
263
- ...(environment && typeof environment === 'object' ? { environment } : {}),
264
- enabled: config.enabled !== false,
265
- };
231
+ return opencodeMcpServerEntry(config);
266
232
  }
267
233
 
268
234
  // ── Session list (read straight from OpenCode's SQLite) ─────────────────────
269
- // The SDK's session.list only answers for the serve's own project; the panel
270
- // lists every project, so server.js reads the `session` table. The query and
271
- // the row mapping live here so they can be tested against a real table.
235
+ // The client's session.list only answers for the serve's own project; the panel
236
+ // lists every project, so server.js reads OpenCode's `session_v2` table. The
237
+ // query and the row mapping live here so they can be tested against a real table.
272
238
  export const SESSION_LIST_MAX = 500;
273
239
 
274
240
  /**
@@ -278,10 +244,8 @@ export const SESSION_LIST_MAX = 500;
278
244
  * search case-insensitive match on title, slug or directory
279
245
  * Un-archiving writes time_archived = 0, so "live" is NULL or 0.
280
246
  */
281
- // `flavor` 'v2' reads OpenCode 2's tables (session_v2, session_message; the
282
- // columns this query names are the same), anything else OpenCode 1's.
283
- export function buildSessionListQuery(options = {}, { flavor = 'v1' } = {}) {
284
- const tables = sessionTablesFor(flavor);
247
+ export function buildSessionListQuery(options = {}) {
248
+ const tables = OPENCODE_SESSION_TABLES;
285
249
  const where = [];
286
250
  const params = [];
287
251
  where.push(options.archived === true
@@ -297,7 +261,7 @@ export function buildSessionListQuery(options = {}, { flavor = 'v1' } = {}) {
297
261
  const requested = Number(options.limit);
298
262
  const limit = Number.isInteger(requested) && requested > 0 ? Math.min(requested, SESSION_LIST_MAX) : null;
299
263
  const sql = `
300
- SELECT s.id, s.title, s.directory, s.slug, s.project_id, s.parent_id, s.share_url,
264
+ SELECT s.id, s.title, s.directory, s.slug, s.project_id, s.parent_id,
301
265
  s.time_created, s.time_updated, s.time_archived,
302
266
  ${tables.messageCount} AS message_count
303
267
  FROM ${tables.session} s
@@ -321,20 +285,19 @@ export function mapSessionListRow(row) {
321
285
  time: { created: epochMs(row.time_created), updated: epochMs(row.time_updated) },
322
286
  };
323
287
  if (row.parent_id) out.parentID = row.parent_id;
324
- if (row.share_url) out.share = { url: row.share_url };
325
288
  const archived = epochMs(row.time_archived);
326
289
  if (archived) out.time.archived = archived;
327
290
  return out;
328
291
  }
329
292
 
330
- // ── Session lifecycle (revert, fork, share, children, delete message) ───────
293
+ // ── Session lifecycle (revert, fork, children) ──────────────────────────────
331
294
 
332
295
  const messageIdOf = (msg) => (typeof msg?.messageID === 'string' && msg.messageID.trim()
333
296
  ? msg.messageID.trim()
334
297
  : (typeof msg?.messageId === 'string' ? msg.messageId.trim() : ''));
335
298
 
336
- // Reverting or deleting under a running turn would pull the transcript out
337
- // from under the model.
299
+ // Reverting under a running turn would pull the transcript out from under the
300
+ // model.
338
301
  function refuseWhileRunning(msg, deps, what) {
339
302
  return deps.isTurnActive?.(sessionIdOf(msg))
340
303
  ? failure(409, `Stop the running turn before you ${what}.`)
@@ -372,48 +335,6 @@ async function sessionChildren(msg, deps) {
372
335
  return { status: r.status, data: Array.isArray(r.data) ? r.data : [] };
373
336
  }
374
337
 
375
- async function messageDelete(msg, deps) {
376
- const busy = refuseWhileRunning(msg, deps, 'delete a message');
377
- if (busy) return busy;
378
- const messageID = messageIdOf(msg);
379
- if (!messageID) return failure(400, 'messageID is required');
380
- const r = await deps.bound(sessionKeyOf(msg)).extra.session.deleteMessage({
381
- sessionID: sessionIdOf(msg), messageID, directory: directoryOf(msg),
382
- });
383
- return { status: r.status, data: r.data };
384
- }
385
-
386
- // OpenCode's `share` setting: manual (default), auto, or disabled.
387
- export function sharePolicyOf(config) {
388
- const value = config?.share;
389
- if (value === 'disabled' || value === 'auto' || value === 'manual') return value;
390
- return 'manual';
391
- }
392
-
393
- async function readSharePolicy(msg, deps) {
394
- const r = await deps.shared.extra.config.get({ directory: directoryOf(msg) });
395
- return sharePolicyOf(r.data);
396
- }
397
-
398
- async function sessionSharePolicy(msg, deps) {
399
- return { status: 200, data: { share: await readSharePolicy(msg, deps) } };
400
- }
401
-
402
- // Publishes the transcript at a public opencode URL. The panel confirms with
403
- // the user every time; the server refuses when the config forbids sharing.
404
- async function sessionShare(msg, deps) {
405
- if (await readSharePolicy(msg, deps) === 'disabled') {
406
- return failure(403, 'Sharing is disabled in the OpenCode config.');
407
- }
408
- const r = await deps.shared.extra.session.share({ sessionID: sessionIdOf(msg), directory: directoryOf(msg) });
409
- return { status: r.status, data: r.data };
410
- }
411
-
412
- async function sessionUnshare(msg, deps) {
413
- const r = await deps.shared.extra.session.unshare({ sessionID: sessionIdOf(msg), directory: directoryOf(msg) });
414
- return { status: r.status, data: r.data };
415
- }
416
-
417
338
  // ── Composer: slash commands, shell, file search ────────────────────────────
418
339
 
419
340
  /** What the slash menu needs of a Command; the template can be 16 KB and is not sent. */
@@ -442,10 +363,12 @@ function modelRef(value) {
442
363
 
443
364
  // Run something that occupies the session like a prompt does: on the
444
365
  // session's dedicated serve, inside the same turn bookkeeping as message:send.
445
- async function withTurn(msg, deps, run) {
366
+ async function withTurn(msg, deps, run, { needsMcp = true } = {}) {
446
367
  const scope = deps.turnScope(sessionIdOf(msg));
447
368
  try {
448
- const client = await deps.turn(sessionKeyOf(msg), msg.mcpProfile);
369
+ const client = await deps.turn(sessionKeyOf(msg), msg.tools);
370
+ if (needsMcp) await deps.waitForTools?.(client, sessionIdOf(msg), directoryOf(msg), scope.signal);
371
+ if (scope.signal?.aborted) throw Object.assign(new Error('OpenCode turn stopped before sending'), { name: 'AbortError', status: 499 });
449
372
  const r = await run(client, scope.signal ? { signal: scope.signal } : undefined);
450
373
  return { status: r.status, data: r.data };
451
374
  } finally {
@@ -483,7 +406,7 @@ async function sessionShell(msg, deps) {
483
406
  agent: typeof msg.agent === 'string' && msg.agent ? msg.agent : 'build',
484
407
  model: modelOf(msg.model) || undefined,
485
408
  directory: directoryOf(msg),
486
- }, options));
409
+ }, options), { needsMcp: false });
487
410
  }
488
411
 
489
412
  export const FIND_FILES_LIMIT = 50;
@@ -651,55 +574,6 @@ async function vcsDiff(msg, deps) {
651
574
  return { status: r.status, data: capFileDiffs(r.data) };
652
575
  }
653
576
 
654
- // ── Worktrees ───────────────────────────────────────────────────────────────
655
-
656
- // worktree.create answers a Worktree { name, branch, directory }; worktree.list
657
- // answers the directories only (confirmed on 1.18.34).
658
- function worktreeRow(w) {
659
- const directory = typeof w === 'string' ? w : (w && typeof w.directory === 'string' ? w.directory : '');
660
- if (!directory) return null;
661
- const leaf = directory.split(/[\\/]/).filter(Boolean).pop() || directory;
662
- return {
663
- name: typeof w === 'object' && w.name ? String(w.name) : leaf,
664
- branch: typeof w === 'object' && typeof w.branch === 'string' ? w.branch : '',
665
- directory,
666
- };
667
- }
668
-
669
- async function worktreeList(msg, deps) {
670
- if (!directoryOf(msg)) return failure(400, 'cwd is required');
671
- const r = await deps.shared.extra.worktree.list({ directory: directoryOf(msg) });
672
- return { status: r.status, data: (Array.isArray(r.data) ? r.data : []).map(worktreeRow).filter(Boolean) };
673
- }
674
-
675
- async function worktreeCreate(msg, deps) {
676
- if (!directoryOf(msg)) return failure(400, 'cwd is required');
677
- const name = typeof msg.name === 'string' ? msg.name.trim() : '';
678
- if (name && !/^[A-Za-z0-9][\w.-]{0,63}$/.test(name)) {
679
- return failure(400, 'A worktree name may use letters, digits, ".", "_" and "-".');
680
- }
681
- const r = await deps.shared.extra.worktree.create({
682
- directory: directoryOf(msg),
683
- worktreeCreateInput: name ? { name } : {},
684
- });
685
- const row = worktreeRow(r.data);
686
- if (!row) return failure(502, 'OpenCode did not return the new worktree.');
687
- return { status: r.status, data: row };
688
- }
689
-
690
- // Removing deletes a directory, so only one OpenCode itself lists as a worktree
691
- // of this project is accepted.
692
- async function worktreeRemove(msg, deps) {
693
- const cwd = directoryOf(msg);
694
- const target = typeof msg.directory === 'string' ? msg.directory.trim() : '';
695
- if (!cwd || !target) return failure(400, 'cwd and directory are required');
696
- const listed = await deps.shared.extra.worktree.list({ directory: cwd });
697
- const known = (Array.isArray(listed.data) ? listed.data : []).some((w) => worktreeRow(w)?.directory === target);
698
- if (!known) return failure(404, 'That directory is not a worktree of this project.');
699
- const r = await deps.shared.extra.worktree.remove({ directory: cwd, worktreeRemoveInput: { directory: target } });
700
- return { status: r.status, data: true };
701
- }
702
-
703
577
  // ── Environment: MCP servers, LSP, formatters ───────────────────────────────
704
578
 
705
579
  // SynaBun's own MCP entry is pinned by the runtime (profile, terminal identity,
@@ -744,19 +618,6 @@ async function mcpSwitch(msg, deps, connect) {
744
618
  return { status: r.status, data: after ? mcpRows(after.data) : [] };
745
619
  }
746
620
 
747
- // OAuth for a remote MCP server: OpenCode opens the browser itself and waits
748
- // for the callback, so the request can take minutes.
749
- async function mcpAuthenticate(msg, deps) {
750
- const name = mcpNameOf(msg);
751
- if (!name) return failure(400, 'MCP server name is required');
752
- if (isManagedMcpName(name)) return failure(403, `${name} is managed by SynaBun.`);
753
- const path = `/mcp/${encodeURIComponent(name)}/auth/authenticate${deps.directoryQuery(msg.cwd)}`;
754
- const r = await deps.proxy('POST', path, {}, 5 * 60 * 1000, deps.baseUrlFor(sessionKeyOf(msg)));
755
- if (r.status >= 400 || typeof r.data === 'string') {
756
- return failure(r.status >= 400 ? r.status : 502, proxyErrorText(r.data, 'Authentication did not complete'));
757
- }
758
- return { status: r.status, data: r.data };
759
- }
760
621
 
761
622
  const MCP_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
762
623
  const stringMap = (value) => value && typeof value === 'object' && !Array.isArray(value)
@@ -864,10 +725,6 @@ const HANDLERS = {
864
725
  'session:unrevert': { session: true, mutates: true, run: sessionUnrevert },
865
726
  'session:fork': { session: true, mutates: true, run: sessionFork },
866
727
  'session:children': { session: true, run: sessionChildren },
867
- 'session:share': { session: true, mutates: true, run: sessionShare },
868
- 'session:unshare': { session: true, mutates: true, run: sessionUnshare },
869
- 'session:share:policy': { run: sessionSharePolicy },
870
- 'message:delete': { session: true, mutates: true, run: messageDelete },
871
728
  'command:list': { run: commandList },
872
729
  'command:run': { session: true, mutates: true, run: commandRun },
873
730
  'session:shell': { session: true, mutates: true, run: sessionShell },
@@ -880,13 +737,9 @@ const HANDLERS = {
880
737
  'vcs:get': { run: vcsGet },
881
738
  'vcs:status': { run: vcsStatus },
882
739
  'vcs:diff': { run: vcsDiff },
883
- 'worktree:list': { run: worktreeList },
884
- 'worktree:create': { run: worktreeCreate },
885
- 'worktree:remove': { run: worktreeRemove },
886
740
  'mcp:status': { run: mcpStatus },
887
741
  'mcp:connect': { session: true, mutates: true, run: (msg, deps) => mcpSwitch(msg, deps, true) },
888
742
  'mcp:disconnect': { session: true, mutates: true, run: (msg, deps) => mcpSwitch(msg, deps, false) },
889
- 'mcp:authenticate': { session: true, mutates: true, run: mcpAuthenticate },
890
743
  'mcp:add': { session: true, mutates: true, run: mcpAdd },
891
744
  'env:status': { run: envStatus },
892
745
  };
@@ -902,42 +755,15 @@ export const OPENCODE_V2_WS_MODULE_TYPES = Object.freeze(Object.keys(HANDLERS));
902
755
  // Behaviours of an existing type that an older server lacks. Listed next to
903
756
  // the types so the panel can gate on them the same way; never a request type.
904
757
  export const OPENCODE_V2_WS_FEATURES = Object.freeze([
758
+ 'feature:tool-lists',
905
759
  'feature:permission-reply-message', // permission:reply forwards a rejection reason
906
- 'feature:compact-summarize', // session:compact falls back to session.summarize
907
760
  'feature:session-list-filters', // session:list takes search / archived / roots / limit, rows carry parentID
908
761
  'feature:mcp-add-persist', // mcp:add with persist:true saves the server without starting it a second time
909
762
  ]);
910
763
 
911
- // The version of the @opencode-ai/sdk this server talks to OpenCode through,
912
- // for the panel's Versions rows ('' when its package.json cannot be read).
913
- let _sdkVersion;
914
- export function opencodeSdkVersion() {
915
- if (_sdkVersion !== undefined) return _sdkVersion;
916
- _sdkVersion = '';
917
- try {
918
- let dir = dirname(fileURLToPath(import.meta.resolve('@opencode-ai/sdk/v2')));
919
- for (let i = 0; i < 6 && dir !== dirname(dir); i += 1, dir = dirname(dir)) {
920
- let pkg = null;
921
- try { pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')); } catch { /* not the package root */ }
922
- if (pkg?.name === '@opencode-ai/sdk') { _sdkVersion = String(pkg.version || ''); break; }
923
- }
924
- } catch { /* stays '' */ }
925
- return _sdkVersion;
926
- }
927
-
928
764
  // Everything this server answers, for the panel's feature gating.
929
- // What an OpenCode 2 serve has no counterpart for (lib/opencode2-client.js
930
- // answers them with "not available"): the panel hides them, as it hides a
931
- // feature an older SynaBun server lacks.
932
- export const OPENCODE2_UNSUPPORTED_TYPES = Object.freeze([
933
- 'session:share', 'session:unshare', 'session:share:policy', 'message:delete',
934
- 'worktree:create', 'worktree:remove', 'mcp:authenticate',
935
- ]);
936
-
937
- /** `protocol`: the OpenCode major the shared serve speaks (2 leaves out OPENCODE2_UNSUPPORTED_TYPES). */
938
- export function opencodeV2WsCapabilities({ protocol = null } = {}) {
939
- const all = [...OPENCODE_V2_WS_INLINE_TYPES, ...OPENCODE_V2_WS_MODULE_TYPES, ...OPENCODE_V2_WS_FEATURES];
940
- return protocol === 2 ? all.filter((type) => !OPENCODE2_UNSUPPORTED_TYPES.includes(type)) : all;
765
+ export function opencodeV2WsCapabilities() {
766
+ return [...OPENCODE_V2_WS_INLINE_TYPES, ...OPENCODE_V2_WS_MODULE_TYPES, ...OPENCODE_V2_WS_FEATURES];
941
767
  }
942
768
 
943
769
  export function ownsOpencodeV2Request(type) {
@@ -0,0 +1,27 @@
1
+ // Isolate the serve/config, not the provider's credential database. OpenCode 2
2
+ // keeps credentials and sessions together. Pin the original XDG stores so a
3
+ // restart cannot silently select another HOME/account or an empty database.
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+ import { isAbsolute, join } from 'node:path';
7
+
8
+ export function openCodeWorkerData(configRoot, { resume = false, env = process.env, home = homedir() } = {}) {
9
+ const file = join(configRoot, 'worker-data.json');
10
+ const keys = ['XDG_DATA_HOME', 'XDG_STATE_HOME', 'XDG_CACHE_HOME'];
11
+ if (resume || existsSync(file)) {
12
+ let saved;
13
+ try { saved = JSON.parse(readFileSync(file, 'utf8')); } catch { throw new Error('OpenCode durable session data descriptor is gone'); }
14
+ if (!keys.every(key => typeof saved[key] === 'string' && isAbsolute(saved[key]))) throw new Error('OpenCode durable session data descriptor is invalid');
15
+ if (!existsSync(join(saved.XDG_DATA_HOME, 'opencode', 'opencode.db'))) throw new Error('OpenCode durable session database is gone');
16
+ return Object.fromEntries(keys.map(key => [key, saved[key]]));
17
+ }
18
+ const saved = {
19
+ XDG_DATA_HOME: env.XDG_DATA_HOME || join(home, '.local', 'share'),
20
+ XDG_STATE_HOME: env.XDG_STATE_HOME || join(home, '.local', 'state'),
21
+ XDG_CACHE_HOME: env.XDG_CACHE_HOME || join(home, '.cache'),
22
+ };
23
+ if (!keys.every(key => isAbsolute(saved[key]))) throw new Error('OpenCode XDG directories must be absolute');
24
+ mkdirSync(configRoot, { recursive: true });
25
+ writeFileSync(file, JSON.stringify(saved) + '\n', { mode: 0o600 });
26
+ return saved;
27
+ }
@@ -1,19 +1,17 @@
1
1
  // ─────────────────────────────────────────────────────────────────────────────
2
- // OpenCode 2 client — the OpenCode 1 SDK's interface over OpenCode 2's API.
2
+ // OpenCode 2 client — the one client SynaBun speaks to `opencode serve` with.
3
3
  //
4
- // Every OpenCode client in SynaBun is built by lib/opencode-v2-client.js
5
- // (`connect`, `createClientInstance`) from the pinned @opencode-ai/sdk, which
6
- // speaks OpenCode 1. For an OpenCode 2 serve it builds this instead: an object
7
- // with the same groups and methods SynaBun calls on the SDK client
8
- // (`session.prompt`, `global.event`, `permission.reply`, …), the same
9
- // arguments, and the same `{ data, error, response }` results, in OpenCode 1's
10
- // shapes (lib/opencode2-translate.js). So the wrapper, and everything above it
11
- // (the OpenCode panel, the Assistant's OpenCode brain, workers, loops, the
12
- // usage meter), runs unchanged on either major.
4
+ // lib/opencode-v2-client.js (`connect`, `createClientInstance`) builds it for
5
+ // every serve. It has the groups and methods SynaBun calls (`session.prompt`,
6
+ // `global.event`, `permission.reply`, …) with the `{ data, error, response }`
7
+ // results SynaBun's consumers read, in the shapes they were written for
8
+ // (OpenCode 1's: lib/opencode2-translate.js). So the wrapper, and everything
9
+ // above it (the OpenCode panel, the Assistant's OpenCode brain, workers, loops,
10
+ // the usage meter), reads one set of shapes.
13
11
  //
14
- // What OpenCode 2 needs that OpenCode 1 did not:
12
+ // What OpenCode 2's API needs:
15
13
  // • HTTP Basic auth (user `opencode`, the launch's password) on every call;
16
- // • the project folder as `location[directory]` (OpenCode 1: `directory`);
14
+ // • the project folder as `location[directory]`;
17
15
  // • a prompt carries no model, agent or system prompt: the session is switched
18
16
  // to the model and agent first (only when they change), and a system prompt
19
17
  // is the session's instruction entry `synabun-system`;
@@ -22,15 +20,18 @@
22
20
  // `session.command` (a slash command only queues its prompt);
23
21
  // • a permission or a question is answered under its session, which the
24
22
  // event translator remembers (or the pending lists say).
25
- // Things OpenCode 2 does not have answer like an older serve would: an empty
26
- // list (todos, LSP, formatters, symbols) or an error that says so (share).
23
+ // Things OpenCode 2 does not have answer with an empty list (todos, LSP,
24
+ // formatters, symbols) or an error that says so (share).
27
25
  // ─────────────────────────────────────────────────────────────────────────────
28
26
 
29
27
  import { basicAuthHeader } from './opencode-protocol.js';
28
+ import { opencodeMcpServerEntry } from './opencode-config.js';
30
29
  import {
31
30
  answersToForm,
32
31
  createEventTranslator,
33
32
  formToQuestionV1,
33
+ integrationEnvNames,
34
+ integrationsToAuthMethods,
34
35
  messagesToV1,
35
36
  modelRefToV2,
36
37
  permissionToV1,
@@ -119,13 +120,15 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
119
120
 
120
121
  /** `{ data }` with `data` mapped, errors passed through. */
121
122
  const map = (result, fn) => (result.error ? result : { ...result, data: fn(result.data) });
123
+ /** The rows of an /api/integration answer (none when it failed: a provider list does not wait on it). */
124
+ const integrationRows = (result) => (result && !result.error && Array.isArray(result.data?.data) ? result.data.data : []);
122
125
  const ok = (result, data) => (result.error ? result : { ...result, data });
123
126
 
124
127
  // ── Sessions ──
125
128
 
126
129
  const noteAll = (rows) => rows.map((info) => translator.noteSession(info));
127
130
 
128
- async function listSessions({ directory, roots, search, limit, parentID } = {}) {
131
+ async function listSessions({ directory, roots, search, limit, parentID, archived } = {}) {
129
132
  const max = Number(limit) > 0 ? Number(limit) : Infinity;
130
133
  const out = [];
131
134
  let cursor = null;
@@ -140,6 +143,8 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
140
143
  limit: String(Math.min(PAGE_LIMIT, Number.isFinite(max) ? max - out.length : PAGE_LIMIT)),
141
144
  order: 'desc',
142
145
  ...(parentID ? { parentID } : (roots === true || roots === 'true' ? { parentID: 'null' } : {})),
146
+ // Archived sessions only (OpenCode lists live ones by default).
147
+ ...(archived === true ? { archived: 'true' } : {}),
143
148
  };
144
149
  last = await call('GET', '/api/session', { query });
145
150
  if (last.error) return last;
@@ -452,26 +457,40 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
452
457
  return out;
453
458
  }
454
459
 
455
- /** OpenCode 1's provider list from OpenCode 2's providers, enabled models and default model. */
460
+ /**
461
+ * The provider list SynaBun reads (`{ all, connected, defaults }`) from OpenCode 2's providers, enabled models,
462
+ * default model and integrations: every integration a provider can be connected through is in `all` (with the
463
+ * variables it reads its key from), the ones with models are `connected`.
464
+ */
456
465
  async function providerCatalog(directory) {
457
- const [providers, models, fallback] = await Promise.all([
466
+ const [providers, models, fallback, integrations] = await Promise.all([
458
467
  call('GET', '/api/provider', { query: located(directory) }),
459
468
  call('GET', '/api/model', { query: located(directory) }),
460
469
  call('GET', '/api/model/default', { query: located(directory) }),
470
+ call('GET', '/api/integration', { query: located(directory) }),
461
471
  ]);
462
472
  if (providers.error) return { error: providers.error, response: providers.response };
463
473
  if (models.error) return { error: models.error, response: models.response };
474
+ const envOf = new Map();
475
+ for (const integration of integrationRows(integrations)) {
476
+ envOf.set(str(integration.id), integrationEnvNames(integration));
477
+ }
464
478
  const byProvider = new Map();
465
479
  for (const provider of Array.isArray(providers.data?.data) ? providers.data.data : []) {
466
480
  byProvider.set(provider.id, {
467
481
  id: str(provider.id),
468
482
  name: str(provider.name || provider.id),
469
483
  source: provider.activation === 'enabled' ? 'config' : 'api',
470
- env: [],
484
+ env: envOf.get(str(provider.integrationID || provider.id)) || [],
471
485
  options: {},
472
486
  models: {},
473
487
  });
474
488
  }
489
+ for (const integration of integrationRows(integrations)) {
490
+ const id = str(integration.id);
491
+ if (!id || byProvider.has(id)) continue;
492
+ byProvider.set(id, { id, name: str(integration.name || id), source: 'api', env: envOf.get(id) || [], options: {}, models: {} });
493
+ }
475
494
  for (const model of Array.isArray(models.data?.data) ? models.data.data : []) {
476
495
  if (!model?.id || !model.providerID || model.enabled === false) continue;
477
496
  let provider = byProvider.get(model.providerID);
@@ -524,16 +543,24 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
524
543
  },
525
544
  get: ({ sessionID } = {}) => getSession(sessionID),
526
545
  update: async ({ sessionID, title, permission, metadata, time } = {}) => {
527
- // OpenCode 2's update takes a title, metadata and permissions: archiving is not in its API.
528
- if (time && typeof time === 'object' && 'archived' in time) return notSupported('Archiving a session');
529
546
  const body = {};
530
547
  if (typeof title === 'string') body.title = title;
531
548
  if (metadata && typeof metadata === 'object') body.metadata = metadata;
532
549
  const rules = rulesToV2(permission);
533
550
  if (rules) body.permissions = rules;
551
+ // Archiving: `archived: true | false` (OpenCode 1's `time.archived`, a time or 0 to bring a session back).
552
+ // 2.1.4 does it; 2.0.32 answers the same request 204 and keeps the session as it was, so the session is read
553
+ // back and an archive that did not happen is refused with a sentence, never a silent success.
554
+ const archive = time && typeof time === 'object' && 'archived' in time ? Boolean(Number(time.archived)) : null;
555
+ if (archive !== null) body.archived = archive;
534
556
  const patched = await call('PATCH', `/api/session/${encodeURIComponent(sessionID)}`, { body });
535
557
  if (patched.error) return patched;
536
- return getSession(sessionID);
558
+ const read = await getSession(sessionID);
559
+ if (archive !== null && !read.error && Boolean(read.data?.time?.archived) !== archive) {
560
+ const error = { _tag: 'NotSupportedError', message: `This OpenCode answered without ${archive ? 'archiving' : 'unarchiving'} the session: it needs a newer OpenCode 2 (run: opencode upgrade).` };
561
+ return { data: undefined, error, response: { status: 501, ok: false } };
562
+ }
563
+ return read;
537
564
  },
538
565
  delete: async ({ sessionID } = {}) => {
539
566
  const removed = await call('DELETE', `/api/session/${encodeURIComponent(sessionID)}`);
@@ -573,8 +600,6 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
573
600
  if (cleared.error) return cleared;
574
601
  return getSession(sessionID);
575
602
  },
576
- share: async () => notSupported('Sharing a session'),
577
- unshare: async () => notSupported('Sharing a session'),
578
603
  summarize: async ({ sessionID } = {}) => ok(await call('POST', `/api/session/${encodeURIComponent(sessionID)}/compact`, { body: {} }), true),
579
604
  init: async ({ sessionID } = {}) => ok(await call('POST', `/api/session/${encodeURIComponent(sessionID)}/command`, { body: { name: 'init', text: '' } }), true),
580
605
  command: (p, o) => runCommand(p, o),
@@ -583,11 +608,9 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
583
608
  if (failed) return failed;
584
609
  return ok(await call('POST', `/api/session/${encodeURIComponent(sessionID)}/shell`, { body: { command: String(command || '') } }), { info: null, parts: [] });
585
610
  },
586
- deleteMessage: async () => notSupported('Deleting one message'),
587
611
  };
588
612
 
589
613
  const client = {
590
- protocol: 2,
591
614
  global: {
592
615
  event: globalEvent,
593
616
  health: async () => map(await call('GET', '/api/info'), (body) => ({ healthy: true, version: str(body?.version) })),
@@ -645,11 +668,8 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
645
668
  connect: async ({ name, directory } = {}) => ok(await call('POST', `/api/experimental/mcp/${encodeURIComponent(name)}/connect`, { query: located(directory) }), true),
646
669
  disconnect: async ({ name, directory } = {}) => ok(await call('POST', `/api/experimental/mcp/${encodeURIComponent(name)}/disconnect`, { query: located(directory) }), true),
647
670
  add: async ({ name, config, directory } = {}) => {
648
- const c = obj(config);
649
- const server = c.url || c.type === 'remote'
650
- ? { type: 'remote', url: str(c.url), ...(c.headers ? { headers: obj(c.headers) } : {}), ...(c.enabled === false ? { disabled: true } : {}) }
651
- : { type: 'local', command: Array.isArray(c.command) ? c.command.map(str) : [str(c.command), ...(Array.isArray(c.args) ? c.args.map(str) : [])].filter(Boolean), ...(c.environment || c.env ? { environment: obj(c.environment || c.env) } : {}), ...(c.enabled === false ? { disabled: true } : {}) };
652
- if (Number(c.timeout) > 0) server.timeout = { startup: Number(c.timeout), catalog: Number(c.timeout), execution: Number(c.timeout) };
671
+ // OpenCode 2's MCP server entry, whatever SynaBun described (lib/opencode-config.js).
672
+ const server = opencodeMcpServerEntry(obj(config));
653
673
  const added = await call('PUT', `/api/experimental/mcp/${encodeURIComponent(name)}`, { query: located(directory), body: { config: server } });
654
674
  if (added.error) return added;
655
675
  return client.mcp.status({ directory });
@@ -703,7 +723,29 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
703
723
  if (catalog.error) return { data: undefined, error: catalog.error, response: catalog.response };
704
724
  return { data: { all: catalog.all, default: catalog.defaults, connected: catalog.connected }, error: undefined, response: catalog.response };
705
725
  },
706
- auth: async () => ({ data: {}, error: undefined, response: { status: 200, ok: true } }),
726
+ // How each provider is connected (OpenCode 1's shape: { [id]: [{ type: 'api' | 'oauth', label, id }] }).
727
+ auth: async ({ directory } = {}) => map(await call('GET', '/api/integration', { query: located(directory) }), (body) => integrationsToAuthMethods(Array.isArray(body?.data) ? body.data : [])),
728
+ },
729
+ // OpenCode 2's integrations (how a provider is connected) and the credentials they stored. Secrets never come
730
+ // back from here: an integration's `connections` name a credential, never its value.
731
+ integration: {
732
+ list: async ({ directory } = {}) => map(await call('GET', '/api/integration', { query: located(directory) }), (body) => (Array.isArray(body?.data) ? body.data : [])),
733
+ connectKey: async ({ integrationID, key, label, answer, directory } = {}) => ok(await call('POST', `/api/integration/${encodeURIComponent(integrationID)}/connect/key`, {
734
+ query: located(directory),
735
+ body: { key: str(key), ...(label ? { label: str(label) } : {}), ...(answer && typeof answer === 'object' ? { answer } : {}) },
736
+ }), true),
737
+ connectOAuth: async ({ integrationID, methodID, answer, directory } = {}) => map(await call('POST', `/api/integration/${encodeURIComponent(integrationID)}/connect/oauth`, {
738
+ query: located(directory),
739
+ body: { methodID: str(methodID), ...(answer && typeof answer === 'object' ? { answer } : {}) },
740
+ }), (body) => obj(body?.data)),
741
+ oauthStatus: async ({ integrationID, attemptID, directory } = {}) => map(await call('GET', `/api/integration/${encodeURIComponent(integrationID)}/connect/oauth/${encodeURIComponent(attemptID)}`, { query: located(directory) }), (body) => obj(body?.data)),
742
+ oauthComplete: async ({ integrationID, attemptID, code, directory } = {}) => ok(await call('POST', `/api/integration/${encodeURIComponent(integrationID)}/connect/oauth/${encodeURIComponent(attemptID)}/complete`, {
743
+ query: located(directory),
744
+ body: { code: code == null ? null : str(code) },
745
+ }), true),
746
+ },
747
+ credential: {
748
+ remove: async ({ credentialID } = {}) => ok(await call('DELETE', `/api/credential/${encodeURIComponent(credentialID)}`), true),
707
749
  },
708
750
  find: {
709
751
  files: async ({ query, directory, limit, type } = {}) => map(await call('GET', '/api/fs/find', { query: located(directory, { query: str(query), ...(type ? { type } : { type: 'file' }), ...(limit ? { limit: String(limit) } : {}) }) }), (body) => (Array.isArray(body?.data) ? body.data : []).map((entry) => str(entry?.path || entry?.name || entry))),
@@ -734,12 +776,6 @@ export function createOpencode2Client({ baseUrl, password, fetchImpl = globalThi
734
776
  // default branch) its 'branch'. OpenCode 2 refuses a diff that names no mode.
735
777
  diff: async ({ directory, mode } = {}) => map(await call('GET', '/api/vcs/diff', { query: located(directory, { mode: vcsModeToV2(mode) }) }), (body) => (Array.isArray(body?.data) ? body.data : []).map((row) => ({ file: str(row.file), patch: str(row.patch), additions: Number(row.additions) || 0, deletions: Number(row.deletions) || 0, status: row.status }))),
736
778
  },
737
- worktree: {
738
- list: async ({ directory } = {}) => map(await call('GET', '/api/worktree', { query: located(directory) }), (body) => (Array.isArray(body?.data) ? body.data : []).map((row) => str(row?.directory || row))),
739
- create: async () => notSupported('Creating a worktree from SynaBun'),
740
- remove: async () => notSupported('Removing a worktree from SynaBun'),
741
- reset: async () => notSupported('Resetting a worktree from SynaBun'),
742
- },
743
779
  lsp: { status: async () => ({ data: [], error: undefined, response: { status: 200, ok: true } }) },
744
780
  formatter: { status: async () => ({ data: [], error: undefined, response: { status: 200, ok: true } }) },
745
781
  path: {