synabun 2.0.5 → 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 (527) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +2 -1
  3. package/THIRD-PARTY-LICENSES.md +13 -0
  4. package/docs/whatsapp.md +151 -16
  5. package/hooks/claude-code/post-plan.mjs +6 -30
  6. package/hooks/claude-code/session-start.mjs +1 -4
  7. package/hooks/claude-code/shared.mjs +112 -75
  8. package/lib/child-path.js +303 -0
  9. package/lib/claude-hooks.js +2 -1
  10. package/lib/data-home-migration.js +2 -0
  11. package/lib/desktop-app.js +265 -0
  12. package/lib/external-tools.js +7 -3
  13. package/lib/log-redact.js +102 -0
  14. package/lib/package-processes.js +670 -0
  15. package/lib/packaged-runtime.js +138 -11
  16. package/lib/runtime-dependencies.js +225 -0
  17. package/mcp-server/dist/http.d.ts +3 -1
  18. package/mcp-server/dist/http.d.ts.map +1 -1
  19. package/mcp-server/dist/http.js +15 -4
  20. package/mcp-server/dist/http.js.map +1 -1
  21. package/mcp-server/dist/index.d.ts.map +1 -1
  22. package/mcp-server/dist/index.js +9 -6
  23. package/mcp-server/dist/index.js.map +1 -1
  24. package/mcp-server/dist/services/browser-bench.d.ts +1 -1
  25. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
  26. package/mcp-server/dist/services/browser-bench.js +1 -1
  27. package/mcp-server/dist/services/browser-bench.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/categories.d.ts +35 -2
  33. package/mcp-server/dist/services/categories.d.ts.map +1 -1
  34. package/mcp-server/dist/services/categories.js +77 -33
  35. package/mcp-server/dist/services/categories.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
  41. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
  42. package/mcp-server/dist/services/desktop-bench.js +1 -1
  43. package/mcp-server/dist/services/desktop-bench.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +11 -0
  45. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  46. package/mcp-server/dist/services/identity.js +24 -0
  47. package/mcp-server/dist/services/identity.js.map +1 -1
  48. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  49. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  50. package/mcp-server/dist/services/neural-interface.js +34 -2
  51. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  52. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  53. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  54. package/mcp-server/dist/services/typesafe-config.js +4 -1
  55. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  56. package/mcp-server/dist/tools/agents.d.ts +11 -8
  57. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  58. package/mcp-server/dist/tools/agents.js +35 -14
  59. package/mcp-server/dist/tools/agents.js.map +1 -1
  60. package/mcp-server/dist/tools/bluesky.js +1 -1
  61. package/mcp-server/dist/tools/bluesky.js.map +1 -1
  62. package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
  63. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
  64. package/mcp-server/dist/tools/browser-advanced.js +1 -1
  65. package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
  66. package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
  67. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
  68. package/mcp-server/dist/tools/browser-batch.js +1 -1
  69. package/mcp-server/dist/tools/browser-batch.js.map +1 -1
  70. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
  71. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
  72. package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
  73. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
  74. package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
  75. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
  76. package/mcp-server/dist/tools/browser-interact.js +1 -1
  77. package/mcp-server/dist/tools/browser-interact.js.map +1 -1
  78. package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
  79. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
  80. package/mcp-server/dist/tools/browser-navigate.js +1 -1
  81. package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
  82. package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
  83. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
  84. package/mcp-server/dist/tools/browser-observe.js +1 -1
  85. package/mcp-server/dist/tools/browser-observe.js.map +1 -1
  86. package/mcp-server/dist/tools/card-tools.d.ts +1 -1
  87. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
  88. package/mcp-server/dist/tools/card-tools.js +1 -1
  89. package/mcp-server/dist/tools/card-tools.js.map +1 -1
  90. package/mcp-server/dist/tools/category.d.ts +1 -1
  91. package/mcp-server/dist/tools/category.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/category.js +5 -1
  93. package/mcp-server/dist/tools/category.js.map +1 -1
  94. package/mcp-server/dist/tools/choice.d.ts +1 -1
  95. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/choice.js +1 -1
  97. package/mcp-server/dist/tools/choice.js.map +1 -1
  98. package/mcp-server/dist/tools/computer.d.ts +1 -1
  99. package/mcp-server/dist/tools/computer.d.ts.map +1 -1
  100. package/mcp-server/dist/tools/computer.js +1 -1
  101. package/mcp-server/dist/tools/computer.js.map +1 -1
  102. package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
  103. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
  104. package/mcp-server/dist/tools/discord-channel.js +1 -1
  105. package/mcp-server/dist/tools/discord-channel.js.map +1 -1
  106. package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
  107. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
  108. package/mcp-server/dist/tools/discord-guild.js +1 -1
  109. package/mcp-server/dist/tools/discord-guild.js.map +1 -1
  110. package/mcp-server/dist/tools/discord-member.d.ts +1 -1
  111. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
  112. package/mcp-server/dist/tools/discord-member.js +1 -1
  113. package/mcp-server/dist/tools/discord-member.js.map +1 -1
  114. package/mcp-server/dist/tools/discord-message.d.ts +1 -1
  115. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
  116. package/mcp-server/dist/tools/discord-message.js +1 -1
  117. package/mcp-server/dist/tools/discord-message.js.map +1 -1
  118. package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
  119. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
  120. package/mcp-server/dist/tools/discord-onboarding.js +1 -1
  121. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
  122. package/mcp-server/dist/tools/discord-role.d.ts +1 -1
  123. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
  124. package/mcp-server/dist/tools/discord-role.js +1 -1
  125. package/mcp-server/dist/tools/discord-role.js.map +1 -1
  126. package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
  127. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
  128. package/mcp-server/dist/tools/discord-thread.js +1 -1
  129. package/mcp-server/dist/tools/discord-thread.js.map +1 -1
  130. package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
  131. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
  132. package/mcp-server/dist/tools/discord-webhook.js +1 -1
  133. package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
  134. package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
  135. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
  136. package/mcp-server/dist/tools/fb-groups.js +1 -1
  137. package/mcp-server/dist/tools/fb-groups.js.map +1 -1
  138. package/mcp-server/dist/tools/forget.d.ts +1 -1
  139. package/mcp-server/dist/tools/forget.d.ts.map +1 -1
  140. package/mcp-server/dist/tools/forget.js +1 -1
  141. package/mcp-server/dist/tools/forget.js.map +1 -1
  142. package/mcp-server/dist/tools/git-tools.d.ts +1 -1
  143. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
  144. package/mcp-server/dist/tools/git-tools.js +1 -1
  145. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  146. package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
  147. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
  148. package/mcp-server/dist/tools/gsc/experience.js +1 -1
  149. package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
  150. package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
  151. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
  152. package/mcp-server/dist/tools/gsc/indexing.js +1 -1
  153. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
  154. package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
  155. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
  156. package/mcp-server/dist/tools/gsc/inspect.js +1 -1
  157. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
  158. package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
  159. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
  160. package/mcp-server/dist/tools/gsc/navigate.js +1 -1
  161. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
  162. package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
  163. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
  164. package/mcp-server/dist/tools/gsc/performance.js +1 -1
  165. package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
  166. package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
  167. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
  168. package/mcp-server/dist/tools/gsc/settings.js +1 -1
  169. package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
  170. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  171. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
  172. package/mcp-server/dist/tools/image-tools.js +1 -1
  173. package/mcp-server/dist/tools/image-tools.js.map +1 -1
  174. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
  175. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
  176. package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
  177. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
  178. package/mcp-server/dist/tools/loop.d.ts +1 -1
  179. package/mcp-server/dist/tools/loop.d.ts.map +1 -1
  180. package/mcp-server/dist/tools/loop.js +1 -1
  181. package/mcp-server/dist/tools/loop.js.map +1 -1
  182. package/mcp-server/dist/tools/memories.d.ts +1 -1
  183. package/mcp-server/dist/tools/memories.d.ts.map +1 -1
  184. package/mcp-server/dist/tools/memories.js +1 -1
  185. package/mcp-server/dist/tools/memories.js.map +1 -1
  186. package/mcp-server/dist/tools/morelogin.d.ts +1 -1
  187. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  188. package/mcp-server/dist/tools/morelogin.js +1 -1
  189. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  190. package/mcp-server/dist/tools/profile.d.ts +1 -1
  191. package/mcp-server/dist/tools/profile.d.ts.map +1 -1
  192. package/mcp-server/dist/tools/profile.js +1 -1
  193. package/mcp-server/dist/tools/profile.js.map +1 -1
  194. package/mcp-server/dist/tools/recall.d.ts +1 -1
  195. package/mcp-server/dist/tools/recall.d.ts.map +1 -1
  196. package/mcp-server/dist/tools/recall.js +1 -1
  197. package/mcp-server/dist/tools/recall.js.map +1 -1
  198. package/mcp-server/dist/tools/reflect.d.ts +1 -1
  199. package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
  200. package/mcp-server/dist/tools/reflect.js +1 -1
  201. package/mcp-server/dist/tools/reflect.js.map +1 -1
  202. package/mcp-server/dist/tools/remember.d.ts +1 -1
  203. package/mcp-server/dist/tools/remember.d.ts.map +1 -1
  204. package/mcp-server/dist/tools/remember.js +13 -11
  205. package/mcp-server/dist/tools/remember.js.map +1 -1
  206. package/mcp-server/dist/tools/restore.d.ts +1 -1
  207. package/mcp-server/dist/tools/restore.d.ts.map +1 -1
  208. package/mcp-server/dist/tools/restore.js +1 -1
  209. package/mcp-server/dist/tools/restore.js.map +1 -1
  210. package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
  211. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
  212. package/mcp-server/dist/tools/style-guide-tools.js +1 -1
  213. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
  214. package/mcp-server/dist/tools/sync.d.ts +1 -1
  215. package/mcp-server/dist/tools/sync.d.ts.map +1 -1
  216. package/mcp-server/dist/tools/sync.js +1 -1
  217. package/mcp-server/dist/tools/sync.js.map +1 -1
  218. package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
  219. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
  220. package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
  221. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
  222. package/mcp-server/dist/tools/utils.d.ts +1 -1
  223. package/mcp-server/dist/tools/utils.d.ts.map +1 -1
  224. package/mcp-server/dist/tools/utils.js +1 -1
  225. package/mcp-server/dist/tools/utils.js.map +1 -1
  226. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  227. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  228. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  229. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  230. package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
  231. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  232. package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
  233. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  234. package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
  235. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
  236. package/mcp-server/dist/tools/youtube/analytics.js +1 -1
  237. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
  238. package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
  239. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
  240. package/mcp-server/dist/tools/youtube/download.js +1 -1
  241. package/mcp-server/dist/tools/youtube/download.js.map +1 -1
  242. package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
  243. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
  244. package/mcp-server/dist/tools/youtube/media.js +1 -1
  245. package/mcp-server/dist/tools/youtube/media.js.map +1 -1
  246. package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
  247. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
  248. package/mcp-server/dist/tools/youtube/metadata.js +1 -1
  249. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
  250. package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
  251. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
  252. package/mcp-server/dist/tools/youtube/navigate.js +1 -1
  253. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
  254. package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
  255. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
  256. package/mcp-server/dist/tools/youtube/playlist.js +1 -1
  257. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
  258. package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
  259. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
  260. package/mcp-server/dist/tools/youtube/schedule.js +1 -1
  261. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
  262. package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
  263. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
  264. package/mcp-server/dist/tools/youtube/source.js +1 -1
  265. package/mcp-server/dist/tools/youtube/source.js.map +1 -1
  266. package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
  267. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
  268. package/mcp-server/dist/tools/youtube/upload.js +4 -9
  269. package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
  270. package/mcp-server/dist/tui.js +18 -5
  271. package/mcp-server/dist/tui.js.map +1 -1
  272. package/mcp-server/dist/version.d.ts +5 -0
  273. package/mcp-server/dist/version.d.ts.map +1 -0
  274. package/mcp-server/dist/version.js +21 -0
  275. package/mcp-server/dist/version.js.map +1 -0
  276. package/mcp-server/package-lock.json +3 -3
  277. package/mcp-server/package.json +3 -3
  278. package/mcp-server/run.mjs +15 -9
  279. package/mcp-server/src/http.ts +18 -3
  280. package/mcp-server/src/index.ts +9 -6
  281. package/mcp-server/src/services/browser-bench.ts +1 -1
  282. package/mcp-server/src/services/budget-judgments.ts +183 -0
  283. package/mcp-server/src/services/categories.ts +77 -32
  284. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  285. package/mcp-server/src/services/desktop-bench.ts +1 -1
  286. package/mcp-server/src/services/identity.ts +27 -0
  287. package/mcp-server/src/services/neural-interface.ts +34 -2
  288. package/mcp-server/src/services/typesafe-config.ts +4 -1
  289. package/mcp-server/src/tools/agents.ts +34 -15
  290. package/mcp-server/src/tools/bluesky.ts +1 -1
  291. package/mcp-server/src/tools/browser-advanced.ts +1 -1
  292. package/mcp-server/src/tools/browser-batch.ts +1 -1
  293. package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
  294. package/mcp-server/src/tools/browser-interact.ts +1 -1
  295. package/mcp-server/src/tools/browser-navigate.ts +1 -1
  296. package/mcp-server/src/tools/browser-observe.ts +1 -1
  297. package/mcp-server/src/tools/card-tools.ts +1 -1
  298. package/mcp-server/src/tools/category.ts +6 -1
  299. package/mcp-server/src/tools/choice.ts +1 -1
  300. package/mcp-server/src/tools/computer.ts +1 -1
  301. package/mcp-server/src/tools/discord-channel.ts +1 -1
  302. package/mcp-server/src/tools/discord-guild.ts +1 -1
  303. package/mcp-server/src/tools/discord-member.ts +1 -1
  304. package/mcp-server/src/tools/discord-message.ts +1 -1
  305. package/mcp-server/src/tools/discord-onboarding.ts +1 -1
  306. package/mcp-server/src/tools/discord-role.ts +1 -1
  307. package/mcp-server/src/tools/discord-thread.ts +1 -1
  308. package/mcp-server/src/tools/discord-webhook.ts +1 -1
  309. package/mcp-server/src/tools/fb-groups.ts +1 -1
  310. package/mcp-server/src/tools/forget.ts +1 -1
  311. package/mcp-server/src/tools/git-tools.ts +1 -1
  312. package/mcp-server/src/tools/gsc/experience.ts +1 -1
  313. package/mcp-server/src/tools/gsc/indexing.ts +1 -1
  314. package/mcp-server/src/tools/gsc/inspect.ts +1 -1
  315. package/mcp-server/src/tools/gsc/navigate.ts +1 -1
  316. package/mcp-server/src/tools/gsc/performance.ts +1 -1
  317. package/mcp-server/src/tools/gsc/settings.ts +1 -1
  318. package/mcp-server/src/tools/image-tools.ts +1 -1
  319. package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
  320. package/mcp-server/src/tools/loop.ts +1 -1
  321. package/mcp-server/src/tools/memories.ts +1 -1
  322. package/mcp-server/src/tools/morelogin.ts +1 -1
  323. package/mcp-server/src/tools/profile.ts +1 -1
  324. package/mcp-server/src/tools/recall.ts +1 -1
  325. package/mcp-server/src/tools/reflect.ts +1 -1
  326. package/mcp-server/src/tools/remember.ts +13 -12
  327. package/mcp-server/src/tools/restore.ts +1 -1
  328. package/mcp-server/src/tools/style-guide-tools.ts +1 -1
  329. package/mcp-server/src/tools/sync.ts +1 -1
  330. package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
  331. package/mcp-server/src/tools/utils.ts +1 -1
  332. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  333. package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
  334. package/mcp-server/src/tools/youtube/analytics.ts +1 -1
  335. package/mcp-server/src/tools/youtube/download.ts +1 -1
  336. package/mcp-server/src/tools/youtube/media.ts +1 -1
  337. package/mcp-server/src/tools/youtube/metadata.ts +1 -1
  338. package/mcp-server/src/tools/youtube/navigate.ts +1 -1
  339. package/mcp-server/src/tools/youtube/playlist.ts +1 -1
  340. package/mcp-server/src/tools/youtube/schedule.ts +1 -1
  341. package/mcp-server/src/tools/youtube/source.ts +1 -1
  342. package/mcp-server/src/tools/youtube/upload.ts +4 -12
  343. package/mcp-server/src/tui.ts +24 -10
  344. package/mcp-server/src/version.ts +22 -0
  345. package/neural-interface/i18n/en.json +645 -57
  346. package/neural-interface/i18n/pt-BR.json +644 -56
  347. package/neural-interface/lib/account-usage.js +163 -0
  348. package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
  349. package/neural-interface/lib/agent-sandbox-setting.js +57 -0
  350. package/neural-interface/lib/assistant-api.js +274 -52
  351. package/neural-interface/lib/assistant-brains/claude.js +15 -2
  352. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  353. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  354. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  355. package/neural-interface/lib/assistant-budget-split.js +408 -0
  356. package/neural-interface/lib/assistant-budget.js +94 -95
  357. package/neural-interface/lib/assistant-config.js +62 -14
  358. package/neural-interface/lib/assistant-dispatch.js +432 -125
  359. package/neural-interface/lib/assistant-jev.js +26 -3
  360. package/neural-interface/lib/assistant-persona.js +68 -11
  361. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  362. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  363. package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
  364. package/neural-interface/lib/assistant-route-gate.js +19 -3
  365. package/neural-interface/lib/assistant-router.js +373 -42
  366. package/neural-interface/lib/assistant-runtime.js +312 -48
  367. package/neural-interface/lib/assistant-user-writer.js +4 -2
  368. package/neural-interface/lib/augmented-path.js +317 -53
  369. package/neural-interface/lib/claude-accounts.js +342 -31
  370. package/neural-interface/lib/claude-agent-bridge.js +90 -3
  371. package/neural-interface/lib/claude-executable.js +6 -1
  372. package/neural-interface/lib/claude-model-catalog.js +3 -3
  373. package/neural-interface/lib/claude-panel-auth.js +221 -0
  374. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  375. package/neural-interface/lib/cli-install-api.js +137 -0
  376. package/neural-interface/lib/cli-install.js +797 -0
  377. package/neural-interface/lib/cli-path.js +130 -0
  378. package/neural-interface/lib/cli-resolver.js +744 -0
  379. package/neural-interface/lib/cli-update-planner.js +143 -7
  380. package/neural-interface/lib/codex-account-auth.js +180 -0
  381. package/neural-interface/lib/codex-accounts.js +502 -0
  382. package/neural-interface/lib/codex-capabilities.js +2 -1
  383. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  384. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  385. package/neural-interface/lib/codex-panel-auth.js +62 -0
  386. package/neural-interface/lib/codex-session-settings.js +1 -1
  387. package/neural-interface/lib/codex-settings-schema.js +122 -0
  388. package/neural-interface/lib/desktop/config.js +3 -0
  389. package/neural-interface/lib/exec-loop-command.js +3 -1
  390. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  391. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  392. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  393. package/neural-interface/lib/git-bash/install.js +529 -0
  394. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  395. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  396. package/neural-interface/lib/git-bash/manager.js +268 -0
  397. package/neural-interface/lib/git-bash/pins.js +206 -0
  398. package/neural-interface/lib/git-bash/pins.json +34 -0
  399. package/neural-interface/lib/git-bash/resolver.js +194 -0
  400. package/neural-interface/lib/git-read.js +34 -2
  401. package/neural-interface/lib/install-details.js +163 -0
  402. package/neural-interface/lib/mcp-installer.js +14 -2
  403. package/neural-interface/lib/native-loop-providers.js +22 -4
  404. package/neural-interface/lib/native-loop-runtime.js +9 -4
  405. package/neural-interface/lib/os-path.js +319 -0
  406. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  407. package/neural-interface/lib/provider-panel-terminal-auth.js +96 -0
  408. package/neural-interface/lib/pty-host/core.js +2 -1
  409. package/neural-interface/lib/pty-host/manager.js +27 -2
  410. package/neural-interface/lib/remote-policy.js +20 -11
  411. package/neural-interface/lib/session-indexer.js +3 -1
  412. package/neural-interface/lib/session-title-generator.js +6 -3
  413. package/neural-interface/lib/system-about.js +340 -0
  414. package/neural-interface/lib/system-activity.js +66 -0
  415. package/neural-interface/lib/terminal-env.js +11 -3
  416. package/neural-interface/lib/terminal-path.js +55 -0
  417. package/neural-interface/lib/virtual-socket.js +12 -1
  418. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  419. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  420. package/neural-interface/lib/whatsapp/commands.js +13 -14
  421. package/neural-interface/lib/whatsapp/config.js +134 -20
  422. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  423. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  424. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  425. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  426. package/neural-interface/lib/whatsapp/manager.js +92 -32
  427. package/neural-interface/lib/whatsapp/paths.js +11 -1
  428. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  429. package/neural-interface/lib/whatsapp/redact.js +63 -0
  430. package/neural-interface/lib/whatsapp/service.js +343 -91
  431. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  432. package/neural-interface/package-lock.json +2 -2
  433. package/neural-interface/package.json +1 -1
  434. package/neural-interface/public/index.html +2 -0
  435. package/neural-interface/public/index2d.html +2 -0
  436. package/neural-interface/public/offline.html +16 -1
  437. package/neural-interface/public/onboarding.html +96 -2
  438. package/neural-interface/public/shared/account-i18n.js +7 -0
  439. package/neural-interface/public/shared/account-menu.js +542 -0
  440. package/neural-interface/public/shared/account-usage.js +79 -0
  441. package/neural-interface/public/shared/account-view.js +336 -0
  442. package/neural-interface/public/shared/agent-runtime-options.js +28 -8
  443. package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
  444. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  445. package/neural-interface/public/shared/assistant/asst-api.js +39 -2
  446. package/neural-interface/public/shared/assistant/asst-brain-picker.js +288 -32
  447. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  448. package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
  449. package/neural-interface/public/shared/assistant/asst-menu.js +219 -80
  450. package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
  451. package/neural-interface/public/shared/assistant/asst-panel.js +87 -16
  452. package/neural-interface/public/shared/assistant/asst-render.js +38 -7
  453. package/neural-interface/public/shared/assistant/asst-route.js +72 -14
  454. package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
  455. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  456. package/neural-interface/public/shared/assistant/asst-state.js +1 -1
  457. package/neural-interface/public/shared/assistant/asst-styles.js +160 -29
  458. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  459. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  460. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  461. package/neural-interface/public/shared/cdx/cdx-panel.js +49 -108
  462. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  463. package/neural-interface/public/shared/cdx/cdx-styles.js +172 -111
  464. package/neural-interface/public/shared/cdx/cdx-tabs.js +264 -664
  465. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  466. package/neural-interface/public/shared/claude-account-view.js +3 -0
  467. package/neural-interface/public/shared/cli-install-view.js +392 -0
  468. package/neural-interface/public/shared/cli-status.js +282 -22
  469. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  470. package/neural-interface/public/shared/cp/cp-styles.js +46 -0
  471. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  472. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  473. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  474. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  475. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  476. package/neural-interface/public/shared/editor/editor-panel.js +1 -1
  477. package/neural-interface/public/shared/explorer/fx-main.js +1 -1
  478. package/neural-interface/public/shared/git-bash-view.js +145 -0
  479. package/neural-interface/public/shared/html-shell.js +1 -0
  480. package/neural-interface/public/shared/i18n.js +19 -1
  481. package/neural-interface/public/shared/install-details-view.js +86 -0
  482. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
  483. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +36 -1
  484. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
  485. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +169 -2
  486. package/neural-interface/public/shared/pwa-install-page.js +3 -1
  487. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  488. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  489. package/neural-interface/public/shared/settings/settings-ia.js +15 -0
  490. package/neural-interface/public/shared/showcase-privacy.js +490 -0
  491. package/neural-interface/public/shared/storage.js +30 -3
  492. package/neural-interface/public/shared/styles.css +281 -32
  493. package/neural-interface/public/shared/synabun-mascot.js +48 -2
  494. package/neural-interface/public/shared/ui-claude-panel.js +262 -217
  495. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  496. package/neural-interface/public/shared/ui-explorer.js +2 -0
  497. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  498. package/neural-interface/public/shared/ui-judgments.js +10 -1
  499. package/neural-interface/public/shared/ui-loading.js +30 -6
  500. package/neural-interface/public/shared/ui-menubar.js +13 -3
  501. package/neural-interface/public/shared/ui-navbar.js +3 -1
  502. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  503. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  504. package/neural-interface/public/shared/ui-sessions.js +9 -3
  505. package/neural-interface/public/shared/ui-settings.js +55 -3
  506. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  507. package/neural-interface/public/shared/ui-sync.js +8 -0
  508. package/neural-interface/public/shared/ui-terminal.js +17 -0
  509. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  510. package/neural-interface/public/shared/ui-update.js +598 -7
  511. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  512. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  513. package/neural-interface/public/shared/worker-end.js +2 -0
  514. package/neural-interface/public/sw.js +1 -1
  515. package/neural-interface/public/variant/2d/main.js +10 -1
  516. package/neural-interface/public/variant/3d/main.js +10 -1
  517. package/neural-interface/scripts/rebuild-pty.js +14 -2
  518. package/neural-interface/server.js +1384 -679
  519. package/npm-shrinkwrap.json +4424 -0
  520. package/package.json +57 -2
  521. package/postinstall.js +61 -47
  522. package/preuninstall.js +8 -35
  523. package/setup.js +80 -47
  524. package/skills/synabun/modules/idea.md +1 -1
  525. package/skills/synabun/modules/schedule.md +1 -1
  526. package/updater.cmd +2 -2
  527. package/updater.mjs +97 -3
@@ -19,6 +19,8 @@ import {
19
19
  getCachedCodexAccounts,
20
20
  getCachedMcpProfilePresets,
21
21
  getCachedOpencodeAgents,
22
+ getClaudeWorkerAccountId,
23
+ getCodexWorkerAccountId,
22
24
  getEffortLevelsForModel,
23
25
  getModelsForProfile,
24
26
  modelEffortIds,
@@ -27,8 +29,13 @@ import {
27
29
  import { getHiddenModels, HIDDEN_MODELS_EVENT, hiddenModelMatch } from '../ocp-hidden-models.js';
28
30
  import { getProviderMeta } from '../provider-icons.js';
29
31
  import { on } from '../state.js';
30
- import { fetchAssistantCatalog, fetchProjects } from './asst-api.js';
32
+ import { fetchAssistantCatalog, fetchProjects, patchClaudeAccount, patchCodexAccount, setClaudeWorkerAccount, setCodexWorkerAccount } from './asst-api.js';
33
+ import { accountBadge, accountIdentity, accountName, accountText } from '../account-view.js';
34
+ import { autoAccountLabel } from '../account-usage.js';
35
+ import { accountChoiceItems, accountManageItem, accountMenuRow, accountModeItem, openAccountMenu } from './asst-account-menu.js';
31
36
  import { closeMenu, currentMenu, isMenuOpen, openMenu } from './asst-menu.js';
37
+ import { createMascotControl } from './asst-mascot.js';
38
+ import { modelChipForms, modelChipName, modelChipTag } from './asst-model-chip.js';
32
39
  import {
33
40
  ACCOUNT_PROVIDERS,
34
41
  brainRequiresSwitch,
@@ -74,6 +81,10 @@ const TEST_DENSITY_COPY = {
74
81
  thorough: ['Thorough', 'Full coverage, whole suite, live checks.'],
75
82
  };
76
83
 
84
+ // English copy of the Admin sandbox row (i18n: assistant.adminSandbox.hint). Names only Codex: the
85
+ // one provider it changes (lib/agent-sandbox-elevation.js; Claude Code and OpenCode are no-ops).
86
+ export const ADMIN_SANDBOX_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.';
87
+
77
88
  /** "Light" — the level's name (a missing or unknown level is the default one). */
78
89
  export function testDensityLabel(level, t) {
79
90
  const id = normalizeTestDensity(level);
@@ -411,7 +422,8 @@ function tierTag(m, tr) {
411
422
  * opts: { brain, onSelect(target), includeHere, hereLabel, title, t, placement,
412
423
  * current:{provider, model}|null, warnSwitch, providers, onToast(text),
413
424
  * accept(provider, model) → bool (only those models), emptyLabel (when none
414
- * passes), allowCustom (default true), zIndex (see asst-menu.js) }
425
+ * passes), allowCustom (default true), zIndex (see asst-menu.js),
426
+ * accountItems (menu items listed first, under accountTitle) }
415
427
  * target: { provider, model, label, tier, vision, outputs, efforts, price } | { here: true }
416
428
  */
417
429
  export async function openModelMenu(anchor, opts = {}) {
@@ -438,6 +450,9 @@ export async function openModelMenu(anchor, opts = {}) {
438
450
  if (!anchor.isConnected || (loading && currentMenu() !== loading.el)) return null;
439
451
  const metadata = modelCatalogStatus();
440
452
  const items = [];
453
+ if (Array.isArray(opts.accountItems) && opts.accountItems.length) {
454
+ items.push({ kind: 'header', label: opts.accountTitle || tr('assistant.brain.account', 'Account profile') }, ...opts.accountItems, { kind: 'separator' });
455
+ }
441
456
  if (includeHere) {
442
457
  items.push({ kind: 'radio', id: '__here', label: hereLabel || tr('assistant.routes.brainDecides', 'Let the brain decide'), selected: !current, onSelect: () => onSelect?.({ here: true }) });
443
458
  }
@@ -498,7 +513,7 @@ export async function openModelMenu(anchor, opts = {}) {
498
513
  role: 'menu',
499
514
  className: `asst-model-menu${opts.routingEditor ? ' asst-route-model-menu' : ''}`,
500
515
  zIndex: opts.zIndex ?? null,
501
- repositionOnResize: opts.routingEditor === true,
516
+ repositionOnResize: true,
502
517
  filterEmptyLabel: tr('assistant.routes.menu.noMatches', 'No matching models. Check Models for hidden choices.'),
503
518
  });
504
519
  if (opts.routingEditor && menu) {
@@ -582,6 +597,7 @@ export function createBrainPicker(host, hooks = {}) {
582
597
  let loading = null;
583
598
  // The empty state's "Choose a project" (mountEmptyProject): the project field's second button.
584
599
  let heroProject = null;
600
+ let heroProjectMascot = null;
585
601
 
586
602
  function currentBrain() {
587
603
  return normalizeBrain(hooks.getBrain?.());
@@ -621,16 +637,51 @@ export function createBrainPicker(host, hooks = {}) {
621
637
  return accounts.find(a => a.id === brain.accountId) || null;
622
638
  }
623
639
 
640
+ const accountProvider = (brain) => (brain.provider === 'codex' ? 'codex' : 'claude');
624
641
  function accountLabel(brain) {
625
642
  const acc = accountOf(brain);
626
- return acc ? (acc.label || acc.email || acc.id) : (brain.accountId || t('assistant.brain.accountDefault', 'Default'));
643
+ const name = acc ? accountName(acc, accountProvider(brain)) : (brain.accountId || t('assistant.brain.accountDefault', 'Default'));
644
+ // An account says what it runs on (its subscription or its API key), or that it is signed out.
645
+ if (acc && acc.mode !== undefined) {
646
+ const badge = accountBadge(acc, accountProvider(brain));
647
+ return badge.text ? `${name} · ${badge.text}` : name;
648
+ }
649
+ return name;
650
+ }
651
+
652
+ // The "⋯" item's value, in the account menu's words: the name and its plan, "API key" or "Signed out" ("Default · Max").
653
+ function accountValue(brain) {
654
+ const acc = accountOf(brain);
655
+ if (!acc) return brain.accountId === 'auto' ? autoAccountLabel(hooks.t) : (brain.accountId || t('assistant.brain.accountDefault', 'Default'));
656
+ const row = accountMenuRow(acc, { provider: accountProvider(brain), t: hooks.t });
657
+ return [row.name, row.badge.text].filter(Boolean).join(' · ');
658
+ }
659
+
660
+ // The model menu's own row first: it also names the "[1m]" / "[extended]" variants the provider list lacks.
661
+ function modelRow(brain) {
662
+ return catalogModelRow(brain.provider, brain.model) || modelOf(brain);
627
663
  }
628
664
 
629
665
  function modelLabel(brain = currentBrain()) {
630
- const model = modelOf(brain);
666
+ const model = modelRow(brain);
631
667
  return model ? (model.label || modelShortName(model.id)) : (brain.model ? modelShortName(brain.model) : t('assistant.brain.modelDefault', 'Default model'));
632
668
  }
633
669
 
670
+ // The composer chip's account cue: the account menu's initial avatar when the provider has more than one
671
+ // account (or the account is signed out), "Auto" for Auto; null otherwise. Its full words are the tooltip's.
672
+ function accountCue(brain) {
673
+ if (!ACCOUNT_PROVIDERS.has(brain.provider)) return null;
674
+ if (brain.accountId === 'auto') return { text: t('assistant.brain.accountAutoShort', 'Auto'), tone: 'muted' };
675
+ const accounts = brain.provider === 'codex' ? getCachedCodexAccounts() : getCachedClaudeAccounts();
676
+ const acc = accountOf(brain);
677
+ const signedOut = acc?.signedIn === false;
678
+ if (accounts.length < 2 && !signedOut) return null;
679
+ const initial = acc
680
+ ? accountMenuRow(acc, { provider: accountProvider(brain), t: hooks.t }).initial
681
+ : Array.from(String(brain.accountId || t('assistant.brain.accountDefault', 'Default')).trim())[0]?.toLocaleUpperCase();
682
+ return { text: initial || '?', tone: signedOut ? 'warn' : 'muted' };
683
+ }
684
+
634
685
  function values(brain = currentBrain()) {
635
686
  const meta = getProviderMeta(brain.provider);
636
687
  const efforts = effortsFor(brain);
@@ -653,6 +704,26 @@ export function createBrainPicker(host, hooks = {}) {
653
704
  };
654
705
  }
655
706
 
707
+ /**
708
+ * The permission menu's Admin sandbox row (a check: the global setting, every agent SynaBun starts),
709
+ * or null while the host has none to show (a server that does not serve it). Its tooltip and
710
+ * aria-label say the value in effect.
711
+ */
712
+ function adminSandboxItem() {
713
+ const on = typeof hooks.getAdminSandbox === 'function' ? hooks.getAdminSandbox() : null;
714
+ if (typeof on !== 'boolean') return null;
715
+ const label = t('assistant.adminSandbox.label', 'Admin sandbox');
716
+ const value = on ? t('assistant.adminSandbox.on', 'On') : t('assistant.adminSandbox.off', 'Off');
717
+ const hint = t('assistant.adminSandbox.hint', ADMIN_SANDBOX_HINT);
718
+ return {
719
+ kind: 'check', id: 'admin-sandbox', label, desc: hint, checked: on,
720
+ className: 'asst-admin-sandbox-item',
721
+ title: `${label}: ${value}. ${hint}`,
722
+ ariaLabel: t('assistant.adminSandbox.aria', '{label}: {value}. Only changes anything when SynaBun runs on Windows.', { label, value }),
723
+ onSelect: () => hooks.onAdminSandbox?.(!on),
724
+ };
725
+ }
726
+
656
727
  /** The session's Test density, or null while the host has none to show (a server that does not know it). */
657
728
  function testDensity() {
658
729
  const level = typeof hooks.getTestDensity === 'function' ? hooks.getTestDensity() : null;
@@ -673,14 +744,117 @@ export function createBrainPicker(host, hooks = {}) {
673
744
  btn.setAttribute('aria-label', aria || tooltip || label || field);
674
745
  }
675
746
 
747
+ // The composer's model chip (asst-model-chip.js): the model's short name, a context tag for a variant,
748
+ // the account cue and "API" on an API key, never the credential's name (the provider icon says it).
749
+ // Model label, id, account, who it is and what it runs on are the tooltip and aria-label.
750
+ // fitModelChip() draws the longest form that fits the row.
751
+ let chipForms = [];
752
+ let chipParts = null;
753
+ let chipCatalog = null;
754
+ function renderModelChip(brain, v) {
755
+ const btn = buttons.model;
756
+ // The catalog was dropped (a model hidden, a provider's list changed): read it again, then the label.
757
+ if (!cachedModelCatalog() && !chipCatalog) {
758
+ chipCatalog = loadModelCatalog().catch(() => null).then(() => {
759
+ chipCatalog = null;
760
+ if (!destroyed && cachedModelCatalog()) render();
761
+ });
762
+ }
763
+ const row = modelRow(brain);
764
+ const id = brain.model || row?.id || '';
765
+ const { name, raw } = id ? modelChipName(row?.label, id) : { name: v.model, raw: false };
766
+ const tag = modelChipTag(id, { contextWindow: row?.contextWindow, label: row?.label });
767
+ const cue = accountCue(brain);
768
+ const p = accountProvider(brain);
769
+ const acc = ACCOUNT_PROVIDERS.has(brain.provider) ? accountOf(brain) : null;
770
+ const api = acc && acc.signedIn !== false && acc.mode === 'apiKey' ? accountText('apiShort', {}, p) : '';
771
+ const names = (cachedModelCatalog()?.[brain.provider] || []).map(m => modelChipName(m.label, m.id).name);
772
+ chipForms = modelChipForms({ name, names, tag: !!tag, cue: !!cue, api: !!api });
773
+
774
+ const who = !ACCOUNT_PROVIDERS.has(brain.provider) ? ''
775
+ : brain.accountId === 'auto' ? autoAccountLabel(hooks.t)
776
+ : acc ? accountName(acc, p) : (brain.accountId || t('assistant.brain.accountDefault', 'Default'));
777
+ const model = brain.model && v.model !== brain.model ? `${v.model} · ${brain.model}` : (brain.model || v.model);
778
+ const tip = [
779
+ v.meta.label, model, who ? t('assistant.brain.asAccount', 'as {account}', { account: who }) : '',
780
+ acc ? accountIdentity(acc) : '', acc ? accountBadge(acc, p).text : '',
781
+ ].filter(Boolean).join(' · ');
782
+ setBtn('model', { label: '', icon: v.meta.icon, color: v.meta.color, tooltip: tip, aria: `${t('assistant.brain.model', 'Model')}: ${tip}` });
783
+
784
+ const label = btn.querySelector('.asst-dd-label');
785
+ label.classList.add('asst-model-label');
786
+ chipParts = {
787
+ name: el('span', 'asst-model-name', name),
788
+ tag: el('span', 'asst-model-tag', tag),
789
+ cue: el('span', 'asst-acct-avatar asst-model-cue', cue?.text || ''),
790
+ api: el('span', 'asst-acct-badge asst-model-api', api),
791
+ };
792
+ if (cue) chipParts.cue.dataset.tone = cue.tone;
793
+ label.replaceChildren(chipParts.name, chipParts.tag, chipParts.cue, chipParts.api);
794
+ // A display name ("Sonnet 5") reads in sans; a raw id nothing names ("opus") stays mono.
795
+ btn.toggleAttribute('data-raw', raw);
796
+ fitModelChip();
797
+ }
798
+
799
+ function drawChipForm(form) {
800
+ chipParts.name.textContent = form.name;
801
+ chipParts.tag.hidden = !form.tag;
802
+ chipParts.cue.hidden = !form.cue;
803
+ chipParts.api.hidden = !form.api;
804
+ }
805
+
806
+ // The first form, longest first, that leaves the row as the shortest one does: no part cut, no other
807
+ // field narrower, the row no taller (it wraps below 320px). None does: the shortest, its name squeezed.
808
+ function fitModelChip() {
809
+ const btn = buttons.model;
810
+ if (destroyed || !btn || !chipParts || !chipForms.length) return;
811
+ btn.removeAttribute('data-squeezed');
812
+ const label = btn.querySelector('.asst-dd-label');
813
+ const slot = btn.parentElement;
814
+ // Nothing to measure (not on screen, no layout): the whole chip, until the row has a width.
815
+ if (chipForms.length === 1 || !isRendered(btn) || !slot || typeof label?.getBoundingClientRect !== 'function') { drawChipForm(chipForms[0]); return; }
816
+ const row = slot.parentElement || null;
817
+ const others = [...slot.querySelectorAll('.asst-dd-label')].filter(n => n !== label && n.getClientRects().length > 0);
818
+ const width = n => n.getBoundingClientRect().width;
819
+ const cut = () => {
820
+ const right = label.getBoundingClientRect().right + 0.5;
821
+ return Object.values(chipParts).some(n => !n.hidden && n.getBoundingClientRect().right > right);
822
+ };
823
+ const shortest = chipForms[chipForms.length - 1];
824
+ drawChipForm(shortest);
825
+ const base = { height: row?.offsetHeight || 0, widths: others.map(width) };
826
+ const fits = () => !cut()
827
+ && (row?.offsetHeight || 0) <= base.height + 0.5
828
+ && others.every((n, i) => width(n) >= base.widths[i] - 0.5)
829
+ && !(slot.scrollWidth > slot.clientWidth + 1);
830
+ for (const form of chipForms.slice(0, -1)) {
831
+ drawChipForm(form);
832
+ if (fits()) return;
833
+ }
834
+ drawChipForm(shortest);
835
+ btn.toggleAttribute('data-squeezed', cut());
836
+ }
837
+
838
+ // The row's width decides the form (a panel resized, a tab shown, the fields that collapse to glyphs).
839
+ const fitHost = buttons.model.parentElement?.parentElement || buttons.model.parentElement;
840
+ let fitFrame = 0;
841
+ let fitWidth = -1;
842
+ const scheduleFit = () => {
843
+ if (fitFrame || destroyed || typeof requestAnimationFrame !== 'function') return;
844
+ fitFrame = requestAnimationFrame(() => { fitFrame = 0; fitModelChip(); });
845
+ };
846
+ const fitObserver = typeof ResizeObserver === 'function' && fitHost ? new ResizeObserver((entries) => {
847
+ const w = Math.round(entries[entries.length - 1]?.contentRect?.width || 0);
848
+ if (w !== fitWidth) { fitWidth = w; scheduleFit(); }
849
+ }) : null;
850
+ fitObserver?.observe(fitHost);
851
+ if (typeof document !== 'undefined') document.fonts?.ready?.then(scheduleFit).catch(() => {});
852
+
676
853
  function render() {
677
854
  if (destroyed) return;
678
855
  const brain = currentBrain();
679
856
  const v = values(brain);
680
- const modelTip = [v.meta.label, brain.model || t('assistant.brain.modelDefault', 'Default model'), v.account ? t('assistant.brain.asAccount', 'as {account}', { account: v.account }) : ''].filter(Boolean).join(' · ');
681
- setBtn('model', { label: v.model, icon: v.meta.icon, color: v.meta.color, tooltip: modelTip, aria: `${t('assistant.brain.model', 'Model')}: ${modelTip}` });
682
- // A display name ("Sonnet 5") reads in sans; a raw id nothing names ("opus[1m]") stays mono.
683
- buttons.model?.toggleAttribute('data-raw', /^[a-z0-9][\w.\-[\]:/]*$/.test(String(v.model || '')));
857
+ renderModelChip(brain, v);
684
858
 
685
859
  if (brain.provider === 'opencode') {
686
860
  setBtn('effort', { label: v.effort, icon: ICON_AGENT, tooltip: `${t('assistant.brain.agent', 'OpenCode agent')}: ${v.effort}` });
@@ -688,9 +862,12 @@ export function createBrainPicker(host, hooks = {}) {
688
862
  setBtn('effort', { label: v.effort, icon: ICON_EFFORT, hidden: !v.effortSupported, tooltip: `${t('assistant.brain.effort', 'Thinking effort')}: ${v.effort}` });
689
863
  }
690
864
  setBtn('variant', { label: v.variant, icon: ICON_EFFORT, hidden: !v.variantSupported, tooltip: `${t('assistant.brain.variant', 'Model variant')}: ${v.variant}` });
691
- // Inline only when there is a choice to make; the "⋯" menu always has it (add / manage).
692
- const accountChoices = ACCOUNT_PROVIDERS.has(brain.provider) ? (brain.provider === 'codex' ? getCachedCodexAccounts() : getCachedClaudeAccounts()).length : 0;
693
- setBtn('account', { label: v.account, icon: ICON_ACCOUNT, hidden: accountChoices < 2, tooltip: `${t('assistant.brain.account', 'Account profile')}: ${v.account}` });
865
+ // A Claude or Codex brain always shows it: which account and which credential (its subscription or
866
+ // its API key) it runs on, or that the account is signed out.
867
+ const brainAcc = ACCOUNT_PROVIDERS.has(brain.provider) ? accountOf(brain) : null;
868
+ const accountTip = [v.account, brainAcc ? accountIdentity(brainAcc) : ''].filter(Boolean).join(' · ');
869
+ setBtn('account', { label: v.account, icon: ICON_ACCOUNT, hidden: true, tooltip: `${t('assistant.brain.account', 'Account profile')}: ${accountTip}` });
870
+ buttons.account?.classList.toggle('asst-dd-warn', !!brainAcc && brainAcc.signedIn === false);
694
871
  setBtn('project', { label: v.project, icon: ICON_FOLDER, tooltip: brain.project || t('assistant.brain.projectNoneTip', 'Working project: home directory'), aria: `${t('assistant.brain.project', 'Working project')}: ${brain.project || v.project}` });
695
872
  setBtn('mode', { label: v.mode, icon: ICON_SHIELD, tooltip: `${t('assistant.brain.mode', 'Permission mode')}: ${v.mode}` });
696
873
  const tests = testDensity();
@@ -721,10 +898,13 @@ export function createBrainPicker(host, hooks = {}) {
721
898
  * sends focus back to it. A new slot replaces the last one; null removes it.
722
899
  */
723
900
  function mountEmptyProject(slot) {
901
+ if (heroProject && isMenuOpen(heroProject)) closeMenu();
902
+ heroProjectMascot?.destroy();
903
+ heroProjectMascot = null;
724
904
  heroProject?.remove();
725
905
  heroProject = null;
726
906
  if (destroyed || !slot || typeof slot.appendChild !== 'function') return null;
727
- const btn = el('button', 'asst-dd asst-empty-project-btn');
907
+ const btn = el('button', 'asst-dd asst-empty-control-btn asst-empty-project-btn');
728
908
  btn.type = 'button';
729
909
  btn.dataset.field = 'project';
730
910
  btn.setAttribute('aria-haspopup', 'menu');
@@ -743,6 +923,7 @@ export function createBrainPicker(host, hooks = {}) {
743
923
  });
744
924
  slot.appendChild(btn);
745
925
  heroProject = btn;
926
+ heroProjectMascot = createMascotControl(btn, { director: hooks.mascot, pose: 'project' });
746
927
  renderEmptyProject();
747
928
  return btn;
748
929
  }
@@ -761,7 +942,7 @@ export function createBrainPicker(host, hooks = {}) {
761
942
  { field: 'mode', label: t('assistant.brain.mode', 'Permission mode'), value: v.mode, icon: ICON_SHIELD, hidden: false },
762
943
  { field: 'tests', label: t('assistant.tests.title', 'Test density'), value: testDensity() ? testDensityLabel(testDensity(), hooks.t) : '', icon: testsIcon(testDensityBars(testDensity())), hidden: !testDensity() },
763
944
  { field: 'mcp', label: t('assistant.brain.mcp', 'MCP tool profile'), value: v.mcp, icon: ICON_PLUG, hidden: false },
764
- { field: 'account', label: t('assistant.brain.account', 'Account profile'), value: v.account, icon: ICON_ACCOUNT, hidden: !ACCOUNT_PROVIDERS.has(brain.provider) },
945
+ { field: 'account', label: t('assistant.brain.accountShort', 'Account'), value: accountValue(brain), icon: ICON_ACCOUNT, hidden: !ACCOUNT_PROVIDERS.has(brain.provider) },
765
946
  ];
766
947
  }
767
948
 
@@ -818,12 +999,15 @@ export function createBrainPicker(host, hooks = {}) {
818
999
  const anchor = anchorFor(field, anchorOverride);
819
1000
  const brain = currentBrain();
820
1001
  if (field === 'model') {
1002
+ if (ACCOUNT_PROVIDERS.has(brain.provider)) await ensureLoaded(brain.provider);
821
1003
  await openModelMenu(anchor, {
822
1004
  brain,
823
1005
  t: hooks.t,
824
1006
  placement,
825
1007
  current: { provider: brain.provider, model: brain.model },
826
1008
  warnSwitch: true,
1009
+ accountItems: ACCOUNT_PROVIDERS.has(brain.provider) ? accountItems(brain) : [],
1010
+ accountTitle: accountText('menuTitle', {}, accountProvider(brain)),
827
1011
  onToast: hooks.onToast,
828
1012
  onManageModels: hooks.onManageModels,
829
1013
  onSelect: (target) => {
@@ -888,29 +1072,24 @@ export function createBrainPicker(host, hooks = {}) {
888
1072
 
889
1073
  if (field === 'account') {
890
1074
  if (!ACCOUNT_PROVIDERS.has(brain.provider)) return;
891
- const accounts = brain.provider === 'codex' ? getCachedCodexAccounts() : getCachedClaudeAccounts();
892
- const items = accounts.map((a) => ({
893
- kind: 'radio',
894
- id: a.id,
895
- label: a.label || a.email || a.id,
896
- desc: a.email && a.email !== a.label ? a.email : (a.isDefault ? t('assistant.accounts.ambient', 'ambient config') : ''),
897
- selected: a.id === brain.accountId,
898
- onSelect: () => commit({ accountId: a.id }, 'account'),
899
- }));
900
- items.push({ kind: 'separator' });
901
- items.push({ id: '__add', label: t('assistant.accounts.add', 'Add account…'), onSelect: () => hooks.onAddAccount?.(brain.provider) });
902
- items.push({ id: '__manage', label: t('assistant.accounts.manage', 'Manage…'), onSelect: () => hooks.onManageAccounts?.(brain.provider) });
903
- menu(t('assistant.brain.account', 'Account profile'), items);
1075
+ // Kept inside the panel that holds the anchor (the sidepanel, or the tab's own root).
1076
+ openAccountMenu(anchor, accountMenu(brain, anchor.closest?.('.assistant-panel') || anchor.closest?.('.asst-root') || null));
904
1077
  return;
905
1078
  }
906
1079
 
907
1080
  if (field === 'project') {
1081
+ const actor = anchor === heroProject ? heroProjectMascot : null;
1082
+ const pick = project => {
1083
+ const changed = (currentBrain().project || null) !== (project || null);
1084
+ commit({ project }, 'project');
1085
+ actor?.commit(changed ? 'hop' : 'nod');
1086
+ };
908
1087
  const projects = _projectsCache || [];
909
- const items = [{ kind: 'radio', id: '', label: t('assistant.brain.projectNone', 'Home directory'), desc: t('assistant.brain.projectNoneDesc', 'no project'), selected: !brain.project, restoreFocus, onSelect: () => commit({ project: null }, 'project') }];
910
- for (const p of projects) items.push({ kind: 'radio', id: p.path, label: p.label || basename(p.path), desc: p.path, selected: p.path === brain.project, restoreFocus, onSelect: () => commit({ project: p.path }, 'project') });
1088
+ const items = [{ kind: 'radio', id: '', label: t('assistant.brain.projectNone', 'Home directory'), desc: t('assistant.brain.projectNoneDesc', 'no project'), selected: !brain.project, restoreFocus, onSelect: () => pick(null) }];
1089
+ for (const p of projects) items.push({ kind: 'radio', id: p.path, label: p.label || basename(p.path), desc: p.path, selected: p.path === brain.project, restoreFocus, onSelect: () => pick(p.path) });
911
1090
  items.push({ kind: 'separator' });
912
- items.push({ kind: 'input', placeholder: t('assistant.brain.projectCustom', 'Type a directory path…'), value: brain.project && !projects.some(p => p.path === brain.project) ? brain.project : '', restoreFocus, onSubmit: (v) => commit({ project: v }, 'project') });
913
- menu(t('assistant.brain.project', 'Working project'), items, { width: 280, filter: projects.length > 8, filterPlaceholder: t('assistant.brain.filterProjects', 'Filter projects…') });
1091
+ items.push({ kind: 'input', placeholder: t('assistant.brain.projectCustom', 'Type a directory path…'), value: brain.project && !projects.some(p => p.path === brain.project) ? brain.project : '', restoreFocus, onSubmit: pick });
1092
+ menu(t('assistant.brain.project', 'Working project'), items, { width: 280, filter: projects.length > 8, filterPlaceholder: t('assistant.brain.filterProjects', 'Filter projects…'), onOpen: node => actor?.open(node), onClose: () => actor?.close() });
914
1093
  return;
915
1094
  }
916
1095
 
@@ -949,10 +1128,77 @@ export function createBrainPicker(host, hooks = {}) {
949
1128
  onSelect: () => commit({ permissionMode: m.id }, 'mode'),
950
1129
  });
951
1130
  }
1131
+ // The global Admin sandbox switch (every agent SynaBun starts, not this conversation), under its own header.
1132
+ const adminSandbox = adminSandboxItem();
1133
+ if (adminSandbox) items.push({ kind: 'separator' }, { kind: 'header', label: t('assistant.adminSandbox.group', 'Every agent') }, adminSandbox);
952
1134
  menu(t('assistant.brain.mode', 'Permission mode'), items);
953
1135
  }
954
1136
  }
955
1137
 
1138
+ // The cog's account menu (asst-account-menu.js): every account once, the conversation's and the
1139
+ // workers' choice, kept inside `bounds`. What each choice does is the same as the composer's.
1140
+ function accountMenu(brain, bounds) {
1141
+ const codex = brain.provider === 'codex';
1142
+ const refetch = () => (codex ? fetchCodexAccounts(true) : fetchClaudeAccounts(true));
1143
+ return {
1144
+ provider: codex ? 'codex' : 'claude',
1145
+ t: hooks.t,
1146
+ placement,
1147
+ bounds,
1148
+ read: () => ({
1149
+ accounts: codex ? getCachedCodexAccounts() : getCachedClaudeAccounts(),
1150
+ currentId: currentBrain().accountId,
1151
+ workerId: codex ? getCodexWorkerAccountId() : getClaudeWorkerAccountId(),
1152
+ running: hooks.getRunning?.() === true,
1153
+ }),
1154
+ onAuto: () => commit({ accountId: 'auto' }, 'account'),
1155
+ onPick: (id) => commit({ accountId: id }, 'account'),
1156
+ onSetWorker: async (id) => { await (codex ? setCodexWorkerAccount : setClaudeWorkerAccount)(id); await refetch(); render(); },
1157
+ onSetMode: async (id, mode) => {
1158
+ const before = currentBrain();
1159
+ try {
1160
+ await (codex ? patchCodexAccount : patchClaudeAccount)(id, { mode }); await refetch();
1161
+ hooks.onChange?.(currentBrain(), { requiresSwitch: true, restartBrain: true, field: 'accountMode', previous: before }); render();
1162
+ }
1163
+ catch (err) { hooks.onToast?.(err?.message || String(err)); }
1164
+ },
1165
+ onAdd: () => hooks.onAddAccount?.(brain.provider),
1166
+ onManage: (id) => (id ? hooks.onManageAccounts?.(brain.provider, id) : hooks.onManageAccounts?.(brain.provider)),
1167
+ onError: (err) => hooks.onToast?.(err?.message || String(err)),
1168
+ };
1169
+ }
1170
+
1171
+ // The composer's Model menu lists the brain's accounts too (Claude or Codex), as the cog's account menu
1172
+ // does (asst-account-menu.js): Auto, every account once (a signed-out one opens its sign-in), the Uses
1173
+ // switch of an account holding both credentials, Add account… and Manage….
1174
+ function accountItems(brain) {
1175
+ const codex = brain.provider === 'codex';
1176
+ const p = codex ? 'codex' : 'claude';
1177
+ const accounts = codex ? getCachedCodexAccounts() : getCachedClaudeAccounts();
1178
+ const refetch = () => (codex ? fetchCodexAccounts(true) : fetchClaudeAccounts(true));
1179
+ const items = accountChoiceItems({
1180
+ t: hooks.t,
1181
+ rows: accounts.map((a) => accountMenuRow(a, { provider: p, t: hooks.t })),
1182
+ currentId: brain.accountId,
1183
+ onAuto: () => commit({ accountId: 'auto' }, 'account'),
1184
+ onPick: (id) => commit({ accountId: id }, 'account'),
1185
+ onSignIn: (id) => hooks.onManageAccounts?.(brain.provider, id),
1186
+ });
1187
+ const uses = accountModeItem({
1188
+ provider: p, t: hooks.t, account: accounts.find((a) => a.id === brain.accountId), running: hooks.getRunning?.() === true,
1189
+ onSetMode: async (id, mode) => {
1190
+ try {
1191
+ await (codex ? patchCodexAccount : patchClaudeAccount)(id, { mode }); await refetch();
1192
+ hooks.onChange?.(currentBrain(), { requiresSwitch: true, restartBrain: true, field: 'accountMode', previous: brain }); render();
1193
+ }
1194
+ catch (err) { hooks.onToast?.(err?.message || String(err)); }
1195
+ },
1196
+ });
1197
+ if (uses) items.push(uses);
1198
+ items.push(accountManageItem({ t: hooks.t, onAdd: () => hooks.onAddAccount?.(brain.provider), onManage: () => hooks.onManageAccounts?.(brain.provider) }));
1199
+ return items;
1200
+ }
1201
+
956
1202
  async function switchProvider(provider, model = '') {
957
1203
  const before = currentBrain();
958
1204
  if (provider === before.provider) return;
@@ -976,6 +1222,16 @@ export function createBrainPicker(host, hooks = {}) {
976
1222
 
977
1223
  render();
978
1224
  refresh().catch(() => {});
1225
+ // Any window changed a Claude or Codex account (signed in or out, a key, its mode, the worker choice,
1226
+ // added, removed): its list is read again and the picker re-drawn, so it never shows a stale state.
1227
+ const accountsChanged = (provider) => (msg) => {
1228
+ const which = provider || (msg?.provider === 'codex' ? 'codex' : msg?.provider === 'claude-code' ? 'claude-code' : null);
1229
+ const jobs = [];
1230
+ if (!which || which === 'claude-code') jobs.push(fetchClaudeAccounts(true));
1231
+ if (!which || which === 'codex') jobs.push(fetchCodexAccounts(true));
1232
+ Promise.all(jobs).then(() => { if (!destroyed) render(); }).catch(() => {});
1233
+ };
1234
+ const offAccounts = [on('sync:claude:accounts-changed', accountsChanged('claude-code')), on('sync:codex:accounts-changed', accountsChanged('codex')), on('sync:assistant:accounts-changed', accountsChanged(null))];
979
1235
 
980
1236
  return {
981
1237
  el: root,
@@ -989,6 +1245,6 @@ export function createBrainPicker(host, hooks = {}) {
989
1245
  mountEmptyProject,
990
1246
  setBrain() { render(); },
991
1247
  close: closeMenu,
992
- destroy() { destroyed = true; closeMenu(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
1248
+ destroy() { destroyed = true; fitObserver?.disconnect(); if (fitFrame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(fitFrame); for (const off of offAccounts) { try { off?.(); } catch {} } closeMenu(); heroProjectMascot?.destroy(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
993
1249
  };
994
1250
  }