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
@@ -4,10 +4,98 @@
4
4
  * which manages Playwright sessions, CDP screencast, stealth, etc.
5
5
  */
6
6
 
7
+ import { AsyncLocalStorage } from 'node:async_hooks';
8
+ import { randomUUID } from 'node:crypto';
9
+
10
+ import {
11
+ type CallerIdentity,
12
+ getIdentity,
13
+ effectivePins,
14
+ terminalIdFor,
15
+ callerRole,
16
+ setReleaseHook,
17
+ peekStdioIdentity,
18
+ isHttpMode,
19
+ } from './identity.js';
20
+
7
21
  const BASE_URL = process.env.NEURAL_INTERFACE_URL
8
22
  || `http://localhost:${process.env.NEURAL_PORT || '3344'}`;
9
23
  const DEFAULT_TIMEOUT = 10_000;
10
24
  const LONG_TIMEOUT = 30_000;
25
+ const SESSION_CREATE_TIMEOUT = 70_000;
26
+ // Existing engagement spacing can consume 90 seconds before the click begins.
27
+ const CLICK_TIMEOUT = 120_000;
28
+
29
+ export function isBrowserV2Enabled(): boolean {
30
+ return process.env.SYNABUN_BROWSER_V2 !== '0';
31
+ }
32
+
33
+ type BrowserRoute = { sessionId: string; tabId?: string };
34
+ const resolvedRoutes = new WeakMap<CallerIdentity, { key: string; route: BrowserRoute; sessionId?: string; tabId?: string; autoCreate?: { url?: string }; platform?: boolean }>();
35
+ const resolutionRefresh = new AsyncLocalStorage<boolean>();
36
+ export interface BrowserBatchContext {
37
+ route: BrowserRoute;
38
+ errors: NiResponse[];
39
+ }
40
+ const browserCancellation = new AsyncLocalStorage<{ signal: AbortSignal; scope: 'browser' | 'batch' }>();
41
+ export function runWithBrowserCancellation<T>(signal: AbortSignal | undefined, run: () => T, scope: 'browser' | 'batch' = 'browser'): T {
42
+ return signal ? browserCancellation.run({ signal, scope }, run) : run();
43
+ }
44
+ const batchContext = new AsyncLocalStorage<BrowserBatchContext>();
45
+ export function runBrowserBatchContext<T>(context: BrowserBatchContext, run: () => T): T {
46
+ return batchContext.run(context, run);
47
+ }
48
+ /** Inside a browser_batch step. Browser assistance is off there: a batch is a known sequence. */
49
+ export function inBrowserBatch(): boolean {
50
+ return batchContext.getStore() !== undefined;
51
+ }
52
+ /** The MCP caller's cancellation signal, so an advisory judgment ends with the tool call that asked for it. */
53
+ export function currentBrowserSignal(): AbortSignal | undefined {
54
+ return browserCancellation.getStore()?.signal;
55
+ }
56
+
57
+ function browserMetric(record: Record<string, unknown>): void {
58
+ if (process.env.SYNABUN_BROWSER_METRICS === '1') {
59
+ console.error(JSON.stringify({ source: 'browser-client', ...record }));
60
+ }
61
+ }
62
+
63
+ // Platform tools (BlueSky, X, Facebook, YouTube, …) always act on normal pages, so
64
+ // inside this scope resolution requires the default browser: an explicit session
65
+ // the server says does not serve it is dropped for the default one.
66
+ const platformRoute = new AsyncLocalStorage<boolean>();
67
+ export function withPlatformRoute<T>(run: () => T): T {
68
+ return platformRoute.run(true, run);
69
+ }
70
+ function inPlatformRoute(): boolean {
71
+ return platformRoute.getStore() === true;
72
+ }
73
+
74
+ // The default browser as the Neural Interface last described it (its
75
+ // X-Synabun-Browser-Default header on every /api/browser response: connect mode
76
+ // and MoreLogin env). Part of every route key, so a change of browser settings
77
+ // drops every cached route of this process; the server in-process calls
78
+ // noteBrowserDefault when the settings are saved.
79
+ let browserDefault = '';
80
+ export function noteBrowserDefault(signature: string | null | undefined): void {
81
+ if (typeof signature === 'string' && signature !== browserDefault) browserDefault = signature;
82
+ }
83
+
84
+ function routeKey(id: CallerIdentity, sessionId?: string, tabId?: string, wantsDefaultBrowser = false): string {
85
+ const pins = effectivePins(id);
86
+ // Only an explicit id resolves differently for page-opening and platform calls
87
+ // (resolveSessionUncached may drop it), so only then do they get their own slot.
88
+ return JSON.stringify([sessionId, tabId, pins.browserSessionId, pins.browserTabId,
89
+ id.state.ancestorPinnedSession, id.state.ancestorPinnedTab, sessionId ? wantsDefaultBrowser : null, browserDefault]);
90
+ }
91
+
92
+ export function isBrowserFastMode(): boolean {
93
+ return process.env.SYNABUN_BROWSER_FAST === '1';
94
+ }
95
+
96
+ export function isBrowserCompactMode(): boolean {
97
+ return process.env.SYNABUN_BROWSER_COMPACT === '1' || isBrowserFastMode();
98
+ }
11
99
 
12
100
  export interface BrowserSessionInfo {
13
101
  id: string;
@@ -15,49 +103,373 @@ export interface BrowserSessionInfo {
15
103
  title: string;
16
104
  createdAt: number;
17
105
  clients: number;
106
+ loopOwned?: boolean;
107
+ interactiveOwned?: boolean;
108
+ agentOwned?: boolean;
109
+ persistent?: boolean;
110
+ activeTabId?: string | null;
111
+ tabs?: Array<{ id: string; url?: string; title?: string; active?: boolean }>;
112
+ /** ownerKey → tabId for every automation that owns a tab in this session.
113
+ * Used to tell a pristine (manual) session apart from one another automation
114
+ * already drives, so we never adopt-and-collide with its tab. */
115
+ tabOwners?: Record<string, string>;
116
+ profileMode?: string;
117
+ moreloginEnvId?: string | null;
118
+ /** Server's verdict (sessionServesMoreLoginDefault): may this session carry normal
119
+ * pages? False only while MoreLogin is the default; absent on older servers. */
120
+ servesDefault?: boolean;
18
121
  }
19
122
 
20
- interface NiResponse {
123
+ export interface NiResponse {
21
124
  ok?: boolean;
22
125
  error?: string;
126
+ tabRecovered?: boolean;
127
+ sessionInvalidated?: boolean;
23
128
  [key: string]: unknown;
24
129
  }
25
130
 
26
- async function request(
131
+ async function requestOnce(
27
132
  method: string,
28
133
  path: string,
29
134
  body?: Record<string, unknown>,
30
135
  timeout = DEFAULT_TIMEOUT
31
136
  ): Promise<NiResponse> {
137
+ const cancellation = browserCancellation.getStore();
138
+ const callerSignal = cancellation?.signal;
139
+ const cancellationCode = cancellation?.scope === 'batch' ? 'BATCH_CANCELLED' : 'BROWSER_CANCELLED';
140
+ const cancellationLabel = cancellation?.scope === 'batch' ? 'Browser batch' : 'Browser request';
141
+ if (callerSignal?.aborted) return { error: `${cancellationLabel} cancelled before the request started.`, code: cancellationCode, actionStarted: false };
142
+ const requestId = randomUUID();
143
+ const startedAt = performance.now();
144
+ const deadline = Date.now() + timeout;
145
+ let responseBytes = 0;
146
+ let estimatedTextTokens = 0;
147
+ let status: number | undefined;
32
148
  const controller = new AbortController();
149
+ const cancel = () => controller.abort();
150
+ callerSignal?.addEventListener('abort', cancel, { once: true });
151
+ if (callerSignal?.aborted) cancel();
152
+ let sent = false;
33
153
  const timer = setTimeout(() => controller.abort(), timeout);
34
154
 
35
155
  try {
156
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
157
+ // Per-caller routing identity: the server maps this to the caller's owned
158
+ // tab (_tabOwners) so tab-less browser calls never land on another
159
+ // automation's tab. Derived per-request over HTTP, per-process for stdio.
160
+ headers['X-Synabun-Terminal'] = terminalIdFor(getIdentity());
161
+ // Assistant runtimes forward their role so /api/assistant/* can tell an
162
+ // orchestrator apart from a worker run (workers must never dispatch).
163
+ const role = callerRole();
164
+ if (role) headers['X-Synabun-Role'] = role;
165
+ headers['X-Synabun-Request-Id'] = requestId;
166
+ headers['X-Synabun-Deadline'] = String(deadline);
167
+ // Lets the server refuse advisory payloads and auto-heal for a batch step on its own authority.
168
+ if (batchContext.getStore()) headers['X-Synabun-Batch'] = '1';
36
169
  const opts: RequestInit = {
37
170
  method,
38
- headers: { 'Content-Type': 'application/json' },
171
+ headers,
39
172
  signal: controller.signal,
40
173
  };
41
174
  if (body && method !== 'GET') {
42
175
  opts.body = JSON.stringify(body);
43
176
  }
177
+ if (callerSignal?.aborted) return { error: `${cancellationLabel} cancelled before the request started.`, code: cancellationCode, actionStarted: false };
178
+ sent = true;
44
179
  const res = await fetch(`${BASE_URL}${path}`, opts);
45
- const data = await res.json() as NiResponse;
180
+ status = res.status;
181
+ noteBrowserDefault(res.headers?.get?.('X-Synabun-Browser-Default'));
182
+ const responseText = await res.text();
183
+ const data = JSON.parse(responseText) as NiResponse;
184
+ if (process.env.SYNABUN_BROWSER_METRICS === '1') {
185
+ responseBytes = Buffer.byteLength(responseText, 'utf8');
186
+ // Approximation, not provider-billed usage; exclude base64 image fields.
187
+ const { image: _image, screenshot: _screenshot, data: _data, ...textFields } = data;
188
+ estimatedTextTokens = Math.ceil(JSON.stringify(textFields).length / 4);
189
+ }
46
190
  if (!res.ok && !data.error) {
47
191
  data.error = `HTTP ${res.status}`;
48
192
  }
49
193
  return data;
50
194
  } catch (err: unknown) {
51
195
  const msg = err instanceof Error ? err.message : String(err);
52
- if (msg.includes('abort')) {
53
- return { error: `Request timed out after ${timeout}ms` };
196
+ if (callerSignal?.aborted) {
197
+ return { error: `${cancellationLabel} cancelled; inspect the page before retrying.`, code: cancellationCode, ...(sent ? { outcome: 'uncertain' } : { actionStarted: false }) };
198
+ }
199
+ if (controller.signal.aborted || /abort/i.test(msg)) {
200
+ return { error: `Request timed out after ${timeout}ms; outcome may be uncertain. Inspect the page before retrying.`, code: 'REQUEST_TIMEOUT', outcome: 'uncertain' };
54
201
  }
55
- return { error: `Neural Interface unreachable: ${msg}. Is the Neural Interface server running?` };
202
+ return { error: `Neural Interface unreachable: ${msg}. Is the Neural Interface server running?`, code: 'TRANSPORT_ERROR', outcome: 'uncertain' };
56
203
  } finally {
57
204
  clearTimeout(timer);
205
+ callerSignal?.removeEventListener('abort', cancel);
206
+ if (path.startsWith('/api/browser/')) {
207
+ browserMetric({ requestId, phase: 'http', method,
208
+ operation: path.split('?')[0].replace(/\/sessions\/[^/]+/, '/sessions/:id'),
209
+ durationMs: Math.round((performance.now() - startedAt) * 100) / 100,
210
+ status, responseBytes, estimatedTextTokens });
211
+ }
58
212
  }
59
213
  }
60
214
 
215
+ async function request(
216
+ method: string,
217
+ path: string,
218
+ body?: Record<string, unknown>,
219
+ timeout = DEFAULT_TIMEOUT,
220
+ ): Promise<NiResponse> {
221
+ const result = await requestOnce(method, path, body, timeout);
222
+ const context = batchContext.getStore();
223
+ const missingRoute = result.actionStarted === false
224
+ && (result.code === 'SESSION_NOT_FOUND' || result.code === 'TAB_NOT_FOUND');
225
+ const match = /^\/api\/browser\/sessions\/([^/?]+)\/(.+)$/.exec(path);
226
+ if (isBrowserV2Enabled() && missingRoute && match) {
227
+ const id = getIdentity();
228
+ const cached = resolvedRoutes.get(id);
229
+ resolvedRoutes.delete(id);
230
+ // Batches are pinned to their original tab and never recover midway.
231
+ const pinnedSession = effectivePins(id).browserSessionId || id.state.ancestorPinnedSession;
232
+ if (!context && cached?.route.sessionId === match[1]
233
+ && (!cached.sessionId || cached.sessionId === pinnedSession)) {
234
+ // Clear unpinned fast-mode state too: a force-refresh must not reuse it.
235
+ if (id.state.interactiveSessionId === match[1]) {
236
+ id.state.interactiveSessionId = null;
237
+ id.state.interactiveTabId = null;
238
+ }
239
+ if (id.state.affinitySessionId === match[1]) id.state.affinitySessionId = null;
240
+ const refreshed = await resolutionRefresh.run(true, () => resolveSession(
241
+ cached.sessionId, cached.autoCreate, cached.tabId, { platform: cached.platform === true },
242
+ ));
243
+ if (!('error' in refreshed)) {
244
+ const nextPath = path.replace(`/sessions/${match[1]}/`, `/sessions/${refreshed.sessionId}/`);
245
+ const nextBody = body ? { ...body, tabId: refreshed.tabId } : undefined;
246
+ const url = new URL(nextPath, BASE_URL);
247
+ if (refreshed.tabId) url.searchParams.set('tabId', refreshed.tabId);
248
+ else url.searchParams.delete('tabId');
249
+ return requestOnce(method, `${url.pathname}${url.search}`, nextBody, timeout);
250
+ }
251
+ // Recovery failed: report why (e.g. "MoreLogin connect failed: …"), not the
252
+ // bare "Session not found" that triggered it. Nothing was started.
253
+ return { ...result, error: refreshed.error, recoveryFailed: true };
254
+ }
255
+ }
256
+ // The server refused a session that does not serve the default browser (the
257
+ // settings changed under a cached route): forget it, so the next call resolves
258
+ // the default browser. Not retried here: the page the call meant is in the other browser.
259
+ if (result.code === 'NOT_DEFAULT_BROWSER' && match) {
260
+ const id = getIdentity();
261
+ resolvedRoutes.delete(id);
262
+ if (id.state.interactiveSessionId === match[1]) {
263
+ id.state.interactiveSessionId = null;
264
+ id.state.interactiveTabId = null;
265
+ }
266
+ if (id.state.affinitySessionId === match[1]) id.state.affinitySessionId = null;
267
+ }
268
+ if (context && result.error) context.errors.push(result);
269
+ else if (context && typeof result.snapshotError === 'string') {
270
+ context.errors.push({ error: result.snapshotError, code: 'SNAPSHOT_FAILED' });
271
+ }
272
+ return result;
273
+ }
274
+
275
+ /**
276
+ * Best-effort reconciliation for an MCP process that changed its own tool
277
+ * profile. Only sidepanel/automation children carry an explicit terminal id;
278
+ * standalone CLI processes stay process-local without contacting Neural
279
+ * Interface. The server derives the owning runtime from X-Synabun-Terminal.
280
+ */
281
+ export type RuntimeMcpProfileReport = {
282
+ reported: boolean;
283
+ hostRefresh: 'scheduled' | 'notification' | 'not-needed' | 'unavailable';
284
+ runtimeKind?: 'opencode' | 'codex' | 'loop';
285
+ correlationId?: string;
286
+ error?: string;
287
+ };
288
+
289
+ export async function reportRuntimeMcpProfile(
290
+ profile: string,
291
+ options: { catalogMode?: 'profiled' | 'deferred' } = {}
292
+ ): Promise<RuntimeMcpProfileReport> {
293
+ if (!String(process.env.SYNABUN_TERMINAL_SESSION || '').trim()) {
294
+ return { reported: false, hostRefresh: 'notification' };
295
+ }
296
+ const result = await request('POST', '/api/mcp/runtime-profile', {
297
+ profile,
298
+ catalogMode: options.catalogMode || 'profiled',
299
+ }, 2_000);
300
+ if (result.ok) {
301
+ const hostRefresh = result.hostRefresh === 'scheduled'
302
+ || result.hostRefresh === 'notification'
303
+ || result.hostRefresh === 'not-needed'
304
+ ? result.hostRefresh
305
+ : 'unavailable';
306
+ const runtimeKind = result.runtimeKind === 'opencode' || result.runtimeKind === 'codex' || result.runtimeKind === 'loop'
307
+ ? result.runtimeKind
308
+ : undefined;
309
+ return {
310
+ reported: true,
311
+ hostRefresh,
312
+ runtimeKind,
313
+ correlationId: typeof result.correlationId === 'string' ? result.correlationId : undefined,
314
+ };
315
+ }
316
+ return {
317
+ reported: false,
318
+ hostRefresh: 'unavailable',
319
+ error: typeof result.error === 'string' ? result.error : 'Runtime profile report failed',
320
+ };
321
+ }
322
+
323
+ // All per-caller identity state (recovery cache, session affinity, interactive
324
+ // tab cache, ancestor-PID pins) lives on the CallerIdentity object resolved by
325
+ // getIdentity() — per HTTP caller over the HTTP transport, a per-process
326
+ // singleton for stdio. Module-level versions of this state were the root cause
327
+ // of cross-automation tab collisions: every HTTP caller shared them.
328
+
329
+ // Join the ONE shared browser with a dedicated, owned tab (instead of launching a
330
+ // second browser on a persistent profile, which fails with "profile is locked").
331
+ async function acquireInteractive(id: CallerIdentity, url?: string): Promise<{ sessionId: string; tabId?: string } | { error: string }> {
332
+ const resp = await request('POST', '/api/browser/acquire', {
333
+ clientId: id.clientId,
334
+ url: url || 'about:blank',
335
+ }, SESSION_CREATE_TIMEOUT);
336
+ if (resp.error || !resp.sessionId) {
337
+ return { error: `Failed to acquire a shared browser tab: ${resp.error || 'no session returned'}` };
338
+ }
339
+ id.state.interactiveSessionId = resp.sessionId as string;
340
+ id.state.interactiveTabId = (resp.tabId as string) || null;
341
+ id.state.affinitySessionId = id.state.interactiveSessionId;
342
+ return { sessionId: id.state.interactiveSessionId, tabId: id.state.interactiveTabId || undefined };
343
+ }
344
+
345
+ function adoptCreatedSession(id: CallerIdentity, created: NiResponse, tabId?: string): { sessionId: string; tabId?: string } {
346
+ const sessionId = created.sessionId as string;
347
+ id.state.affinitySessionId = sessionId;
348
+ return { sessionId, tabId };
349
+ }
350
+
351
+ // Pages (localhost included) only run where the server says the default browser
352
+ // is. Absent servesDefault (older server, or a managed / real-Chrome setup) keeps
353
+ // the previous behavior.
354
+ function servesDefault(s: BrowserSessionInfo): boolean {
355
+ return s.servesDefault !== false;
356
+ }
357
+ function describeSession(s: BrowserSessionInfo): string {
358
+ return `${s.profileMode || 'unknown'} mode${s.moreloginEnvId ? `, MoreLogin env ${s.moreloginEnvId}` : ''}`;
359
+ }
360
+ function describeDefault(list: NiResponse): string {
361
+ const def = list.defaultBrowser as { connectMode?: string | null; moreloginEnvId?: string | null } | undefined;
362
+ if (def?.connectMode === 'morelogin') return `MoreLogin${def.moreloginEnvId ? ` env ${def.moreloginEnvId}` : ''}`;
363
+ return 'the configured browser';
364
+ }
365
+
366
+ // Best-effort release of an identity's owned tab, so the shared browser can be
367
+ // grace-reaped once no owners remain. Fire-and-forget — never blocks shutdown.
368
+ // Also registered as the identity-eviction hook (HTTP DELETE / idle TTL).
369
+ function releaseTabFor(id: CallerIdentity): void {
370
+ if (!id.state.interactiveSessionId) return;
371
+ try {
372
+ void fetch(`${BASE_URL}/api/browser/release`, {
373
+ method: 'POST',
374
+ headers: { 'Content-Type': 'application/json' },
375
+ body: JSON.stringify({ clientId: id.clientId }),
376
+ }).catch(() => {});
377
+ } catch { /* best-effort */ }
378
+ }
379
+ setReleaseHook(releaseTabFor);
380
+
381
+ // Process exit hooks only matter for stdio (one process per caller). In HTTP
382
+ // mode the NI server hosts many identities — their tabs are released by MCP
383
+ // session DELETE or idle eviction, and an NI shutdown takes the browser with it.
384
+ function releaseOnExit(): void {
385
+ if (isHttpMode()) return;
386
+ const stdio = peekStdioIdentity();
387
+ if (stdio) releaseTabFor(stdio);
388
+ }
389
+ process.once('exit', releaseOnExit);
390
+
391
+ // SIGTERM / SIGINT end a stdio server here: release its tab, exit 0. Served
392
+ // over HTTP, the process belongs to its host. The Neural Interface runs
393
+ // mcp-server/dist in process and owns these signals (server.js
394
+ // gracefulShutdown: WhatsApp Link, terminal host, then the database); this
395
+ // listener is registered before its listeners, so exiting here would preempt
396
+ // all of them. The mode is read when the signal arrives, not at import:
397
+ // server.js loads this module through its static imports, long before
398
+ // createMcpRoutes() calls markHttpMode(). A host with no listener of its own
399
+ // (the standalone dist/http.js) still exits here, because registering this
400
+ // listener already took Node's default exit on the signal away from it.
401
+ function exitOnSignal(signal: 'SIGTERM' | 'SIGINT'): () => void {
402
+ const onSignal = (): void => {
403
+ if (isHttpMode() && process.listeners(signal).some((listener) => listener !== onSignal)) return;
404
+ releaseOnExit();
405
+ process.exit(0);
406
+ };
407
+ return onSignal;
408
+ }
409
+ process.once('SIGTERM', exitOnSignal('SIGTERM'));
410
+ process.once('SIGINT', exitOnSignal('SIGINT'));
411
+
412
+ // Ancestor-PID fallback (Layer B). Walks the process tree once and caches the
413
+ // resolved loop pins on the identity. Used when codex/opencode strip
414
+ // SYNABUN_BROWSER_* env on MCP child spawn — we map our PID chain up to a known
415
+ // PTY and pull the loop's session/tab IDs from that loop's state file.
416
+ // Bounded retry instead of a permanent single-shot latch: a fast codex/opencode
417
+ // exec spawn can call MCP before its PTY is registered server-side, so the first
418
+ // ancestor lookup misses. Retry a few times until matched, then give up.
419
+ const ANCESTOR_MAX_ATTEMPTS = 4;
420
+
421
+ async function getAncestorPids(): Promise<number[]> {
422
+ const ppid = typeof process.ppid === 'number' ? process.ppid : 0;
423
+ if (!ppid) return [];
424
+ const pids: number[] = [ppid];
425
+ const isWin = process.platform === 'win32';
426
+ const { exec } = await import('node:child_process');
427
+ const runCmd = (cmd: string): Promise<string> => new Promise((resolve) => {
428
+ exec(cmd, { timeout: 1500, windowsHide: true }, (_err: unknown, stdout: string) => resolve(stdout || ''));
429
+ });
430
+ let current = ppid;
431
+ for (let i = 0; i < 6; i++) {
432
+ let parent = 0;
433
+ try {
434
+ if (isWin) {
435
+ const out = await runCmd(`wmic process where ProcessId=${current} get ParentProcessId /value`);
436
+ const m = /ParentProcessId=(\d+)/i.exec(out);
437
+ parent = m ? Number(m[1]) : 0;
438
+ } else {
439
+ const out = await runCmd(`ps -o ppid= -p ${current}`);
440
+ parent = Number(out.trim().split(/\s+/)[0]) || 0;
441
+ }
442
+ } catch { parent = 0; }
443
+ if (!parent || parent === 1 || parent === current) break;
444
+ pids.push(parent);
445
+ current = parent;
446
+ }
447
+ return pids;
448
+ }
449
+
450
+ async function resolveFromAncestors(id: CallerIdentity): Promise<void> {
451
+ // Over HTTP this code runs in the Neural Interface's own process — walking
452
+ // OUR pid chain would resolve the NI server, not the caller. HTTP callers
453
+ // carry their identity in headers/Mcp-Session-Id instead.
454
+ if (id.source !== 'stdio') return;
455
+ if (id.state.ancestorPinnedSession) return; // already resolved
456
+ if (id.state.ancestorAttempts >= ANCESTOR_MAX_ATTEMPTS) return; // give up after N misses
457
+ id.state.ancestorAttempts++;
458
+ try {
459
+ const pids = await getAncestorPids();
460
+ if (pids.length === 0) return;
461
+ const resp = await request('POST', '/api/loop/resolve-from-ancestors', { pids }, 2000);
462
+ // Capture the owning terminalSessionId whenever a PTY matched (even if its
463
+ // loop file lacked browser pins) — it's needed for the X-Synabun-Terminal header.
464
+ if (resp.terminalSessionId) id.state.ancestorPinnedTerminal = resp.terminalSessionId as string;
465
+ if (resp.matched) {
466
+ id.state.ancestorPinnedSession = (resp.browserSessionId as string) || null;
467
+ id.state.ancestorPinnedTab = (resp.browserTabId as string) || null;
468
+ console.error(`[MCP] ancestor lookup matched: session=${id.state.ancestorPinnedSession} tab=${id.state.ancestorPinnedTab} terminal=${id.state.ancestorPinnedTerminal}`);
469
+ }
470
+ } catch { /* best-effort */ }
471
+ }
472
+
61
473
  /**
62
474
  * Resolve which session ID to use.
63
475
  * - If sessionId provided, return it immediately (server returns 404 if invalid).
@@ -65,53 +477,286 @@ async function request(
65
477
  * - If 0 sessions exist and autoCreate is true, create one.
66
478
  * - If multiple sessions and no ID, return error listing them.
67
479
  */
68
- export async function resolveSession(
480
+ async function resolveSessionUncached(
69
481
  sessionId?: string,
70
- autoCreate?: { url?: string }
71
- ): Promise<{ sessionId: string } | { error: string }> {
72
- // Agent-scoped browser session — set by the agent orchestrator to pin
73
- // this MCP instance to a specific browser session (multi-agent isolation).
74
- const pinnedSession = process.env.SYNABUN_BROWSER_SESSION;
75
- if (pinnedSession && !sessionId) {
76
- return { sessionId: pinnedSession };
482
+ autoCreate?: { url?: string },
483
+ tabId?: string,
484
+ platform = false,
485
+ ): Promise<{ sessionId: string; tabId?: string } | { error: string }> {
486
+ // Per-caller identity: pins come from launch headers (HTTP) or live env
487
+ // (stdio); all caches below live on the identity, never module-level.
488
+ const id = getIdentity();
489
+ const pins = effectivePins(id);
490
+ // Every page tool runs in the default browser only. An explicit id the server
491
+ // says does not serve it (managed Chrome, or another MoreLogin env while
492
+ // MoreLogin is the default) is dropped for a navigation (localhost included)
493
+ // or a platform tool, and the default browser resolved instead; any other page
494
+ // tool is refused, since the page it names is in the other browser. The
495
+ // server refuses them too (NOT_DEFAULT_BROWSER).
496
+ const wantsDefaultBrowser = platform || !!autoCreate;
497
+ if (sessionId && sessionId !== (pins.browserSessionId || id.state.ancestorPinnedSession)) {
498
+ const listed = await request('GET', '/api/browser/sessions');
499
+ const named = ((listed.sessions || []) as BrowserSessionInfo[]).find(s => s.id === sessionId);
500
+ if (named && !servesDefault(named)) {
501
+ if (!wantsDefaultBrowser) {
502
+ return { error: `Browser session ${sessionId} is not the default browser (${describeSession(named)}; the default is ${describeDefault(listed)}), so SynaBun's browser tools do not act on it. Leave sessionId out: browser_navigate opens the page in the default browser.` };
503
+ }
504
+ console.error(`[MCP] not using session ${sessionId} (${describeSession(named)}) — resolving ${describeDefault(listed)}`);
505
+ sessionId = undefined;
506
+ tabId = undefined;
507
+ }
508
+ }
509
+
510
+ // Resolve tab ID from explicit param or pin.
511
+ // When a tab is pinned (loop/agent context), the pin wins even if the caller
512
+ // passes a different tabId — prevents tab-leak across loops.
513
+ // If pins are missing (codex/opencode env-strip case), try ancestor-PID
514
+ // lookup (stdio only) and use whatever the Neural Interface reports for our PTY.
515
+ if (!pins.browserSessionId && !pins.browserTabId && !sessionId) {
516
+ await resolveFromAncestors(id);
517
+ }
518
+ const pinnedTab = pins.browserTabId || id.state.ancestorPinnedTab || undefined;
519
+ const resolvedTabId = pinnedTab || tabId || undefined;
520
+ if (pinnedTab && tabId && tabId !== pinnedTab) {
521
+ console.error(`[MCP] tabId override ignored: pinned=${pinnedTab} requested=${tabId}`);
522
+ }
523
+
524
+ // Agent/loop-scoped browser session — set by the orchestrator to pin
525
+ // this caller to a specific browser session (multi-session isolation).
526
+ // If pinned session died, check recovery cache first, then auto-create.
527
+ const pinnedSession = pins.browserSessionId || id.state.ancestorPinnedSession || undefined;
528
+ // Loop prompts intentionally pass their injected sessionId/tabId on every
529
+ // call. Treat an explicit session that matches the pin as pinned traffic too;
530
+ // otherwise the old fast path skipped liveness checks and returned a stale
531
+ // tab forever after a renderer/page closed.
532
+ if (pinnedSession && (!sessionId || sessionId === pinnedSession)) {
533
+ // Check recovery cache first — avoids re-creating on every call after recovery.
534
+ // Return the RECOVERED tab, never the stale pinned tab (which 404s).
535
+ if (id.state.recoveredSessionId) {
536
+ const recheck = await request('GET', '/api/browser/sessions');
537
+ const alive = ((recheck.sessions || []) as BrowserSessionInfo[]).find(s => s.id === id.state.recoveredSessionId);
538
+ if (alive) return { sessionId: id.state.recoveredSessionId, tabId: id.state.recoveredTabId || undefined };
539
+ id.state.recoveredSessionId = null; // recovered session also died — try fresh recovery
540
+ id.state.recoveredTabId = null;
541
+ }
542
+
543
+ const check = await request('GET', '/api/browser/sessions');
544
+ const active = ((check.sessions || []) as BrowserSessionInfo[]).find(s => s.id === pinnedSession);
545
+ // A run's pinned browser must be the default one. Hard-fail rather than work
546
+ // in the wrong browser.
547
+ if (active && !servesDefault(active)) {
548
+ return { error: `Pinned browser session ${pinnedSession} is not the default browser (${describeSession(active)}; the default is ${describeDefault(check)}), so this run will not use it. Start the run again so it acquires the default browser.` };
549
+ }
550
+ if (active) {
551
+ // Also verify the resolved tab still exists in the session; if not, recover.
552
+ const tabList = active.tabs;
553
+ const tabAlive = !resolvedTabId || !tabList || tabList.some(t => t.id === resolvedTabId);
554
+ if (tabAlive) return { sessionId: pinnedSession, tabId: resolvedTabId };
555
+ console.error(`[MCP] Pinned tab ${resolvedTabId} not found in active session ${pinnedSession} — recovering`);
556
+ const terminalSessionId = pins.terminalSessionId || undefined;
557
+ const recovered = await request(
558
+ 'POST',
559
+ '/api/loop/recover-browser',
560
+ terminalSessionId ? { terminalSessionId } : {},
561
+ SESSION_CREATE_TIMEOUT
562
+ );
563
+ if (!recovered.error && recovered.sessionId) {
564
+ id.state.recoveredSessionId = recovered.sessionId as string;
565
+ id.state.recoveredTabId = (recovered.tabId as string) || null;
566
+ return { sessionId: id.state.recoveredSessionId, tabId: id.state.recoveredTabId || undefined };
567
+ }
568
+ return { sessionId: pinnedSession, tabId: resolvedTabId }; // fall through to old tab (will fail gracefully)
569
+ }
570
+
571
+ // Pinned session is gone — rejoin the SHARED loop browser with a fresh owned
572
+ // tab (not an isolated new browser), and adopt the NEW tabId. Forwarding the
573
+ // stale pinned tab into a different session would 404 every subsequent call.
574
+ console.error(`[MCP] Pinned browser session ${pinnedSession} is gone — recovering via shared loop browser`);
575
+ const terminalSessionId = pins.terminalSessionId || undefined;
576
+ const recovered = await request(
577
+ 'POST',
578
+ '/api/loop/recover-browser',
579
+ terminalSessionId ? { terminalSessionId } : {},
580
+ SESSION_CREATE_TIMEOUT
581
+ );
582
+ if (recovered.error || !recovered.sessionId) {
583
+ return { error: `Pinned browser session ${pinnedSession} is no longer available and recovery failed: ${recovered.error || 'no session returned'}` };
584
+ }
585
+ id.state.recoveredSessionId = recovered.sessionId as string;
586
+ id.state.recoveredTabId = (recovered.tabId as string) || null;
587
+ return { sessionId: id.state.recoveredSessionId, tabId: id.state.recoveredTabId || undefined };
77
588
  }
78
589
 
79
590
  if (sessionId) {
80
- // Trust the server — it will 404 if the session doesn't exist.
81
- // Skipping the extra GET /api/browser/sessions verification call saves a full round-trip.
82
- return { sessionId };
591
+ // Checked above against the default browser; the server 404s a session that
592
+ // does not exist. The route cache spares the check on the next calls.
593
+ id.state.affinitySessionId = sessionId;
594
+ return { sessionId, tabId: resolvedTabId };
595
+ }
596
+
597
+ // Reuse the dedicated tab THIS caller already acquired in the shared
598
+ // browser (set by a prior acquireInteractive). The X-Synabun-Terminal header routes
599
+ // tab-less calls to it, but returning the tabId keeps routing robust if the header
600
+ // is ever dropped. Re-acquired below if the underlying session has since died.
601
+ if (!sessionId && id.state.interactiveSessionId) {
602
+ if (isBrowserFastMode()) {
603
+ // Fast mode trusts the cache without a verify round-trip.
604
+ return { sessionId: id.state.interactiveSessionId, tabId: resolvedTabId || id.state.interactiveTabId || undefined };
605
+ }
606
+ const check = await request('GET', '/api/browser/sessions');
607
+ const alive = ((check.sessions || []) as BrowserSessionInfo[]).find(s => s.id === id.state.interactiveSessionId);
608
+ if (alive && servesDefault(alive)) return { sessionId: id.state.interactiveSessionId, tabId: resolvedTabId || id.state.interactiveTabId || undefined };
609
+ id.state.interactiveSessionId = null;
610
+ id.state.interactiveTabId = null; // session gone — fall through to re-acquire
611
+ }
612
+
613
+ // Codex fast mode favors one direct create call on first navigate. This avoids
614
+ // the list-sessions round trip that dominates short browser flows. If the profile
615
+ // is persistent, a second launchPersistentContext fails with "profile is locked" —
616
+ // fall back to acquiring a dedicated tab in the shared browser instead.
617
+ if (autoCreate && isBrowserFastMode()) {
618
+ const created = await request('POST', '/api/browser/sessions', {
619
+ url: autoCreate.url || 'about:blank',
620
+ }, SESSION_CREATE_TIMEOUT);
621
+ if (created.error) {
622
+ if (/lock/i.test(created.error)) return acquireInteractive(id, autoCreate.url);
623
+ return { error: `Failed to auto-create session: ${created.error}` };
624
+ }
625
+ return adoptCreatedSession(id, created, resolvedTabId);
83
626
  }
84
627
 
85
- // List sessions
628
+ // List sessions (single GET used for both affinity check and auto-selection)
86
629
  const data = await request('GET', '/api/browser/sessions');
87
630
  if (data.error) return { error: data.error };
88
- const sessions = (data.sessions || []) as BrowserSessionInfo[];
631
+ const allSessions = (data.sessions || []) as BrowserSessionInfo[];
632
+
633
+ // Check affinity — reuse session this caller previously used/created
634
+ if (id.state.affinitySessionId) {
635
+ const affinityAlive = allSessions.find(s => s.id === id.state.affinitySessionId);
636
+ if (affinityAlive && servesDefault(affinityAlive)) {
637
+ return { sessionId: id.state.affinitySessionId, tabId: resolvedTabId };
638
+ }
639
+ id.state.affinitySessionId = null; // session gone (or not the default browser), clear affinity
640
+ }
641
+
642
+ // Interactive sessions (no pinned env var) must not grab loop/agent/interactive-owned
643
+ // sessions. Pinned sessions already returned above; explicit sessionId trusted above.
644
+ // Never adopt or auto-select a browser that is not the default one for normal pages.
645
+ const sessions = allSessions.filter(s => !s.loopOwned && !s.agentOwned && !s.interactiveOwned && servesDefault(s));
646
+
647
+ // Under a persistent profile, a second launchPersistentContext fails with "profile is
648
+ // locked", and any owned session means a shared persistent browser is already running.
649
+ // In those cases, don't try to spawn a separate browser — join the ONE shared browser
650
+ // with a dedicated, owned tab. For a clean profile with no owners, keep the historical
651
+ // separate-browser behavior (no lock, no regression).
652
+ const lockRisk = allSessions.some(s => s.persistent || s.loopOwned || s.agentOwned || s.interactiveOwned);
653
+ const autoCreateSession = async (): Promise<{ sessionId: string; tabId?: string } | { error: string }> => {
654
+ if (lockRisk) return acquireInteractive(id, autoCreate?.url);
655
+ const created = await request('POST', '/api/browser/sessions', {
656
+ url: autoCreate?.url || 'about:blank',
657
+ }, SESSION_CREATE_TIMEOUT);
658
+ if (created.error) {
659
+ if (/lock/i.test(created.error)) return acquireInteractive(id, autoCreate?.url);
660
+ return { error: `Failed to auto-create session: ${created.error}` };
661
+ }
662
+ return adoptCreatedSession(id, created, resolvedTabId);
663
+ };
664
+
665
+ // If autoCreate is available (browser_navigate) and unowned sessions exist but none
666
+ // are ours (no affinity), create/acquire our own instead of hijacking another caller's.
667
+ // This prevents sidepanel from grabbing the CLI's session and vice versa.
668
+ if (sessions.length > 0 && autoCreate) {
669
+ return autoCreateSession();
670
+ }
89
671
 
90
672
  if (sessions.length === 1) {
91
- return { sessionId: sessions[0].id };
673
+ // No autoCreate — caller wants to interact with an existing page (click, snapshot, etc.).
674
+ // Adopting is ONLY safe for a pristine, unowned session (e.g. a page the user opened
675
+ // manually in the NI UI). If ANOTHER automation already owns a tab here (it plain-created
676
+ // the session or acquired a tab — see _tabOwners), adopting makes us a tab-less freeloader
677
+ // that FAILS CLOSED (404) the moment the session goes multi-tab — the sidepanel-vs-CLI
678
+ // collision. In that case acquire our OWN dedicated tab in the shared browser instead.
679
+ const myTerminalId = terminalIdFor(id);
680
+ const loneOwners = sessions[0].tabOwners || {};
681
+ const ownedByOther = Object.keys(loneOwners).some(k => k !== id.clientId && k !== myTerminalId);
682
+ if (ownedByOther) {
683
+ return acquireInteractive(id, autoCreate?.url);
684
+ }
685
+ id.state.affinitySessionId = sessions[0].id;
686
+ return { sessionId: sessions[0].id, tabId: resolvedTabId };
92
687
  }
93
688
 
94
689
  if (sessions.length === 0) {
95
690
  if (autoCreate) {
96
- const created = await request('POST', '/api/browser/sessions', {
97
- url: autoCreate.url || 'about:blank',
98
- });
99
- if (created.error) return { error: `Failed to auto-create session: ${created.error}` };
100
- return { sessionId: created.sessionId as string };
691
+ return autoCreateSession();
692
+ }
693
+ const ownedCount = allSessions.length - sessions.length;
694
+ if (ownedCount > 0) {
695
+ // Active automations hold the shared browser and there is no unowned session for
696
+ // us. Don't grab their tab and don't error — acquire our OWN dedicated tab so even
697
+ // a tab-less first tool (snapshot/click) is isolated. about:blank is fine; a later
698
+ // navigate reuses this same owned tab via the X-Synabun-Terminal routing.
699
+ return acquireInteractive(id, undefined);
101
700
  }
102
701
  return { error: 'No browser sessions open. Use browser_session to create one first, or use browser_navigate with a URL to auto-create.' };
103
702
  }
104
703
 
105
- // Multiple sessions — require explicit ID
704
+ // Multiple available sessions — require explicit ID
106
705
  const list = sessions.map(s => ` ${s.id} — ${s.title || s.url}`).join('\n');
107
706
  return { error: `Multiple browser sessions open. Specify sessionId:\n${list}` };
108
707
  }
109
708
 
709
+ /**
710
+ * Per-caller route cache. Only the server can declare an action safe to retry.
711
+ * `platform` (or a withPlatformRoute scope) marks a platform tool call: like a
712
+ * navigation, it only runs in the default browser.
713
+ */
714
+ export async function resolveSession(
715
+ sessionId?: string,
716
+ autoCreate?: { url?: string },
717
+ tabId?: string,
718
+ opts: { platform?: boolean } = {},
719
+ ): Promise<BrowserRoute | { error: string }> {
720
+ const context = batchContext.getStore();
721
+ if (context) return context.route;
722
+ const startedAt = performance.now();
723
+ const id = getIdentity();
724
+ const platform = opts.platform === true || inPlatformRoute();
725
+ const key = routeKey(id, sessionId, tabId, platform || !!autoCreate);
726
+ const cached = resolvedRoutes.get(id);
727
+ if (isBrowserV2Enabled() && !resolutionRefresh.getStore() && cached?.key === key) {
728
+ browserMetric({ phase: 'session-resolution', cached: true, durationMs: performance.now() - startedAt });
729
+ return { ...cached.route };
730
+ }
731
+ const result = await resolveSessionUncached(sessionId, autoCreate, tabId, platform);
732
+ if (!('error' in result)) resolvedRoutes.set(id, { key: routeKey(id, sessionId, tabId, platform || !!autoCreate), route: { ...result }, sessionId, tabId, autoCreate, platform });
733
+ browserMetric({ phase: 'session-resolution', cached: false, durationMs: performance.now() - startedAt });
734
+ return result;
735
+ }
736
+
737
+ /** Batches require a concrete tab owned by this caller, verified once up front. */
738
+ export async function resolveBatchRoute(sessionId?: string, tabId?: string): Promise<BrowserRoute | { error: string }> {
739
+ const resolved = await resolveSession(sessionId, undefined, tabId);
740
+ if ('error' in resolved) return resolved;
741
+ const result = await listSessions();
742
+ if (result.error) return { error: result.error };
743
+ const session = (result.sessions as BrowserSessionInfo[] | undefined)?.find(s => s.id === resolved.sessionId);
744
+ const id = getIdentity();
745
+ const ownedTabs = [session?.tabOwners?.[terminalIdFor(id)], session?.tabOwners?.[id.clientId]].filter(Boolean);
746
+ const ownedTab = resolved.tabId || ownedTabs[0];
747
+ if (!ownedTab || !ownedTabs.includes(ownedTab)) {
748
+ return { error: 'browser_batch requires a tab owned by this caller. Use browser_navigate to acquire a tab first.' };
749
+ }
750
+ return { sessionId: resolved.sessionId, tabId: ownedTab };
751
+ }
752
+
110
753
  // ── Cache invalidation ──
111
754
 
112
- export async function invalidateCache(reason: string): Promise<void> {
755
+ export async function invalidateCache(reason: string, id?: string): Promise<void> {
113
756
  try {
114
- await request('POST', '/api/cache/invalidate', { reason });
757
+ // Passing the changed memory id lets the NI patch its link cache
758
+ // incrementally instead of recomputing all pairwise links.
759
+ await request('POST', '/api/cache/invalidate', { reason, id });
115
760
  } catch {
116
761
  // Fire-and-forget — don't block MCP tools if Neural Interface is down
117
762
  }
@@ -126,7 +771,7 @@ export async function listSessions(): Promise<NiResponse> {
126
771
  export async function createSession(url?: string): Promise<NiResponse> {
127
772
  return request('POST', '/api/browser/sessions', {
128
773
  url: url || 'about:blank',
129
- });
774
+ }, SESSION_CREATE_TIMEOUT);
130
775
  }
131
776
 
132
777
  export async function closeSession(sessionId: string): Promise<NiResponse> {
@@ -135,104 +780,300 @@ export async function closeSession(sessionId: string): Promise<NiResponse> {
135
780
 
136
781
  // ── Navigation ──
137
782
 
138
- export async function navigate(sessionId: string, url: string): Promise<NiResponse> {
139
- return request('POST', `/api/browser/sessions/${sessionId}/navigate`, { url }, LONG_TIMEOUT);
783
+ export async function navigate(
784
+ sessionId: string,
785
+ url: string,
786
+ tabId?: string,
787
+ returnSnapshot?: { mode?: string; selector?: string; viewport?: boolean },
788
+ snapshot?: 'diff' | 'full' | 'none',
789
+ snapshotOptions?: { baselineId?: string; snapshotMaxChars?: number },
790
+ assist?: { semanticContext?: boolean },
791
+ ): Promise<NiResponse> {
792
+ return request('POST', `/api/browser/sessions/${sessionId}/navigate`, {
793
+ url,
794
+ ...(isBrowserCompactMode() && { compact: true }),
795
+ ...(snapshot && { snapshot }),
796
+ ...(isBrowserV2Enabled() && { snapshotMaxChars: 12000 }),
797
+ ...snapshotOptions,
798
+ ...(returnSnapshot && { returnSnapshot }),
799
+ ...(assist?.semanticContext && { semanticContext: true }),
800
+ ...(tabId && { tabId }),
801
+ }, LONG_TIMEOUT);
140
802
  }
141
803
 
142
- export async function goBack(sessionId: string): Promise<NiResponse> {
143
- return request('POST', `/api/browser/sessions/${sessionId}/back`);
804
+ export async function goBack(sessionId: string, tabId?: string): Promise<NiResponse> {
805
+ return request('POST', `/api/browser/sessions/${sessionId}/back`, { ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
144
806
  }
145
807
 
146
- export async function goForward(sessionId: string): Promise<NiResponse> {
147
- return request('POST', `/api/browser/sessions/${sessionId}/forward`);
808
+ export async function goForward(sessionId: string, tabId?: string): Promise<NiResponse> {
809
+ return request('POST', `/api/browser/sessions/${sessionId}/forward`, { ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
148
810
  }
149
811
 
150
- export async function reload(sessionId: string): Promise<NiResponse> {
151
- return request('POST', `/api/browser/sessions/${sessionId}/reload`, undefined, LONG_TIMEOUT);
812
+ export async function reload(sessionId: string, tabId?: string): Promise<NiResponse> {
813
+ return request('POST', `/api/browser/sessions/${sessionId}/reload`, { ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) }, LONG_TIMEOUT);
152
814
  }
153
815
 
154
- // ── Interaction (selector-based) ──
816
+ // ── Interaction (ref- or selector-based) ──
155
817
 
156
- export async function click(sessionId: string, selector: string, nthMatch?: number): Promise<NiResponse> {
157
- return request('POST', `/api/browser/sessions/${sessionId}/click`, { selector, ...(nthMatch !== undefined && { nthMatch }) });
818
+ export async function click(
819
+ sessionId: string,
820
+ selector: string | undefined,
821
+ nthMatch?: number,
822
+ tabId?: string,
823
+ textHint?: string,
824
+ returnSnapshot?: { mode?: string; selector?: string; viewport?: boolean },
825
+ ref?: string,
826
+ snapshot?: 'diff' | 'full' | 'none',
827
+ snapshotOptions?: { baselineId?: string; snapshotMaxChars?: number },
828
+ assist?: boolean,
829
+ ): Promise<NiResponse> {
830
+ return request('POST', `/api/browser/sessions/${sessionId}/click`, {
831
+ ...(selector && { selector }),
832
+ ...(ref && { ref }),
833
+ ...(nthMatch !== undefined && { nthMatch }),
834
+ ...(textHint && { textHint }),
835
+ ...(assist && { assist: true }),
836
+ ...(isBrowserCompactMode() && { compact: true }),
837
+ ...(snapshot && { snapshot }),
838
+ ...(isBrowserV2Enabled() && { snapshotMaxChars: 6000 }),
839
+ ...snapshotOptions,
840
+ ...(returnSnapshot && { returnSnapshot }),
841
+ ...(tabId && { tabId }),
842
+ }, CLICK_TIMEOUT);
158
843
  }
159
844
 
160
- export async function fill(sessionId: string, selector: string, value: string, nthMatch?: number): Promise<NiResponse> {
161
- return request('POST', `/api/browser/sessions/${sessionId}/fill`, { selector, value, ...(nthMatch !== undefined && { nthMatch }) });
845
+ export async function fill(sessionId: string, selector: string | undefined, value: string, nthMatch?: number, tabId?: string, textHint?: string, ref?: string, assist?: boolean): Promise<NiResponse> {
846
+ return request('POST', `/api/browser/sessions/${sessionId}/fill`, { ...(selector && { selector }), ...(ref && { ref }), value, ...(assist && { assist: true }), ...(nthMatch !== undefined && { nthMatch }), ...(textHint && { textHint }), ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
162
847
  }
163
848
 
164
- export async function type(sessionId: string, selector: string | null, text: string, nthMatch?: number): Promise<NiResponse> {
165
- return request('POST', `/api/browser/sessions/${sessionId}/type`, { selector, text, ...(nthMatch !== undefined && { nthMatch }) });
849
+ export async function type(sessionId: string, selector: string | null, text: string, nthMatch?: number, tabId?: string, textHint?: string, mode?: 'sequential' | 'insert' | 'paragraphs', ref?: string, assist?: boolean): Promise<NiResponse> {
850
+ // An explicit mode (including 'paragraphs') is always honored; only the unset case falls back to fast-mode insert.
851
+ const resolvedMode = mode || (isBrowserFastMode() ? 'insert' : 'sequential');
852
+ return request('POST', `/api/browser/sessions/${sessionId}/type`, { selector, ...(ref && { ref }), text, mode: resolvedMode, ...(assist && { assist: true }), ...(nthMatch !== undefined && { nthMatch }), ...(textHint && { textHint }), ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
166
853
  }
167
854
 
168
- export async function hover(sessionId: string, selector: string, nthMatch?: number): Promise<NiResponse> {
169
- return request('POST', `/api/browser/sessions/${sessionId}/hover`, { selector, ...(nthMatch !== undefined && { nthMatch }) });
855
+ export async function hover(sessionId: string, selector: string | undefined, nthMatch?: number, tabId?: string, textHint?: string, ref?: string, assist?: boolean): Promise<NiResponse> {
856
+ return request('POST', `/api/browser/sessions/${sessionId}/hover`, { ...(selector && { selector }), ...(ref && { ref }), ...(assist && { assist: true }), ...(nthMatch !== undefined && { nthMatch }), ...(textHint && { textHint }), ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
170
857
  }
171
858
 
172
- export async function selectOption(sessionId: string, selector: string, value: string, nthMatch?: number): Promise<NiResponse> {
173
- return request('POST', `/api/browser/sessions/${sessionId}/select`, { selector, value, ...(nthMatch !== undefined && { nthMatch }) });
859
+ export async function selectOption(sessionId: string, selector: string | undefined, value: string, nthMatch?: number, tabId?: string, ref?: string, textHint?: string, assist?: boolean): Promise<NiResponse> {
860
+ return request('POST', `/api/browser/sessions/${sessionId}/select`, { ...(selector && { selector }), ...(ref && { ref }), value, ...(textHint && { textHint }), ...(assist && { assist: true }), ...(nthMatch !== undefined && { nthMatch }), ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
174
861
  }
175
862
 
176
- export async function pressKey(sessionId: string, key: string): Promise<NiResponse> {
177
- return request('POST', `/api/browser/sessions/${sessionId}/press`, { key });
863
+ export async function pressKey(sessionId: string, key: string, tabId?: string): Promise<NiResponse> {
864
+ return request('POST', `/api/browser/sessions/${sessionId}/press`, { key, ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) });
178
865
  }
179
866
 
180
867
  export async function scroll(
181
868
  sessionId: string,
182
- opts: { direction: string; distance?: number; selector?: string }
869
+ opts: { direction: string; distance?: number; selector?: string; ref?: string; snapshot?: 'diff' | 'full' | 'none'; baselineId?: string; snapshotMaxChars?: number; returnSnapshot?: { mode?: string; selector?: string; viewport?: boolean } },
870
+ tabId?: string
183
871
  ): Promise<NiResponse> {
184
- return request('POST', `/api/browser/sessions/${sessionId}/scroll`, opts as Record<string, unknown>);
872
+ return request('POST', `/api/browser/sessions/${sessionId}/scroll`, { ...(isBrowserV2Enabled() && { snapshotMaxChars: 12000 }), ...opts, ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) } as Record<string, unknown>, LONG_TIMEOUT);
185
873
  }
186
874
 
187
875
  export async function upload(
188
876
  sessionId: string,
189
- selector: string,
877
+ selector: string | undefined,
190
878
  filePaths: string[],
191
- nthMatch?: number
879
+ nthMatch?: number,
880
+ tabId?: string,
881
+ ref?: string,
882
+ textHint?: string,
883
+ assist?: boolean,
192
884
  ): Promise<NiResponse> {
193
- return request('POST', `/api/browser/sessions/${sessionId}/upload`, { selector, filePaths, ...(nthMatch !== undefined && { nthMatch }) }, LONG_TIMEOUT);
885
+ return request('POST', `/api/browser/sessions/${sessionId}/upload`, { ...(selector && { selector }), ...(ref && { ref }), filePaths, ...(textHint && { textHint }), ...(assist && { assist: true }), ...(nthMatch !== undefined && { nthMatch }), ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) }, LONG_TIMEOUT);
194
886
  }
195
887
 
196
888
  // ── Observation ──
197
889
 
198
- export async function snapshot(sessionId: string, selector?: string): Promise<NiResponse> {
199
- if (selector) return request('POST', `/api/browser/sessions/${sessionId}/snapshot`, { selector });
200
- return request('GET', `/api/browser/sessions/${sessionId}/snapshot`);
890
+ export async function snapshot(
891
+ sessionId: string,
892
+ selector?: string,
893
+ tabId?: string,
894
+ opts?: { mode?: string; viewport?: boolean; depth?: number; diff?: boolean; force?: boolean; maxChars?: number; baselineId?: string; includeFullText?: boolean }
895
+ ): Promise<NiResponse> {
896
+ const hasOpts = !!(opts && (opts.mode || opts.viewport || opts.depth || opts.diff || opts.force || opts.maxChars || opts.baselineId || opts.includeFullText));
897
+ // Always POST when selector OR opts present (POST supports a body); GET only for bare defaults.
898
+ if (selector || hasOpts) {
899
+ return request('POST', `/api/browser/sessions/${sessionId}/snapshot`, {
900
+ ...(selector && { selector }),
901
+ ...(opts?.mode && { mode: opts.mode }),
902
+ ...(opts?.viewport && { viewport: true }),
903
+ ...(opts?.depth && { depth: opts.depth }),
904
+ ...(opts?.diff && { diff: true }),
905
+ ...(opts?.force && { force: true }),
906
+ ...(opts?.maxChars && { maxChars: opts.maxChars }),
907
+ ...(opts?.baselineId && { baselineId: opts.baselineId }),
908
+ // Intent ranking parses every ref of this capture even when only a diff is shown; never a second capture.
909
+ ...(opts?.includeFullText && { includeFullText: true }),
910
+ ...(tabId && { tabId }),
911
+ }, LONG_TIMEOUT);
912
+ }
913
+ const qs = tabId ? `?tabId=${tabId}` : '';
914
+ return request('GET', `/api/browser/sessions/${sessionId}/snapshot${qs}`);
201
915
  }
202
916
 
203
- export async function getContent(sessionId: string): Promise<NiResponse> {
204
- return request('GET', `/api/browser/sessions/${sessionId}/content`);
917
+ export async function getContent(sessionId: string, tabId?: string, opts?: { maxChars?: number; offset?: number; selector?: string }): Promise<NiResponse> {
918
+ const params = new URLSearchParams();
919
+ if (tabId) params.set('tabId', tabId);
920
+ if (opts?.maxChars) params.set('maxChars', String(opts.maxChars));
921
+ if (opts?.offset) params.set('offset', String(opts.offset));
922
+ if (opts?.selector) params.set('selector', opts.selector);
923
+ const qs = params.size ? `?${params}` : '';
924
+ return request('GET', `/api/browser/sessions/${sessionId}/content${qs}`);
205
925
  }
206
926
 
207
- export async function getMarkdown(sessionId: string): Promise<NiResponse> {
208
- return request('GET', `/api/browser/sessions/${sessionId}/markdown`, undefined, LONG_TIMEOUT);
927
+ export async function getMarkdown(sessionId: string, tabId?: string, opts?: { maxChars?: number; offset?: number; selector?: string }): Promise<NiResponse> {
928
+ const params = new URLSearchParams();
929
+ if (tabId) params.set('tabId', tabId);
930
+ if (opts?.maxChars) params.set('maxChars', String(opts.maxChars));
931
+ if (opts?.offset) params.set('offset', String(opts.offset));
932
+ if (opts?.selector) params.set('selector', opts.selector);
933
+ const qs = params.size ? `?${params}` : '';
934
+ return request('GET', `/api/browser/sessions/${sessionId}/markdown${qs}`, undefined, LONG_TIMEOUT);
209
935
  }
210
936
 
211
937
  export async function fetchMarkdown(url: string, timeout?: number): Promise<NiResponse> {
212
938
  return request('POST', '/api/fetch-markdown', { url, timeout }, LONG_TIMEOUT);
213
939
  }
214
940
 
215
- export async function screenshot(sessionId: string): Promise<NiResponse> {
216
- return request('GET', `/api/browser/sessions/${sessionId}/screenshot-base64`);
941
+ export interface ScreenshotOptions {
942
+ maxWidth?: number;
943
+ quality?: number;
944
+ /** Viewport size for this capture only (CDP device metrics, cleared right after). */
945
+ width?: number;
946
+ height?: number;
947
+ fullPage?: boolean;
948
+ format?: 'png' | 'jpeg';
949
+ save?: boolean;
950
+ path?: string;
951
+ }
952
+
953
+ export async function screenshot(sessionId: string, tabId?: string, opts?: ScreenshotOptions): Promise<NiResponse> {
954
+ const params = new URLSearchParams();
955
+ if (tabId) params.set('tabId', tabId);
956
+ if (opts?.maxWidth !== undefined) params.set('maxWidth', String(opts.maxWidth));
957
+ if (opts?.quality !== undefined) params.set('quality', String(opts.quality));
958
+ if (opts?.width !== undefined) params.set('width', String(opts.width));
959
+ if (opts?.height !== undefined) params.set('height', String(opts.height));
960
+ if (opts?.fullPage) params.set('fullPage', '1');
961
+ if (opts?.format) params.set('format', opts.format);
962
+ if (opts?.save) params.set('save', '1');
963
+ if (opts?.path) params.set('path', opts.path);
964
+ const qs = params.size ? `?${params}` : '';
965
+ // A resized or full-page capture (and a saved file) takes longer than a plain one.
966
+ return request('GET', `/api/browser/sessions/${sessionId}/screenshot-base64${qs}`, undefined, LONG_TIMEOUT);
967
+ }
968
+
969
+ /** The tab's buffered console messages and page errors (browser_console). */
970
+ export async function consoleMessages(
971
+ sessionId: string,
972
+ tabId?: string,
973
+ opts?: { level?: string; since?: string; limit?: number; clear?: boolean },
974
+ ): Promise<NiResponse> {
975
+ const params = new URLSearchParams();
976
+ if (tabId) params.set('tabId', tabId);
977
+ if (opts?.level) params.set('level', opts.level);
978
+ if (opts?.since) params.set('since', opts.since);
979
+ if (opts?.limit !== undefined) params.set('limit', String(opts.limit));
980
+ if (opts?.clear) params.set('clear', '1');
981
+ const qs = params.size ? `?${params}` : '';
982
+ return request('GET', `/api/browser/sessions/${sessionId}/console${qs}`);
217
983
  }
218
984
 
219
985
  // ── Advanced ──
220
986
 
221
- export async function evaluate(sessionId: string, script: string): Promise<NiResponse> {
222
- return request('POST', `/api/browser/sessions/${sessionId}/evaluate`, { script }, LONG_TIMEOUT);
987
+ export async function evaluate(sessionId: string, script: string, tabId?: string): Promise<NiResponse> {
988
+ return request('POST', `/api/browser/sessions/${sessionId}/evaluate`, { script, ...(tabId && { tabId }) }, LONG_TIMEOUT);
989
+ }
990
+
991
+ export interface BrowserExtractOptions {
992
+ scrolls?: number;
993
+ minItems?: number;
994
+ maxItems?: number;
995
+ dedupeKeys: string[];
996
+ scrollTarget?: string;
997
+ scrollDistance?: number;
998
+ scrollDirection?: 1 | -1;
999
+ settleMs?: number;
1000
+ scrollIfEmpty?: boolean;
1001
+ fields?: string[];
1002
+ maxChars?: number;
1003
+ timeoutMs?: number;
1004
+ }
1005
+
1006
+ export async function extract(sessionId: string, script: string, tabId: string | undefined, opts: BrowserExtractOptions): Promise<NiResponse> {
1007
+ return request('POST', `/api/browser/sessions/${sessionId}/extract`, {
1008
+ script, ...opts, ...(tabId && { tabId }),
1009
+ }, Math.min(30_000, opts.timeoutMs || 15_000) + 2_000);
1010
+ }
1011
+
1012
+ // Read-only structured state of the X/Twitter composer (quote card, submit button, modal,
1013
+ // stale draft). The detection script lives server-side so the loop publish gate shares it.
1014
+ export async function xComposeState(sessionId: string, tabId?: string): Promise<NiResponse> {
1015
+ return request('POST', `/api/browser/sessions/${sessionId}/x-compose-state`, { ...(tabId && { tabId }) }, LONG_TIMEOUT);
1016
+ }
1017
+
1018
+ // ── Browser assistance (read-only context; the judgments run in this process, never in the Neural Interface) ──
1019
+
1020
+ /** Bounded, value-free description of the current page. Read-only: safe to call after any outcome. */
1021
+ export async function semanticContext(
1022
+ sessionId: string,
1023
+ tabId?: string,
1024
+ opts?: { purpose?: 'page-state' | 'empty-extractor' | 'social' | 'batch'; scope?: string; social?: boolean },
1025
+ ): Promise<NiResponse> {
1026
+ return request('POST', `/api/browser/sessions/${sessionId}/semantic-context`, {
1027
+ ...(opts?.purpose && { purpose: opts.purpose }),
1028
+ ...(opts?.scope && { scope: opts.scope }),
1029
+ ...(opts?.social && { social: true }),
1030
+ ...(tabId && { tabId }),
1031
+ }, 6000);
1032
+ }
1033
+
1034
+ /**
1035
+ * The one auto-heal click. It names a candidate inside a context the server
1036
+ * minted after a click it confirmed never started; the selector and the
1037
+ * fingerprint stay on the server. Deliberately `requestOnce`: route recovery
1038
+ * could re-issue this on another tab, and a heal is never sent twice.
1039
+ */
1040
+ export async function clickAutoHeal(
1041
+ sessionId: string,
1042
+ tabId: string | undefined,
1043
+ pick: { contextId: string; candidateId: string },
1044
+ snapshot?: 'diff' | 'full' | 'none',
1045
+ ): Promise<NiResponse> {
1046
+ return requestOnce('POST', `/api/browser/sessions/${sessionId}/click`, {
1047
+ autoHeal: { contextId: pick.contextId, candidateId: pick.candidateId },
1048
+ ...(isBrowserCompactMode() && { compact: true }),
1049
+ ...(snapshot && { snapshot }),
1050
+ ...(isBrowserV2Enabled() && { snapshotMaxChars: 6000 }),
1051
+ ...(tabId && { tabId }),
1052
+ }, 15_000);
1053
+ }
1054
+
1055
+ // Upload a local image file to BlueSky as a blob, using the page's own session
1056
+ // token. The Neural Interface reads the file from disk (the MCP page context
1057
+ // can't), pulls accessJwt/pdsUrl from the bsky.app tab, and POSTs the bytes to
1058
+ // com.atproto.repo.uploadBlob. Returns { ok, blob } (the AT Protocol blob ref).
1059
+ export async function uploadBlueskyBlob(sessionId: string, filePath: string, tabId?: string): Promise<NiResponse> {
1060
+ return request('POST', `/api/browser/sessions/${sessionId}/bluesky/upload-blob`, { filePath, ...(tabId && { tabId }) }, LONG_TIMEOUT);
223
1061
  }
224
1062
 
225
1063
  export async function waitFor(
226
1064
  sessionId: string,
227
- opts: { selector?: string; state?: string; loadState?: string; timeout?: number }
1065
+ opts: { selector?: string; state?: string; loadState?: string; timeout?: number },
1066
+ tabId?: string
228
1067
  ): Promise<NiResponse> {
229
- return request('POST', `/api/browser/sessions/${sessionId}/wait`, opts, LONG_TIMEOUT);
1068
+ return request('POST', `/api/browser/sessions/${sessionId}/wait`, { ...opts, ...(isBrowserCompactMode() && { compact: true }), ...(tabId && { tabId }) }, LONG_TIMEOUT);
230
1069
  }
231
1070
 
232
1071
  // ── Whiteboard ──
233
1072
 
234
- export async function getWhiteboard(): Promise<NiResponse> {
235
- return request('GET', '/api/whiteboard');
1073
+ /** Read the board. `images: false` asks the server to replace image dataUrls
1074
+ * with their byte size (the tools never need the base64 payload). */
1075
+ export async function getWhiteboard(opts: { images?: boolean } = {}): Promise<NiResponse> {
1076
+ return request('GET', opts.images === false ? '/api/whiteboard?images=0' : '/api/whiteboard');
236
1077
  }
237
1078
 
238
1079
  export async function addWhiteboardElements(
@@ -243,6 +1084,14 @@ export async function addWhiteboardElements(
243
1084
  return request('POST', '/api/whiteboard/elements', { elements, coordMode, layout } as Record<string, unknown>);
244
1085
  }
245
1086
 
1087
+ /** Atomic update/remove ops (one save, one broadcast, per-op results). */
1088
+ export async function batchWhiteboardOps(
1089
+ ops: Record<string, unknown>[],
1090
+ coordMode?: string
1091
+ ): Promise<NiResponse> {
1092
+ return request('POST', '/api/whiteboard/elements/batch', { ops, coordMode } as Record<string, unknown>);
1093
+ }
1094
+
246
1095
  export async function updateWhiteboardElement(
247
1096
  id: string,
248
1097
  updates: Record<string, unknown>,
@@ -259,8 +1108,9 @@ export async function clearWhiteboard(): Promise<NiResponse> {
259
1108
  return request('POST', '/api/whiteboard/clear');
260
1109
  }
261
1110
 
262
- export async function whiteboardScreenshot(): Promise<NiResponse> {
263
- return request('GET', '/api/whiteboard/screenshot', undefined, 15_000);
1111
+ export async function whiteboardScreenshot(opts: { crop?: string } = {}): Promise<NiResponse> {
1112
+ const query = opts.crop === 'usable' ? '?crop=usable' : '';
1113
+ return request('GET', `/api/whiteboard/screenshot${query}`, undefined, 15_000);
264
1114
  }
265
1115
 
266
1116
  // ── Cards (Memory Card MCP integration) ──
@@ -333,6 +1183,60 @@ export async function gitBranches(path: string): Promise<NiResponse> {
333
1183
  return request('GET', `/api/terminal/branches?path=${encodeURIComponent(path)}`);
334
1184
  }
335
1185
 
1186
+ // ── Style Guide ──
1187
+
1188
+ // `projectPath` is any path inside a registered project; the server resolves it to the project root.
1189
+ function styleGuideQuery(params: Record<string, string | undefined | null>): string {
1190
+ const query = new URLSearchParams();
1191
+ for (const [key, value] of Object.entries(params)) if (value !== undefined && value !== null && value !== '') query.set(key, value);
1192
+ return query.toString();
1193
+ }
1194
+
1195
+ export async function getStyleGuide(projectPath: string): Promise<NiResponse> {
1196
+ return request('GET', `/api/style-guide?${styleGuideQuery({ projectPath })}`);
1197
+ }
1198
+
1199
+ /** The compact text agents get: { summary, tokensEstimate, saved, revision, projectPath }. */
1200
+ export async function getStyleGuideSummary(projectPath: string, taskClass?: string): Promise<NiResponse> {
1201
+ return request('GET', `/api/style-guide/summary?${styleGuideQuery({ projectPath, taskClass })}`);
1202
+ }
1203
+
1204
+ /** One export format as text: { format, contentType, filename, text, revision }. */
1205
+ export async function getStyleGuideExport(projectPath: string, format: string, theme?: string): Promise<NiResponse> {
1206
+ return request('GET', `/api/style-guide/export?${styleGuideQuery({ projectPath, format, theme, as: 'json' })}`);
1207
+ }
1208
+
1209
+ /** Write the project's artifacts now: { written: [{ format, path, changed }] }. */
1210
+ export async function writeStyleGuideExports(projectPath: string, formats?: string[]): Promise<NiResponse> {
1211
+ return request('POST', '/api/style-guide/export', { projectPath, ...(formats?.length ? { formats } : {}) });
1212
+ }
1213
+
1214
+ /** WCAG ratio + APCA for two colors (or aliases, with a project): { ratio, aa, aaa, aaLarge, apca, fg, bg }. */
1215
+ export async function styleGuideContrast(body: { fg: string; bg: string; size?: string; projectPath?: string; theme?: string }): Promise<NiResponse> {
1216
+ return request('POST', '/api/style-guide/contrast', body);
1217
+ }
1218
+
1219
+ /** An agent's only write to a guide: a merge patch the user accepts or rejects. { id, pending, ignored, diff } */
1220
+ export async function proposeStyleGuideChange(body: { projectPath: string; changes: Record<string, unknown>; reason: string; runId?: string; provider?: string; model?: string }): Promise<NiResponse> {
1221
+ const identity = getIdentity();
1222
+ const runId = effectivePins(identity).terminalSessionId || identity.state.ancestorPinnedTerminal;
1223
+ return request('POST', '/api/style-guide/proposals', { ...(!body.runId && runId ? { runId } : {}), ...body });
1224
+ }
1225
+
1226
+ /** The project's proposals (a bare array on success). */
1227
+ export async function listStyleGuideProposals(projectPath: string, status?: string): Promise<NiResponse> {
1228
+ return request('GET', `/api/style-guide/proposals?${styleGuideQuery({ projectPath, status })}`);
1229
+ }
1230
+
1231
+ /** Registered projects with their guide's status (a bare array on success). */
1232
+ export async function listStyleGuideProjects(): Promise<NiResponse> {
1233
+ return request('GET', '/api/style-guide/projects');
1234
+ }
1235
+
1236
+ export async function listProjects(): Promise<NiResponse> {
1237
+ return request('GET', '/api/projects');
1238
+ }
1239
+
336
1240
  // ── Image store ──
337
1241
 
338
1242
  export async function listImages(): Promise<NiResponse> {
@@ -342,3 +1246,243 @@ export async function listImages(): Promise<NiResponse> {
342
1246
  export async function deleteImage(filename: string): Promise<NiResponse> {
343
1247
  return request('DELETE', `/api/images/${encodeURIComponent(filename)}`);
344
1248
  }
1249
+
1250
+ // ── YouTube trailer pipeline (sourcing + download + API) ──
1251
+ // These delegate to the Neural Interface server, which holds the IGDB/Twitch
1252
+ // token, the Steam proxy, the yt-dlp binary, and the YouTube OAuth credentials.
1253
+
1254
+ const DOWNLOAD_TIMEOUT = 600_000; // trailer downloads can be large/slow
1255
+
1256
+ /**
1257
+ * Discover candidate trailers. `source` selects the discovery backend:
1258
+ * 'igdb' — query IGDB for games + Steam appid + YouTube trailer ids
1259
+ * 'steam' — resolve Steam appdetails movies[] for explicit appids/query
1260
+ * 'youtube' — search YouTube directly for recently published trailer videos
1261
+ * 'auto' — IGDB discovery enriched with Steam mp4 URLs (default)
1262
+ */
1263
+ export async function youtubeDiscover(body: {
1264
+ source?: 'igdb' | 'steam' | 'youtube' | 'auto';
1265
+ query?: string;
1266
+ appids?: number[];
1267
+ limit?: number;
1268
+ filters?: Record<string, unknown>;
1269
+ }): Promise<NiResponse> {
1270
+ return request('POST', '/api/sources/discover', body as Record<string, unknown>, LONG_TIMEOUT);
1271
+ }
1272
+
1273
+ /**
1274
+ * Download a trailer to disk. Prefers a direct Steam mp4 URL; falls back to
1275
+ * yt-dlp on a YouTube id/url. Returns `{ filePath, bytes, source }`.
1276
+ */
1277
+ export async function youtubeDownload(body: {
1278
+ steamMp4Url?: string;
1279
+ ytId?: string;
1280
+ url?: string;
1281
+ appid?: number;
1282
+ outputDir?: string;
1283
+ filename?: string;
1284
+ quality?: string;
1285
+ useCookies?: boolean;
1286
+ }): Promise<NiResponse> {
1287
+ return request('POST', '/api/youtube/download', body as Record<string, unknown>, DOWNLOAD_TIMEOUT);
1288
+ }
1289
+
1290
+ export async function youtubeGetConfig(): Promise<NiResponse> {
1291
+ return request('GET', '/api/youtube/config');
1292
+ }
1293
+
1294
+ export async function youtubeSetConfig(config: Record<string, unknown>): Promise<NiResponse> {
1295
+ return request('PUT', '/api/youtube/config', config);
1296
+ }
1297
+
1298
+ /** Test configured credentials. `target`: 'youtube' | 'igdb' | 'steam' | 'all'. */
1299
+ export async function youtubeTest(target?: string): Promise<NiResponse> {
1300
+ return request('POST', '/api/youtube/test', target ? { target } : {}, LONG_TIMEOUT);
1301
+ }
1302
+
1303
+ /** Read-only analytics via the YouTube Data/Analytics API (OAuth). */
1304
+ export async function youtubeAnalytics(body: {
1305
+ videoId?: string;
1306
+ channelId?: string;
1307
+ metrics?: string[];
1308
+ startDate?: string;
1309
+ endDate?: string;
1310
+ }): Promise<NiResponse> {
1311
+ return request('POST', '/api/youtube/analytics', body as Record<string, unknown>, LONG_TIMEOUT);
1312
+ }
1313
+
1314
+ // ── MoreLogin (anti-detect browser) — proxied to the Neural Interface ──
1315
+
1316
+ export async function moreloginStatus(): Promise<NiResponse> {
1317
+ return request('GET', '/api/morelogin/status', undefined, LONG_TIMEOUT);
1318
+ }
1319
+
1320
+ export async function moreloginProfiles(): Promise<NiResponse> {
1321
+ return request('GET', '/api/morelogin/profiles', undefined, LONG_TIMEOUT);
1322
+ }
1323
+
1324
+ export async function moreloginCreate(name?: string): Promise<NiResponse> {
1325
+ return request('POST', '/api/morelogin/profiles', name ? { name } : {}, LONG_TIMEOUT);
1326
+ }
1327
+
1328
+ export async function moreloginStart(envId: string): Promise<NiResponse> {
1329
+ return request('POST', '/api/morelogin/start', { envId }, SESSION_CREATE_TIMEOUT);
1330
+ }
1331
+
1332
+ export async function moreloginStop(envId: string): Promise<NiResponse> {
1333
+ return request('POST', '/api/morelogin/stop', { envId }, LONG_TIMEOUT);
1334
+ }
1335
+
1336
+ export async function moreloginUseDefault(envId: string | null): Promise<NiResponse> {
1337
+ return request('POST', '/api/morelogin/use-default', envId ? { envId } : { clear: true });
1338
+ }
1339
+
1340
+ // ── SynaBun assistant orchestration (role-gated `agents` tool group) ──
1341
+ // Thin proxies over /api/assistant/* (neural-interface/lib/assistant-api.js),
1342
+ // which owns dispatch state, rails and authorization. requestOnce already
1343
+ // sends X-Synabun-Terminal (the assistant pin) and X-Synabun-Role.
1344
+
1345
+ export type AssistantProvider = 'claude-code' | 'codex' | 'opencode';
1346
+
1347
+ export type AssistantDispatchBody = {
1348
+ /** Optional when routeId names an approved route (the server applies it). */
1349
+ provider?: AssistantProvider;
1350
+ task: string;
1351
+ cwd: string;
1352
+ assistantSessionId: string;
1353
+ model?: string;
1354
+ effort?: string;
1355
+ mcpProfile?: string;
1356
+ routeId?: string;
1357
+ taskClass?: string;
1358
+ confidence?: number;
1359
+ usesComputer?: boolean;
1360
+ agent?: string;
1361
+ accountId?: string;
1362
+ codexAccountId?: string;
1363
+ claudeAccountId?: string;
1364
+ permissionPolicy?: 'auto' | 'ask' | 'restricted';
1365
+ capability?: 'read-only' | 'workspace' | 'full';
1366
+ maxMinutes?: number;
1367
+ budgetUsd?: number;
1368
+ usesBrowser?: boolean;
1369
+ focus?: boolean;
1370
+ title?: string;
1371
+ context?: string;
1372
+ tags?: string[];
1373
+ workflowId?: string;
1374
+ parentRunId?: string;
1375
+ outputSchema?: Record<string, unknown>;
1376
+ idempotencyKey?: string;
1377
+ briefId?: string;
1378
+ independent?: boolean;
1379
+ };
1380
+
1381
+ // A dispatch may acquire a browser tab before the run is accepted.
1382
+ const ASSISTANT_DISPATCH_TIMEOUT = 90_000;
1383
+ // Long-poll waits block server-side for up to timeoutMs; the HTTP deadline
1384
+ // must always outlive them so a timed-out wait is reported by the server.
1385
+ const ASSISTANT_WAIT_MARGIN_MS = 15_000;
1386
+
1387
+ function assistantRunPath(runId: string, suffix = ''): string {
1388
+ return `/api/assistant/runs/${encodeURIComponent(runId)}${suffix}`;
1389
+ }
1390
+
1391
+ function queryString(params: Record<string, string | number | boolean | undefined | null>): string {
1392
+ const qs = new URLSearchParams();
1393
+ for (const [key, value] of Object.entries(params)) {
1394
+ if (value === undefined || value === null || value === '') continue;
1395
+ qs.set(key, String(value));
1396
+ }
1397
+ const rendered = qs.toString();
1398
+ return rendered ? `?${rendered}` : '';
1399
+ }
1400
+
1401
+ export async function assistantCatalog(opts: { provider?: string; search?: string } = {}): Promise<NiResponse> {
1402
+ return request('GET', `/api/assistant/catalog${queryString({ view: 'brain', provider: opts.provider, q: opts.search })}`, undefined, LONG_TIMEOUT);
1403
+ }
1404
+
1405
+ // agent_route may show the user a route card and wait for the answer; the
1406
+ // server caps its own wait below this deadline (X-Synabun-Deadline).
1407
+ const ASSISTANT_ROUTE_TIMEOUT = 115_000;
1408
+
1409
+ export async function assistantRoute(body: {
1410
+ assistantSessionId: string;
1411
+ task_class: string;
1412
+ summary: string;
1413
+ confidence: number;
1414
+ needs_vision?: boolean;
1415
+ proposals: Array<{ kind: 'direct' | 'dispatch'; provider?: string; model?: string; effort?: string; reason?: string }>;
1416
+ independent?: boolean;
1417
+ }): Promise<NiResponse> {
1418
+ return request('POST', '/api/assistant/route', body as unknown as Record<string, unknown>, ASSISTANT_ROUTE_TIMEOUT);
1419
+ }
1420
+
1421
+ // agent_clarify shows the user a question card and waits briefly for the
1422
+ // answers; like agent_route, the server caps its wait below this deadline.
1423
+ export async function assistantClarify(body: {
1424
+ assistantSessionId: string;
1425
+ summary: string;
1426
+ questions: Array<{ id?: string; header?: string; question: string; options: Array<{ label: string; description?: string }>; multi_select?: boolean }>;
1427
+ constraints?: string[];
1428
+ assumptions?: string[];
1429
+ }): Promise<NiResponse> {
1430
+ return request('POST', '/api/assistant/clarify', body as unknown as Record<string, unknown>, ASSISTANT_ROUTE_TIMEOUT);
1431
+ }
1432
+
1433
+ export async function assistantListRuns(opts: { activeOnly?: boolean; assistantSessionId?: string; workflowId?: string } = {}): Promise<NiResponse> {
1434
+ return request('GET', `/api/assistant/runs${queryString({
1435
+ active: opts.activeOnly ? 1 : undefined,
1436
+ assistantSessionId: opts.assistantSessionId,
1437
+ workflowId: opts.workflowId,
1438
+ })}`);
1439
+ }
1440
+
1441
+ /** Usage of one task ("current", an id) or the session ("all"); `runId` with "all" adds that run's totals over its tasks. */
1442
+ export async function assistantUsage(assistantSessionId: string, task = 'current', runId?: string): Promise<NiResponse> {
1443
+ return request('GET', `/api/assistant/sessions/${encodeURIComponent(assistantSessionId)}/usage${queryString({ task, run: runId })}`);
1444
+ }
1445
+
1446
+ export async function assistantDispatch(body: AssistantDispatchBody): Promise<NiResponse> {
1447
+ return request('POST', '/api/assistant/dispatch', body, ASSISTANT_DISPATCH_TIMEOUT);
1448
+ }
1449
+
1450
+ export async function assistantRun(runId: string): Promise<NiResponse> {
1451
+ return request('GET', assistantRunPath(runId));
1452
+ }
1453
+
1454
+ export async function assistantResult(runId: string): Promise<NiResponse> {
1455
+ return request('GET', assistantRunPath(runId, '/result'));
1456
+ }
1457
+
1458
+ export async function assistantTranscript(runId: string, opts: { format?: string; tail?: number; maxChars?: number } = {}): Promise<NiResponse> {
1459
+ return request('GET', assistantRunPath(runId, `/transcript${queryString({ format: opts.format, tail: opts.tail, maxChars: opts.maxChars })}`), undefined, LONG_TIMEOUT);
1460
+ }
1461
+
1462
+ export async function assistantSend(runId: string, body: { text: string; queue?: boolean }): Promise<NiResponse> {
1463
+ return request('POST', assistantRunPath(runId, '/send'), body, LONG_TIMEOUT);
1464
+ }
1465
+
1466
+ export async function assistantPermission(runId: string, body: { requestId: string; behavior: 'allow' | 'deny'; answers?: Record<string, unknown>; message?: string }): Promise<NiResponse> {
1467
+ return request('POST', assistantRunPath(runId, '/permission'), body, LONG_TIMEOUT);
1468
+ }
1469
+
1470
+ export async function assistantWaitRun(runId: string, opts: { until?: string; timeoutMs: number }): Promise<NiResponse> {
1471
+ return request('GET', assistantRunPath(runId, `/wait${queryString({ until: opts.until, timeout: opts.timeoutMs })}`), undefined, opts.timeoutMs + ASSISTANT_WAIT_MARGIN_MS);
1472
+ }
1473
+
1474
+ export async function assistantWaitMany(body: { runIds?: string[]; workflowId?: string; mode?: string; until?: string; timeoutMs: number }): Promise<NiResponse> {
1475
+ return request('POST', '/api/assistant/wait', body, body.timeoutMs + ASSISTANT_WAIT_MARGIN_MS);
1476
+ }
1477
+
1478
+ export async function assistantStop(runId: string, reason?: string): Promise<NiResponse> {
1479
+ return request('POST', assistantRunPath(runId, '/stop'), reason ? { reason } : {}, LONG_TIMEOUT);
1480
+ }
1481
+
1482
+ export async function assistantKillAll(body: { assistantSessionId?: string; workflowId?: string; reason?: string } = {}): Promise<NiResponse> {
1483
+ return request('POST', '/api/assistant/kill-all', body, LONG_TIMEOUT);
1484
+ }
1485
+
1486
+ export async function assistantFocus(runId: string, focus: boolean): Promise<NiResponse> {
1487
+ return request('POST', assistantRunPath(runId, '/focus'), { focus });
1488
+ }