synabun 2.0.9 → 2.0.10

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 (252) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/THIRD-PARTY-LICENSES.md +46 -4
  4. package/launcher.mjs +9 -3
  5. package/lib/start-launcher.js +6 -1
  6. package/mcp-server/package-lock.json +2 -2
  7. package/mcp-server/package.json +1 -1
  8. package/neural-interface/i18n/en.json +218 -7
  9. package/neural-interface/i18n/pt-BR.json +220 -9
  10. package/neural-interface/lib/account-usage.js +5 -3
  11. package/neural-interface/lib/app-controls-api.js +161 -0
  12. package/neural-interface/lib/assistant-api.js +21 -7
  13. package/neural-interface/lib/assistant-brains/opencode.js +13 -1
  14. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
  15. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
  16. package/neural-interface/lib/assistant-catalog.js +17 -2
  17. package/neural-interface/lib/assistant-dispatch.js +3 -0
  18. package/neural-interface/lib/assistant-envelope.js +19 -1
  19. package/neural-interface/lib/assistant-runtime.js +127 -8
  20. package/neural-interface/lib/claude-model-catalog.js +64 -14
  21. package/neural-interface/lib/codex-settings-schema.js +109 -47
  22. package/neural-interface/lib/editor-completion-opencode.js +132 -35
  23. package/neural-interface/lib/exec-loop-command.js +8 -1
  24. package/neural-interface/lib/launch-cache.js +293 -0
  25. package/neural-interface/lib/login-start.js +524 -0
  26. package/neural-interface/lib/native-loop-providers.js +18 -3
  27. package/neural-interface/lib/native-loop-runtime.js +19 -0
  28. package/neural-interface/lib/opencode-db-schema.js +75 -0
  29. package/neural-interface/lib/opencode-history.js +16 -1
  30. package/neural-interface/lib/opencode-protocol.js +173 -0
  31. package/neural-interface/lib/opencode-provider-view.js +81 -0
  32. package/neural-interface/lib/opencode-runtime-config.js +10 -3
  33. package/neural-interface/lib/opencode-serve-records.js +606 -0
  34. package/neural-interface/lib/opencode-v2-client.js +33 -6
  35. package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
  36. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  37. package/neural-interface/lib/opencode2-client.js +760 -0
  38. package/neural-interface/lib/opencode2-proxy.js +116 -0
  39. package/neural-interface/lib/opencode2-translate.js +1008 -0
  40. package/neural-interface/lib/process-identity.js +361 -0
  41. package/neural-interface/lib/session-cache-builder.js +26 -5
  42. package/neural-interface/lib/session-title-generator.js +18 -0
  43. package/neural-interface/lib/style-guide/api.js +4 -3
  44. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  45. package/neural-interface/package-lock.json +1343 -45
  46. package/neural-interface/package.json +5 -1
  47. package/neural-interface/public/index.html +2 -2
  48. package/neural-interface/public/shared/agent-runtime-options.js +55 -11
  49. package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
  50. package/neural-interface/public/shared/assistant/asst-api.js +11 -3
  51. package/neural-interface/public/shared/assistant/asst-brain-picker.js +195 -80
  52. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  53. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  54. package/neural-interface/public/shared/assistant/asst-menu.js +77 -6
  55. package/neural-interface/public/shared/assistant/asst-panel.js +197 -33
  56. package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
  57. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  58. package/neural-interface/public/shared/assistant/asst-render.js +382 -36
  59. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  60. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  61. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  62. package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
  63. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  64. package/neural-interface/public/shared/assistant/asst-state.js +14 -0
  65. package/neural-interface/public/shared/assistant/asst-styles.js +43 -0
  66. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
  67. package/neural-interface/public/shared/cc-dropdown.js +480 -0
  68. package/neural-interface/public/shared/cdx/cdx-panel.js +13 -1
  69. package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
  70. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  71. package/neural-interface/public/shared/cdx/cdx-tabs.js +149 -35
  72. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  73. package/neural-interface/public/shared/data-cache.js +223 -0
  74. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  75. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  76. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  77. package/neural-interface/public/shared/desktop-update-view.js +15 -5
  78. package/neural-interface/public/shared/file-links.js +73 -8
  79. package/neural-interface/public/shared/html-shell.js +74 -80
  80. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  81. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  82. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +7 -2
  83. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  84. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +93 -2
  85. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
  86. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  87. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  88. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
  89. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  90. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
  91. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
  92. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -19
  93. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
  94. package/neural-interface/public/shared/opencode-providers.js +39 -0
  95. package/neural-interface/public/shared/schedules-view.js +318 -0
  96. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  97. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  98. package/neural-interface/public/shared/settings/settings-data.js +88 -0
  99. package/neural-interface/public/shared/settings/settings-ia.js +17 -0
  100. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  101. package/neural-interface/public/shared/styles.css +540 -371
  102. package/neural-interface/public/shared/ui-assistant.js +27 -5
  103. package/neural-interface/public/shared/ui-automation-studio.js +67 -27
  104. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  105. package/neural-interface/public/shared/ui-claude-panel.js +76 -8
  106. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  107. package/neural-interface/public/shared/ui-detail.js +6 -6
  108. package/neural-interface/public/shared/ui-file-links.js +1 -0
  109. package/neural-interface/public/shared/ui-image-gallery.js +6 -5
  110. package/neural-interface/public/shared/ui-invite.js +18 -2
  111. package/neural-interface/public/shared/ui-link.js +9 -9
  112. package/neural-interface/public/shared/ui-menubar.js +232 -33
  113. package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
  114. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  115. package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
  116. package/neural-interface/public/shared/ui-sessions.js +15 -4
  117. package/neural-interface/public/shared/ui-settings.js +87 -36
  118. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  119. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  120. package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
  121. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  122. package/neural-interface/public/shared/ui-skills.js +70 -12
  123. package/neural-interface/public/shared/ui-sync.js +7 -0
  124. package/neural-interface/public/shared/ui-terminal.js +36 -20
  125. package/neural-interface/public/shared/ui-trash.js +8 -1
  126. package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
  127. package/neural-interface/public/shared/ui-tutorial.js +15 -5
  128. package/neural-interface/public/shared/ui-update.js +83 -14
  129. package/neural-interface/public/shared/ui-whiteboard.js +2 -1
  130. package/neural-interface/public/variant/2d/main.js +4 -1
  131. package/neural-interface/public/variant/3d/main.js +3 -0
  132. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  133. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  134. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  135. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  136. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  137. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  138. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  139. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  140. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  141. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  142. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  143. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  144. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  145. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  146. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  147. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  148. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  149. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  150. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  151. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  152. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  153. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  154. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  155. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  156. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  157. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  158. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  159. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  160. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  161. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  162. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  163. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  164. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  165. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  166. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  167. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  168. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  169. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  170. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  171. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  172. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  173. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  174. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  175. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  176. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  177. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  178. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  179. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  180. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  181. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  182. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  183. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  184. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  185. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  186. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  187. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  188. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  189. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  190. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  191. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  192. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  193. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  194. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  195. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  196. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  197. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  198. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  199. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  200. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  201. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  202. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  203. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  204. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  205. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  206. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  207. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  208. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  209. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  210. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  211. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  212. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  213. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  214. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  215. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  216. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  217. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  218. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  219. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  220. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  221. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  222. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  223. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  224. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  225. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  226. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  227. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  228. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  229. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  230. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  231. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  232. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  233. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  234. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  235. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  236. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  237. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  238. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  239. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  240. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  241. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  242. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  243. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  244. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  245. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  246. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  247. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  248. package/neural-interface/scripts/build-vendor.mjs +84 -3
  249. package/neural-interface/server.js +1099 -286
  250. package/npm-shrinkwrap.json +2 -2
  251. package/package.json +1 -1
  252. package/skills/synabun/modules/schedule.md +1 -1
@@ -1,12 +1,66 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
1
+ import { execFile } from 'node:child_process';
2
+ import { mkdtemp, readFile, rm, stat } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
  // The probes run no command: always the Windows sandbox that needs no administrator (lib/agent-sandbox-elevation.js).
6
6
  import { CODEX_PROBE_SANDBOX_ARGS } from './agent-sandbox-elevation.js';
7
7
 
8
+ // The probes start Codex itself (a 300 MB binary on Windows) once per setting value: asynchronous, never
9
+ // more than two at a time, one run per binary for every caller, and kept per binary identity (path, mtime,
10
+ // size: an update is another binary) in memory and, through setCodexSettingsStore(), across restarts.
8
11
  const cache = new Map();
9
12
  const featureCache = new Map();
13
+ const inflight = new Map();
14
+ const latestSchema = new Map(); // codexBin → the schema of the binary as it was last read
15
+ let store = null;
16
+ const MAX_PROBES = 2;
17
+ let running = 0;
18
+ const waiting = [];
19
+
20
+ /** Where answers are kept across restarts: `{ get(key) → value | undefined, set(key, value) }` (lib/launch-cache.js). */
21
+ export function setCodexSettingsStore(next) {
22
+ store = next && typeof next.get === 'function' && typeof next.set === 'function' ? next : null;
23
+ }
24
+
25
+ function limited(job) {
26
+ return new Promise((resolve, reject) => {
27
+ waiting.push({ job, resolve, reject });
28
+ const pump = () => {
29
+ while (running < MAX_PROBES && waiting.length) {
30
+ const next = waiting.shift();
31
+ running += 1;
32
+ Promise.resolve().then(next.job).then(next.resolve, next.reject).finally(() => { running -= 1; pump(); });
33
+ }
34
+ };
35
+ pump();
36
+ });
37
+ }
38
+
39
+ // One Codex run: { status, stdout } (status null when it could not start or ran out of time).
40
+ function probe(codexBin, args, { env, timeout }) {
41
+ return limited(() => new Promise((resolve) => {
42
+ execFile(codexBin, args, { env, encoding: 'utf8', timeout, windowsHide: true, maxBuffer: 16 * 1024 * 1024 }, (error, stdout) => {
43
+ resolve({ status: error ? (typeof error.code === 'number' ? error.code : null) : 0, stdout: String(stdout || '') });
44
+ });
45
+ }));
46
+ }
47
+
48
+ // What was found for `key` before (this run, or the last one through the store), or undefined.
49
+ function known(memo, key) {
50
+ if (memo.has(key)) return memo.get(key);
51
+ try {
52
+ const kept = store?.get(key);
53
+ if (kept && typeof kept === 'object') { memo.set(key, kept); return kept; }
54
+ } catch { /* nothing kept */ }
55
+ return undefined;
56
+ }
57
+
58
+ function once(key, run) {
59
+ if (inflight.has(key)) return inflight.get(key);
60
+ const job = Promise.resolve().then(run).finally(() => inflight.delete(key));
61
+ inflight.set(key, job);
62
+ return job;
63
+ }
10
64
  export const CODEX_SETTINGS_KEYS = Object.freeze([
11
65
  'model', 'review_model', 'model_provider', 'approval_policy', 'sandbox_mode', 'personality',
12
66
  'service_tier', 'model_reasoning_effort', 'model_reasoning_summary', 'model_verbosity',
@@ -40,33 +94,38 @@ export function settingsSchemaFromProtocol(configSchema, threadSchema) {
40
94
  }
41
95
 
42
96
  /** Generate from the executable we actually launch; never use the global home. */
43
- export function readCodexSettingsSchema(codexBin) {
44
- const stamp = statSync(codexBin);
45
- const key = `${codexBin}:${stamp.mtimeMs}:${stamp.size}`;
46
- if (cache.has(key)) return cache.get(key);
47
- const home = mkdtempSync(join(tmpdir(), 'synabun-codex-schema-'));
48
- try {
49
- const out = join(home, 'schema');
50
- const run = spawnSync(codexBin, ['app-server', 'generate-json-schema', '--experimental', '--out', out, ...CODEX_PROBE_SANDBOX_ARGS], {
51
- env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 15000, windowsHide: true,
52
- });
53
- if (run.error || run.status !== 0) throw new Error('Codex could not report its settings schema');
54
- const read = name => JSON.parse(readFileSync(join(out, 'v2', `${name}.json`), 'utf8'));
55
- const schema = settingsSchemaFromProtocol(read('ConfigReadResponse'), read('ThreadStartParams'));
56
- // Protocol enums can retain deprecated variants that the config loader
57
- // already rejects (0.162 still exports `untrusted`). Ask the loader too.
58
- // `features list` validates config locally and never runs a model or tool.
59
- for (const [fieldKey, field] of Object.entries(schema.fields)) {
60
- field.values = field.values.filter(value => {
61
- const check = spawnSync(codexBin, [...CODEX_PROBE_SANDBOX_ARGS, '-c', `${fieldKey}=${JSON.stringify(value)}`, 'features', 'list'], {
62
- env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 5000, windowsHide: true,
63
- });
64
- return !check.error && check.status === 0;
65
- });
66
- }
67
- cache.set(key, schema);
68
- return schema;
69
- } finally { rmSync(home, { recursive: true, force: true }); }
97
+ export async function readCodexSettingsSchema(codexBin) {
98
+ const stamp = await stat(codexBin);
99
+ const key = `schema:${codexBin}:${stamp.mtimeMs}:${stamp.size}`;
100
+ const found = known(cache, key);
101
+ if (found) { latestSchema.set(codexBin, found); return found; }
102
+ return once(key, async () => {
103
+ const home = await mkdtemp(join(tmpdir(), 'synabun-codex-schema-'));
104
+ try {
105
+ const env = { ...process.env, CODEX_HOME: home };
106
+ const out = join(home, 'schema');
107
+ const run = await probe(codexBin, ['app-server', 'generate-json-schema', '--experimental', '--out', out, ...CODEX_PROBE_SANDBOX_ARGS], { env, timeout: 15000 });
108
+ if (run.status !== 0) throw new Error('Codex could not report its settings schema');
109
+ const read = async name => JSON.parse(await readFile(join(out, 'v2', `${name}.json`), 'utf8'));
110
+ const schema = settingsSchemaFromProtocol(await read('ConfigReadResponse'), await read('ThreadStartParams'));
111
+ // Protocol enums can retain deprecated variants that the config loader
112
+ // already rejects (0.162 still exports `untrusted`). Ask the loader too.
113
+ // `features list` validates config locally and never runs a model or tool.
114
+ for (const [fieldKey, field] of Object.entries(schema.fields)) {
115
+ const checks = await Promise.all(field.values.map(value => probe(codexBin, [...CODEX_PROBE_SANDBOX_ARGS, '-c', `${fieldKey}=${JSON.stringify(value)}`, 'features', 'list'], { env, timeout: 5000 })));
116
+ field.values = field.values.filter((_, index) => checks[index].status === 0);
117
+ }
118
+ cache.set(key, schema);
119
+ latestSchema.set(codexBin, schema);
120
+ try { store?.set(key, schema); } catch { /* kept in memory */ }
121
+ return schema;
122
+ } finally { await rm(home, { recursive: true, force: true }).catch(() => {}); }
123
+ });
124
+ }
125
+
126
+ /** The schema readCodexSettingsSchema() last gave for this binary, or null: never starts Codex. */
127
+ export function cachedCodexSettingsSchema(codexBin) {
128
+ return latestSchema.get(codexBin) || null;
70
129
  }
71
130
 
72
131
  export function validateCodexSettings(config, schema, features = []) {
@@ -99,24 +158,27 @@ export function validateCodexSettings(config, schema, features = []) {
99
158
 
100
159
  /** Some stable flags are now mandatory and ignore false (e.g. unified_exec).
101
160
  * Check both states with the local loader instead of offering a no-op switch. */
102
- export function readCodexFeatureSupport(codexBin, features) {
161
+ export async function readCodexFeatureSupport(codexBin, features) {
103
162
  const names = features.filter(f => ['stable', 'beta'].includes(f.stage) && /^[A-Za-z0-9_-]+$/.test(f.name)).map(f => f.name).sort();
104
- const stat = statSync(codexBin);
105
- const key = `${codexBin}:${stat.mtimeMs}:${stat.size}:${names.join(',')}`;
106
- if (featureCache.has(key)) return featureCache.get(key);
107
- const home = mkdtempSync(join(tmpdir(), 'synabun-feature-support-'));
108
- try {
109
- const states = [false, true].map(value => {
110
- const check = spawnSync(codexBin, [...CODEX_PROBE_SANDBOX_ARGS, '-c', `features={${names.map(name => `${name}=${value}`).join(',')}}`, 'features', 'list'], {
111
- env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 10000, windowsHide: true,
112
- });
113
- if (check.error || check.status !== 0) throw new Error('Codex could not validate feature switches');
114
- return new Map(check.stdout.split('\n').map(line => {
115
- const parts = line.trim().split(/\s+/); return [parts[0], parts.at(-1)];
163
+ const stamp = await stat(codexBin);
164
+ const key = `features:${codexBin}:${stamp.mtimeMs}:${stamp.size}:${names.join(',')}`;
165
+ const found = known(featureCache, key);
166
+ if (found) return found;
167
+ return once(key, async () => {
168
+ const home = await mkdtemp(join(tmpdir(), 'synabun-feature-support-'));
169
+ try {
170
+ const env = { ...process.env, CODEX_HOME: home };
171
+ const states = await Promise.all([false, true].map(async value => {
172
+ const check = await probe(codexBin, [...CODEX_PROBE_SANDBOX_ARGS, '-c', `features={${names.map(name => `${name}=${value}`).join(',')}}`, 'features', 'list'], { env, timeout: 10000 });
173
+ if (check.status !== 0) throw new Error('Codex could not validate feature switches');
174
+ return new Map(check.stdout.split('\n').map(line => {
175
+ const parts = line.trim().split(/\s+/); return [parts[0], parts.at(-1)];
176
+ }));
116
177
  }));
117
- });
118
- const supported = Object.fromEntries(names.map(name => [name, states[0].get(name) === 'false' && states[1].get(name) === 'true']));
119
- featureCache.set(key, supported);
120
- return supported;
121
- } finally { rmSync(home, { recursive: true, force: true }); }
178
+ const supported = Object.fromEntries(names.map(name => [name, states[0].get(name) === 'false' && states[1].get(name) === 'true']));
179
+ featureCache.set(key, supported);
180
+ try { store?.set(key, supported); } catch { /* kept in memory */ }
181
+ return supported;
182
+ } finally { await rm(home, { recursive: true, force: true }).catch(() => {}); }
183
+ });
122
184
  }
@@ -26,13 +26,22 @@
26
26
  // that folder are deleted: every request that arrives meanwhile waits for the
27
27
  // same start, so no request's own session is ever taken for a leftover. A start
28
28
  // that fails after its process was spawned (the serve never answers, its client
29
- // cannot be built, a stop is asked for meanwhile) stops that process and fails
30
- // for everybody who waited for it; stop() ends a start that is under way too.
29
+ // cannot be built, a stop is asked for meanwhile) stops that process (SIGTERM,
30
+ // then SIGKILL when it does not end) and fails for everybody who waited for it;
31
+ // stop() ends a start that is under way too. Another server that took the port
32
+ // (trackServe says 'foreign') is left running while the start tries another.
31
33
  // There is no fallback to the shared serve. Everything that touches a process,
32
34
  // a port or the network is injected: tests run against a fake.
33
35
 
34
36
  import { mkdirSync, writeFileSync } from 'node:fs';
35
37
  import { isAbsolute, join, resolve, sep } from 'node:path';
38
+ import { checkOpencodeServer, newServePassword } from './opencode-protocol.js';
39
+
40
+ // OpenCode 2 (lib/opencode-protocol.js) runs the same way: the serve gets a
41
+ // password of its own, is healthy only when its /api answers JSON, and is
42
+ // spoken to through lib/opencode2-client.js; its configuration is the OpenCode
43
+ // 1 file below, which OpenCode 2 reads (agent → agents, prompt → system,
44
+ // temperature → request body).
36
45
 
37
46
  export const COMPLETION_AGENT = 'synabun-complete';
38
47
  /** Every session of this serve carries this title: no title is generated, and leftovers of a crash are recognisable. */
@@ -41,6 +50,8 @@ export const IDLE_STOP_MS = 10 * 60_000;
41
50
  export const START_TIMEOUT_MS = 15_000;
42
51
  /** How long a start waits for the sessions a crash left behind to be listed and deleted. */
43
52
  export const SWEEP_TIMEOUT_MS = 5000;
53
+ /** How many ports a start tries when another server takes the one it was given. */
54
+ export const START_ATTEMPTS = 3;
44
55
 
45
56
  // What of the user's OpenCode configuration decides which models can be reached.
46
57
  const CARRIED_KEYS = ['provider', 'disabled_providers', 'enabled_providers'];
@@ -196,11 +207,25 @@ function abortError() {
196
207
  * @param {() => object | null} deps.readUserConfig the user's OpenCode configuration, read at each start
197
208
  * @param {() => string | null} [deps.userConfigDir] the folder that configuration is in (relative {file:…}
198
209
  * references of its providers are resolved against it)
199
- * @param {() => number | null} deps.allocPort a free port (server.js allocOpencodeIsoPort)
200
- * @param {(o: { port: number, env: object, cwd: string }) => object} deps.spawnServe starts `opencode serve` on the
201
- * port and returns its process (`on('exit')`, `kill()`)
202
- * @param {(proc: object) => void} [deps.killServe] stops that process (default: SIGTERM)
203
- * @param {(o: { port: number }) => object} deps.createClient lib/opencode-v2-client.js createClientInstance
210
+ * @param {() => number | null | Promise<number | null>} deps.allocPort a free port (server.js: what an earlier
211
+ * SynaBun left running goes first, then allocOpencodeIsoPort)
212
+ * @param {(o: { port: number, env: object, cwd: string, protocol: 1 | 2 | null, password: string | null }) => object}
213
+ * deps.spawnServe starts `opencode serve` on the port (OpenCode 2: with that password) and returns its process
214
+ * (`on('exit')`, `kill()`)
215
+ * @param {(proc: object, signal?: string) => void} [deps.killServe] stops that process: SIGTERM, or SIGKILL for a
216
+ * failed start's process that did not end (default: proc.kill(signal))
217
+ * @param {(o: { port: number, protocol?: number, password?: string | null, version?: string }) => object}
218
+ * deps.createClient lib/opencode-v2-client.js createClientInstance
219
+ * @param {() => Promise<number | null | { protocol: number | null, major?: number | null, supported?: boolean, reason?: string }>}
220
+ * [deps.detectProtocol] the OpenCode major the serve will be (server.js detectOpencodeProtocol); 1 when not given
221
+ * @param {(protocol: number | null, run: () => Promise<any>) => Promise<any>} [deps.startOneAtATime] runs a start
222
+ * in its turn among every serve SynaBun starts (server.js oneOpencodeStartAtATime); by itself when not given
223
+ * @param {number} [deps.exitWaitMs] how long a failed start waits for its process to exit after SIGTERM, and again
224
+ * after SIGKILL, before its turn ends
225
+ * @param {(o: { port: number, proc: object }) => any} [deps.trackServe] told once something answers on the port
226
+ * (server.js keeps the serve on record from its spawn, so that a crash of SynaBun does not leave it running for
227
+ * good); 'foreign' (or a promise of it): what answers is not this start's process but another server that took
228
+ * the port, which is left running while the start ends its own process and tries another port
204
229
  * @param {typeof fetch} [deps.fetchImpl]
205
230
  * @param {object} [deps.env] the environment the serve starts from
206
231
  * @param {number} [deps.idleMs] the serve is stopped after this long without a request
@@ -210,9 +235,11 @@ function abortError() {
210
235
  */
211
236
  export function createOpencodeCompletionEngine({
212
237
  dataDir, system, readUserConfig = () => null, userConfigDir = () => null, allocPort, spawnServe,
213
- killServe = (proc) => { try { proc.kill('SIGTERM'); } catch { /* gone */ } },
238
+ killServe = (proc, signal = 'SIGTERM') => { try { proc.kill(signal); } catch { /* gone */ } },
214
239
  createClient, fetchImpl = (...args) => fetch(...args), env = process.env, now = Date.now,
215
240
  idleMs = IDLE_STOP_MS, startTimeoutMs = START_TIMEOUT_MS, sweepTimeoutMs = SWEEP_TIMEOUT_MS, healthPollMs = 100, log = () => {},
241
+ detectProtocol = async () => 1, newPassword = newServePassword,
242
+ startOneAtATime = (_protocol, run) => run(), exitWaitMs = 3000, trackServe = () => {},
216
243
  } = {}) {
217
244
  if (!dataDir || !system) throw new Error('createOpencodeCompletionEngine requires dataDir and system');
218
245
  if (typeof allocPort !== 'function' || typeof spawnServe !== 'function' || typeof createClient !== 'function') {
@@ -266,7 +293,7 @@ export function createOpencodeCompletionEngine({
266
293
 
267
294
  async function start() {
268
295
  const mine = epoch;
269
- const port = allocPort();
296
+ let port = await allocPort();
270
297
  if (port == null) throw new Error('No free port for the completion server');
271
298
  mkdirSync(join(dataDir, 'opencode'), { recursive: true });
272
299
  mkdirSync(workDir, { recursive: true });
@@ -276,39 +303,100 @@ export function createOpencodeCompletionEngine({
276
303
  try { configDir = userConfigDir() || null; } catch { configDir = null; }
277
304
  writeFileSync(join(dataDir, 'opencode', 'config.json'), `${JSON.stringify(buildCompletionServeConfig(userConfig, { system, configDir, log }), null, 2)}\n`, 'utf8');
278
305
 
279
- const started = { port, proc: null, client: null, variants: new Map(), providers: null, providersAt: 0, exited: false, killed: false, halt: null };
306
+ // Which OpenCode this is (null: not known, told apart by what answers); a major SynaBun does not speak never starts.
307
+ const detected = await detectProtocol();
308
+ const protocol = detected && typeof detected === 'object' ? (detected.protocol ?? null) : (detected ?? null);
309
+ if (detected && typeof detected === 'object' && detected.supported === false && detected.major != null) {
310
+ throw new Error(detected.reason || `OpenCode ${detected.major} is not supported`);
311
+ }
312
+ if (mine !== epoch) throw new Error('The completion server was stopped while it was starting');
313
+ const password = protocol === 1 ? null : newPassword();
314
+
315
+ // Another server that takes the port while the serve boots is a start collision: it is left running, this start's
316
+ // own process is ended, and the start tries another port (START_ATTEMPTS ports at most).
317
+ for (let attempt = 1; ; attempt++) {
318
+ try {
319
+ return await startOn(port, { mine, protocol, password });
320
+ } catch (error) {
321
+ if (!error?.portTaken || attempt >= START_ATTEMPTS || mine !== epoch) throw error;
322
+ log('editor-complete:serve', `another server answers on port ${port}: it is left running, and the completion server tries another port`);
323
+ port = await allocPort();
324
+ if (port == null) throw new Error('No free port for the completion server');
325
+ }
326
+ }
327
+ }
328
+
329
+ // A start that failed holds its turn until its process has exited (OpenCode 2 cannot open its database while a
330
+ // serve that is going holds it). It was sent SIGTERM (end()); one still running after `exitWaitMs` is sent SIGKILL
331
+ // (killServe, through its handle: SynaBun's own child, never another pid) and waited for once more. One that
332
+ // outlives even that stays on record (server.js keeps a serve on record until its exit is seen). True once it exited.
333
+ async function waitForExit(serve) {
334
+ const exited = async () => {
335
+ for (let waited = 0; serve.proc && !serve.exited && waited < exitWaitMs; waited += 25) await sleep(25);
336
+ return !serve.proc || serve.exited;
337
+ };
338
+ if (await exited()) return true;
339
+ log('editor-complete:serve', `the completion server's process did not end within ${Math.round(exitWaitMs / 1000)} s of SIGTERM: SIGKILL`);
340
+ try { killServe(serve.proc, 'SIGKILL'); } catch { /* already gone */ }
341
+ if (await exited()) return true;
342
+ log('editor-complete:serve', 'the completion server\'s process still runs after SIGKILL: it stays on record');
343
+ return false;
344
+ }
345
+
346
+ async function startOn(port, { mine, protocol, password }) {
347
+ const started = { port, proc: null, client: null, variants: new Map(), providers: null, providersAt: 0, exited: false, killed: false, halt: null, protocol, password };
280
348
  // What a stop wakes: a start that waits (for the serve to answer, for the next look) goes on at once.
281
349
  const halted = new Promise((done) => { started.halt = done; });
282
- started.proc = spawnServe({ port, env: completionServeEnvironment(env, { configHome: dataDir }), cwd: workDir });
283
- starts++;
284
- spawned = started;
285
- // From here on a process runs. Whatever goes wrong before the serve is ready, it does not stay behind.
350
+ // Both are checked before and after each wait: a stop may have been asked for, or the process may be gone.
351
+ const stillWanted = () => {
352
+ if (mine !== epoch) throw new Error('The completion server was stopped while it was starting');
353
+ if (started.exited) throw new Error('The completion server stopped while it was starting. Is OpenCode installed?');
354
+ };
355
+ // Whatever goes wrong once a process runs, before the serve is ready, it does not stay behind.
286
356
  try {
287
- started.proc?.on?.('exit', () => {
288
- started.exited = true;
289
- if (entry === started) { entry = null; if (idleTimer) { clearTimeout(idleTimer); idleTimer = null; } }
290
- });
291
- started.proc?.on?.('error', () => { started.exited = true; });
292
-
293
- // Both are checked before and after each wait: a stop may have been asked for, or the process may be gone.
294
- const stillWanted = () => {
295
- if (mine !== epoch) throw new Error('The completion server was stopped while it was starting');
296
- if (started.exited) throw new Error('The completion server stopped while it was starting. Is OpenCode installed?');
297
- };
298
- const deadline = now() + startTimeoutMs;
299
- for (;;) {
357
+ // One start at a time with every other serve SynaBun starts (OpenCode 2 cannot open its database while
358
+ // another serve is opening it): from the spawn until this serve answers, or until a failed start's process
359
+ // is gone.
360
+ const check = await startOneAtATime(protocol, async () => {
300
361
  stillWanted();
362
+ started.proc = spawnServe({ port, env: completionServeEnvironment(env, { configHome: dataDir }), cwd: workDir, protocol, password });
363
+ starts++;
364
+ spawned = started;
365
+ started.proc?.on?.('exit', () => {
366
+ started.exited = true;
367
+ if (entry === started) { entry = null; if (idleTimer) { clearTimeout(idleTimer); idleTimer = null; } }
368
+ });
369
+ started.proc?.on?.('error', () => { started.exited = true; });
301
370
  try {
302
- const res = await Promise.race([fetchImpl(`http://127.0.0.1:${port}/global/health`, { signal: AbortSignal.timeout(2000) }), halted]);
303
- if (res?.ok) break;
304
- } catch { /* not up yet */ }
305
- stillWanted();
306
- if (now() >= deadline) throw new Error(`The completion server did not answer within ${Math.round(startTimeoutMs / 1000)} seconds`);
307
- await Promise.race([sleep(healthPollMs), halted]);
308
- }
371
+ const deadline = now() + startTimeoutMs;
372
+ for (;;) {
373
+ stillWanted();
374
+ // The right API answering JSON, never a web page (lib/opencode-protocol.js).
375
+ const answer = await Promise.race([checkOpencodeServer({ port, protocol, password, fetchImpl, timeoutMs: 2000 }), halted]);
376
+ if (answer?.ok) {
377
+ // Only this start's own process counts: another server that took the port answers for itself.
378
+ let verdict = null;
379
+ try { verdict = await trackServe({ port, proc: started.proc }); } catch { /* the record is the caller's */ }
380
+ if (verdict === 'foreign') throw Object.assign(new Error(`Another server answers on port ${port}`), { portTaken: true });
381
+ return answer;
382
+ }
383
+ stillWanted();
384
+ if (now() >= deadline) {
385
+ const what = answer && answer.state && answer.state !== 'down' ? `: ${answer.error}` : '';
386
+ throw new Error(`The completion server did not answer within ${Math.round(startTimeoutMs / 1000)} seconds${what}`);
387
+ }
388
+ await Promise.race([sleep(healthPollMs), halted]);
389
+ }
390
+ } catch (error) {
391
+ end(started);
392
+ await waitForExit(started);
393
+ throw error;
394
+ }
395
+ });
309
396
  stillWanted();
310
397
 
311
- started.client = createClient({ port });
398
+ started.protocol = check.protocol || protocol || 1;
399
+ started.client = createClient({ port, protocol: started.protocol, password, version: check.version || '' });
312
400
  // It answers, and takes no request yet. First the sessions a crash left behind go: a sweep that ran beside the
313
401
  // first requests would find their sessions in its listing and delete them under the model call. Meanwhile,
314
402
  // which lighter reasoning setting each model offers; worth a short wait, as without it the first completions
@@ -335,6 +423,15 @@ export function createOpencodeCompletionEngine({
335
423
 
336
424
  async function loadProviders(serve, { signal } = {}) {
337
425
  if (serve.providers && now() - serve.providersAt < 60_000) return serve.providers;
426
+ if (serve.protocol === 2) {
427
+ // OpenCode 2 has no /provider: its client answers in OpenCode 1's shape ({ all, default, connected }).
428
+ const listed = await serve.client.getClient().provider.list({ directory: workDir });
429
+ if (listed?.error) throw new Error(`The completion server answered ${listed.response?.status || 'an error'} for its model list`);
430
+ serve.providers = listed.data;
431
+ serve.providersAt = now();
432
+ serve.variants = variantsByModel(serve.providers);
433
+ return serve.providers;
434
+ }
338
435
  const res = await fetchImpl(serveUrl(serve.port, '/provider'), { signal: signal || AbortSignal.timeout(8000) });
339
436
  if (!res.ok) throw new Error(`The completion server answered ${res.status} for its model list`);
340
437
  serve.providers = await res.json();
@@ -84,6 +84,8 @@ export function buildExecInvocation({
84
84
  mcpEnvOverrides = [],
85
85
  // Codex: launch overrides as `-c` words (the Windows sandbox of the Admin sandbox setting).
86
86
  codexConfigArgs = [],
87
+ // OpenCode: the major of the CLI (lib/opencode-protocol.js); 2 has other `run` flags.
88
+ opencodeMajor = 1,
87
89
  platform = process.platform,
88
90
  commandExists = existsSync,
89
91
  } = {}) {
@@ -111,7 +113,12 @@ export function buildExecInvocation({
111
113
  if (model) args.push('--model', String(model));
112
114
  break;
113
115
  case 'opencode':
114
- args.push('run', '--dangerously-skip-permissions');
116
+ // OpenCode 2: a private server for this run (`--standalone`; without it the
117
+ // CLI uses the machine's background service, which has none of the loop's
118
+ // configuration) and `--auto` (approve what is not denied) for the flag
119
+ // OpenCode 1 took.
120
+ if (Number(opencodeMajor) >= 2) args.push('run', '--standalone', '--auto');
121
+ else args.push('run', '--dangerously-skip-permissions');
115
122
  if (model) args.push('--model', String(model));
116
123
  args.push('--format', 'default');
117
124
  break;