synabun 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1163) hide show
  1. package/CHANGELOG.md +205 -662
  2. package/LICENSE-COMMERCIAL.md +33 -0
  3. package/NOTICE +17 -0
  4. package/README.md +175 -853
  5. package/THIRD-PARTY-LICENSES.md +136 -0
  6. package/benchmarks/browser-live-results.json +79 -0
  7. package/benchmarks/browser-v2-results.json +258 -0
  8. package/benchmarks/fixtures/browser/README.md +57 -0
  9. package/benchmarks/fixtures/browser/page-state.json +132 -0
  10. package/benchmarks/fixtures/browser/social.json +92 -0
  11. package/benchmarks/fixtures/browser/target.json +319 -0
  12. package/benchmarks/fixtures/desktop/README.md +58 -0
  13. package/benchmarks/fixtures/desktop/abstention.json +59 -0
  14. package/benchmarks/fixtures/desktop/adversarial.json +22 -0
  15. package/benchmarks/fixtures/desktop/forbidden.json +93 -0
  16. package/benchmarks/fixtures/desktop/generate.mjs +1023 -0
  17. package/benchmarks/fixtures/desktop/target.json +136 -0
  18. package/bin/synabun.js +30 -0
  19. package/docs/whatsapp.md +325 -0
  20. package/hooks/claude-code/post-plan.mjs +613 -391
  21. package/hooks/claude-code/post-remember.mjs +246 -122
  22. package/hooks/claude-code/pre-compact.mjs +171 -110
  23. package/hooks/claude-code/pre-task.mjs +133 -0
  24. package/hooks/claude-code/pre-websearch.mjs +15 -4
  25. package/hooks/claude-code/prompt-origin.mjs +94 -0
  26. package/hooks/claude-code/prompt-submit.mjs +1244 -861
  27. package/hooks/claude-code/session-start.mjs +114 -156
  28. package/hooks/claude-code/shared.mjs +1228 -368
  29. package/hooks/claude-code/state.mjs +167 -0
  30. package/hooks/claude-code/stop.mjs +515 -140
  31. package/hooks/claude-code/subagent-start.mjs +154 -0
  32. package/hooks/claude-code/subagent-stop.mjs +102 -0
  33. package/hooks/claude-code/task-gate.mjs +150 -0
  34. package/launcher.mjs +40 -0
  35. package/lib/claude-hooks.js +137 -0
  36. package/lib/claude-usage-audit.js +141 -0
  37. package/lib/client-config-repair.js +145 -0
  38. package/lib/data-home-migration.js +502 -0
  39. package/lib/desktop-pwa.js +206 -0
  40. package/lib/external-tools.js +221 -0
  41. package/lib/first-launch-protection.js +245 -0
  42. package/lib/packaged-runtime.js +110 -0
  43. package/lib/paths.d.ts +21 -0
  44. package/lib/paths.js +103 -0
  45. package/lib/start-launcher.js +1309 -0
  46. package/mcp-server/dist/config.d.ts +22 -0
  47. package/mcp-server/dist/config.d.ts.map +1 -0
  48. package/mcp-server/dist/config.js +109 -0
  49. package/mcp-server/dist/config.js.map +1 -0
  50. package/mcp-server/dist/http.d.ts +30 -0
  51. package/mcp-server/dist/http.d.ts.map +1 -0
  52. package/mcp-server/dist/http.js +276 -0
  53. package/mcp-server/dist/http.js.map +1 -0
  54. package/mcp-server/dist/index.d.ts +48 -0
  55. package/mcp-server/dist/index.d.ts.map +1 -0
  56. package/mcp-server/dist/index.js +538 -0
  57. package/mcp-server/dist/index.js.map +1 -0
  58. package/mcp-server/dist/preload-http.d.ts +3 -0
  59. package/mcp-server/dist/preload-http.d.ts.map +1 -0
  60. package/mcp-server/dist/preload-http.js +28 -0
  61. package/mcp-server/dist/preload-http.js.map +1 -0
  62. package/mcp-server/dist/preload.d.ts +3 -0
  63. package/mcp-server/dist/preload.d.ts.map +1 -0
  64. package/mcp-server/dist/preload.js +32 -0
  65. package/mcp-server/dist/preload.js.map +1 -0
  66. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts +19 -0
  67. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts.map +1 -0
  68. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js +403 -0
  69. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js.map +1 -0
  70. package/mcp-server/dist/scripts/setup-embeddings.d.ts +3 -0
  71. package/mcp-server/dist/scripts/setup-embeddings.d.ts.map +1 -0
  72. package/mcp-server/dist/scripts/setup-embeddings.js +15 -0
  73. package/mcp-server/dist/scripts/setup-embeddings.js.map +1 -0
  74. package/mcp-server/dist/services/assistant-judgments.d.ts +79 -0
  75. package/mcp-server/dist/services/assistant-judgments.d.ts.map +1 -0
  76. package/mcp-server/dist/services/assistant-judgments.js +128 -0
  77. package/mcp-server/dist/services/assistant-judgments.js.map +1 -0
  78. package/mcp-server/dist/services/browser-assist-gate.d.ts +314 -0
  79. package/mcp-server/dist/services/browser-assist-gate.d.ts.map +1 -0
  80. package/mcp-server/dist/services/browser-assist-gate.js +344 -0
  81. package/mcp-server/dist/services/browser-assist-gate.js.map +1 -0
  82. package/mcp-server/dist/services/browser-assist.d.ts +104 -0
  83. package/mcp-server/dist/services/browser-assist.d.ts.map +1 -0
  84. package/mcp-server/dist/services/browser-assist.js +282 -0
  85. package/mcp-server/dist/services/browser-assist.js.map +1 -0
  86. package/mcp-server/dist/services/browser-bench.d.ts +772 -0
  87. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -0
  88. package/mcp-server/dist/services/browser-bench.js +424 -0
  89. package/mcp-server/dist/services/browser-bench.js.map +1 -0
  90. package/mcp-server/dist/services/browser-judgments.d.ts +196 -0
  91. package/mcp-server/dist/services/browser-judgments.d.ts.map +1 -0
  92. package/mcp-server/dist/services/browser-judgments.js +363 -0
  93. package/mcp-server/dist/services/browser-judgments.js.map +1 -0
  94. package/mcp-server/dist/services/browser-risk.d.ts +166 -0
  95. package/mcp-server/dist/services/browser-risk.d.ts.map +1 -0
  96. package/mcp-server/dist/services/browser-risk.js +458 -0
  97. package/mcp-server/dist/services/browser-risk.js.map +1 -0
  98. package/mcp-server/dist/services/categories.d.ts +61 -0
  99. package/mcp-server/dist/services/categories.d.ts.map +1 -0
  100. package/mcp-server/dist/services/categories.js +377 -0
  101. package/mcp-server/dist/services/categories.js.map +1 -0
  102. package/mcp-server/dist/services/codex-config-heal.d.ts +49 -0
  103. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -0
  104. package/mcp-server/dist/services/codex-config-heal.js +346 -0
  105. package/mcp-server/dist/services/codex-config-heal.js.map +1 -0
  106. package/mcp-server/dist/services/desktop-assist-gate.d.ts +198 -0
  107. package/mcp-server/dist/services/desktop-assist-gate.d.ts.map +1 -0
  108. package/mcp-server/dist/services/desktop-assist-gate.js +202 -0
  109. package/mcp-server/dist/services/desktop-assist-gate.js.map +1 -0
  110. package/mcp-server/dist/services/desktop-assist.d.ts +104 -0
  111. package/mcp-server/dist/services/desktop-assist.d.ts.map +1 -0
  112. package/mcp-server/dist/services/desktop-assist.js +266 -0
  113. package/mcp-server/dist/services/desktop-assist.js.map +1 -0
  114. package/mcp-server/dist/services/desktop-bench.d.ts +553 -0
  115. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -0
  116. package/mcp-server/dist/services/desktop-bench.js +311 -0
  117. package/mcp-server/dist/services/desktop-bench.js.map +1 -0
  118. package/mcp-server/dist/services/desktop-client.d.ts +23 -0
  119. package/mcp-server/dist/services/desktop-client.d.ts.map +1 -0
  120. package/mcp-server/dist/services/desktop-client.js +79 -0
  121. package/mcp-server/dist/services/desktop-client.js.map +1 -0
  122. package/mcp-server/dist/services/desktop-judgments.d.ts +125 -0
  123. package/mcp-server/dist/services/desktop-judgments.d.ts.map +1 -0
  124. package/mcp-server/dist/services/desktop-judgments.js +177 -0
  125. package/mcp-server/dist/services/desktop-judgments.js.map +1 -0
  126. package/mcp-server/dist/services/desktop-risk.d.ts +283 -0
  127. package/mcp-server/dist/services/desktop-risk.d.ts.map +1 -0
  128. package/mcp-server/dist/services/desktop-risk.js +605 -0
  129. package/mcp-server/dist/services/desktop-risk.js.map +1 -0
  130. package/mcp-server/dist/services/discord.d.ts +112 -0
  131. package/mcp-server/dist/services/discord.d.ts.map +1 -0
  132. package/mcp-server/dist/services/discord.js +477 -0
  133. package/mcp-server/dist/services/discord.js.map +1 -0
  134. package/mcp-server/dist/services/edit-stale.d.ts +81 -0
  135. package/mcp-server/dist/services/edit-stale.d.ts.map +1 -0
  136. package/mcp-server/dist/services/edit-stale.js +358 -0
  137. package/mcp-server/dist/services/edit-stale.js.map +1 -0
  138. package/mcp-server/dist/services/embedding-service.d.ts +16 -0
  139. package/mcp-server/dist/services/embedding-service.d.ts.map +1 -0
  140. package/mcp-server/dist/services/embedding-service.js +126 -0
  141. package/mcp-server/dist/services/embedding-service.js.map +1 -0
  142. package/mcp-server/dist/services/embedding-worker.d.ts +2 -0
  143. package/mcp-server/dist/services/embedding-worker.d.ts.map +1 -0
  144. package/mcp-server/dist/services/embedding-worker.js +52 -0
  145. package/mcp-server/dist/services/embedding-worker.js.map +1 -0
  146. package/mcp-server/dist/services/fb-regions.d.ts +71 -0
  147. package/mcp-server/dist/services/fb-regions.d.ts.map +1 -0
  148. package/mcp-server/dist/services/fb-regions.js +211 -0
  149. package/mcp-server/dist/services/fb-regions.js.map +1 -0
  150. package/mcp-server/dist/services/file-checksums.d.ts +22 -0
  151. package/mcp-server/dist/services/file-checksums.d.ts.map +1 -0
  152. package/mcp-server/dist/services/file-checksums.js +55 -0
  153. package/mcp-server/dist/services/file-checksums.js.map +1 -0
  154. package/mcp-server/dist/services/file-excerpt.d.ts +24 -0
  155. package/mcp-server/dist/services/file-excerpt.d.ts.map +1 -0
  156. package/mcp-server/dist/services/file-excerpt.js +65 -0
  157. package/mcp-server/dist/services/file-excerpt.js.map +1 -0
  158. package/mcp-server/dist/services/identity.d.ts +124 -0
  159. package/mcp-server/dist/services/identity.d.ts.map +1 -0
  160. package/mcp-server/dist/services/identity.js +241 -0
  161. package/mcp-server/dist/services/identity.js.map +1 -0
  162. package/mcp-server/dist/services/local-embeddings.d.ts +2 -0
  163. package/mcp-server/dist/services/local-embeddings.d.ts.map +1 -0
  164. package/mcp-server/dist/services/local-embeddings.js +3 -0
  165. package/mcp-server/dist/services/local-embeddings.js.map +1 -0
  166. package/mcp-server/dist/services/memory-client.d.ts +4 -0
  167. package/mcp-server/dist/services/memory-client.d.ts.map +1 -0
  168. package/mcp-server/dist/services/memory-client.js +27 -0
  169. package/mcp-server/dist/services/memory-client.js.map +1 -0
  170. package/mcp-server/dist/services/memory-judge-backfill.d.ts +135 -0
  171. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -0
  172. package/mcp-server/dist/services/memory-judge-backfill.js +510 -0
  173. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -0
  174. package/mcp-server/dist/services/memory-judgments.d.ts +362 -0
  175. package/mcp-server/dist/services/memory-judgments.d.ts.map +1 -0
  176. package/mcp-server/dist/services/memory-judgments.js +674 -0
  177. package/mcp-server/dist/services/memory-judgments.js.map +1 -0
  178. package/mcp-server/dist/services/memory-maintenance.d.ts +108 -0
  179. package/mcp-server/dist/services/memory-maintenance.d.ts.map +1 -0
  180. package/mcp-server/dist/services/memory-maintenance.js +403 -0
  181. package/mcp-server/dist/services/memory-maintenance.js.map +1 -0
  182. package/mcp-server/dist/services/memory-retrieval.d.ts +66 -0
  183. package/mcp-server/dist/services/memory-retrieval.d.ts.map +1 -0
  184. package/mcp-server/dist/services/memory-retrieval.js +412 -0
  185. package/mcp-server/dist/services/memory-retrieval.js.map +1 -0
  186. package/mcp-server/dist/services/memory-schema.d.ts +16 -0
  187. package/mcp-server/dist/services/memory-schema.d.ts.map +1 -0
  188. package/mcp-server/dist/services/memory-schema.js +282 -0
  189. package/mcp-server/dist/services/memory-schema.js.map +1 -0
  190. package/mcp-server/dist/services/memory-sessions.d.ts +24 -0
  191. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -0
  192. package/mcp-server/dist/services/memory-sessions.js +110 -0
  193. package/mcp-server/dist/services/memory-sessions.js.map +1 -0
  194. package/mcp-server/dist/services/memory-triage.d.ts +60 -0
  195. package/mcp-server/dist/services/memory-triage.d.ts.map +1 -0
  196. package/mcp-server/dist/services/memory-triage.js +189 -0
  197. package/mcp-server/dist/services/memory-triage.js.map +1 -0
  198. package/mcp-server/dist/services/memory-verification.d.ts +24 -0
  199. package/mcp-server/dist/services/memory-verification.d.ts.map +1 -0
  200. package/mcp-server/dist/services/memory-verification.js +92 -0
  201. package/mcp-server/dist/services/memory-verification.js.map +1 -0
  202. package/mcp-server/dist/services/memory-writer.d.ts +26 -0
  203. package/mcp-server/dist/services/memory-writer.d.ts.map +1 -0
  204. package/mcp-server/dist/services/memory-writer.js +126 -0
  205. package/mcp-server/dist/services/memory-writer.js.map +1 -0
  206. package/mcp-server/dist/services/neural-interface.d.ts +439 -0
  207. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -0
  208. package/mcp-server/dist/services/neural-interface.js +1135 -0
  209. package/mcp-server/dist/services/neural-interface.js.map +1 -0
  210. package/mcp-server/dist/services/profiles.d.ts +120 -0
  211. package/mcp-server/dist/services/profiles.d.ts.map +1 -0
  212. package/mcp-server/dist/services/profiles.js +387 -0
  213. package/mcp-server/dist/services/profiles.js.map +1 -0
  214. package/mcp-server/dist/services/secret-gate.d.ts +29 -0
  215. package/mcp-server/dist/services/secret-gate.d.ts.map +1 -0
  216. package/mcp-server/dist/services/secret-gate.js +43 -0
  217. package/mcp-server/dist/services/secret-gate.js.map +1 -0
  218. package/mcp-server/dist/services/server-instructions.d.ts +10 -0
  219. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -0
  220. package/mcp-server/dist/services/server-instructions.js +101 -0
  221. package/mcp-server/dist/services/server-instructions.js.map +1 -0
  222. package/mcp-server/dist/services/sqlite.d.ts +235 -0
  223. package/mcp-server/dist/services/sqlite.d.ts.map +1 -0
  224. package/mcp-server/dist/services/sqlite.js +1185 -0
  225. package/mcp-server/dist/services/sqlite.js.map +1 -0
  226. package/mcp-server/dist/services/typesafe-bench.d.ts +99 -0
  227. package/mcp-server/dist/services/typesafe-bench.d.ts.map +1 -0
  228. package/mcp-server/dist/services/typesafe-bench.js +218 -0
  229. package/mcp-server/dist/services/typesafe-bench.js.map +1 -0
  230. package/mcp-server/dist/services/typesafe-config.d.ts +342 -0
  231. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -0
  232. package/mcp-server/dist/services/typesafe-config.js +746 -0
  233. package/mcp-server/dist/services/typesafe-config.js.map +1 -0
  234. package/mcp-server/dist/services/typesafe.d.ts +213 -0
  235. package/mcp-server/dist/services/typesafe.d.ts.map +1 -0
  236. package/mcp-server/dist/services/typesafe.js +330 -0
  237. package/mcp-server/dist/services/typesafe.js.map +1 -0
  238. package/mcp-server/dist/services/vector-cache.d.ts +68 -0
  239. package/mcp-server/dist/services/vector-cache.d.ts.map +1 -0
  240. package/mcp-server/dist/services/vector-cache.js +271 -0
  241. package/mcp-server/dist/services/vector-cache.js.map +1 -0
  242. package/mcp-server/dist/services/whiteboard-geometry.d.ts +305 -0
  243. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -0
  244. package/mcp-server/dist/services/whiteboard-geometry.js +666 -0
  245. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -0
  246. package/mcp-server/dist/tools/agents.d.ts +212 -0
  247. package/mcp-server/dist/tools/agents.d.ts.map +1 -0
  248. package/mcp-server/dist/tools/agents.js +407 -0
  249. package/mcp-server/dist/tools/agents.js.map +1 -0
  250. package/mcp-server/dist/tools/bluesky.d.ts +39 -0
  251. package/mcp-server/dist/tools/bluesky.d.ts.map +1 -0
  252. package/mcp-server/dist/tools/bluesky.js +1144 -0
  253. package/mcp-server/dist/tools/bluesky.js.map +1 -0
  254. package/mcp-server/dist/tools/browser-advanced.d.ts +58 -0
  255. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -0
  256. package/mcp-server/dist/tools/browser-advanced.js +106 -0
  257. package/mcp-server/dist/tools/browser-advanced.js.map +1 -0
  258. package/mcp-server/dist/tools/browser-batch.d.ts +99 -0
  259. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -0
  260. package/mcp-server/dist/tools/browser-batch.js +187 -0
  261. package/mcp-server/dist/tools/browser-batch.js.map +1 -0
  262. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +21 -0
  263. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -0
  264. package/mcp-server/dist/tools/browser-cheatsheet.js +144 -0
  265. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -0
  266. package/mcp-server/dist/tools/browser-interact.d.ts +239 -0
  267. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -0
  268. package/mcp-server/dist/tools/browser-interact.js +300 -0
  269. package/mcp-server/dist/tools/browser-interact.js.map +1 -0
  270. package/mcp-server/dist/tools/browser-navigate.d.ts +90 -0
  271. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -0
  272. package/mcp-server/dist/tools/browser-navigate.js +142 -0
  273. package/mcp-server/dist/tools/browser-navigate.js.map +1 -0
  274. package/mcp-server/dist/tools/browser-observe.d.ts +561 -0
  275. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -0
  276. package/mcp-server/dist/tools/browser-observe.js +2009 -0
  277. package/mcp-server/dist/tools/browser-observe.js.map +1 -0
  278. package/mcp-server/dist/tools/browser.d.ts +34 -0
  279. package/mcp-server/dist/tools/browser.d.ts.map +1 -0
  280. package/mcp-server/dist/tools/browser.js +112 -0
  281. package/mcp-server/dist/tools/browser.js.map +1 -0
  282. package/mcp-server/dist/tools/card-tools.d.ts +82 -0
  283. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -0
  284. package/mcp-server/dist/tools/card-tools.js +137 -0
  285. package/mcp-server/dist/tools/card-tools.js.map +1 -0
  286. package/mcp-server/dist/tools/card.d.ts +8 -0
  287. package/mcp-server/dist/tools/card.d.ts.map +1 -0
  288. package/mcp-server/dist/tools/card.js +16 -0
  289. package/mcp-server/dist/tools/card.js.map +1 -0
  290. package/mcp-server/dist/tools/category.d.ts +32 -0
  291. package/mcp-server/dist/tools/category.d.ts.map +1 -0
  292. package/mcp-server/dist/tools/category.js +290 -0
  293. package/mcp-server/dist/tools/category.js.map +1 -0
  294. package/mcp-server/dist/tools/choice.d.ts +160 -0
  295. package/mcp-server/dist/tools/choice.d.ts.map +1 -0
  296. package/mcp-server/dist/tools/choice.js +80 -0
  297. package/mcp-server/dist/tools/choice.js.map +1 -0
  298. package/mcp-server/dist/tools/computer.d.ts +71 -0
  299. package/mcp-server/dist/tools/computer.d.ts.map +1 -0
  300. package/mcp-server/dist/tools/computer.js +174 -0
  301. package/mcp-server/dist/tools/computer.js.map +1 -0
  302. package/mcp-server/dist/tools/discord-channel.d.ts +44 -0
  303. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -0
  304. package/mcp-server/dist/tools/discord-channel.js +236 -0
  305. package/mcp-server/dist/tools/discord-channel.js.map +1 -0
  306. package/mcp-server/dist/tools/discord-guild.d.ts +20 -0
  307. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -0
  308. package/mcp-server/dist/tools/discord-guild.js +138 -0
  309. package/mcp-server/dist/tools/discord-guild.js.map +1 -0
  310. package/mcp-server/dist/tools/discord-member.d.ts +26 -0
  311. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -0
  312. package/mcp-server/dist/tools/discord-member.js +122 -0
  313. package/mcp-server/dist/tools/discord-member.js.map +1 -0
  314. package/mcp-server/dist/tools/discord-message.d.ts +36 -0
  315. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -0
  316. package/mcp-server/dist/tools/discord-message.js +184 -0
  317. package/mcp-server/dist/tools/discord-message.js.map +1 -0
  318. package/mcp-server/dist/tools/discord-onboarding.d.ts +32 -0
  319. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -0
  320. package/mcp-server/dist/tools/discord-onboarding.js +179 -0
  321. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -0
  322. package/mcp-server/dist/tools/discord-role.d.ts +34 -0
  323. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -0
  324. package/mcp-server/dist/tools/discord-role.js +178 -0
  325. package/mcp-server/dist/tools/discord-role.js.map +1 -0
  326. package/mcp-server/dist/tools/discord-thread.d.ts +28 -0
  327. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -0
  328. package/mcp-server/dist/tools/discord-thread.js +88 -0
  329. package/mcp-server/dist/tools/discord-thread.js.map +1 -0
  330. package/mcp-server/dist/tools/discord-webhook.d.ts +32 -0
  331. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -0
  332. package/mcp-server/dist/tools/discord-webhook.js +154 -0
  333. package/mcp-server/dist/tools/discord-webhook.js.map +1 -0
  334. package/mcp-server/dist/tools/discord.d.ts +7 -0
  335. package/mcp-server/dist/tools/discord.d.ts.map +1 -0
  336. package/mcp-server/dist/tools/discord.js +25 -0
  337. package/mcp-server/dist/tools/discord.js.map +1 -0
  338. package/mcp-server/dist/tools/fb-groups.d.ts +98 -0
  339. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -0
  340. package/mcp-server/dist/tools/fb-groups.js +369 -0
  341. package/mcp-server/dist/tools/fb-groups.js.map +1 -0
  342. package/mcp-server/dist/tools/forget.d.ts +14 -0
  343. package/mcp-server/dist/tools/forget.d.ts.map +1 -0
  344. package/mcp-server/dist/tools/forget.js +26 -0
  345. package/mcp-server/dist/tools/forget.js.map +1 -0
  346. package/mcp-server/dist/tools/git-tools.d.ts +24 -0
  347. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -0
  348. package/mcp-server/dist/tools/git-tools.js +107 -0
  349. package/mcp-server/dist/tools/git-tools.js.map +1 -0
  350. package/mcp-server/dist/tools/git.d.ts +7 -0
  351. package/mcp-server/dist/tools/git.d.ts.map +1 -0
  352. package/mcp-server/dist/tools/git.js +11 -0
  353. package/mcp-server/dist/tools/git.js.map +1 -0
  354. package/mcp-server/dist/tools/gsc/experience.d.ts +135 -0
  355. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -0
  356. package/mcp-server/dist/tools/gsc/experience.js +351 -0
  357. package/mcp-server/dist/tools/gsc/experience.js.map +1 -0
  358. package/mcp-server/dist/tools/gsc/helpers.d.ts +78 -0
  359. package/mcp-server/dist/tools/gsc/helpers.d.ts.map +1 -0
  360. package/mcp-server/dist/tools/gsc/helpers.js +258 -0
  361. package/mcp-server/dist/tools/gsc/helpers.js.map +1 -0
  362. package/mcp-server/dist/tools/gsc/indexing.d.ts +121 -0
  363. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -0
  364. package/mcp-server/dist/tools/gsc/indexing.js +357 -0
  365. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -0
  366. package/mcp-server/dist/tools/gsc/inspect.d.ts +78 -0
  367. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -0
  368. package/mcp-server/dist/tools/gsc/inspect.js +321 -0
  369. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -0
  370. package/mcp-server/dist/tools/gsc/navigate.d.ts +44 -0
  371. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -0
  372. package/mcp-server/dist/tools/gsc/navigate.js +197 -0
  373. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -0
  374. package/mcp-server/dist/tools/gsc/performance.d.ts +84 -0
  375. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -0
  376. package/mcp-server/dist/tools/gsc/performance.js +274 -0
  377. package/mcp-server/dist/tools/gsc/performance.js.map +1 -0
  378. package/mcp-server/dist/tools/gsc/settings.d.ts +153 -0
  379. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -0
  380. package/mcp-server/dist/tools/gsc/settings.js +425 -0
  381. package/mcp-server/dist/tools/gsc/settings.js.map +1 -0
  382. package/mcp-server/dist/tools/gsc.d.ts +7 -0
  383. package/mcp-server/dist/tools/gsc.d.ts.map +1 -0
  384. package/mcp-server/dist/tools/gsc.js +51 -0
  385. package/mcp-server/dist/tools/gsc.js.map +1 -0
  386. package/mcp-server/dist/tools/image-tools.d.ts +24 -0
  387. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -0
  388. package/mcp-server/dist/tools/image-tools.js +80 -0
  389. package/mcp-server/dist/tools/image-tools.js.map +1 -0
  390. package/mcp-server/dist/tools/image.d.ts +10 -0
  391. package/mcp-server/dist/tools/image.d.ts.map +1 -0
  392. package/mcp-server/dist/tools/image.js +14 -0
  393. package/mcp-server/dist/tools/image.js.map +1 -0
  394. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +97 -0
  395. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -0
  396. package/mcp-server/dist/tools/leonardo-browser-tools.js +228 -0
  397. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -0
  398. package/mcp-server/dist/tools/leonardo.d.ts +13 -0
  399. package/mcp-server/dist/tools/leonardo.d.ts.map +1 -0
  400. package/mcp-server/dist/tools/leonardo.js +21 -0
  401. package/mcp-server/dist/tools/leonardo.js.map +1 -0
  402. package/mcp-server/dist/tools/loop.d.ts +30 -0
  403. package/mcp-server/dist/tools/loop.d.ts.map +1 -0
  404. package/mcp-server/dist/tools/loop.js +401 -0
  405. package/mcp-server/dist/tools/loop.js.map +1 -0
  406. package/mcp-server/dist/tools/memories.d.ts +56 -0
  407. package/mcp-server/dist/tools/memories.d.ts.map +1 -0
  408. package/mcp-server/dist/tools/memories.js +171 -0
  409. package/mcp-server/dist/tools/memories.js.map +1 -0
  410. package/mcp-server/dist/tools/morelogin.d.ts +24 -0
  411. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -0
  412. package/mcp-server/dist/tools/morelogin.js +79 -0
  413. package/mcp-server/dist/tools/morelogin.js.map +1 -0
  414. package/mcp-server/dist/tools/profile.d.ts +17 -0
  415. package/mcp-server/dist/tools/profile.d.ts.map +1 -0
  416. package/mcp-server/dist/tools/profile.js +114 -0
  417. package/mcp-server/dist/tools/profile.js.map +1 -0
  418. package/mcp-server/dist/tools/recall.d.ts +71 -0
  419. package/mcp-server/dist/tools/recall.d.ts.map +1 -0
  420. package/mcp-server/dist/tools/recall.js +147 -0
  421. package/mcp-server/dist/tools/recall.js.map +1 -0
  422. package/mcp-server/dist/tools/reflect.d.ts +56 -0
  423. package/mcp-server/dist/tools/reflect.d.ts.map +1 -0
  424. package/mcp-server/dist/tools/reflect.js +157 -0
  425. package/mcp-server/dist/tools/reflect.js.map +1 -0
  426. package/mcp-server/dist/tools/remember.d.ts +62 -0
  427. package/mcp-server/dist/tools/remember.d.ts.map +1 -0
  428. package/mcp-server/dist/tools/remember.js +201 -0
  429. package/mcp-server/dist/tools/remember.js.map +1 -0
  430. package/mcp-server/dist/tools/response.d.ts +23 -0
  431. package/mcp-server/dist/tools/response.d.ts.map +1 -0
  432. package/mcp-server/dist/tools/response.js +24 -0
  433. package/mcp-server/dist/tools/response.js.map +1 -0
  434. package/mcp-server/dist/tools/restore.d.ts +14 -0
  435. package/mcp-server/dist/tools/restore.d.ts.map +1 -0
  436. package/mcp-server/dist/tools/restore.js +23 -0
  437. package/mcp-server/dist/tools/restore.js.map +1 -0
  438. package/mcp-server/dist/tools/style-guide-tools.d.ts +51 -0
  439. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -0
  440. package/mcp-server/dist/tools/style-guide-tools.js +231 -0
  441. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -0
  442. package/mcp-server/dist/tools/style-guide.d.ts +8 -0
  443. package/mcp-server/dist/tools/style-guide.d.ts.map +1 -0
  444. package/mcp-server/dist/tools/style-guide.js +12 -0
  445. package/mcp-server/dist/tools/style-guide.js.map +1 -0
  446. package/mcp-server/dist/tools/sync.d.ts +20 -0
  447. package/mcp-server/dist/tools/sync.d.ts.map +1 -0
  448. package/mcp-server/dist/tools/sync.js +156 -0
  449. package/mcp-server/dist/tools/sync.js.map +1 -0
  450. package/mcp-server/dist/tools/tictactoe-tools.d.ts +18 -0
  451. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -0
  452. package/mcp-server/dist/tools/tictactoe-tools.js +78 -0
  453. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -0
  454. package/mcp-server/dist/tools/tictactoe.d.ts +7 -0
  455. package/mcp-server/dist/tools/tictactoe.d.ts.map +1 -0
  456. package/mcp-server/dist/tools/tictactoe.js +11 -0
  457. package/mcp-server/dist/tools/tictactoe.js.map +1 -0
  458. package/mcp-server/dist/tools/utils.d.ts +19 -0
  459. package/mcp-server/dist/tools/utils.d.ts.map +1 -0
  460. package/mcp-server/dist/tools/utils.js +54 -0
  461. package/mcp-server/dist/tools/utils.js.map +1 -0
  462. package/mcp-server/dist/tools/whiteboard-format.d.ts +88 -0
  463. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -0
  464. package/mcp-server/dist/tools/whiteboard-format.js +337 -0
  465. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -0
  466. package/mcp-server/dist/tools/whiteboard-tools.d.ts +326 -0
  467. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -0
  468. package/mcp-server/dist/tools/whiteboard-tools.js +222 -0
  469. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -0
  470. package/mcp-server/dist/tools/whiteboard.d.ts +8 -0
  471. package/mcp-server/dist/tools/whiteboard.d.ts.map +1 -0
  472. package/mcp-server/dist/tools/whiteboard.js +16 -0
  473. package/mcp-server/dist/tools/whiteboard.js.map +1 -0
  474. package/mcp-server/dist/tools/youtube/analytics.d.ts +46 -0
  475. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -0
  476. package/mcp-server/dist/tools/youtube/analytics.js +83 -0
  477. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -0
  478. package/mcp-server/dist/tools/youtube/dedup.d.ts +35 -0
  479. package/mcp-server/dist/tools/youtube/dedup.d.ts.map +1 -0
  480. package/mcp-server/dist/tools/youtube/dedup.js +56 -0
  481. package/mcp-server/dist/tools/youtube/dedup.js.map +1 -0
  482. package/mcp-server/dist/tools/youtube/download.d.ts +31 -0
  483. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -0
  484. package/mcp-server/dist/tools/youtube/download.js +30 -0
  485. package/mcp-server/dist/tools/youtube/download.js.map +1 -0
  486. package/mcp-server/dist/tools/youtube/helpers.d.ts +66 -0
  487. package/mcp-server/dist/tools/youtube/helpers.d.ts.map +1 -0
  488. package/mcp-server/dist/tools/youtube/helpers.js +143 -0
  489. package/mcp-server/dist/tools/youtube/helpers.js.map +1 -0
  490. package/mcp-server/dist/tools/youtube/media.d.ts +43 -0
  491. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -0
  492. package/mcp-server/dist/tools/youtube/media.js +97 -0
  493. package/mcp-server/dist/tools/youtube/media.js.map +1 -0
  494. package/mcp-server/dist/tools/youtube/metadata.d.ts +33 -0
  495. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -0
  496. package/mcp-server/dist/tools/youtube/metadata.js +106 -0
  497. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -0
  498. package/mcp-server/dist/tools/youtube/navigate.d.ts +30 -0
  499. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -0
  500. package/mcp-server/dist/tools/youtube/navigate.js +71 -0
  501. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -0
  502. package/mcp-server/dist/tools/youtube/playlist.d.ts +29 -0
  503. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -0
  504. package/mcp-server/dist/tools/youtube/playlist.js +128 -0
  505. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -0
  506. package/mcp-server/dist/tools/youtube/schedule.d.ts +31 -0
  507. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -0
  508. package/mcp-server/dist/tools/youtube/schedule.js +147 -0
  509. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -0
  510. package/mcp-server/dist/tools/youtube/source.d.ts +36 -0
  511. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -0
  512. package/mcp-server/dist/tools/youtube/source.js +85 -0
  513. package/mcp-server/dist/tools/youtube/source.js.map +1 -0
  514. package/mcp-server/dist/tools/youtube/upload.d.ts +53 -0
  515. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -0
  516. package/mcp-server/dist/tools/youtube/upload.js +255 -0
  517. package/mcp-server/dist/tools/youtube/upload.js.map +1 -0
  518. package/mcp-server/dist/tools/youtube.d.ts +12 -0
  519. package/mcp-server/dist/tools/youtube.d.ts.map +1 -0
  520. package/mcp-server/dist/tools/youtube.js +38 -0
  521. package/mcp-server/dist/tools/youtube.js.map +1 -0
  522. package/mcp-server/dist/tui.d.ts +3 -0
  523. package/mcp-server/dist/tui.d.ts.map +1 -0
  524. package/mcp-server/dist/tui.js +806 -0
  525. package/mcp-server/dist/tui.js.map +1 -0
  526. package/mcp-server/dist/types.d.ts +77 -0
  527. package/mcp-server/dist/types.d.ts.map +1 -0
  528. package/mcp-server/dist/types.js +2 -0
  529. package/mcp-server/dist/types.js.map +1 -0
  530. package/mcp-server/package-lock.json +4798 -0
  531. package/mcp-server/package.json +55 -38
  532. package/mcp-server/run.mjs +12 -2
  533. package/mcp-server/src/config.ts +48 -3
  534. package/mcp-server/src/http.ts +296 -93
  535. package/mcp-server/src/index.ts +546 -203
  536. package/mcp-server/src/preload-http.ts +19 -18
  537. package/mcp-server/src/preload.ts +19 -18
  538. package/mcp-server/src/scripts/setup-embeddings.ts +9 -0
  539. package/mcp-server/src/services/assistant-judgments.ts +157 -0
  540. package/mcp-server/src/services/browser-assist-gate.ts +383 -0
  541. package/mcp-server/src/services/browser-assist.ts +267 -0
  542. package/mcp-server/src/services/browser-bench.ts +477 -0
  543. package/mcp-server/src/services/browser-judgments.ts +414 -0
  544. package/mcp-server/src/services/browser-risk.ts +484 -0
  545. package/mcp-server/src/services/categories.ts +36 -19
  546. package/mcp-server/src/services/codex-config-heal.ts +403 -0
  547. package/mcp-server/src/services/desktop-assist-gate.ts +251 -0
  548. package/mcp-server/src/services/desktop-assist.ts +308 -0
  549. package/mcp-server/src/services/desktop-bench.ts +372 -0
  550. package/mcp-server/src/services/desktop-client.ts +76 -0
  551. package/mcp-server/src/services/desktop-judgments.ts +218 -0
  552. package/mcp-server/src/services/desktop-risk.ts +743 -0
  553. package/mcp-server/src/services/edit-stale.ts +299 -0
  554. package/mcp-server/src/services/embedding-service.ts +108 -0
  555. package/mcp-server/src/services/embedding-worker.ts +46 -0
  556. package/mcp-server/src/services/fb-regions.ts +221 -0
  557. package/mcp-server/src/services/file-checksums.ts +26 -9
  558. package/mcp-server/src/services/file-excerpt.ts +59 -0
  559. package/mcp-server/src/services/identity.ts +291 -0
  560. package/mcp-server/src/services/local-embeddings.ts +2 -77
  561. package/mcp-server/src/services/memory-client.ts +23 -0
  562. package/mcp-server/src/services/memory-judge-backfill.ts +462 -0
  563. package/mcp-server/src/services/memory-judgments.ts +901 -0
  564. package/mcp-server/src/services/memory-maintenance.ts +360 -0
  565. package/mcp-server/src/services/memory-retrieval.ts +349 -0
  566. package/mcp-server/src/services/memory-schema.ts +280 -0
  567. package/mcp-server/src/services/memory-sessions.ts +87 -0
  568. package/mcp-server/src/services/memory-triage.ts +182 -0
  569. package/mcp-server/src/services/memory-verification.ts +68 -0
  570. package/mcp-server/src/services/memory-writer.ts +125 -0
  571. package/mcp-server/src/services/neural-interface.ts +1217 -73
  572. package/mcp-server/src/services/profiles.ts +428 -0
  573. package/mcp-server/src/services/secret-gate.ts +52 -0
  574. package/mcp-server/src/services/server-instructions.ts +122 -0
  575. package/mcp-server/src/services/sqlite.ts +1456 -823
  576. package/mcp-server/src/services/typesafe-bench.ts +247 -0
  577. package/mcp-server/src/services/typesafe-config.ts +790 -0
  578. package/mcp-server/src/services/typesafe.ts +390 -0
  579. package/mcp-server/src/services/vector-cache.ts +255 -0
  580. package/mcp-server/src/services/whiteboard-geometry.ts +711 -0
  581. package/mcp-server/src/tools/agents.ts +539 -0
  582. package/mcp-server/src/tools/bluesky.ts +1186 -0
  583. package/mcp-server/src/tools/browser-advanced.ts +15 -6
  584. package/mcp-server/src/tools/browser-batch.ts +178 -0
  585. package/mcp-server/src/tools/browser-cheatsheet.ts +155 -0
  586. package/mcp-server/src/tools/browser-interact.ts +362 -255
  587. package/mcp-server/src/tools/browser-navigate.ts +164 -77
  588. package/mcp-server/src/tools/browser-observe.ts +2222 -1330
  589. package/mcp-server/src/tools/browser.ts +181 -107
  590. package/mcp-server/src/tools/card.ts +7 -5
  591. package/mcp-server/src/tools/choice.ts +103 -0
  592. package/mcp-server/src/tools/computer.ts +190 -0
  593. package/mcp-server/src/tools/discord.ts +10 -23
  594. package/mcp-server/src/tools/fb-groups.ts +387 -0
  595. package/mcp-server/src/tools/forget.ts +1 -1
  596. package/mcp-server/src/tools/git.ts +3 -1
  597. package/mcp-server/src/tools/gsc/experience.ts +440 -0
  598. package/mcp-server/src/tools/gsc/helpers.ts +288 -0
  599. package/mcp-server/src/tools/gsc/indexing.ts +495 -0
  600. package/mcp-server/src/tools/gsc/inspect.ts +391 -0
  601. package/mcp-server/src/tools/gsc/navigate.ts +236 -0
  602. package/mcp-server/src/tools/gsc/performance.ts +352 -0
  603. package/mcp-server/src/tools/gsc/settings.ts +564 -0
  604. package/mcp-server/src/tools/gsc.ts +95 -0
  605. package/mcp-server/src/tools/image.ts +3 -1
  606. package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
  607. package/mcp-server/src/tools/leonardo.ts +7 -5
  608. package/mcp-server/src/tools/loop.ts +118 -25
  609. package/mcp-server/src/tools/memories.ts +65 -5
  610. package/mcp-server/src/tools/morelogin.ts +81 -0
  611. package/mcp-server/src/tools/profile.ts +122 -0
  612. package/mcp-server/src/tools/recall.ts +105 -261
  613. package/mcp-server/src/tools/reflect.ts +171 -145
  614. package/mcp-server/src/tools/remember.ts +223 -115
  615. package/mcp-server/src/tools/style-guide-tools.ts +250 -0
  616. package/mcp-server/src/tools/style-guide.ts +16 -0
  617. package/mcp-server/src/tools/sync.ts +66 -9
  618. package/mcp-server/src/tools/tictactoe.ts +3 -1
  619. package/mcp-server/src/tools/utils.ts +22 -0
  620. package/mcp-server/src/tools/whiteboard-format.ts +394 -0
  621. package/mcp-server/src/tools/whiteboard-tools.ts +191 -183
  622. package/mcp-server/src/tools/whiteboard.ts +7 -5
  623. package/mcp-server/src/tools/youtube/analytics.ts +106 -0
  624. package/mcp-server/src/tools/youtube/dedup.ts +57 -0
  625. package/mcp-server/src/tools/youtube/download.ts +41 -0
  626. package/mcp-server/src/tools/youtube/helpers.ts +183 -0
  627. package/mcp-server/src/tools/youtube/media.ts +109 -0
  628. package/mcp-server/src/tools/youtube/metadata.ts +112 -0
  629. package/mcp-server/src/tools/youtube/navigate.ts +81 -0
  630. package/mcp-server/src/tools/youtube/playlist.ts +134 -0
  631. package/mcp-server/src/tools/youtube/schedule.ts +158 -0
  632. package/mcp-server/src/tools/youtube/source.ts +101 -0
  633. package/mcp-server/src/tools/youtube/upload.ts +296 -0
  634. package/mcp-server/src/tools/youtube.ts +47 -0
  635. package/mcp-server/src/tui.ts +8 -9
  636. package/neural-interface/games/TicTacToe/1.svg +1 -0
  637. package/neural-interface/games/TicTacToe/2.svg +1 -0
  638. package/neural-interface/games/TicTacToe/3.svg +1 -0
  639. package/neural-interface/games/TicTacToe/4.svg +1 -0
  640. package/neural-interface/games/TicTacToe/5.svg +1 -0
  641. package/neural-interface/games/TicTacToe/6.svg +1 -0
  642. package/neural-interface/games/TicTacToe/7.svg +1 -0
  643. package/neural-interface/games/TicTacToe/8.svg +1 -0
  644. package/neural-interface/games/TicTacToe/9.svg +1 -0
  645. package/neural-interface/games/TicTacToe/Board.svg +1 -0
  646. package/neural-interface/games/TicTacToe/Circle.svg +1 -0
  647. package/neural-interface/games/TicTacToe/Cross.svg +1 -0
  648. package/neural-interface/i18n/en.json +7022 -0
  649. package/neural-interface/i18n/pt-BR.json +7026 -0
  650. package/neural-interface/lib/assistant-api.js +1005 -0
  651. package/neural-interface/lib/assistant-attachments.js +121 -0
  652. package/neural-interface/lib/assistant-brains/claude.js +207 -0
  653. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +43 -0
  654. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +56 -0
  655. package/neural-interface/lib/assistant-brains/codex.js +213 -0
  656. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +47 -0
  657. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +62 -0
  658. package/neural-interface/lib/assistant-brains/opencode.js +320 -0
  659. package/neural-interface/lib/assistant-budget.js +318 -0
  660. package/neural-interface/lib/assistant-catalog.js +731 -0
  661. package/neural-interface/lib/assistant-clarify.js +664 -0
  662. package/neural-interface/lib/assistant-config.js +232 -0
  663. package/neural-interface/lib/assistant-dispatch.js +2744 -0
  664. package/neural-interface/lib/assistant-envelope.js +582 -0
  665. package/neural-interface/lib/assistant-evidence.js +236 -0
  666. package/neural-interface/lib/assistant-hidden-models.js +272 -0
  667. package/neural-interface/lib/assistant-jev.js +117 -0
  668. package/neural-interface/lib/assistant-judgments.js +111 -0
  669. package/neural-interface/lib/assistant-media.js +335 -0
  670. package/neural-interface/lib/assistant-memory.js +398 -0
  671. package/neural-interface/lib/assistant-persona.js +444 -0
  672. package/neural-interface/lib/assistant-plan-permissions.js +448 -0
  673. package/neural-interface/lib/assistant-playbooks/design.md +87 -0
  674. package/neural-interface/lib/assistant-playbooks.js +72 -0
  675. package/neural-interface/lib/assistant-pricing.js +159 -0
  676. package/neural-interface/lib/assistant-route-gate-codex.js +163 -0
  677. package/neural-interface/lib/assistant-route-gate.js +212 -0
  678. package/neural-interface/lib/assistant-router.js +1324 -0
  679. package/neural-interface/lib/assistant-runtime.js +3337 -0
  680. package/neural-interface/lib/assistant-task-prompt.js +472 -0
  681. package/neural-interface/lib/assistant-usage.js +1688 -0
  682. package/neural-interface/lib/augmented-path.js +70 -0
  683. package/neural-interface/lib/automation-storage-migration.js +54 -0
  684. package/neural-interface/lib/backup-service.js +851 -0
  685. package/neural-interface/lib/backup-zip-reader.js +262 -0
  686. package/neural-interface/lib/browser-assist-api.js +41 -0
  687. package/neural-interface/lib/browser-console.js +176 -0
  688. package/neural-interface/lib/browser-execution.js +241 -0
  689. package/neural-interface/lib/browser-extraction.js +191 -0
  690. package/neural-interface/lib/browser-language.js +83 -0
  691. package/neural-interface/lib/browser-screenshot.js +399 -0
  692. package/neural-interface/lib/browser-semantic-context.js +639 -0
  693. package/neural-interface/lib/browser-snapshots.js +192 -0
  694. package/neural-interface/lib/browser-tab-recovery.js +119 -0
  695. package/neural-interface/lib/browser-targets.js +70 -0
  696. package/neural-interface/lib/browser-tool-policy.js +926 -0
  697. package/neural-interface/lib/claude-accounts.js +230 -0
  698. package/neural-interface/lib/claude-agent-bridge.js +3203 -0
  699. package/neural-interface/lib/claude-bypass-policy.js +99 -0
  700. package/neural-interface/lib/claude-executable.js +241 -0
  701. package/neural-interface/lib/claude-history.js +429 -0
  702. package/neural-interface/lib/claude-model-catalog.js +444 -0
  703. package/neural-interface/lib/claude-panel-requests.js +165 -0
  704. package/neural-interface/lib/claude-panel-session.js +272 -0
  705. package/neural-interface/lib/claude-permission-rules.js +153 -0
  706. package/neural-interface/lib/claude-session-cleanup.js +71 -0
  707. package/neural-interface/lib/claude-session-list.js +82 -0
  708. package/neural-interface/lib/claude-session-ops.js +140 -0
  709. package/neural-interface/lib/claude-settings-view.js +51 -0
  710. package/neural-interface/lib/claude-skin-connect.js +79 -0
  711. package/neural-interface/lib/claude-stall-policy.js +7 -0
  712. package/neural-interface/lib/claude-temporary.js +435 -0
  713. package/neural-interface/lib/cli-update-planner.js +312 -0
  714. package/neural-interface/lib/codex-capabilities.d.ts +41 -0
  715. package/neural-interface/lib/codex-capabilities.js +148 -0
  716. package/neural-interface/lib/codex-collaboration-mode.js +17 -0
  717. package/neural-interface/lib/codex-compat-rpc.js +241 -0
  718. package/neural-interface/lib/codex-mcp-approvals.js +56 -0
  719. package/neural-interface/lib/codex-mcp-apps.js +47 -0
  720. package/neural-interface/lib/codex-model-context.js +202 -0
  721. package/neural-interface/lib/codex-plan-store.js +71 -0
  722. package/neural-interface/lib/codex-request-journal.js +242 -0
  723. package/neural-interface/lib/codex-runtime-path.js +213 -0
  724. package/neural-interface/lib/codex-session-catalog.js +231 -0
  725. package/neural-interface/lib/codex-session-settings.js +46 -0
  726. package/neural-interface/lib/codex-sidepanel-permissions.js +110 -0
  727. package/neural-interface/lib/codex-thread-history.js +165 -0
  728. package/neural-interface/lib/codex-user-verification.js +34 -0
  729. package/neural-interface/lib/codex-writer-lifecycle.js +152 -0
  730. package/neural-interface/lib/confined-fs.js +256 -0
  731. package/neural-interface/lib/db.js +922 -0
  732. package/neural-interface/lib/design-md.js +9 -0
  733. package/neural-interface/lib/desktop/api.js +92 -0
  734. package/neural-interface/lib/desktop/audit.js +62 -0
  735. package/neural-interface/lib/desktop/build.js +423 -0
  736. package/neural-interface/lib/desktop/config.js +606 -0
  737. package/neural-interface/lib/desktop/coords.js +76 -0
  738. package/neural-interface/lib/desktop/fake-helper.js +1192 -0
  739. package/neural-interface/lib/desktop/frames.js +77 -0
  740. package/neural-interface/lib/desktop/grants.js +84 -0
  741. package/neural-interface/lib/desktop/guards.js +170 -0
  742. package/neural-interface/lib/desktop/helper/SynabunDesktop.swift +3056 -0
  743. package/neural-interface/lib/desktop/lease.js +51 -0
  744. package/neural-interface/lib/desktop/manager.js +545 -0
  745. package/neural-interface/lib/desktop/press-contexts.js +103 -0
  746. package/neural-interface/lib/desktop/protocol.js +495 -0
  747. package/neural-interface/lib/desktop/service.js +1202 -0
  748. package/neural-interface/lib/effort-levels.js +148 -0
  749. package/neural-interface/lib/event-loop-monitor.js +67 -0
  750. package/neural-interface/lib/exec-loop-command.js +209 -0
  751. package/neural-interface/lib/folder-picker.ps1 +77 -0
  752. package/neural-interface/lib/http-guards.js +287 -0
  753. package/neural-interface/lib/jsonl-lines.js +94 -0
  754. package/neural-interface/lib/loop-logger.js +127 -0
  755. package/neural-interface/lib/mcp-client-config.js +133 -0
  756. package/neural-interface/lib/mcp-installer.js +487 -0
  757. package/neural-interface/lib/mcp-profile-refresh-coordinator.js +78 -0
  758. package/neural-interface/lib/mcp-profile-servers.js +120 -0
  759. package/neural-interface/lib/memo-cache.js +84 -0
  760. package/neural-interface/lib/memory-api.js +758 -0
  761. package/neural-interface/lib/memory-map-api.js +1150 -0
  762. package/neural-interface/lib/memory-map-layout.js +872 -0
  763. package/neural-interface/lib/memory-map-worker.js +165 -0
  764. package/neural-interface/lib/memory-session-capture.js +81 -0
  765. package/neural-interface/lib/memory-timers.js +62 -0
  766. package/neural-interface/lib/morelogin.js +301 -0
  767. package/neural-interface/lib/native-binary-runtime.js +382 -0
  768. package/neural-interface/lib/native-loop-goal.js +33 -0
  769. package/neural-interface/lib/native-loop-providers.js +1002 -0
  770. package/neural-interface/lib/native-loop-runtime.js +747 -0
  771. package/neural-interface/lib/offline-page.js +54 -0
  772. package/neural-interface/lib/opencode-hidden-models.js +113 -0
  773. package/neural-interface/lib/opencode-history.js +253 -0
  774. package/neural-interface/lib/opencode-runtime-config.js +58 -0
  775. package/neural-interface/lib/opencode-v2-client.js +486 -0
  776. package/neural-interface/lib/opencode-v2-ws-requests.js +955 -0
  777. package/neural-interface/lib/path-confine.js +213 -0
  778. package/neural-interface/lib/plugin-installer.js +264 -0
  779. package/neural-interface/lib/project-storage.js +766 -0
  780. package/neural-interface/lib/pty-host/core.js +603 -0
  781. package/neural-interface/lib/pty-host/flow.js +43 -0
  782. package/neural-interface/lib/pty-host/host.js +47 -0
  783. package/neural-interface/lib/pty-host/manager.js +360 -0
  784. package/neural-interface/lib/pty-host/protocol.js +81 -0
  785. package/neural-interface/lib/pty-host/snapshot.js +133 -0
  786. package/neural-interface/lib/remote-policy.js +582 -0
  787. package/neural-interface/lib/rulesets/api.js +175 -0
  788. package/neural-interface/lib/rulesets/fs-safe.js +245 -0
  789. package/neural-interface/lib/rulesets/installer.js +1453 -0
  790. package/neural-interface/lib/rulesets/json-edit.js +186 -0
  791. package/neural-interface/lib/rulesets/lock.js +170 -0
  792. package/neural-interface/lib/rulesets/managed-text.js +314 -0
  793. package/neural-interface/lib/rulesets/normalise.js +24 -0
  794. package/neural-interface/lib/rulesets/render.js +84 -0
  795. package/neural-interface/lib/schedule-launch-intent.js +40 -0
  796. package/neural-interface/lib/session-cache-builder.js +524 -0
  797. package/neural-interface/lib/session-chunker.js +344 -0
  798. package/neural-interface/lib/session-indexer.js +327 -0
  799. package/neural-interface/lib/session-title-generator.js +232 -0
  800. package/neural-interface/lib/skill-prompt-bridge.js +11 -0
  801. package/neural-interface/lib/style-guide/api.js +375 -0
  802. package/neural-interface/lib/style-guide/color.js +352 -0
  803. package/neural-interface/lib/style-guide/export-css.js +99 -0
  804. package/neural-interface/lib/style-guide/export-dtcg.js +209 -0
  805. package/neural-interface/lib/style-guide/export-tailwind.js +102 -0
  806. package/neural-interface/lib/style-guide/fonts.json +227 -0
  807. package/neural-interface/lib/style-guide/import.js +1268 -0
  808. package/neural-interface/lib/style-guide/pointers.js +103 -0
  809. package/neural-interface/lib/style-guide/presets.js +232 -0
  810. package/neural-interface/lib/style-guide/render-design-md.js +319 -0
  811. package/neural-interface/lib/style-guide/schema.js +848 -0
  812. package/neural-interface/lib/style-guide/security.js +43 -0
  813. package/neural-interface/lib/style-guide/store.js +491 -0
  814. package/neural-interface/lib/style-guide/summary.js +106 -0
  815. package/neural-interface/lib/style-guide/tokens.js +170 -0
  816. package/neural-interface/lib/synabun-update-plan.js +324 -0
  817. package/neural-interface/lib/synabun-version.js +179 -0
  818. package/neural-interface/lib/system-restore.js +99 -0
  819. package/neural-interface/lib/terminal-env.js +86 -0
  820. package/neural-interface/lib/ui-blob-store.js +106 -0
  821. package/neural-interface/lib/vector-cache.js +2 -0
  822. package/neural-interface/lib/virtual-socket.js +60 -0
  823. package/neural-interface/lib/whatsapp/api.js +146 -0
  824. package/neural-interface/lib/whatsapp/auth-store.js +389 -0
  825. package/neural-interface/lib/whatsapp/baileys-adapter.js +457 -0
  826. package/neural-interface/lib/whatsapp/brain.js +128 -0
  827. package/neural-interface/lib/whatsapp/bridge.js +1499 -0
  828. package/neural-interface/lib/whatsapp/cards.js +519 -0
  829. package/neural-interface/lib/whatsapp/commands.js +92 -0
  830. package/neural-interface/lib/whatsapp/config.js +291 -0
  831. package/neural-interface/lib/whatsapp/connector/entry.mjs +25 -0
  832. package/neural-interface/lib/whatsapp/connector/manifest.json +6 -0
  833. package/neural-interface/lib/whatsapp/connector/npm-shrinkwrap.json +717 -0
  834. package/neural-interface/lib/whatsapp/connector/package.json +8 -0
  835. package/neural-interface/lib/whatsapp/fake-baileys.js +444 -0
  836. package/neural-interface/lib/whatsapp/format.js +552 -0
  837. package/neural-interface/lib/whatsapp/host-core.js +1225 -0
  838. package/neural-interface/lib/whatsapp/host.js +119 -0
  839. package/neural-interface/lib/whatsapp/identity.js +288 -0
  840. package/neural-interface/lib/whatsapp/inbound.js +149 -0
  841. package/neural-interface/lib/whatsapp/installer.js +562 -0
  842. package/neural-interface/lib/whatsapp/manager.js +638 -0
  843. package/neural-interface/lib/whatsapp/paths.js +81 -0
  844. package/neural-interface/lib/whatsapp/protocol.js +327 -0
  845. package/neural-interface/lib/whatsapp/qr-svg.js +57 -0
  846. package/neural-interface/lib/whatsapp/redact.js +130 -0
  847. package/neural-interface/lib/whatsapp/service.js +1305 -0
  848. package/neural-interface/lib/whatsapp/vendor/qrcodegen.js +848 -0
  849. package/neural-interface/package-lock.json +4431 -0
  850. package/neural-interface/package.json +58 -27
  851. package/neural-interface/public/apple-touch-icon.png +0 -0
  852. package/neural-interface/public/category-logos/synabun.png +0 -0
  853. package/neural-interface/public/claude.svg +1 -0
  854. package/neural-interface/public/favicon-16x16.png +0 -0
  855. package/neural-interface/public/favicon-32x32.png +0 -0
  856. package/neural-interface/public/icon-192.png +0 -0
  857. package/neural-interface/public/icon-512.png +0 -0
  858. package/neural-interface/public/index.html +32 -182
  859. package/neural-interface/public/index2d.html +2 -2
  860. package/neural-interface/public/install-app.html +31 -0
  861. package/neural-interface/public/logo-synabun.png +0 -0
  862. package/neural-interface/public/logoHD.png +0 -0
  863. package/neural-interface/public/manifest.json +2 -0
  864. package/neural-interface/public/mcp.svg +4 -0
  865. package/neural-interface/public/offline.html +670 -299
  866. package/neural-interface/public/onboarding.html +804 -161
  867. package/neural-interface/public/opencode-logo-dark-square.svg +1 -0
  868. package/neural-interface/public/shared/agent-runtime-options.js +596 -0
  869. package/neural-interface/public/shared/api.js +339 -28
  870. package/neural-interface/public/shared/assistant/asst-accounts.js +318 -0
  871. package/neural-interface/public/shared/assistant/asst-agents-dock.js +296 -0
  872. package/neural-interface/public/shared/assistant/asst-api.js +270 -0
  873. package/neural-interface/public/shared/assistant/asst-brain-picker.js +744 -0
  874. package/neural-interface/public/shared/assistant/asst-budget.js +333 -0
  875. package/neural-interface/public/shared/assistant/asst-composer.js +656 -0
  876. package/neural-interface/public/shared/assistant/asst-computer.js +803 -0
  877. package/neural-interface/public/shared/assistant/asst-control.js +1018 -0
  878. package/neural-interface/public/shared/assistant/asst-hosts.js +262 -0
  879. package/neural-interface/public/shared/assistant/asst-mascot.js +313 -0
  880. package/neural-interface/public/shared/assistant/asst-menu.js +375 -0
  881. package/neural-interface/public/shared/assistant/asst-panel.js +1845 -0
  882. package/neural-interface/public/shared/assistant/asst-rack-styles.js +586 -0
  883. package/neural-interface/public/shared/assistant/asst-render.js +3305 -0
  884. package/neural-interface/public/shared/assistant/asst-route.js +1653 -0
  885. package/neural-interface/public/shared/assistant/asst-roving.js +75 -0
  886. package/neural-interface/public/shared/assistant/asst-sanitize.js +117 -0
  887. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +488 -0
  888. package/neural-interface/public/shared/assistant/asst-sidepanel.js +675 -0
  889. package/neural-interface/public/shared/assistant/asst-slash.js +258 -0
  890. package/neural-interface/public/shared/assistant/asst-state.js +727 -0
  891. package/neural-interface/public/shared/assistant/asst-styles.js +1686 -0
  892. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +1017 -0
  893. package/neural-interface/public/shared/assistant/asst-usage.js +444 -0
  894. package/neural-interface/public/shared/assistant/asst-ws.js +175 -0
  895. package/neural-interface/public/shared/automation-context.js +75 -0
  896. package/neural-interface/public/shared/blob-store.js +99 -0
  897. package/neural-interface/public/shared/cdx/cdx-capabilities.js +118 -0
  898. package/neural-interface/public/shared/cdx/cdx-cards.js +37 -0
  899. package/neural-interface/public/shared/cdx/cdx-commands.js +171 -0
  900. package/neural-interface/public/shared/cdx/cdx-context-menu.js +402 -0
  901. package/neural-interface/public/shared/cdx/cdx-context-model.js +220 -0
  902. package/neural-interface/public/shared/cdx/cdx-features.js +408 -0
  903. package/neural-interface/public/shared/cdx/cdx-icons.js +89 -0
  904. package/neural-interface/public/shared/cdx/cdx-mcp-app-host.js +414 -0
  905. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.html +6 -0
  906. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.js +56 -0
  907. package/neural-interface/public/shared/cdx/cdx-mcp-app.js +178 -0
  908. package/neural-interface/public/shared/cdx/cdx-model-catalog.js +120 -0
  909. package/neural-interface/public/shared/cdx/cdx-output-model.js +261 -0
  910. package/neural-interface/public/shared/cdx/cdx-output.js +382 -0
  911. package/neural-interface/public/shared/cdx/cdx-panel.js +1372 -0
  912. package/neural-interface/public/shared/cdx/cdx-plan.js +266 -0
  913. package/neural-interface/public/shared/cdx/cdx-protocol.js +682 -0
  914. package/neural-interface/public/shared/cdx/cdx-render.js +3381 -0
  915. package/neural-interface/public/shared/cdx/cdx-requests.js +1136 -0
  916. package/neural-interface/public/shared/cdx/cdx-styles.js +3251 -0
  917. package/neural-interface/public/shared/cdx/cdx-tabs.js +7992 -0
  918. package/neural-interface/public/shared/cdx/cdx-ws.js +363 -0
  919. package/neural-interface/public/shared/cli-status.js +169 -0
  920. package/neural-interface/public/shared/command-runner-model.js +30 -0
  921. package/neural-interface/public/shared/constants.js +22 -5
  922. package/neural-interface/public/shared/cp/cp-agents.js +186 -0
  923. package/neural-interface/public/shared/cp/cp-bash.js +252 -0
  924. package/neural-interface/public/shared/cp/cp-compose.js +159 -0
  925. package/neural-interface/public/shared/cp/cp-context-menu.js +309 -0
  926. package/neural-interface/public/shared/cp/cp-context-model.js +157 -0
  927. package/neural-interface/public/shared/cp/cp-context-window.js +80 -0
  928. package/neural-interface/public/shared/cp/cp-ctx.js +11 -0
  929. package/neural-interface/public/shared/cp/cp-diff.js +255 -0
  930. package/neural-interface/public/shared/cp/cp-event-rows.js +637 -0
  931. package/neural-interface/public/shared/cp/cp-events.js +846 -0
  932. package/neural-interface/public/shared/cp/cp-markdown.js +104 -0
  933. package/neural-interface/public/shared/cp/cp-permission-model.js +905 -0
  934. package/neural-interface/public/shared/cp/cp-permissions.js +626 -0
  935. package/neural-interface/public/shared/cp/cp-rehydrate.js +113 -0
  936. package/neural-interface/public/shared/cp/cp-restore.js +224 -0
  937. package/neural-interface/public/shared/cp/cp-session-model.js +338 -0
  938. package/neural-interface/public/shared/cp/cp-session-settings.js +254 -0
  939. package/neural-interface/public/shared/cp/cp-sessions.js +227 -0
  940. package/neural-interface/public/shared/cp/cp-statusline.js +153 -0
  941. package/neural-interface/public/shared/cp/cp-styles.js +349 -0
  942. package/neural-interface/public/shared/cp/cp-tasks-model.js +282 -0
  943. package/neural-interface/public/shared/cp/cp-tasks.js +107 -0
  944. package/neural-interface/public/shared/cp/cp-temporary-view.js +56 -0
  945. package/neural-interface/public/shared/cp/cp-temporary.js +231 -0
  946. package/neural-interface/public/shared/cp/cp-tool-cards.js +326 -0
  947. package/neural-interface/public/shared/cp/cp-tool-results.js +618 -0
  948. package/neural-interface/public/shared/cp/cp-usage-model.js +124 -0
  949. package/neural-interface/public/shared/graph-activity.js +68 -0
  950. package/neural-interface/public/shared/html-shell.js +116 -17
  951. package/neural-interface/public/shared/i18n.js +132 -34
  952. package/neural-interface/public/shared/node-positions-store.js +44 -0
  953. package/neural-interface/public/shared/ocp-hidden-models.js +116 -0
  954. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +355 -0
  955. package/neural-interface/public/shared/ocp-v2/ocp-v2-autoaccept-button.js +35 -0
  956. package/neural-interface/public/shared/ocp-v2/ocp-v2-automation-ownership.js +72 -0
  957. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +280 -0
  958. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +67 -0
  959. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +179 -0
  960. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes.js +164 -0
  961. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +725 -0
  962. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +75 -0
  963. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +1204 -0
  964. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm-logic.js +117 -0
  965. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm.js +59 -0
  966. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-gauge.js +252 -0
  967. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +478 -0
  968. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +179 -0
  969. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +233 -0
  970. package/neural-interface/public/shared/ocp-v2/ocp-v2-icons.js +30 -0
  971. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +1065 -0
  972. package/neural-interface/public/shared/ocp-v2/ocp-v2-mention-hints.js +182 -0
  973. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +655 -0
  974. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +2636 -0
  975. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +601 -0
  976. package/neural-interface/public/shared/ocp-v2/ocp-v2-profile-inheritance.js +78 -0
  977. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +649 -0
  978. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +35 -0
  979. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +357 -0
  980. package/neural-interface/public/shared/ocp-v2/ocp-v2-render-logic.js +189 -0
  981. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1869 -0
  982. package/neural-interface/public/shared/ocp-v2/ocp-v2-select.js +42 -0
  983. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +117 -0
  984. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +1714 -0
  985. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +468 -0
  986. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-widgets.js +63 -0
  987. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +366 -0
  988. package/neural-interface/public/shared/ocp-v2/ocp-v2-settings-link.js +19 -0
  989. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +343 -0
  990. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +746 -0
  991. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +347 -0
  992. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +338 -0
  993. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +3543 -0
  994. package/neural-interface/public/shared/ocp-v2/ocp-v2-subagents-logic.js +482 -0
  995. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools-logic.js +351 -0
  996. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools.js +168 -0
  997. package/neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js +315 -0
  998. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +545 -0
  999. package/neural-interface/public/shared/provider-icons.js +30 -0
  1000. package/neural-interface/public/shared/pwa-install-page.js +34 -0
  1001. package/neural-interface/public/shared/pwa-install.css +12 -0
  1002. package/neural-interface/public/shared/pwa-install.js +28 -0
  1003. package/neural-interface/public/shared/session-title.js +100 -0
  1004. package/neural-interface/public/shared/settings/settings-ia.js +389 -0
  1005. package/neural-interface/public/shared/settings/settings-kit.js +234 -0
  1006. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  1007. package/neural-interface/public/shared/settings/settings-search.js +463 -0
  1008. package/neural-interface/public/shared/settings/settings-shell.js +570 -0
  1009. package/neural-interface/public/shared/start-bridge.js +583 -0
  1010. package/neural-interface/public/shared/state.js +5 -0
  1011. package/neural-interface/public/shared/storage.js +50 -7
  1012. package/neural-interface/public/shared/styleguide/sg-agents.js +42 -0
  1013. package/neural-interface/public/shared/styleguide/sg-brand.js +4 -0
  1014. package/neural-interface/public/shared/styleguide/sg-color-client.js +388 -0
  1015. package/neural-interface/public/shared/styleguide/sg-colors.js +151 -0
  1016. package/neural-interface/public/shared/styleguide/sg-components.js +35 -0
  1017. package/neural-interface/public/shared/styleguide/sg-history.js +41 -0
  1018. package/neural-interface/public/shared/styleguide/sg-icons-logo.js +38 -0
  1019. package/neural-interface/public/shared/styleguide/sg-imagery.js +4 -0
  1020. package/neural-interface/public/shared/styleguide/sg-import-export.js +48 -0
  1021. package/neural-interface/public/shared/styleguide/sg-kit.js +284 -0
  1022. package/neural-interface/public/shared/styleguide/sg-labels.js +318 -0
  1023. package/neural-interface/public/shared/styleguide/sg-layout.js +4 -0
  1024. package/neural-interface/public/shared/styleguide/sg-motion.js +5 -0
  1025. package/neural-interface/public/shared/styleguide/sg-panel.js +237 -0
  1026. package/neural-interface/public/shared/styleguide/sg-preview.js +138 -0
  1027. package/neural-interface/public/shared/styleguide/sg-rules.js +9 -0
  1028. package/neural-interface/public/shared/styleguide/sg-shape.js +24 -0
  1029. package/neural-interface/public/shared/styleguide/sg-state.js +132 -0
  1030. package/neural-interface/public/shared/styleguide/sg-typography.js +125 -0
  1031. package/neural-interface/public/shared/styles.css +8614 -1477
  1032. package/neural-interface/public/shared/synabun-mascot.js +1625 -0
  1033. package/neural-interface/public/shared/synabun-ticker.js +376 -0
  1034. package/neural-interface/public/shared/term-ready.js +307 -0
  1035. package/neural-interface/public/shared/term-renderer.js +343 -0
  1036. package/neural-interface/public/shared/ui-assistant-panel.js +10 -0
  1037. package/neural-interface/public/shared/ui-assistant.js +77 -0
  1038. package/neural-interface/public/shared/ui-automation-studio.js +5105 -3573
  1039. package/neural-interface/public/shared/ui-claude-panel.js +11685 -6333
  1040. package/neural-interface/public/shared/ui-codex-panel.js +12 -0
  1041. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  1042. package/neural-interface/public/shared/ui-cost-widget.js +55 -34
  1043. package/neural-interface/public/shared/ui-detail.js +77 -14
  1044. package/neural-interface/public/shared/ui-explorer.js +360 -181
  1045. package/neural-interface/public/shared/ui-file-explorer.js +485 -107
  1046. package/neural-interface/public/shared/ui-help.js +2 -2
  1047. package/neural-interface/public/shared/ui-image-gallery.js +45 -21
  1048. package/neural-interface/public/shared/ui-judgments.js +1346 -0
  1049. package/neural-interface/public/shared/ui-keybinds.js +3 -2
  1050. package/neural-interface/public/shared/ui-link.js +105 -51
  1051. package/neural-interface/public/shared/ui-loading.js +222 -11
  1052. package/neural-interface/public/shared/ui-memory-maintenance.js +111 -0
  1053. package/neural-interface/public/shared/ui-menubar.js +100 -31
  1054. package/neural-interface/public/shared/ui-native-loop-router-state.js +94 -0
  1055. package/neural-interface/public/shared/ui-native-loop-router.js +493 -0
  1056. package/neural-interface/public/shared/ui-native-window-id.js +7 -0
  1057. package/neural-interface/public/shared/ui-navbar.js +357 -222
  1058. package/neural-interface/public/shared/ui-notifications.js +58 -12
  1059. package/neural-interface/public/shared/ui-opencode-panel-v2.js +17 -0
  1060. package/neural-interface/public/shared/ui-panels.js +15 -7
  1061. package/neural-interface/public/shared/ui-resume.js +823 -653
  1062. package/neural-interface/public/shared/ui-schedule-queue.js +172 -0
  1063. package/neural-interface/public/shared/ui-schedules-studio.js +1882 -0
  1064. package/neural-interface/public/shared/ui-scroll-follow.js +194 -0
  1065. package/neural-interface/public/shared/ui-sessions.js +789 -34
  1066. package/neural-interface/public/shared/ui-settings.js +5774 -2194
  1067. package/neural-interface/public/shared/ui-sidepanel-layout.js +109 -0
  1068. package/neural-interface/public/shared/ui-sidepanel-runtime.js +19 -0
  1069. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +40 -0
  1070. package/neural-interface/public/shared/ui-sidepanel-sessions.js +221 -0
  1071. package/neural-interface/public/shared/ui-sidepanel-state.js +131 -0
  1072. package/neural-interface/public/shared/ui-sidepanel-tray.js +547 -0
  1073. package/neural-interface/public/shared/ui-sidepanel-windows.js +387 -0
  1074. package/neural-interface/public/shared/ui-skills.js +18 -32
  1075. package/neural-interface/public/shared/ui-styleguide.js +35 -0
  1076. package/neural-interface/public/shared/ui-sync.js +87 -0
  1077. package/neural-interface/public/shared/ui-terminal.js +7863 -6385
  1078. package/neural-interface/public/shared/ui-tool-updates.js +292 -0
  1079. package/neural-interface/public/shared/ui-tooltip.js +156 -15
  1080. package/neural-interface/public/shared/ui-tutorial-draw.js +119 -683
  1081. package/neural-interface/public/shared/ui-tutorial-layout.js +135 -0
  1082. package/neural-interface/public/shared/ui-tutorial-steps.js +59 -2209
  1083. package/neural-interface/public/shared/ui-tutorial.css +102 -0
  1084. package/neural-interface/public/shared/ui-tutorial.js +557 -1076
  1085. package/neural-interface/public/shared/ui-update.js +420 -68
  1086. package/neural-interface/public/shared/ui-whatsapp.js +941 -0
  1087. package/neural-interface/public/shared/ui-whiteboard.js +1076 -233
  1088. package/neural-interface/public/shared/ui-workspaces.js +2 -1
  1089. package/neural-interface/public/shared/utils.js +58 -0
  1090. package/neural-interface/public/shared/vterm-buffer.js +15 -5
  1091. package/neural-interface/public/shared/whatsapp/wa-phone.js +65 -0
  1092. package/neural-interface/public/shared/whatsapp/wa-view.js +411 -0
  1093. package/neural-interface/public/sidepanel-session.html +18 -0
  1094. package/neural-interface/public/sw.js +10 -2
  1095. package/neural-interface/public/synabun.png +0 -0
  1096. package/neural-interface/public/variant/2d/graph.js +1707 -1635
  1097. package/neural-interface/public/variant/2d/lasso.js +2 -1
  1098. package/neural-interface/public/variant/2d/layout.js +595 -594
  1099. package/neural-interface/public/variant/2d/main.js +557 -521
  1100. package/neural-interface/public/variant/2d/settings-gfx.js +18 -24
  1101. package/neural-interface/public/variant/3d/main.js +517 -500
  1102. package/neural-interface/public/variant/3d/map-controls.js +144 -0
  1103. package/neural-interface/public/variant/3d/map-data.js +251 -0
  1104. package/neural-interface/public/variant/3d/map-labels.js +241 -0
  1105. package/neural-interface/public/variant/3d/map-pick.js +90 -0
  1106. package/neural-interface/public/variant/3d/map-renderer.js +521 -0
  1107. package/neural-interface/public/variant/3d/map-shaders.js +158 -0
  1108. package/neural-interface/public/variant/3d/map-view.js +618 -0
  1109. package/neural-interface/public/vendor/three/manifest.json +3 -0
  1110. package/neural-interface/public/vendor/three/three-0.169.0.min.js +3844 -0
  1111. package/neural-interface/public/vendor/xterm/manifest.json +3 -0
  1112. package/neural-interface/public/vendor/xterm/xterm-6.0.0.css +285 -0
  1113. package/neural-interface/public/vendor/xterm/xterm-6.0.0.min.js +192 -0
  1114. package/neural-interface/scripts/build-vendor.mjs +64 -0
  1115. package/neural-interface/scripts/codex-protocol-fixture.mjs +83 -0
  1116. package/neural-interface/scripts/rebuild-pty.js +39 -22
  1117. package/neural-interface/scripts/vendor-three-entry.js +24 -0
  1118. package/neural-interface/scripts/vendor-xterm-entry.js +8 -0
  1119. package/neural-interface/server.js +33983 -15000
  1120. package/neural-interface/skins/default/skin.css +90 -0
  1121. package/neural-interface/skins/default/skin.json +8 -0
  1122. package/neural-interface/templates/rulesets/README.md +9 -0
  1123. package/neural-interface/templates/rulesets/claude.md +3 -0
  1124. package/neural-interface/templates/rulesets/codex.md +2 -0
  1125. package/neural-interface/templates/rulesets/coexistence.md +6 -0
  1126. package/neural-interface/templates/rulesets/core.md +26 -0
  1127. package/neural-interface/templates/rulesets/cursor.md +2 -0
  1128. package/neural-interface/templates/rulesets/gemini.md +2 -0
  1129. package/neural-interface/templates/rulesets/legacy-hashes.json +227 -0
  1130. package/neural-interface/templates/rulesets/manifest.json +12 -0
  1131. package/neural-interface/templates/rulesets/opencode.md +2 -0
  1132. package/package.json +131 -53
  1133. package/postinstall.js +55 -26
  1134. package/preuninstall.js +187 -0
  1135. package/scripts/restore-data-home-from-backup.mjs +239 -0
  1136. package/setup.js +872 -260
  1137. package/skills/leonardo/SKILL.md +19 -10
  1138. package/skills/leonardo/modules/image-prompter.md +18 -12
  1139. package/skills/leonardo/modules/video-prompter.md +19 -13
  1140. package/skills/synabun/SKILL.md +209 -185
  1141. package/skills/synabun/icon.png +0 -0
  1142. package/skills/synabun/modules/audit.md +380 -374
  1143. package/skills/synabun/modules/changelog.md +15 -6
  1144. package/skills/synabun/modules/idea.md +148 -144
  1145. package/skills/synabun/modules/memorize.md +13 -4
  1146. package/skills/synabun/modules/schedule.md +198 -0
  1147. package/skills/synabun/synabunicon.png +0 -0
  1148. package/updater.cmd +31 -0
  1149. package/updater.mjs +382 -0
  1150. package/updater.sh +29 -0
  1151. package/hooks/claude-code/test-user-learning.mjs +0 -531
  1152. package/neural-interface/public/claude-chat.css +0 -878
  1153. package/neural-interface/public/claude-chat.html +0 -92
  1154. package/neural-interface/public/claude-chat.js +0 -1168
  1155. package/neural-interface/public/index-legacy.html +0 -14677
  1156. package/neural-interface/public/index2d-legacy.html +0 -8193
  1157. package/neural-interface/public/shared/html-term-renderer.js +0 -1022
  1158. package/neural-interface/public/variant/3d/background.js +0 -884
  1159. package/neural-interface/public/variant/3d/camera.js +0 -661
  1160. package/neural-interface/public/variant/3d/gfx.js +0 -116
  1161. package/neural-interface/public/variant/3d/graph.js +0 -2399
  1162. package/neural-interface/public/variant/3d/settings-gfx.js +0 -228
  1163. package/neural-interface/templates/CLAUDE-template.md +0 -211
@@ -0,0 +1,1714 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // OpenCode V2 — Compose box (textarea + Send/Abort) — multi-instance
3
+ // Each mountCompose(rootEl, store) creates an independent compose instance
4
+ // bound to its own DOM and store. Returns { unmount, focus, appendPath,
5
+ // sendTextMessage }. Legacy named exports route to the primary (first-mounted)
6
+ // instance for back-compat with shared bindings.
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+
9
+ import { api, supports, capabilities, onEvent } from './ocp-v2-ws.js';
10
+ import { getDefaultStore, agentForMode } from './ocp-v2-state.js';
11
+ import { mountModelPicker } from './ocp-v2-modelpicker.js';
12
+ import { mountVariantPicker } from './ocp-v2-variantpicker.js';
13
+ import { mountSlashHints, getSlashCatalog, loadSlashCatalog } from './ocp-v2-slash-hints.js';
14
+ import { mountMentionHints } from './ocp-v2-mention-hints.js';
15
+ import { replyFailed, replyError } from './ocp-v2-caps.js';
16
+ import { fetchProvidersFull } from './ocp-v2-providers.js';
17
+ import {
18
+ resolveSlash, shellKey, shellAfterProgrammaticInput, shellRunAllowed,
19
+ createPromptQueue, queueCanDrain, createPromptHistory, historyKeyApplies, escapeStop,
20
+ createAgentCatalog, createAgentChoice, resolveAgent, nextAgent, modeForAgent, agentLabel,
21
+ pathFileParts, attachmentVerdict, modelCapabilitiesOf, mentionFileParts, commandFileParts,
22
+ mentionGroups, createKeyedCache, createParkedPrompts, parkedPromptsNotice, parkingFullNotice,
23
+ keptPromptNotice, keptPromptDraft, helpCardView,
24
+ } from './ocp-v2-composer-logic.js';
25
+ import { storage } from '../storage.js';
26
+ import { emit } from '../state.js';
27
+ import {
28
+ configurePlanLifecycle, beginPlanTurnForSend, maybeFinalizePlanTurn,
29
+ isPostPlanBlocked,
30
+ } from './ocp-v2-plan.js';
31
+ import {
32
+ trackOptimisticMessage, beginTurn, currentTurn, endTurn, settleFailedSend, TRANSCRIPT_SAFETY_POLL_MS,
33
+ } from './ocp-v2-send-logic.js';
34
+ import { hydrateTranscript } from './ocp-v2-rehydrate.js';
35
+ import { captureBinding, trackBinding, isBindingToken, searchOnBinding } from './ocp-v2-binding.js';
36
+
37
+ const STOR_MODE = 'opencode-v2-mode'; // the last agent picked (build / plan / a custom one)
38
+ const STOR_HISTORY = 'ocp-v2-prompt-history';
39
+
40
+ let _primaryInstance = null; // first compose mounted; legacy targets
41
+
42
+ // ── Shared lookups (one fetch for every compose on the page) ────────────────
43
+ // Agents per project directory. The composer mounts before OpenCode is up, so
44
+ // an empty answer is not kept: it is asked again once the server is ready.
45
+ const _agentCatalog = createAgentCatalog(async (cwd) => {
46
+ if (supports('agent:list')) {
47
+ const res = await api.agentList({ cwd: cwd || undefined });
48
+ return replyFailed(res) ? [] : res.data;
49
+ }
50
+ // A server that predates agent:list answers for its own directory only.
51
+ const res = await fetch('/api/opencode/agents').then((r) => r.json());
52
+ return res?.data;
53
+ });
54
+
55
+ // What the @ picker lists besides files and symbols. Both are whole lists that
56
+ // change rarely, so they are read once per session + directory and filtered
57
+ // in the picker while the user types.
58
+ const dataOrEmpty = (res) => (replyFailed(res) || !Array.isArray(res.data) ? [] : res.data);
59
+ const splitKey = (key) => { const at = key.indexOf('|'); return { sessionId: key.slice(0, at) || undefined, cwd: key.slice(at + 1) || undefined }; };
60
+ const _mcpResources = createKeyedCache(async (key) => dataOrEmpty(await api.resourceList(splitKey(key))));
61
+ const _references = createKeyedCache(async (key) => dataOrEmpty(await api.referenceList(splitKey(key))));
62
+ // A server was added, connected or dropped, or the references changed: ask again.
63
+ onEvent((eventType) => {
64
+ if (eventType === 'mcp.tools.changed') _mcpResources.clear();
65
+ else if (eventType === 'reference.updated') _references.clear();
66
+ });
67
+
68
+ let _providersPromise = null;
69
+ function loadProviders() {
70
+ if (!_providersPromise) {
71
+ _providersPromise = fetchProvidersFull()
72
+ .then((res) => { const data = res?.data || {}; return data.all || data.providers || []; })
73
+ .catch(() => { _providersPromise = null; return []; });
74
+ }
75
+ return _providersPromise;
76
+ }
77
+ if (typeof document !== 'undefined') {
78
+ document.addEventListener('ocp-providers-changed', () => { _providersPromise = null; });
79
+ }
80
+
81
+ function loadStoredHistory() {
82
+ try {
83
+ const parsed = JSON.parse(sessionStorage.getItem(STOR_HISTORY) || '[]');
84
+ return Array.isArray(parsed) ? parsed : [];
85
+ } catch { return []; }
86
+ }
87
+
88
+ // A File as a data URL announced with `mime` (a text file must say text/plain
89
+ // for OpenCode to read it as text, whatever the browser guessed).
90
+ function readFileAsDataUrl(file, mime) {
91
+ return new Promise((resolve, reject) => {
92
+ const reader = new FileReader();
93
+ reader.onerror = () => reject(reader.error || new Error('Could not read the file'));
94
+ reader.onload = () => {
95
+ const result = String(reader.result || '');
96
+ const comma = result.indexOf(',');
97
+ resolve(comma < 0 ? result : `data:${mime};base64,${result.slice(comma + 1)}`);
98
+ };
99
+ reader.readAsDataURL(file);
100
+ });
101
+ }
102
+
103
+ export function mountCompose(rootEl, store = getDefaultStore(), opts = {}) {
104
+ // The model/variant pickers are module-level singletons (see ocp-v2-modelpicker
105
+ // / ocp-v2-variantpicker): each mount removes the previously-mounted dropdown
106
+ // from the DOM. The main panel mounts first; if a child/sub-agent panel then
107
+ // mounts its own compose it would steal those dropdowns and leave the main
108
+ // footer empty. Child composes therefore opt out of pickers — sub-agents
109
+ // inherit the parent session's model, so they don't need their own selectors.
110
+ const mountPickers = opts.pickers !== false;
111
+ const storedAgent = storage.getItem(STOR_MODE);
112
+ if (storedAgent) store.setAgent(storedAgent);
113
+ // What this composer registers, it removes when it is unmounted (a sub-agent
114
+ // panel that is closed): the plan lifecycle would go on visiting its store
115
+ // and calling its sender.
116
+ const releasePlanLifecycle = configurePlanLifecycle({
117
+ sendTextMessage: (text, opts) => sendTextMessage(text, opts),
118
+ store,
119
+ });
120
+
121
+ let _root = rootEl;
122
+ let _input = null;
123
+ let _btn = null;
124
+ let _imageStrip = null;
125
+ let _pathStrip = null;
126
+ let _modeButtons = [];
127
+ let _modeToggle = null;
128
+ let _modelPicker = null;
129
+ let _variantPicker = null;
130
+ let _slashHints = null;
131
+ let _mentionHints = null;
132
+ let _queueTray = null;
133
+ let _wrap = null;
134
+ let _fileInput = null;
135
+ // The agents on offer and whether the selected one may stay. The list
136
+ // belongs to a project directory: see createAgentChoice.
137
+ const _agentChoice = createAgentChoice();
138
+ let _lastServerStatus = store.getState().serverStatus;
139
+ // The directory this composer's session runs in (a sub-agent panel has no
140
+ // cwd of its own; its session knows the directory).
141
+ const composerCwd = () => { const s = store.getState(); return s.cwd || s.sessionInfo?.directory || ''; };
142
+ let _composerCwd = composerCwd();
143
+ let _shell = false; // shell mode: see shellKey() for the only way in
144
+ let _shellTypedValue = ''; // what the user's own input events put in the box while in shell mode
145
+ let _escArmedAt = 0;
146
+ let _draining = false;
147
+ const _queue = createPromptQueue();
148
+ // Prompts and commands that left the queue or the box and that OpenCode has
149
+ // not accepted yet. Until it has, each is still this composer's: a sub-agent
150
+ // panel counts them as unsent before it closes (`sending()`), and when the
151
+ // composer is unmounted while one is out and its send then fails, the
152
+ // prompt goes to `opts.onUndelivered`, whole, instead of into a box, a strip
153
+ // or a queue nobody will see again.
154
+ const _outgoing = new Set();
155
+ let _unmounted = false;
156
+ const undeliverable = () => _unmounted && typeof opts.onUndelivered === 'function';
157
+ const handUndelivered = (item, error = '') => {
158
+ try { opts.onUndelivered(item, { error }); } catch (err) { console.warn('[ocp-v2-send] handing over an undelivered prompt failed', err); }
159
+ };
160
+ // What a turn of this composer keeps running (its transcript poll, the wait
161
+ // for its end): stopped when the composer is unmounted.
162
+ const _turnWatches = new Set();
163
+ // Nothing the user queued or sent is lost when the panel moves. What a
164
+ // session's composer still owes it waits here for that session: its queue
165
+ // as it was when the panel left, and prompts or commands that failed to go
166
+ // out after it had left. `_queueNote` says why they are back in the tray;
167
+ // `_queueBack` is what the note counts.
168
+ // Nothing is evicted from the parking: every entry is unsent prompts. Its
169
+ // bound is kept at the one place where refusing loses nothing (onEnter).
170
+ // Nothing is parked for a session that has no tab (opts.hasTab, from the
171
+ // panel): what fails for one is kept for the user instead (keepPrompts).
172
+ const _parked = createParkedPrompts({ hasTab: opts.hasTab });
173
+ let _queueNote = '';
174
+ let _queueBack = null;
175
+ // The session this composer's queue belongs to (the store's, as of the last
176
+ // rebinding this composer handled).
177
+ let _queueSessionId = store.getState().sessionId || null;
178
+ // Everything below that outlives an await captures the store's binding
179
+ // first and checks it afterwards: see ocp-v2-binding.js.
180
+ const _rebind = trackBinding(store);
181
+ const _history = createPromptHistory(loadStoredHistory());
182
+ const _pickedMentions = new Map(); // token → item chosen from the @ picker (see mentionGroups)
183
+ let _suppressAbortErrorUntil = 0;
184
+ let _suppressAbortErrorCount = 0;
185
+
186
+ _root.innerHTML = '';
187
+ _root.className = 'ocpv2-compose';
188
+
189
+ // Prompts typed while a turn runs wait here (client-side queue).
190
+ _queueTray = document.createElement('div');
191
+ _queueTray.className = 'ocpv2-queue-tray';
192
+ _queueTray.hidden = true;
193
+ _root.appendChild(_queueTray);
194
+
195
+ // Attachment strip — images, PDFs and text files (rendered above input)
196
+ _imageStrip = document.createElement('div');
197
+ _imageStrip.className = 'ocpv2-image-strip';
198
+ _imageStrip.hidden = true;
199
+ _root.appendChild(_imageStrip);
200
+
201
+ _pathStrip = document.createElement('div');
202
+ _pathStrip.className = 'ocpv2-path-strip';
203
+ _pathStrip.hidden = true;
204
+ _root.appendChild(_pathStrip);
205
+
206
+ const area = document.createElement('div');
207
+ area.className = 'ocpv2-input-area';
208
+
209
+ const wrap = document.createElement('div');
210
+ wrap.className = 'ocpv2-input-wrap';
211
+ _wrap = wrap;
212
+
213
+ const inner = document.createElement('div');
214
+ inner.className = 'ocpv2-input-inner';
215
+
216
+ _input = document.createElement('textarea');
217
+ _input.className = 'ocpv2-compose-input';
218
+ _input.placeholder = 'Send a message…';
219
+ _input.rows = 1;
220
+ _input.setAttribute('autocomplete', 'off');
221
+ _input.setAttribute('spellcheck', 'false');
222
+ _input.addEventListener('keydown', onKeyDown);
223
+ _input.addEventListener('input', onInput);
224
+ _input.addEventListener('paste', onPaste);
225
+ inner.appendChild(_input);
226
+ _root.addEventListener('dragover', onDragOver);
227
+ _root.addEventListener('drop', onDrop);
228
+
229
+ _btn = document.createElement('button');
230
+ _btn.className = 'ocpv2-compose-btn';
231
+ _btn.type = 'button';
232
+ _btn.setAttribute('data-tooltip', 'Send');
233
+ _btn.innerHTML =
234
+ '<span class="ocpv2-send-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg></span>'
235
+ + '<span class="ocpv2-stop-icon"><svg viewBox="0 0 24 24" fill="currentColor"><rect x="7" y="7" width="10" height="10" rx="2"/></svg></span>';
236
+ _btn.addEventListener('click', onSendOrAbort);
237
+ inner.appendChild(_btn);
238
+
239
+ wrap.appendChild(inner);
240
+ area.appendChild(wrap);
241
+ _root.appendChild(area);
242
+
243
+ // Slash-command picker. Anchored to .ocpv2-input-area (which is set to
244
+ // position:relative in CSS) so it floats above the textarea via
245
+ // bottom:100%. Mounting on .ocpv2-input-wrap won't work — that element has
246
+ // overflow:hidden for the rotating border mask and would clip the picker.
247
+ _slashHints = mountSlashHints(area, _input, {
248
+ onTuiSelect: (cmd) => openInTui(cmd.name),
249
+ // A panel action picked from the menu runs at once.
250
+ onLocalSelect: (cmd) => { runLocalSlash({ action: cmd.action, name: cmd.name, args: '' }); },
251
+ isAvailable: () => !_shell,
252
+ getCwd: composerCwd,
253
+ });
254
+ _mentionHints = mountMentionHints(area, _input, {
255
+ search: searchMentions,
256
+ isAvailable: () => mentionSourcesAvailable() && !_shell,
257
+ onPick: (item) => { _pickedMentions.set(item.token, item); },
258
+ });
259
+ loadSlashCatalog(_composerCwd);
260
+
261
+ const toolbar = document.createElement('div');
262
+ toolbar.className = 'ocpv2-footer-toolbar';
263
+ toolbar.innerHTML =
264
+ '<div class="ocpv2-footer-left">'
265
+ + '<a class="ocpv2-brand-link" href="https://opencode.ai" target="_blank" rel="noopener noreferrer" data-tooltip="OpenCode">'
266
+ + '<svg class="ocpv2-brand" viewBox="0 0 240 300" fill="currentColor" width="14" height="14"><path fill-rule="evenodd" d="M0 0h240v300H0V0zm30 30v240h180V30H30z"/><rect x="30" y="150" width="180" height="120" opacity=".45"/></svg>'
267
+ + '</a>'
268
+ + '<button class="ocpv2-attach-btn" type="button" data-tooltip="Attach images, PDFs or text files" aria-label="Attach files">'
269
+ + '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" width="13" height="13"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>'
270
+ + '</button>'
271
+ + '<div class="ocpv2-mode-toggle" role="group" aria-label="OpenCode agent"></div>'
272
+ + '</div>'
273
+ + '<div class="ocpv2-footer-right"></div>';
274
+ _root.appendChild(toolbar);
275
+
276
+ _modeToggle = toolbar.querySelector('.ocpv2-mode-toggle');
277
+ renderAgentButtons();
278
+ refreshAgents();
279
+
280
+ _fileInput = document.createElement('input');
281
+ _fileInput.type = 'file';
282
+ _fileInput.multiple = true;
283
+ _fileInput.hidden = true;
284
+ _fileInput.addEventListener('change', () => {
285
+ addFiles(_fileInput.files);
286
+ _fileInput.value = '';
287
+ });
288
+ _root.appendChild(_fileInput);
289
+ toolbar.querySelector('.ocpv2-attach-btn')?.addEventListener('click', () => _fileInput?.click());
290
+
291
+ const footerRight = toolbar.querySelector('.ocpv2-footer-right');
292
+ if (footerRight && mountPickers) {
293
+ _variantPicker = mountVariantPicker(footerRight, store);
294
+ _modelPicker = mountModelPicker(footerRight, store);
295
+ }
296
+
297
+ const unsubscribe = store.subscribe((event, state) => {
298
+ // First of all: the panel was bound to another session. What belonged to
299
+ // the binding it left goes before anything below looks at the new one.
300
+ if (_rebind.changed()) onRebound(state);
301
+ if (event?.type === 'attachments:set') renderImageStrip();
302
+ if (event?.type === 'paths:set') renderPathStrip();
303
+ if (event?.type === 'config:mode' || event?.type === 'config:agent' || event?.type === 'running:set') syncModeToggle();
304
+ // An agent set from outside (a session's last agent) must be one the toggle offers.
305
+ if (event?.type === 'config:agent') validateAgent();
306
+ // The project directory decides the commands and the agents on offer.
307
+ if (event?.type === 'config:cwd' || event?.type === 'session:set' || event?.type === 'session:info') {
308
+ const cwd = composerCwd();
309
+ if (cwd !== _composerCwd) {
310
+ _composerCwd = cwd;
311
+ loadSlashCatalog(cwd);
312
+ // The agent list on hand is the previous project's. Nothing is checked
313
+ // against it: the toggle takes this directory's list when it is
314
+ // already known, else the built-ins until it arrives.
315
+ _agentChoice.enter(cwd, _agentCatalog.get(cwd));
316
+ renderAgentButtons();
317
+ validateAgent();
318
+ refreshAgents();
319
+ // What the @ picker shows, or is still searching, is the old directory's.
320
+ _mentionHints?.hide();
321
+ }
322
+ }
323
+ // OpenCode came up (cold start) or came back: the list asked for at mount
324
+ // was empty or may be stale.
325
+ if (event?.type === 'server:status') {
326
+ const became = state.serverStatus === 'ready' && _lastServerStatus !== 'ready';
327
+ _lastServerStatus = state.serverStatus;
328
+ if (became) refreshAgents({ force: true });
329
+ }
330
+ // A turn that ended in an error is not a clean finish: hold the queue.
331
+ // (A notice is not an error of the turn.)
332
+ if (event?.type === 'error:push' && !state.errors[state.errors.length - 1]?.notice) _queue.pause();
333
+ syncButton();
334
+ scheduleDrain();
335
+ });
336
+ const unsubscribeQueue = _queue.subscribe(() => {
337
+ if (!_queue.isPaused() || !_queue.size()) { _queueNote = ''; _queueBack = null; }
338
+ renderQueueTray();
339
+ scheduleDrain();
340
+ });
341
+
342
+ // The store moved to another session (a new one, a switch, a project change,
343
+ // or back to one it was on before). Shell mode, the agent the fallback
344
+ // replaced and whatever the @ picker shows or is still searching belong to
345
+ // the binding that was left. So does the queue: it is parked for the session
346
+ // it was typed in, and what is parked for the session the panel is on now
347
+ // comes back, paused. (The draft itself, text, attachments and the mentions
348
+ // already picked in it, is the panel's and stays, as it always did.)
349
+ function onRebound(state) {
350
+ const left = _queueSessionId;
351
+ _queueSessionId = state.sessionId || null;
352
+ _agentChoice.forget();
353
+ // (A queue typed into a session after it was lost has nobody to wait
354
+ // for: `leave` keeps nothing for it, and its prompts are kept on notices.)
355
+ if (left) tellIfLost(left, _queue.list());
356
+ if (left) _parked.leave(left, _queue.list());
357
+ // The session the panel is on exists, whatever was forgotten about it
358
+ // before (a tab that was archived and opened again).
359
+ if (_queueSessionId) _parked.reopen(_queueSessionId);
360
+ _queue.clear();
361
+ // A queued prompt of the session that was left may still be on its way
362
+ // (a turn can take minutes). That is not this binding's drain: its queue
363
+ // must not wait for it.
364
+ _draining = false;
365
+ setShellMode(false);
366
+ _mentionHints?.hide();
367
+ restoreParkedHere();
368
+ }
369
+
370
+ // What is parked for the session on screen goes into its queue, at the
371
+ // front and paused. Called on every rebinding, and after every failure that
372
+ // was parked: the panel may already be back on that session (A → B → A),
373
+ // and then the prompt must not wait, unseen, for the next rebinding.
374
+ function restoreParkedHere() {
375
+ const sessionId = store.getState().sessionId;
376
+ if (!sessionId || !_parked.has(sessionId)) return false;
377
+ const parked = _parked.take(sessionId);
378
+ if (!parked || !_queue.restore(parked.items)) return false;
379
+ _queueBack = {
380
+ items: [...(_queueBack?.items || []), ...parked.items],
381
+ failed: (_queueBack?.failed || 0) + parked.failed,
382
+ queued: (_queueBack?.queued || 0) + parked.queued,
383
+ errors: [...new Set([...(_queueBack?.errors || []), ...parked.errors])],
384
+ };
385
+ _queueNote = parkedPromptsNotice(_queueBack);
386
+ renderQueueTray();
387
+ return true;
388
+ }
389
+
390
+ // A prompt or a command of `sessionId` that could not go out after the
391
+ // panel had left that session.
392
+ function parkFailed(sessionId, item, error = '') {
393
+ if (!sessionId) return false;
394
+ if (!_parked.park(sessionId, { item, error })) { tellIfLost(sessionId, item); return false; }
395
+ restoreParkedHere();
396
+ return true;
397
+ }
398
+
399
+ // A prompt that failed for a session that is gone has nobody to go back
400
+ // to. When the user closed that tab, that was their decision (they were
401
+ // asked about what was waiting). When the session was lost (deleted
402
+ // elsewhere, dropped by OpenCode, or without a tab and nothing remembers
403
+ // why), the prompt is kept for the user.
404
+ function tellIfLost(sessionId, item) {
405
+ const items = (Array.isArray(item) ? item : [item]).filter(Boolean);
406
+ if (!items.length || _parked.goneAs(sessionId) !== 'lost') return;
407
+ keepPrompts(items, sessionLabel(sessionId));
408
+ }
409
+
410
+ function sessionLabel(sessionId) {
411
+ const title = store.getState().knownSessions?.get?.(sessionId)?.title;
412
+ return (typeof title === 'string' && title.trim()) || String(sessionId || '').slice(0, 8);
413
+ }
414
+
415
+ // Prompts that have no session to go to any more and that nobody was asked
416
+ // about. Each is kept whole on a notice of its own (a notice stays until
417
+ // the user deals with it: no switch, recovery or other error removes it),
418
+ // with the two things that can be done with it.
419
+ function keepPrompts(items, label) {
420
+ for (const item of items) {
421
+ const kept = keptOf(item);
422
+ store.pushError({
423
+ message: keptPromptNotice({ item: kept, label }),
424
+ notice: true,
425
+ kept,
426
+ actions: [
427
+ { label: 'Put back', run: (err) => { if (putBackKept(kept)) store.dismissError(err.id); } },
428
+ { label: 'Discard', run: (err) => store.dismissError(err.id) },
429
+ ],
430
+ });
431
+ }
432
+ }
433
+
434
+ // A prompt as it is kept: whole, and nothing but the prompt.
435
+ function keptOf(item) {
436
+ const kept = {
437
+ text: String(item.text || ''),
438
+ images: Array.isArray(item.images) ? [...item.images] : [],
439
+ paths: Array.isArray(item.paths) ? [...item.paths] : [],
440
+ mentions: Array.isArray(item.mentions) ? [...item.mentions] : [],
441
+ };
442
+ if (item.command?.command) kept.command = { command: String(item.command.command), args: String(item.command.args || '') };
443
+ return kept;
444
+ }
445
+
446
+ // A kept prompt goes into the draft of the session on screen, for the user
447
+ // to send: its text under what is being typed, its attachments and paths
448
+ // in the strips, its mentions picked again (keptPromptDraft). A command
449
+ // comes back as the line that was typed. False when there is no box to put
450
+ // it in: the notice then keeps it.
451
+ function putBackKept(kept) {
452
+ if (!_input) return false;
453
+ const back = keptPromptDraft(kept, _input.value);
454
+ // Text placed by code is a prompt, never a shell command.
455
+ setShellMode(shellAfterProgrammaticInput());
456
+ _input.value = back.text;
457
+ for (const img of back.images) store.addAttachedImage(img, { restore: true });
458
+ for (const path of back.paths) store.addPendingPath(path);
459
+ // (A mention picked meanwhile under the same token stays.)
460
+ for (const mention of back.mentions) { if (!_pickedMentions.has(mention.token)) _pickedMentions.set(mention.token, mention); }
461
+ if (back.lost.length) {
462
+ store.pushError({ message: `Put back without ${back.lost.join(', ')}: pick ${back.lost.length === 1 ? 'it' : 'them'} again with @.`, notice: true });
463
+ }
464
+ autosize();
465
+ syncButton();
466
+ _input.focus();
467
+ return true;
468
+ }
469
+ const unsubscribeCaps = capabilities.subscribe(() => {
470
+ syncModeToggle();
471
+ // agent:list (per directory) may have become available, or gone.
472
+ refreshAgents({ force: true });
473
+ });
474
+ renderImageStrip();
475
+ renderPathStrip();
476
+ renderQueueTray();
477
+ syncModeToggle();
478
+ syncButton();
479
+
480
+ function renderImageStrip() {
481
+ if (!_imageStrip) return;
482
+ const { attachedImages = [] } = store.getState();
483
+ _imageStrip.innerHTML = '';
484
+ _imageStrip.hidden = attachedImages.length === 0;
485
+ attachedImages.forEach((img, idx) => {
486
+ const isImage = String(img.mime || '').startsWith('image/');
487
+ const chip = document.createElement('div');
488
+ chip.className = isImage ? 'ocpv2-image-chip' : 'ocpv2-path-chip';
489
+ const removeBtn = document.createElement('button');
490
+ removeBtn.className = isImage ? 'ocpv2-image-chip-remove' : 'ocpv2-path-chip-remove';
491
+ removeBtn.type = 'button';
492
+ removeBtn.textContent = '×';
493
+ removeBtn.setAttribute('data-tooltip', 'Remove');
494
+ removeBtn.addEventListener('click', (event) => {
495
+ event.preventDefault();
496
+ event.stopPropagation();
497
+ store.removeAttachedImage(idx);
498
+ });
499
+ if (isImage) {
500
+ const imgEl = document.createElement('img');
501
+ imgEl.src = img.dataUrl || '';
502
+ imgEl.alt = img.name || `Image ${idx + 1}`;
503
+ chip.appendChild(imgEl);
504
+ } else {
505
+ // A PDF or a text file: a name chip.
506
+ chip.title = img.name || 'file';
507
+ const label = document.createElement('span');
508
+ label.className = 'ocpv2-path-chip-name';
509
+ label.textContent = img.name || 'file';
510
+ chip.appendChild(label);
511
+ }
512
+ chip.appendChild(removeBtn);
513
+ _imageStrip.appendChild(chip);
514
+ });
515
+ }
516
+
517
+ // ── @ mentions ────────────────────────────────────────────────────────────
518
+ // Each source is asked only when the server answers its request type.
519
+ const MENTION_SOURCES = ['find:files', 'find:symbols', 'resource:list', 'reference:list'];
520
+ function mentionSourcesAvailable() { return MENTION_SOURCES.some((type) => supports(type)); }
521
+
522
+ // Resolves the groups for `query`, or null when the panel moved to another
523
+ // session or directory while the search was out: those rows are another
524
+ // project's and are never offered (the picker ignores a null answer).
525
+ const searchMentionSources = searchOnBinding(store, composerCwd, async (query, { sessionId, cwd }) => {
526
+ const key = `${sessionId || ''}|${cwd}`;
527
+ const [files, symbols, resources, references] = await Promise.all([
528
+ supports('find:files') ? api.findFiles({ query, cwd: cwd || undefined }).then(dataOrEmpty, () => []) : [],
529
+ // The language servers want something to search for.
530
+ supports('find:symbols') && query.trim().length >= 2
531
+ ? api.findSymbols({ sessionId, query, cwd: cwd || undefined }).then(dataOrEmpty, () => [])
532
+ : [],
533
+ supports('resource:list') ? _mcpResources.get(key) : [],
534
+ supports('reference:list') ? _references.get(key) : [],
535
+ ]);
536
+ return mentionGroups({ query, cwd, files, symbols, resources, references });
537
+ });
538
+ function searchMentions(query) { return searchMentionSources(query); }
539
+
540
+ // ── Attachments: paste, drop, picker ──────────────────────────────────────
541
+ async function addFiles(fileList) {
542
+ const files = Array.from(fileList || []);
543
+ if (!files.length) return;
544
+ // Attachments are the draft's, and the draft is the panel's, not a
545
+ // session's: a file is checked against the model that is selected when it
546
+ // is actually added, so the state is read after the wait on purpose.
547
+ const providers = await loadProviders();
548
+ const capabilities = modelCapabilitiesOf(providers, store.getState().model);
549
+ for (const file of files) {
550
+ const verdict = attachmentVerdict(file, capabilities, { count: store.getState().attachedImages.length });
551
+ if (!verdict.ok) { store.pushError({ message: verdict.reason }); continue; }
552
+ const mime = verdict.kind === 'image' ? (file.type || 'image/png')
553
+ : verdict.kind === 'pdf' ? 'application/pdf'
554
+ : 'text/plain';
555
+ try {
556
+ const dataUrl = await readFileAsDataUrl(file, mime);
557
+ store.addAttachedImage({ name: file.name || `attachment-${Date.now()}`, mime, dataUrl });
558
+ } catch (err) {
559
+ store.pushError({ message: err?.message || `Could not read ${file.name || 'the file'}` });
560
+ }
561
+ }
562
+ _input?.focus();
563
+ }
564
+
565
+ function onPaste(event) {
566
+ const files = Array.from(event.clipboardData?.files || []);
567
+ if (!files.length) return; // plain text: the browser pastes it
568
+ event.preventDefault();
569
+ addFiles(files);
570
+ }
571
+
572
+ function onDragOver(event) {
573
+ if (Array.from(event.dataTransfer?.types || []).includes('Files')) event.preventDefault();
574
+ }
575
+
576
+ function onDrop(event) {
577
+ const files = Array.from(event.dataTransfer?.files || []);
578
+ if (!files.length) return;
579
+ event.preventDefault();
580
+ addFiles(files);
581
+ }
582
+
583
+ // ── Queue tray ────────────────────────────────────────────────────────────
584
+ function renderQueueTray() {
585
+ if (!_queueTray) return;
586
+ const items = _queue.list();
587
+ _queueTray.innerHTML = '';
588
+ _queueTray.hidden = items.length === 0;
589
+ if (!items.length) return;
590
+ const head = document.createElement('div');
591
+ head.className = 'ocpv2-queue-head';
592
+ const title = document.createElement('span');
593
+ title.textContent = _queue.isPaused()
594
+ ? `Queue paused · ${items.length} waiting`
595
+ : `Queued · ${items.length} ${items.length === 1 ? 'prompt goes' : 'prompts go'} out when this turn ends`;
596
+ // Prompts that came back because they could not be delivered say so.
597
+ if (_queueNote && _queue.isPaused()) title.title = _queueNote;
598
+ head.appendChild(title);
599
+ if (_queue.isPaused()) {
600
+ const resume = document.createElement('button');
601
+ resume.type = 'button';
602
+ resume.className = 'ocpv2-queue-action';
603
+ resume.textContent = 'Resume';
604
+ resume.addEventListener('click', () => _queue.resume());
605
+ head.appendChild(resume);
606
+ }
607
+ const clear = document.createElement('button');
608
+ clear.type = 'button';
609
+ clear.className = 'ocpv2-queue-action';
610
+ clear.textContent = 'Clear';
611
+ clear.addEventListener('click', () => _queue.clear());
612
+ head.appendChild(clear);
613
+ _queueTray.appendChild(head);
614
+ for (const item of items) {
615
+ const row = document.createElement('div');
616
+ row.className = 'ocpv2-queue-item';
617
+ const text = document.createElement('span');
618
+ text.className = 'ocpv2-queue-text';
619
+ const extras = item.images.length + item.paths.length;
620
+ text.textContent = (item.text || '(attachments)') + (extras ? ` +${extras}` : '');
621
+ text.title = item.text;
622
+ const remove = document.createElement('button');
623
+ remove.type = 'button';
624
+ remove.className = 'ocpv2-queue-action';
625
+ remove.textContent = '×';
626
+ remove.setAttribute('data-tooltip', 'Remove');
627
+ remove.addEventListener('click', () => _queue.remove(item.id));
628
+ row.append(text, remove);
629
+ _queueTray.appendChild(row);
630
+ }
631
+ }
632
+
633
+ let _drainTimer = null;
634
+ function scheduleDrain() {
635
+ if (_drainTimer || _draining) return;
636
+ if (!queueCanDrain(_queue, store.getState())) return;
637
+ // A beat after the turn ends, so its last events (plan card, question) land first.
638
+ _drainTimer = setTimeout(() => { _drainTimer = null; drainQueue(); }, 300);
639
+ }
640
+
641
+ async function drainQueue() {
642
+ if (_draining || !queueCanDrain(_queue, store.getState())) return;
643
+ const item = _queue.shift();
644
+ if (!item) return;
645
+ _draining = true;
646
+ // The queue this prompt came out of is this binding's. A failure that
647
+ // comes back after the panel moved must not put it into the next
648
+ // session's queue: sendTextMessage parks it for its own session instead.
649
+ const at = captureBinding(store);
650
+ try {
651
+ // A command that came back from the parking runs as that command again;
652
+ // everything else in the queue is a prompt.
653
+ const sent = item.command
654
+ ? (await runCommandFor(item)).ran
655
+ : await sendTextMessage(item.text, {
656
+ images: item.images, paths: item.paths, mentions: item.mentions, allowEmptyText: true, binding: at,
657
+ });
658
+ // (Not into the queue of a composer that is gone: sendTextMessage and
659
+ // runCommandFor handed the prompt over.)
660
+ if (!sent && at.isCurrent() && !undeliverable()) { _queue.unshift(item); _queue.pause(); }
661
+ } finally {
662
+ // The flag (and the next drain) are this binding's only while the panel
663
+ // is still on it: onRebound already released it otherwise.
664
+ if (at.isCurrent()) {
665
+ _draining = false;
666
+ scheduleDrain();
667
+ }
668
+ }
669
+ }
670
+
671
+ // ── Shell mode ────────────────────────────────────────────────────────────
672
+ function setShellMode(on) {
673
+ const next = !!on;
674
+ if (_shell === next) return;
675
+ _shell = next;
676
+ _shellTypedValue = '';
677
+ _wrap?.classList.toggle('ocpv2-shell-mode', _shell);
678
+ if (_shell) { _slashHints?.hide(); _mentionHints?.hide(); }
679
+ syncModeToggle();
680
+ syncButton();
681
+ }
682
+
683
+ function basenamePath(path) {
684
+ const text = String(path || '');
685
+ return text.split(/[\\/]/).filter(Boolean).pop() || text;
686
+ }
687
+
688
+ function renderPathStrip() {
689
+ if (!_pathStrip) return;
690
+ const { pendingPaths = [] } = store.getState();
691
+ _pathStrip.innerHTML = '';
692
+ _pathStrip.hidden = pendingPaths.length === 0;
693
+ pendingPaths.forEach((path, idx) => {
694
+ const chip = document.createElement('div');
695
+ chip.className = 'ocpv2-path-chip';
696
+ chip.title = path;
697
+
698
+ const icon = document.createElement('span');
699
+ icon.className = 'ocpv2-path-chip-icon';
700
+ icon.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>';
701
+
702
+ const label = document.createElement('span');
703
+ label.className = 'ocpv2-path-chip-name';
704
+ label.textContent = basenamePath(path);
705
+
706
+ const removeBtn = document.createElement('button');
707
+ removeBtn.className = 'ocpv2-path-chip-remove';
708
+ removeBtn.type = 'button';
709
+ removeBtn.textContent = 'x';
710
+ removeBtn.setAttribute('data-tooltip', 'Remove');
711
+ removeBtn.addEventListener('click', (event) => {
712
+ event.preventDefault();
713
+ event.stopPropagation();
714
+ store.removePendingPath(idx);
715
+ });
716
+
717
+ chip.append(icon, label, removeBtn);
718
+ _pathStrip.appendChild(chip);
719
+ });
720
+ }
721
+
722
+ function focus() { _input?.focus(); }
723
+
724
+ // Put a draft into the box (undo hands the reverted prompt back). A draft
725
+ // the user is already typing is never overwritten.
726
+ function setText(text, { replace = false } = {}) {
727
+ if (!_input) return false;
728
+ // Text placed by code is a prompt, never a shell command.
729
+ setShellMode(shellAfterProgrammaticInput());
730
+ if (_input.value.trim() && !replace) return false;
731
+ _input.value = String(text || '');
732
+ autosize();
733
+ syncButton();
734
+ _input.focus();
735
+ return true;
736
+ }
737
+
738
+ function appendPath(path) {
739
+ setShellMode(shellAfterProgrammaticInput());
740
+ const added = store.addPendingPath(path);
741
+ _input?.focus();
742
+ syncButton();
743
+ return added;
744
+ }
745
+
746
+ function onInput(event) {
747
+ // An input event nobody typed (openOpencodeWithPrompt, a handoff) carries
748
+ // text written by code: it must not sit in a composer armed for the shell.
749
+ if (!event?.isTrusted) setShellMode(shellAfterProgrammaticInput());
750
+ else {
751
+ _history.reset();
752
+ if (_shell) _shellTypedValue = _input.value;
753
+ }
754
+ autosize();
755
+ }
756
+
757
+ function onKeyDown(e) {
758
+ if (_mentionHints?.isOpen()) {
759
+ if (e.key === 'ArrowDown') { e.preventDefault(); _mentionHints.navigate(1); return; }
760
+ if (e.key === 'ArrowUp') { e.preventDefault(); _mentionHints.navigate(-1); return; }
761
+ if (e.key === 'Enter' || e.key === 'Tab') {
762
+ if (e.key === 'Enter' && e.shiftKey) return;
763
+ e.preventDefault();
764
+ _mentionHints.applySelected();
765
+ return;
766
+ }
767
+ if (e.key === 'Escape') { e.preventDefault(); _mentionHints.hide(); return; }
768
+ }
769
+ if (_slashHints?.isOpen()) {
770
+ if (e.key === 'ArrowDown') { e.preventDefault(); _slashHints.navigate(1); return; }
771
+ if (e.key === 'ArrowUp') { e.preventDefault(); _slashHints.navigate(-1); return; }
772
+ if (e.key === 'Enter' || e.key === 'Tab') {
773
+ if (e.key === 'Enter' && e.shiftKey) return; // shift+enter = newline
774
+ e.preventDefault();
775
+ _slashHints.applySelected();
776
+ return;
777
+ }
778
+ if (e.key === 'Escape') { e.preventDefault(); _slashHints.hide(); return; }
779
+ }
780
+
781
+ // Shell mode: "!" typed on an empty composer, and nothing else, enters it.
782
+ const shell = shellKey({
783
+ shell: _shell, value: _input.value, key: e.key, trusted: e.isTrusted,
784
+ composing: e.isComposing, supported: supports('session:shell'),
785
+ });
786
+ if (shell.shell !== _shell) setShellMode(shell.shell);
787
+ if (shell.preventDefault) { e.preventDefault(); return; }
788
+
789
+ if ((e.key === 'ArrowUp' || e.key === 'ArrowDown') && !e.shiftKey && !e.altKey && !e.metaKey && !e.ctrlKey && !_shell
790
+ && historyKeyApplies({
791
+ key: e.key, value: _input.value, selectionStart: _input.selectionStart, selectionEnd: _input.selectionEnd,
792
+ browsing: _history.browsing(),
793
+ })) {
794
+ const recalled = e.key === 'ArrowUp' ? _history.prev(_input.value) : _history.next();
795
+ if (recalled != null) {
796
+ e.preventDefault();
797
+ _input.value = recalled;
798
+ autosize();
799
+ syncButton();
800
+ try { _input.setSelectionRange(recalled.length, recalled.length); } catch {}
801
+ }
802
+ return;
803
+ }
804
+
805
+ if (e.key === 'Escape') {
806
+ // Two presses stop a running turn; one is too easy to hit by accident.
807
+ const result = escapeStop(_escArmedAt, Date.now(), !!store.getState().running);
808
+ _escArmedAt = result.armedAt;
809
+ if (result.stop) { e.preventDefault(); doAbort(); }
810
+ else if (result.armedAt) {
811
+ e.preventDefault();
812
+ _btn?.setAttribute('data-tooltip', 'Press Esc again to stop');
813
+ }
814
+ return;
815
+ }
816
+
817
+ // Shift+Tab steps through the agents (the TUI's Tab).
818
+ if (e.key === 'Tab' && e.shiftKey && !store.getState().running && !_shell) {
819
+ e.preventDefault();
820
+ setComposeAgent(nextAgent(store.getState().agent, _agentChoice.agents()));
821
+ return;
822
+ }
823
+
824
+ if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
825
+ e.preventDefault();
826
+ onEnter();
827
+ }
828
+ }
829
+
830
+ function autosize() {
831
+ if (!_input) return;
832
+ _input.style.height = 'auto';
833
+ _input.style.height = `${Math.min(_input.scrollHeight, 160)}px`;
834
+ }
835
+
836
+ function syncButton() {
837
+ if (!_btn) return;
838
+ const s = store.getState();
839
+ if (s.running) {
840
+ _btn.classList.add('ocpv2-compose-abort');
841
+ _btn.setAttribute('data-tooltip', 'Stop');
842
+ _btn.disabled = false;
843
+ } else {
844
+ _escArmedAt = 0;
845
+ _btn.classList.remove('ocpv2-compose-abort');
846
+ const hasText = !!_input?.value?.trim();
847
+ const hasImages = (s.attachedImages?.length || 0) > 0;
848
+ const hasPaths = (s.pendingPaths?.length || 0) > 0;
849
+ if (_shell) {
850
+ _btn.setAttribute('data-tooltip', 'Run shell command');
851
+ _btn.disabled = !hasText;
852
+ } else {
853
+ _btn.setAttribute('data-tooltip', s.showPostPlanActions ? 'Choose a plan action' : 'Send');
854
+ _btn.disabled = !!s.showPostPlanActions || (!hasText && !hasImages && !hasPaths);
855
+ }
856
+ }
857
+ }
858
+
859
+ // An agent that was stored, or that the session last ran with, but that
860
+ // OpenCode does not offer here falls back to build. Only the server's list
861
+ // for the directory this composer is in decides: never the built-in
862
+ // fallback, never the previous project's list. An agent the fallback
863
+ // replaced comes back when this directory's list offers it after all.
864
+ function validateAgent() {
865
+ const current = store.getState().agent;
866
+ const next = _agentChoice.settle(current, composerCwd());
867
+ if (next && next !== current) store.setAgent(next);
868
+ }
869
+
870
+ async function refreshAgents({ force = false } = {}) {
871
+ const cwd = composerCwd();
872
+ const agents = await _agentCatalog.load(cwd, { force });
873
+ // Unmounted, nothing to show yet, or the composer moved to another project.
874
+ if (!_root || !_agentChoice.accept(cwd, agents, composerCwd())) return;
875
+ validateAgent();
876
+ renderAgentButtons();
877
+ }
878
+
879
+ function renderAgentButtons() {
880
+ if (!_modeToggle) return;
881
+ _modeToggle.innerHTML = '';
882
+ _modeButtons = _agentChoice.agents().map((agent) => {
883
+ const btn = document.createElement('button');
884
+ btn.className = 'ocpv2-mode-btn';
885
+ btn.type = 'button';
886
+ btn.dataset.agent = agent.name;
887
+ btn.textContent = agentLabel(agent.name);
888
+ if (agent.description) btn.setAttribute('data-tooltip', agent.description.slice(0, 160));
889
+ btn.addEventListener('click', () => setComposeAgent(agent.name));
890
+ _modeToggle.appendChild(btn);
891
+ return btn;
892
+ });
893
+ syncModeToggle();
894
+ }
895
+
896
+ function syncModeToggle() {
897
+ const s = store.getState();
898
+ for (const btn of _modeButtons) {
899
+ const active = btn.dataset.agent === s.agent;
900
+ btn.classList.toggle('active', active);
901
+ btn.setAttribute('aria-pressed', active ? 'true' : 'false');
902
+ btn.disabled = !!s.running;
903
+ }
904
+ if (_input) {
905
+ _input.placeholder = _shell
906
+ ? 'Shell command… (Esc to leave shell mode)'
907
+ : s.mode === 'plan' ? 'Ask OpenCode to plan…'
908
+ : supports('session:shell') ? 'Send a message… ( / commands · @ files · ! shell )'
909
+ : 'Send a message…';
910
+ }
911
+ }
912
+
913
+ function setComposeAgent(agent) {
914
+ const s = store.getState();
915
+ if (s.running) return;
916
+ const next = resolveAgent(agent, _agentChoice.agents());
917
+ // The user's own pick: nothing replaced earlier is put back over it.
918
+ _agentChoice.forget();
919
+ store.setAgent(next);
920
+ storage.setItem(STOR_MODE, next);
921
+ }
922
+
923
+ // Enter: send; while a turn runs, queue what was typed (the button stops).
924
+ async function onEnter() {
925
+ const s = store.getState();
926
+ if (!s.running) return doSend();
927
+ if (_shell) {
928
+ store.pushError({ message: 'Wait for the current turn to finish before running a shell command.' });
929
+ return false;
930
+ }
931
+ const text = _input.value.trim();
932
+ const images = Array.isArray(s.attachedImages) ? [...s.attachedImages] : [];
933
+ const paths = Array.isArray(s.pendingPaths) ? [...s.pendingPaths].filter(Boolean) : [];
934
+ if (!text && !images.length && !paths.length) return false;
935
+ const slash = resolveSlash(text, getSlashCatalog(composerCwd()));
936
+ if (slash?.kind === 'local' || slash?.kind === 'tui') return doSend();
937
+ if (slash?.kind === 'command') {
938
+ store.pushError({ message: 'Wait for the current turn to finish before running a command.' });
939
+ return false;
940
+ }
941
+ const mentions = mentionFileParts(text, _pickedMentions, s.cwd || s.sessionInfo?.directory || '');
942
+ // One more session with prompts waiting than the parking takes: this
943
+ // queue is not started. Nothing is taken from the box or the strips, and
944
+ // nothing that is already waiting somewhere is dropped to make room.
945
+ if (!_queue.size() && _parked.isFull()) {
946
+ store.pushError({ message: parkingFullNotice() });
947
+ return false;
948
+ }
949
+ if (!_queue.add({ text, images, paths, mentions })) {
950
+ store.pushError({ message: 'The queue is full. Wait for a turn to finish or clear it.' });
951
+ return false;
952
+ }
953
+ rememberPrompt(text);
954
+ _pickedMentions.clear();
955
+ if (images.length) store.clearAttachedImages();
956
+ if (paths.length) store.clearPendingPaths();
957
+ clearInput();
958
+ return true;
959
+ }
960
+
961
+ function clearInput() {
962
+ if (!_input) return;
963
+ _input.value = '';
964
+ autosize();
965
+ syncButton();
966
+ }
967
+
968
+ function rememberPrompt(text) {
969
+ if (!text) return;
970
+ _history.push(text);
971
+ try { sessionStorage.setItem(STOR_HISTORY, JSON.stringify(_history.entries())); } catch {}
972
+ }
973
+
974
+ function openInTui(name) {
975
+ // TUI-only OpenCode builtins (/themes, /login, …) have no server API.
976
+ // Launch the OpenCode CLI in the Terminal panel and type the slash there.
977
+ emit('terminal:open-with-command', {
978
+ profile: 'opencode',
979
+ cwd: store.getState().cwd || undefined,
980
+ command: '/' + name,
981
+ });
982
+ }
983
+
984
+ // A panel action behind a slash command. Session-level ones come from the
985
+ // panel (opts.slashActions); a sub-agent panel has none of those.
986
+ async function runLocalSlash(slash) {
987
+ const s = store.getState();
988
+ // A failure is reported in the session the command was typed in.
989
+ const typedAt = captureBinding(store);
990
+ try {
991
+ switch (slash.action) {
992
+ case 'compact': {
993
+ if (!s.sessionId) return;
994
+ if (s.running) { store.pushError({ message: 'Wait for the current turn to finish before compacting.' }); return; }
995
+ const at = captureBinding(store);
996
+ const res = await api.compact({
997
+ sessionId: at.sessionId, cwd: at.cwd || undefined, mcpProfile: at.mcpProfile || undefined, model: at.model || undefined,
998
+ });
999
+ // A failure of the session the panel has left is not this one's banner.
1000
+ if (at.isCurrent() && (res?.error || res?.ok === false)) store.pushError({ message: res?.error || 'Compact failed' });
1001
+ return;
1002
+ }
1003
+ case 'models':
1004
+ document.getElementById('ocpv2-model-dd')?.click();
1005
+ return;
1006
+ case 'agents':
1007
+ setComposeAgent(nextAgent(s.agent, _agentChoice.agents()));
1008
+ return;
1009
+ case 'help':
1010
+ // A card at the end of the transcript, from the catalog the slash
1011
+ // menu lists. Nothing is sent, and the box is left as it is.
1012
+ store.setHelpCard?.(helpCardView({
1013
+ catalog: getSlashCatalog(composerCwd()), supports, canAct: !s.parentSessionId,
1014
+ }));
1015
+ return;
1016
+ default: {
1017
+ const action = opts.slashActions?.[slash.action];
1018
+ if (typeof action !== 'function') {
1019
+ store.pushError({ message: `/${slash.name} is not available in this panel.` });
1020
+ return;
1021
+ }
1022
+ await action(slash.args);
1023
+ }
1024
+ }
1025
+ } catch (err) {
1026
+ if (typedAt.isCurrent()) store.pushError({ message: err?.message || `/${slash.name} failed` });
1027
+ }
1028
+ }
1029
+
1030
+ // session.command / session.shell: both occupy the session like a prompt.
1031
+ // `outcome` (optional) is filled with `error`: what went wrong, also when no
1032
+ // banner is shown for it because the panel has moved.
1033
+ async function runServerTurn(label, request, outcome = {}) {
1034
+ // getState() hands out the live state, so `s.sessionId` read after an
1035
+ // await is wherever the panel is by then. The turn belongs to the binding
1036
+ // captured here: the request, the callbacks and the end of the turn all
1037
+ // read this snapshot.
1038
+ const turn = captureBinding(store);
1039
+ if (!turn.sessionId) { store.pushError({ message: 'No session. Reopen the panel to retry.' }); return false; }
1040
+ if (opts.canSend && opts.canSend(turn) === false) {
1041
+ store.pushError({ message: 'Stop the running automation before sending a manual turn.' });
1042
+ return false;
1043
+ }
1044
+ try { opts.onBeforeSend?.(turn); } catch {}
1045
+ // The turn this command is: what ends `running` afterwards ends it only
1046
+ // while no newer turn began on this binding (ocp-v2-send-logic.js).
1047
+ const run = beginTurn(store, turn);
1048
+ try {
1049
+ const res = await request(turn);
1050
+ if (replyFailed(res)) {
1051
+ outcome.error = replyError(res, `${label} failed`);
1052
+ if (!shouldSuppressAbortError(res.error || '') && turn.isCurrent()) store.pushError({ message: outcome.error });
1053
+ return false;
1054
+ }
1055
+ // The session's transcript, for the session it was run in: nothing is
1056
+ // applied when the panel moved to another one meanwhile.
1057
+ const refreshed = await hydrateTranscript(store, api, { sessionId: turn.sessionId, isCurrent: turn.isCurrent, syncRunning: false });
1058
+ if (refreshed.error) console.warn('[ocp-v2-send] transcript refresh failed', refreshed.error);
1059
+ return true;
1060
+ } catch (err) {
1061
+ outcome.error = err?.message || `${label} failed`;
1062
+ if (!shouldSuppressAbortError(err) && turn.isCurrent()) store.pushError({ message: outcome.error });
1063
+ return false;
1064
+ } finally {
1065
+ endTurn(store, run);
1066
+ }
1067
+ }
1068
+
1069
+ // A command turn carries the same file parts a prompt does: what is
1070
+ // attached, the referenced paths and the @ mentions.
1071
+ function runCommand(slash, attached, outcome) {
1072
+ const parts = commandFileParts(attached);
1073
+ return runServerTurn(`/${slash.command}`, (turn) => api.commandRun({
1074
+ sessionId: turn.sessionId,
1075
+ command: slash.command,
1076
+ arguments: slash.args,
1077
+ agent: turn.agent || undefined,
1078
+ model: turn.model || undefined,
1079
+ variant: turn.variant || undefined,
1080
+ parts: parts.length ? parts : undefined,
1081
+ cwd: turn.cwd || undefined,
1082
+ mcpProfile: turn.mcpProfile || undefined,
1083
+ }), outcome);
1084
+ }
1085
+
1086
+ // A slash command with what it took from the draft: `item` is `{ text,
1087
+ // images, paths, mentions, command: { command, args } }`. Resolves `{ ran,
1088
+ // here }`. A command that failed while the panel was still on its session
1089
+ // is the caller's to put back (`here`). One that failed after the panel had
1090
+ // moved is parked for its session, whole: it comes back in that session's
1091
+ // queue, paused, and runs as the same command when the user resumes.
1092
+ async function runCommandFor(item) {
1093
+ // On its way until OpenCode answers, which for a command is when its turn is over.
1094
+ _outgoing.add(item);
1095
+ let out;
1096
+ try { out = await runCommandOn(item); } finally { _outgoing.delete(item); }
1097
+ // The composer is gone (its sub-agent panel went while the command was
1098
+ // out): there is no box and no queue to put it back into.
1099
+ if (!out.ran && out.here && undeliverable()) { handUndelivered(item, out.error); return { ran: false, here: false }; }
1100
+ return { ran: out.ran, here: out.here };
1101
+ }
1102
+
1103
+ async function runCommandOn(item) {
1104
+ const at = captureBinding(store);
1105
+ const outcome = {};
1106
+ const ran = await runCommand(item.command, item, outcome);
1107
+ const here = at.isCurrent();
1108
+ if (!ran && !here) parkFailed(at.sessionId, item, outcome.error);
1109
+ return { ran, here, error: outcome.error };
1110
+ }
1111
+
1112
+ // Reached only from doSend() while the composer is in shell mode.
1113
+ function runShell(command) {
1114
+ return runServerTurn('Shell command', (turn) => api.sessionShell({
1115
+ sessionId: turn.sessionId,
1116
+ command,
1117
+ agent: turn.agent || undefined,
1118
+ model: turn.model || undefined,
1119
+ cwd: turn.cwd || undefined,
1120
+ mcpProfile: turn.mcpProfile || undefined,
1121
+ }));
1122
+ }
1123
+
1124
+ // The button: Stop while a turn runs, Send otherwise.
1125
+ async function onSendOrAbort() {
1126
+ const s = store.getState();
1127
+ if (s.running) return doAbort();
1128
+ return doSend();
1129
+ }
1130
+
1131
+ // What the user typed and sent. This is the only path that interprets the
1132
+ // text (shell mode, slash commands, @ mentions); sendTextMessage(), which
1133
+ // handoffs, automations and the plan lifecycle call, always sends a prompt.
1134
+ async function doSend() {
1135
+ const text = _input.value.trim();
1136
+ const s = store.getState();
1137
+ const images = Array.isArray(s.attachedImages) ? [...s.attachedImages] : [];
1138
+ const hasImages = images.length > 0;
1139
+ const hasPaths = (s.pendingPaths?.length || 0) > 0;
1140
+
1141
+ if (_shell) {
1142
+ // Only what the user typed (or pasted) into the shell-mode composer runs.
1143
+ // Anything that got into the box another way drops back to a prompt.
1144
+ const allowed = shellRunAllowed({ shell: _shell, value: _input.value, typedValue: _shellTypedValue });
1145
+ setShellMode(false);
1146
+ if (allowed) {
1147
+ rememberPrompt(text);
1148
+ clearInput();
1149
+ return runShell(text);
1150
+ }
1151
+ }
1152
+ if (!text && !hasImages && !hasPaths) return;
1153
+
1154
+ const slash = resolveSlash(text, getSlashCatalog(composerCwd()));
1155
+ if (slash) {
1156
+ rememberPrompt(text);
1157
+ clearInput();
1158
+ if (slash.kind === 'local') return runLocalSlash(slash);
1159
+ if (slash.kind === 'tui') return openInTui(slash.command);
1160
+ // The draft's attachments go with the command; they come back if it fails.
1161
+ const paths = Array.isArray(s.pendingPaths) ? [...s.pendingPaths].filter(Boolean) : [];
1162
+ const picked = new Map(_pickedMentions);
1163
+ const mentions = mentionFileParts(text, _pickedMentions, s.cwd || s.sessionInfo?.directory || '');
1164
+ if (hasImages) store.clearAttachedImages();
1165
+ if (paths.length) store.clearPendingPaths();
1166
+ _pickedMentions.clear();
1167
+ // What the command took from the draft comes back into the draft it was
1168
+ // taken from. After a move the composer is another session's: the
1169
+ // command is parked, with everything it took, for its own (runCommandFor).
1170
+ const { ran, here } = await runCommandFor({
1171
+ text, images, paths, mentions, command: { command: slash.command, args: slash.args },
1172
+ });
1173
+ if (!ran && here) {
1174
+ for (const img of images) store.addAttachedImage(img, { restore: true });
1175
+ for (const path of paths) store.addPendingPath(path);
1176
+ for (const [token, item] of picked) { if (!_pickedMentions.has(token)) _pickedMentions.set(token, item); }
1177
+ if (_input && !_input.value) { _input.value = text; autosize(); syncButton(); }
1178
+ }
1179
+ return ran;
1180
+ }
1181
+
1182
+ const mentions = mentionFileParts(text, _pickedMentions, s.cwd || s.sessionInfo?.directory || '');
1183
+ if (!s.sessionId || isPostPlanBlocked(store)) {
1184
+ return sendTextMessage(text, { allowEmptyText: hasImages || hasPaths, mentions });
1185
+ }
1186
+
1187
+ rememberPrompt(text);
1188
+ // Sending by hand takes a held queue up again, as it always did. Not one
1189
+ // that came back from the parking: those prompts (or that command) wait
1190
+ // for the user's own Resume, whatever else is sent in the meantime.
1191
+ if (!_queueBack) _queue.resume();
1192
+ clearInput();
1193
+ // The mentions picked for this prompt (a symbol's range, an MCP resource)
1194
+ // leave the draft with it, and come back with it if it fails.
1195
+ const picked = new Map(_pickedMentions);
1196
+ _pickedMentions.clear();
1197
+ // A send that fails after the panel moved does not put its text into the
1198
+ // box of the session on screen: sendTextMessage parks the prompt for the
1199
+ // session it was written for.
1200
+ const at = captureBinding(store);
1201
+ const sent = await sendTextMessage(text, { allowEmptyText: hasImages || hasPaths, mentions, binding: at });
1202
+ if (!sent && at.isCurrent() && !undeliverable()) {
1203
+ // Without them the restored text would go out again with its @ tokens
1204
+ // as plain text. (A mention picked meanwhile under the same token stays.)
1205
+ for (const [token, item] of picked) { if (!_pickedMentions.has(token)) _pickedMentions.set(token, item); }
1206
+ if (text && _input && !_input.value) {
1207
+ _input.value = text;
1208
+ autosize();
1209
+ syncButton();
1210
+ }
1211
+ }
1212
+ return sent;
1213
+ }
1214
+
1215
+ async function sendTextMessage(text, options = {}) {
1216
+ text = String(text || '').trim();
1217
+ // The action this send is the last step of (Retry, a queued prompt) was
1218
+ // started on a binding the panel has left: its prompt is not sent into the
1219
+ // session on screen.
1220
+ if (isBindingToken(options.binding) && !options.binding.isCurrent()) return false;
1221
+ // The binding this prompt belongs to, with the model, agent and directory
1222
+ // it goes out with. `s` is the live state and is only read before the
1223
+ // first await; everything after it reads `turn`.
1224
+ const turn = captureBinding(store);
1225
+ const s = store.getState();
1226
+ if (opts.canSend && opts.canSend(turn) === false) {
1227
+ store.pushError({ message: 'Stop the running automation before sending a manual turn.' });
1228
+ return false;
1229
+ }
1230
+ if (!options.ignorePostPlanBlock && isPostPlanBlocked(store)) {
1231
+ store.pushError({ message: 'Choose Continue, Continue planning, Compact, or Edit before sending another prompt.' });
1232
+ return false;
1233
+ }
1234
+ // A queued prompt carries the attachments it was typed with; anything
1235
+ // else takes what is in the strips now.
1236
+ const ownAttachments = Array.isArray(options.images) || Array.isArray(options.paths);
1237
+ const images = ownAttachments ? [...(options.images || [])] : (Array.isArray(s.attachedImages) ? [...s.attachedImages] : []);
1238
+ const paths = ownAttachments ? [...(options.paths || [])].filter(Boolean) : (Array.isArray(s.pendingPaths) ? [...s.pendingPaths].filter(Boolean) : []);
1239
+ if (!text && !images.length && !paths.length && !options.allowEmptyText) return false;
1240
+ if (!ownAttachments) {
1241
+ if (images.length) store.clearAttachedImages();
1242
+ if (paths.length) store.clearPendingPaths();
1243
+ }
1244
+ // A send that fails puts its attachments back in the strips; a queued
1245
+ // prompt keeps its own (it goes back to the front of the queue).
1246
+ const restoreAttachments = () => {
1247
+ if (ownAttachments) return;
1248
+ for (const img of images) store.addAttachedImage(img, { restore: true });
1249
+ for (const path of paths) store.addPendingPath(path);
1250
+ };
1251
+ if (!s.sessionId) {
1252
+ store.pushError({ message: 'No session. Reopen the panel to retry.' });
1253
+ restoreAttachments();
1254
+ return false;
1255
+ }
1256
+ // The session this prompt goes to. `s` is the live state: after an await
1257
+ // `s.sessionId` is wherever the panel is by then, which may be another tab.
1258
+ const turnSessionId = turn.sessionId;
1259
+ try { opts.onBeforeSend?.(turn); } catch {}
1260
+
1261
+ // Referenced paths and @ mentions go out as file parts: OpenCode reads
1262
+ // them itself (a file, a directory listing, an image).
1263
+ const finalText = text;
1264
+ const pathParts = [...pathFileParts(paths), ...(Array.isArray(options.mentions) ? options.mentions : [])];
1265
+ // An explicit mode (the plan lifecycle) means build or plan; otherwise the
1266
+ // agent picked in the toggle, which may be a custom one.
1267
+ const outgoingAgent = options.mode ? agentForMode(options.mode) : (s.agent || agentForMode(s.mode));
1268
+ const mode = modeForAgent(outgoingAgent);
1269
+ const isPlanTurn = mode === 'plan';
1270
+ const sendMeta = {
1271
+ prompt: text,
1272
+ paths,
1273
+ hasImages: images.length > 0,
1274
+ mode,
1275
+ agent: outgoingAgent,
1276
+ };
1277
+ // The callbacks get the snapshot of the session the prompt went to, in
1278
+ // every phase: its bookkeeping is that session's, wherever the panel is.
1279
+ const notifyManualSend = (phase) => {
1280
+ try { opts.onManualSend?.(turn, phase, sendMeta); } catch (err) {
1281
+ console.warn('[ocp-v2-send] onManualSend callback failed', err);
1282
+ }
1283
+ };
1284
+ // This prompt as a queue item, for the parking (see settleFailedSend).
1285
+ const promptItem = { text, images, paths, mentions: Array.isArray(options.mentions) ? options.mentions : [] };
1286
+ // On its way from here on, and this composer's until OpenCode has it: the
1287
+ // reply to the send, or OpenCode's own copy of the prompt in the transcript.
1288
+ _outgoing.add(promptItem);
1289
+ const leaveOutgoing = () => _outgoing.delete(promptItem);
1290
+
1291
+ if (options.abortFirst) {
1292
+ _suppressAbortErrorUntil = Date.now() + 15000;
1293
+ _suppressAbortErrorCount += 1;
1294
+ try {
1295
+ await api.abort(turnSessionId);
1296
+ } catch (err) {
1297
+ console.warn('[ocp-v2-send] abort before send failed', err);
1298
+ }
1299
+ // The panel moved while the old turn was being stopped: nothing has been
1300
+ // sent yet, and nothing is drawn in the session on screen. The prompt
1301
+ // waits for its own session.
1302
+ if (!turn.isCurrent()) {
1303
+ leaveOutgoing();
1304
+ if (settleFailedSend(turn, _parked, { item: promptItem }).parked) restoreParkedHere();
1305
+ else tellIfLost(turn.sessionId, promptItem);
1306
+ return false;
1307
+ }
1308
+ }
1309
+
1310
+ // The plan turn this prompt starts (0 for any other turn): its finalizer
1311
+ // and its failure act on that plan turn and on no later one.
1312
+ const planTurn = isPlanTurn ? beginPlanTurnForSend(store) : 0;
1313
+
1314
+ const optimisticId = `local-user-${Date.now()}`;
1315
+ store.upsertMessage({ id: optimisticId, role: 'user' });
1316
+ let partIdx = 0;
1317
+ if (finalText) {
1318
+ store.upsertPart({
1319
+ id: `${optimisticId}-text`,
1320
+ messageID: optimisticId,
1321
+ type: 'text',
1322
+ text: finalText,
1323
+ index: partIdx++,
1324
+ });
1325
+ }
1326
+ for (let i = 0; i < images.length; i++) {
1327
+ const img = images[i];
1328
+ store.upsertPart({
1329
+ id: `${optimisticId}-file-${i}`,
1330
+ messageID: optimisticId,
1331
+ type: 'file',
1332
+ mime: img.mime,
1333
+ filename: img.name,
1334
+ url: img.dataUrl,
1335
+ index: partIdx++,
1336
+ });
1337
+ }
1338
+ pathParts.forEach((part, i) => {
1339
+ store.upsertPart({ ...part, id: `${optimisticId}-path-${i}`, messageID: optimisticId, index: partIdx++ });
1340
+ });
1341
+
1342
+ // `turn` is the binding: the transcript, a banner and the strips are the
1343
+ // session's. `run` is this turn on that binding: `running` and the plan
1344
+ // turn are cleared through it, and only while no newer turn began.
1345
+ const run = beginTurn(store, turn);
1346
+
1347
+ let optimisticDropped = false;
1348
+ const existingMessageIds = new Set(s.messageOrder || []);
1349
+ const sendStartedAt = Date.now();
1350
+ const removeOptimistic = () => {
1351
+ if (optimisticDropped) return;
1352
+ optimisticDropped = true;
1353
+ // (OpenCode's own copy of the prompt is in the transcript, or the send failed.)
1354
+ leaveOutgoing();
1355
+ // After a move the bubble went with the transcript it was in.
1356
+ if (turn.isCurrent()) store.removeMessage(optimisticId);
1357
+ };
1358
+ // The server's copy of this prompt arrives over the socket; the local
1359
+ // bubble goes the moment that copy has something to show.
1360
+ trackOptimisticMessage(store, { optimisticId, existingMessageIds, onDrop: removeOptimistic, isCurrent: turn.isCurrent });
1361
+ const replaceOptimisticIfServerHasOutgoing = (items) => {
1362
+ if (!optimisticDropped && transcriptHasOutgoing(items, {
1363
+ finalText,
1364
+ images,
1365
+ existingMessageIds,
1366
+ sendStartedAt,
1367
+ })) {
1368
+ removeOptimistic();
1369
+ }
1370
+ };
1371
+ let polling = false;
1372
+ const drainOnce = async (force = false) => {
1373
+ if (polling && !force) return;
1374
+ while (polling && force) {
1375
+ await new Promise((resolve) => setTimeout(resolve, 25));
1376
+ }
1377
+ polling = true;
1378
+ try {
1379
+ // A snapshot of the session the prompt went to. Once the panel is on
1380
+ // another session it is not applied (the turn goes on without this panel).
1381
+ await hydrateTranscript(store, api, {
1382
+ sessionId: turnSessionId, isCurrent: turn.isCurrent, syncRunning: false, beforeApply: replaceOptimisticIfServerHasOutgoing,
1383
+ });
1384
+ } catch {} finally {
1385
+ polling = false;
1386
+ }
1387
+ };
1388
+ // Bus events (message.updated / message.part.updated / message.part.delta)
1389
+ // drive the transcript live. This poller only repairs an event the socket
1390
+ // dropped, so it re-reads the transcript rarely; hydrateMessages never lets
1391
+ // a snapshot roll back text that is still streaming.
1392
+ const pollHandle = setInterval(drainOnce, TRANSCRIPT_SAFETY_POLL_MS);
1393
+ const stopPolling = () => { clearInterval(pollHandle); _turnWatches.delete(stopPolling); };
1394
+ _turnWatches.add(stopPolling);
1395
+
1396
+ const sendParts = [];
1397
+ if (finalText) sendParts.push({ type: 'text', text: finalText });
1398
+ for (const img of images) {
1399
+ sendParts.push({
1400
+ type: 'file',
1401
+ mime: img.mime || 'image/png',
1402
+ filename: img.name || 'attachment',
1403
+ url: img.dataUrl,
1404
+ });
1405
+ }
1406
+ sendParts.push(...pathParts);
1407
+
1408
+ // The send failed. `message` is what to tell the user; none for a stop
1409
+ // they asked for. On the binding the prompt was sent from this is what it
1410
+ // always was: the bubble goes, the banner shows, the attachments return to
1411
+ // the strips. Once the panel is elsewhere none of it touches the session on
1412
+ // screen: the prompt is parked for its own session.
1413
+ const failSend = (message) => {
1414
+ notifyManualSend('failed');
1415
+ stopPolling();
1416
+ leaveOutgoing();
1417
+ // The composer is gone (its sub-agent panel went while the prompt was
1418
+ // out): there is no box, strip or queue to put it back into. It is handed
1419
+ // over whole, to be kept for the session it was written for.
1420
+ if (undeliverable()) { handUndelivered(promptItem, message); return false; }
1421
+ const settled = settleFailedSend(turn, _parked, { item: promptItem, error: message });
1422
+ if (!settled.here) {
1423
+ // Parked for its session; when the panel is already back on that
1424
+ // session it goes into the queue on screen now.
1425
+ if (settled.parked) restoreParkedHere();
1426
+ else tellIfLost(turn.sessionId, promptItem);
1427
+ return false;
1428
+ }
1429
+ removeOptimistic();
1430
+ if (message) store.pushError({ message });
1431
+ restoreAttachments();
1432
+ if (message) {
1433
+ // Only this prompt's own plan turn and run state: a newer turn of the
1434
+ // same session keeps its own.
1435
+ if (isPlanTurn && store.getState().planTurnId === planTurn) store.clearPlanState({ preserveMode: true });
1436
+ endTurn(store, run);
1437
+ }
1438
+ return false;
1439
+ };
1440
+
1441
+ notifyManualSend('started');
1442
+ try {
1443
+ const res = await api.send({
1444
+ sessionId: turnSessionId,
1445
+ parts: sendParts,
1446
+ model: turn.model || undefined,
1447
+ agent: outgoingAgent,
1448
+ mode,
1449
+ variant: turn.variant || undefined,
1450
+ cwd: options.cwd || turn.cwd || undefined,
1451
+ mcpProfile: turn.mcpProfile || undefined,
1452
+ });
1453
+ if (res?.error) return failSend(shouldSuppressAbortError(res.error) ? '' : res.error);
1454
+ // Delivered: that stands, whatever the panel shows by now.
1455
+ leaveOutgoing();
1456
+ notifyManualSend('succeeded');
1457
+ if (res?.async) {
1458
+ // (A composer that was unmounted meanwhile watches no turn.)
1459
+ if (_unmounted) { stopPolling(); return true; }
1460
+ waitForAsyncTurn({ stopPolling, drainOnce, isPlanTurn, planTurn, turn, run }).catch((err) => {
1461
+ stopPolling();
1462
+ if (run.isCurrent()) store.pushError({ message: err?.message || 'Async turn tracking failed' });
1463
+ endTurn(store, run);
1464
+ });
1465
+ return true;
1466
+ }
1467
+ stopPolling();
1468
+ // The reply is this turn's: it enters the store only while the store is
1469
+ // still on the binding the prompt was sent from.
1470
+ if (res?.data?.info && turn.isCurrent()) {
1471
+ store.upsertMessage(res.data.info);
1472
+ for (const part of (res.data.parts || [])) store.upsertPart(part);
1473
+ }
1474
+ const refreshed = await hydrateTranscript(store, api, {
1475
+ sessionId: turnSessionId, isCurrent: turn.isCurrent, syncRunning: false, beforeApply: replaceOptimisticIfServerHasOutgoing,
1476
+ });
1477
+ if (refreshed.error) console.warn('[ocp-v2-send] sessionMessages refresh failed', refreshed.error);
1478
+ if (isPlanTurn && turn.isCurrent()) {
1479
+ await maybeFinalizePlanTurn('send:complete', store, { lenient: true, planTurn });
1480
+ }
1481
+ // An idle event may have let the next prompt out while the transcript
1482
+ // was read: that turn's `running` is not this one's to clear.
1483
+ endTurn(store, run);
1484
+ return true;
1485
+ } catch (err) {
1486
+ return failSend(shouldSuppressAbortError(err) ? '' : (err?.message || 'Send failed'));
1487
+ }
1488
+ }
1489
+
1490
+ async function waitForAsyncTurn({ stopPolling, drainOnce, isPlanTurn, planTurn, turn, run }) {
1491
+ let done = false;
1492
+ let cleanup = null;
1493
+ let timer = null;
1494
+
1495
+ await new Promise((resolve) => {
1496
+ // The composer was unmounted: nobody waits for the end of this turn any
1497
+ // more. Its poll, its timer and its subscription end here.
1498
+ const abandon = () => {
1499
+ if (done) return;
1500
+ done = true;
1501
+ _turnWatches.delete(abandon);
1502
+ if (timer) clearTimeout(timer);
1503
+ stopPolling();
1504
+ if (unsubInner) unsubInner();
1505
+ resolve();
1506
+ };
1507
+ cleanup = async () => {
1508
+ if (done) return;
1509
+ done = true;
1510
+ _turnWatches.delete(abandon);
1511
+ if (timer) clearTimeout(timer);
1512
+ stopPolling();
1513
+ if (unsubInner) unsubInner();
1514
+ await drainOnce(true);
1515
+ // The plan of this turn, on the binding it ran on; never the plan
1516
+ // turn of the session the panel moved to.
1517
+ if (isPlanTurn && turn.isCurrent()) await maybeFinalizePlanTurn('async:idle', store, { lenient: true, planTurn });
1518
+ resolve();
1519
+ };
1520
+ // The turn is over for this panel when it stops running, or when the
1521
+ // panel is bound to another session (the turn itself goes on there).
1522
+ const unsubInner = store.subscribe((event, state) => {
1523
+ if ((event?.type === 'running:set' && !state.running) || !turn.isCurrent()) cleanup();
1524
+ });
1525
+ timer = setTimeout(() => {
1526
+ if (run.isCurrent()) store.pushError({ message: 'OpenCode turn timed out waiting for session.idle.' });
1527
+ endTurn(store, run);
1528
+ cleanup();
1529
+ }, 30 * 60 * 1000);
1530
+ _turnWatches.add(abandon);
1531
+ if (!store.getState().running) cleanup();
1532
+ });
1533
+ }
1534
+
1535
+ async function doAbort() {
1536
+ // `turn` is the session Stop was pressed in, and the turn that was running
1537
+ // there. The loop-stop request and the abort are for that session; what is
1538
+ // cleared afterwards is cleared only while the panel is still on that
1539
+ // binding and no newer turn began (a Stop that comes back late does not
1540
+ // end the next prompt).
1541
+ const turn = currentTurn(store, captureBinding(store));
1542
+ if (!turn.sessionId) return;
1543
+ // Stop means stop: what is queued waits for the user to resume it.
1544
+ _queue.pause();
1545
+ const clearStoppedTurn = () => {
1546
+ if (turn.isCurrent() && store.getState().planTurnActive) store.clearPlanState({ preserveMode: true });
1547
+ endTurn(store, turn);
1548
+ };
1549
+ if (opts.onAbort && await opts.onAbort(turn)) {
1550
+ clearStoppedTurn();
1551
+ return;
1552
+ }
1553
+ _suppressAbortErrorUntil = Date.now() + 15000;
1554
+ _suppressAbortErrorCount += 1;
1555
+ try {
1556
+ await api.abort(turn.sessionId);
1557
+ } catch (err) {
1558
+ console.warn('[ocp-v2-send] abort failed', err);
1559
+ } finally {
1560
+ clearStoppedTurn();
1561
+ }
1562
+ }
1563
+
1564
+ function shouldSuppressAbortError(err) {
1565
+ if (_suppressAbortErrorCount <= 0) return false;
1566
+ if (Date.now() > _suppressAbortErrorUntil) return false;
1567
+ const msg = typeof err === 'string' ? err : (err?.message || err?.error || String(err || ''));
1568
+ if (!/aborted|aborterror/i.test(msg)) return false;
1569
+ _suppressAbortErrorCount -= 1;
1570
+ return true;
1571
+ }
1572
+
1573
+ function transcriptHasOutgoing(items, { finalText, images, existingMessageIds, sendStartedAt }) {
1574
+ if (!Array.isArray(items) || !items.length) return false;
1575
+ for (const item of items) {
1576
+ const info = item?.info || {};
1577
+ const id = info.id || info.messageID || info.messageId;
1578
+ if (!id || existingMessageIds.has(id)) continue;
1579
+ if ((info.role || '').toLowerCase() !== 'user') continue;
1580
+ const createdMs = messageCreatedMs(info);
1581
+ if (createdMs && createdMs < sendStartedAt - 5000) continue;
1582
+ if (outgoingPartsMatch(item?.parts || [], finalText, images)) return true;
1583
+ }
1584
+ return false;
1585
+ }
1586
+
1587
+ function outgoingPartsMatch(parts, finalText, images) {
1588
+ const text = String(finalText || '').trim();
1589
+ const textMatches = !text || parts.some((part) => (
1590
+ (part?.type === 'text' || part?.text != null)
1591
+ && String(part.text || '').trim() === text
1592
+ ));
1593
+ if (!textMatches) return false;
1594
+ if (!images?.length) return true;
1595
+ const fileParts = parts.filter((part) => part?.type === 'file');
1596
+ return images.every((img) => fileParts.some((part) => {
1597
+ const filename = part.filename || part.name || '';
1598
+ const mime = part.mime || part.mediaType || '';
1599
+ return (img.name && filename === img.name)
1600
+ || (img.mime && mime === img.mime)
1601
+ || (img.dataUrl && part.url === img.dataUrl);
1602
+ }));
1603
+ }
1604
+
1605
+ function messageCreatedMs(info) {
1606
+ const raw = info?.time?.created ?? info?.time?.start ?? info?.createdAt ?? info?.created;
1607
+ if (raw == null) return 0;
1608
+ if (typeof raw === 'number') return raw < 1000000000000 ? raw * 1000 : raw;
1609
+ const parsed = Date.parse(String(raw));
1610
+ return Number.isFinite(parsed) ? parsed : 0;
1611
+ }
1612
+
1613
+ const instance = {
1614
+ unmount() {
1615
+ // From here on a send that fails has no box to go back to: it is handed
1616
+ // to opts.onUndelivered (the manager of a sub-agent's panel keeps it).
1617
+ _unmounted = true;
1618
+ try { unsubscribe(); } catch {}
1619
+ try { unsubscribeQueue(); } catch {}
1620
+ try { unsubscribeCaps(); } catch {}
1621
+ try { releasePlanLifecycle(); } catch {}
1622
+ for (const stop of [..._turnWatches]) { try { stop(); } catch {} }
1623
+ if (_drainTimer) { clearTimeout(_drainTimer); _drainTimer = null; }
1624
+ if (_mentionHints) { try { _mentionHints.destroy(); } catch {} }
1625
+ if (_modelPicker) { try { _modelPicker.destroy(); } catch {} }
1626
+ if (_variantPicker) { try { _variantPicker.destroy(); } catch {} }
1627
+ if (_slashHints) { try { _slashHints.destroy(); } catch {} }
1628
+ _root = null; _input = null; _btn = null;
1629
+ _imageStrip = null; _pathStrip = null;
1630
+ _modeButtons = []; _modelPicker = null; _variantPicker = null; _slashHints = null; _mentionHints = null;
1631
+ _queueTray = null; _wrap = null; _fileInput = null; _modeToggle = null;
1632
+ if (_primaryInstance === instance) _primaryInstance = null;
1633
+ },
1634
+ focus,
1635
+ appendPath,
1636
+ setText,
1637
+ // Undo hands a prompt's @ mentions back (ocp-v2-composer-logic.js,
1638
+ // draftRestorePlan): while a token stays in the text, the next send
1639
+ // rebuilds its file part, range and source included.
1640
+ restoreMentions(items) {
1641
+ for (const item of Array.isArray(items) ? items : []) {
1642
+ if (item?.token) _pickedMentions.set(item.token, item);
1643
+ }
1644
+ },
1645
+ // What is still waiting to be sent in `sessionId`: what is parked for it
1646
+ // and, when it is the session on screen, its queue. Nothing is removed:
1647
+ // the panel asks the user with this before it closes the session's tab.
1648
+ waitingFor(sessionId) {
1649
+ const waiting = _parked.peek(sessionId);
1650
+ if (sessionId && sessionId === store.getState().sessionId) waiting.push(..._queue.list());
1651
+ return waiting;
1652
+ },
1653
+ // The session's tab was closed (the user was asked first when prompts
1654
+ // were waiting), or the session is gone (deleted elsewhere, lost by
1655
+ // OpenCode): what was waiting for it cannot be delivered there. Returns
1656
+ // the prompts that left the parking and the queue.
1657
+ // `lost`: nobody closed it by hand, so nobody was asked: those prompts,
1658
+ // and one of its sends that fails later, are kept for the user on
1659
+ // notices (`label` names the session in them). A tab the user closed
1660
+ // takes them with it.
1661
+ forgetSession(sessionId, { lost = false, label = '' } = {}) {
1662
+ const dropped = _parked.forget(sessionId, { lost });
1663
+ // Still the session on screen (it was lost, not left): its queue goes
1664
+ // with it too.
1665
+ if (sessionId && sessionId === store.getState().sessionId && _queue.size()) {
1666
+ dropped.push(..._queue.list());
1667
+ _queue.clear();
1668
+ }
1669
+ if (lost) keepPrompts(dropped, label || sessionLabel(sessionId));
1670
+ return dropped;
1671
+ },
1672
+ // What the box and its strips hold and have not sent, as the prompt it
1673
+ // would be (null when there is nothing). Nothing is taken: a sub-agent
1674
+ // panel asks with it before it closes, because its box goes with it.
1675
+ draft() {
1676
+ if (!_input) return null;
1677
+ const text = _input.value.trim();
1678
+ const s = store.getState();
1679
+ const images = Array.isArray(s.attachedImages) ? [...s.attachedImages] : [];
1680
+ const paths = Array.isArray(s.pendingPaths) ? [...s.pendingPaths].filter(Boolean) : [];
1681
+ if (!text && !images.length && !paths.length) return null;
1682
+ return { text, images, paths, mentions: mentionFileParts(text, _pickedMentions, composerCwd()) };
1683
+ },
1684
+ // Prompts and commands that are on their way and that OpenCode has not
1685
+ // accepted yet: still this composer's, so a sub-agent panel lists them
1686
+ // before it closes. Nothing is taken.
1687
+ sending() {
1688
+ return [..._outgoing].map((item) => ({ ...item, sending: true }));
1689
+ },
1690
+ // A prompt that was kept for this composer's session while it had no
1691
+ // panel (its send failed after the panel had gone) is back in the box,
1692
+ // with its attachments, paths and mentions. False when there is no box.
1693
+ putBack(item) {
1694
+ return item ? putBackKept(keptOf(item)) : false;
1695
+ },
1696
+ // Prompts another panel could not keep (a sub-agent panel that went away
1697
+ // with its session, without anybody being asked): one notice each, here.
1698
+ keep(items, label = '') {
1699
+ keepPrompts((Array.isArray(items) ? items : []).filter(Boolean), label);
1700
+ },
1701
+ sendTextMessage,
1702
+ };
1703
+ if (!_primaryInstance) _primaryInstance = instance;
1704
+ return instance;
1705
+ }
1706
+
1707
+ // ── Legacy named exports — route to primary (first-mounted) compose ───────
1708
+ export function unmountCompose() { _primaryInstance?.unmount?.(); }
1709
+ export function focusCompose() { _primaryInstance?.focus?.(); }
1710
+ export function appendPathToCompose(path) { return _primaryInstance?.appendPath?.(path) ?? false; }
1711
+ export async function sendTextMessage(text, options = {}) {
1712
+ if (!_primaryInstance) return false;
1713
+ return _primaryInstance.sendTextMessage(text, options);
1714
+ }