synabun 2.0.7 → 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 (565) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +1 -1
  3. package/THIRD-PARTY-LICENSES.md +46 -4
  4. package/hooks/claude-code/post-plan.mjs +6 -30
  5. package/hooks/claude-code/session-start.mjs +1 -4
  6. package/hooks/claude-code/shared.mjs +106 -72
  7. package/launcher.mjs +9 -3
  8. package/lib/package-processes.js +670 -0
  9. package/lib/runtime-dependencies.js +225 -0
  10. package/lib/start-launcher.js +6 -1
  11. package/mcp-server/dist/services/browser-bench.d.ts +1 -1
  12. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
  13. package/mcp-server/dist/services/browser-bench.js +1 -1
  14. package/mcp-server/dist/services/browser-bench.js.map +1 -1
  15. package/mcp-server/dist/services/categories.d.ts +35 -2
  16. package/mcp-server/dist/services/categories.d.ts.map +1 -1
  17. package/mcp-server/dist/services/categories.js +77 -33
  18. package/mcp-server/dist/services/categories.js.map +1 -1
  19. package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
  20. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
  21. package/mcp-server/dist/services/desktop-bench.js +1 -1
  22. package/mcp-server/dist/services/desktop-bench.js.map +1 -1
  23. package/mcp-server/dist/tools/agents.d.ts +1 -1
  24. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  25. package/mcp-server/dist/tools/agents.js +1 -1
  26. package/mcp-server/dist/tools/agents.js.map +1 -1
  27. package/mcp-server/dist/tools/bluesky.js +1 -1
  28. package/mcp-server/dist/tools/bluesky.js.map +1 -1
  29. package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
  30. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
  31. package/mcp-server/dist/tools/browser-advanced.js +1 -1
  32. package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
  33. package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
  34. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
  35. package/mcp-server/dist/tools/browser-batch.js +1 -1
  36. package/mcp-server/dist/tools/browser-batch.js.map +1 -1
  37. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
  38. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
  39. package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
  40. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
  41. package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
  42. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
  43. package/mcp-server/dist/tools/browser-interact.js +1 -1
  44. package/mcp-server/dist/tools/browser-interact.js.map +1 -1
  45. package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
  46. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
  47. package/mcp-server/dist/tools/browser-navigate.js +1 -1
  48. package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
  49. package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
  50. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
  51. package/mcp-server/dist/tools/browser-observe.js +1 -1
  52. package/mcp-server/dist/tools/browser-observe.js.map +1 -1
  53. package/mcp-server/dist/tools/card-tools.d.ts +1 -1
  54. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
  55. package/mcp-server/dist/tools/card-tools.js +1 -1
  56. package/mcp-server/dist/tools/card-tools.js.map +1 -1
  57. package/mcp-server/dist/tools/category.d.ts +1 -1
  58. package/mcp-server/dist/tools/category.d.ts.map +1 -1
  59. package/mcp-server/dist/tools/category.js +5 -1
  60. package/mcp-server/dist/tools/category.js.map +1 -1
  61. package/mcp-server/dist/tools/choice.d.ts +1 -1
  62. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  63. package/mcp-server/dist/tools/choice.js +1 -1
  64. package/mcp-server/dist/tools/choice.js.map +1 -1
  65. package/mcp-server/dist/tools/computer.d.ts +1 -1
  66. package/mcp-server/dist/tools/computer.d.ts.map +1 -1
  67. package/mcp-server/dist/tools/computer.js +1 -1
  68. package/mcp-server/dist/tools/computer.js.map +1 -1
  69. package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
  70. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
  71. package/mcp-server/dist/tools/discord-channel.js +1 -1
  72. package/mcp-server/dist/tools/discord-channel.js.map +1 -1
  73. package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
  74. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
  75. package/mcp-server/dist/tools/discord-guild.js +1 -1
  76. package/mcp-server/dist/tools/discord-guild.js.map +1 -1
  77. package/mcp-server/dist/tools/discord-member.d.ts +1 -1
  78. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
  79. package/mcp-server/dist/tools/discord-member.js +1 -1
  80. package/mcp-server/dist/tools/discord-member.js.map +1 -1
  81. package/mcp-server/dist/tools/discord-message.d.ts +1 -1
  82. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
  83. package/mcp-server/dist/tools/discord-message.js +1 -1
  84. package/mcp-server/dist/tools/discord-message.js.map +1 -1
  85. package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
  86. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
  87. package/mcp-server/dist/tools/discord-onboarding.js +1 -1
  88. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
  89. package/mcp-server/dist/tools/discord-role.d.ts +1 -1
  90. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/discord-role.js +1 -1
  92. package/mcp-server/dist/tools/discord-role.js.map +1 -1
  93. package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
  94. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
  95. package/mcp-server/dist/tools/discord-thread.js +1 -1
  96. package/mcp-server/dist/tools/discord-thread.js.map +1 -1
  97. package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
  98. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
  99. package/mcp-server/dist/tools/discord-webhook.js +1 -1
  100. package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
  101. package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
  102. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
  103. package/mcp-server/dist/tools/fb-groups.js +1 -1
  104. package/mcp-server/dist/tools/fb-groups.js.map +1 -1
  105. package/mcp-server/dist/tools/forget.d.ts +1 -1
  106. package/mcp-server/dist/tools/forget.d.ts.map +1 -1
  107. package/mcp-server/dist/tools/forget.js +1 -1
  108. package/mcp-server/dist/tools/forget.js.map +1 -1
  109. package/mcp-server/dist/tools/git-tools.d.ts +1 -1
  110. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
  111. package/mcp-server/dist/tools/git-tools.js +1 -1
  112. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  113. package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
  114. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
  115. package/mcp-server/dist/tools/gsc/experience.js +1 -1
  116. package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
  117. package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
  118. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
  119. package/mcp-server/dist/tools/gsc/indexing.js +1 -1
  120. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
  121. package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
  122. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
  123. package/mcp-server/dist/tools/gsc/inspect.js +1 -1
  124. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
  125. package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
  126. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
  127. package/mcp-server/dist/tools/gsc/navigate.js +1 -1
  128. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
  129. package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
  130. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
  131. package/mcp-server/dist/tools/gsc/performance.js +1 -1
  132. package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
  133. package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
  134. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
  135. package/mcp-server/dist/tools/gsc/settings.js +1 -1
  136. package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
  137. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  138. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
  139. package/mcp-server/dist/tools/image-tools.js +1 -1
  140. package/mcp-server/dist/tools/image-tools.js.map +1 -1
  141. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
  142. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
  143. package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
  144. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
  145. package/mcp-server/dist/tools/loop.d.ts +1 -1
  146. package/mcp-server/dist/tools/loop.d.ts.map +1 -1
  147. package/mcp-server/dist/tools/loop.js +1 -1
  148. package/mcp-server/dist/tools/loop.js.map +1 -1
  149. package/mcp-server/dist/tools/memories.d.ts +1 -1
  150. package/mcp-server/dist/tools/memories.d.ts.map +1 -1
  151. package/mcp-server/dist/tools/memories.js +1 -1
  152. package/mcp-server/dist/tools/memories.js.map +1 -1
  153. package/mcp-server/dist/tools/morelogin.d.ts +1 -1
  154. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  155. package/mcp-server/dist/tools/morelogin.js +1 -1
  156. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  157. package/mcp-server/dist/tools/profile.d.ts +1 -1
  158. package/mcp-server/dist/tools/profile.d.ts.map +1 -1
  159. package/mcp-server/dist/tools/profile.js +1 -1
  160. package/mcp-server/dist/tools/profile.js.map +1 -1
  161. package/mcp-server/dist/tools/recall.d.ts +1 -1
  162. package/mcp-server/dist/tools/recall.d.ts.map +1 -1
  163. package/mcp-server/dist/tools/recall.js +1 -1
  164. package/mcp-server/dist/tools/recall.js.map +1 -1
  165. package/mcp-server/dist/tools/reflect.d.ts +1 -1
  166. package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
  167. package/mcp-server/dist/tools/reflect.js +1 -1
  168. package/mcp-server/dist/tools/reflect.js.map +1 -1
  169. package/mcp-server/dist/tools/remember.d.ts +1 -1
  170. package/mcp-server/dist/tools/remember.d.ts.map +1 -1
  171. package/mcp-server/dist/tools/remember.js +13 -11
  172. package/mcp-server/dist/tools/remember.js.map +1 -1
  173. package/mcp-server/dist/tools/restore.d.ts +1 -1
  174. package/mcp-server/dist/tools/restore.d.ts.map +1 -1
  175. package/mcp-server/dist/tools/restore.js +1 -1
  176. package/mcp-server/dist/tools/restore.js.map +1 -1
  177. package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
  178. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
  179. package/mcp-server/dist/tools/style-guide-tools.js +1 -1
  180. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
  181. package/mcp-server/dist/tools/sync.d.ts +1 -1
  182. package/mcp-server/dist/tools/sync.d.ts.map +1 -1
  183. package/mcp-server/dist/tools/sync.js +1 -1
  184. package/mcp-server/dist/tools/sync.js.map +1 -1
  185. package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
  186. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
  187. package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
  188. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
  189. package/mcp-server/dist/tools/utils.d.ts +1 -1
  190. package/mcp-server/dist/tools/utils.d.ts.map +1 -1
  191. package/mcp-server/dist/tools/utils.js +1 -1
  192. package/mcp-server/dist/tools/utils.js.map +1 -1
  193. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +1 -1
  194. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -1
  195. package/mcp-server/dist/tools/whatsapp-send-file.js +1 -1
  196. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -1
  197. package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
  198. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  199. package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
  200. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  201. package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
  202. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
  203. package/mcp-server/dist/tools/youtube/analytics.js +1 -1
  204. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
  205. package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
  206. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
  207. package/mcp-server/dist/tools/youtube/download.js +1 -1
  208. package/mcp-server/dist/tools/youtube/download.js.map +1 -1
  209. package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
  210. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
  211. package/mcp-server/dist/tools/youtube/media.js +1 -1
  212. package/mcp-server/dist/tools/youtube/media.js.map +1 -1
  213. package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
  214. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
  215. package/mcp-server/dist/tools/youtube/metadata.js +1 -1
  216. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
  217. package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
  218. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
  219. package/mcp-server/dist/tools/youtube/navigate.js +1 -1
  220. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
  221. package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
  222. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
  223. package/mcp-server/dist/tools/youtube/playlist.js +1 -1
  224. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
  225. package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
  226. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
  227. package/mcp-server/dist/tools/youtube/schedule.js +1 -1
  228. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
  229. package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
  230. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
  231. package/mcp-server/dist/tools/youtube/source.js +1 -1
  232. package/mcp-server/dist/tools/youtube/source.js.map +1 -1
  233. package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
  234. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
  235. package/mcp-server/dist/tools/youtube/upload.js +4 -9
  236. package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
  237. package/mcp-server/dist/tui.js +16 -4
  238. package/mcp-server/dist/tui.js.map +1 -1
  239. package/mcp-server/package-lock.json +3 -3
  240. package/mcp-server/package.json +3 -3
  241. package/mcp-server/src/services/browser-bench.ts +1 -1
  242. package/mcp-server/src/services/categories.ts +77 -32
  243. package/mcp-server/src/services/desktop-bench.ts +1 -1
  244. package/mcp-server/src/tools/agents.ts +1 -1
  245. package/mcp-server/src/tools/bluesky.ts +1 -1
  246. package/mcp-server/src/tools/browser-advanced.ts +1 -1
  247. package/mcp-server/src/tools/browser-batch.ts +1 -1
  248. package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
  249. package/mcp-server/src/tools/browser-interact.ts +1 -1
  250. package/mcp-server/src/tools/browser-navigate.ts +1 -1
  251. package/mcp-server/src/tools/browser-observe.ts +1 -1
  252. package/mcp-server/src/tools/card-tools.ts +1 -1
  253. package/mcp-server/src/tools/category.ts +6 -1
  254. package/mcp-server/src/tools/choice.ts +1 -1
  255. package/mcp-server/src/tools/computer.ts +1 -1
  256. package/mcp-server/src/tools/discord-channel.ts +1 -1
  257. package/mcp-server/src/tools/discord-guild.ts +1 -1
  258. package/mcp-server/src/tools/discord-member.ts +1 -1
  259. package/mcp-server/src/tools/discord-message.ts +1 -1
  260. package/mcp-server/src/tools/discord-onboarding.ts +1 -1
  261. package/mcp-server/src/tools/discord-role.ts +1 -1
  262. package/mcp-server/src/tools/discord-thread.ts +1 -1
  263. package/mcp-server/src/tools/discord-webhook.ts +1 -1
  264. package/mcp-server/src/tools/fb-groups.ts +1 -1
  265. package/mcp-server/src/tools/forget.ts +1 -1
  266. package/mcp-server/src/tools/git-tools.ts +1 -1
  267. package/mcp-server/src/tools/gsc/experience.ts +1 -1
  268. package/mcp-server/src/tools/gsc/indexing.ts +1 -1
  269. package/mcp-server/src/tools/gsc/inspect.ts +1 -1
  270. package/mcp-server/src/tools/gsc/navigate.ts +1 -1
  271. package/mcp-server/src/tools/gsc/performance.ts +1 -1
  272. package/mcp-server/src/tools/gsc/settings.ts +1 -1
  273. package/mcp-server/src/tools/image-tools.ts +1 -1
  274. package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
  275. package/mcp-server/src/tools/loop.ts +1 -1
  276. package/mcp-server/src/tools/memories.ts +1 -1
  277. package/mcp-server/src/tools/morelogin.ts +1 -1
  278. package/mcp-server/src/tools/profile.ts +1 -1
  279. package/mcp-server/src/tools/recall.ts +1 -1
  280. package/mcp-server/src/tools/reflect.ts +1 -1
  281. package/mcp-server/src/tools/remember.ts +13 -12
  282. package/mcp-server/src/tools/restore.ts +1 -1
  283. package/mcp-server/src/tools/style-guide-tools.ts +1 -1
  284. package/mcp-server/src/tools/sync.ts +1 -1
  285. package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
  286. package/mcp-server/src/tools/utils.ts +1 -1
  287. package/mcp-server/src/tools/whatsapp-send-file.ts +1 -1
  288. package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
  289. package/mcp-server/src/tools/youtube/analytics.ts +1 -1
  290. package/mcp-server/src/tools/youtube/download.ts +1 -1
  291. package/mcp-server/src/tools/youtube/media.ts +1 -1
  292. package/mcp-server/src/tools/youtube/metadata.ts +1 -1
  293. package/mcp-server/src/tools/youtube/navigate.ts +1 -1
  294. package/mcp-server/src/tools/youtube/playlist.ts +1 -1
  295. package/mcp-server/src/tools/youtube/schedule.ts +1 -1
  296. package/mcp-server/src/tools/youtube/source.ts +1 -1
  297. package/mcp-server/src/tools/youtube/upload.ts +4 -12
  298. package/mcp-server/src/tui.ts +22 -9
  299. package/neural-interface/i18n/en.json +236 -7
  300. package/neural-interface/i18n/pt-BR.json +238 -9
  301. package/neural-interface/lib/account-usage.js +5 -3
  302. package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
  303. package/neural-interface/lib/agent-sandbox-setting.js +57 -0
  304. package/neural-interface/lib/app-controls-api.js +161 -0
  305. package/neural-interface/lib/assistant-api.js +40 -7
  306. package/neural-interface/lib/assistant-brains/opencode.js +13 -1
  307. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
  308. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
  309. package/neural-interface/lib/assistant-catalog.js +17 -2
  310. package/neural-interface/lib/assistant-dispatch.js +3 -0
  311. package/neural-interface/lib/assistant-envelope.js +19 -1
  312. package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
  313. package/neural-interface/lib/assistant-runtime.js +131 -8
  314. package/neural-interface/lib/claude-model-catalog.js +64 -14
  315. package/neural-interface/lib/codex-account-auth.js +4 -2
  316. package/neural-interface/lib/codex-accounts.js +14 -1
  317. package/neural-interface/lib/codex-settings-schema.js +111 -47
  318. package/neural-interface/lib/editor-completion-opencode.js +132 -35
  319. package/neural-interface/lib/exec-loop-command.js +11 -2
  320. package/neural-interface/lib/install-details.js +23 -5
  321. package/neural-interface/lib/launch-cache.js +293 -0
  322. package/neural-interface/lib/login-start.js +524 -0
  323. package/neural-interface/lib/native-loop-providers.js +24 -4
  324. package/neural-interface/lib/native-loop-runtime.js +19 -0
  325. package/neural-interface/lib/opencode-db-schema.js +75 -0
  326. package/neural-interface/lib/opencode-history.js +16 -1
  327. package/neural-interface/lib/opencode-protocol.js +173 -0
  328. package/neural-interface/lib/opencode-provider-view.js +81 -0
  329. package/neural-interface/lib/opencode-runtime-config.js +10 -3
  330. package/neural-interface/lib/opencode-serve-records.js +606 -0
  331. package/neural-interface/lib/opencode-v2-client.js +33 -6
  332. package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
  333. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  334. package/neural-interface/lib/opencode2-client.js +760 -0
  335. package/neural-interface/lib/opencode2-proxy.js +116 -0
  336. package/neural-interface/lib/opencode2-translate.js +1008 -0
  337. package/neural-interface/lib/process-identity.js +361 -0
  338. package/neural-interface/lib/provider-panel-terminal-auth.js +3 -2
  339. package/neural-interface/lib/session-cache-builder.js +26 -5
  340. package/neural-interface/lib/session-indexer.js +3 -1
  341. package/neural-interface/lib/session-title-generator.js +20 -0
  342. package/neural-interface/lib/style-guide/api.js +4 -3
  343. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  344. package/neural-interface/package-lock.json +1342 -44
  345. package/neural-interface/package.json +5 -1
  346. package/neural-interface/public/index.html +2 -2
  347. package/neural-interface/public/shared/account-menu.js +4 -1
  348. package/neural-interface/public/shared/account-usage.js +62 -2
  349. package/neural-interface/public/shared/account-view.js +2 -1
  350. package/neural-interface/public/shared/agent-runtime-options.js +55 -11
  351. package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
  352. package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
  353. package/neural-interface/public/shared/assistant/asst-api.js +18 -3
  354. package/neural-interface/public/shared/assistant/asst-brain-picker.js +424 -152
  355. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  356. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  357. package/neural-interface/public/shared/assistant/asst-menu.js +275 -77
  358. package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
  359. package/neural-interface/public/shared/assistant/asst-panel.js +236 -35
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
  361. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  362. package/neural-interface/public/shared/assistant/asst-render.js +382 -36
  363. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  364. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  365. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  366. package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
  367. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  368. package/neural-interface/public/shared/assistant/asst-state.js +14 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +109 -17
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
  371. package/neural-interface/public/shared/cc-dropdown.js +480 -0
  372. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  373. package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
  374. package/neural-interface/public/shared/cdx/cdx-styles.js +49 -5
  375. package/neural-interface/public/shared/cdx/cdx-tabs.js +152 -35
  376. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  377. package/neural-interface/public/shared/data-cache.js +223 -0
  378. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  379. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  380. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  381. package/neural-interface/public/shared/desktop-update-view.js +15 -5
  382. package/neural-interface/public/shared/file-links.js +73 -8
  383. package/neural-interface/public/shared/html-shell.js +74 -80
  384. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  385. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  386. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +11 -3
  387. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  388. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +97 -3
  389. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
  390. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  391. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  392. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
  393. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  394. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
  395. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
  396. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +83 -5
  397. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
  398. package/neural-interface/public/shared/opencode-providers.js +39 -0
  399. package/neural-interface/public/shared/schedules-view.js +318 -0
  400. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  401. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  402. package/neural-interface/public/shared/settings/settings-data.js +88 -0
  403. package/neural-interface/public/shared/settings/settings-ia.js +17 -0
  404. package/neural-interface/public/shared/showcase-privacy.js +2 -1
  405. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  406. package/neural-interface/public/shared/styles.css +543 -372
  407. package/neural-interface/public/shared/ui-assistant.js +27 -5
  408. package/neural-interface/public/shared/ui-automation-studio.js +67 -27
  409. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  410. package/neural-interface/public/shared/ui-claude-panel.js +126 -12
  411. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  412. package/neural-interface/public/shared/ui-detail.js +6 -6
  413. package/neural-interface/public/shared/ui-explorer.js +2 -0
  414. package/neural-interface/public/shared/ui-file-links.js +1 -0
  415. package/neural-interface/public/shared/ui-image-gallery.js +6 -5
  416. package/neural-interface/public/shared/ui-invite.js +18 -2
  417. package/neural-interface/public/shared/ui-link.js +9 -9
  418. package/neural-interface/public/shared/ui-menubar.js +232 -33
  419. package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
  420. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  421. package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
  422. package/neural-interface/public/shared/ui-sessions.js +15 -4
  423. package/neural-interface/public/shared/ui-settings.js +87 -36
  424. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  425. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  426. package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
  427. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  428. package/neural-interface/public/shared/ui-skills.js +70 -12
  429. package/neural-interface/public/shared/ui-sync.js +9 -0
  430. package/neural-interface/public/shared/ui-terminal.js +36 -20
  431. package/neural-interface/public/shared/ui-trash.js +8 -1
  432. package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
  433. package/neural-interface/public/shared/ui-tutorial.js +15 -5
  434. package/neural-interface/public/shared/ui-update.js +87 -16
  435. package/neural-interface/public/shared/ui-whiteboard.js +2 -1
  436. package/neural-interface/public/variant/2d/main.js +4 -1
  437. package/neural-interface/public/variant/3d/main.js +3 -0
  438. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  439. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  440. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  441. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  442. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  443. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  444. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  445. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  446. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  447. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  448. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  449. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  450. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  451. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  452. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  453. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  454. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  455. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  456. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  457. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  458. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  459. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  460. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  461. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  462. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  463. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  464. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  465. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  466. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  467. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  468. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  469. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  470. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  471. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  472. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  473. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  474. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  475. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  476. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  477. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  478. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  479. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  480. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  481. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  482. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  483. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  484. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  485. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  486. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  487. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  488. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  489. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  490. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  491. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  492. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  493. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  494. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  495. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  496. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  497. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  498. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  499. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  500. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  501. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  502. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  503. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  504. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  505. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  506. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  507. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  508. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  509. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  510. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  511. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  512. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  513. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  514. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  515. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  516. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  517. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  518. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  519. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  520. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  521. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  522. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  523. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  524. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  525. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  526. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  527. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  528. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  529. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  530. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  531. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  532. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  533. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  534. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  535. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  536. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  537. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  538. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  539. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  540. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  541. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  542. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  543. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  544. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  545. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  546. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  547. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  548. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  549. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  550. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  551. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  552. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  553. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  554. package/neural-interface/scripts/build-vendor.mjs +84 -3
  555. package/neural-interface/scripts/rebuild-pty.js +14 -2
  556. package/neural-interface/server.js +1279 -372
  557. package/npm-shrinkwrap.json +4424 -0
  558. package/package.json +54 -2
  559. package/postinstall.js +61 -47
  560. package/preuninstall.js +8 -35
  561. package/setup.js +76 -44
  562. package/skills/synabun/modules/idea.md +1 -1
  563. package/skills/synabun/modules/schedule.md +2 -2
  564. package/updater.cmd +2 -2
  565. package/updater.mjs +97 -3
@@ -0,0 +1,161 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — /api/system/app: Settings > App for an npm or Git install
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // GET /api/system/app { ok, install, desktop, updates: { auto }, startup: { … } }
6
+ // PUT /api/system/app/updates { auto: boolean } the automatic update check and its badge
7
+ // PUT /api/system/app/startup { enabled: boolean } start the server at login (lib/login-start.js)
8
+ // POST /api/system/app/open-folder { which: 'data' | 'logs' }
9
+ //
10
+ // Automatic updates here are the check: on (the default), the server looks for
11
+ // a newer version when it starts and the page shows the update badge and asks
12
+ // again every half hour; off, it looks only when the user asks (Check for
13
+ // updates). Installing always goes through the update dialog and its backup
14
+ // step; nothing installs unattended. The setting is DATA_HOME/data/app-settings.json.
15
+ //
16
+ // In the desktop app (`desktop: true`) the app's own settings are the
17
+ // window's (packaging/desktop/src/app-prefs.mjs, through the preload bridge):
18
+ // this answers what it is and changes nothing.
19
+ //
20
+ // These are the owner's acts on this computer (a login entry, a folder opened
21
+ // on its screen), so the routes take the guards of /api/cli/install
22
+ // (lib/http-guards.js): a loopback socket and no proxy header, a localhost
23
+ // Host, no invite guest; a write also its own Origin, a JSON body,
24
+ // X-SynaBun-UI: 1 and no agent header. (`/api/system` is admin-only in
25
+ // lib/request-gate.js too.) A request names a switch, never a path or a command.
26
+
27
+ import { Router } from 'express';
28
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
29
+ import { dirname } from 'node:path';
30
+ import { guestForbidden, noStore, requireLocal, requireLocalHost, requireSameOriginJson, sendGuardError, uiOnly } from './http-guards.js';
31
+ import { runProgram } from './file-explorer-system.js';
32
+
33
+ export const APP_SETTINGS_DEFAULTS = Object.freeze({ autoUpdateCheck: true });
34
+
35
+ export function normalizeAppSettings(raw) {
36
+ const source = raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
37
+ const out = {};
38
+ for (const [key, value] of Object.entries(APP_SETTINGS_DEFAULTS)) out[key] = typeof source[key] === typeof value ? source[key] : value;
39
+ return out;
40
+ }
41
+
42
+ /** DATA_HOME/data/app-settings.json, read once and written through (this server is its only writer). */
43
+ export function createAppSettingsStore({ path, fs = { mkdirSync, readFileSync, renameSync, writeFileSync } } = {}) {
44
+ let current = null;
45
+ const load = () => {
46
+ if (current) return current;
47
+ try { current = normalizeAppSettings(JSON.parse(fs.readFileSync(path, 'utf8'))); } catch { current = normalizeAppSettings(null); }
48
+ return current;
49
+ };
50
+ return {
51
+ get: () => ({ ...load() }),
52
+ set(patch) {
53
+ const next = normalizeAppSettings({ ...load(), ...patch });
54
+ fs.mkdirSync(dirname(path), { recursive: true });
55
+ const temp = `${path}.${process.pid}.tmp`;
56
+ fs.writeFileSync(temp, JSON.stringify(next, null, 2) + '\n');
57
+ fs.renameSync(temp, path);
58
+ current = next;
59
+ return { ...next };
60
+ },
61
+ };
62
+ }
63
+
64
+ /** The program that shows a folder in the system's file manager (no shell; the path is an argument of its own). */
65
+ export function openFolderCommand(platform, path, { env = process.env } = {}) {
66
+ if (platform === 'darwin') return { file: 'open', args: [path] };
67
+ // explorer.exe answers 1 even when it opened the folder.
68
+ if (platform === 'win32') return { file: 'explorer.exe', args: [path], anyExit: true };
69
+ if (!env.DISPLAY && !env.WAYLAND_DISPLAY) return null;
70
+ return { file: 'xdg-open', args: [path] };
71
+ }
72
+
73
+ /** Open a folder on this computer's screen; resolves true when the file manager took it. */
74
+ export async function openFolderOnScreen(path, { platform = process.platform, env = process.env, run = runProgram, mkdir = mkdirSync } = {}) {
75
+ const command = openFolderCommand(platform, path, { env });
76
+ if (!command) return false;
77
+ try { mkdir(path, { recursive: true }); } catch {}
78
+ try {
79
+ await run(command.file, command.args);
80
+ return true;
81
+ } catch (error) {
82
+ return !!command.anyExit && error?.code !== 'ENOENT';
83
+ }
84
+ }
85
+
86
+ function localRouter({ isGuestRequest, port }) {
87
+ const router = Router();
88
+ const portOf = () => Number(typeof port === 'function' ? port() : port);
89
+ const built = new Map();
90
+ const perPort = (name, factory) => (req, res, next) => {
91
+ const key = `${name}:${portOf()}`;
92
+ let guard = built.get(key);
93
+ if (!guard) {
94
+ try { guard = factory(portOf()); } catch { return sendGuardError(res, 500, 'BAD_PORT', 'The app settings have no valid port.'); }
95
+ built.set(key, guard);
96
+ }
97
+ return guard(req, res, next);
98
+ };
99
+ router.use(noStore, guestForbidden(isGuestRequest), requireLocal, perPort('host', requireLocalHost));
100
+ return { router, mutating: [perPort('origin', requireSameOriginJson), uiOnly] };
101
+ }
102
+
103
+ /**
104
+ * @param {object} deps
105
+ * @param {ReturnType<typeof createAppSettingsStore>} deps.store
106
+ * @param {ReturnType<import('./login-start.js').createLoginStart>} deps.loginStart
107
+ * @param {{ data: string, logs: string }} deps.folders
108
+ * @param {string} deps.install 'npm' | 'git' | 'app' | 'desktop'
109
+ * @param {(auto: boolean) => void} [deps.onAutoUpdateChange]
110
+ * @param {(path: string) => Promise<boolean>} [deps.openFolder]
111
+ */
112
+ export function createAppControlsApi({
113
+ store, loginStart, folders, install = 'npm', onAutoUpdateChange = () => {}, openFolder = (path) => openFolderOnScreen(path),
114
+ isGuestRequest = () => false, port = 3344, log = () => {},
115
+ } = {}) {
116
+ if (!store || !loginStart || !folders) throw new Error('createAppControlsApi needs the settings store, the login start and the folders');
117
+ const { router, mutating } = localRouter({ isGuestRequest, port });
118
+ const desktop = install === 'desktop';
119
+
120
+ const body = () => {
121
+ let startup;
122
+ try { startup = loginStart.status(); } catch (error) { startup = { supported: false, reason: 'unsupported', enabled: false, state: 'off', owner: null, error: String(error?.message || error) }; }
123
+ return { ok: true, install, desktop, updates: { auto: store.get().autoUpdateCheck !== false }, startup };
124
+ };
125
+
126
+ router.get('/', (req, res) => { res.json(body()); });
127
+
128
+ router.put('/updates', ...mutating, (req, res) => {
129
+ const auto = req.body?.auto;
130
+ if (typeof auto !== 'boolean') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: 'Send { auto: true | false }.' });
131
+ if (desktop) return res.status(409).json({ ok: false, code: 'DESKTOP_MANAGED', error: 'The SynaBun app keeps this setting itself.' });
132
+ try { store.set({ autoUpdateCheck: auto }); } catch (error) {
133
+ return res.status(500).json({ ok: false, code: 'SAVE_FAILED', error: String(error?.message || error) });
134
+ }
135
+ try { onAutoUpdateChange(auto); } catch {}
136
+ res.json(body());
137
+ });
138
+
139
+ router.put('/startup', ...mutating, async (req, res) => {
140
+ const enabled = req.body?.enabled;
141
+ if (typeof enabled !== 'boolean') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: 'Send { enabled: true | false }.' });
142
+ try {
143
+ await loginStart.set(enabled);
144
+ } catch (error) {
145
+ const code = String(error?.code || 'refused');
146
+ log(`[app] start at login could not be turned ${enabled ? 'on' : 'off'}: ${error?.message || error}`);
147
+ return res.status(409).json({ ...body(), ok: false, code, error: String(error?.message || error).slice(0, 300) });
148
+ }
149
+ res.json(body());
150
+ });
151
+
152
+ router.post('/open-folder', ...mutating, async (req, res) => {
153
+ const which = req.body?.which;
154
+ if (which !== 'data' && which !== 'logs') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: "Send { which: 'data' | 'logs' }." });
155
+ const opened = await openFolder(folders[which]).catch(() => false);
156
+ if (!opened) return res.status(422).json({ ok: false, code: 'NO_FILE_MANAGER', error: 'No file manager opened the folder.' });
157
+ res.json({ ok: true });
158
+ });
159
+
160
+ return router;
161
+ }
@@ -17,6 +17,7 @@ import { tooLargeMessage } from './assistant-attachments.js';
17
17
  import { getSessionPolicy, requiresHumanApproval } from './remote-policy.js';
18
18
  import { clampPlanDispatch, PLAN_DISPATCH_NOTE, runCanChangeCode } from './assistant-plan-permissions.js';
19
19
  import { noStore, requireJson, requireLocal, requireLocalHost, sendGuardError } from './http-guards.js';
20
+ import { setUntrustedFileHeaders } from './untrusted-file-headers.js';
20
21
 
21
22
  /** The header whatsapp_send_file's caller proves it is the WhatsApp conversation's own brain with (assistant-runtime.js BRAIN_CREDENTIAL_RE). */
22
23
  export const BRAIN_CREDENTIAL_HEADER = 'x-synabun-brain-token';
@@ -150,6 +151,8 @@ export function createAssistantApi({
150
151
  // after this router), and the port the server listens on (the Host check of that route).
151
152
  whatsapp = null,
152
153
  port = 3344,
154
+ // The Admin sandbox setting (lib/agent-sandbox-setting.js): one global value for every agent SynaBun starts.
155
+ adminSandbox = null,
153
156
  } = {}) {
154
157
  if (!dispatcher) throw new Error('createAssistantApi requires a dispatcher');
155
158
  const router = Router();
@@ -299,7 +302,10 @@ export function createAssistantApi({
299
302
  // ── catalog ────────────────────────────────────────────────────────────────
300
303
  router.get('/catalog', async (req, res) => {
301
304
  try {
302
- await accountUsage?.refresh();
305
+ // Quotas are shown as last read and read again in the background (the probe can take seconds; the
306
+ // dispatcher and the router read them fresh before they choose an account).
307
+ accountUsage?.refresh()?.catch?.(() => {});
308
+ const usage = () => accountUsage?.list({ stale: true });
303
309
  // refresh=1 rebuilds from the providers' endpoints; deep=1 also makes them ask the CLI / app-server again.
304
310
  const deep = req.query.deep === '1';
305
311
  const force = req.query.refresh === '1' || deep;
@@ -318,8 +324,8 @@ export function createAssistantApi({
318
324
  // names none.
319
325
  const accounts = {
320
326
  ...(brief.accounts || {}),
321
- ...(claudeAccounts ? { 'claude-code': accountUsage?.list()?.['claude-code'] || claudeAccounts.listForClient() } : {}),
322
- ...(codexAccounts ? { codex: accountUsage?.list()?.codex || codexAccounts.listForClient() } : {}),
327
+ ...(claudeAccounts ? { 'claude-code': usage()?.['claude-code'] || claudeAccounts.listForClient() } : {}),
328
+ ...(codexAccounts ? { codex: usage()?.codex || codexAccounts.listForClient() } : {}),
323
329
  };
324
330
  return res.json({ ok: true, generatedAt: new Date(now()).toISOString(), ...brief, accounts, claudeWorkerAccount: claudeWorkerAccountId(), codexWorkerAccount: codexWorkerAccountId(), limits: dispatcher.limits, runs: dispatcher.list({ activeOnly: true }), totals: dispatcher.totals() });
325
331
  }
@@ -327,7 +333,7 @@ export function createAssistantApi({
327
333
  // within seconds), so it is asked every time: this answer is reused only while it hands back the
328
334
  // build the answer was made from (a rebuild or invalidate() gives another one).
329
335
  const base = typeof buildCatalog === 'function' ? await buildCatalog({ force, deep }) : NO_CATALOG;
330
- if (!force && catalogCache && catalogCache.base === base && now() - catalogCache.at < 30_000) return res.json(accountUsage ? { ...catalogCache.value, accounts: { ...catalogCache.value.accounts, ...accountUsage.list() } } : catalogCache.value);
336
+ if (!force && catalogCache && catalogCache.base === base && now() - catalogCache.at < 30_000) return res.json(accountUsage ? { ...catalogCache.value, accounts: { ...catalogCache.value.accounts, ...usage() } } : catalogCache.value);
331
337
  // Hidden rows stay server-side (view=manage lists them); the lists themselves ship.
332
338
  const { hiddenRows: _hiddenRows, ...visible } = base || {};
333
339
  const value = {
@@ -336,8 +342,8 @@ export function createAssistantApi({
336
342
  ...visible,
337
343
  accounts: {
338
344
  ...(base.accounts || {}),
339
- 'claude-code': accountUsage?.list()?.['claude-code'] || (claudeAccounts ? claudeAccounts.listForClient() : (base.accounts?.['claude-code'] || [])),
340
- codex: accountUsage?.list()?.codex || (codexAccounts ? codexAccounts.listForClient() : (base.accounts?.codex || [])),
345
+ 'claude-code': usage()?.['claude-code'] || (claudeAccounts ? claudeAccounts.listForClient() : (base.accounts?.['claude-code'] || [])),
346
+ codex: usage()?.codex || (codexAccounts ? codexAccounts.listForClient() : (base.accounts?.codex || [])),
341
347
  },
342
348
  claudeWorkerAccount: claudeWorkerAccountId(),
343
349
  codexWorkerAccount: codexWorkerAccountId(),
@@ -834,6 +840,8 @@ export function createAssistantApi({
834
840
  'Accept-Ranges': 'bytes', 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'private, max-age=3600',
835
841
  });
836
842
  if (range) res.set('Content-Range', `bytes ${start}-${end}/${size}`);
843
+ // A run's media is what a worker made: opened by its address it runs nothing, and an SVG is a download.
844
+ setUntrustedFileHeaders(res, file.path, file.mime);
837
845
  if (req.method === 'HEAD' || !size) { await handle.close().catch(() => {}); return res.end(); }
838
846
  const stream = handle.createReadStream({ start, end });
839
847
  stream.on('error', () => res.destroy());
@@ -994,9 +1002,17 @@ export function createAssistantApi({
994
1002
  if (!requireRuntime(res)) return;
995
1003
  try { res.json({ ok: true, session: await runtime.closeSession(req.params.id) }); } catch (error) { sendError(res, error); }
996
1004
  });
1005
+ // Remove (the Sessions menu): the record, its transcript and its usage journal go for good, after its
1006
+ // running turn and the workers it dispatched are stopped. Memories made from it stay.
997
1007
  router.delete('/sessions/:id', async (req, res) => {
998
1008
  if (!requireRuntime(res)) return;
999
- try { res.json({ ok: true, removed: await runtime.destroySession(req.params.id) }); } catch (error) { sendError(res, error); }
1009
+ try {
1010
+ if (runtime.removeSession) {
1011
+ const { removed, stoppedRuns } = await runtime.removeSession(req.params.id);
1012
+ return res.json({ ok: true, removed, stoppedRuns });
1013
+ }
1014
+ res.json({ ok: true, removed: await runtime.destroySession(req.params.id) });
1015
+ } catch (error) { sendError(res, error); }
1000
1016
  });
1001
1017
 
1002
1018
  // ── attachments ────────────────────────────────────────────────────────────
@@ -1230,6 +1246,23 @@ export function createAssistantApi({
1230
1246
  } catch (error) { sendError(res, error); }
1231
1247
  });
1232
1248
 
1249
+ // ── Admin sandbox (lib/agent-sandbox-setting.js) ───────────────────────────
1250
+ // Whether an agent SynaBun starts may use a sandbox whose setup needs Windows administrator
1251
+ // approval. Global, not per session. The user's setting: an agent cannot change it.
1252
+ router.get('/admin-sandbox', (req, res) => {
1253
+ if (!adminSandbox) return res.status(503).json({ ok: false, code: 'ADMIN_SANDBOX_UNAVAILABLE', error: 'The Admin sandbox setting is not available on this server' });
1254
+ res.json({ ok: true, ...adminSandbox.view() });
1255
+ });
1256
+ router.put('/admin-sandbox', requireJson, (req, res) => {
1257
+ if (!adminSandbox) return res.status(503).json({ ok: false, code: 'ADMIN_SANDBOX_UNAVAILABLE', error: 'The Admin sandbox setting is not available on this server' });
1258
+ if (callerContext(req).kind !== 'ui' || req.get('x-synabun-desktop-grant')) {
1259
+ return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Only the user can change the Admin sandbox.' });
1260
+ }
1261
+ const value = req.body?.adminSandbox;
1262
+ if (typeof value !== 'boolean') return res.status(400).json({ ok: false, code: 'INVALID_VALUE', error: 'adminSandbox must be true or false.' });
1263
+ try { res.json({ ok: true, ...adminSandbox.set(value) }); } catch (error) { sendError(res, error); }
1264
+ });
1265
+
1233
1266
  // ── sidepanel tab registry (UI heartbeat) ──────────────────────────────────
1234
1267
  function listTabs() {
1235
1268
  const cutoff = now() - tabTtlMs;
@@ -36,6 +36,12 @@ export function opencodeImageParts(images = []) {
36
36
 
37
37
  /** The route-gate plugin (opencode-route-gate.js) as a file URL the serve can load. */
38
38
  export const OPENCODE_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode-route-gate.js')).href;
39
+ /**
40
+ * The same gate for OpenCode 2 (opencode2-route-gate/: OpenCode 2 loads a plugin
41
+ * folder from the config's `plugins` list). Both entries are written: OpenCode 1
42
+ * ignores `plugins`, OpenCode 2 skips the 1.x file (a warning in its log).
43
+ */
44
+ export const OPENCODE2_ROUTE_GATE_PLUGIN = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'opencode2-route-gate')).href;
39
45
 
40
46
  /**
41
47
  * SynaBun's plan agent. OpenCode adds its own read-only reminder ("commands may
@@ -96,9 +102,15 @@ export function patchOpenCodeAssistantConfig(xdgRoot, { persona = '', role = 'as
96
102
  // read-only classifier decides on the whole line.
97
103
  config.agent.plan = { ...(config.agent.plan || {}), permission: { ...(config.agent.plan?.permission || {}), bash: 'ask' } };
98
104
  const plugins = (Array.isArray(config.plugin) ? config.plugin : []).filter((entry) => (Array.isArray(entry) ? entry[0] : entry) !== OPENCODE_ROUTE_GATE_PLUGIN);
105
+ const plugins2 = (Array.isArray(config.plugins) ? config.plugins : []).filter((entry) => (entry && typeof entry === 'object' ? entry.package : entry) !== OPENCODE2_ROUTE_GATE_PLUGIN);
99
106
  // `remote`: a WhatsApp session's serve — the plugin refuses a call it could not check (fail closed).
100
- if (routeGate?.url && routeGate?.session && routeGate?.token) plugins.push([OPENCODE_ROUTE_GATE_PLUGIN, { url: routeGate.url, session: routeGate.session, token: routeGate.token, ...(routeGate.remote === true ? { remote: true } : {}) }]);
107
+ if (routeGate?.url && routeGate?.session && routeGate?.token) {
108
+ const options = { url: routeGate.url, session: routeGate.session, token: routeGate.token, ...(routeGate.remote === true ? { remote: true } : {}) };
109
+ plugins.push([OPENCODE_ROUTE_GATE_PLUGIN, options]);
110
+ plugins2.push({ package: OPENCODE2_ROUTE_GATE_PLUGIN, options });
111
+ }
101
112
  if (plugins.length) config.plugin = plugins; else delete config.plugin;
113
+ if (plugins2.length) config.plugins = plugins2; else delete config.plugins;
102
114
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
103
115
  // It carries this brain's grants (the desktop grant): readable by this user only.
104
116
  try { chmodSync(configPath, 0o600); } catch { /* a filesystem without modes */ }
@@ -0,0 +1,44 @@
1
+ // SynaBun — an Assistant task run's browser policy as an OpenCode 2 plugin.
2
+ // The same policy as ../opencode-browser-policy.js (read that file for what it
3
+ // does and why), for OpenCode 2's plugin API: a directory OpenCode loads from
4
+ // the config's `plugins` entry, whose default export is `{ id, setup(context) }`
5
+ // with a hook on `tool.execute.before`; a refusal is thrown. OpenCode 2 calls
6
+ // the shell tool `shell` and the sub-agent tool `subagent`: the policy, written
7
+ // for OpenCode 1's names, is asked with `bash` and `task`.
8
+
9
+ // An absolute file URL: the policy loads from SynaBun's lib/ wherever OpenCode imports this file from.
10
+ const POLICY_URL = new URL('../../browser-tool-policy.js', import.meta.url).href;
11
+ const V1_NAMES = { shell: 'bash', subagent: 'task' };
12
+
13
+ // OpenCode's own tools that run no command and fetch no page (OpenCode 1's names).
14
+ const OWN_TOOLS = new Set([
15
+ 'read', 'list', 'glob', 'grep', 'edit', 'write', 'patch', 'multiedit', 'apply_patch', 'todowrite', 'todoread',
16
+ 'task', 'skill', 'lsp', 'codesearch', 'question', 'batch', 'invalid', 'plan_exit', 'plan_enter',
17
+ ]);
18
+
19
+ export default {
20
+ id: 'synabun.browser-policy',
21
+ async setup(context) {
22
+ const options = context?.options || {};
23
+ let policy = null;
24
+ let loadError = null;
25
+ try { policy = await import(POLICY_URL); } catch (error) { loadError = error; }
26
+ const cwd = (typeof options.cwd === 'string' && options.cwd) || context?.location?.directory || process.cwd();
27
+ if (typeof policy?.browserToolDenial !== 'function') {
28
+ const why = loadError?.message || 'browserToolDenial is missing';
29
+ console.error(`[SynaBun] browser policy could not load from ${POLICY_URL} (${why}); refusing bash, webfetch, websearch and other servers' tools for this run`);
30
+ await context.tool.hook('execute.before', async (call) => {
31
+ const raw = String(call?.tool || '');
32
+ const tool = V1_NAMES[raw] || raw;
33
+ if (/^synabun_/i.test(tool) || OWN_TOOLS.has(tool.toLowerCase())) return;
34
+ throw new Error(`SynaBun browser policy: ${tool} was refused — SynaBun's browser policy could not load (${why}), so no command, web tool or other server's tool can be checked for a browser launch. SynaBun's own tools (SynaBun_*) and file tools still work. Stop and report this.`);
35
+ });
36
+ return;
37
+ }
38
+ await context.tool.hook('execute.before', async (call) => {
39
+ const raw = String(call?.tool || '');
40
+ const reason = policy.browserToolDenial(V1_NAMES[raw] || raw, call?.input || {}, { host: 'opencode', cwd });
41
+ if (reason) throw new Error(reason);
42
+ });
43
+ },
44
+ };
@@ -0,0 +1,57 @@
1
+ // SynaBun — the Assistant's route gate as an OpenCode 2 plugin.
2
+ // The same gate as ../opencode-route-gate.js (read that file for what it does
3
+ // and why), for OpenCode 2's plugin API: a directory OpenCode loads from the
4
+ // config's `plugins` entry ({ package: <this folder as a file URL>, options }),
5
+ // whose default export is `{ id, setup(context) }`; a hook on
6
+ // `tool.execute.before` sees `{ tool, sessionID, input }` and refuses a call by
7
+ // throwing (OpenCode records the message as that tool's error). OpenCode 2
8
+ // calls two tools by other names than OpenCode 1 (`shell` for bash,
9
+ // `subagent` for task): the gate is asked with OpenCode 1's names, which the
10
+ // rest of SynaBun knows. OpenCode 1 never reads this folder (it loads the
11
+ // `plugin` entry), and OpenCode 2 never loads the 1.x file.
12
+
13
+ const MAX_INPUT_BYTES = 256 * 1024;
14
+ const UNCHECKED = 'SynaBun could not check this call, so a WhatsApp session does not run it.';
15
+ const V1_NAMES = { shell: 'bash', subagent: 'task' };
16
+
17
+ /** { input, inputComplete }: the whole arguments when small enough to send, else their command/action. */
18
+ function callArguments(args) {
19
+ if (!args || typeof args !== 'object' || Array.isArray(args)) return { input: {}, inputComplete: false };
20
+ let size = Infinity;
21
+ try { size = JSON.stringify(args).length; } catch {}
22
+ if (size <= MAX_INPUT_BYTES) return { input: args, inputComplete: true };
23
+ const subset = {};
24
+ for (const key of ['command', 'action']) {
25
+ if (typeof args[key] === 'string' && args[key].length <= 64 * 1024) subset[key] = args[key];
26
+ }
27
+ return { input: subset, inputComplete: false };
28
+ }
29
+
30
+ export default {
31
+ id: 'synabun.route-gate',
32
+ async setup(context) {
33
+ const { url, session, token, remote = false } = context?.options || {};
34
+ if (!url || !session || !token) return;
35
+ const post = (body) => fetch(url, {
36
+ method: 'POST',
37
+ headers: { 'content-type': 'application/json' },
38
+ body: JSON.stringify({ session, token, host: 'opencode', ...body }),
39
+ signal: AbortSignal.timeout(5000),
40
+ });
41
+ // Never blocks loading; a lost hello only keeps the reactive fallback on.
42
+ post({ hello: true }).catch(() => {});
43
+ await context.tool.hook('execute.before', async (call) => {
44
+ const name = String(call?.tool || '');
45
+ let decision = null;
46
+ try {
47
+ const response = await post({ tool: V1_NAMES[name] || name, providerSessionId: call?.sessionID || null, ...callArguments(call?.input) });
48
+ decision = await response.json();
49
+ } catch {
50
+ if (remote === true) throw new Error(UNCHECKED);
51
+ return;
52
+ }
53
+ if (decision && decision.allow === false) throw new Error(String(decision.reason || 'SynaBun route gate: call agent_route first.'));
54
+ if (remote === true && !(decision && decision.allow === true)) throw new Error(UNCHECKED);
55
+ });
56
+ },
57
+ };
@@ -605,6 +605,8 @@ export function compactRow(row) {
605
605
  * @param {(change:{providers:string[], generatedAt:string})=>void} [deps.onChange] a build listed other
606
606
  * models than the build before it (hiding has its own event and does not count)
607
607
  * @param {number} [deps.retryMs] how soon a build is redone while Claude Code or Codex gave no verified list
608
+ * @param {boolean} [deps.staleWhileRevalidate] an old build (past its lifetime) is given at once and built
609
+ * again in the background, one build at a time; only a first build, invalidate() and force wait for one
608
610
  */
609
611
  export function createAssistantCatalog({
610
612
  fetchJson,
@@ -617,6 +619,7 @@ export function createAssistantCatalog({
617
619
  onChange = null,
618
620
  ttlMs = 30_000,
619
621
  retryMs = 5_000,
622
+ staleWhileRevalidate = false,
620
623
  now = Date.now,
621
624
  log = () => {},
622
625
  } = {}) {
@@ -753,7 +756,17 @@ export function createAssistantCatalog({
753
756
  */
754
757
  async function full({ force = false, deep = false } = {}) {
755
758
  if (!force && !deep && cache && now() - cache.at < cache.ttl) return cache.value;
759
+ // Past its lifetime: with staleWhileRevalidate the old build is the answer while the next one is made
760
+ // (never a build that began before an invalidate(): `current` says it was made for the lists as they are).
761
+ if (staleWhileRevalidate && !force && !deep && cache?.current) {
762
+ if (!inflight) startBuild({ deep: false }).catch((error) => log('catalog:background-error', error?.message || String(error)));
763
+ return cache.value;
764
+ }
756
765
  if (inflight) return inflight;
766
+ return startBuild({ deep });
767
+ }
768
+
769
+ function startBuild({ deep = false } = {}) {
757
770
  const started = epoch;
758
771
  inflight = build({ deep }).then((built) => {
759
772
  const { changedProviders, ...value } = built;
@@ -762,7 +775,7 @@ export function createAssistantCatalog({
762
775
  const degraded = ['claude-code', 'codex'].some((p) => value.sources[p] !== 'live');
763
776
  degradedBuilds = degraded ? degradedBuilds + 1 : 0;
764
777
  const ttl = degraded ? Math.min(ttlMs, retryMs * 2 ** (degradedBuilds - 1)) : ttlMs;
765
- cache = { at: now(), value, ttl: started === epoch ? ttl : 0 };
778
+ cache = { at: now(), value, ttl: started === epoch ? ttl : 0, current: started === epoch };
766
779
  if (changedProviders.length && typeof onChange === 'function') {
767
780
  try { onChange({ providers: changedProviders, generatedAt: value.generatedAt }); } catch (error) { log('catalog:onchange-error', error?.message || String(error)); }
768
781
  }
@@ -872,6 +885,8 @@ export function createAssistantCatalog({
872
885
  }
873
886
 
874
887
  function invalidate() { cache = null; epoch += 1; }
888
+ /** The lists may have changed (a serve restarted, a CLI updated): the next read still answers with this build and makes the next one. */
889
+ function markStale() { if (cache) cache = { ...cache, at: -Infinity }; }
875
890
  /** A provider's own list changed: drop the copy and build the next one now (onChange reports the difference). */
876
891
  function refresh() {
877
892
  invalidate();
@@ -879,5 +894,5 @@ export function createAssistantCatalog({
879
894
  return pending ? pending.catch(() => null).then(() => full()) : full();
880
895
  }
881
896
 
882
- return { get, full, peek, find, hiddenId, manageRows, manageSnapshot, brainInfo, invalidate, refresh };
897
+ return { get, full, peek, find, hiddenId, manageRows, manageSnapshot, brainInfo, invalidate, markStale, refresh };
883
898
  }
@@ -1894,6 +1894,9 @@ export function createAssistantDispatcher({
1894
1894
  liveState.turnTokens = null;
1895
1895
  entry.turns = [...(entry.turns || []), turn].slice(-50);
1896
1896
  entry.lastResult = parsed;
1897
+ // The run's own descriptor (lib/native-loop-runtime.js) carries it too, for the surfaces that read that one
1898
+ // (the OpenCode panel's pill): a warm worker whose turn ended asking waits on the user, it does not rest.
1899
+ ctx.setOutcome?.(parsed.status || null);
1897
1900
  entry.lastText = text.slice(0, 20_000);
1898
1901
  // The turn has ended: its result is in. Idle time from here on is not work.
1899
1902
  pauseWork(entry, liveState);
@@ -449,7 +449,7 @@ export function opencodeControlResponse(pending, response = {}) {
449
449
  export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd = () => {}, onUsageEvent = () => {}, brain = {}, planTurn = () => false } = {}) {
450
450
  // costUsd is the session's running total: OpenCode reports cost per assistant
451
451
  // message, so costs are summed by message id (the runtime takes deltas).
452
- const state = { sessionId, roles: new Map(), order: [], textParts: new Map(), reasoningParts: new Map(), tools: new Map(), started: false, announcedInit: false, lastText: '', costUsd: 0, costByMessage: new Map(), usage: null };
452
+ const state = { sessionId, roles: new Map(), order: [], textParts: new Map(), reasoningParts: new Map(), partKinds: new Map(), tools: new Map(), started: false, announcedInit: false, lastText: '', costUsd: 0, costByMessage: new Map(), usage: null };
453
453
  const event = (payload) => emit({ type: 'event', event: payload });
454
454
  const belongs = (ev) => {
455
455
  const sid = ev?.sessionID || ev?.sessionId || ev?.part?.sessionID || ev?.info?.sessionID || ev?.info?.id || ev?.session?.id || null;
@@ -485,12 +485,15 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
485
485
  function handlePart(part = {}) {
486
486
  if (!part.messageID) return;
487
487
  if (state.roles.get(part.messageID) === 'user') return;
488
+ if ((part.type === 'text' || part.type === 'reasoning') && part.id) state.partKinds.set(part.id, { messageID: part.messageID, type: part.type });
488
489
  if (part.type === 'text' && typeof part.text === 'string') {
489
490
  let parts = state.textParts.get(part.messageID);
490
491
  if (!parts) { parts = new Map(); state.textParts.set(part.messageID, parts); }
491
492
  const key = part.id || `${parts.size}`;
492
493
  const prev = parts.get(key) || '';
493
494
  const next = part.text;
495
+ // An update made before the deltas that already arrived streams nothing again.
496
+ if (prev.length > next.length && prev.startsWith(next)) return;
494
497
  parts.set(key, next);
495
498
  const delta = next.startsWith(prev) ? next.slice(prev.length) : next;
496
499
  const shown = delta && planTurn() === true ? planTextDelta(`${part.messageID}:${key}`, delta) : delta;
@@ -500,6 +503,7 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
500
503
  if (part.type === 'reasoning' && typeof part.text === 'string') {
501
504
  const key = `${part.messageID}:${part.id || 'r'}`;
502
505
  const prev = state.reasoningParts.get(key) || '';
506
+ if (prev.length > part.text.length && prev.startsWith(part.text)) return;
503
507
  state.reasoningParts.set(key, part.text);
504
508
  const delta = part.text.startsWith(prev) ? part.text.slice(prev.length) : part.text;
505
509
  if (delta) { startIfNeeded(); event(thinkingDelta(delta)); }
@@ -521,6 +525,19 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
521
525
  }
522
526
  }
523
527
  }
528
+ // A part's text arrives as `message.part.delta` between the update that announces the part and the one that
529
+ // ends it (OpenCode 2 streams only this way, through lib/opencode2-translate.js): each delta extends that part,
530
+ // and streams as an update would. A delta for a part not announced yet is left to the update that ends it,
531
+ // which carries the whole text.
532
+ function handleDelta(ev = {}) {
533
+ if ((ev.field && ev.field !== 'text') || typeof ev.delta !== 'string' || !ev.delta) return;
534
+ const known = state.partKinds.get(ev.partID);
535
+ if (!known || (ev.messageID && ev.messageID !== known.messageID)) return;
536
+ const prev = known.type === 'text'
537
+ ? state.textParts.get(known.messageID)?.get(ev.partID) || ''
538
+ : state.reasoningParts.get(`${known.messageID}:${ev.partID}`) || '';
539
+ handlePart({ id: ev.partID, messageID: known.messageID, type: known.type, text: `${prev}${ev.delta}` });
540
+ }
524
541
  return {
525
542
  state,
526
543
  handle(eventType, ev = {}) {
@@ -546,6 +563,7 @@ export function createOpenCodeEnvelopeTranslator({ sessionId, emit, onTurnEnd =
546
563
  return true;
547
564
  }
548
565
  if (/^message[.:]part[.:]updated$/i.test(eventType)) { handlePart(ev.part || {}); return true; }
566
+ if (/^message[.:]part[.:]delta$/i.test(eventType)) { handleDelta(ev); return true; }
549
567
  if (/^session[.:]idle$/i.test(eventType)) {
550
568
  if (state.errored) { state.errored = false; state.started = false; return true; } // error already closed this turn
551
569
  const said = latestAssistantText();
@@ -19,6 +19,7 @@ import { mkdirSync } from 'node:fs';
19
19
  import { tmpdir } from 'node:os';
20
20
  import { dirname, resolve, sep } from 'node:path';
21
21
  import { fileURLToPath } from 'node:url';
22
+ import { CODEX_PROBE_SANDBOX_ARGS } from './agent-sandbox-elevation.js';
22
23
 
23
24
  export const CODEX_GATE_HOOK_PATH = resolve(dirname(fileURLToPath(import.meta.url)), 'assistant-brains', 'codex-route-gate-hook.mjs');
24
25
  export const CODEX_BROWSER_POLICY_HOOK_PATH = resolve(dirname(fileURLToPath(import.meta.url)), 'assistant-brains', 'codex-browser-policy-hook.mjs');
@@ -80,7 +81,8 @@ function probe({ codexBin, command, timeoutMs, spawnImpl }) {
80
81
  if (codexNeedsShell(bin)) { done(null); return; }
81
82
  const home = resolve(tmpdir(), 'synabun-codex-gate-probe');
82
83
  try { mkdirSync(home, { recursive: true }); } catch {}
83
- const args = [...codexGateHookFlags(command), 'app-server'];
84
+ // A probe runs no command: the Windows sandbox that needs no administrator (lib/agent-sandbox-elevation.js).
85
+ const args = [...codexGateHookFlags(command), ...CODEX_PROBE_SANDBOX_ARGS, 'app-server'];
84
86
  if (/\.js$/i.test(bin)) { args.unshift(bin); bin = process.execPath; }
85
87
  let proc;
86
88
  try {