synabun 2.0.7 → 2.0.9

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 (346) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +1 -1
  3. package/hooks/claude-code/post-plan.mjs +6 -30
  4. package/hooks/claude-code/session-start.mjs +1 -4
  5. package/hooks/claude-code/shared.mjs +106 -72
  6. package/lib/package-processes.js +670 -0
  7. package/lib/runtime-dependencies.js +225 -0
  8. package/mcp-server/dist/services/browser-bench.d.ts +1 -1
  9. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
  10. package/mcp-server/dist/services/browser-bench.js +1 -1
  11. package/mcp-server/dist/services/browser-bench.js.map +1 -1
  12. package/mcp-server/dist/services/categories.d.ts +35 -2
  13. package/mcp-server/dist/services/categories.d.ts.map +1 -1
  14. package/mcp-server/dist/services/categories.js +77 -33
  15. package/mcp-server/dist/services/categories.js.map +1 -1
  16. package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
  17. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
  18. package/mcp-server/dist/services/desktop-bench.js +1 -1
  19. package/mcp-server/dist/services/desktop-bench.js.map +1 -1
  20. package/mcp-server/dist/tools/agents.d.ts +1 -1
  21. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  22. package/mcp-server/dist/tools/agents.js +1 -1
  23. package/mcp-server/dist/tools/agents.js.map +1 -1
  24. package/mcp-server/dist/tools/bluesky.js +1 -1
  25. package/mcp-server/dist/tools/bluesky.js.map +1 -1
  26. package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
  27. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
  28. package/mcp-server/dist/tools/browser-advanced.js +1 -1
  29. package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
  30. package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
  31. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
  32. package/mcp-server/dist/tools/browser-batch.js +1 -1
  33. package/mcp-server/dist/tools/browser-batch.js.map +1 -1
  34. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
  35. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
  36. package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
  37. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
  38. package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
  39. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
  40. package/mcp-server/dist/tools/browser-interact.js +1 -1
  41. package/mcp-server/dist/tools/browser-interact.js.map +1 -1
  42. package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
  43. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
  44. package/mcp-server/dist/tools/browser-navigate.js +1 -1
  45. package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
  46. package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
  47. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
  48. package/mcp-server/dist/tools/browser-observe.js +1 -1
  49. package/mcp-server/dist/tools/browser-observe.js.map +1 -1
  50. package/mcp-server/dist/tools/card-tools.d.ts +1 -1
  51. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
  52. package/mcp-server/dist/tools/card-tools.js +1 -1
  53. package/mcp-server/dist/tools/card-tools.js.map +1 -1
  54. package/mcp-server/dist/tools/category.d.ts +1 -1
  55. package/mcp-server/dist/tools/category.d.ts.map +1 -1
  56. package/mcp-server/dist/tools/category.js +5 -1
  57. package/mcp-server/dist/tools/category.js.map +1 -1
  58. package/mcp-server/dist/tools/choice.d.ts +1 -1
  59. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  60. package/mcp-server/dist/tools/choice.js +1 -1
  61. package/mcp-server/dist/tools/choice.js.map +1 -1
  62. package/mcp-server/dist/tools/computer.d.ts +1 -1
  63. package/mcp-server/dist/tools/computer.d.ts.map +1 -1
  64. package/mcp-server/dist/tools/computer.js +1 -1
  65. package/mcp-server/dist/tools/computer.js.map +1 -1
  66. package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
  67. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
  68. package/mcp-server/dist/tools/discord-channel.js +1 -1
  69. package/mcp-server/dist/tools/discord-channel.js.map +1 -1
  70. package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
  71. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
  72. package/mcp-server/dist/tools/discord-guild.js +1 -1
  73. package/mcp-server/dist/tools/discord-guild.js.map +1 -1
  74. package/mcp-server/dist/tools/discord-member.d.ts +1 -1
  75. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
  76. package/mcp-server/dist/tools/discord-member.js +1 -1
  77. package/mcp-server/dist/tools/discord-member.js.map +1 -1
  78. package/mcp-server/dist/tools/discord-message.d.ts +1 -1
  79. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
  80. package/mcp-server/dist/tools/discord-message.js +1 -1
  81. package/mcp-server/dist/tools/discord-message.js.map +1 -1
  82. package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
  83. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
  84. package/mcp-server/dist/tools/discord-onboarding.js +1 -1
  85. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
  86. package/mcp-server/dist/tools/discord-role.d.ts +1 -1
  87. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
  88. package/mcp-server/dist/tools/discord-role.js +1 -1
  89. package/mcp-server/dist/tools/discord-role.js.map +1 -1
  90. package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
  91. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/discord-thread.js +1 -1
  93. package/mcp-server/dist/tools/discord-thread.js.map +1 -1
  94. package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
  95. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/discord-webhook.js +1 -1
  97. package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
  98. package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
  99. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
  100. package/mcp-server/dist/tools/fb-groups.js +1 -1
  101. package/mcp-server/dist/tools/fb-groups.js.map +1 -1
  102. package/mcp-server/dist/tools/forget.d.ts +1 -1
  103. package/mcp-server/dist/tools/forget.d.ts.map +1 -1
  104. package/mcp-server/dist/tools/forget.js +1 -1
  105. package/mcp-server/dist/tools/forget.js.map +1 -1
  106. package/mcp-server/dist/tools/git-tools.d.ts +1 -1
  107. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
  108. package/mcp-server/dist/tools/git-tools.js +1 -1
  109. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  110. package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
  111. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
  112. package/mcp-server/dist/tools/gsc/experience.js +1 -1
  113. package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
  114. package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
  115. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
  116. package/mcp-server/dist/tools/gsc/indexing.js +1 -1
  117. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
  118. package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
  119. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
  120. package/mcp-server/dist/tools/gsc/inspect.js +1 -1
  121. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
  122. package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
  123. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
  124. package/mcp-server/dist/tools/gsc/navigate.js +1 -1
  125. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
  126. package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
  127. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
  128. package/mcp-server/dist/tools/gsc/performance.js +1 -1
  129. package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
  130. package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
  131. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
  132. package/mcp-server/dist/tools/gsc/settings.js +1 -1
  133. package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
  134. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  135. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
  136. package/mcp-server/dist/tools/image-tools.js +1 -1
  137. package/mcp-server/dist/tools/image-tools.js.map +1 -1
  138. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
  139. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
  140. package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
  141. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
  142. package/mcp-server/dist/tools/loop.d.ts +1 -1
  143. package/mcp-server/dist/tools/loop.d.ts.map +1 -1
  144. package/mcp-server/dist/tools/loop.js +1 -1
  145. package/mcp-server/dist/tools/loop.js.map +1 -1
  146. package/mcp-server/dist/tools/memories.d.ts +1 -1
  147. package/mcp-server/dist/tools/memories.d.ts.map +1 -1
  148. package/mcp-server/dist/tools/memories.js +1 -1
  149. package/mcp-server/dist/tools/memories.js.map +1 -1
  150. package/mcp-server/dist/tools/morelogin.d.ts +1 -1
  151. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  152. package/mcp-server/dist/tools/morelogin.js +1 -1
  153. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  154. package/mcp-server/dist/tools/profile.d.ts +1 -1
  155. package/mcp-server/dist/tools/profile.d.ts.map +1 -1
  156. package/mcp-server/dist/tools/profile.js +1 -1
  157. package/mcp-server/dist/tools/profile.js.map +1 -1
  158. package/mcp-server/dist/tools/recall.d.ts +1 -1
  159. package/mcp-server/dist/tools/recall.d.ts.map +1 -1
  160. package/mcp-server/dist/tools/recall.js +1 -1
  161. package/mcp-server/dist/tools/recall.js.map +1 -1
  162. package/mcp-server/dist/tools/reflect.d.ts +1 -1
  163. package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
  164. package/mcp-server/dist/tools/reflect.js +1 -1
  165. package/mcp-server/dist/tools/reflect.js.map +1 -1
  166. package/mcp-server/dist/tools/remember.d.ts +1 -1
  167. package/mcp-server/dist/tools/remember.d.ts.map +1 -1
  168. package/mcp-server/dist/tools/remember.js +13 -11
  169. package/mcp-server/dist/tools/remember.js.map +1 -1
  170. package/mcp-server/dist/tools/restore.d.ts +1 -1
  171. package/mcp-server/dist/tools/restore.d.ts.map +1 -1
  172. package/mcp-server/dist/tools/restore.js +1 -1
  173. package/mcp-server/dist/tools/restore.js.map +1 -1
  174. package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
  175. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
  176. package/mcp-server/dist/tools/style-guide-tools.js +1 -1
  177. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
  178. package/mcp-server/dist/tools/sync.d.ts +1 -1
  179. package/mcp-server/dist/tools/sync.d.ts.map +1 -1
  180. package/mcp-server/dist/tools/sync.js +1 -1
  181. package/mcp-server/dist/tools/sync.js.map +1 -1
  182. package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
  183. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
  184. package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
  185. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
  186. package/mcp-server/dist/tools/utils.d.ts +1 -1
  187. package/mcp-server/dist/tools/utils.d.ts.map +1 -1
  188. package/mcp-server/dist/tools/utils.js +1 -1
  189. package/mcp-server/dist/tools/utils.js.map +1 -1
  190. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +1 -1
  191. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -1
  192. package/mcp-server/dist/tools/whatsapp-send-file.js +1 -1
  193. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -1
  194. package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
  195. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  196. package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
  197. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  198. package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
  199. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
  200. package/mcp-server/dist/tools/youtube/analytics.js +1 -1
  201. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
  202. package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
  203. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
  204. package/mcp-server/dist/tools/youtube/download.js +1 -1
  205. package/mcp-server/dist/tools/youtube/download.js.map +1 -1
  206. package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
  207. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
  208. package/mcp-server/dist/tools/youtube/media.js +1 -1
  209. package/mcp-server/dist/tools/youtube/media.js.map +1 -1
  210. package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
  211. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
  212. package/mcp-server/dist/tools/youtube/metadata.js +1 -1
  213. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
  214. package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
  215. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
  216. package/mcp-server/dist/tools/youtube/navigate.js +1 -1
  217. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
  218. package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
  219. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
  220. package/mcp-server/dist/tools/youtube/playlist.js +1 -1
  221. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
  222. package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
  223. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
  224. package/mcp-server/dist/tools/youtube/schedule.js +1 -1
  225. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
  226. package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
  227. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
  228. package/mcp-server/dist/tools/youtube/source.js +1 -1
  229. package/mcp-server/dist/tools/youtube/source.js.map +1 -1
  230. package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
  231. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
  232. package/mcp-server/dist/tools/youtube/upload.js +4 -9
  233. package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
  234. package/mcp-server/dist/tui.js +16 -4
  235. package/mcp-server/dist/tui.js.map +1 -1
  236. package/mcp-server/package-lock.json +3 -3
  237. package/mcp-server/package.json +3 -3
  238. package/mcp-server/src/services/browser-bench.ts +1 -1
  239. package/mcp-server/src/services/categories.ts +77 -32
  240. package/mcp-server/src/services/desktop-bench.ts +1 -1
  241. package/mcp-server/src/tools/agents.ts +1 -1
  242. package/mcp-server/src/tools/bluesky.ts +1 -1
  243. package/mcp-server/src/tools/browser-advanced.ts +1 -1
  244. package/mcp-server/src/tools/browser-batch.ts +1 -1
  245. package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
  246. package/mcp-server/src/tools/browser-interact.ts +1 -1
  247. package/mcp-server/src/tools/browser-navigate.ts +1 -1
  248. package/mcp-server/src/tools/browser-observe.ts +1 -1
  249. package/mcp-server/src/tools/card-tools.ts +1 -1
  250. package/mcp-server/src/tools/category.ts +6 -1
  251. package/mcp-server/src/tools/choice.ts +1 -1
  252. package/mcp-server/src/tools/computer.ts +1 -1
  253. package/mcp-server/src/tools/discord-channel.ts +1 -1
  254. package/mcp-server/src/tools/discord-guild.ts +1 -1
  255. package/mcp-server/src/tools/discord-member.ts +1 -1
  256. package/mcp-server/src/tools/discord-message.ts +1 -1
  257. package/mcp-server/src/tools/discord-onboarding.ts +1 -1
  258. package/mcp-server/src/tools/discord-role.ts +1 -1
  259. package/mcp-server/src/tools/discord-thread.ts +1 -1
  260. package/mcp-server/src/tools/discord-webhook.ts +1 -1
  261. package/mcp-server/src/tools/fb-groups.ts +1 -1
  262. package/mcp-server/src/tools/forget.ts +1 -1
  263. package/mcp-server/src/tools/git-tools.ts +1 -1
  264. package/mcp-server/src/tools/gsc/experience.ts +1 -1
  265. package/mcp-server/src/tools/gsc/indexing.ts +1 -1
  266. package/mcp-server/src/tools/gsc/inspect.ts +1 -1
  267. package/mcp-server/src/tools/gsc/navigate.ts +1 -1
  268. package/mcp-server/src/tools/gsc/performance.ts +1 -1
  269. package/mcp-server/src/tools/gsc/settings.ts +1 -1
  270. package/mcp-server/src/tools/image-tools.ts +1 -1
  271. package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
  272. package/mcp-server/src/tools/loop.ts +1 -1
  273. package/mcp-server/src/tools/memories.ts +1 -1
  274. package/mcp-server/src/tools/morelogin.ts +1 -1
  275. package/mcp-server/src/tools/profile.ts +1 -1
  276. package/mcp-server/src/tools/recall.ts +1 -1
  277. package/mcp-server/src/tools/reflect.ts +1 -1
  278. package/mcp-server/src/tools/remember.ts +13 -12
  279. package/mcp-server/src/tools/restore.ts +1 -1
  280. package/mcp-server/src/tools/style-guide-tools.ts +1 -1
  281. package/mcp-server/src/tools/sync.ts +1 -1
  282. package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
  283. package/mcp-server/src/tools/utils.ts +1 -1
  284. package/mcp-server/src/tools/whatsapp-send-file.ts +1 -1
  285. package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
  286. package/mcp-server/src/tools/youtube/analytics.ts +1 -1
  287. package/mcp-server/src/tools/youtube/download.ts +1 -1
  288. package/mcp-server/src/tools/youtube/media.ts +1 -1
  289. package/mcp-server/src/tools/youtube/metadata.ts +1 -1
  290. package/mcp-server/src/tools/youtube/navigate.ts +1 -1
  291. package/mcp-server/src/tools/youtube/playlist.ts +1 -1
  292. package/mcp-server/src/tools/youtube/schedule.ts +1 -1
  293. package/mcp-server/src/tools/youtube/source.ts +1 -1
  294. package/mcp-server/src/tools/youtube/upload.ts +4 -12
  295. package/mcp-server/src/tui.ts +22 -9
  296. package/neural-interface/i18n/en.json +18 -0
  297. package/neural-interface/i18n/pt-BR.json +18 -0
  298. package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
  299. package/neural-interface/lib/agent-sandbox-setting.js +57 -0
  300. package/neural-interface/lib/assistant-api.js +19 -0
  301. package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
  302. package/neural-interface/lib/assistant-runtime.js +5 -1
  303. package/neural-interface/lib/codex-account-auth.js +4 -2
  304. package/neural-interface/lib/codex-accounts.js +14 -1
  305. package/neural-interface/lib/codex-settings-schema.js +5 -3
  306. package/neural-interface/lib/exec-loop-command.js +3 -1
  307. package/neural-interface/lib/install-details.js +23 -5
  308. package/neural-interface/lib/native-loop-providers.js +6 -1
  309. package/neural-interface/lib/provider-panel-terminal-auth.js +3 -2
  310. package/neural-interface/lib/session-indexer.js +3 -1
  311. package/neural-interface/lib/session-title-generator.js +2 -0
  312. package/neural-interface/package-lock.json +2 -2
  313. package/neural-interface/package.json +1 -1
  314. package/neural-interface/public/shared/account-menu.js +4 -1
  315. package/neural-interface/public/shared/account-usage.js +62 -2
  316. package/neural-interface/public/shared/account-view.js +2 -1
  317. package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
  318. package/neural-interface/public/shared/assistant/asst-api.js +7 -0
  319. package/neural-interface/public/shared/assistant/asst-brain-picker.js +232 -75
  320. package/neural-interface/public/shared/assistant/asst-menu.js +204 -77
  321. package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
  322. package/neural-interface/public/shared/assistant/asst-panel.js +39 -2
  323. package/neural-interface/public/shared/assistant/asst-styles.js +66 -17
  324. package/neural-interface/public/shared/cdx/cdx-panel.js +1 -1
  325. package/neural-interface/public/shared/cdx/cdx-styles.js +45 -5
  326. package/neural-interface/public/shared/cdx/cdx-tabs.js +3 -0
  327. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
  328. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +4 -1
  329. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +51 -2
  330. package/neural-interface/public/shared/showcase-privacy.js +2 -1
  331. package/neural-interface/public/shared/styles.css +3 -1
  332. package/neural-interface/public/shared/ui-claude-panel.js +50 -4
  333. package/neural-interface/public/shared/ui-explorer.js +2 -0
  334. package/neural-interface/public/shared/ui-sync.js +2 -0
  335. package/neural-interface/public/shared/ui-update.js +4 -2
  336. package/neural-interface/scripts/rebuild-pty.js +14 -2
  337. package/neural-interface/server.js +180 -86
  338. package/npm-shrinkwrap.json +4424 -0
  339. package/package.json +54 -2
  340. package/postinstall.js +61 -47
  341. package/preuninstall.js +8 -35
  342. package/setup.js +76 -44
  343. package/skills/synabun/modules/idea.md +1 -1
  344. package/skills/synabun/modules/schedule.md +1 -1
  345. package/updater.cmd +2 -2
  346. package/updater.mjs +97 -3
@@ -14,12 +14,12 @@
14
14
  * look successful).
15
15
  */
16
16
 
17
- import { z } from 'zod';
17
+ import { z } from 'zod/v3';
18
18
  import { v4 as uuidv4 } from 'uuid';
19
19
  import * as ni from '../../services/neural-interface.js';
20
20
  import { generateEmbedding } from '../../services/local-embeddings.js';
21
21
  import { upsertMemory } from '../../services/sqlite.js';
22
- import { categoryExists, addCategory } from '../../services/categories.js';
22
+ import { ensureCategory, FEATURE_CATEGORIES } from '../../services/categories.js';
23
23
  import { detectProject } from '../../config.js';
24
24
  import type { MemoryPayload } from '../../types.js';
25
25
  import { text } from '../response.js';
@@ -63,17 +63,9 @@ async function ensureOnStudio(r: Resolved): Promise<string | null> {
63
63
  return ensureAuth(r);
64
64
  }
65
65
 
66
- /** Ensure the dedup-ledger category exists (so it shows in the UI and routing). */
66
+ /** Ensure the dedup-ledger category exists (so it shows in the UI and routing): made on the first upload, under "social" when that exists. */
67
67
  function ensureTrailerCategory(): void {
68
- try {
69
- if (!categoryExists(TRAILER_CATEGORY)) {
70
- addCategory(
71
- TRAILER_CATEGORY,
72
- 'One memory per uploaded YouTube trailer — the dedup ledger. Written automatically by youtube_upload; tagged with canonical source ids (igdb:/appid:/ytid:/name:) that youtube_source reads to skip already-posted trailers. Do not store anything else here.',
73
- categoryExists('social') ? 'social' : undefined,
74
- );
75
- }
76
- } catch { /* non-fatal — upsertMemory does not require a registered category */ }
68
+ ensureCategory(TRAILER_CATEGORY, FEATURE_CATEGORIES[TRAILER_CATEGORY].description, { parent: 'social' });
77
69
  }
78
70
 
79
71
  /** Write the dedup-ledger memory for a just-uploaded trailer. Returns the memory id, or null on failure. */
@@ -2,11 +2,11 @@
2
2
  import { retrieveMemory } from './services/memory-retrieval.js';
3
3
 
4
4
  // Load .env from parent directory before any service imports
5
- import { readFileSync } from 'fs';
6
- import { resolve } from 'path';
7
- import { getDataHome } from '../../lib/paths.js';
8
-
9
- const envPath = process.env.DOTENV_PATH || resolve(getDataHome(), '.env');
5
+ import { readFileSync } from 'fs';
6
+ import { resolve } from 'path';
7
+ import { getDataHome } from '../../lib/paths.js';
8
+
9
+ const envPath = process.env.DOTENV_PATH || resolve(getDataHome(), '.env');
10
10
  try {
11
11
  const envContent = readFileSync(envPath, 'utf-8');
12
12
  for (const line of envContent.split('\n')) {
@@ -407,6 +407,10 @@ async function editMemory(id: string, payload: MemoryPayload): Promise<void> {
407
407
  console.log(success(` ${ICONS.check} Content updated & re-embedded.`));
408
408
  } else if (field === 'category') {
409
409
  const cats = getAllCategories();
410
+ if (cats.length === 0) {
411
+ console.log(warn(` ${ICONS.cross} No categories yet. Create one under Categories first.`));
412
+ return;
413
+ }
410
414
  const val = await safePrompt(() =>
411
415
  select({
412
416
  message: ' Category:',
@@ -608,6 +612,13 @@ async function searchScreen(): Promise<void> {
608
612
  async function createScreen(): Promise<void> {
609
613
  printScreenHeader(`${ICONS.plus} Create Memory`, 'Store a new piece of knowledge');
610
614
 
615
+ // A fresh install has no categories until a project is added or one is created.
616
+ const cats = getAllCategories();
617
+ if (cats.length === 0) {
618
+ console.log(warn(` ${ICONS.cross} No categories yet. Create one under Categories first.`));
619
+ return;
620
+ }
621
+
611
622
  // Content
612
623
  const content = await safePrompt(() =>
613
624
  input({
@@ -618,7 +629,6 @@ async function createScreen(): Promise<void> {
618
629
  if (content === null) return;
619
630
 
620
631
  // Category
621
- const cats = getAllCategories();
622
632
  const category = await safePrompt(() =>
623
633
  select({
624
634
  message: accent(` ${ICONS.folder} Category:`),
@@ -793,12 +803,15 @@ async function categoriesScreen(): Promise<void> {
793
803
  );
794
804
  if (reassign === null || !reassign) continue;
795
805
 
806
+ const others = getAllCategories().filter(c => c !== name);
807
+ if (others.length === 0) {
808
+ console.log(warn(` ${ICONS.cross} No other category to move them to. Create one first.`));
809
+ continue;
810
+ }
796
811
  const target = await safePrompt(() =>
797
812
  select({
798
813
  message: accent(' Reassign to:'),
799
- choices: getAllCategories()
800
- .filter(c => c !== name)
801
- .map(c => ({ name: ` ${colorCategory(c)}`, value: c })),
814
+ choices: others.map(c => ({ name: ` ${colorCategory(c)}`, value: c })),
802
815
  })
803
816
  );
804
817
  if (target === null) continue;
@@ -6355,6 +6355,12 @@
6355
6355
  "left": "{percent}% left",
6356
6356
  "resets": "resets {time}"
6357
6357
  },
6358
+ "accountMenu": {
6359
+ "uses": "{label} uses",
6360
+ "workers": "Workers use",
6361
+ "workersHint": "Used when a dispatch names no account.",
6362
+ "switchNote": "Another account starts a new conversation."
6363
+ },
6358
6364
  "usage": {
6359
6365
  "task": "Current task",
6360
6366
  "tokens": "tokens",
@@ -7315,6 +7321,8 @@
7315
7321
  "variant": "Model variant",
7316
7322
  "variantDefault": "default variant",
7317
7323
  "account": "Account profile",
7324
+ "accountShort": "Account",
7325
+ "accountAutoShort": "Auto",
7318
7326
  "accountHint": "as",
7319
7327
  "project": "Working project",
7320
7328
  "projectHint": "in",
@@ -7365,6 +7373,16 @@
7365
7373
  "thorough": "Full coverage, whole suite, live checks."
7366
7374
  }
7367
7375
  },
7376
+ "adminSandbox": {
7377
+ "group": "Every agent",
7378
+ "label": "Admin sandbox",
7379
+ "on": "On",
7380
+ "off": "Off",
7381
+ "hint": "Only changes anything when SynaBun runs on Windows. On: Codex can set up its strongest sandbox, which asks for administrator approval once per account. Off: no admin prompt; Codex uses a sandbox that needs no admin, with weaker isolation.",
7382
+ "aria": "{label}: {value}. Only changes anything when SynaBun runs on Windows.",
7383
+ "statusOn": "Admin sandbox: on. On Windows, Codex may ask for administrator approval once per account to set up its sandbox.",
7384
+ "statusOff": "Admin sandbox: off. No agent SynaBun starts asks for administrator approval for a sandbox."
7385
+ },
7368
7386
  "accounts": {
7369
7387
  "signInAccount": "Sign in to {label}…",
7370
7388
  "title": "{provider} accounts",
@@ -6307,6 +6307,12 @@
6307
6307
  "left": "{percent}% disponível",
6308
6308
  "resets": "renova em {time}"
6309
6309
  },
6310
+ "accountMenu": {
6311
+ "uses": "{label} usa",
6312
+ "workers": "Workers usam",
6313
+ "workersHint": "Usada quando o despacho não indica uma conta.",
6314
+ "switchNote": "Outra conta inicia uma nova conversa."
6315
+ },
6310
6316
  "usage": {
6311
6317
  "task": "Tarefa atual",
6312
6318
  "tokens": "tokens",
@@ -7267,6 +7273,8 @@
7267
7273
  "variant": "Variante do modelo",
7268
7274
  "variantDefault": "variante padrão",
7269
7275
  "account": "Perfil da conta",
7276
+ "accountShort": "Conta",
7277
+ "accountAutoShort": "Auto",
7270
7278
  "accountHint": "como",
7271
7279
  "project": "Projeto de trabalho",
7272
7280
  "projectHint": "entrada",
@@ -7317,6 +7325,16 @@
7317
7325
  "thorough": "Cobertura completa, suíte inteira, verificações ao vivo."
7318
7326
  }
7319
7327
  },
7328
+ "adminSandbox": {
7329
+ "group": "Todos os agentes",
7330
+ "label": "Sandbox de administrador",
7331
+ "on": "Ativado",
7332
+ "off": "Desativado",
7333
+ "hint": "Só muda algo quando o SynaBun roda no Windows. Ativado: o Codex pode configurar seu sandbox mais forte, que pede aprovação de administrador uma vez por conta. Desativado: nenhum pedido de administrador; o Codex usa um sandbox que não precisa de administrador, com isolamento mais fraco.",
7334
+ "aria": "{label}: {value}. Só muda algo quando o SynaBun roda no Windows.",
7335
+ "statusOn": "Sandbox de administrador: ativado. No Windows, o Codex pode pedir aprovação de administrador uma vez por conta para configurar seu sandbox.",
7336
+ "statusOff": "Sandbox de administrador: desativado. Nenhum agente iniciado pelo SynaBun pede aprovação de administrador para um sandbox."
7337
+ },
7320
7338
  "accounts": {
7321
7339
  "signInAccount": "Entrar em {label}…",
7322
7340
  "title": "Contas de {provider}",
@@ -0,0 +1,102 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Admin sandbox: what it means for each agent (pure)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // One global setting (lib/agent-sandbox-setting.js, the Assistant's permission
6
+ // menu) decides whether an agent SynaBun starts may use a sandbox whose setup
7
+ // needs Windows administrator approval (a UAC prompt). Off is the default:
8
+ // then nothing SynaBun starts can ask for one. Each provider says here what
9
+ // the setting means for it; a launcher asks this module and never decides by
10
+ // itself. A provider SynaBun can start must have an entry
11
+ // (tests/agent-sandbox-elevation.test.mjs fails otherwise).
12
+ //
13
+ // The setting never changes a provider's sandbox policy (read-only,
14
+ // workspace-write, full access): only which implementation of it may need an
15
+ // administrator. It is a launch override, never written into a config file.
16
+
17
+ export const ADMIN_SANDBOX_DEFAULT = false;
18
+
19
+ const CODEX_SETTING = 'windows.sandbox';
20
+
21
+ export const SANDBOX_ELEVATION = Object.freeze({
22
+ // `windows.sandbox` (Codex 0.163: "elevated" | "unelevated" | "mxc"): "elevated"
23
+ // provisions two local sandbox accounts with codex-windows-sandbox-setup.exe, a
24
+ // UAC prompt once per CODEX_HOME (SynaBun's accounts each have one);
25
+ // "unelevated" is Codex's restricted-token sandbox: no administrator, weaker isolation.
26
+ codex: Object.freeze({
27
+ changes: true,
28
+ setting: CODEX_SETTING,
29
+ off: 'unelevated',
30
+ on: 'elevated',
31
+ reason: 'Codex picks its Windows sandbox with windows.sandbox: "elevated" needs a one-time administrator setup per account, "unelevated" needs none.',
32
+ }),
33
+ // Claude Code 2.1.303 has a Windows sandbox whose one-time install asks for
34
+ // administrator approval, but only `claude sandbox install` or `/sandbox install`
35
+ // starts it: a session never does, and SynaBun never runs either command.
36
+ 'claude-code': Object.freeze({
37
+ changes: false,
38
+ reason: 'Claude Code installs its Windows sandbox only through `claude sandbox install` or `/sandbox install`, which SynaBun never runs; a session never asks for administrator approval by itself.',
39
+ }),
40
+ // OpenCode has no operating-system sandbox: permission rules only (its
41
+ // "sandboxes" are worktree folders).
42
+ opencode: Object.freeze({
43
+ changes: false,
44
+ reason: 'OpenCode has no operating-system sandbox, only permission rules, so nothing it starts needs administrator approval.',
45
+ }),
46
+ });
47
+
48
+ /** Only `true` turns it on: anything else (missing, a string, null) is the default, off. */
49
+ export function normalizeAdminSandbox(value) {
50
+ return value === true;
51
+ }
52
+
53
+ /** Providers the setting changes (['codex']): what the panel's copy may name. */
54
+ export function adminSandboxProviders() {
55
+ return Object.keys(SANDBOX_ELEVATION).filter((provider) => SANDBOX_ELEVATION[provider].changes);
56
+ }
57
+
58
+ /**
59
+ * What a launcher of `provider` adds for the setting: `args` (CLI words, before
60
+ * the subcommand or anywhere an override is taken), `config` (the same as an
61
+ * SDK config object) and `value`. A provider the setting does not change gets
62
+ * nothing to add and its reason. An unknown provider throws: it must declare
63
+ * its mapping in SANDBOX_ELEVATION first.
64
+ */
65
+ export function sandboxElevationFor(provider, adminSandbox) {
66
+ const entry = SANDBOX_ELEVATION[provider];
67
+ if (!entry) throw new Error(`No admin sandbox mapping for provider "${provider}": add it to SANDBOX_ELEVATION.`);
68
+ const on = normalizeAdminSandbox(adminSandbox);
69
+ if (!entry.changes) return { provider, changes: false, adminSandbox: on, value: null, args: [], config: {}, reason: entry.reason };
70
+ const value = on ? entry.on : entry.off;
71
+ return { provider, changes: true, adminSandbox: on, value, args: ['-c', `${entry.setting}=${JSON.stringify(value)}`], config: configAt(entry.setting, value), reason: entry.reason };
72
+ }
73
+
74
+ function configAt(dotted, value) {
75
+ const keys = dotted.split('.');
76
+ const out = {};
77
+ let node = out;
78
+ keys.forEach((key, index) => { node[key] = index === keys.length - 1 ? value : {}; node = node[key]; });
79
+ return out;
80
+ }
81
+
82
+ /** 'elevated' when the setting is on, else 'unelevated'. */
83
+ export function codexWindowsSandbox(adminSandbox) {
84
+ return sandboxElevationFor('codex', adminSandbox).value;
85
+ }
86
+
87
+ /** ['-c', 'windows.sandbox="unelevated"'] (or "elevated"): a Codex launch override. */
88
+ export function codexWindowsSandboxArgs(adminSandbox) {
89
+ return sandboxElevationFor('codex', adminSandbox).args;
90
+ }
91
+
92
+ /** { windows: { sandbox: 'unelevated' } } (or 'elevated'): the same for the Codex SDK's `config`. */
93
+ export function codexWindowsSandboxConfig(adminSandbox) {
94
+ return sandboxElevationFor('codex', adminSandbox).config;
95
+ }
96
+
97
+ /**
98
+ * A Codex process that runs no command and no turn (a probe in a throwaway
99
+ * home: hooks, settings schema, feature switches) needs no sandbox at all: it
100
+ * is always started with the one that needs no administrator, whatever the setting.
101
+ */
102
+ export const CODEX_PROBE_SANDBOX_ARGS = Object.freeze([...codexWindowsSandboxArgs(false)]);
@@ -0,0 +1,57 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Admin sandbox: the one global value
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // DATA_HOME/data/agent-sandbox.json ({ adminSandbox, updatedAt }), read live:
6
+ // one value for every agent SynaBun starts, not per session or tab. No file
7
+ // (or one that cannot be read) is the default, off. What the value means for
8
+ // each provider is lib/agent-sandbox-elevation.js. Only the UI changes it
9
+ // (PUT /api/assistant/admin-sandbox answers UI_ONLY to agents); a change
10
+ // reaches every window as `agents:admin-sandbox-changed` and every listener
11
+ // (server.js restarts the idle Codex app-servers there).
12
+
13
+ import { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
14
+ import { dirname } from 'node:path';
15
+ import { ADMIN_SANDBOX_DEFAULT, adminSandboxProviders, normalizeAdminSandbox } from './agent-sandbox-elevation.js';
16
+
17
+ export const ADMIN_SANDBOX_EVENT = 'agents:admin-sandbox-changed';
18
+
19
+ export function createAdminSandboxSetting({ path, broadcast = () => {}, log = () => {}, now = Date.now } = {}) {
20
+ if (!path) throw new Error('createAdminSandboxSetting requires a path');
21
+ const listeners = new Set();
22
+ let cache = null; // { mtimeMs, value }
23
+
24
+ function read() {
25
+ try {
26
+ const stat = statSync(path);
27
+ if (cache && cache.mtimeMs === stat.mtimeMs) return cache.value;
28
+ const data = JSON.parse(readFileSync(path, 'utf8'));
29
+ const value = normalizeAdminSandbox(data?.adminSandbox);
30
+ cache = { mtimeMs: stat.mtimeMs, value };
31
+ return value;
32
+ } catch {
33
+ return ADMIN_SANDBOX_DEFAULT;
34
+ }
35
+ }
36
+ /** What the UI shows: the value and the providers it changes. */
37
+ function view() {
38
+ return { adminSandbox: read(), providers: adminSandboxProviders() };
39
+ }
40
+ function set(value) {
41
+ const next = normalizeAdminSandbox(value);
42
+ const before = read();
43
+ mkdirSync(dirname(path), { recursive: true });
44
+ const tmp = `${path}.${process.pid}.tmp`;
45
+ writeFileSync(tmp, JSON.stringify({ adminSandbox: next, updatedAt: new Date(now()).toISOString() }, null, 2) + '\n', 'utf8');
46
+ renameSync(tmp, path);
47
+ cache = null;
48
+ const changed = next !== before;
49
+ if (changed) log('agent-sandbox', `admin sandbox ${next ? 'on' : 'off'}`);
50
+ try { broadcast({ type: ADMIN_SANDBOX_EVENT, ...view() }); } catch { /* nobody listening */ }
51
+ if (changed) for (const listener of [...listeners]) { try { listener(next); } catch { /* the listener's own failure */ } }
52
+ return view();
53
+ }
54
+ function subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); }
55
+
56
+ return { enabled: read, view, set, subscribe, path };
57
+ }
@@ -150,6 +150,8 @@ export function createAssistantApi({
150
150
  // after this router), and the port the server listens on (the Host check of that route).
151
151
  whatsapp = null,
152
152
  port = 3344,
153
+ // The Admin sandbox setting (lib/agent-sandbox-setting.js): one global value for every agent SynaBun starts.
154
+ adminSandbox = null,
153
155
  } = {}) {
154
156
  if (!dispatcher) throw new Error('createAssistantApi requires a dispatcher');
155
157
  const router = Router();
@@ -1230,6 +1232,23 @@ export function createAssistantApi({
1230
1232
  } catch (error) { sendError(res, error); }
1231
1233
  });
1232
1234
 
1235
+ // ── Admin sandbox (lib/agent-sandbox-setting.js) ───────────────────────────
1236
+ // Whether an agent SynaBun starts may use a sandbox whose setup needs Windows administrator
1237
+ // approval. Global, not per session. The user's setting: an agent cannot change it.
1238
+ router.get('/admin-sandbox', (req, res) => {
1239
+ if (!adminSandbox) return res.status(503).json({ ok: false, code: 'ADMIN_SANDBOX_UNAVAILABLE', error: 'The Admin sandbox setting is not available on this server' });
1240
+ res.json({ ok: true, ...adminSandbox.view() });
1241
+ });
1242
+ router.put('/admin-sandbox', requireJson, (req, res) => {
1243
+ if (!adminSandbox) return res.status(503).json({ ok: false, code: 'ADMIN_SANDBOX_UNAVAILABLE', error: 'The Admin sandbox setting is not available on this server' });
1244
+ if (callerContext(req).kind !== 'ui' || req.get('x-synabun-desktop-grant')) {
1245
+ return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Only the user can change the Admin sandbox.' });
1246
+ }
1247
+ const value = req.body?.adminSandbox;
1248
+ if (typeof value !== 'boolean') return res.status(400).json({ ok: false, code: 'INVALID_VALUE', error: 'adminSandbox must be true or false.' });
1249
+ try { res.json({ ok: true, ...adminSandbox.set(value) }); } catch (error) { sendError(res, error); }
1250
+ });
1251
+
1233
1252
  // ── sidepanel tab registry (UI heartbeat) ──────────────────────────────────
1234
1253
  function listTabs() {
1235
1254
  const cutoff = now() - tabTtlMs;
@@ -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 {
@@ -246,6 +246,8 @@ export function createAssistantRuntime({
246
246
  // (capability) → boolean, the verifier of lib/remote-policy.js createPhoneAuthority(): is this the
247
247
  // capability of the one WhatsApp bridge? Without a verifier nothing can grant computer use remotely.
248
248
  phoneAuthority = null,
249
+ // The Admin sandbox setting (lib/agent-sandbox-setting.js): global, shown in every session view.
250
+ adminSandbox = null,
249
251
  } = {}) {
250
252
  if (!dataDir) throw new Error('createAssistantRuntime requires dataDir');
251
253
  const settings = {
@@ -906,7 +908,9 @@ export function createAssistantRuntime({
906
908
  out.testDensity = testDensityOf(record);
907
909
  out.testDensityExplicit = normalizeTestDensity(record.testDensity, null);
908
910
  // `rename`: this server keeps a rename on the record (userRenamed), so a panel writes it here.
909
- out.features = { routing: !!router, computer: computerSupported(), testDensity: true, rename: true };
911
+ // `adminSandbox`: the global setting is served here (PUT /api/assistant/admin-sandbox).
912
+ out.features = { routing: !!router, computer: computerSupported(), testDensity: true, rename: true, adminSandbox: !!adminSandbox };
913
+ if (adminSandbox) { try { out.adminSandbox = adminSandbox.enabled() === true; } catch { out.adminSandbox = false; } }
910
914
  out.budget = sessionMoney(record.id);
911
915
  out.remoteLevel = policy ? effectiveLevel(policy, { untrusted: live.turnUntrusted === true, now: now() }) : null;
912
916
  }
@@ -9,7 +9,7 @@
9
9
  import { spawn } from 'node:child_process';
10
10
  import { verifyOpenAiApiKey } from './claude-profile-api-key.js';
11
11
  import { validateProviderApiKey } from './provider-panel-api-key-auth.js';
12
- import { applyCodexLaunchEnv } from './codex-accounts.js';
12
+ import { applyCodexLaunchEnv, codexLaunchArgs } from './codex-accounts.js';
13
13
 
14
14
  const fail = (message, status = 409, code = null) => Object.assign(new Error(message), { status, ...(code ? { code } : {}) });
15
15
 
@@ -32,13 +32,15 @@ export function createCodexAccountAuth({
32
32
  }
33
33
  // Codex run under the account's own store: inherited credentials out, its
34
34
  // CODEX_HOME in, never the key SynaBun holds (that is not Codex's store).
35
- function launch(id, args) {
35
+ // The launch's overrides (the Windows sandbox of the Admin sandbox setting) follow the subcommand.
36
+ function launch(id, words) {
36
37
  account(id);
37
38
  const plan = accounts.launchFor(id);
38
39
  if (!plan.ok) throw fail(plan.message, 404);
39
40
  prepareConfig(plan.home);
40
41
  let file = codexBin();
41
42
  if (!file) throw fail('Codex CLI not found. Install Codex to sign in.', 503);
43
+ const args = [...words, ...codexLaunchArgs(plan)];
42
44
  const script = /\.[cm]?js$/i.test(file);
43
45
  const shell = process.platform === 'win32' && /\.(cmd|bat)$/i.test(file);
44
46
  const proc = spawnProcess(script ? process.execPath : file, script ? [file, ...args] : args, {
@@ -32,6 +32,7 @@ import { dirname, join, resolve, sep } from 'node:path';
32
32
  import { homedir } from 'node:os';
33
33
  import { readProfileApiKey, saveProfileApiKey, clearProfileApiKey } from './claude-profile-api-key.js';
34
34
  import { apiKeyHint, resolveClaudeAuth } from './claude-accounts.js';
35
+ import { normalizeAdminSandbox, sandboxElevationFor } from './agent-sandbox-elevation.js';
35
36
 
36
37
  export const CODEX_DEFAULT_ACCOUNT_ID = 'default';
37
38
  export const CODEX_AUTH_MODES = Object.freeze(['subscription', 'apiKey']);
@@ -123,9 +124,13 @@ export function applyCodexLaunchEnv(base, launch) {
123
124
  export function codexExecEnv(launch) {
124
125
  return { ...(launch?.env || {}), ...(launch?.apiKey ? { CODEX_API_KEY: launch.apiKey } : {}) };
125
126
  }
127
+ /** The `-c` overrides every Codex process of the launch starts with: the Windows sandbox the Admin sandbox setting allows. */
128
+ export function codexLaunchArgs(launch) {
129
+ return [...(launch?.configArgs || [])];
130
+ }
126
131
  /** The `-c` overrides an app-server of the account starts with (before the subcommand). */
127
132
  export function codexAppServerArgs(launch) {
128
- return launch?.apiKey ? [...CODEX_EPHEMERAL_STORE_ARGS] : [];
133
+ return [...codexLaunchArgs(launch), ...(launch?.apiKey ? CODEX_EPHEMERAL_STORE_ARGS : [])];
129
134
  }
130
135
 
131
136
  export function createCodexAccounts({
@@ -141,6 +146,8 @@ export function createCodexAccounts({
141
146
  statusTimeoutMs = 20_000,
142
147
  // Called before a new account links Default's config.toml (server.js repairs it there).
143
148
  prepareDefaultConfig = () => {},
149
+ // () => the Admin sandbox setting (lib/agent-sandbox-setting.js): which Windows sandbox every launch may use.
150
+ adminSandbox = () => false,
144
151
  } = {}) {
145
152
  if (!dataHome) throw new Error('createCodexAccounts requires dataHome');
146
153
  const registryPath = resolve(dataHome, 'data', 'codex-accounts.json');
@@ -273,11 +280,17 @@ export function createCodexAccounts({
273
280
  }
274
281
  const { row, key } = describeProfile(profile, data);
275
282
  const apiKey = row.mode === 'apiKey' && row.apiKey.source === 'synabun' && key ? key : null;
283
+ // A launch override, never written into config.toml: Default's home is the user's own ~/.codex.
284
+ let on = false;
285
+ try { on = normalizeAdminSandbox(adminSandbox()); } catch { on = false; }
286
+ const sandbox = sandboxElevationFor('codex', on);
276
287
  return {
277
288
  ok: true, accountId: wanted, label: row.label, mode: row.mode, signedIn: row.signedIn, identity: row.identity, home: row.home,
278
289
  env: { CODEX_HOME: row.home }, unset: [...CODEX_CREDENTIAL_ENV], apiKey,
279
290
  // Which credential a process started with: a change means the account's idle processes restart.
280
291
  credential: [row.mode || 'none', row.apiKey.source || '', row.apiKey.hint || '', row.subscription.email || ''].join(':'),
292
+ // The Windows sandbox it may use ('elevated' | 'unelevated'): a change restarts idle app-servers too.
293
+ windowsSandbox: sandbox.value, configArgs: sandbox.args,
281
294
  };
282
295
  }
283
296
 
@@ -2,6 +2,8 @@ import { spawnSync } from 'node:child_process';
2
2
  import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
+ // The probes run no command: always the Windows sandbox that needs no administrator (lib/agent-sandbox-elevation.js).
6
+ import { CODEX_PROBE_SANDBOX_ARGS } from './agent-sandbox-elevation.js';
5
7
 
6
8
  const cache = new Map();
7
9
  const featureCache = new Map();
@@ -45,7 +47,7 @@ export function readCodexSettingsSchema(codexBin) {
45
47
  const home = mkdtempSync(join(tmpdir(), 'synabun-codex-schema-'));
46
48
  try {
47
49
  const out = join(home, 'schema');
48
- const run = spawnSync(codexBin, ['app-server', 'generate-json-schema', '--experimental', '--out', out], {
50
+ const run = spawnSync(codexBin, ['app-server', 'generate-json-schema', '--experimental', '--out', out, ...CODEX_PROBE_SANDBOX_ARGS], {
49
51
  env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 15000, windowsHide: true,
50
52
  });
51
53
  if (run.error || run.status !== 0) throw new Error('Codex could not report its settings schema');
@@ -56,7 +58,7 @@ export function readCodexSettingsSchema(codexBin) {
56
58
  // `features list` validates config locally and never runs a model or tool.
57
59
  for (const [fieldKey, field] of Object.entries(schema.fields)) {
58
60
  field.values = field.values.filter(value => {
59
- const check = spawnSync(codexBin, ['-c', `${fieldKey}=${JSON.stringify(value)}`, 'features', 'list'], {
61
+ const check = spawnSync(codexBin, [...CODEX_PROBE_SANDBOX_ARGS, '-c', `${fieldKey}=${JSON.stringify(value)}`, 'features', 'list'], {
60
62
  env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 5000, windowsHide: true,
61
63
  });
62
64
  return !check.error && check.status === 0;
@@ -105,7 +107,7 @@ export function readCodexFeatureSupport(codexBin, features) {
105
107
  const home = mkdtempSync(join(tmpdir(), 'synabun-feature-support-'));
106
108
  try {
107
109
  const states = [false, true].map(value => {
108
- const check = spawnSync(codexBin, ['-c', `features={${names.map(name => `${name}=${value}`).join(',')}}`, 'features', 'list'], {
110
+ const check = spawnSync(codexBin, [...CODEX_PROBE_SANDBOX_ARGS, '-c', `features={${names.map(name => `${name}=${value}`).join(',')}}`, 'features', 'list'], {
109
111
  env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 10000, windowsHide: true,
110
112
  });
111
113
  if (check.error || check.status !== 0) throw new Error('Codex could not validate feature switches');
@@ -82,6 +82,8 @@ export function buildExecInvocation({
82
82
  model,
83
83
  effort,
84
84
  mcpEnvOverrides = [],
85
+ // Codex: launch overrides as `-c` words (the Windows sandbox of the Admin sandbox setting).
86
+ codexConfigArgs = [],
85
87
  platform = process.platform,
86
88
  commandExists = existsSync,
87
89
  } = {}) {
@@ -94,7 +96,7 @@ export function buildExecInvocation({
94
96
 
95
97
  switch (profile) {
96
98
  case 'codex': {
97
- args.push('exec', '--dangerously-bypass-approvals-and-sandbox');
99
+ args.push('exec', '--dangerously-bypass-approvals-and-sandbox', ...codexConfigArgs);
98
100
  for (const [key, value] of mcpEnvOverrides) {
99
101
  args.push('-c', `mcp_servers.SynaBun.env.${key}=${codexConfigString(value)}`);
100
102
  }
@@ -26,15 +26,29 @@ export function packagesInLock(lock) {
26
26
  return entries.filter(key => /(?:^|\/)node_modules\//.test(key)).length;
27
27
  }
28
28
 
29
+ /** How many packages of a lockfile are on disk under `root` (npm-shrinkwrap.json of an npm install). */
30
+ export function installedFromLock(lock, root, { exists = existsSync } = {}) {
31
+ const entries = lock && typeof lock.packages === 'object' ? Object.keys(lock.packages) : [];
32
+ return entries.filter(key => /(?:^|\/)node_modules\//.test(key) && exists(path.join(root, ...key.split('/')))).length;
33
+ }
34
+
29
35
  /**
30
- * A part's own dependencies (its package.json), each with the version installed in its node_modules, or null
31
- * when it is not installed. Sorted by name.
36
+ * A part's own dependencies (its package.json), each with the version Node finds for it, or null when it is not
37
+ * installed. Sorted by name. Found in the part's node_modules, and with `stopAt` (the package root) in each folder
38
+ * up to it too: `npm i -g synabun` installs every part's dependencies at the package root.
32
39
  */
33
- export function directDependencies(dir, { readJson = readJsonFile } = {}) {
40
+ export function directDependencies(dir, { readJson = readJsonFile, stopAt = null } = {}) {
34
41
  const manifest = readJson(path.join(dir, 'package.json')) || {};
35
42
  const wanted = { ...(manifest.optionalDependencies || {}), ...(manifest.dependencies || {}) };
43
+ const limit = stopAt ? path.resolve(stopAt) : null;
44
+ const find = (name) => {
45
+ for (let folder = path.resolve(dir); ; folder = path.dirname(folder)) {
46
+ const found = readJson(path.join(folder, 'node_modules', ...name.split('/'), 'package.json'));
47
+ if (found || !limit || folder === limit || path.dirname(folder) === folder) return found;
48
+ }
49
+ };
36
50
  return Object.keys(wanted).sort().map((name) => {
37
- const installed = readJson(path.join(dir, 'node_modules', ...name.split('/'), 'package.json'));
51
+ const installed = find(name);
38
52
  return { name, wanted: String(wanted[name]), installed: installed?.version ? String(installed.version) : null };
39
53
  });
40
54
  }
@@ -112,10 +126,14 @@ export async function collectInstallDetails({
112
126
  const userNpmPath = firstOnPath('npm', osPath, { platform, exists, skip: runtimeBin ? [runtimeBin, impl.dirname(execPath)] : [] });
113
127
  const userNpm = userNpmPath && userNpmPath !== ownNpm ? { path: userNpmPath, ...(await version(userNpmPath, { platform, env })) } : null;
114
128
 
129
+ // A part's own install record when it has one (a development checkout, a desktop build); an npm install keeps
130
+ // both parts' dependencies at the package root instead, pinned by npm-shrinkwrap.json.
131
+ const shrinkwrap = readJson(path.join(packageRoot, 'npm-shrinkwrap.json'));
115
132
  const parts = [['Neural Interface', 'neural-interface'], ['MCP server', 'mcp-server']].map(([name, folder]) => {
116
133
  const dir = path.join(packageRoot, folder);
117
134
  const installed = readJson(path.join(dir, 'node_modules', '.package-lock.json'));
118
- return { name, folder, packages: packagesInLock(installed), dependencies: directDependencies(dir, { readJson }) };
135
+ const packages = installed ? packagesInLock(installed) : installedFromLock(shrinkwrap, packageRoot, { exists });
136
+ return { name, folder, packages, dependencies: directDependencies(dir, { readJson, stopAt: packageRoot }) };
119
137
  });
120
138
  const mcpBuilt = exists(path.join(packageRoot, 'mcp-server', 'dist', 'index.js'));
121
139
 
@@ -10,6 +10,7 @@ import { codexReasoningEffort } from './effort-levels.js';
10
10
  import { codexStreamMedia, collectCodexMedia, openCodeMediaPart, ownCodexImages } from './assistant-media.js';
11
11
  import { browserToolDenial } from './browser-tool-policy.js';
12
12
  import { CODEX_BROWSER_POLICY_UNTRUSTED_NOTE, codexBrowserPolicyHook } from './assistant-route-gate-codex.js';
13
+ import { codexWindowsSandbox, codexWindowsSandboxConfig } from './agent-sandbox-elevation.js';
13
14
 
14
15
  function stringEnv(source) {
15
16
  const out = {};
@@ -251,6 +252,9 @@ export async function createCodexNativeLoopAdapter(options = {}) {
251
252
  // Inherited variables to remove before extraEnv applies (the Codex account's launch:
252
253
  // lib/codex-accounts.js launchFor() — inherited OpenAI/Codex credentials never reach the run).
253
254
  unsetEnv = [],
255
+ // The Admin sandbox setting when the run started (lib/agent-sandbox-elevation.js): the Windows
256
+ // sandbox every `codex exec` of the thread may use. The run keeps it while it lives.
257
+ adminSandbox = false,
254
258
  } = options;
255
259
  const permissionPolicy = normalizePermissionPolicy(rawPolicy);
256
260
  const capability = normalizeCapability(rawCapability);
@@ -287,7 +291,7 @@ export async function createCodexNativeLoopAdapter(options = {}) {
287
291
  const inherited = { ...process.env };
288
292
  for (const key of Array.isArray(unsetEnv) ? unsetEnv : []) delete inherited[key];
289
293
  const env = stringEnv({ ...inherited, ...pins, CODEX_HOME: codexHome, ...(browserPolicy ? { SYNABUN_BROWSER_POLICY_CWD: cwd || process.cwd() } : {}), ...stringEnv(extraEnv) });
290
- const config = {};
294
+ const config = { ...codexWindowsSandboxConfig(adminSandbox) };
291
295
  if (Object.keys(pins).length) config.mcp_servers = { SynaBun: { env: pins } };
292
296
  if (extended) config.model_context_window = Math.floor(Number(modelContextWindow));
293
297
  if (browserPolicy) Object.assign(config, browserPolicy.config);
@@ -334,6 +338,7 @@ export async function createCodexNativeLoopAdapter(options = {}) {
334
338
  isAlive: () => alive,
335
339
  describe: () => ({
336
340
  provider: 'codex', permissionPolicy, capability, sandboxMode: codexSandboxModeFor({ permissionPolicy, capability }),
341
+ windowsSandbox: codexWindowsSandbox(adminSandbox),
337
342
  ...(task ? { browserPolicy: browserPolicy ? 'hook' : 'instructions' } : {}),
338
343
  }),
339
344
  async runTurn(prompt, meta = {}) {