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
@@ -1,6 +1,8 @@
1
1
  // Model routes: draft-only editor. All model data comes through asst-route-models.
2
2
  // The global default and saved preferences are written together only on Save.
3
- import { clampEffortId, getEffortLevelsForModel, modelEffortIds } from '../agent-runtime-options.js';
3
+ import { clampEffortId, getEffortLevelsForModel, modelEffortIds, fetchClaudeAccounts, fetchCodexAccounts, getCachedClaudeAccounts, getCachedCodexAccounts, getClaudeWorkerAccountId, getCodexWorkerAccountId } from '../agent-runtime-options.js';
4
+ import { accountSummary, accountName, accountBadge, accountIdentity } from '../account-view.js';
5
+ import { accountUsageText, autoAccountLabel } from '../account-usage.js';
4
6
  import { getProviderMeta } from '../provider-icons.js';
5
7
  import { getRouting, putRouting } from './asst-api.js';
6
8
  import { catalogModelRow, hiddenModelFor, ICON_CARET, openModelMenu } from './asst-brain-picker.js';
@@ -30,7 +32,33 @@ function translator(t) {
30
32
  return typeof text === 'string' && text !== key ? text : String(fallback).replace(/\{(\w+)\}/g, (_, k) => params?.[k] ?? `{${k}}`);
31
33
  };
32
34
  }
33
- function choiceKey(p) { return p ? JSON.stringify([p.kind || 'direct', p.provider || '', p.model || '', p.effort || '']) : ''; }
35
+ function choiceKey(p) { return p ? JSON.stringify([p.kind || 'direct', p.provider || '', p.model || '', p.effort || '', p.accountId || '']) : ''; }
36
+
37
+ export function routeAccountView(pref, task, brain, accounts, workers = {}, t = null) {
38
+ const tr = translator(t);
39
+ const empty = { label: tr('assistant.routes.editorUnusedValue', '—'), readOnly: true, options: [], hint: '' };
40
+ if (!pref || pref.provider === 'opencode') return empty;
41
+ const direct = task.id === 'chat' || routeExecutionKind(pref, task, brain) === 'direct';
42
+ const provider = task.id === 'chat' ? brain.provider : pref.provider;
43
+ if (!['claude-code', 'codex'].includes(provider)) return empty;
44
+ const rows = accounts[provider] || [];
45
+ const providerView = provider === 'codex' ? 'codex' : 'claude';
46
+ const id = task.id === 'chat' ? brain.accountId || 'default' : pref.accountId || (direct ? brain.accountId || 'default' : null);
47
+ const selected = rows.find(a => a.id === id);
48
+ const missing = id && id !== 'auto' && !selected;
49
+ const removed = tr('assistant.routes.account.removed', '{id} · account removed', { id });
50
+ const warning = missing ? tr('assistant.routes.account.removedHint', 'Account removed. Save another account choice before running this route.') : selected?.signedIn === false ? tr('assistant.routes.account.signedOut', 'Signed out. Sign in from the provider account menu before running this route.') : '';
51
+ const worker = rows.find(a => a.id === (workers[provider] || 'default'));
52
+ const label = id === 'auto' ? autoAccountLabel(t) : selected ? accountSummary(selected, providerView) : missing ? removed : tr('assistant.routes.account.workersDefault', 'Workers default');
53
+ const options = [
54
+ { id: '', label: direct ? tr('assistant.routes.editorInConversation', 'This conversation') : tr('assistant.routes.account.workersDefault', 'Workers default'), desc: worker ? accountSummary(worker, providerView) : '', selected: !pref.accountId },
55
+ { id: 'auto', label: autoAccountLabel(t), desc: tr('assistant.accountUsage.autoHint', 'Signed-in subscriptions only. Another account runs on a worker.'), selected: id === 'auto' },
56
+ ...rows.map(a => ({ id: a.id, label: accountName(a, providerView), tag: accountBadge(a, providerView).text, desc: a.signedIn === false ? tr('assistant.routes.account.signedOut', 'Signed out. Sign in from the provider account menu before running this route.') : [accountIdentity(a), accountUsageText(a, t)].filter(Boolean).join(' · '), selected: !!pref.accountId && a.id === id })),
57
+ ];
58
+ return { label, readOnly: task.id === 'chat', options, warning, hint: warning || (selected ? `${label} · ${accountUsageText(selected, t)}` : label) };
59
+ }
60
+
61
+ export function routeModelAccount(pref, provider) { return pref?.provider === provider ? pref.accountId || null : null; }
34
62
 
35
63
  /** Only actual choices change the draft. Catalog label/by updates are not user edits. */
36
64
  export function routeEditorPatch(mode, initialMode, prefs, edits) {
@@ -307,7 +335,7 @@ export async function openRoutesEditor(hooks = {}) {
307
335
  const note = el('div', 'asst-routes-catalog-note'); note.setAttribute('role', 'status'); note.append(el('span', '', tr('assistant.routes.editorState.modelsFailed', 'Could not refresh the model list. Showing provider lists where available. Reload models to verify your choices.'))); table.append(note);
308
336
  } else if (!rowsOf(state.catalog).length) table.append(el('div', 'asst-routes-catalog-note', tr('assistant.routes.editorState.modelsEmpty', 'No models are available. Check Models or connect a provider, then reload.')));
309
337
  const columns = el('div', 'asst-routes-columns'); columns.setAttribute('aria-hidden', 'true');
310
- for (const key of ['task', 'model', 'effort', 'run', 'reset']) columns.append(el('span', '', tr(`assistant.routes.columns.${key}`, key)));
338
+ for (const key of ['task', 'model', 'effort', 'run', 'account', 'reset']) columns.append(el('span', '', tr(`assistant.routes.columns.${key}`, key)));
311
339
  table.append(columns);
312
340
  const list = el('div', 'asst-routes-list');
313
341
  const taskList = tasks();
@@ -363,7 +391,7 @@ export async function openRoutesEditor(hooks = {}) {
363
391
  else if (target?.model) {
364
392
  const levels = modelEffortIds(catalogModelRow(target.provider, target.model, state.catalog));
365
393
  const effort = !pref?.effort ? null : levels ? clampEffortId(pref.effort, levels) : pref.provider === target.provider ? pref.effort : null;
366
- setChoice(task.id, { kind: task.dispatchOnly ? 'dispatch' : pref?.kind || task.defaultKind || 'direct', provider: target.provider, model: target.model, effort, label: target.label || target.model }, modelBtn.dataset.focusKey);
394
+ setChoice(task.id, { kind: task.dispatchOnly ? 'dispatch' : pref?.kind || task.defaultKind || 'direct', provider: target.provider, model: target.model, effort, accountId: routeModelAccount(pref, target.provider), label: target.label || target.model }, modelBtn.dataset.focusKey);
367
395
  }
368
396
  },
369
397
  });
@@ -420,9 +448,20 @@ export async function openRoutesEditor(hooks = {}) {
420
448
  if (task.dispatchOnly) run.title = tr('assistant.routes.workerOnly', 'Worker only');
421
449
  else if (pref && pref.provider !== brain().provider && task.id !== 'chat') run.title = tr('assistant.routes.otherProviderWorker', 'A model from another provider runs on a worker.');
422
450
  const runCell = el('div', 'asst-routes-cell asst-routes-run-cell'); runCell.append(el('span', 'asst-routes-cell-label', tr('assistant.routes.columns.run', 'Runs in')), run); node.append(runCell);
451
+ const account = routeAccountView(pref, task, brain(), { 'claude-code': getCachedClaudeAccounts(), codex: getCachedCodexAccounts() }, { 'claude-code': getClaudeWorkerAccountId(), codex: getCodexWorkerAccountId() }, hooks.t);
452
+ const accountCell = el('div', 'asst-routes-cell asst-routes-account-cell');
453
+ const accountBtn = fieldButton('asst-routes-account', task, tr('assistant.routes.columns.account', 'Account'), account.label, () => {
454
+ state.menuFocus = `${task.id}-account`;
455
+ openMenuFor(accountBtn, tr('assistant.routes.columns.account', 'Account'), account.options.map(o => ({ ...o, kind: 'radio', onSelect: () => setChoice(task.id, { ...pref, accountId: o.id || null }, `${task.id}-account`) })));
456
+ });
457
+ accountBtn.dataset.focusKey = `${task.id}-account`; accountBtn.disabled = account.readOnly; accountBtn.title = account.hint;
458
+ if (!account.readOnly) caret(accountBtn);
459
+ if (account.warning) accountBtn.classList.add('asst-dd-warn');
460
+ accountCell.append(el('span', 'asst-routes-cell-label', tr('assistant.routes.columns.account', 'Account')), accountBtn); node.append(accountCell);
423
461
  const resetCell = el('div', 'asst-routes-reset-cell');
424
462
  if (pref) { const reset = button('asst-iconbtn asst-routes-reset', tr('assistant.routes.reset', 'Reset {taskClass}', { taskClass: task.label }), () => setChoice(task.id, null, `${task.id}-model`)); reset.dataset.focusKey = `${task.id}-reset`; reset.title = reset.getAttribute('aria-label'); reset.innerHTML = ICON_RESET; resetCell.append(reset); }
425
463
  node.append(resetCell);
464
+ if (account.warning) { const note = el('div', 'asst-routes-row-warning', account.warning); note.id = `${nameId}-account-warning`; accountBtn.setAttribute('aria-describedby', note.id); node.append(note); }
426
465
  if (capabilityNote) node.append(capabilityNote);
427
466
  if (issue) { description.hidden = true; node.querySelector('.asst-routes-row-note')?.remove(); const note = el('div', 'asst-routes-row-warning', issueText(issue, task)); note.title = note.textContent; note.id = `${nameId}-issue`; modelBtn.setAttribute('aria-describedby', `${source.id} ${note.id}`); modelBtn.title = `${label} — ${note.textContent}`; node.append(note); }
428
467
  if (task.id === 'chat' || task.id === 'vision') {
@@ -453,7 +492,7 @@ export async function openRoutesEditor(hooks = {}) {
453
492
  const pending = preserve ? changes() : {};
454
493
  state.loading = true; state.error = ''; state.modelsLoading = true; render();
455
494
  try {
456
- const [data, result] = await Promise.all([getRouting(), loadRouteModels().then(catalog => ({ catalog })).catch(() => ({ catalog: state.catalog, error: true }))]);
495
+ const [data, result] = await Promise.all([getRouting(), loadRouteModels().then(catalog => ({ catalog })).catch(() => ({ catalog: state.catalog, error: true })), Promise.allSettled([fetchClaudeAccounts(true), fetchCodexAccounts(true)])]);
457
496
  if (!overlay.isConnected) return;
458
497
  state.data = data; state.version = data.version ?? null; state.initialMode = normalizeRouteMode(data.routing?.defaultMode, DEFAULT_ROUTE_MODE);
459
498
  state.mode = pending.defaultMode || state.initialMode; state.prefs = { ...(data.routing?.preferences || {}) }; state.patch = pending.preferences || {};
@@ -485,7 +524,7 @@ export async function openRoutesEditor(hooks = {}) {
485
524
  import { openMenu } from './asst-menu.js';
486
525
  function openMenuFor(anchor, title, items) {
487
526
  if (currentMenu()) { closeMenu({ restoreFocus: true }); return null; }
488
- const menu = openMenu(anchor, { title, items, className: 'asst-route-field-menu', role: 'menu' });
527
+ const menu = openMenu(anchor, { title, items, className: `asst-route-field-menu${anchor.classList.contains('asst-routes-account') ? ' asst-route-account-menu' : ''}`, role: 'menu' });
489
528
  const overlay = anchor.closest('.asst-routes-overlay'); if (overlay) adoptMenu(menu, overlay); return menu;
490
529
  }
491
530
  function adoptMenu(menu, overlay) {
@@ -15,7 +15,7 @@ export const SLASH_COMMANDS = [
15
15
  { name: 'project', usage: '/project <path>', descKey: 'assistant.slash.project', desc: 'Set the working project' },
16
16
  { name: 'routes', usage: '/routes [always|unsure|never]', descKey: 'assistant.slash.routes', desc: 'Set when to ask before routing (or open the model routes)' },
17
17
  { name: 'models', usage: '/models [list | archived | hide <id…> | show <id…>]', descKey: 'assistant.slash.models', desc: 'Switch models off or on for the Assistant (or open the Models list)' },
18
- { name: 'budget', usage: '/budget', descKey: 'assistant.slash.budget', desc: 'Set the money caps (per run, session, brain)' },
18
+ { name: 'budget', usage: '/budget', descKey: 'assistant.slash.budget', desc: 'Set the session budget and view provider shares' },
19
19
  { name: 'computer', usage: '/computer [on|off]', descKey: 'assistant.slash.computer', desc: 'Let the assistant use your Mac' },
20
20
  { name: 'stop', usage: '/stop [runId|all]', descKey: 'assistant.slash.stop', desc: 'Stop a run, everything, or the current turn' },
21
21
  { name: 'new', usage: '/new [provider]', descKey: 'assistant.slash.new', desc: 'Start a fresh session' },
@@ -181,7 +181,7 @@ export function resolveBrainDefaults(brain, catalog = {}) {
181
181
 
182
182
  if (ACCOUNT_PROVIDERS.has(b.provider)) {
183
183
  const accounts = catalog.accounts?.[b.provider] || [];
184
- if (!b.accountId || (accounts.length && !accounts.some(a => a?.id === b.accountId))) {
184
+ if (!b.accountId || (b.accountId !== 'auto' && accounts.length && !accounts.some(a => a?.id === b.accountId))) {
185
185
  b.accountId = accounts.find(a => a?.isDefault)?.id || accounts[0]?.id || 'default';
186
186
  }
187
187
  } else {
@@ -367,10 +367,10 @@ export function injectAssistantStyles() {
367
367
  .asst-chip.asst-suggestion > .asst-icon svg { width: 20px; height: 14px; }
368
368
  .asst-chip.asst-suggestion > .asst-icon { color: var(--asst-text-3); }
369
369
  .asst-chip.asst-suggestion:is(:hover, :focus-visible) > .asst-icon { color: var(--asst-text); }
370
- /* "Choose a project": the toolbar's Working project field, centred under the lead (asst-brain-picker.js mountEmptyProject). An md capsule like the suggestions. */
371
- .asst-empty-project { display: flex; justify-content: center; max-width: 100%; }
372
- .asst-dd.asst-empty-project-btn { max-width: min(100%, 360px); }
373
- .asst-empty-project-btn .asst-dd-label { max-width: 30ch; }
370
+ /* Project and Budget: shared md capsules under the lead, like the suggestions. */
371
+ .asst-empty-project { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; max-width: 100%; }
372
+ .asst-dd.asst-empty-control-btn { max-width: min(100%, 360px); }
373
+ .asst-empty-control-btn .asst-dd-label { max-width: 30ch; }
374
374
  .asst-empty-project-key { flex-shrink: 0; color: var(--asst-text-3); font-weight: 400; }
375
375
 
376
376
  /* ── Messages ───────────────────────────────────────────────────── */
@@ -578,32 +578,61 @@ export function injectAssistantStyles() {
578
578
  color: var(--asst-text-2);
579
579
  }
580
580
 
581
- /* ── Route lines ────────────────────────────────────────────────── */
581
+ /* ── Route lines ────────────────────────────────────────────────────
582
+ One line at any width, in the cards' column (the rack's 560px, no
583
+ hang): the glyph, arrow, kind and mark hold; the rest (model ·
584
+ account · usage, or the state) takes only the room left (flex-basis 0)
585
+ and ellipsizes first; the class gives way once the rest is down to
586
+ its "…". The head's title carries the whole text. */
582
587
  .asst-route-line { font-size: 12.5px; }
583
588
  .asst-route-line-head {
584
- display: inline-flex;
589
+ position: relative;
590
+ display: flex;
585
591
  align-items: center;
586
592
  gap: 6px;
587
- max-width: calc(100% + 8px);
593
+ width: fit-content;
594
+ max-width: min(100%, 560px);
595
+ min-width: 0;
588
596
  min-height: 26px;
589
- margin-left: -8px;
590
597
  padding: 3px 8px;
598
+ overflow: hidden;
591
599
  border: 0;
592
600
  border-radius: var(--asst-r-ctl);
593
601
  background: transparent;
594
602
  color: var(--asst-text-2);
595
603
  font: inherit;
604
+ white-space: nowrap;
596
605
  text-align: left;
597
606
  cursor: pointer;
598
607
  }
599
608
  .asst-route-line-head:hover { background: var(--asst-fill-1); }
600
609
  .asst-route-glyph { color: var(--asst-brand); }
610
+ .asst-route-glyph svg, .asst-route-icon svg { width: 12px; height: 12px; }
611
+ .asst-route-glyph, .asst-route-arrow, .asst-route-kind, .asst-route-icon, .asst-route-chev { flex: none; }
601
612
  .asst-route-arrow, .asst-route-chev { color: var(--asst-text-4); }
602
613
  .asst-route-kind { color: var(--asst-text-3); font-size: 11.5px; }
603
- .asst-route-icon svg { width: 12px; height: 12px; }
604
- .asst-route-target { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--asst-text); font-weight: 500; }
614
+ .asst-route-class { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
615
+ .asst-route-rest { flex: 1 1 0; min-width: 1.2em; overflow: hidden; text-overflow: ellipsis; color: var(--asst-text-3); }
616
+ .asst-route-target { color: var(--asst-text); font-weight: 500; }
605
617
  .asst-route-meta, .asst-route-state { flex-shrink: 0; color: var(--asst-text-3); white-space: nowrap; }
618
+ .asst-route-meta:not([hidden]) + .asst-route-state::before { content: '· '; color: var(--asst-text-3); }
606
619
  .asst-route-line.pending .asst-route-state { color: var(--asst-warn); }
620
+ /* Live: the rack's sweep (asst-rk-sweep, asst-rack-styles.js) on the row's bottom edge, gold while
621
+ a run it dispatched works (the mark ticks like the rack's running tick), the warn tone while it
622
+ waits for your choice. Settled rows hold still. */
623
+ .asst-route-sweep { position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px; overflow: hidden; pointer-events: none; }
624
+ .asst-route-line[data-live] .asst-route-sweep::after {
625
+ content: '';
626
+ position: absolute;
627
+ top: 0;
628
+ bottom: 0;
629
+ left: 0;
630
+ width: 38%;
631
+ background: linear-gradient(90deg, transparent, var(--asst-brand), transparent);
632
+ animation: asst-rk-sweep 1.8s cubic-bezier(.45, 0, .55, 1) infinite;
633
+ }
634
+ .asst-route-line[data-live="waiting"] .asst-route-sweep::after { background: linear-gradient(90deg, transparent, var(--asst-warn), transparent); }
635
+ .asst-route-line[data-live="working"] .asst-route-icon { animation: asst-rk-tick 1.1s ease-in-out infinite; }
607
636
  .asst-route-line.muted .asst-route-glyph { color: var(--asst-text-4); }
608
637
  .asst-route-line.muted .asst-route-class { color: var(--asst-text-3); }
609
638
  .asst-route-chev { transition: transform var(--asst-dur-base) var(--asst-ease-standard); }
@@ -1079,8 +1108,21 @@ export function injectAssistantStyles() {
1079
1108
  /* Fields (toolbar and composer): glyph · seam · value ▾ */
1080
1109
  .asst-dd { flex: 0 1 auto; justify-content: flex-start; max-width: calc(18ch + 60px); }
1081
1110
  .asst-dd .asst-dd-label { min-width: 0; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; }
1082
- /* A display name ("Sonnet 5", a project) is sans; a raw model id (opus[1m]) is mono. */
1083
- .asst-dd.asst-dd-model[data-raw] .asst-dd-label { font-family: var(--asst-mono); font-weight: 400; }
1111
+ /* The model chip: name · context tag · account cue · API (asst-model-chip.js). The picker
1112
+ draws the longest form that fits the row, so no part shrinks or ends in an ellipsis; the
1113
+ cue and the API badge are the account menu's avatar and badge at the row's xs size. */
1114
+ .asst-dd.asst-dd-model { max-width: calc(30ch + 60px); }
1115
+ .asst-dd-model .asst-dd-label.asst-model-label { display: inline-flex; align-items: center; gap: 4px; max-width: none; }
1116
+ .asst-model-label > * { flex: none; }
1117
+ .asst-model-label > [hidden] { display: none; }
1118
+ .asst-model-tag { color: var(--asst-text-3); font-family: var(--asst-mono); font-size: 10.5px; font-weight: 400; }
1119
+ .asst-model-label > .asst-acct-avatar { flex: 0 0 auto; min-width: 16px; width: auto; height: 16px; padding: 0 4px; font-size: 10px; }
1120
+ .asst-model-label > .asst-acct-avatar[data-tone="warn"] { box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); color: var(--asst-warn); }
1121
+ .asst-model-label > .asst-acct-badge { margin: 0; }
1122
+ /* Even the shortest form does not fit (a row narrower than 320px that cannot wrap): the name gives. */
1123
+ .asst-dd-model[data-squeezed] .asst-model-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1124
+ /* A display name ("Sonnet 5", a project) is sans; a raw model id (opus) is mono. */
1125
+ .asst-dd.asst-dd-model[data-raw] .asst-model-name { font-family: var(--asst-mono); font-weight: 400; }
1084
1126
  .asst-dd-caret {
1085
1127
  display: inline-flex;
1086
1128
  flex-shrink: 0;
@@ -1386,8 +1428,9 @@ export function injectAssistantStyles() {
1386
1428
  z-index: 100030; /* above modals (100020): the routes editor opens model menus */
1387
1429
  display: flex;
1388
1430
  flex-direction: column;
1389
- min-width: 200px;
1390
- max-width: min(380px, calc(100vw - 16px));
1431
+ /* --asst-dd-room: the width of the box the menu stays inside, less its margins (asst-menu.js positionMenu). */
1432
+ min-width: min(200px, var(--asst-dd-room, 200px));
1433
+ max-width: min(380px, calc(100vw - 16px), var(--asst-dd-room, 100vw));
1391
1434
  max-height: 380px;
1392
1435
  overflow: hidden;
1393
1436
  padding: 4px;
@@ -1434,6 +1477,8 @@ export function injectAssistantStyles() {
1434
1477
  .asst-dd-item-desc { color: var(--asst-text-3); font-size: 11px; }
1435
1478
  /* Test density: each level's description is a sentence, so it wraps instead of truncating. */
1436
1479
  .asst-tests-menu .asst-dd-item-desc { white-space: normal; }
1480
+ /* Admin sandbox (permission menu): its note is a sentence too. */
1481
+ .asst-dd-item.asst-admin-sandbox-item .asst-dd-item-desc { white-space: normal; }
1437
1482
  .asst-dd-item-tag { flex-shrink: 0; margin-left: auto; color: var(--asst-text-3); font-size: 11px; }
1438
1483
  .asst-dd-kbd { flex-shrink: 0; margin-left: auto; color: var(--asst-text-3); font-family: var(--asst-mono); font-size: 10.5px; }
1439
1484
  .asst-dd-sep { flex-shrink: 0; height: 1px; margin: 4px 6px; background: var(--asst-line); }
@@ -1463,6 +1508,45 @@ export function injectAssistantStyles() {
1463
1508
  .asst-dd-input::placeholder { color: var(--asst-text-3); }
1464
1509
  .asst-dd-filter { margin: 2px 0 4px; }
1465
1510
  .asst-dd-empty { padding: 8px 10px; color: var(--asst-text-3); }
1511
+ /* A footer stays under a scrolling list; a button row is one line of compact items. */
1512
+ .asst-dd-foot { flex-shrink: 0; margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--asst-line); }
1513
+ .asst-dd-btnrow { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; }
1514
+ .asst-dd-btnrow-label { flex: 1 1 auto; min-width: 0; padding: 5px 10px; color: var(--asst-text-2); overflow-wrap: anywhere; }
1515
+ .asst-dd-btnrow .asst-dd-item { flex: 0 1 auto; width: auto; }
1516
+
1517
+ /* ── Account menu (asst-account-menu.js) ────────────────────────
1518
+ Every account once: initial · name ✓ · plan or credential badge, then who it is and its
1519
+ usage in full (lines wrap, nothing is cut). Kept inside the panel by asst-menu.js. The
1520
+ composer's Model menu lists the same rows: the rules follow the rows' own classes. */
1521
+ .asst-dd-item.asst-acct-item { gap: 8px; padding: 6px 8px; }
1522
+ :is(.asst-account-menu .asst-dd-item, .asst-dd-item:is(.asst-acct-item, .asst-acct-choice))[data-checked="1"] { background: var(--asst-fill-1); }
1523
+ :is(.asst-account-menu .asst-dd-item, .asst-dd-item:is(.asst-acct-item, .asst-acct-choice)):is(:hover, .focused) { background: var(--asst-fill-2); }
1524
+ .asst-acct-avatar { display: inline-flex; flex: 0 0 24px; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-2); color: var(--asst-text-2); font-size: 11px; font-weight: 600; line-height: 1; }
1525
+ .asst-acct-avatar svg { width: 14px; height: 14px; }
1526
+ .asst-acct-main { display: flex; flex: 1 1 auto; flex-direction: column; gap: 1px; min-width: 0; }
1527
+ /* A long name wraps; its check and badge stay on its first line. */
1528
+ .asst-acct-head { display: flex; align-items: flex-start; gap: 6px; min-width: 0; }
1529
+ .asst-acct-name { min-width: 0; font-weight: 500; overflow-wrap: anywhere; }
1530
+ .asst-acct-check { display: inline-flex; flex: none; margin-top: 3px; color: var(--asst-text); }
1531
+ .asst-acct-check svg { width: 12px; height: 12px; }
1532
+ .asst-acct-badge { flex: none; margin: 1px 0 0 auto; padding: 0 var(--asst-pill-px-xs); border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-1); box-shadow: inset 0 0 0 1px var(--asst-line); color: var(--asst-text-2); font-size: 10.5px; font-weight: 500; line-height: 16px; white-space: nowrap; }
1533
+ .asst-acct-line { color: var(--asst-text-3); font-size: 11px; line-height: 1.35; overflow-wrap: anywhere; }
1534
+ .asst-dd-item:is(:hover, .focused) .asst-acct-line:not([data-tone="warn"]) { color: var(--asst-text-2); }
1535
+ :is(.asst-acct-badge, .asst-acct-line, .asst-acct-value-text)[data-tone="warn"] { color: var(--asst-warn); }
1536
+ /* The note under the accounts lines up with their names. */
1537
+ .asst-dd-info.asst-acct-note { padding: 3px 8px 5px 40px; font-size: 11px; }
1538
+ .asst-dd-item.asst-acct-value { gap: 6px; }
1539
+ /* Footer rows read "label … value": the label muted, as the button row's. */
1540
+ .asst-acct-value-label { flex: 1 1 auto; min-width: 0; color: var(--asst-text-2); overflow-wrap: anywhere; }
1541
+ .asst-acct-value-text { flex: 0 1 auto; min-width: 0; text-align: right; overflow-wrap: anywhere; }
1542
+ .asst-acct-next { display: inline-flex; flex: none; color: var(--asst-text-3); }
1543
+ .asst-acct-next svg { width: 12px; height: 12px; }
1544
+ .asst-dd-item.asst-acct-back { gap: 6px; color: var(--asst-text-2); }
1545
+ .asst-dd-item.asst-acct-choice { gap: 4px; padding: 5px 8px; }
1546
+ .asst-dd-btnrow[data-id="manage-row"] .asst-dd-item { flex: 1 1 0; }
1547
+ @media (pointer: coarse) {
1548
+ :is(.asst-account-menu .asst-dd-item, .asst-dd-item:is(.asst-acct-item, .asst-acct-choice), .asst-dd-btnrow[data-id="manage-row"] .asst-dd-item) { min-height: 44px; }
1549
+ }
1466
1550
 
1467
1551
  /* ── Modals (help, accounts, model routes) ──────────────────────── */
1468
1552
  .asst-modal-overlay {
@@ -1503,11 +1587,50 @@ export function injectAssistantStyles() {
1503
1587
  .asst-account-label { color: var(--asst-text); }
1504
1588
  .asst-account-sub { color: var(--asst-text-3); font-size: 11px; }
1505
1589
  .asst-account-row input.asst-dd-input { margin: 0; }
1590
+ /* Claude accounts (claude-account-view.js): what each runs on, its credentials and their actions. */
1591
+ .asst-account-row.claude { flex-wrap: wrap; align-items: flex-start; }
1592
+ .asst-account-row.claude .asst-account-main { flex-basis: 60%; }
1593
+ .asst-account-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
1594
+ .asst-account-badge { flex-shrink: 0; padding: 1px 6px; border-radius: var(--asst-pill-r-xs); color: var(--asst-text-2); font-size: 10.5px; box-shadow: inset 0 0 0 1px var(--asst-line-2); }
1595
+ .asst-account-badge[data-tone="ok"] { color: var(--asst-text); box-shadow: inset 0 0 0 1px var(--asst-pill-on-ring); }
1596
+ .asst-account-badge[data-tone="warn"] { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
1597
+ .asst-account-creds { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; color: var(--asst-text-3); font-size: 11px; }
1598
+ .asst-account-cred { display: flex; align-items: center; gap: 6px; }
1599
+ .asst-account-cred-k { min-width: 120px; }
1600
+ .asst-account-actions { display: flex; flex-wrap: wrap; flex-basis: 100%; gap: 6px; margin-top: 6px; }
1601
+ .asst-account-form { display: flex; flex-basis: 100%; gap: 6px; margin-top: 6px; }
1602
+ .asst-account-form input { flex: 1; min-width: 0; }
1603
+ .asst-account-msg { flex-basis: 100%; margin-top: 4px; color: var(--asst-text-3); font-size: 11px; }
1604
+ .asst-dd.asst-dd-warn { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
1506
1605
  .asst-login-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-radius: var(--asst-r-field); box-shadow: inset 0 0 0 1px var(--asst-line-2); }
1507
1606
  .asst-login-code { color: var(--asst-text); font-family: var(--asst-mono); font-size: 15px; letter-spacing: .1em; }
1508
1607
 
1509
1608
  .asst-routes-modal { width: min(660px, 100%); }
1510
- .asst-budget-row { align-items: flex-start; }
1609
+ .asst-budget-modal { width: min(560px, calc(100vw - 24px)); }
1610
+ .asst-budget-controls { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; min-width: 0; }
1611
+ .asst-budget-controls > :is(.asst-modal-note, .asst-budget-field-error, .asst-budget-switch-label) { grid-column: 1 / -1; }
1612
+ .asst-budget-controls > .asst-btn { justify-self: start; }
1613
+ .asst-budget-controls .asst-budget-input { width: 96px; min-width: 0; box-sizing: border-box; }
1614
+ .asst-budget-switch-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--asst-text); cursor: pointer; }
1615
+ .asst-budget-switch { appearance: none; position: relative; width: 30px; height: 18px; margin: 0; border: 0; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-3); box-shadow: inset 0 0 0 1px var(--asst-line-2); cursor: pointer; }
1616
+ .asst-budget-switch::before { content: ''; position: absolute; width: 12px; height: 12px; top: 3px; left: 3px; border-radius: var(--asst-pill-r-xs); background: var(--asst-text-3); transition: transform var(--asst-dur-fast) var(--asst-ease-standard); }
1617
+ .asst-budget-switch:checked { background: var(--asst-brand); }
1618
+ .asst-budget-switch:checked::before { background: var(--asst-ink); transform: translateX(12px); }
1619
+ .asst-budget-controls :is(input, button):focus-visible { outline: 2px solid var(--asst-focus); outline-offset: 2px; }
1620
+ .asst-budget-controls input:disabled { opacity: .5; }
1621
+ .asst-budget-popover .asst-dd-list { padding: 8px; }
1622
+ .asst-budget-session { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--asst-line); }
1623
+ .asst-budget-total { font-family: var(--asst-mono); }
1624
+ .asst-budget-session[data-tone="warn"] .asst-budget-total, .asst-budget-dry { color: var(--asst-warn); }
1625
+ .asst-budget-session[data-tone="over"] .asst-budget-total { color: var(--asst-live); }
1626
+ .asst-budget-providers { display: flex; flex-direction: column; gap: 8px; }
1627
+ .asst-budget-provider { padding: 8px; border-radius: var(--asst-r-field); background: var(--asst-fill-1); overflow-wrap: anywhere; }
1628
+ .asst-budget-provider[data-eligible="false"] { color: var(--asst-text-3); }
1629
+ .asst-budget-provider-name { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
1630
+ .asst-budget-provider-money { margin-top: 4px; font: 12px/1.5 var(--asst-mono); }
1631
+ .asst-budget-brain { padding: 0 6px; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-2); font-size: 11.5px; }
1632
+ .asst-budget-dry { font-size: 12px; }
1633
+ .asst-budget-meter { width: 100%; accent-color: var(--asst-brand); }
1511
1634
  .asst-budget-desc, .asst-budget-meta { color: var(--asst-text-3); font-size: 11.5px; line-height: 1.4; }
1512
1635
  .asst-budget-meta { font-variant-numeric: tabular-nums; }
1513
1636
  .asst-budget-field-error { color: var(--asst-live); font-size: 11.5px; line-height: 1.4; }
@@ -1592,7 +1715,7 @@ export function injectAssistantStyles() {
1592
1715
  /* Model routes editor: Settings density on a desktop pointer. Scoped here
1593
1716
  so route cards, the model manager and other Assistant menus keep their UI. */
1594
1717
  .asst-routes-overlay { font-family: var(--stg-font, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Inter, sans-serif); }
1595
- .asst-routes-overlay .asst-routes-modal { width: min(800px, 100%); max-height: calc(100dvh - 32px); border-radius: var(--asst-r-card); line-height: 1.4; }
1718
+ .asst-routes-overlay .asst-routes-modal { width: min(1040px, 100%); max-height: calc(100dvh - 32px); border-radius: var(--asst-r-card); line-height: 1.4; }
1596
1719
  .asst-routes-modal :is(.asst-btn, .asst-dd, .asst-iconbtn) { --cap-h: var(--asst-pill-h-sm); --cap-r: var(--asst-pill-r-xs); --cap-px: var(--asst-pill-px-sm); --cap-icon: 12px; --cap-gap: 4px; }
1597
1720
  .asst-routes-modal .asst-modal-head { min-height: 40px; padding: 8px 14px 8px 22px; font-size: 13px; }
1598
1721
  .asst-routes-modal .asst-modal-head .asst-icon { color: var(--asst-text-2); }
@@ -1616,7 +1739,7 @@ export function injectAssistantStyles() {
1616
1739
  .asst-routes-modal .asst-routes-list-actions .asst-btn:hover:not(:disabled) { color: var(--asst-text); background: var(--asst-fill-1); }
1617
1740
  /* Reserve the widths the current model names, effort words and destinations
1618
1741
  need; give all remaining space to task descriptions instead of a share. */
1619
- .asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { display: grid; grid-template-columns: minmax(0, 1fr) 208px 84px 128px var(--asst-pill-h-sm); column-gap: 6px; }
1742
+ .asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { display: grid; grid-template-columns: minmax(100px, 1fr) minmax(120px, 200px) 70px 116px minmax(120px, 190px) var(--asst-pill-h-sm); column-gap: 6px; }
1620
1743
  .asst-routes-columns { position: sticky; top: -14px; z-index: 1; padding: 4px 8px; color: var(--asst-text-3); font-size: 11.5px; background: var(--asst-popover); }
1621
1744
  .asst-routes-columns span:last-child { visibility: hidden; }
1622
1745
  .asst-routes-modal .asst-routes-list { padding: 0 8px; border: 1px solid var(--asst-line); border-radius: var(--radius-group, 9px); background: var(--asst-field); }
@@ -1650,11 +1773,16 @@ export function injectAssistantStyles() {
1650
1773
  .asst-routes-catalog-note { padding: 8px; color: var(--asst-warn); font-size: 12px; line-height: 1.45; background: var(--asst-fill-1); border-radius: var(--asst-pill-r-xs); }
1651
1774
  .asst-routes-modal .asst-routes-error { flex-wrap: wrap; }
1652
1775
  .asst-routes-modal .asst-routes-error span { min-width: 0; overflow-wrap: anywhere; }
1653
- .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) { max-width: min(440px, calc(100vw - 16px)); font-family: inherit; font-size: 12px; }
1776
+ .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) { max-width: min(440px, calc(100vw - 16px), var(--asst-dd-room, 100vw)); font-family: inherit; font-size: 12px; }
1654
1777
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item { height: var(--asst-pill-h-sm); min-height: var(--asst-pill-h-sm); padding: 0 8px; border-radius: var(--asst-pill-r-xs); }
1655
1778
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-main { flex-direction: row; align-items: center; gap: 8px; }
1656
1779
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-label { flex-shrink: 0; max-width: 60%; }
1657
1780
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-desc { flex: 1; text-align: end; }
1781
+ /* Account identities and quota windows need a second line in their menu. */
1782
+ .asst-routes-overlay .asst-route-account-menu .asst-dd-item { height: auto; min-height: var(--asst-pill-h-sm); padding: 4px 8px; }
1783
+ .asst-routes-overlay .asst-route-account-menu .asst-dd-item-main { flex-direction: column; align-items: stretch; gap: 2px; }
1784
+ .asst-routes-overlay .asst-route-account-menu :is(.asst-dd-item-label, .asst-dd-item-desc) { white-space: normal; max-width: none; text-align: start; overflow-wrap: anywhere; }
1785
+ .asst-model-menu .asst-dd-item[data-kind="radio"]:not([data-id*=":"]) .asst-dd-item-desc { white-space: normal; overflow-wrap: anywhere; }
1658
1786
  .asst-routes-overlay .asst-route-model-menu .asst-dd-input { height: var(--asst-pill-h-sm); min-height: var(--asst-pill-h-sm); font-size: 12px; border-radius: var(--asst-pill-r-xs); }
1659
1787
  .asst-routes-overlay .asst-route-model-menu .asst-dd-input:focus-visible { outline: 2px solid var(--asst-focus); outline-offset: -2px; }
1660
1788
  .asst-route-menu-foot { flex-shrink: 0; display: flex; flex-wrap: wrap; align-items: center; border-top: 1px solid var(--asst-line-2); padding-top: 4px; }
@@ -1665,7 +1793,7 @@ export function injectAssistantStyles() {
1665
1793
  .asst-routes-modal :is(.asst-btn, .asst-dd, .asst-iconbtn) { --cap-h: 44px; }
1666
1794
  .asst-routes-modal .asst-routes-mode { height: auto; min-height: 44px; }
1667
1795
  .asst-routes-modal .asst-routes-mode-label { white-space: normal; }
1668
- .asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.55fr) minmax(0, .7fr) minmax(0, .9fr) 44px; }
1796
+ .asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, .7fr) minmax(0, .9fr) minmax(0, 1.4fr) 44px; }
1669
1797
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) :is(.asst-dd-item, .asst-dd-input) { min-height: 44px; height: auto; padding: 8px; }
1670
1798
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-main { flex-direction: column; align-items: stretch; gap: 2px; }
1671
1799
  .asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) :is(.asst-dd-item-label, .asst-dd-item-desc) { white-space: normal; max-width: none; text-align: start; overflow-wrap: anywhere; }
@@ -1678,7 +1806,7 @@ export function injectAssistantStyles() {
1678
1806
  .asst-routes-modal .asst-routes-modes { grid-template-columns: 1fr; }
1679
1807
  .asst-routes-modal .asst-routes-columns { display: none; }
1680
1808
  .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 44px; gap: 8px; padding: 12px 0; }
1681
- .asst-routes-modal .asst-routes-row-main, .asst-routes-modal .asst-routes-model-cell { grid-column: 1 / -1; }
1809
+ .asst-routes-modal .asst-routes-row-main, .asst-routes-modal .asst-routes-model-cell, .asst-routes-modal .asst-routes-account-cell { grid-column: 1 / -1; }
1682
1810
  .asst-routes-cell { flex-direction: column; gap: 4px; }
1683
1811
  .asst-routes-cell-label { display: block; color: var(--asst-text-3); font-size: 11.5px; }
1684
1812
  .asst-routes-reset-cell { align-self: end; }
@@ -1749,8 +1877,9 @@ export function injectAssistantStyles() {
1749
1877
  density is its gauge alone. A capsule that runs out of
1750
1878
  room drops its label half first and stays a glyph (its words remain in
1751
1879
  aria-label and the tooltip): route mode and Computer ≤960px, the mode
1752
- ≤540px, every composer field but the model ≤440px. Model, history, new
1753
- and "⋯" never hide; whatever hides stays in "⋯". A toolbar placed
1880
+ ≤540px, every composer field but the model ≤440px, where the model
1881
+ drops its caret and keeps its name (its form fits the row: see the
1882
+ model chip above). Model, history, new and "⋯" never hide; whatever hides stays in "⋯". A toolbar placed
1754
1883
  outside the root (the sidepanel header) is out of reach of these rules:
1755
1884
  its host sizes it (asst-sidepanel-styles.js).
1756
1885
  Glyph only is the same three rules everywhere (see Capsules): the
@@ -1776,13 +1905,13 @@ export function injectAssistantStyles() {
1776
1905
  .asst-composer-brain .asst-dd.asst-dd-mode { display: grid; place-content: center; place-items: center; padding: 0; }
1777
1906
  .asst-composer-brain .asst-dd.asst-dd-mode > :is(.asst-dd-label, .asst-dd-caret) { display: none; }
1778
1907
  .asst-composer-brain .asst-dd.asst-dd-mode > .asst-icon:not([hidden]) { margin: 0; padding: 0 var(--cap-glyph-pad); box-shadow: none; }
1779
- .asst-dd-model .asst-dd-label { max-width: 14ch; }
1780
1908
  .asst-route-badge { display: none; }
1781
1909
  }
1782
1910
  @container asst (max-width: 440px) {
1783
1911
  .asst-composer-brain .asst-dd:is(.asst-dd-effort, .asst-dd-variant, .asst-dd-mode, .asst-dd-account) { display: grid; place-content: center; place-items: center; padding: 0; }
1784
1912
  .asst-composer-brain .asst-dd:is(.asst-dd-effort, .asst-dd-variant, .asst-dd-mode, .asst-dd-account) > :is(.asst-dd-label, .asst-dd-caret) { display: none; }
1785
1913
  .asst-composer-brain .asst-dd:is(.asst-dd-effort, .asst-dd-variant, .asst-dd-mode, .asst-dd-account) > .asst-icon:not([hidden]) { margin: 0; padding: 0 var(--cap-glyph-pad); box-shadow: none; }
1914
+ .asst-composer-brain .asst-dd.asst-dd-model > .asst-dd-caret { display: none; }
1786
1915
  }
1787
1916
  @container asst (max-width: 400px) {
1788
1917
  .asst-root > * { --asst-inline: 6px; }
@@ -1790,19 +1919,17 @@ export function injectAssistantStyles() {
1790
1919
  .asst-suggestions { flex-direction: column; align-items: stretch; width: 100%; }
1791
1920
  .asst-chip.asst-suggestion { justify-content: flex-start; max-width: none; }
1792
1921
  .asst-empty-project { width: 100%; }
1793
- .asst-dd.asst-empty-project-btn { flex: 1 1 auto; justify-content: flex-start; max-width: none; }
1794
- .asst-empty-project-btn .asst-dd-label { flex: 1 1 auto; max-width: none; text-align: start; }
1922
+ .asst-dd.asst-empty-control-btn { flex: 1 1 auto; justify-content: flex-start; max-width: none; }
1923
+ .asst-empty-control-btn .asst-dd-label { flex: 1 1 auto; max-width: none; text-align: start; }
1795
1924
  .asst-msg.msg-user .asst-msg-content { max-width: 92%; }
1796
- .asst-dd-model .asst-dd-label { max-width: 9ch; }
1797
1925
  .asst-route-opt-sub { display: none; }
1798
1926
  .asst-run-main { flex-wrap: wrap; row-gap: 2px; }
1799
1927
  .asst-run-title { flex-basis: 100%; }
1800
1928
  }
1801
1929
  @container asst (max-width: 320px) {
1802
1930
  .asst-bar-group[data-group="computer"] { display: none !important; }
1803
- /* The model keeps a readable name (at least 5ch): the composer row wraps instead of truncating it. */
1804
- .asst-dd-model .asst-dd-label { min-width: 5ch; max-width: 14ch; }
1805
- /* A tighter gap keeps attach · model · effort · variant · mode · tests · send on one row at 320px. */
1931
+ /* A tighter gap keeps attach · model · effort · variant · mode · tests · send on one row at 320px;
1932
+ narrower, the row wraps rather than cut the model chip. */
1806
1933
  .asst-composer-actions { flex-wrap: wrap; gap: 6px 4px; }
1807
1934
  .asst-composer-brain { flex-wrap: wrap; gap: 6px 4px; }
1808
1935
  }
@@ -1825,6 +1952,8 @@ export function injectAssistantStyles() {
1825
1952
  @keyframes asst-spin { to { transform: rotate(360deg); } }
1826
1953
 
1827
1954
  body.ui-interacting :is(.asst-live-dot, .asst-spinner, .asst-computer-dot, .syna-cameo-eye) { animation-play-state: paused !important; }
1955
+ body.ui-interacting .asst-route-line[data-live] .asst-route-icon,
1956
+ body.ui-interacting .asst-route-line[data-live] .asst-route-sweep::after { animation-play-state: paused !important; }
1828
1957
 
1829
1958
  @media (prefers-reduced-motion: reduce) {
1830
1959
  .asst-transcript { scroll-behavior: auto; }
@@ -1834,6 +1963,8 @@ export function injectAssistantStyles() {
1834
1963
  .term-minimized-pill,
1835
1964
  .term-minimized-pill::before,
1836
1965
  .term-minimized-pill::after { animation: none !important; transition: none !important; }
1966
+ /* A frozen sweep would be a stray stub: the route row draws none (its run card and state words remain). */
1967
+ .asst-route-sweep { display: none; }
1837
1968
  }
1838
1969
  `;
1839
1970
  document.head.appendChild(style);
@@ -20,6 +20,8 @@ const definitions = [
20
20
  ['hooks', 'Inspect configured hooks', 'hooks_list'],
21
21
  ['init', 'Create or update AGENTS.md guidance', null, true],
22
22
  ['logout', 'Sign out of Codex', 'account_logout'],
23
+ ['login', 'Sign in with ChatGPT'],
24
+ ['account', 'Choose an account or sign in'],
23
25
  ['mcp', 'Search MCP servers and tools', 'mcp_status'],
24
26
  ['mention', 'Add a structured file mention', null, true],
25
27
  ['model', 'Choose model'],
@@ -146,10 +148,11 @@ export function configChanges(definitions, inputs, original) {
146
148
  export const SESSION_CONFIG_FIELDS = Object.freeze({
147
149
  model: 'model', model_reasoning_effort: 'effort', model_reasoning_summary: 'summary',
148
150
  personality: 'personality', service_tier: 'serviceTier', approval_policy: 'approvalPolicy',
151
+ sandbox_mode: 'sandboxPolicy',
149
152
  });
150
153
 
151
154
  export function sessionSettings(config) {
152
- return Object.fromEntries(Object.entries(config).filter(([key]) => SESSION_CONFIG_FIELDS[key]).map(([key, value]) => [SESSION_CONFIG_FIELDS[key], value]));
155
+ return Object.fromEntries(Object.entries(config).filter(([key]) => SESSION_CONFIG_FIELDS[key]).map(([key, value]) => [key === 'sandbox_mode' ? 'sandboxMode' : SESSION_CONFIG_FIELDS[key], value]));
153
156
  }
154
157
 
155
158
  export function modelServiceTiers(model) {
@@ -1,4 +1,5 @@
1
1
  import { capabilityAvailability, flattenNativeCatalog, reviewTarget } from './cdx-commands.js';
2
+ import { t } from '../i18n.js';
2
3
  import { knownCodexCapabilities } from './cdx-capabilities.js';
3
4
  import { codexReadableValue, parseCodexAttachmentPayload, codexGatewayState, codexVerificationState, codexHttpsHandoff } from './cdx-protocol.js';
4
5
 
@@ -333,23 +334,27 @@ export function updateCodexGateway(tab, value) {
333
334
  accountSecurityViews.get(tab)?.();
334
335
  }
335
336
 
336
- export async function mountCodexAccountSecurity({ host, request, tab, capabilities }) {
337
+ export async function mountCodexAccountSecurity({ host, experimentalHost = host, request, tab, capabilities }) {
338
+ const tr = (key, params) => t(`codexSettings.${key}`, params);
337
339
  const view = { error: message => { error.textContent = message; } };
338
340
  const error = element('div', 'cxp-settings-hint'); error.setAttribute('role', 'alert');
339
341
  const gateway = element('div', 'cxp-settings-section');
340
342
  const verification = element('div', 'cxp-settings-section');
341
- host.append(gateway, verification, error);
343
+ host.append(gateway, error);
344
+ experimentalHost.append(verification);
342
345
  const available = name => capabilityAvailability(knownCodexCapabilities(tab) || capabilities, name);
343
346
  const renderGateway = () => {
344
347
  if (!host.isConnected || tab.closed) { accountSecurityViews.delete(tab); return; }
345
348
  gateway.replaceChildren();
346
349
  if (!available('gateway_oauth_read').supported) return;
347
350
  const value = tab.gatewayOAuth || {};
351
+ gateway.hidden = !value.required;
352
+ if (!value.required) return;
348
353
  const state = codexGatewayState(value);
349
- gateway.append(element('div', 'cxp-settings-section-title', 'Gateway OAuth'));
350
- gateway.append(element('div', 'cxp-settings-hint', `${value.providerName || value.providerId || 'Provider'} · ${state.label}`));
354
+ gateway.append(element('div', 'cxp-settings-section-title', tr('gateway')));
355
+ gateway.append(element('div', 'cxp-settings-hint', `${value.providerName || value.providerId || tr('provider')} · ${tr(`gatewayStates.${value.status || 'notReady'}`)}`));
351
356
  const action = state.pending ? 'gateway_oauth_cancel' : 'gateway_oauth_login';
352
- gateway.append(button(state.pending ? 'Cancel' : 'Sign in', async () => {
357
+ gateway.append(button(tr(state.pending ? 'cancel' : 'signIn'), async () => {
353
358
  if (!available(action).supported) throw new Error(available(action).reason);
354
359
  await request(action, {});
355
360
  // Notification is authoritative; do not overwrite a completed login with a stale optimistic state.
@@ -360,10 +365,10 @@ export async function mountCodexAccountSecurity({ host, request, tab, capabiliti
360
365
  }, view, available(action)));
361
366
  if (!available(action).supported) gateway.append(element('div', 'cxp-settings-hint', available(action).reason));
362
367
  const handoff = codexHttpsHandoff(value.authUrl);
363
- if (value.authUrl && !handoff) gateway.append(element('div', 'cxp-settings-hint', 'Authorization URL is unavailable: HTTPS is required.'));
368
+ if (value.authUrl && !handoff) gateway.append(element('div', 'cxp-settings-hint', tr('httpsRequired')));
364
369
  if (handoff && state.pending) {
365
- gateway.append(element('div', 'cxp-settings-hint', `Continue sign-in at ${handoff.host}`));
366
- gateway.append(button(`Open ${handoff.host}`, () => {
370
+ gateway.append(element('div', 'cxp-settings-hint', tr('continueAt', { host: handoff.host })));
371
+ gateway.append(button(tr('openHost', { host: handoff.host }), () => {
367
372
  window.open(handoff.url, '_blank', 'noopener,noreferrer');
368
373
  }, view));
369
374
  }
@@ -386,12 +391,15 @@ export async function mountCodexAccountSecurity({ host, request, tab, capabiliti
386
391
  const response = await request('verification_status', {});
387
392
  if (!host.isConnected || tab.closed) return;
388
393
  const state = codexVerificationState(response.verification);
389
- verification.append(element('div', 'cxp-settings-section-title', 'User verification · Experimental'));
390
- verification.append(element('div', 'cxp-settings-hint', state.label));
391
- verification.append(element('div', 'cxp-settings-hint', 'Local device credential only; backend registration is managed by the verifier.'));
394
+ const reason = response.verification?.unavailableReason;
395
+ verification.hidden = !!reason && reason !== 'credentialMissing';
396
+ if (verification.hidden) return;
397
+ verification.append(element('div', 'cxp-settings-section-title', tr('verification')));
398
+ verification.append(element('div', 'cxp-settings-hint', tr(state.enrolled ? 'credentialReady' : 'credentialMissing')));
399
+ verification.append(element('div', 'cxp-settings-hint', tr('localCredential')));
392
400
  const action = state.enrolled ? 'verification_delete' : 'verification_enroll';
393
- const control = button(state.enrolled ? 'Remove' : 'Enroll', async () => {
394
- if (state.enrolled && !window.confirm('Remove the local user verification credential?')) return;
401
+ const control = button(tr(state.enrolled ? 'remove' : 'enroll'), async () => {
402
+ if (state.enrolled && !window.confirm(tr('removeCredential'))) return;
395
403
  await request(action, {}); await readVerification();
396
404
  }, view, available(action));
397
405
  if (state.enrolled) control.style.color = 'var(--cxp-danger)';
@@ -399,7 +407,7 @@ export async function mountCodexAccountSecurity({ host, request, tab, capabiliti
399
407
  if (!available(action).supported) verification.append(element('div', 'cxp-settings-hint', available(action).reason));
400
408
  } catch (failure) {
401
409
  if (available('verification_status').supported) {
402
- verification.append(element('div', 'cxp-settings-hint', `User verification unavailable: ${failure.message}`));
410
+ verification.append(element('div', 'cxp-settings-hint', tr('error', { message: failure.message })));
403
411
  }
404
412
  }
405
413
  }
@@ -41,6 +41,8 @@ export let windowId = hostedSidepanelId || sessionStorage.getItem('cxp-window-id
41
41
  // ── Storage Keys ──
42
42
  export const STOR = {
43
43
  threadId: 'synabun-codex-panel-thread',
44
+ // The account that thread belongs to (its rollout lives in that account's CODEX_HOME).
45
+ threadAccount: 'synabun-codex-panel-thread-account',
44
46
  title: 'synabun-codex-panel-title',
45
47
  project: 'synabun-codex-panel-project',
46
48
  projectThreads: 'synabun-codex-panel-project-threads',