synabun 1.3.0 → 2.0.0

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 (1163) hide show
  1. package/CHANGELOG.md +205 -662
  2. package/LICENSE-COMMERCIAL.md +33 -0
  3. package/NOTICE +17 -0
  4. package/README.md +175 -853
  5. package/THIRD-PARTY-LICENSES.md +136 -0
  6. package/benchmarks/browser-live-results.json +79 -0
  7. package/benchmarks/browser-v2-results.json +258 -0
  8. package/benchmarks/fixtures/browser/README.md +57 -0
  9. package/benchmarks/fixtures/browser/page-state.json +132 -0
  10. package/benchmarks/fixtures/browser/social.json +92 -0
  11. package/benchmarks/fixtures/browser/target.json +319 -0
  12. package/benchmarks/fixtures/desktop/README.md +58 -0
  13. package/benchmarks/fixtures/desktop/abstention.json +59 -0
  14. package/benchmarks/fixtures/desktop/adversarial.json +22 -0
  15. package/benchmarks/fixtures/desktop/forbidden.json +93 -0
  16. package/benchmarks/fixtures/desktop/generate.mjs +1023 -0
  17. package/benchmarks/fixtures/desktop/target.json +136 -0
  18. package/bin/synabun.js +30 -0
  19. package/docs/whatsapp.md +325 -0
  20. package/hooks/claude-code/post-plan.mjs +613 -391
  21. package/hooks/claude-code/post-remember.mjs +246 -122
  22. package/hooks/claude-code/pre-compact.mjs +171 -110
  23. package/hooks/claude-code/pre-task.mjs +133 -0
  24. package/hooks/claude-code/pre-websearch.mjs +15 -4
  25. package/hooks/claude-code/prompt-origin.mjs +94 -0
  26. package/hooks/claude-code/prompt-submit.mjs +1244 -861
  27. package/hooks/claude-code/session-start.mjs +114 -156
  28. package/hooks/claude-code/shared.mjs +1228 -368
  29. package/hooks/claude-code/state.mjs +167 -0
  30. package/hooks/claude-code/stop.mjs +515 -140
  31. package/hooks/claude-code/subagent-start.mjs +154 -0
  32. package/hooks/claude-code/subagent-stop.mjs +102 -0
  33. package/hooks/claude-code/task-gate.mjs +150 -0
  34. package/launcher.mjs +40 -0
  35. package/lib/claude-hooks.js +137 -0
  36. package/lib/claude-usage-audit.js +141 -0
  37. package/lib/client-config-repair.js +145 -0
  38. package/lib/data-home-migration.js +502 -0
  39. package/lib/desktop-pwa.js +206 -0
  40. package/lib/external-tools.js +221 -0
  41. package/lib/first-launch-protection.js +245 -0
  42. package/lib/packaged-runtime.js +110 -0
  43. package/lib/paths.d.ts +21 -0
  44. package/lib/paths.js +103 -0
  45. package/lib/start-launcher.js +1309 -0
  46. package/mcp-server/dist/config.d.ts +22 -0
  47. package/mcp-server/dist/config.d.ts.map +1 -0
  48. package/mcp-server/dist/config.js +109 -0
  49. package/mcp-server/dist/config.js.map +1 -0
  50. package/mcp-server/dist/http.d.ts +30 -0
  51. package/mcp-server/dist/http.d.ts.map +1 -0
  52. package/mcp-server/dist/http.js +276 -0
  53. package/mcp-server/dist/http.js.map +1 -0
  54. package/mcp-server/dist/index.d.ts +48 -0
  55. package/mcp-server/dist/index.d.ts.map +1 -0
  56. package/mcp-server/dist/index.js +538 -0
  57. package/mcp-server/dist/index.js.map +1 -0
  58. package/mcp-server/dist/preload-http.d.ts +3 -0
  59. package/mcp-server/dist/preload-http.d.ts.map +1 -0
  60. package/mcp-server/dist/preload-http.js +28 -0
  61. package/mcp-server/dist/preload-http.js.map +1 -0
  62. package/mcp-server/dist/preload.d.ts +3 -0
  63. package/mcp-server/dist/preload.d.ts.map +1 -0
  64. package/mcp-server/dist/preload.js +32 -0
  65. package/mcp-server/dist/preload.js.map +1 -0
  66. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts +19 -0
  67. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts.map +1 -0
  68. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js +403 -0
  69. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js.map +1 -0
  70. package/mcp-server/dist/scripts/setup-embeddings.d.ts +3 -0
  71. package/mcp-server/dist/scripts/setup-embeddings.d.ts.map +1 -0
  72. package/mcp-server/dist/scripts/setup-embeddings.js +15 -0
  73. package/mcp-server/dist/scripts/setup-embeddings.js.map +1 -0
  74. package/mcp-server/dist/services/assistant-judgments.d.ts +79 -0
  75. package/mcp-server/dist/services/assistant-judgments.d.ts.map +1 -0
  76. package/mcp-server/dist/services/assistant-judgments.js +128 -0
  77. package/mcp-server/dist/services/assistant-judgments.js.map +1 -0
  78. package/mcp-server/dist/services/browser-assist-gate.d.ts +314 -0
  79. package/mcp-server/dist/services/browser-assist-gate.d.ts.map +1 -0
  80. package/mcp-server/dist/services/browser-assist-gate.js +344 -0
  81. package/mcp-server/dist/services/browser-assist-gate.js.map +1 -0
  82. package/mcp-server/dist/services/browser-assist.d.ts +104 -0
  83. package/mcp-server/dist/services/browser-assist.d.ts.map +1 -0
  84. package/mcp-server/dist/services/browser-assist.js +282 -0
  85. package/mcp-server/dist/services/browser-assist.js.map +1 -0
  86. package/mcp-server/dist/services/browser-bench.d.ts +772 -0
  87. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -0
  88. package/mcp-server/dist/services/browser-bench.js +424 -0
  89. package/mcp-server/dist/services/browser-bench.js.map +1 -0
  90. package/mcp-server/dist/services/browser-judgments.d.ts +196 -0
  91. package/mcp-server/dist/services/browser-judgments.d.ts.map +1 -0
  92. package/mcp-server/dist/services/browser-judgments.js +363 -0
  93. package/mcp-server/dist/services/browser-judgments.js.map +1 -0
  94. package/mcp-server/dist/services/browser-risk.d.ts +166 -0
  95. package/mcp-server/dist/services/browser-risk.d.ts.map +1 -0
  96. package/mcp-server/dist/services/browser-risk.js +458 -0
  97. package/mcp-server/dist/services/browser-risk.js.map +1 -0
  98. package/mcp-server/dist/services/categories.d.ts +61 -0
  99. package/mcp-server/dist/services/categories.d.ts.map +1 -0
  100. package/mcp-server/dist/services/categories.js +377 -0
  101. package/mcp-server/dist/services/categories.js.map +1 -0
  102. package/mcp-server/dist/services/codex-config-heal.d.ts +49 -0
  103. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -0
  104. package/mcp-server/dist/services/codex-config-heal.js +346 -0
  105. package/mcp-server/dist/services/codex-config-heal.js.map +1 -0
  106. package/mcp-server/dist/services/desktop-assist-gate.d.ts +198 -0
  107. package/mcp-server/dist/services/desktop-assist-gate.d.ts.map +1 -0
  108. package/mcp-server/dist/services/desktop-assist-gate.js +202 -0
  109. package/mcp-server/dist/services/desktop-assist-gate.js.map +1 -0
  110. package/mcp-server/dist/services/desktop-assist.d.ts +104 -0
  111. package/mcp-server/dist/services/desktop-assist.d.ts.map +1 -0
  112. package/mcp-server/dist/services/desktop-assist.js +266 -0
  113. package/mcp-server/dist/services/desktop-assist.js.map +1 -0
  114. package/mcp-server/dist/services/desktop-bench.d.ts +553 -0
  115. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -0
  116. package/mcp-server/dist/services/desktop-bench.js +311 -0
  117. package/mcp-server/dist/services/desktop-bench.js.map +1 -0
  118. package/mcp-server/dist/services/desktop-client.d.ts +23 -0
  119. package/mcp-server/dist/services/desktop-client.d.ts.map +1 -0
  120. package/mcp-server/dist/services/desktop-client.js +79 -0
  121. package/mcp-server/dist/services/desktop-client.js.map +1 -0
  122. package/mcp-server/dist/services/desktop-judgments.d.ts +125 -0
  123. package/mcp-server/dist/services/desktop-judgments.d.ts.map +1 -0
  124. package/mcp-server/dist/services/desktop-judgments.js +177 -0
  125. package/mcp-server/dist/services/desktop-judgments.js.map +1 -0
  126. package/mcp-server/dist/services/desktop-risk.d.ts +283 -0
  127. package/mcp-server/dist/services/desktop-risk.d.ts.map +1 -0
  128. package/mcp-server/dist/services/desktop-risk.js +605 -0
  129. package/mcp-server/dist/services/desktop-risk.js.map +1 -0
  130. package/mcp-server/dist/services/discord.d.ts +112 -0
  131. package/mcp-server/dist/services/discord.d.ts.map +1 -0
  132. package/mcp-server/dist/services/discord.js +477 -0
  133. package/mcp-server/dist/services/discord.js.map +1 -0
  134. package/mcp-server/dist/services/edit-stale.d.ts +81 -0
  135. package/mcp-server/dist/services/edit-stale.d.ts.map +1 -0
  136. package/mcp-server/dist/services/edit-stale.js +358 -0
  137. package/mcp-server/dist/services/edit-stale.js.map +1 -0
  138. package/mcp-server/dist/services/embedding-service.d.ts +16 -0
  139. package/mcp-server/dist/services/embedding-service.d.ts.map +1 -0
  140. package/mcp-server/dist/services/embedding-service.js +126 -0
  141. package/mcp-server/dist/services/embedding-service.js.map +1 -0
  142. package/mcp-server/dist/services/embedding-worker.d.ts +2 -0
  143. package/mcp-server/dist/services/embedding-worker.d.ts.map +1 -0
  144. package/mcp-server/dist/services/embedding-worker.js +52 -0
  145. package/mcp-server/dist/services/embedding-worker.js.map +1 -0
  146. package/mcp-server/dist/services/fb-regions.d.ts +71 -0
  147. package/mcp-server/dist/services/fb-regions.d.ts.map +1 -0
  148. package/mcp-server/dist/services/fb-regions.js +211 -0
  149. package/mcp-server/dist/services/fb-regions.js.map +1 -0
  150. package/mcp-server/dist/services/file-checksums.d.ts +22 -0
  151. package/mcp-server/dist/services/file-checksums.d.ts.map +1 -0
  152. package/mcp-server/dist/services/file-checksums.js +55 -0
  153. package/mcp-server/dist/services/file-checksums.js.map +1 -0
  154. package/mcp-server/dist/services/file-excerpt.d.ts +24 -0
  155. package/mcp-server/dist/services/file-excerpt.d.ts.map +1 -0
  156. package/mcp-server/dist/services/file-excerpt.js +65 -0
  157. package/mcp-server/dist/services/file-excerpt.js.map +1 -0
  158. package/mcp-server/dist/services/identity.d.ts +124 -0
  159. package/mcp-server/dist/services/identity.d.ts.map +1 -0
  160. package/mcp-server/dist/services/identity.js +241 -0
  161. package/mcp-server/dist/services/identity.js.map +1 -0
  162. package/mcp-server/dist/services/local-embeddings.d.ts +2 -0
  163. package/mcp-server/dist/services/local-embeddings.d.ts.map +1 -0
  164. package/mcp-server/dist/services/local-embeddings.js +3 -0
  165. package/mcp-server/dist/services/local-embeddings.js.map +1 -0
  166. package/mcp-server/dist/services/memory-client.d.ts +4 -0
  167. package/mcp-server/dist/services/memory-client.d.ts.map +1 -0
  168. package/mcp-server/dist/services/memory-client.js +27 -0
  169. package/mcp-server/dist/services/memory-client.js.map +1 -0
  170. package/mcp-server/dist/services/memory-judge-backfill.d.ts +135 -0
  171. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -0
  172. package/mcp-server/dist/services/memory-judge-backfill.js +510 -0
  173. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -0
  174. package/mcp-server/dist/services/memory-judgments.d.ts +362 -0
  175. package/mcp-server/dist/services/memory-judgments.d.ts.map +1 -0
  176. package/mcp-server/dist/services/memory-judgments.js +674 -0
  177. package/mcp-server/dist/services/memory-judgments.js.map +1 -0
  178. package/mcp-server/dist/services/memory-maintenance.d.ts +108 -0
  179. package/mcp-server/dist/services/memory-maintenance.d.ts.map +1 -0
  180. package/mcp-server/dist/services/memory-maintenance.js +403 -0
  181. package/mcp-server/dist/services/memory-maintenance.js.map +1 -0
  182. package/mcp-server/dist/services/memory-retrieval.d.ts +66 -0
  183. package/mcp-server/dist/services/memory-retrieval.d.ts.map +1 -0
  184. package/mcp-server/dist/services/memory-retrieval.js +412 -0
  185. package/mcp-server/dist/services/memory-retrieval.js.map +1 -0
  186. package/mcp-server/dist/services/memory-schema.d.ts +16 -0
  187. package/mcp-server/dist/services/memory-schema.d.ts.map +1 -0
  188. package/mcp-server/dist/services/memory-schema.js +282 -0
  189. package/mcp-server/dist/services/memory-schema.js.map +1 -0
  190. package/mcp-server/dist/services/memory-sessions.d.ts +24 -0
  191. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -0
  192. package/mcp-server/dist/services/memory-sessions.js +110 -0
  193. package/mcp-server/dist/services/memory-sessions.js.map +1 -0
  194. package/mcp-server/dist/services/memory-triage.d.ts +60 -0
  195. package/mcp-server/dist/services/memory-triage.d.ts.map +1 -0
  196. package/mcp-server/dist/services/memory-triage.js +189 -0
  197. package/mcp-server/dist/services/memory-triage.js.map +1 -0
  198. package/mcp-server/dist/services/memory-verification.d.ts +24 -0
  199. package/mcp-server/dist/services/memory-verification.d.ts.map +1 -0
  200. package/mcp-server/dist/services/memory-verification.js +92 -0
  201. package/mcp-server/dist/services/memory-verification.js.map +1 -0
  202. package/mcp-server/dist/services/memory-writer.d.ts +26 -0
  203. package/mcp-server/dist/services/memory-writer.d.ts.map +1 -0
  204. package/mcp-server/dist/services/memory-writer.js +126 -0
  205. package/mcp-server/dist/services/memory-writer.js.map +1 -0
  206. package/mcp-server/dist/services/neural-interface.d.ts +439 -0
  207. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -0
  208. package/mcp-server/dist/services/neural-interface.js +1135 -0
  209. package/mcp-server/dist/services/neural-interface.js.map +1 -0
  210. package/mcp-server/dist/services/profiles.d.ts +120 -0
  211. package/mcp-server/dist/services/profiles.d.ts.map +1 -0
  212. package/mcp-server/dist/services/profiles.js +387 -0
  213. package/mcp-server/dist/services/profiles.js.map +1 -0
  214. package/mcp-server/dist/services/secret-gate.d.ts +29 -0
  215. package/mcp-server/dist/services/secret-gate.d.ts.map +1 -0
  216. package/mcp-server/dist/services/secret-gate.js +43 -0
  217. package/mcp-server/dist/services/secret-gate.js.map +1 -0
  218. package/mcp-server/dist/services/server-instructions.d.ts +10 -0
  219. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -0
  220. package/mcp-server/dist/services/server-instructions.js +101 -0
  221. package/mcp-server/dist/services/server-instructions.js.map +1 -0
  222. package/mcp-server/dist/services/sqlite.d.ts +235 -0
  223. package/mcp-server/dist/services/sqlite.d.ts.map +1 -0
  224. package/mcp-server/dist/services/sqlite.js +1185 -0
  225. package/mcp-server/dist/services/sqlite.js.map +1 -0
  226. package/mcp-server/dist/services/typesafe-bench.d.ts +99 -0
  227. package/mcp-server/dist/services/typesafe-bench.d.ts.map +1 -0
  228. package/mcp-server/dist/services/typesafe-bench.js +218 -0
  229. package/mcp-server/dist/services/typesafe-bench.js.map +1 -0
  230. package/mcp-server/dist/services/typesafe-config.d.ts +342 -0
  231. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -0
  232. package/mcp-server/dist/services/typesafe-config.js +746 -0
  233. package/mcp-server/dist/services/typesafe-config.js.map +1 -0
  234. package/mcp-server/dist/services/typesafe.d.ts +213 -0
  235. package/mcp-server/dist/services/typesafe.d.ts.map +1 -0
  236. package/mcp-server/dist/services/typesafe.js +330 -0
  237. package/mcp-server/dist/services/typesafe.js.map +1 -0
  238. package/mcp-server/dist/services/vector-cache.d.ts +68 -0
  239. package/mcp-server/dist/services/vector-cache.d.ts.map +1 -0
  240. package/mcp-server/dist/services/vector-cache.js +271 -0
  241. package/mcp-server/dist/services/vector-cache.js.map +1 -0
  242. package/mcp-server/dist/services/whiteboard-geometry.d.ts +305 -0
  243. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -0
  244. package/mcp-server/dist/services/whiteboard-geometry.js +666 -0
  245. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -0
  246. package/mcp-server/dist/tools/agents.d.ts +212 -0
  247. package/mcp-server/dist/tools/agents.d.ts.map +1 -0
  248. package/mcp-server/dist/tools/agents.js +407 -0
  249. package/mcp-server/dist/tools/agents.js.map +1 -0
  250. package/mcp-server/dist/tools/bluesky.d.ts +39 -0
  251. package/mcp-server/dist/tools/bluesky.d.ts.map +1 -0
  252. package/mcp-server/dist/tools/bluesky.js +1144 -0
  253. package/mcp-server/dist/tools/bluesky.js.map +1 -0
  254. package/mcp-server/dist/tools/browser-advanced.d.ts +58 -0
  255. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -0
  256. package/mcp-server/dist/tools/browser-advanced.js +106 -0
  257. package/mcp-server/dist/tools/browser-advanced.js.map +1 -0
  258. package/mcp-server/dist/tools/browser-batch.d.ts +99 -0
  259. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -0
  260. package/mcp-server/dist/tools/browser-batch.js +187 -0
  261. package/mcp-server/dist/tools/browser-batch.js.map +1 -0
  262. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +21 -0
  263. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -0
  264. package/mcp-server/dist/tools/browser-cheatsheet.js +144 -0
  265. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -0
  266. package/mcp-server/dist/tools/browser-interact.d.ts +239 -0
  267. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -0
  268. package/mcp-server/dist/tools/browser-interact.js +300 -0
  269. package/mcp-server/dist/tools/browser-interact.js.map +1 -0
  270. package/mcp-server/dist/tools/browser-navigate.d.ts +90 -0
  271. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -0
  272. package/mcp-server/dist/tools/browser-navigate.js +142 -0
  273. package/mcp-server/dist/tools/browser-navigate.js.map +1 -0
  274. package/mcp-server/dist/tools/browser-observe.d.ts +561 -0
  275. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -0
  276. package/mcp-server/dist/tools/browser-observe.js +2009 -0
  277. package/mcp-server/dist/tools/browser-observe.js.map +1 -0
  278. package/mcp-server/dist/tools/browser.d.ts +34 -0
  279. package/mcp-server/dist/tools/browser.d.ts.map +1 -0
  280. package/mcp-server/dist/tools/browser.js +112 -0
  281. package/mcp-server/dist/tools/browser.js.map +1 -0
  282. package/mcp-server/dist/tools/card-tools.d.ts +82 -0
  283. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -0
  284. package/mcp-server/dist/tools/card-tools.js +137 -0
  285. package/mcp-server/dist/tools/card-tools.js.map +1 -0
  286. package/mcp-server/dist/tools/card.d.ts +8 -0
  287. package/mcp-server/dist/tools/card.d.ts.map +1 -0
  288. package/mcp-server/dist/tools/card.js +16 -0
  289. package/mcp-server/dist/tools/card.js.map +1 -0
  290. package/mcp-server/dist/tools/category.d.ts +32 -0
  291. package/mcp-server/dist/tools/category.d.ts.map +1 -0
  292. package/mcp-server/dist/tools/category.js +290 -0
  293. package/mcp-server/dist/tools/category.js.map +1 -0
  294. package/mcp-server/dist/tools/choice.d.ts +160 -0
  295. package/mcp-server/dist/tools/choice.d.ts.map +1 -0
  296. package/mcp-server/dist/tools/choice.js +80 -0
  297. package/mcp-server/dist/tools/choice.js.map +1 -0
  298. package/mcp-server/dist/tools/computer.d.ts +71 -0
  299. package/mcp-server/dist/tools/computer.d.ts.map +1 -0
  300. package/mcp-server/dist/tools/computer.js +174 -0
  301. package/mcp-server/dist/tools/computer.js.map +1 -0
  302. package/mcp-server/dist/tools/discord-channel.d.ts +44 -0
  303. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -0
  304. package/mcp-server/dist/tools/discord-channel.js +236 -0
  305. package/mcp-server/dist/tools/discord-channel.js.map +1 -0
  306. package/mcp-server/dist/tools/discord-guild.d.ts +20 -0
  307. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -0
  308. package/mcp-server/dist/tools/discord-guild.js +138 -0
  309. package/mcp-server/dist/tools/discord-guild.js.map +1 -0
  310. package/mcp-server/dist/tools/discord-member.d.ts +26 -0
  311. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -0
  312. package/mcp-server/dist/tools/discord-member.js +122 -0
  313. package/mcp-server/dist/tools/discord-member.js.map +1 -0
  314. package/mcp-server/dist/tools/discord-message.d.ts +36 -0
  315. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -0
  316. package/mcp-server/dist/tools/discord-message.js +184 -0
  317. package/mcp-server/dist/tools/discord-message.js.map +1 -0
  318. package/mcp-server/dist/tools/discord-onboarding.d.ts +32 -0
  319. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -0
  320. package/mcp-server/dist/tools/discord-onboarding.js +179 -0
  321. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -0
  322. package/mcp-server/dist/tools/discord-role.d.ts +34 -0
  323. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -0
  324. package/mcp-server/dist/tools/discord-role.js +178 -0
  325. package/mcp-server/dist/tools/discord-role.js.map +1 -0
  326. package/mcp-server/dist/tools/discord-thread.d.ts +28 -0
  327. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -0
  328. package/mcp-server/dist/tools/discord-thread.js +88 -0
  329. package/mcp-server/dist/tools/discord-thread.js.map +1 -0
  330. package/mcp-server/dist/tools/discord-webhook.d.ts +32 -0
  331. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -0
  332. package/mcp-server/dist/tools/discord-webhook.js +154 -0
  333. package/mcp-server/dist/tools/discord-webhook.js.map +1 -0
  334. package/mcp-server/dist/tools/discord.d.ts +7 -0
  335. package/mcp-server/dist/tools/discord.d.ts.map +1 -0
  336. package/mcp-server/dist/tools/discord.js +25 -0
  337. package/mcp-server/dist/tools/discord.js.map +1 -0
  338. package/mcp-server/dist/tools/fb-groups.d.ts +98 -0
  339. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -0
  340. package/mcp-server/dist/tools/fb-groups.js +369 -0
  341. package/mcp-server/dist/tools/fb-groups.js.map +1 -0
  342. package/mcp-server/dist/tools/forget.d.ts +14 -0
  343. package/mcp-server/dist/tools/forget.d.ts.map +1 -0
  344. package/mcp-server/dist/tools/forget.js +26 -0
  345. package/mcp-server/dist/tools/forget.js.map +1 -0
  346. package/mcp-server/dist/tools/git-tools.d.ts +24 -0
  347. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -0
  348. package/mcp-server/dist/tools/git-tools.js +107 -0
  349. package/mcp-server/dist/tools/git-tools.js.map +1 -0
  350. package/mcp-server/dist/tools/git.d.ts +7 -0
  351. package/mcp-server/dist/tools/git.d.ts.map +1 -0
  352. package/mcp-server/dist/tools/git.js +11 -0
  353. package/mcp-server/dist/tools/git.js.map +1 -0
  354. package/mcp-server/dist/tools/gsc/experience.d.ts +135 -0
  355. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -0
  356. package/mcp-server/dist/tools/gsc/experience.js +351 -0
  357. package/mcp-server/dist/tools/gsc/experience.js.map +1 -0
  358. package/mcp-server/dist/tools/gsc/helpers.d.ts +78 -0
  359. package/mcp-server/dist/tools/gsc/helpers.d.ts.map +1 -0
  360. package/mcp-server/dist/tools/gsc/helpers.js +258 -0
  361. package/mcp-server/dist/tools/gsc/helpers.js.map +1 -0
  362. package/mcp-server/dist/tools/gsc/indexing.d.ts +121 -0
  363. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -0
  364. package/mcp-server/dist/tools/gsc/indexing.js +357 -0
  365. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -0
  366. package/mcp-server/dist/tools/gsc/inspect.d.ts +78 -0
  367. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -0
  368. package/mcp-server/dist/tools/gsc/inspect.js +321 -0
  369. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -0
  370. package/mcp-server/dist/tools/gsc/navigate.d.ts +44 -0
  371. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -0
  372. package/mcp-server/dist/tools/gsc/navigate.js +197 -0
  373. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -0
  374. package/mcp-server/dist/tools/gsc/performance.d.ts +84 -0
  375. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -0
  376. package/mcp-server/dist/tools/gsc/performance.js +274 -0
  377. package/mcp-server/dist/tools/gsc/performance.js.map +1 -0
  378. package/mcp-server/dist/tools/gsc/settings.d.ts +153 -0
  379. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -0
  380. package/mcp-server/dist/tools/gsc/settings.js +425 -0
  381. package/mcp-server/dist/tools/gsc/settings.js.map +1 -0
  382. package/mcp-server/dist/tools/gsc.d.ts +7 -0
  383. package/mcp-server/dist/tools/gsc.d.ts.map +1 -0
  384. package/mcp-server/dist/tools/gsc.js +51 -0
  385. package/mcp-server/dist/tools/gsc.js.map +1 -0
  386. package/mcp-server/dist/tools/image-tools.d.ts +24 -0
  387. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -0
  388. package/mcp-server/dist/tools/image-tools.js +80 -0
  389. package/mcp-server/dist/tools/image-tools.js.map +1 -0
  390. package/mcp-server/dist/tools/image.d.ts +10 -0
  391. package/mcp-server/dist/tools/image.d.ts.map +1 -0
  392. package/mcp-server/dist/tools/image.js +14 -0
  393. package/mcp-server/dist/tools/image.js.map +1 -0
  394. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +97 -0
  395. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -0
  396. package/mcp-server/dist/tools/leonardo-browser-tools.js +228 -0
  397. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -0
  398. package/mcp-server/dist/tools/leonardo.d.ts +13 -0
  399. package/mcp-server/dist/tools/leonardo.d.ts.map +1 -0
  400. package/mcp-server/dist/tools/leonardo.js +21 -0
  401. package/mcp-server/dist/tools/leonardo.js.map +1 -0
  402. package/mcp-server/dist/tools/loop.d.ts +30 -0
  403. package/mcp-server/dist/tools/loop.d.ts.map +1 -0
  404. package/mcp-server/dist/tools/loop.js +401 -0
  405. package/mcp-server/dist/tools/loop.js.map +1 -0
  406. package/mcp-server/dist/tools/memories.d.ts +56 -0
  407. package/mcp-server/dist/tools/memories.d.ts.map +1 -0
  408. package/mcp-server/dist/tools/memories.js +171 -0
  409. package/mcp-server/dist/tools/memories.js.map +1 -0
  410. package/mcp-server/dist/tools/morelogin.d.ts +24 -0
  411. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -0
  412. package/mcp-server/dist/tools/morelogin.js +79 -0
  413. package/mcp-server/dist/tools/morelogin.js.map +1 -0
  414. package/mcp-server/dist/tools/profile.d.ts +17 -0
  415. package/mcp-server/dist/tools/profile.d.ts.map +1 -0
  416. package/mcp-server/dist/tools/profile.js +114 -0
  417. package/mcp-server/dist/tools/profile.js.map +1 -0
  418. package/mcp-server/dist/tools/recall.d.ts +71 -0
  419. package/mcp-server/dist/tools/recall.d.ts.map +1 -0
  420. package/mcp-server/dist/tools/recall.js +147 -0
  421. package/mcp-server/dist/tools/recall.js.map +1 -0
  422. package/mcp-server/dist/tools/reflect.d.ts +56 -0
  423. package/mcp-server/dist/tools/reflect.d.ts.map +1 -0
  424. package/mcp-server/dist/tools/reflect.js +157 -0
  425. package/mcp-server/dist/tools/reflect.js.map +1 -0
  426. package/mcp-server/dist/tools/remember.d.ts +62 -0
  427. package/mcp-server/dist/tools/remember.d.ts.map +1 -0
  428. package/mcp-server/dist/tools/remember.js +201 -0
  429. package/mcp-server/dist/tools/remember.js.map +1 -0
  430. package/mcp-server/dist/tools/response.d.ts +23 -0
  431. package/mcp-server/dist/tools/response.d.ts.map +1 -0
  432. package/mcp-server/dist/tools/response.js +24 -0
  433. package/mcp-server/dist/tools/response.js.map +1 -0
  434. package/mcp-server/dist/tools/restore.d.ts +14 -0
  435. package/mcp-server/dist/tools/restore.d.ts.map +1 -0
  436. package/mcp-server/dist/tools/restore.js +23 -0
  437. package/mcp-server/dist/tools/restore.js.map +1 -0
  438. package/mcp-server/dist/tools/style-guide-tools.d.ts +51 -0
  439. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -0
  440. package/mcp-server/dist/tools/style-guide-tools.js +231 -0
  441. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -0
  442. package/mcp-server/dist/tools/style-guide.d.ts +8 -0
  443. package/mcp-server/dist/tools/style-guide.d.ts.map +1 -0
  444. package/mcp-server/dist/tools/style-guide.js +12 -0
  445. package/mcp-server/dist/tools/style-guide.js.map +1 -0
  446. package/mcp-server/dist/tools/sync.d.ts +20 -0
  447. package/mcp-server/dist/tools/sync.d.ts.map +1 -0
  448. package/mcp-server/dist/tools/sync.js +156 -0
  449. package/mcp-server/dist/tools/sync.js.map +1 -0
  450. package/mcp-server/dist/tools/tictactoe-tools.d.ts +18 -0
  451. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -0
  452. package/mcp-server/dist/tools/tictactoe-tools.js +78 -0
  453. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -0
  454. package/mcp-server/dist/tools/tictactoe.d.ts +7 -0
  455. package/mcp-server/dist/tools/tictactoe.d.ts.map +1 -0
  456. package/mcp-server/dist/tools/tictactoe.js +11 -0
  457. package/mcp-server/dist/tools/tictactoe.js.map +1 -0
  458. package/mcp-server/dist/tools/utils.d.ts +19 -0
  459. package/mcp-server/dist/tools/utils.d.ts.map +1 -0
  460. package/mcp-server/dist/tools/utils.js +54 -0
  461. package/mcp-server/dist/tools/utils.js.map +1 -0
  462. package/mcp-server/dist/tools/whiteboard-format.d.ts +88 -0
  463. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -0
  464. package/mcp-server/dist/tools/whiteboard-format.js +337 -0
  465. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -0
  466. package/mcp-server/dist/tools/whiteboard-tools.d.ts +326 -0
  467. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -0
  468. package/mcp-server/dist/tools/whiteboard-tools.js +222 -0
  469. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -0
  470. package/mcp-server/dist/tools/whiteboard.d.ts +8 -0
  471. package/mcp-server/dist/tools/whiteboard.d.ts.map +1 -0
  472. package/mcp-server/dist/tools/whiteboard.js +16 -0
  473. package/mcp-server/dist/tools/whiteboard.js.map +1 -0
  474. package/mcp-server/dist/tools/youtube/analytics.d.ts +46 -0
  475. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -0
  476. package/mcp-server/dist/tools/youtube/analytics.js +83 -0
  477. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -0
  478. package/mcp-server/dist/tools/youtube/dedup.d.ts +35 -0
  479. package/mcp-server/dist/tools/youtube/dedup.d.ts.map +1 -0
  480. package/mcp-server/dist/tools/youtube/dedup.js +56 -0
  481. package/mcp-server/dist/tools/youtube/dedup.js.map +1 -0
  482. package/mcp-server/dist/tools/youtube/download.d.ts +31 -0
  483. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -0
  484. package/mcp-server/dist/tools/youtube/download.js +30 -0
  485. package/mcp-server/dist/tools/youtube/download.js.map +1 -0
  486. package/mcp-server/dist/tools/youtube/helpers.d.ts +66 -0
  487. package/mcp-server/dist/tools/youtube/helpers.d.ts.map +1 -0
  488. package/mcp-server/dist/tools/youtube/helpers.js +143 -0
  489. package/mcp-server/dist/tools/youtube/helpers.js.map +1 -0
  490. package/mcp-server/dist/tools/youtube/media.d.ts +43 -0
  491. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -0
  492. package/mcp-server/dist/tools/youtube/media.js +97 -0
  493. package/mcp-server/dist/tools/youtube/media.js.map +1 -0
  494. package/mcp-server/dist/tools/youtube/metadata.d.ts +33 -0
  495. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -0
  496. package/mcp-server/dist/tools/youtube/metadata.js +106 -0
  497. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -0
  498. package/mcp-server/dist/tools/youtube/navigate.d.ts +30 -0
  499. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -0
  500. package/mcp-server/dist/tools/youtube/navigate.js +71 -0
  501. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -0
  502. package/mcp-server/dist/tools/youtube/playlist.d.ts +29 -0
  503. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -0
  504. package/mcp-server/dist/tools/youtube/playlist.js +128 -0
  505. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -0
  506. package/mcp-server/dist/tools/youtube/schedule.d.ts +31 -0
  507. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -0
  508. package/mcp-server/dist/tools/youtube/schedule.js +147 -0
  509. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -0
  510. package/mcp-server/dist/tools/youtube/source.d.ts +36 -0
  511. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -0
  512. package/mcp-server/dist/tools/youtube/source.js +85 -0
  513. package/mcp-server/dist/tools/youtube/source.js.map +1 -0
  514. package/mcp-server/dist/tools/youtube/upload.d.ts +53 -0
  515. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -0
  516. package/mcp-server/dist/tools/youtube/upload.js +255 -0
  517. package/mcp-server/dist/tools/youtube/upload.js.map +1 -0
  518. package/mcp-server/dist/tools/youtube.d.ts +12 -0
  519. package/mcp-server/dist/tools/youtube.d.ts.map +1 -0
  520. package/mcp-server/dist/tools/youtube.js +38 -0
  521. package/mcp-server/dist/tools/youtube.js.map +1 -0
  522. package/mcp-server/dist/tui.d.ts +3 -0
  523. package/mcp-server/dist/tui.d.ts.map +1 -0
  524. package/mcp-server/dist/tui.js +806 -0
  525. package/mcp-server/dist/tui.js.map +1 -0
  526. package/mcp-server/dist/types.d.ts +77 -0
  527. package/mcp-server/dist/types.d.ts.map +1 -0
  528. package/mcp-server/dist/types.js +2 -0
  529. package/mcp-server/dist/types.js.map +1 -0
  530. package/mcp-server/package-lock.json +4798 -0
  531. package/mcp-server/package.json +55 -38
  532. package/mcp-server/run.mjs +12 -2
  533. package/mcp-server/src/config.ts +48 -3
  534. package/mcp-server/src/http.ts +296 -93
  535. package/mcp-server/src/index.ts +546 -203
  536. package/mcp-server/src/preload-http.ts +19 -18
  537. package/mcp-server/src/preload.ts +19 -18
  538. package/mcp-server/src/scripts/setup-embeddings.ts +9 -0
  539. package/mcp-server/src/services/assistant-judgments.ts +157 -0
  540. package/mcp-server/src/services/browser-assist-gate.ts +383 -0
  541. package/mcp-server/src/services/browser-assist.ts +267 -0
  542. package/mcp-server/src/services/browser-bench.ts +477 -0
  543. package/mcp-server/src/services/browser-judgments.ts +414 -0
  544. package/mcp-server/src/services/browser-risk.ts +484 -0
  545. package/mcp-server/src/services/categories.ts +36 -19
  546. package/mcp-server/src/services/codex-config-heal.ts +403 -0
  547. package/mcp-server/src/services/desktop-assist-gate.ts +251 -0
  548. package/mcp-server/src/services/desktop-assist.ts +308 -0
  549. package/mcp-server/src/services/desktop-bench.ts +372 -0
  550. package/mcp-server/src/services/desktop-client.ts +76 -0
  551. package/mcp-server/src/services/desktop-judgments.ts +218 -0
  552. package/mcp-server/src/services/desktop-risk.ts +743 -0
  553. package/mcp-server/src/services/edit-stale.ts +299 -0
  554. package/mcp-server/src/services/embedding-service.ts +108 -0
  555. package/mcp-server/src/services/embedding-worker.ts +46 -0
  556. package/mcp-server/src/services/fb-regions.ts +221 -0
  557. package/mcp-server/src/services/file-checksums.ts +26 -9
  558. package/mcp-server/src/services/file-excerpt.ts +59 -0
  559. package/mcp-server/src/services/identity.ts +291 -0
  560. package/mcp-server/src/services/local-embeddings.ts +2 -77
  561. package/mcp-server/src/services/memory-client.ts +23 -0
  562. package/mcp-server/src/services/memory-judge-backfill.ts +462 -0
  563. package/mcp-server/src/services/memory-judgments.ts +901 -0
  564. package/mcp-server/src/services/memory-maintenance.ts +360 -0
  565. package/mcp-server/src/services/memory-retrieval.ts +349 -0
  566. package/mcp-server/src/services/memory-schema.ts +280 -0
  567. package/mcp-server/src/services/memory-sessions.ts +87 -0
  568. package/mcp-server/src/services/memory-triage.ts +182 -0
  569. package/mcp-server/src/services/memory-verification.ts +68 -0
  570. package/mcp-server/src/services/memory-writer.ts +125 -0
  571. package/mcp-server/src/services/neural-interface.ts +1217 -73
  572. package/mcp-server/src/services/profiles.ts +428 -0
  573. package/mcp-server/src/services/secret-gate.ts +52 -0
  574. package/mcp-server/src/services/server-instructions.ts +122 -0
  575. package/mcp-server/src/services/sqlite.ts +1456 -823
  576. package/mcp-server/src/services/typesafe-bench.ts +247 -0
  577. package/mcp-server/src/services/typesafe-config.ts +790 -0
  578. package/mcp-server/src/services/typesafe.ts +390 -0
  579. package/mcp-server/src/services/vector-cache.ts +255 -0
  580. package/mcp-server/src/services/whiteboard-geometry.ts +711 -0
  581. package/mcp-server/src/tools/agents.ts +539 -0
  582. package/mcp-server/src/tools/bluesky.ts +1186 -0
  583. package/mcp-server/src/tools/browser-advanced.ts +15 -6
  584. package/mcp-server/src/tools/browser-batch.ts +178 -0
  585. package/mcp-server/src/tools/browser-cheatsheet.ts +155 -0
  586. package/mcp-server/src/tools/browser-interact.ts +362 -255
  587. package/mcp-server/src/tools/browser-navigate.ts +164 -77
  588. package/mcp-server/src/tools/browser-observe.ts +2222 -1330
  589. package/mcp-server/src/tools/browser.ts +181 -107
  590. package/mcp-server/src/tools/card.ts +7 -5
  591. package/mcp-server/src/tools/choice.ts +103 -0
  592. package/mcp-server/src/tools/computer.ts +190 -0
  593. package/mcp-server/src/tools/discord.ts +10 -23
  594. package/mcp-server/src/tools/fb-groups.ts +387 -0
  595. package/mcp-server/src/tools/forget.ts +1 -1
  596. package/mcp-server/src/tools/git.ts +3 -1
  597. package/mcp-server/src/tools/gsc/experience.ts +440 -0
  598. package/mcp-server/src/tools/gsc/helpers.ts +288 -0
  599. package/mcp-server/src/tools/gsc/indexing.ts +495 -0
  600. package/mcp-server/src/tools/gsc/inspect.ts +391 -0
  601. package/mcp-server/src/tools/gsc/navigate.ts +236 -0
  602. package/mcp-server/src/tools/gsc/performance.ts +352 -0
  603. package/mcp-server/src/tools/gsc/settings.ts +564 -0
  604. package/mcp-server/src/tools/gsc.ts +95 -0
  605. package/mcp-server/src/tools/image.ts +3 -1
  606. package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
  607. package/mcp-server/src/tools/leonardo.ts +7 -5
  608. package/mcp-server/src/tools/loop.ts +118 -25
  609. package/mcp-server/src/tools/memories.ts +65 -5
  610. package/mcp-server/src/tools/morelogin.ts +81 -0
  611. package/mcp-server/src/tools/profile.ts +122 -0
  612. package/mcp-server/src/tools/recall.ts +105 -261
  613. package/mcp-server/src/tools/reflect.ts +171 -145
  614. package/mcp-server/src/tools/remember.ts +223 -115
  615. package/mcp-server/src/tools/style-guide-tools.ts +250 -0
  616. package/mcp-server/src/tools/style-guide.ts +16 -0
  617. package/mcp-server/src/tools/sync.ts +66 -9
  618. package/mcp-server/src/tools/tictactoe.ts +3 -1
  619. package/mcp-server/src/tools/utils.ts +22 -0
  620. package/mcp-server/src/tools/whiteboard-format.ts +394 -0
  621. package/mcp-server/src/tools/whiteboard-tools.ts +191 -183
  622. package/mcp-server/src/tools/whiteboard.ts +7 -5
  623. package/mcp-server/src/tools/youtube/analytics.ts +106 -0
  624. package/mcp-server/src/tools/youtube/dedup.ts +57 -0
  625. package/mcp-server/src/tools/youtube/download.ts +41 -0
  626. package/mcp-server/src/tools/youtube/helpers.ts +183 -0
  627. package/mcp-server/src/tools/youtube/media.ts +109 -0
  628. package/mcp-server/src/tools/youtube/metadata.ts +112 -0
  629. package/mcp-server/src/tools/youtube/navigate.ts +81 -0
  630. package/mcp-server/src/tools/youtube/playlist.ts +134 -0
  631. package/mcp-server/src/tools/youtube/schedule.ts +158 -0
  632. package/mcp-server/src/tools/youtube/source.ts +101 -0
  633. package/mcp-server/src/tools/youtube/upload.ts +296 -0
  634. package/mcp-server/src/tools/youtube.ts +47 -0
  635. package/mcp-server/src/tui.ts +8 -9
  636. package/neural-interface/games/TicTacToe/1.svg +1 -0
  637. package/neural-interface/games/TicTacToe/2.svg +1 -0
  638. package/neural-interface/games/TicTacToe/3.svg +1 -0
  639. package/neural-interface/games/TicTacToe/4.svg +1 -0
  640. package/neural-interface/games/TicTacToe/5.svg +1 -0
  641. package/neural-interface/games/TicTacToe/6.svg +1 -0
  642. package/neural-interface/games/TicTacToe/7.svg +1 -0
  643. package/neural-interface/games/TicTacToe/8.svg +1 -0
  644. package/neural-interface/games/TicTacToe/9.svg +1 -0
  645. package/neural-interface/games/TicTacToe/Board.svg +1 -0
  646. package/neural-interface/games/TicTacToe/Circle.svg +1 -0
  647. package/neural-interface/games/TicTacToe/Cross.svg +1 -0
  648. package/neural-interface/i18n/en.json +7022 -0
  649. package/neural-interface/i18n/pt-BR.json +7026 -0
  650. package/neural-interface/lib/assistant-api.js +1005 -0
  651. package/neural-interface/lib/assistant-attachments.js +121 -0
  652. package/neural-interface/lib/assistant-brains/claude.js +207 -0
  653. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +43 -0
  654. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +56 -0
  655. package/neural-interface/lib/assistant-brains/codex.js +213 -0
  656. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +47 -0
  657. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +62 -0
  658. package/neural-interface/lib/assistant-brains/opencode.js +320 -0
  659. package/neural-interface/lib/assistant-budget.js +318 -0
  660. package/neural-interface/lib/assistant-catalog.js +731 -0
  661. package/neural-interface/lib/assistant-clarify.js +664 -0
  662. package/neural-interface/lib/assistant-config.js +232 -0
  663. package/neural-interface/lib/assistant-dispatch.js +2744 -0
  664. package/neural-interface/lib/assistant-envelope.js +582 -0
  665. package/neural-interface/lib/assistant-evidence.js +236 -0
  666. package/neural-interface/lib/assistant-hidden-models.js +272 -0
  667. package/neural-interface/lib/assistant-jev.js +117 -0
  668. package/neural-interface/lib/assistant-judgments.js +111 -0
  669. package/neural-interface/lib/assistant-media.js +335 -0
  670. package/neural-interface/lib/assistant-memory.js +398 -0
  671. package/neural-interface/lib/assistant-persona.js +444 -0
  672. package/neural-interface/lib/assistant-plan-permissions.js +448 -0
  673. package/neural-interface/lib/assistant-playbooks/design.md +87 -0
  674. package/neural-interface/lib/assistant-playbooks.js +72 -0
  675. package/neural-interface/lib/assistant-pricing.js +159 -0
  676. package/neural-interface/lib/assistant-route-gate-codex.js +163 -0
  677. package/neural-interface/lib/assistant-route-gate.js +212 -0
  678. package/neural-interface/lib/assistant-router.js +1324 -0
  679. package/neural-interface/lib/assistant-runtime.js +3337 -0
  680. package/neural-interface/lib/assistant-task-prompt.js +472 -0
  681. package/neural-interface/lib/assistant-usage.js +1688 -0
  682. package/neural-interface/lib/augmented-path.js +70 -0
  683. package/neural-interface/lib/automation-storage-migration.js +54 -0
  684. package/neural-interface/lib/backup-service.js +851 -0
  685. package/neural-interface/lib/backup-zip-reader.js +262 -0
  686. package/neural-interface/lib/browser-assist-api.js +41 -0
  687. package/neural-interface/lib/browser-console.js +176 -0
  688. package/neural-interface/lib/browser-execution.js +241 -0
  689. package/neural-interface/lib/browser-extraction.js +191 -0
  690. package/neural-interface/lib/browser-language.js +83 -0
  691. package/neural-interface/lib/browser-screenshot.js +399 -0
  692. package/neural-interface/lib/browser-semantic-context.js +639 -0
  693. package/neural-interface/lib/browser-snapshots.js +192 -0
  694. package/neural-interface/lib/browser-tab-recovery.js +119 -0
  695. package/neural-interface/lib/browser-targets.js +70 -0
  696. package/neural-interface/lib/browser-tool-policy.js +926 -0
  697. package/neural-interface/lib/claude-accounts.js +230 -0
  698. package/neural-interface/lib/claude-agent-bridge.js +3203 -0
  699. package/neural-interface/lib/claude-bypass-policy.js +99 -0
  700. package/neural-interface/lib/claude-executable.js +241 -0
  701. package/neural-interface/lib/claude-history.js +429 -0
  702. package/neural-interface/lib/claude-model-catalog.js +444 -0
  703. package/neural-interface/lib/claude-panel-requests.js +165 -0
  704. package/neural-interface/lib/claude-panel-session.js +272 -0
  705. package/neural-interface/lib/claude-permission-rules.js +153 -0
  706. package/neural-interface/lib/claude-session-cleanup.js +71 -0
  707. package/neural-interface/lib/claude-session-list.js +82 -0
  708. package/neural-interface/lib/claude-session-ops.js +140 -0
  709. package/neural-interface/lib/claude-settings-view.js +51 -0
  710. package/neural-interface/lib/claude-skin-connect.js +79 -0
  711. package/neural-interface/lib/claude-stall-policy.js +7 -0
  712. package/neural-interface/lib/claude-temporary.js +435 -0
  713. package/neural-interface/lib/cli-update-planner.js +312 -0
  714. package/neural-interface/lib/codex-capabilities.d.ts +41 -0
  715. package/neural-interface/lib/codex-capabilities.js +148 -0
  716. package/neural-interface/lib/codex-collaboration-mode.js +17 -0
  717. package/neural-interface/lib/codex-compat-rpc.js +241 -0
  718. package/neural-interface/lib/codex-mcp-approvals.js +56 -0
  719. package/neural-interface/lib/codex-mcp-apps.js +47 -0
  720. package/neural-interface/lib/codex-model-context.js +202 -0
  721. package/neural-interface/lib/codex-plan-store.js +71 -0
  722. package/neural-interface/lib/codex-request-journal.js +242 -0
  723. package/neural-interface/lib/codex-runtime-path.js +213 -0
  724. package/neural-interface/lib/codex-session-catalog.js +231 -0
  725. package/neural-interface/lib/codex-session-settings.js +46 -0
  726. package/neural-interface/lib/codex-sidepanel-permissions.js +110 -0
  727. package/neural-interface/lib/codex-thread-history.js +165 -0
  728. package/neural-interface/lib/codex-user-verification.js +34 -0
  729. package/neural-interface/lib/codex-writer-lifecycle.js +152 -0
  730. package/neural-interface/lib/confined-fs.js +256 -0
  731. package/neural-interface/lib/db.js +922 -0
  732. package/neural-interface/lib/design-md.js +9 -0
  733. package/neural-interface/lib/desktop/api.js +92 -0
  734. package/neural-interface/lib/desktop/audit.js +62 -0
  735. package/neural-interface/lib/desktop/build.js +423 -0
  736. package/neural-interface/lib/desktop/config.js +606 -0
  737. package/neural-interface/lib/desktop/coords.js +76 -0
  738. package/neural-interface/lib/desktop/fake-helper.js +1192 -0
  739. package/neural-interface/lib/desktop/frames.js +77 -0
  740. package/neural-interface/lib/desktop/grants.js +84 -0
  741. package/neural-interface/lib/desktop/guards.js +170 -0
  742. package/neural-interface/lib/desktop/helper/SynabunDesktop.swift +3056 -0
  743. package/neural-interface/lib/desktop/lease.js +51 -0
  744. package/neural-interface/lib/desktop/manager.js +545 -0
  745. package/neural-interface/lib/desktop/press-contexts.js +103 -0
  746. package/neural-interface/lib/desktop/protocol.js +495 -0
  747. package/neural-interface/lib/desktop/service.js +1202 -0
  748. package/neural-interface/lib/effort-levels.js +148 -0
  749. package/neural-interface/lib/event-loop-monitor.js +67 -0
  750. package/neural-interface/lib/exec-loop-command.js +209 -0
  751. package/neural-interface/lib/folder-picker.ps1 +77 -0
  752. package/neural-interface/lib/http-guards.js +287 -0
  753. package/neural-interface/lib/jsonl-lines.js +94 -0
  754. package/neural-interface/lib/loop-logger.js +127 -0
  755. package/neural-interface/lib/mcp-client-config.js +133 -0
  756. package/neural-interface/lib/mcp-installer.js +487 -0
  757. package/neural-interface/lib/mcp-profile-refresh-coordinator.js +78 -0
  758. package/neural-interface/lib/mcp-profile-servers.js +120 -0
  759. package/neural-interface/lib/memo-cache.js +84 -0
  760. package/neural-interface/lib/memory-api.js +758 -0
  761. package/neural-interface/lib/memory-map-api.js +1150 -0
  762. package/neural-interface/lib/memory-map-layout.js +872 -0
  763. package/neural-interface/lib/memory-map-worker.js +165 -0
  764. package/neural-interface/lib/memory-session-capture.js +81 -0
  765. package/neural-interface/lib/memory-timers.js +62 -0
  766. package/neural-interface/lib/morelogin.js +301 -0
  767. package/neural-interface/lib/native-binary-runtime.js +382 -0
  768. package/neural-interface/lib/native-loop-goal.js +33 -0
  769. package/neural-interface/lib/native-loop-providers.js +1002 -0
  770. package/neural-interface/lib/native-loop-runtime.js +747 -0
  771. package/neural-interface/lib/offline-page.js +54 -0
  772. package/neural-interface/lib/opencode-hidden-models.js +113 -0
  773. package/neural-interface/lib/opencode-history.js +253 -0
  774. package/neural-interface/lib/opencode-runtime-config.js +58 -0
  775. package/neural-interface/lib/opencode-v2-client.js +486 -0
  776. package/neural-interface/lib/opencode-v2-ws-requests.js +955 -0
  777. package/neural-interface/lib/path-confine.js +213 -0
  778. package/neural-interface/lib/plugin-installer.js +264 -0
  779. package/neural-interface/lib/project-storage.js +766 -0
  780. package/neural-interface/lib/pty-host/core.js +603 -0
  781. package/neural-interface/lib/pty-host/flow.js +43 -0
  782. package/neural-interface/lib/pty-host/host.js +47 -0
  783. package/neural-interface/lib/pty-host/manager.js +360 -0
  784. package/neural-interface/lib/pty-host/protocol.js +81 -0
  785. package/neural-interface/lib/pty-host/snapshot.js +133 -0
  786. package/neural-interface/lib/remote-policy.js +582 -0
  787. package/neural-interface/lib/rulesets/api.js +175 -0
  788. package/neural-interface/lib/rulesets/fs-safe.js +245 -0
  789. package/neural-interface/lib/rulesets/installer.js +1453 -0
  790. package/neural-interface/lib/rulesets/json-edit.js +186 -0
  791. package/neural-interface/lib/rulesets/lock.js +170 -0
  792. package/neural-interface/lib/rulesets/managed-text.js +314 -0
  793. package/neural-interface/lib/rulesets/normalise.js +24 -0
  794. package/neural-interface/lib/rulesets/render.js +84 -0
  795. package/neural-interface/lib/schedule-launch-intent.js +40 -0
  796. package/neural-interface/lib/session-cache-builder.js +524 -0
  797. package/neural-interface/lib/session-chunker.js +344 -0
  798. package/neural-interface/lib/session-indexer.js +327 -0
  799. package/neural-interface/lib/session-title-generator.js +232 -0
  800. package/neural-interface/lib/skill-prompt-bridge.js +11 -0
  801. package/neural-interface/lib/style-guide/api.js +375 -0
  802. package/neural-interface/lib/style-guide/color.js +352 -0
  803. package/neural-interface/lib/style-guide/export-css.js +99 -0
  804. package/neural-interface/lib/style-guide/export-dtcg.js +209 -0
  805. package/neural-interface/lib/style-guide/export-tailwind.js +102 -0
  806. package/neural-interface/lib/style-guide/fonts.json +227 -0
  807. package/neural-interface/lib/style-guide/import.js +1268 -0
  808. package/neural-interface/lib/style-guide/pointers.js +103 -0
  809. package/neural-interface/lib/style-guide/presets.js +232 -0
  810. package/neural-interface/lib/style-guide/render-design-md.js +319 -0
  811. package/neural-interface/lib/style-guide/schema.js +848 -0
  812. package/neural-interface/lib/style-guide/security.js +43 -0
  813. package/neural-interface/lib/style-guide/store.js +491 -0
  814. package/neural-interface/lib/style-guide/summary.js +106 -0
  815. package/neural-interface/lib/style-guide/tokens.js +170 -0
  816. package/neural-interface/lib/synabun-update-plan.js +324 -0
  817. package/neural-interface/lib/synabun-version.js +179 -0
  818. package/neural-interface/lib/system-restore.js +99 -0
  819. package/neural-interface/lib/terminal-env.js +86 -0
  820. package/neural-interface/lib/ui-blob-store.js +106 -0
  821. package/neural-interface/lib/vector-cache.js +2 -0
  822. package/neural-interface/lib/virtual-socket.js +60 -0
  823. package/neural-interface/lib/whatsapp/api.js +146 -0
  824. package/neural-interface/lib/whatsapp/auth-store.js +389 -0
  825. package/neural-interface/lib/whatsapp/baileys-adapter.js +457 -0
  826. package/neural-interface/lib/whatsapp/brain.js +128 -0
  827. package/neural-interface/lib/whatsapp/bridge.js +1499 -0
  828. package/neural-interface/lib/whatsapp/cards.js +519 -0
  829. package/neural-interface/lib/whatsapp/commands.js +92 -0
  830. package/neural-interface/lib/whatsapp/config.js +291 -0
  831. package/neural-interface/lib/whatsapp/connector/entry.mjs +25 -0
  832. package/neural-interface/lib/whatsapp/connector/manifest.json +6 -0
  833. package/neural-interface/lib/whatsapp/connector/npm-shrinkwrap.json +717 -0
  834. package/neural-interface/lib/whatsapp/connector/package.json +8 -0
  835. package/neural-interface/lib/whatsapp/fake-baileys.js +444 -0
  836. package/neural-interface/lib/whatsapp/format.js +552 -0
  837. package/neural-interface/lib/whatsapp/host-core.js +1225 -0
  838. package/neural-interface/lib/whatsapp/host.js +119 -0
  839. package/neural-interface/lib/whatsapp/identity.js +288 -0
  840. package/neural-interface/lib/whatsapp/inbound.js +149 -0
  841. package/neural-interface/lib/whatsapp/installer.js +562 -0
  842. package/neural-interface/lib/whatsapp/manager.js +638 -0
  843. package/neural-interface/lib/whatsapp/paths.js +81 -0
  844. package/neural-interface/lib/whatsapp/protocol.js +327 -0
  845. package/neural-interface/lib/whatsapp/qr-svg.js +57 -0
  846. package/neural-interface/lib/whatsapp/redact.js +130 -0
  847. package/neural-interface/lib/whatsapp/service.js +1305 -0
  848. package/neural-interface/lib/whatsapp/vendor/qrcodegen.js +848 -0
  849. package/neural-interface/package-lock.json +4431 -0
  850. package/neural-interface/package.json +58 -27
  851. package/neural-interface/public/apple-touch-icon.png +0 -0
  852. package/neural-interface/public/category-logos/synabun.png +0 -0
  853. package/neural-interface/public/claude.svg +1 -0
  854. package/neural-interface/public/favicon-16x16.png +0 -0
  855. package/neural-interface/public/favicon-32x32.png +0 -0
  856. package/neural-interface/public/icon-192.png +0 -0
  857. package/neural-interface/public/icon-512.png +0 -0
  858. package/neural-interface/public/index.html +32 -182
  859. package/neural-interface/public/index2d.html +2 -2
  860. package/neural-interface/public/install-app.html +31 -0
  861. package/neural-interface/public/logo-synabun.png +0 -0
  862. package/neural-interface/public/logoHD.png +0 -0
  863. package/neural-interface/public/manifest.json +2 -0
  864. package/neural-interface/public/mcp.svg +4 -0
  865. package/neural-interface/public/offline.html +670 -299
  866. package/neural-interface/public/onboarding.html +804 -161
  867. package/neural-interface/public/opencode-logo-dark-square.svg +1 -0
  868. package/neural-interface/public/shared/agent-runtime-options.js +596 -0
  869. package/neural-interface/public/shared/api.js +339 -28
  870. package/neural-interface/public/shared/assistant/asst-accounts.js +318 -0
  871. package/neural-interface/public/shared/assistant/asst-agents-dock.js +296 -0
  872. package/neural-interface/public/shared/assistant/asst-api.js +270 -0
  873. package/neural-interface/public/shared/assistant/asst-brain-picker.js +744 -0
  874. package/neural-interface/public/shared/assistant/asst-budget.js +333 -0
  875. package/neural-interface/public/shared/assistant/asst-composer.js +656 -0
  876. package/neural-interface/public/shared/assistant/asst-computer.js +803 -0
  877. package/neural-interface/public/shared/assistant/asst-control.js +1018 -0
  878. package/neural-interface/public/shared/assistant/asst-hosts.js +262 -0
  879. package/neural-interface/public/shared/assistant/asst-mascot.js +313 -0
  880. package/neural-interface/public/shared/assistant/asst-menu.js +375 -0
  881. package/neural-interface/public/shared/assistant/asst-panel.js +1845 -0
  882. package/neural-interface/public/shared/assistant/asst-rack-styles.js +586 -0
  883. package/neural-interface/public/shared/assistant/asst-render.js +3305 -0
  884. package/neural-interface/public/shared/assistant/asst-route.js +1653 -0
  885. package/neural-interface/public/shared/assistant/asst-roving.js +75 -0
  886. package/neural-interface/public/shared/assistant/asst-sanitize.js +117 -0
  887. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +488 -0
  888. package/neural-interface/public/shared/assistant/asst-sidepanel.js +675 -0
  889. package/neural-interface/public/shared/assistant/asst-slash.js +258 -0
  890. package/neural-interface/public/shared/assistant/asst-state.js +727 -0
  891. package/neural-interface/public/shared/assistant/asst-styles.js +1686 -0
  892. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +1017 -0
  893. package/neural-interface/public/shared/assistant/asst-usage.js +444 -0
  894. package/neural-interface/public/shared/assistant/asst-ws.js +175 -0
  895. package/neural-interface/public/shared/automation-context.js +75 -0
  896. package/neural-interface/public/shared/blob-store.js +99 -0
  897. package/neural-interface/public/shared/cdx/cdx-capabilities.js +118 -0
  898. package/neural-interface/public/shared/cdx/cdx-cards.js +37 -0
  899. package/neural-interface/public/shared/cdx/cdx-commands.js +171 -0
  900. package/neural-interface/public/shared/cdx/cdx-context-menu.js +402 -0
  901. package/neural-interface/public/shared/cdx/cdx-context-model.js +220 -0
  902. package/neural-interface/public/shared/cdx/cdx-features.js +408 -0
  903. package/neural-interface/public/shared/cdx/cdx-icons.js +89 -0
  904. package/neural-interface/public/shared/cdx/cdx-mcp-app-host.js +414 -0
  905. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.html +6 -0
  906. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.js +56 -0
  907. package/neural-interface/public/shared/cdx/cdx-mcp-app.js +178 -0
  908. package/neural-interface/public/shared/cdx/cdx-model-catalog.js +120 -0
  909. package/neural-interface/public/shared/cdx/cdx-output-model.js +261 -0
  910. package/neural-interface/public/shared/cdx/cdx-output.js +382 -0
  911. package/neural-interface/public/shared/cdx/cdx-panel.js +1372 -0
  912. package/neural-interface/public/shared/cdx/cdx-plan.js +266 -0
  913. package/neural-interface/public/shared/cdx/cdx-protocol.js +682 -0
  914. package/neural-interface/public/shared/cdx/cdx-render.js +3381 -0
  915. package/neural-interface/public/shared/cdx/cdx-requests.js +1136 -0
  916. package/neural-interface/public/shared/cdx/cdx-styles.js +3251 -0
  917. package/neural-interface/public/shared/cdx/cdx-tabs.js +7992 -0
  918. package/neural-interface/public/shared/cdx/cdx-ws.js +363 -0
  919. package/neural-interface/public/shared/cli-status.js +169 -0
  920. package/neural-interface/public/shared/command-runner-model.js +30 -0
  921. package/neural-interface/public/shared/constants.js +22 -5
  922. package/neural-interface/public/shared/cp/cp-agents.js +186 -0
  923. package/neural-interface/public/shared/cp/cp-bash.js +252 -0
  924. package/neural-interface/public/shared/cp/cp-compose.js +159 -0
  925. package/neural-interface/public/shared/cp/cp-context-menu.js +309 -0
  926. package/neural-interface/public/shared/cp/cp-context-model.js +157 -0
  927. package/neural-interface/public/shared/cp/cp-context-window.js +80 -0
  928. package/neural-interface/public/shared/cp/cp-ctx.js +11 -0
  929. package/neural-interface/public/shared/cp/cp-diff.js +255 -0
  930. package/neural-interface/public/shared/cp/cp-event-rows.js +637 -0
  931. package/neural-interface/public/shared/cp/cp-events.js +846 -0
  932. package/neural-interface/public/shared/cp/cp-markdown.js +104 -0
  933. package/neural-interface/public/shared/cp/cp-permission-model.js +905 -0
  934. package/neural-interface/public/shared/cp/cp-permissions.js +626 -0
  935. package/neural-interface/public/shared/cp/cp-rehydrate.js +113 -0
  936. package/neural-interface/public/shared/cp/cp-restore.js +224 -0
  937. package/neural-interface/public/shared/cp/cp-session-model.js +338 -0
  938. package/neural-interface/public/shared/cp/cp-session-settings.js +254 -0
  939. package/neural-interface/public/shared/cp/cp-sessions.js +227 -0
  940. package/neural-interface/public/shared/cp/cp-statusline.js +153 -0
  941. package/neural-interface/public/shared/cp/cp-styles.js +349 -0
  942. package/neural-interface/public/shared/cp/cp-tasks-model.js +282 -0
  943. package/neural-interface/public/shared/cp/cp-tasks.js +107 -0
  944. package/neural-interface/public/shared/cp/cp-temporary-view.js +56 -0
  945. package/neural-interface/public/shared/cp/cp-temporary.js +231 -0
  946. package/neural-interface/public/shared/cp/cp-tool-cards.js +326 -0
  947. package/neural-interface/public/shared/cp/cp-tool-results.js +618 -0
  948. package/neural-interface/public/shared/cp/cp-usage-model.js +124 -0
  949. package/neural-interface/public/shared/graph-activity.js +68 -0
  950. package/neural-interface/public/shared/html-shell.js +116 -17
  951. package/neural-interface/public/shared/i18n.js +132 -34
  952. package/neural-interface/public/shared/node-positions-store.js +44 -0
  953. package/neural-interface/public/shared/ocp-hidden-models.js +116 -0
  954. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +355 -0
  955. package/neural-interface/public/shared/ocp-v2/ocp-v2-autoaccept-button.js +35 -0
  956. package/neural-interface/public/shared/ocp-v2/ocp-v2-automation-ownership.js +72 -0
  957. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +280 -0
  958. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +67 -0
  959. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +179 -0
  960. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes.js +164 -0
  961. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +725 -0
  962. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +75 -0
  963. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +1204 -0
  964. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm-logic.js +117 -0
  965. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm.js +59 -0
  966. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-gauge.js +252 -0
  967. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +478 -0
  968. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +179 -0
  969. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +233 -0
  970. package/neural-interface/public/shared/ocp-v2/ocp-v2-icons.js +30 -0
  971. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +1065 -0
  972. package/neural-interface/public/shared/ocp-v2/ocp-v2-mention-hints.js +182 -0
  973. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +655 -0
  974. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +2636 -0
  975. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +601 -0
  976. package/neural-interface/public/shared/ocp-v2/ocp-v2-profile-inheritance.js +78 -0
  977. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +649 -0
  978. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +35 -0
  979. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +357 -0
  980. package/neural-interface/public/shared/ocp-v2/ocp-v2-render-logic.js +189 -0
  981. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1869 -0
  982. package/neural-interface/public/shared/ocp-v2/ocp-v2-select.js +42 -0
  983. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +117 -0
  984. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +1714 -0
  985. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +468 -0
  986. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-widgets.js +63 -0
  987. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +366 -0
  988. package/neural-interface/public/shared/ocp-v2/ocp-v2-settings-link.js +19 -0
  989. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +343 -0
  990. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +746 -0
  991. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +347 -0
  992. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +338 -0
  993. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +3543 -0
  994. package/neural-interface/public/shared/ocp-v2/ocp-v2-subagents-logic.js +482 -0
  995. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools-logic.js +351 -0
  996. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools.js +168 -0
  997. package/neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js +315 -0
  998. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +545 -0
  999. package/neural-interface/public/shared/provider-icons.js +30 -0
  1000. package/neural-interface/public/shared/pwa-install-page.js +34 -0
  1001. package/neural-interface/public/shared/pwa-install.css +12 -0
  1002. package/neural-interface/public/shared/pwa-install.js +28 -0
  1003. package/neural-interface/public/shared/session-title.js +100 -0
  1004. package/neural-interface/public/shared/settings/settings-ia.js +389 -0
  1005. package/neural-interface/public/shared/settings/settings-kit.js +234 -0
  1006. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  1007. package/neural-interface/public/shared/settings/settings-search.js +463 -0
  1008. package/neural-interface/public/shared/settings/settings-shell.js +570 -0
  1009. package/neural-interface/public/shared/start-bridge.js +583 -0
  1010. package/neural-interface/public/shared/state.js +5 -0
  1011. package/neural-interface/public/shared/storage.js +50 -7
  1012. package/neural-interface/public/shared/styleguide/sg-agents.js +42 -0
  1013. package/neural-interface/public/shared/styleguide/sg-brand.js +4 -0
  1014. package/neural-interface/public/shared/styleguide/sg-color-client.js +388 -0
  1015. package/neural-interface/public/shared/styleguide/sg-colors.js +151 -0
  1016. package/neural-interface/public/shared/styleguide/sg-components.js +35 -0
  1017. package/neural-interface/public/shared/styleguide/sg-history.js +41 -0
  1018. package/neural-interface/public/shared/styleguide/sg-icons-logo.js +38 -0
  1019. package/neural-interface/public/shared/styleguide/sg-imagery.js +4 -0
  1020. package/neural-interface/public/shared/styleguide/sg-import-export.js +48 -0
  1021. package/neural-interface/public/shared/styleguide/sg-kit.js +284 -0
  1022. package/neural-interface/public/shared/styleguide/sg-labels.js +318 -0
  1023. package/neural-interface/public/shared/styleguide/sg-layout.js +4 -0
  1024. package/neural-interface/public/shared/styleguide/sg-motion.js +5 -0
  1025. package/neural-interface/public/shared/styleguide/sg-panel.js +237 -0
  1026. package/neural-interface/public/shared/styleguide/sg-preview.js +138 -0
  1027. package/neural-interface/public/shared/styleguide/sg-rules.js +9 -0
  1028. package/neural-interface/public/shared/styleguide/sg-shape.js +24 -0
  1029. package/neural-interface/public/shared/styleguide/sg-state.js +132 -0
  1030. package/neural-interface/public/shared/styleguide/sg-typography.js +125 -0
  1031. package/neural-interface/public/shared/styles.css +8614 -1477
  1032. package/neural-interface/public/shared/synabun-mascot.js +1625 -0
  1033. package/neural-interface/public/shared/synabun-ticker.js +376 -0
  1034. package/neural-interface/public/shared/term-ready.js +307 -0
  1035. package/neural-interface/public/shared/term-renderer.js +343 -0
  1036. package/neural-interface/public/shared/ui-assistant-panel.js +10 -0
  1037. package/neural-interface/public/shared/ui-assistant.js +77 -0
  1038. package/neural-interface/public/shared/ui-automation-studio.js +5105 -3573
  1039. package/neural-interface/public/shared/ui-claude-panel.js +11685 -6333
  1040. package/neural-interface/public/shared/ui-codex-panel.js +12 -0
  1041. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  1042. package/neural-interface/public/shared/ui-cost-widget.js +55 -34
  1043. package/neural-interface/public/shared/ui-detail.js +77 -14
  1044. package/neural-interface/public/shared/ui-explorer.js +360 -181
  1045. package/neural-interface/public/shared/ui-file-explorer.js +485 -107
  1046. package/neural-interface/public/shared/ui-help.js +2 -2
  1047. package/neural-interface/public/shared/ui-image-gallery.js +45 -21
  1048. package/neural-interface/public/shared/ui-judgments.js +1346 -0
  1049. package/neural-interface/public/shared/ui-keybinds.js +3 -2
  1050. package/neural-interface/public/shared/ui-link.js +105 -51
  1051. package/neural-interface/public/shared/ui-loading.js +222 -11
  1052. package/neural-interface/public/shared/ui-memory-maintenance.js +111 -0
  1053. package/neural-interface/public/shared/ui-menubar.js +100 -31
  1054. package/neural-interface/public/shared/ui-native-loop-router-state.js +94 -0
  1055. package/neural-interface/public/shared/ui-native-loop-router.js +493 -0
  1056. package/neural-interface/public/shared/ui-native-window-id.js +7 -0
  1057. package/neural-interface/public/shared/ui-navbar.js +357 -222
  1058. package/neural-interface/public/shared/ui-notifications.js +58 -12
  1059. package/neural-interface/public/shared/ui-opencode-panel-v2.js +17 -0
  1060. package/neural-interface/public/shared/ui-panels.js +15 -7
  1061. package/neural-interface/public/shared/ui-resume.js +823 -653
  1062. package/neural-interface/public/shared/ui-schedule-queue.js +172 -0
  1063. package/neural-interface/public/shared/ui-schedules-studio.js +1882 -0
  1064. package/neural-interface/public/shared/ui-scroll-follow.js +194 -0
  1065. package/neural-interface/public/shared/ui-sessions.js +789 -34
  1066. package/neural-interface/public/shared/ui-settings.js +5774 -2194
  1067. package/neural-interface/public/shared/ui-sidepanel-layout.js +109 -0
  1068. package/neural-interface/public/shared/ui-sidepanel-runtime.js +19 -0
  1069. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +40 -0
  1070. package/neural-interface/public/shared/ui-sidepanel-sessions.js +221 -0
  1071. package/neural-interface/public/shared/ui-sidepanel-state.js +131 -0
  1072. package/neural-interface/public/shared/ui-sidepanel-tray.js +547 -0
  1073. package/neural-interface/public/shared/ui-sidepanel-windows.js +387 -0
  1074. package/neural-interface/public/shared/ui-skills.js +18 -32
  1075. package/neural-interface/public/shared/ui-styleguide.js +35 -0
  1076. package/neural-interface/public/shared/ui-sync.js +87 -0
  1077. package/neural-interface/public/shared/ui-terminal.js +7863 -6385
  1078. package/neural-interface/public/shared/ui-tool-updates.js +292 -0
  1079. package/neural-interface/public/shared/ui-tooltip.js +156 -15
  1080. package/neural-interface/public/shared/ui-tutorial-draw.js +119 -683
  1081. package/neural-interface/public/shared/ui-tutorial-layout.js +135 -0
  1082. package/neural-interface/public/shared/ui-tutorial-steps.js +59 -2209
  1083. package/neural-interface/public/shared/ui-tutorial.css +102 -0
  1084. package/neural-interface/public/shared/ui-tutorial.js +557 -1076
  1085. package/neural-interface/public/shared/ui-update.js +420 -68
  1086. package/neural-interface/public/shared/ui-whatsapp.js +941 -0
  1087. package/neural-interface/public/shared/ui-whiteboard.js +1076 -233
  1088. package/neural-interface/public/shared/ui-workspaces.js +2 -1
  1089. package/neural-interface/public/shared/utils.js +58 -0
  1090. package/neural-interface/public/shared/vterm-buffer.js +15 -5
  1091. package/neural-interface/public/shared/whatsapp/wa-phone.js +65 -0
  1092. package/neural-interface/public/shared/whatsapp/wa-view.js +411 -0
  1093. package/neural-interface/public/sidepanel-session.html +18 -0
  1094. package/neural-interface/public/sw.js +10 -2
  1095. package/neural-interface/public/synabun.png +0 -0
  1096. package/neural-interface/public/variant/2d/graph.js +1707 -1635
  1097. package/neural-interface/public/variant/2d/lasso.js +2 -1
  1098. package/neural-interface/public/variant/2d/layout.js +595 -594
  1099. package/neural-interface/public/variant/2d/main.js +557 -521
  1100. package/neural-interface/public/variant/2d/settings-gfx.js +18 -24
  1101. package/neural-interface/public/variant/3d/main.js +517 -500
  1102. package/neural-interface/public/variant/3d/map-controls.js +144 -0
  1103. package/neural-interface/public/variant/3d/map-data.js +251 -0
  1104. package/neural-interface/public/variant/3d/map-labels.js +241 -0
  1105. package/neural-interface/public/variant/3d/map-pick.js +90 -0
  1106. package/neural-interface/public/variant/3d/map-renderer.js +521 -0
  1107. package/neural-interface/public/variant/3d/map-shaders.js +158 -0
  1108. package/neural-interface/public/variant/3d/map-view.js +618 -0
  1109. package/neural-interface/public/vendor/three/manifest.json +3 -0
  1110. package/neural-interface/public/vendor/three/three-0.169.0.min.js +3844 -0
  1111. package/neural-interface/public/vendor/xterm/manifest.json +3 -0
  1112. package/neural-interface/public/vendor/xterm/xterm-6.0.0.css +285 -0
  1113. package/neural-interface/public/vendor/xterm/xterm-6.0.0.min.js +192 -0
  1114. package/neural-interface/scripts/build-vendor.mjs +64 -0
  1115. package/neural-interface/scripts/codex-protocol-fixture.mjs +83 -0
  1116. package/neural-interface/scripts/rebuild-pty.js +39 -22
  1117. package/neural-interface/scripts/vendor-three-entry.js +24 -0
  1118. package/neural-interface/scripts/vendor-xterm-entry.js +8 -0
  1119. package/neural-interface/server.js +33983 -15000
  1120. package/neural-interface/skins/default/skin.css +90 -0
  1121. package/neural-interface/skins/default/skin.json +8 -0
  1122. package/neural-interface/templates/rulesets/README.md +9 -0
  1123. package/neural-interface/templates/rulesets/claude.md +3 -0
  1124. package/neural-interface/templates/rulesets/codex.md +2 -0
  1125. package/neural-interface/templates/rulesets/coexistence.md +6 -0
  1126. package/neural-interface/templates/rulesets/core.md +26 -0
  1127. package/neural-interface/templates/rulesets/cursor.md +2 -0
  1128. package/neural-interface/templates/rulesets/gemini.md +2 -0
  1129. package/neural-interface/templates/rulesets/legacy-hashes.json +227 -0
  1130. package/neural-interface/templates/rulesets/manifest.json +12 -0
  1131. package/neural-interface/templates/rulesets/opencode.md +2 -0
  1132. package/package.json +131 -53
  1133. package/postinstall.js +55 -26
  1134. package/preuninstall.js +187 -0
  1135. package/scripts/restore-data-home-from-backup.mjs +239 -0
  1136. package/setup.js +872 -260
  1137. package/skills/leonardo/SKILL.md +19 -10
  1138. package/skills/leonardo/modules/image-prompter.md +18 -12
  1139. package/skills/leonardo/modules/video-prompter.md +19 -13
  1140. package/skills/synabun/SKILL.md +209 -185
  1141. package/skills/synabun/icon.png +0 -0
  1142. package/skills/synabun/modules/audit.md +380 -374
  1143. package/skills/synabun/modules/changelog.md +15 -6
  1144. package/skills/synabun/modules/idea.md +148 -144
  1145. package/skills/synabun/modules/memorize.md +13 -4
  1146. package/skills/synabun/modules/schedule.md +198 -0
  1147. package/skills/synabun/synabunicon.png +0 -0
  1148. package/updater.cmd +31 -0
  1149. package/updater.mjs +382 -0
  1150. package/updater.sh +29 -0
  1151. package/hooks/claude-code/test-user-learning.mjs +0 -531
  1152. package/neural-interface/public/claude-chat.css +0 -878
  1153. package/neural-interface/public/claude-chat.html +0 -92
  1154. package/neural-interface/public/claude-chat.js +0 -1168
  1155. package/neural-interface/public/index-legacy.html +0 -14677
  1156. package/neural-interface/public/index2d-legacy.html +0 -8193
  1157. package/neural-interface/public/shared/html-term-renderer.js +0 -1022
  1158. package/neural-interface/public/variant/3d/background.js +0 -884
  1159. package/neural-interface/public/variant/3d/camera.js +0 -661
  1160. package/neural-interface/public/variant/3d/gfx.js +0 -116
  1161. package/neural-interface/public/variant/3d/graph.js +0 -2399
  1162. package/neural-interface/public/variant/3d/settings-gfx.js +0 -228
  1163. package/neural-interface/templates/CLAUDE-template.md +0 -211
@@ -0,0 +1,1005 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Assistant REST API (/api/assistant/*)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // One router for everything the assistant UI and the MCP `agents` tool group
6
+ // need: catalog, dispatch + run control, waits, transcripts, assistant sessions,
7
+ // composer attachments, Claude/Codex account management, and the sidepanel tab registry.
8
+ // Mounted from server.js with `app.use('/api/assistant', createAssistantApi(deps))`.
9
+
10
+ import { Router } from 'express';
11
+ import { DispatchError } from './assistant-dispatch.js';
12
+ import { openServable } from './assistant-media.js';
13
+ import { designFit, targetMakes } from './assistant-router.js';
14
+ import { normalizeEffort } from './effort-levels.js';
15
+ import { BUDGET_DEFAULTS, BUDGET_FIELDS, BUDGET_MAX_USD, BUDGET_MIN_USD } from './assistant-budget.js';
16
+ import { tooLargeMessage } from './assistant-attachments.js';
17
+ import { getSessionPolicy, requiresHumanApproval } from './remote-policy.js';
18
+ import { clampPlanDispatch, PLAN_DISPATCH_NOTE, runCanChangeCode } from './assistant-plan-permissions.js';
19
+
20
+ /**
21
+ * How far each provider's spend can be held to a cap (shown in the Budget tab).
22
+ * A cap is checked where the provider lets it be checked; between two checks a
23
+ * run can go past it by that much.
24
+ */
25
+ export const BUDGET_ENFORCEMENT = Object.freeze({
26
+ 'claude-code': { metered: 'reported', perRun: 'The Claude CLI stops the run (--max-budget-usd); it checks between model calls, so one call can pass the cap.', session: 'Checked after every turn; at the hard cap the run is stopped mid-turn.' },
27
+ opencode: { metered: 'reported', perRun: 'OpenCode reports each step\'s cost; the run stops at the first step past its cap.', session: 'Checked at every step.' },
28
+ codex: { metered: 'estimated', perRun: 'Codex reports tokens only when a turn ends: priced at the models.dev list price (an API-price estimate; ChatGPT-plan accounts are billed by plan quota) and checked after each turn, so one turn can pass the cap. A failed turn is priced from its rollout.', session: 'Checked after every turn.' },
29
+ brain: { metered: 'reported', perRun: 'Checked before every brain turn; a Claude brain\'s CLI also stops at what is left of the brain cap. A Codex brain\'s tokens are priced at list price (estimated); a brain model with no price is refused.', session: 'The brain\'s spend counts toward the session hard cap.' },
30
+ });
31
+
32
+ const OWNER_ONLY = { error: 'The assistant is available to the owner only', code: 'GUEST_FORBIDDEN' };
33
+
34
+ function sendError(res, error, fallbackStatus = 500) {
35
+ if (error instanceof DispatchError || (error && error.code && error.status)) {
36
+ const status = Number(error.status) || fallbackStatus;
37
+ const body = typeof error.toJSON === 'function' ? error.toJSON() : { code: error.code, message: error.message };
38
+ return res.status(status).json({ ok: false, error: body.message, ...body });
39
+ }
40
+ return res.status(fallbackStatus).json({ ok: false, error: error?.message || String(error), code: error?.code || 'INTERNAL' });
41
+ }
42
+
43
+ function clampInt(value, min, max, fallback) {
44
+ const n = Number(value);
45
+ if (!Number.isFinite(n)) return fallback;
46
+ return Math.min(max, Math.max(min, Math.round(n)));
47
+ }
48
+
49
+ function clipSummary(value, max = 300) {
50
+ const text = String(value ?? '').replace(/\s+/g, ' ').trim();
51
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
52
+ }
53
+
54
+ // A WhatsApp conversation never holds a turn open for workers (docs/whatsapp.md): the wait returns
55
+ // after this long (the dispatcher's shortest wait) and says what to do instead.
56
+ export const CHAT_WAIT_MS = 1000;
57
+ export const CHAT_WAIT_NEXT = 'This conversation is on WhatsApp: you do not wait for workers here. End your turn now with one short line that says what is running. Each result reaches you as a [SynaBun Mailbox] event, and you tell the user then. Do not call agent_wait again for these runs.';
58
+ export const CHAT_CLARIFY_NEXT = 'This conversation is on WhatsApp, where a question is asked in plain text, not on a card: nothing was shown to the user. Ask it in your reply (one short question; name the options inside the sentence only when they help) and end your turn. The user\'s next message is the answer. Work that does not depend on the answer may go ahead.';
59
+
60
+ /**
61
+ * A run view for a brain (agent_wait / agent_status): the worker's full text
62
+ * is dropped (`lastText`, `lastResult.raw`, `turns[].text`, `turns[].result.raw`)
63
+ * and each turn keeps its status and a summary of at most 300 characters.
64
+ * A 4.2k-character worker message used to make a 26.6k-character view, which
65
+ * the MCP client truncated before the brain reached `lastResult`.
66
+ * `agent_read format:"text"` still returns the full text.
67
+ */
68
+ export function slimRunView(run) {
69
+ if (!run || typeof run !== 'object') return run;
70
+ const { lastText: _lastText, ...out } = run;
71
+ if (run.lastResult && typeof run.lastResult === 'object') {
72
+ const { raw: _raw, ...result } = run.lastResult;
73
+ out.lastResult = result;
74
+ }
75
+ if (Array.isArray(run.turns)) {
76
+ out.turns = run.turns.map((turn) => {
77
+ if (!turn || typeof turn !== 'object') return turn;
78
+ const { text: _text, result, ...rest } = turn;
79
+ return { ...rest, status: result?.status || null, summary: clipSummary(result?.summary) };
80
+ });
81
+ }
82
+ return out;
83
+ }
84
+
85
+ /**
86
+ * @param {object} deps
87
+ * @param {object} deps.dispatcher createAssistantDispatcher()
88
+ * @param {object} [deps.runtime] createAssistantRuntime() (sessions; optional until wired)
89
+ * @param {object} [deps.claudeAccounts] createClaudeAccounts()
90
+ * @param {object} [deps.codexAccounts] { listForClient(), rename(id,label), remove(id, {inUse}), addStart() }
91
+ * @param {Function} [deps.buildCatalog] async () => catalog object (models/accounts/projects/profiles)
92
+ * @param {Function} [deps.createLoginTerminal] async ({ env, cwd, profile, accountId }) => { terminalSessionId, profile }
93
+ * @param {object} [deps.attachments] createAttachmentStore() (POST /attachments)
94
+ */
95
+ export function createAssistantApi({
96
+ dispatcher,
97
+ runtime = null,
98
+ usage = null,
99
+ claudeAccounts = null,
100
+ codexAccounts = null,
101
+ buildCatalog = null,
102
+ createLoginTerminal = null,
103
+ broadcastSync = () => {},
104
+ isGuestRequest = () => false,
105
+ log = () => {},
106
+ tabTtlMs = 90_000,
107
+ now = Date.now,
108
+ // Routing: assistant-router.js service, assistant-catalog.js, assistant-config.js store.
109
+ assistantRouter = null,
110
+ catalog = null,
111
+ configStore = null,
112
+ taskClasses = null,
113
+ // Codex list prices (assistant-budget.js createModelPricing): its status shows in the Budget tab.
114
+ pricing = null,
115
+ // Clarification (assistant-clarify.js): agent_clarify, clarify-card answers, briefs.
116
+ clarifier = null,
117
+ // Composer uploads (assistant-attachments.js createAttachmentStore).
118
+ attachments = null,
119
+ // Remote sessions (lib/remote-policy.js): the session's policy, or null. Defaults
120
+ // to the runtime's view, else the process-wide registry.
121
+ sessionPolicy = null,
122
+ } = {}) {
123
+ if (!dispatcher) throw new Error('createAssistantApi requires a dispatcher');
124
+ const router = Router();
125
+ const uiTabs = new Map(); // windowId → { panels, at }
126
+ let catalogCache = null;
127
+
128
+ router.use((req, res, next) => {
129
+ if (isGuestRequest(req)) return res.status(403).json(OWNER_ONLY);
130
+ next();
131
+ });
132
+
133
+ // Resolve who is calling: the UI (no pin), an assistant brain (assistant-<uuid>
134
+ // pin), or a dispatched worker (run id pin → forbidden for orchestration).
135
+ function callerContext(req) {
136
+ const pin = String(req.get('x-synabun-terminal') || req.get('x-synabun-assistant') || '').trim();
137
+ const bodySession = req.body?.assistantSessionId || req.body?.assistant_session_id || req.query?.assistantSessionId || null;
138
+ if (!pin) return { kind: 'ui', assistantSessionId: bodySession ? String(bodySession) : null };
139
+ if (dispatcher.get(pin)) return { kind: 'worker', forbidden: true, pin };
140
+ // A brain's pin resolves to its session (Claude pins the session id; the
141
+ // OpenCode brain pins assistant-oc-<24>, which must never be taken as an id).
142
+ const resolved = runtime?.resolveTerminal?.(pin) || null;
143
+ if (resolved) return { kind: 'assistant', assistantSessionId: resolved, pin };
144
+ if (/^assistant-oc-/.test(pin)) return { kind: 'assistant', assistantSessionId: bodySession ? String(bodySession) : null, pin };
145
+ if (/^assistant-/.test(pin)) return { kind: 'assistant', assistantSessionId: pin, pin };
146
+ return { kind: 'unknown', assistantSessionId: bodySession ? String(bodySession) : null, pin };
147
+ }
148
+ /**
149
+ * The plan mode of the brain making this call: 'read-only' (a remote session
150
+ * at the read-only level: no worker at all), 'plan' (no code changes: its
151
+ * workers run read-only), or null (not planning, or not a brain: the UI, the
152
+ * user, is never limited).
153
+ */
154
+ function planState(req, assistantSessionId) {
155
+ if (!assistantSessionId || !brainCaller(req)) return null;
156
+ try {
157
+ if (runtime?.isPlanning?.(String(assistantSessionId)) !== true) return null;
158
+ return runtime?.isPlanReadOnly?.(String(assistantSessionId)) === true ? 'read-only' : 'plan';
159
+ } catch { return null; }
160
+ }
161
+ /** A read-only plan (a remote session's read-only level) starts and steers no worker: 409 PLAN_MODE_READ_ONLY. */
162
+ function planRefusal(req, res, assistantSessionId, what) {
163
+ if (planState(req, assistantSessionId) !== 'read-only') return false;
164
+ res.status(409).json({ ok: false, code: 'PLAN_MODE_READ_ONLY', error: `Plan mode is read-only, so SynaBun did not ${what}. Keep exploring with read-only tools and put the delegation in the plan; it runs after the user approves the plan.` });
165
+ return true;
166
+ }
167
+ /** A planning brain steers only workers that cannot change code: 409 PLAN_MODE_CODE_CHANGE for a workspace / full run. */
168
+ function planSendRefusal(res, run) {
169
+ if (!run || !runCanChangeCode(run)) return false;
170
+ res.status(409).json({ ok: false, code: 'PLAN_MODE_CODE_CHANGE', error: `Plan mode blocks code changes, so SynaBun did not send run ${run.runId} a message: it runs with capability "${run.capability || 'full'}" and can change code. Put that step in the plan; it runs after the user approves the plan. To explore meanwhile, dispatch a worker (it runs read-only while you plan).` });
171
+ return true;
172
+ }
173
+ /** Milliseconds left before the MCP caller gives up (X-Synabun-Deadline), or null. */
174
+ function callerBudgetMs(req) {
175
+ const deadline = Number(req.get('x-synabun-deadline'));
176
+ return Number.isFinite(deadline) && deadline > 0 ? deadline - now() : null;
177
+ }
178
+ function orchestrationGuard(req, res, next) {
179
+ const caller = callerContext(req);
180
+ if (caller.forbidden) return res.status(403).json({ ok: false, code: 'AGENTS_NOT_PERMITTED', error: 'Dispatched workers cannot orchestrate other agents.' });
181
+ req.assistantCaller = caller;
182
+ next();
183
+ }
184
+ /**
185
+ * The caller when it is an assistant brain, else null. Brains pin their
186
+ * session (Claude) or assistant-oc-<24> (OpenCode); the Codex brain's MCP
187
+ * child pins codex-sp-<uuid>, which no session resolves, but it sends
188
+ * X-Synabun-Role: assistant. The UI, workers and other sessions never count.
189
+ */
190
+ function brainCaller(req) {
191
+ const caller = req.assistantCaller || callerContext(req);
192
+ if (caller.kind === 'assistant') return caller;
193
+ if (caller.kind === 'unknown' && String(req.get('x-synabun-role') || '').trim().toLowerCase() === 'assistant') return caller;
194
+ return null;
195
+ }
196
+ /** A remote session whose workers only a person may answer (strict worker approvals below autonomous). */
197
+ function humanApprovalRequired(assistantSessionId) {
198
+ if (!assistantSessionId) return false;
199
+ let policy = null;
200
+ try {
201
+ policy = typeof sessionPolicy === 'function' ? sessionPolicy(String(assistantSessionId))
202
+ : (runtime?.sessionPolicy ? runtime.sessionPolicy(String(assistantSessionId)) : getSessionPolicy(String(assistantSessionId)));
203
+ } catch { policy = null; }
204
+ return requiresHumanApproval(policy || null);
205
+ }
206
+ /**
207
+ * A brain read these runs' results: once the response is out, record the
208
+ * turn it saw, so the mailbox does not hand it the same result again.
209
+ */
210
+ function noteBrainRead(req, res, runs, via) {
211
+ if (typeof dispatcher.markDelivered !== 'function') return;
212
+ const caller = brainCaller(req);
213
+ if (!caller) return;
214
+ const marks = (Array.isArray(runs) ? runs : [runs])
215
+ .filter((run) => run && run.runId)
216
+ .map((run) => ({ runId: run.runId, turn: Number(run.resultTurn) || 0, state: run.state || null }));
217
+ if (!marks.length) return;
218
+ res.on('finish', () => {
219
+ for (const mark of marks) {
220
+ try { dispatcher.markDelivered(mark.runId, { turn: mark.turn, state: mark.state, via, assistantSessionId: caller.assistantSessionId || null }); } catch {}
221
+ }
222
+ });
223
+ }
224
+ /** Where a session's conversation happens: 'whatsapp' (WhatsApp Link), or null (the panel). */
225
+ function channelOf(assistantSessionId) {
226
+ if (!assistantSessionId) return null;
227
+ const id = String(assistantSessionId);
228
+ try { const own = runtime?.sessionChannel?.(id); if (own) return own; } catch {}
229
+ try {
230
+ const policy = typeof sessionPolicy === 'function' ? sessionPolicy(id) : (runtime?.sessionPolicy ? runtime.sessionPolicy(id) : getSessionPolicy(id));
231
+ return policy?.channel || null;
232
+ } catch { return null; }
233
+ }
234
+ /** The assistant session a wait is for: the calling brain's, else the one that owns the runs. */
235
+ function waitSession(req, { runId = null, runIds = null, workflowId = null } = {}) {
236
+ const own = req.assistantCaller?.assistantSessionId || null;
237
+ if (own) return own;
238
+ try {
239
+ const first = runId || (Array.isArray(runIds) ? runIds[0] : null);
240
+ if (first) return dispatcher.get(first)?.assistantSessionId || null;
241
+ if (workflowId) return dispatcher.list({ workflowId, limit: 1 })?.[0]?.assistantSessionId || null;
242
+ } catch {}
243
+ return null;
244
+ }
245
+ /**
246
+ * A brain talking to its user on WhatsApp never sits in agent_wait: the wait
247
+ * returns at once (what is done already is still reported) and tells the
248
+ * brain to end its turn, because the result arrives as a mailbox event. The
249
+ * user keeps chatting meanwhile. Desktop sessions wait as asked.
250
+ */
251
+ function chatWait(req, target) {
252
+ return !!brainCaller(req) && channelOf(waitSession(req, target)) === 'whatsapp';
253
+ }
254
+ function chatWaitView(result) {
255
+ if (!result || typeof result !== 'object' || !Array.isArray(result.pending) || !result.pending.length) return result;
256
+ return { ...result, channel: 'whatsapp', next: CHAT_WAIT_NEXT };
257
+ }
258
+ /** Wait/status payloads for a brain carry slim run views (see slimRunView). */
259
+ function brainWaitView(result) {
260
+ if (!result || typeof result !== 'object') return result;
261
+ return {
262
+ ...result,
263
+ done: Array.isArray(result.done) ? result.done.map(slimRunView) : result.done,
264
+ pending: Array.isArray(result.pending) ? result.pending.map(slimRunView) : result.pending,
265
+ };
266
+ }
267
+
268
+ // ── catalog ────────────────────────────────────────────────────────────────
269
+ router.get('/catalog', async (req, res) => {
270
+ try {
271
+ const force = req.query.refresh === '1';
272
+ // Brain view / search: compact rows (OpenCode can list hundreds of models).
273
+ const view = req.query.view === 'brain' ? 'brain' : req.query.view === 'manage' ? 'manage' : 'full';
274
+ const provider = req.query.provider ? String(req.query.provider) : null;
275
+ const q = req.query.q || req.query.search ? String(req.query.q || req.query.search) : null;
276
+ // The Models manager: every row with hidden: true|false.
277
+ if (view === 'manage' && catalog?.get) return res.json({ ok: true, ...(await catalog.get({ force, view: 'manage', provider })) });
278
+ if (catalog?.get && (view === 'brain' || provider || q)) {
279
+ let preferences = {};
280
+ try { preferences = configStore?.routing?.().preferences || {}; } catch {}
281
+ const brief = await catalog.get({ force, view: 'brain', provider, q, preferences });
282
+ return res.json({ ok: true, generatedAt: new Date(now()).toISOString(), ...brief, limits: dispatcher.limits, runs: dispatcher.list({ activeOnly: true }), totals: dispatcher.totals() });
283
+ }
284
+ // Reuse only while the catalog underneath is the same build (invalidate() drops it).
285
+ if (!force && catalogCache && now() - catalogCache.at < 30_000 && (!catalog?.peek || catalog.peek() === catalogCache.base)) return res.json(catalogCache.value);
286
+ const base = typeof buildCatalog === 'function' ? await buildCatalog({ force }) : {};
287
+ // Hidden rows stay server-side (view=manage lists them); the lists themselves ship.
288
+ const { hiddenRows: _hiddenRows, ...visible } = base || {};
289
+ const value = {
290
+ ok: true,
291
+ generatedAt: new Date(now()).toISOString(),
292
+ ...visible,
293
+ accounts: {
294
+ ...(base.accounts || {}),
295
+ 'claude-code': claudeAccounts ? claudeAccounts.listForClient() : (base.accounts?.['claude-code'] || []),
296
+ codex: codexAccounts ? codexAccounts.listForClient() : (base.accounts?.codex || []),
297
+ },
298
+ limits: dispatcher.limits,
299
+ runs: dispatcher.list({ activeOnly: true }),
300
+ totals: dispatcher.totals(),
301
+ sidepanelTabs: listTabs(),
302
+ assistantSessions: runtime ? runtime.listSessions() : [],
303
+ };
304
+ catalogCache = { at: now(), value, base };
305
+ res.json(value);
306
+ } catch (error) { sendError(res, error); }
307
+ });
308
+
309
+ // ── dispatch + runs ────────────────────────────────────────────────────────
310
+ router.post('/dispatch', orchestrationGuard, async (req, res) => {
311
+ try {
312
+ const body = req.body || {};
313
+ const spec = {
314
+ ...body,
315
+ accountId: body.accountId || body.account_id || body.codexAccountId || body.claudeAccountId || null,
316
+ mcpProfile: body.mcpProfile || body.mcp_profile,
317
+ permissionPolicy: body.permissionPolicy || body.permission_policy,
318
+ maxMinutes: body.maxMinutes ?? body.max_minutes,
319
+ budgetUsd: body.budgetUsd ?? body.budget_usd,
320
+ usesBrowser: body.usesBrowser ?? body.uses_browser,
321
+ workflowId: body.workflowId || body.workflow_id,
322
+ parentRunId: body.parentRunId || body.parent_run_id,
323
+ outputSchema: body.outputSchema || body.output_schema,
324
+ idempotencyKey: body.idempotencyKey || body.idempotency_key,
325
+ queueIfBusy: body.queueIfBusy ?? body.queue_if_busy,
326
+ idleTimeoutMs: body.idleTimeoutMs ?? (body.idle_timeout_sec ? Number(body.idle_timeout_sec) * 1000 : undefined),
327
+ maxTurns: body.maxTurns ?? body.max_turns,
328
+ routeId: body.routeId || body.route_id || null,
329
+ taskClass: body.taskClass || body.task_class || null,
330
+ confidence: body.confidence ?? null,
331
+ usesComputer: body.usesComputer ?? body.uses_computer,
332
+ agent: body.agent || null,
333
+ // The user's brief after agent_clarify (the server attaches it; a body never carries one).
334
+ briefId: body.briefId || body.brief_id || null,
335
+ independent: body.independent === true || body.independent === 'true',
336
+ };
337
+ delete spec.brief;
338
+ const assistantSessionId = req.assistantCaller.assistantSessionId || null;
339
+ if (planRefusal(req, res, assistantSessionId, 'dispatch a worker')) return;
340
+ // A planning brain's worker runs read-only (it cannot change code); the run's notes and the answer say so.
341
+ const planning = planState(req, assistantSessionId) === 'plan';
342
+ const notes = [];
343
+ const result = planning
344
+ ? await dispatcher.dispatch(clampPlanDispatch(spec, { notes }), { assistantSessionId, origin: req.assistantCaller.kind, notes })
345
+ : await dispatcher.dispatch(spec, { assistantSessionId, origin: req.assistantCaller.kind });
346
+ if (assistantSessionId && runtime?.noteDispatch) { try { runtime.noteDispatch(assistantSessionId, result.run.runId); } catch {} }
347
+ res.status(result.queued ? 202 : 200).json(planning ? { ...result, planMode: { capability: 'read-only', note: PLAN_DISPATCH_NOTE } } : result);
348
+ } catch (error) { sendError(res, error); }
349
+ });
350
+
351
+ // ── routing ────────────────────────────────────────────────────────────────
352
+ function requireRouter(res) {
353
+ if (!assistantRouter) { res.status(503).json({ ok: false, code: 'ROUTING_UNAVAILABLE', error: 'Routing is not available' }); return false; }
354
+ return true;
355
+ }
356
+ function routingPayload() {
357
+ const routing = configStore?.routing?.() || assistantRouter?.routing?.() || {};
358
+ return {
359
+ ok: true, version: configStore?.version?.() ?? 0, routing,
360
+ // requires: the medium the model must generate (image / video creation); vision (only when true): only models
361
+ // that can see (design); dispatchOnly: always a worker.
362
+ taskClasses: Object.entries(taskClasses || {}).map(([id, meta]) => ({ id, label: meta.label, description: meta.description, defaultKind: meta.defaultKind, requires: meta.requires || null, ...(meta.vision === true ? { vision: true } : {}), dispatchOnly: meta.dispatchOnly === true })),
363
+ modes: ['always-ask', 'ask-unsure', 'never'],
364
+ };
365
+ }
366
+ function routingInvalid(field, message) {
367
+ const error = new Error(message);
368
+ error.code = 'ROUTING_INVALID';
369
+ error.field = field;
370
+ error.status = 400;
371
+ return error;
372
+ }
373
+ /** The same saved route (kind, provider, model, effort), whatever its label or bookkeeping. */
374
+ function samePreference(a, b) {
375
+ if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false;
376
+ return (a.kind || 'dispatch') === (b.kind || 'dispatch') && String(a.provider || '') === String(b.provider || '')
377
+ && String(a.model || '') === String(b.model || '') && String(a.effort || '') === String(b.effort || '');
378
+ }
379
+ /**
380
+ * PUT /routing, classes that need sight (design): a new or changed route must be a worker on a
381
+ * model confirmed to see and available (the router's designFit). It fails closed: without a
382
+ * catalog value the route is refused. Clearing the row is always allowed, and a route the user
383
+ * already has is not checked again. Image / video routes keep their own check below.
384
+ */
385
+ async function checkSightPreferences(preferences) {
386
+ const saved = configStore?.routing?.()?.preferences || {};
387
+ let cat;
388
+ for (const [key, pref] of Object.entries(preferences)) {
389
+ const meta = taskClasses?.[key] || null;
390
+ if (meta?.vision !== true || !pref || typeof pref !== 'object' || samePreference(pref, saved[key])) continue;
391
+ if (pref.kind === 'direct') throw routingInvalid(`preferences.${key}`, `${meta.label} always runs on a worker: pick a model, not "here".`);
392
+ if (!pref.provider) continue; // patchRouting refuses it with the precise field
393
+ const name = pref.label || pref.model || `the ${pref.provider} default model`;
394
+ if (cat === undefined) {
395
+ cat = null;
396
+ if (catalog?.full) { try { cat = await catalog.full(); } catch { cat = catalog.peek?.() || null; } }
397
+ }
398
+ if (!cat) throw routingInvalid(`preferences.${key}`, `Can't confirm that ${name} can see images right now: the model catalog is unavailable. Try again shortly, or clear the ${meta.label} row.`);
399
+ const fit = designFit(cat, { ...pref, kind: 'dispatch' });
400
+ if (fit !== 'ok') {
401
+ throw routingInvalid(`preferences.${key}`, `${name} ${fit === 'blind' ? "can't see images" : fit === 'unavailable' ? "isn't available right now" : "isn't known to see images"}; ${meta.label} needs a model that can.`);
402
+ }
403
+ }
404
+ }
405
+ router.get('/routing', (req, res) => {
406
+ if (!requireRouter(res)) return;
407
+ try { res.json(routingPayload()); } catch (error) { sendError(res, error); }
408
+ });
409
+ router.put('/routing', async (req, res) => {
410
+ if (!requireRouter(res)) return;
411
+ if (!configStore?.patchRouting) return res.status(503).json({ ok: false, code: 'CONFIG_UNAVAILABLE', error: 'Routing settings are read-only' });
412
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Routing settings are changed from the assistant panel only.' });
413
+ try {
414
+ const body = req.body || {};
415
+ const patch = body.routing && typeof body.routing === 'object' ? body.routing : body;
416
+ const { version: _v, ...clean } = patch;
417
+ const expectedVersion = body.version ?? patch.version ?? null;
418
+ // Each preference's effort is checked against its model; corrections are reported.
419
+ // An image / video creation route must be a worker on a model that makes the medium;
420
+ // a design route is checked first, on its own (checkSightPreferences).
421
+ const corrections = [];
422
+ if (clean.preferences && typeof clean.preferences === 'object') await checkSightPreferences(clean.preferences);
423
+ if (clean.preferences && typeof clean.preferences === 'object' && catalog?.full) {
424
+ let cat = null;
425
+ try { cat = await catalog.full(); } catch { cat = catalog.peek?.() || null; }
426
+ const preferences = {};
427
+ for (const [key, raw] of Object.entries(clean.preferences)) {
428
+ let pref = raw;
429
+ const meta = taskClasses?.[key] || null;
430
+ if (pref && typeof pref === 'object' && meta?.requires) {
431
+ if (meta.dispatchOnly) pref = { ...pref, kind: 'dispatch' };
432
+ if (cat && pref.provider && !targetMakes(cat, pref, meta.requires)) {
433
+ throw routingInvalid(`preferences.${key}`, `${pref.label || pref.model || `the ${pref.provider} default model`} can't generate ${meta.requires === 'video' ? 'videos' : 'images'}; ${meta.label} needs a model that does.`);
434
+ }
435
+ }
436
+ if (!pref || typeof pref !== 'object' || !pref.effort) { preferences[key] = pref; continue; }
437
+ const { effort, corrected } = normalizeEffort({ provider: pref.provider, model: pref.model || null, effort: pref.effort, catalog: cat });
438
+ if (corrected) corrections.push({ key, field: 'effort', from: corrected.from, to: corrected.to, reason: 'effort_unsupported' });
439
+ preferences[key] = { ...pref, effort };
440
+ }
441
+ clean.preferences = preferences;
442
+ }
443
+ configStore.patchRouting(clean, { expectedVersion, taskClasses: [...Object.keys(taskClasses || {}), 'vision'] });
444
+ broadcastSync({ type: 'assistant:routing-changed', version: configStore.version() });
445
+ res.json({ ...routingPayload(), corrections });
446
+ } catch (error) {
447
+ if (error?.code === 'ROUTING_INVALID') return res.status(400).json({ ok: false, code: error.code, error: error.message, message: error.message, field: error.field || null });
448
+ sendError(res, error, 400);
449
+ }
450
+ });
451
+ router.delete('/routing/preferences/:key', (req, res) => {
452
+ if (!requireRouter(res)) return;
453
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Routing settings are changed from the assistant panel only.' });
454
+ try {
455
+ configStore.patchRouting({ preferences: { [req.params.key]: null } }, { taskClasses: [...Object.keys(taskClasses || {}), 'vision'] });
456
+ broadcastSync({ type: 'assistant:routing-changed', version: configStore.version() });
457
+ res.json(routingPayload());
458
+ } catch (error) { sendError(res, error, 400); }
459
+ });
460
+ // ── budget ─────────────────────────────────────────────────────────────────
461
+ function budgetPayload(sessionId = null) {
462
+ const current = configStore.budget();
463
+ const budget = Object.fromEntries(BUDGET_FIELDS.map((field) => [field, current[field]]));
464
+ let session = null;
465
+ if (sessionId) { try { session = dispatcher.sessionBudget?.(String(sessionId)) || null; } catch { session = null; } }
466
+ let pricingStatus = null;
467
+ try { pricingStatus = pricing?.status?.() || null; } catch { pricingStatus = null; }
468
+ return {
469
+ ok: true, version: current.version, budget, defaults: { ...BUDGET_DEFAULTS }, sources: current.sources, repairs: current.repairs,
470
+ bounds: { minUsd: BUDGET_MIN_USD, maxUsd: BUDGET_MAX_USD }, session, pricing: pricingStatus, enforcement: BUDGET_ENFORCEMENT,
471
+ };
472
+ }
473
+ router.get('/budget', (req, res) => {
474
+ if (!configStore?.budget) return res.status(503).json({ ok: false, code: 'CONFIG_UNAVAILABLE', error: 'Budget settings are not available' });
475
+ try { res.json(budgetPayload(req.query.sessionId || null)); } catch (error) { sendError(res, error); }
476
+ });
477
+ router.put('/budget', (req, res) => {
478
+ if (!configStore?.patchBudget) return res.status(503).json({ ok: false, code: 'CONFIG_UNAVAILABLE', error: 'Budget settings are read-only' });
479
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Budget settings are changed from the assistant panel only.' });
480
+ try {
481
+ const body = req.body || {};
482
+ const patch = body.budget && typeof body.budget === 'object' ? body.budget : {};
483
+ const expectedVersion = body.version ?? null;
484
+ const saved = configStore.patchBudget(patch, { expectedVersion });
485
+ // Applies now: the dispatcher re-reads its limits, the runtime re-checks every session.
486
+ try { dispatcher.refreshLimits?.(); } catch {}
487
+ try { runtime?.onBudgetChanged?.(); } catch {}
488
+ broadcastSync({ type: 'assistant:budget-changed', version: saved.version });
489
+ res.json(budgetPayload(body.sessionId || null));
490
+ } catch (error) {
491
+ if (error?.code === 'BUDGET_INVALID' || error?.code === 'VERSION_CONFLICT') {
492
+ return res.status(error.status || 400).json({ ok: false, code: error.code, error: error.message, message: error.message, field: error.field || null, version: error.version ?? null });
493
+ }
494
+ sendError(res, error, 400);
495
+ }
496
+ });
497
+
498
+ // The route gate's answer for one tool call, asked by the assistant brains'
499
+ // OpenCode plugin and Codex hook (each holds a per-session token).
500
+ router.post('/route-gate/check', (req, res) => {
501
+ if (!runtime?.gateCheck) return res.json({ allow: true });
502
+ const body = req.body || {};
503
+ const input = body.input && typeof body.input === 'object' && !Array.isArray(body.input) ? body.input : {};
504
+ // inputComplete: the hook sent the call's whole arguments (a remote session refuses a call checked without them).
505
+ try { res.json(runtime.gateCheck({ session: body.session, token: body.token, tool: body.tool, input, inputComplete: body.inputComplete === true, providerSessionId: body.providerSessionId || null, agent: body.agent || null, hello: body.hello === true, host: typeof body.host === 'string' ? body.host : null })); }
506
+ catch (error) { sendError(res, error); }
507
+ });
508
+ // agent_route: the brain proposes; the router approves, asks, or says pending.
509
+ router.post('/route', orchestrationGuard, async (req, res) => {
510
+ if (!requireRouter(res)) return;
511
+ try {
512
+ const body = req.body || {};
513
+ const sessionId = req.assistantCaller.assistantSessionId || body.assistantSessionId || body.assistant_session_id || null;
514
+ if (!sessionId) return res.status(400).json({ ok: false, code: 'SESSION_REQUIRED', error: 'assistant_session_id is required' });
515
+ const budget = callerBudgetMs(req);
516
+ const waitMs = budget === null ? null : Math.max(0, budget - 5000);
517
+ const controller = new AbortController();
518
+ req.on('close', () => { if (!res.writableEnded) controller.abort(); });
519
+ const result = await assistantRouter.propose({ sessionId: String(sessionId), body, origin: 'agent_route', waitMs, signal: controller.signal });
520
+ res.status(result.status === 'pending' ? 202 : 200).json(result);
521
+ } catch (error) { sendError(res, error); }
522
+ });
523
+ // agent_clarify: the brain's 1-3 questions go to the user on a card; the
524
+ // answers (or "pending") come back, and dispatches carry the brief.
525
+ function requireClarifier(res) {
526
+ if (!clarifier) { res.status(503).json({ ok: false, code: 'CLARIFY_UNAVAILABLE', error: 'Clarification is not available' }); return false; }
527
+ return true;
528
+ }
529
+ router.post('/clarify', orchestrationGuard, async (req, res) => {
530
+ if (!requireClarifier(res)) return;
531
+ try {
532
+ const body = req.body || {};
533
+ const sessionId = req.assistantCaller.assistantSessionId || body.assistantSessionId || body.assistant_session_id || null;
534
+ if (!sessionId) return res.status(400).json({ ok: false, code: 'SESSION_REQUIRED', error: 'assistant_session_id is required' });
535
+ // On WhatsApp a question is asked in the chat itself: no card is raised, so nothing waits behind one.
536
+ if (channelOf(sessionId) === 'whatsapp') return res.json({ ok: false, status: 'text_only', code: 'CLARIFY_TEXT_ONLY', next: CHAT_CLARIFY_NEXT });
537
+ const budget = callerBudgetMs(req);
538
+ const waitMs = budget === null ? null : Math.max(0, budget - 5000);
539
+ const controller = new AbortController();
540
+ req.on('close', () => { if (!res.writableEnded) controller.abort(); });
541
+ const result = await clarifier.ask({ sessionId: String(sessionId), body, waitMs, signal: controller.signal });
542
+ res.status(result.status === 'pending' ? 202 : 200).json(result);
543
+ } catch (error) { sendError(res, error); }
544
+ });
545
+ // A card answer as it came over HTTP, without `superseded`: that marker ("the user wrote something
546
+ // else": a silent cancel) is the WhatsApp bridge's alone and is never taken from a request.
547
+ function cardAnswer(req) {
548
+ const raw = req.body?.response || req.body || {};
549
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return raw;
550
+ const { superseded: _dropped, ...response } = raw;
551
+ return response;
552
+ }
553
+ router.post('/clarify/:requestId/answer', (req, res) => {
554
+ if (!requireClarifier(res)) return;
555
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Clarify cards are answered by the user.' });
556
+ try { res.json(clarifier.answer(req.params.requestId, cardAnswer(req), { origin: 'rest' })); } catch (error) { sendError(res, error); }
557
+ });
558
+ router.get('/briefs/:briefId', (req, res) => {
559
+ if (!requireClarifier(res)) return;
560
+ const brief = clarifier.get(req.params.briefId);
561
+ if (!brief) return res.status(404).json({ ok: false, code: 'BRIEF_NOT_FOUND', error: `Unknown brief ${req.params.briefId}` });
562
+ res.json({ ok: true, brief });
563
+ });
564
+ router.get('/sessions/:id/clarify', (req, res) => {
565
+ if (!requireClarifier(res)) return;
566
+ res.json({ ok: true, cards: clarifier.pendingCards(req.params.id) });
567
+ });
568
+
569
+ router.get('/routes/:routeId', (req, res) => {
570
+ if (!requireRouter(res)) return;
571
+ const status = assistantRouter.status(req.params.routeId);
572
+ if (!status) return res.status(404).json({ ok: false, code: 'ROUTE_NOT_FOUND', error: `Unknown route ${req.params.routeId}` });
573
+ res.json({ ok: true, route: status });
574
+ });
575
+ router.post('/routes/:routeId/answer', async (req, res) => {
576
+ if (!requireRouter(res)) return;
577
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Route cards are answered by the user.' });
578
+ try { res.json(await assistantRouter.answer(req.params.routeId, cardAnswer(req), { origin: 'rest' })); } catch (error) { sendError(res, error); }
579
+ });
580
+ router.get('/sessions/:id/routes', (req, res) => {
581
+ if (!requireRouter(res)) return;
582
+ res.json({ ok: true, cards: assistantRouter.pendingCards(req.params.id) });
583
+ });
584
+ router.post('/runs/:runId/escalate', orchestrationGuard, async (req, res) => {
585
+ try {
586
+ const result = await dispatcher.escalate(req.params.runId, { target: req.body?.target || null });
587
+ const sessionId = result?.run?.assistantSessionId;
588
+ if (sessionId && runtime?.noteDispatch) { try { runtime.noteDispatch(sessionId, result.run.runId); } catch {} }
589
+ res.status(result?.queued ? 202 : 200).json(result);
590
+ } catch (error) { sendError(res, error); }
591
+ });
592
+
593
+ router.get('/runs', (req, res) => {
594
+ try {
595
+ const active = req.query.active === '1' || req.query.activeOnly === '1';
596
+ res.json({
597
+ ok: true,
598
+ runs: dispatcher.list({
599
+ activeOnly: active,
600
+ assistantSessionId: req.query.assistantSessionId || null,
601
+ workflowId: req.query.workflowId || null,
602
+ status: req.query.status || null,
603
+ limit: clampInt(req.query.limit, 1, 500, 100),
604
+ }),
605
+ totals: dispatcher.totals({ assistantSessionId: req.query.assistantSessionId || null, workflowId: req.query.workflowId || null }),
606
+ });
607
+ } catch (error) { sendError(res, error); }
608
+ });
609
+
610
+ router.post('/wait', orchestrationGuard, async (req, res) => {
611
+ try {
612
+ const body = req.body || {};
613
+ const target = { runId: body.runId || body.run_id || null, runIds: body.runIds || body.run_ids || null, workflowId: body.workflowId || body.workflow_id || null };
614
+ const chat = chatWait(req, target);
615
+ const result = await dispatcher.wait({
616
+ ...target,
617
+ assistantSessionId: req.assistantCaller.assistantSessionId || null,
618
+ until: body.until || 'idle',
619
+ mode: body.mode || 'all',
620
+ timeoutMs: chat ? CHAT_WAIT_MS : (body.timeoutMs ?? (body.timeout_seconds ? Number(body.timeout_seconds) * 1000 : 30_000)),
621
+ });
622
+ const brain = brainCaller(req);
623
+ if (brain) noteBrainRead(req, res, result?.done, 'agent_wait');
624
+ res.json(brain ? brainWaitView(chat ? chatWaitView(result) : result) : result);
625
+ } catch (error) { sendError(res, error); }
626
+ });
627
+
628
+ router.post('/kill-all', orchestrationGuard, async (req, res) => {
629
+ try {
630
+ const body = req.body || {};
631
+ const assistantSessionId = body.assistantSessionId || body.assistant_session_id || req.assistantCaller.assistantSessionId || null;
632
+ const workflowId = body.workflowId || body.workflow_id || null;
633
+ // Only the user may stop every run of every session; a brain must name
634
+ // its own session (or a workflow).
635
+ if (!assistantSessionId && !workflowId && req.assistantCaller.kind !== 'ui') {
636
+ return res.status(400).json({ ok: false, code: 'SCOPE_REQUIRED', error: 'Pass assistant_session_id (your ASSISTANT_SESSION_ID) or workflow_id to stop runs.' });
637
+ }
638
+ const result = await dispatcher.killAll({ assistantSessionId, workflowId, reason: body.reason || 'kill_all' });
639
+ res.json(result);
640
+ } catch (error) { sendError(res, error); }
641
+ });
642
+
643
+ router.get('/runs/:runId', (req, res) => {
644
+ const run = dispatcher.get(req.params.runId);
645
+ if (!run) return res.status(404).json({ ok: false, code: 'RUN_NOT_FOUND', error: `Unknown run ${req.params.runId}` });
646
+ const brain = brainCaller(req);
647
+ if (brain) noteBrainRead(req, res, run, 'agent_status');
648
+ res.json({ ok: true, run: brain ? slimRunView(run) : run });
649
+ });
650
+ router.get('/runs/:runId/result', (req, res) => {
651
+ try {
652
+ const out = dispatcher.transcript(req.params.runId, { format: 'result' });
653
+ if (out?.result) {
654
+ const run = dispatcher.get(req.params.runId);
655
+ if (run) noteBrainRead(req, res, run, 'agent_read');
656
+ }
657
+ res.json({ ok: true, ...out });
658
+ } catch (error) { sendError(res, error); }
659
+ });
660
+ router.get('/runs/:runId/transcript', (req, res) => {
661
+ try {
662
+ const format = req.query.format || 'tail';
663
+ const out = dispatcher.transcript(req.params.runId, {
664
+ format,
665
+ tail: clampInt(req.query.tail, 1, 500, 40),
666
+ maxChars: clampInt(req.query.maxChars, 200, 20_000, 4000),
667
+ });
668
+ // Only the full text is a read of the result; a tail of events is not.
669
+ if (format === 'text') {
670
+ const run = dispatcher.get(req.params.runId);
671
+ if (run) noteBrainRead(req, res, run, 'agent_read');
672
+ }
673
+ res.json({ ok: true, ...out });
674
+ } catch (error) { sendError(res, error); }
675
+ });
676
+ // The images / videos an image or video creation run generated: only files the
677
+ // dispatcher recorded for that run (copied under data/media/<runId>/), never a
678
+ // path from the request; owner-only like everything here. sendFile answers
679
+ // Range requests, so a video seeks.
680
+ router.get('/runs/:runId/media/:n', async (req, res) => {
681
+ const notFound = () => res.status(404).json({ ok: false, code: 'MEDIA_NOT_FOUND', error: `No media ${req.params.n} for run ${req.params.runId}` });
682
+ if (!/^\d{1,4}$/.test(String(req.params.n))) return notFound();
683
+ let file = null;
684
+ try { file = dispatcher.mediaFile?.(req.params.runId, Number(req.params.n)) || null; } catch { file = null; }
685
+ // Opened once (O_NOFOLLOW, still the checked file) and streamed from that handle: nothing swapped in after the check is served.
686
+ const opened = file?.path ? await openServable(file.path) : null;
687
+ if (!opened) return notFound();
688
+ const { handle, size } = opened;
689
+ let start = 0;
690
+ let end = size - 1;
691
+ // One range only: several ("bytes=0-1,3-4") are ignored and the whole file is sent, as RFC 9110 allows.
692
+ const header = String(req.get('range') || '').trim();
693
+ const range = header.includes(',') ? '' : header;
694
+ if (range) {
695
+ const m = /^bytes=(\d*)-(\d*)$/.exec(range);
696
+ if (m && (m[1] || m[2])) {
697
+ if (m[1]) { start = Number(m[1]); if (m[2]) end = Math.min(Number(m[2]), size - 1); }
698
+ else start = Math.max(0, size - Number(m[2]));
699
+ }
700
+ if (!m || (!m[1] && !m[2]) || start > end || start >= size) {
701
+ await handle.close().catch(() => {});
702
+ return res.status(416).set('Content-Range', `bytes */${size}`).end();
703
+ }
704
+ }
705
+ res.status(range ? 206 : 200).set({
706
+ 'Content-Type': file.mime || 'application/octet-stream', 'Content-Length': String(size ? end - start + 1 : 0),
707
+ 'Accept-Ranges': 'bytes', 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'private, max-age=3600',
708
+ });
709
+ if (range) res.set('Content-Range', `bytes ${start}-${end}/${size}`);
710
+ if (req.method === 'HEAD' || !size) { await handle.close().catch(() => {}); return res.end(); }
711
+ const stream = handle.createReadStream({ start, end });
712
+ stream.on('error', () => res.destroy());
713
+ res.on('close', () => stream.destroy());
714
+ stream.pipe(res);
715
+ });
716
+ router.get('/runs/:runId/wait', orchestrationGuard, async (req, res) => {
717
+ try {
718
+ const chat = chatWait(req, { runId: req.params.runId });
719
+ const result = await dispatcher.wait({
720
+ runId: req.params.runId,
721
+ until: req.query.until || 'idle',
722
+ timeoutMs: chat ? CHAT_WAIT_MS : clampInt(req.query.timeout, 1000, 120_000, 30_000),
723
+ });
724
+ const brain = brainCaller(req);
725
+ if (brain) noteBrainRead(req, res, result?.done, 'agent_wait');
726
+ res.json(brain ? brainWaitView(chat ? chatWaitView(result) : result) : result);
727
+ } catch (error) { sendError(res, error); }
728
+ });
729
+ router.post('/runs/:runId/send', orchestrationGuard, (req, res) => {
730
+ try {
731
+ const body = req.body || {};
732
+ const assistantSessionId = req.assistantCaller.assistantSessionId || dispatcher.get(req.params.runId)?.assistantSessionId || null;
733
+ if (planRefusal(req, res, assistantSessionId, 'send a worker a message')) return;
734
+ if (planState(req, assistantSessionId) === 'plan' && planSendRefusal(res, dispatcher.get(req.params.runId))) return;
735
+ const origin = body.origin === 'user' ? 'user' : (req.assistantCaller.kind === 'ui' ? (body.origin || 'assistant') : 'assistant');
736
+ res.json(dispatcher.sendTurn(req.params.runId, body.text ?? body.message, { origin, queue: body.queue !== false }));
737
+ } catch (error) { sendError(res, error); }
738
+ });
739
+ router.post('/runs/:runId/permission', orchestrationGuard, (req, res) => {
740
+ try {
741
+ const body = req.body || {};
742
+ const requestId = body.requestId || body.request_id;
743
+ if (!requestId) return res.status(400).json({ ok: false, code: 'REQUEST_ID_REQUIRED', error: 'requestId is required' });
744
+ // A brain answers the workers of its own session only (allow and deny alike): the run's session
745
+ // against the caller's, which is its pin's (Claude, OpenCode) or the one it declares (a Codex
746
+ // brain's MCP child, whose pin resolves to no session). A brain caller with no session at all
747
+ // cannot be placed and is left as it was; the UI (a person) answers any run.
748
+ const brain = brainCaller(req);
749
+ const runSession = dispatcher.get(req.params.runId)?.assistantSessionId || null;
750
+ if (brain?.assistantSessionId && runSession && String(runSession) !== String(brain.assistantSessionId)) {
751
+ return res.status(403).json({ ok: false, code: 'RUN_NOT_IN_SESSION', error: `Run ${req.params.runId} belongs to another assistant session` });
752
+ }
753
+ // A remote session under strict worker approvals: only a person answers its workers.
754
+ if (brain && humanApprovalRequired(runSession)) {
755
+ return res.status(403).json({ ok: false, code: 'HUMAN_APPROVAL_REQUIRED', error: 'This run belongs to a WhatsApp session with strict worker approvals: the user answers its permission requests (on their phone or in SynaBun). Tell the user it is waiting for them.' });
756
+ }
757
+ // Who resolved it is the caller: only the UI may name another origin.
758
+ const origin = req.assistantCaller.kind === 'ui' ? (body.origin || 'user') : 'assistant';
759
+ res.json({ ok: true, run: dispatcher.respondPermission(req.params.runId, requestId, body, { origin }) });
760
+ } catch (error) { sendError(res, error); }
761
+ });
762
+ router.post('/runs/:runId/stop', orchestrationGuard, async (req, res) => {
763
+ try { res.json(await dispatcher.stop(req.params.runId, req.body?.reason || (req.assistantCaller.kind === 'ui' ? 'user' : 'assistant'))); } catch (error) { sendError(res, error); }
764
+ });
765
+ // Finished runs are cleared from the agents tray by the user, never by an agent.
766
+ router.delete('/runs/:runId', async (req, res) => {
767
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Finished runs are removed from the assistant panel only.' });
768
+ try { res.json(await dispatcher.remove(req.params.runId, { assistantSessionId: req.query.assistantSessionId || req.body?.assistantSessionId || null })); } catch (error) { sendError(res, error); }
769
+ });
770
+ router.post('/runs/clear-finished', async (req, res) => {
771
+ if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Finished runs are removed from the assistant panel only.' });
772
+ const body = req.body || {};
773
+ const assistantSessionId = body.assistantSessionId || body.assistant_session_id || null;
774
+ const workflowId = body.workflowId || body.workflow_id || null;
775
+ const runIds = Array.isArray(body.runIds) ? body.runIds : null;
776
+ if (!assistantSessionId && !workflowId && !runIds) return res.status(400).json({ ok: false, code: 'SCOPE_REQUIRED', error: 'Pass assistantSessionId, workflowId or runIds.' });
777
+ try { res.json(await dispatcher.removeFinished({ assistantSessionId, workflowId, runIds })); } catch (error) { sendError(res, error); }
778
+ });
779
+ router.post('/runs/:runId/complete', orchestrationGuard, (req, res) => {
780
+ try { res.json(dispatcher.complete(req.params.runId)); } catch (error) { sendError(res, error); }
781
+ });
782
+ router.post('/runs/:runId/focus', (req, res) => {
783
+ const run = dispatcher.get(req.params.runId);
784
+ if (!run) return res.status(404).json({ ok: false, code: 'RUN_NOT_FOUND', error: `Unknown run ${req.params.runId}` });
785
+ const focus = req.body?.focus !== false;
786
+ broadcastSync({ type: 'assistant:focus', runId: run.runId, provider: run.provider, providerSessionId: run.providerSessionId, providerThreadId: run.providerThreadId, assistantSessionId: run.assistantSessionId, focus });
787
+ res.json({ ok: true, focused: true, runId: run.runId, provider: run.provider });
788
+ });
789
+
790
+ // ── assistant sessions ─────────────────────────────────────────────────────
791
+ function requireRuntime(res) {
792
+ if (!runtime) { res.status(503).json({ ok: false, code: 'RUNTIME_UNAVAILABLE', error: 'Assistant runtime is not available' }); return false; }
793
+ return true;
794
+ }
795
+ router.get('/sessions', (req, res) => {
796
+ if (!requireRuntime(res)) return;
797
+ res.json({ ok: true, sessions: runtime.listSessions() });
798
+ });
799
+ router.post('/sessions', async (req, res) => {
800
+ if (!requireRuntime(res)) return;
801
+ try {
802
+ const session = await runtime.createSession(req.body || {});
803
+ res.status(201).json({ ok: true, session });
804
+ } catch (error) { sendError(res, error); }
805
+ });
806
+ router.get('/sessions/:id/usage', (req, res) => {
807
+ if (!requireRuntime(res)) return;
808
+ const id = req.params.id;
809
+ if (!runtime.getSession(id, { transcript: false })) return res.status(404).json({ ok: false, code: 'SESSION_NOT_FOUND', error: `Unknown assistant session ${id}` });
810
+ try {
811
+ const task = typeof req.query.task === 'string' && req.query.task ? req.query.task : 'current';
812
+ // The headline is the same everywhere (the gauge's packet, this endpoint, agent_usage): the
813
+ // session block, every task of the session added up from the ledger file.
814
+ // No ledger wired: an explicit null, so a caller can tell "no metering" from a dropped body.
815
+ if (task === 'all') {
816
+ // With `run`: that run's tokens over every task it worked in, in the same answer.
817
+ const run = typeof req.query.run === 'string' && req.query.run ? req.query.run : null;
818
+ const session = usage?.sessionView(id) ?? null;
819
+ // Whether work is in progress is the runtime's to say (a turn that has no tokens yet).
820
+ const head = session ? runtime.usageView?.(id, 'current')?.session || null : null;
821
+ return res.json({ ok: true, session: session && head ? { ...session, live: head.live, fidelity: head.fidelity } : session, ...(run ? { run: usage?.runView?.(id, run) ?? null } : {}) });
822
+ }
823
+ res.json({ ok: true, usage: runtime.usageView?.(id, task) ?? usage?.taskView(id, task) ?? null });
824
+ } catch (error) { sendError(res, error); }
825
+ });
826
+ router.get('/sessions/:id', (req, res) => {
827
+ if (!requireRuntime(res)) return;
828
+ const session = runtime.getSession(req.params.id, { transcript: req.query.transcript !== '0', limit: clampInt(req.query.limit, 1, 2000, 400) });
829
+ if (!session) return res.status(404).json({ ok: false, code: 'SESSION_NOT_FOUND', error: `Unknown assistant session ${req.params.id}` });
830
+ res.json({ ok: true, session });
831
+ });
832
+ router.patch('/sessions/:id', async (req, res) => {
833
+ if (!requireRuntime(res)) return;
834
+ // The computer toggle belongs to the user, never to an agent.
835
+ if (req.body && 'computerUse' in req.body && (req.get('x-synabun-terminal') || req.get('x-synabun-desktop-grant'))) {
836
+ return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Only the user can switch computer use on or off.' });
837
+ }
838
+ try {
839
+ const session = await runtime.updateSession(req.params.id, req.body || {});
840
+ if (!session) return res.status(404).json({ ok: false, code: 'SESSION_NOT_FOUND', error: `Unknown assistant session ${req.params.id}` });
841
+ res.json({ ok: true, session });
842
+ } catch (error) { sendError(res, error); }
843
+ });
844
+ router.post('/sessions/:id/close', async (req, res) => {
845
+ if (!requireRuntime(res)) return;
846
+ try { res.json({ ok: true, session: await runtime.closeSession(req.params.id) }); } catch (error) { sendError(res, error); }
847
+ });
848
+ router.delete('/sessions/:id', async (req, res) => {
849
+ if (!requireRuntime(res)) return;
850
+ try { res.json({ ok: true, removed: await runtime.destroySession(req.params.id) }); } catch (error) { sendError(res, error); }
851
+ });
852
+
853
+ // ── attachments ────────────────────────────────────────────────────────────
854
+ // The paperclip's files that do not go inline: the raw body streams to disk
855
+ // (assistant-attachments.js) and the brain gets the absolute path. Guests are
856
+ // refused above; the octet-stream type and the X-Synabun-Filename header make
857
+ // a cross-site post a preflighted request, which this server never allows.
858
+ router.post('/attachments', async (req, res) => {
859
+ if (!attachments) return res.status(503).json({ ok: false, code: 'ATTACHMENTS_UNAVAILABLE', error: 'Attachments are not available on this server' });
860
+ const rawName = req.get('x-synabun-filename');
861
+ let name = null;
862
+ try { name = rawName ? decodeURIComponent(rawName) : null; } catch { name = null; }
863
+ if (!req.is('application/octet-stream') || !name) {
864
+ return res.status(400).json({ ok: false, code: 'ATTACHMENT_BAD_REQUEST', error: 'Send the file as application/octet-stream with an X-Synabun-Filename header' });
865
+ }
866
+ const declared = Number(req.get('content-length'));
867
+ if (Number.isFinite(declared) && declared > attachments.maxBytes) {
868
+ res.set('Connection', 'close');
869
+ return res.status(413).json({ ok: false, code: 'ATTACHMENT_TOO_LARGE', error: tooLargeMessage(attachments.maxBytes), limit: attachments.maxBytes });
870
+ }
871
+ try {
872
+ const attachment = await attachments.save(req, { name, sessionId: req.query.assistantSessionId || null, mime: req.get('x-synabun-mime') || '' });
873
+ log('attachment', `${attachment.name} (${attachment.size} bytes) → ${attachment.path}`);
874
+ res.json({ ok: true, attachment });
875
+ } catch (error) {
876
+ if (res.headersSent) return;
877
+ if (error?.code === 'ATTACHMENT_TOO_LARGE') res.set('Connection', 'close');
878
+ sendError(res, error);
879
+ }
880
+ });
881
+
882
+ // ── accounts ───────────────────────────────────────────────────────────────
883
+ function accountInUse(provider) {
884
+ return (accountId) => {
885
+ if (dispatcher.accountInUse(provider, accountId)) return `Stop the dispatched ${provider} runs using account ${accountId} first`;
886
+ if (runtime?.accountInUse?.(provider, accountId)) return `Close the assistant sessions using account ${accountId} first`;
887
+ return null;
888
+ };
889
+ }
890
+ router.get('/claude/accounts', (req, res) => {
891
+ if (!claudeAccounts) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Claude accounts are not available' });
892
+ res.json({ ok: true, accounts: claudeAccounts.listForClient() });
893
+ });
894
+ router.post('/claude/accounts', async (req, res) => {
895
+ if (!claudeAccounts) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Claude accounts are not available' });
896
+ try {
897
+ const account = claudeAccounts.create({ label: req.body?.label });
898
+ let login = null;
899
+ if (req.body?.login !== false && typeof createLoginTerminal === 'function') {
900
+ login = await startClaudeLogin(account.id);
901
+ }
902
+ res.status(201).json({ ok: true, account: claudeAccounts.listForClient().find((row) => row.id === account.id) || account, login });
903
+ } catch (error) { sendError(res, error); }
904
+ });
905
+ async function startClaudeLogin(accountId) {
906
+ const command = claudeAccounts.loginCommand(accountId);
907
+ const terminal = await createLoginTerminal({ env: command.env, cwd: command.cwd, profile: 'claude-code', accountId, command: command.command });
908
+ claudeAccounts.watchLogin(accountId, {
909
+ onLogin: (identity) => {
910
+ broadcastSync({ type: 'assistant:accounts-changed', provider: 'claude-code', accountId, email: identity?.email || null });
911
+ catalogCache = null;
912
+ },
913
+ });
914
+ return { mode: 'terminal', terminalSessionId: terminal?.terminalSessionId || terminal?.id || null, profile: terminal?.profile || 'claude-code' };
915
+ }
916
+ router.post('/claude/accounts/:id/login', async (req, res) => {
917
+ if (!claudeAccounts) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Claude accounts are not available' });
918
+ if (typeof createLoginTerminal !== 'function') return res.status(501).json({ ok: false, code: 'LOGIN_UNAVAILABLE', error: 'Login terminals are not wired' });
919
+ try {
920
+ if (!claudeAccounts.get(req.params.id)) return res.status(404).json({ ok: false, code: 'ACCOUNT_NOT_FOUND', error: `Unknown Claude account ${req.params.id}` });
921
+ res.json({ ok: true, login: await startClaudeLogin(req.params.id) });
922
+ } catch (error) { sendError(res, error); }
923
+ });
924
+ router.patch('/claude/accounts/:id', (req, res) => {
925
+ if (!claudeAccounts) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Claude accounts are not available' });
926
+ try {
927
+ const account = claudeAccounts.rename(req.params.id, req.body?.label);
928
+ catalogCache = null;
929
+ broadcastSync({ type: 'assistant:accounts-changed', provider: 'claude-code', accountId: req.params.id });
930
+ res.json({ ok: true, account });
931
+ } catch (error) { sendError(res, error); }
932
+ });
933
+ router.delete('/claude/accounts/:id', (req, res) => {
934
+ if (!claudeAccounts) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Claude accounts are not available' });
935
+ try {
936
+ const result = claudeAccounts.remove(req.params.id, { inUse: accountInUse('claude-code') });
937
+ catalogCache = null;
938
+ broadcastSync({ type: 'assistant:accounts-changed', provider: 'claude-code', accountId: req.params.id, removed: true });
939
+ res.json({ ok: true, ...result });
940
+ } catch (error) { sendError(res, error); }
941
+ });
942
+ router.get('/codex/accounts', (req, res) => {
943
+ if (!codexAccounts) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Codex accounts are not available' });
944
+ res.json({ ok: true, accounts: codexAccounts.listForClient() });
945
+ });
946
+ router.post('/codex/accounts/add-start', async (req, res) => {
947
+ if (!codexAccounts?.addStart) return res.status(501).json({ ok: false, code: 'NOT_IMPLEMENTED', error: 'Add a Codex account from the Codex sidepanel account menu' });
948
+ try {
949
+ const result = await codexAccounts.addStart(req.body || {});
950
+ catalogCache = null;
951
+ broadcastSync({ type: 'assistant:accounts-changed', provider: 'codex', accountId: result?.accountId || null });
952
+ res.status(201).json({ ok: true, ...result });
953
+ } catch (error) { sendError(res, error); }
954
+ });
955
+ router.patch('/codex/accounts/:id', (req, res) => {
956
+ if (!codexAccounts?.rename) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Codex accounts are not available' });
957
+ try {
958
+ const account = codexAccounts.rename(req.params.id, req.body?.label);
959
+ catalogCache = null;
960
+ broadcastSync({ type: 'assistant:accounts-changed', provider: 'codex', accountId: req.params.id });
961
+ res.json({ ok: true, account });
962
+ } catch (error) { sendError(res, error); }
963
+ });
964
+ router.delete('/codex/accounts/:id', (req, res) => {
965
+ if (!codexAccounts?.remove) return res.status(503).json({ ok: false, code: 'ACCOUNTS_UNAVAILABLE', error: 'Codex accounts are not available' });
966
+ try {
967
+ const result = codexAccounts.remove(req.params.id, { inUse: accountInUse('codex') });
968
+ catalogCache = null;
969
+ broadcastSync({ type: 'assistant:accounts-changed', provider: 'codex', accountId: req.params.id, removed: true });
970
+ res.json({ ok: true, ...result });
971
+ } catch (error) { sendError(res, error); }
972
+ });
973
+
974
+ // ── sidepanel tab registry (UI heartbeat) ──────────────────────────────────
975
+ function listTabs() {
976
+ const cutoff = now() - tabTtlMs;
977
+ const out = [];
978
+ for (const [windowId, row] of uiTabs) {
979
+ if (row.at < cutoff) { uiTabs.delete(windowId); continue; }
980
+ out.push({ windowId, at: new Date(row.at).toISOString(), panels: row.panels });
981
+ }
982
+ return out;
983
+ }
984
+ router.post('/ui/tabs', (req, res) => {
985
+ const windowId = String(req.body?.windowId || '').trim().slice(0, 128);
986
+ if (!windowId) return res.status(400).json({ ok: false, code: 'WINDOW_ID_REQUIRED', error: 'windowId is required' });
987
+ const panels = req.body?.panels && typeof req.body.panels === 'object' ? req.body.panels : {};
988
+ uiTabs.set(windowId, { panels, at: now() });
989
+ if (uiTabs.size > 50) uiTabs.delete(uiTabs.keys().next().value);
990
+ res.json({ ok: true, windows: uiTabs.size });
991
+ });
992
+ router.get('/ui/tabs', (req, res) => res.json({ ok: true, windows: listTabs() }));
993
+ router.post('/ui/focus', (req, res) => {
994
+ const body = req.body || {};
995
+ broadcastSync({ type: 'assistant:focus', provider: body.provider || null, tabId: body.tabId || null, providerSessionId: body.sessionId || body.providerSessionId || null, runId: body.runId || null, focus: body.focus !== false });
996
+ res.json({ ok: true });
997
+ });
998
+
999
+ router.use((error, req, res, next) => { // eslint-disable-line no-unused-vars
1000
+ log('assistant-api', error?.message || String(error));
1001
+ sendError(res, error);
1002
+ });
1003
+
1004
+ return router;
1005
+ }