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,1204 @@
1
+ // ─────────────────────────────────────────────────────────────────────────────
2
+ // OpenCode V2 — composer decisions (no DOM)
3
+ // Slash commands, shell mode, the prompt queue, prompt history, the agent list,
4
+ // attachments and @ mentions. ocp-v2-send.js is the glue that asks these
5
+ // questions on key presses and sends.
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+
8
+ // ── Slash commands ──────────────────────────────────────────────────────────
9
+ // kind:
10
+ // local handled by the panel (`action`), never reaches the model
11
+ // command an OpenCode command / skill / MCP prompt → session.command
12
+ // tui only exists in the terminal UI → opens the CLI in the Terminal
13
+ // text no way to run it on this server → sent as typed (legacy fallback)
14
+ // `needs`: request types the action depends on; without them the entry is left
15
+ // out, so a server that predates them never shows a command that cannot work.
16
+
17
+ export const LOCAL_SLASH_COMMANDS = Object.freeze([
18
+ { name: 'new', description: 'Start a new session', action: 'new', aliases: ['clear'] },
19
+ { name: 'sessions', description: 'List, search and switch sessions', action: 'sessions', aliases: ['resume', 'continue'] },
20
+ { name: 'compact', description: 'Compact context', action: 'compact', aliases: ['summarize'] },
21
+ { name: 'undo', description: 'Undo the last prompt', action: 'undo', needs: ['session:revert'] },
22
+ { name: 'redo', description: 'Restore what was undone', action: 'redo', needs: ['session:unrevert'] },
23
+ { name: 'share', description: 'Share this session (public link)', action: 'share', needs: ['session:share', 'session:unshare'] },
24
+ { name: 'unshare', description: 'Stop sharing this session', action: 'unshare', needs: ['session:share', 'session:unshare'] },
25
+ { name: 'export', description: 'Export the transcript as JSON', action: 'export' },
26
+ { name: 'fork', description: 'Fork this session', action: 'fork', needs: ['session:fork'] },
27
+ { name: 'models', description: 'Pick a model', action: 'models' },
28
+ { name: 'agents', description: 'Switch agent', action: 'agents', aliases: ['agent'] },
29
+ { name: 'help', description: 'Show what the composer can do', action: 'help' },
30
+ ]);
31
+
32
+ // Terminal-UI-only commands: they act on the TUI itself.
33
+ export const TUI_SLASH_COMMANDS = Object.freeze([
34
+ { name: 'providers', description: 'Manage providers & auth' },
35
+ { name: 'themes', description: 'Pick a theme' },
36
+ { name: 'editor', description: 'Open in editor' },
37
+ { name: 'tokens', description: 'Token usage stats' },
38
+ { name: 'config', description: 'Open config' },
39
+ { name: 'login', description: 'Provider login' },
40
+ { name: 'logout', description: 'Provider logout' },
41
+ { name: 'exit', description: 'Exit OpenCode', aliases: ['quit'] },
42
+ ]);
43
+
44
+ const SERVER_SOURCES = new Set(['command', 'skill', 'mcp']);
45
+
46
+ /**
47
+ * Everything the slash menu lists.
48
+ * serverCommands rows of command:list, or null when the server has none
49
+ * legacy { skills, userCommands } from the pre-SDK REST lists, used
50
+ * only when serverCommands is null
51
+ * supports(type) capability check
52
+ * sharePolicy OpenCode's `share` setting; 'disabled' removes /share and /unshare
53
+ */
54
+ export function buildSlashCatalog({ serverCommands = null, legacy = null, supports = () => false, sharePolicy = null } = {}) {
55
+ const out = [];
56
+ const seen = new Set();
57
+ const add = (entry) => {
58
+ const names = [entry.name, ...(entry.aliases || [])];
59
+ if (names.some((n) => seen.has(n))) return;
60
+ names.forEach((n) => seen.add(n));
61
+ out.push(entry);
62
+ };
63
+ for (const cmd of LOCAL_SLASH_COMMANDS) {
64
+ if ((cmd.needs || []).some((type) => !supports(type))) continue;
65
+ if (sharePolicy === 'disabled' && (cmd.action === 'share' || cmd.action === 'unshare')) continue;
66
+ add({ name: cmd.name, description: cmd.description, aliases: cmd.aliases || [], source: 'builtin', kind: 'local', action: cmd.action });
67
+ }
68
+ const canRun = supports('command:run');
69
+ if (Array.isArray(serverCommands) && canRun) {
70
+ for (const cmd of serverCommands) {
71
+ if (!cmd || typeof cmd.name !== 'string' || !cmd.name) continue;
72
+ add({
73
+ name: cmd.name,
74
+ description: cmd.description || '',
75
+ source: SERVER_SOURCES.has(cmd.source) ? cmd.source : 'command',
76
+ kind: 'command',
77
+ hints: Array.isArray(cmd.hints) ? cmd.hints : [],
78
+ });
79
+ }
80
+ } else if (legacy) {
81
+ // The server cannot run commands: list what the old REST endpoints know,
82
+ // and send them as typed, exactly as before.
83
+ for (const s of legacy.skills || []) {
84
+ const name = String(s?.name || s?.dirName || '').trim();
85
+ if (name) add({ name, description: String(s.description || '').trim(), source: 'skill', kind: 'text' });
86
+ }
87
+ for (const c of legacy.userCommands || []) {
88
+ const name = String(c?.name || '').trim();
89
+ if (name) add({ name, description: String(c.description || '').trim(), source: 'command', kind: 'text' });
90
+ }
91
+ add({ name: 'init', description: 'Initialize project (AGENTS.md)', source: 'builtin', kind: 'tui' });
92
+ }
93
+ for (const cmd of TUI_SLASH_COMMANDS) {
94
+ add({ name: cmd.name, description: cmd.description, aliases: cmd.aliases || [], source: 'builtin', kind: 'tui' });
95
+ }
96
+ return out;
97
+ }
98
+
99
+ // ── /help ───────────────────────────────────────────────────────────────────
100
+ // A card at the end of the transcript, built by the panel from the catalog the
101
+ // slash menu lists. It is not a message: nothing of it reaches the model.
102
+
103
+ const HELP_SOURCE_LABELS = Object.freeze({ builtin: 'panel', command: 'OpenCode command', skill: 'skill', mcp: 'MCP prompt' });
104
+ const HELP_SOURCE_ORDER = ['builtin', 'command', 'skill', 'mcp'];
105
+ // Commands of the project can be many; the slash menu lists them all.
106
+ export const HELP_COMMANDS_PER_SOURCE = 8;
107
+
108
+ /**
109
+ * What /help shows: `{ title, note, sections: [{ title, rows: [{ name, text,
110
+ * source }] }] }`.
111
+ * catalog the slash catalog of the composer's directory (buildSlashCatalog)
112
+ * supports(type) capability check: what the server cannot do is not listed
113
+ * canAct false in a sub-agent panel (its messages offer Copy only)
114
+ */
115
+ export function helpCardView({ catalog = [], supports = () => false, canAct = true } = {}) {
116
+ const sections = [];
117
+ const entries = (Array.isArray(catalog) ? catalog : []).filter((entry) => entry && typeof entry.name === 'string' && entry.name);
118
+ const commandRow = (entry) => ({
119
+ name: [entry.name, ...(entry.aliases || [])].map((name) => `/${name}`).join(', '),
120
+ text: String(entry.description || ''),
121
+ source: entry.kind === 'tui' ? 'opens the terminal' : (HELP_SOURCE_LABELS[entry.source] || HELP_SOURCE_LABELS.command),
122
+ });
123
+ const commands = [];
124
+ const local = entries.filter((entry) => entry.kind !== 'tui' && (entry.source || 'builtin') === 'builtin');
125
+ commands.push(...local.map(commandRow));
126
+ for (const source of HELP_SOURCE_ORDER.slice(1)) {
127
+ const group = entries.filter((entry) => entry.kind !== 'tui' && entry.source === source);
128
+ commands.push(...group.slice(0, HELP_COMMANDS_PER_SOURCE).map(commandRow));
129
+ if (group.length > HELP_COMMANDS_PER_SOURCE) {
130
+ commands.push({ name: '…', text: `and ${group.length - HELP_COMMANDS_PER_SOURCE} more: type / to list them all`, source: HELP_SOURCE_LABELS[source] });
131
+ }
132
+ }
133
+ commands.push(...entries.filter((entry) => entry.kind === 'tui').map(commandRow));
134
+ if (commands.length) sections.push({ title: 'Slash commands', rows: commands });
135
+
136
+ const mentionable = [
137
+ supports('find:files') && 'a file',
138
+ supports('find:symbols') && 'a workspace symbol',
139
+ supports('resource:list') && 'an MCP resource',
140
+ supports('reference:list') && 'a reference',
141
+ ].filter(Boolean);
142
+ if (mentionable.length) {
143
+ const list = mentionable.length > 1 ? `${mentionable.slice(0, -1).join(', ')} or ${mentionable[mentionable.length - 1]}` : mentionable[0];
144
+ sections.push({ title: 'Mentions', rows: [{ name: '@', text: `Type @ and pick ${list}: it is sent with the prompt.` }] });
145
+ }
146
+ if (supports('session:shell')) {
147
+ sections.push({ title: 'Shell mode', rows: [{
148
+ name: '!',
149
+ text: 'Press ! in the empty box to run a shell command in the session’s directory, with no model in between. Escape, or Backspace on the empty line, leaves.',
150
+ }] });
151
+ }
152
+ sections.push({ title: 'Queue', rows: [{
153
+ name: 'Enter',
154
+ text: 'While a turn runs, Enter queues the prompt. The queue is listed above the box and sent in order when the turn ends; after a failed turn it waits for Resume.',
155
+ }] });
156
+ const mine = ['Copy', canAct && supports('session:revert') && 'Undo to here', canAct && supports('session:fork') && 'Fork from here', canAct && supports('message:delete') && 'Delete'].filter(Boolean);
157
+ const replies = ['Copy', canAct && supports('session:revert') && 'Retry'].filter(Boolean);
158
+ sections.push({ title: 'Message actions', rows: [
159
+ { name: 'Your prompts', text: `Hover one: ${mine.join(', ')}.` },
160
+ { name: 'Replies', text: `Hover one: ${replies.join(', ')}.` },
161
+ ] });
162
+ return { title: 'OpenCode panel help', note: 'Shown here only: nothing was sent to the model.', sections };
163
+ }
164
+
165
+ /** `/name rest…` at the very start of the text → { name, args }, else null. */
166
+ export function parseSlashInput(text) {
167
+ const m = /^\/([A-Za-z0-9][\w:.-]*)(?:\s+([\s\S]*))?$/.exec(String(text || '').trim());
168
+ if (!m) return null;
169
+ return { name: m[1], args: (m[2] || '').trim() };
170
+ }
171
+
172
+ /**
173
+ * What a typed line does. null → it is an ordinary prompt (including a
174
+ * `/word` no command is named after, an absolute path, a `text` entry).
175
+ */
176
+ export function resolveSlash(text, catalog) {
177
+ const parsed = parseSlashInput(text);
178
+ if (!parsed) return null;
179
+ const entry = (catalog || []).find((c) => c.name === parsed.name || (c.aliases || []).includes(parsed.name));
180
+ if (!entry || entry.kind === 'text') return null;
181
+ if (entry.kind === 'local') return { kind: 'local', action: entry.action, name: entry.name, args: parsed.args };
182
+ if (entry.kind === 'command') return { kind: 'command', command: entry.name, args: parsed.args };
183
+ return { kind: 'tui', command: entry.name, args: parsed.args };
184
+ }
185
+
186
+ /**
187
+ * Slash catalogs by project directory. Commands, skills and MCP prompts depend
188
+ * on the directory, and several composers are on the page at once (the panel
189
+ * and a sub-agent panel, each with its own session), so there is no "current"
190
+ * catalog: each composer reads the one of its own directory.
191
+ * load(cwd) async → the catalog for that directory (throws on failure)
192
+ * fallback() the catalog to show before (or without) a loaded one
193
+ */
194
+ export function createSlashCatalogCache({ load, fallback }) {
195
+ let generation = 0;
196
+ const loaded = new Map(); // cwd → catalog
197
+ const inflight = new Map(); // cwd → Promise
198
+ const keyOf = (cwd) => String(cwd || '');
199
+ return {
200
+ /** What this directory lists right now; never waits. */
201
+ get(cwd) { return loaded.get(keyOf(cwd)) || fallback(); },
202
+ has(cwd) { return loaded.has(keyOf(cwd)); },
203
+ /** Load once per directory (until invalidate). A failure is not cached. */
204
+ load(cwd) {
205
+ const key = keyOf(cwd);
206
+ if (loaded.has(key)) return Promise.resolve(loaded.get(key));
207
+ if (inflight.has(key)) return inflight.get(key);
208
+ const startedAt = generation;
209
+ const promise = Promise.resolve()
210
+ .then(() => load(key))
211
+ .then((catalog) => {
212
+ if (startedAt === generation && Array.isArray(catalog)) loaded.set(key, catalog);
213
+ return loaded.get(key) || fallback();
214
+ })
215
+ .catch(() => fallback())
216
+ .finally(() => { if (inflight.get(key) === promise) inflight.delete(key); });
217
+ inflight.set(key, promise);
218
+ return promise;
219
+ },
220
+ /** Forget everything (the server's capabilities changed). */
221
+ invalidate() {
222
+ generation += 1;
223
+ loaded.clear();
224
+ inflight.clear();
225
+ },
226
+ };
227
+ }
228
+
229
+ // ── Shell mode ──────────────────────────────────────────────────────────────
230
+ // A shell command runs with no model in between, so the way in is narrow: the
231
+ // user presses "!" on an empty composer. Text that arrives any other way
232
+ // (pasted, set by a handoff / automation / openOpencodeWithPrompt, a file
233
+ // attach) is a prompt, whatever its first character, and takes the composer
234
+ // out of shell mode if it was in it.
235
+
236
+ /**
237
+ * A key press in the composer.
238
+ * { shell, value, key, trusted, composing, supported } → { shell, preventDefault }
239
+ * `trusted` is event.isTrusted: a synthetic key event never enters shell mode.
240
+ */
241
+ export function shellKey({ shell = false, value = '', key = '', trusted = false, composing = false, supported = false } = {}) {
242
+ if (!shell) {
243
+ const enter = supported && trusted && !composing && key === '!' && value === '';
244
+ return { shell: enter, preventDefault: enter };
245
+ }
246
+ // In shell mode: Escape leaves; Backspace on an empty line leaves.
247
+ if (key === 'Escape') return { shell: false, preventDefault: true };
248
+ if (key === 'Backspace' && value === '') return { shell: false, preventDefault: true };
249
+ return { shell: true, preventDefault: false };
250
+ }
251
+
252
+ /**
253
+ * The last check before a shell command runs: the composer is in shell mode
254
+ * and holds exactly what the user's own input events produced there. Text that
255
+ * got into the box any other way while it was armed is not run.
256
+ */
257
+ export function shellRunAllowed({ shell = false, value = '', typedValue = '' } = {}) {
258
+ return shell === true && value === typedValue && value.trim() !== '';
259
+ }
260
+
261
+ /** Text was written into the composer by code, not typed: never a shell command. */
262
+ export function shellAfterProgrammaticInput() {
263
+ return false;
264
+ }
265
+
266
+ // ── Prompt queue (client side) ──────────────────────────────────────────────
267
+ // Prompts typed while a turn runs wait here and go out one by one as turns
268
+ // finish. Stopping a turn or an error pauses the queue: the user asked for a
269
+ // halt, not for the next prompt to fire.
270
+
271
+ export const PROMPT_QUEUE_MAX = 20;
272
+
273
+ export function createPromptQueue() {
274
+ let items = [];
275
+ let paused = false;
276
+ let seq = 0;
277
+ const listeners = new Set();
278
+ const emit = () => { for (const fn of listeners) { try { fn(); } catch { /* listener's problem */ } } };
279
+ return {
280
+ list: () => items.slice(),
281
+ size: () => items.length,
282
+ isPaused: () => paused,
283
+ add(entry) {
284
+ const text = String(entry?.text || '').trim();
285
+ const images = Array.isArray(entry?.images) ? entry.images : [];
286
+ const paths = Array.isArray(entry?.paths) ? entry.paths : [];
287
+ // File parts of the @ mentions picked while typing this prompt.
288
+ const mentions = Array.isArray(entry?.mentions) ? entry.mentions : [];
289
+ if (!text && !images.length && !paths.length) return null;
290
+ if (items.length >= PROMPT_QUEUE_MAX) return null;
291
+ seq += 1;
292
+ const item = { id: `q${seq}`, text, images, paths, mentions };
293
+ items = [...items, item];
294
+ emit();
295
+ return item;
296
+ },
297
+ remove(id) {
298
+ const before = items.length;
299
+ items = items.filter((item) => item.id !== id);
300
+ if (items.length !== before) emit();
301
+ },
302
+ /** Take the next prompt out. */
303
+ shift() {
304
+ const [next, ...rest] = items;
305
+ if (!next) return null;
306
+ items = rest;
307
+ emit();
308
+ return next;
309
+ },
310
+ /** Put a prompt back at the front (its send failed). */
311
+ unshift(item) {
312
+ if (!item) return;
313
+ items = [item, ...items];
314
+ emit();
315
+ },
316
+ /**
317
+ * Put prompts back that were taken out of this session's queue (or sent
318
+ * from its composer) and could not be delivered while the panel was on
319
+ * another session: at the front, in their order, and the queue holds.
320
+ */
321
+ restore(entries) {
322
+ const back = [];
323
+ for (const entry of Array.isArray(entries) ? entries : []) {
324
+ if (!entry) continue;
325
+ seq += 1;
326
+ const item = {
327
+ id: `q${seq}`,
328
+ text: String(entry.text || '').trim(),
329
+ images: Array.isArray(entry.images) ? entry.images : [],
330
+ paths: Array.isArray(entry.paths) ? entry.paths : [],
331
+ mentions: Array.isArray(entry.mentions) ? entry.mentions : [],
332
+ };
333
+ // A slash command that could not run (only the parking hands these
334
+ // out; `add` never makes one): it runs as that command again.
335
+ if (entry.command?.command) item.command = { command: String(entry.command.command), args: String(entry.command.args || '') };
336
+ back.push(item);
337
+ }
338
+ if (!back.length) return 0;
339
+ items = [...back, ...items];
340
+ paused = true;
341
+ emit();
342
+ return back.length;
343
+ },
344
+ pause() { if (!paused && items.length) { paused = true; emit(); } },
345
+ resume() { if (paused) { paused = false; emit(); } },
346
+ clear() {
347
+ if (!items.length && !paused) return;
348
+ items = [];
349
+ paused = false;
350
+ emit();
351
+ },
352
+ subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn); },
353
+ };
354
+ }
355
+
356
+ /**
357
+ * What a session's composer still owes it while the panel is on another
358
+ * session, kept per session and in memory:
359
+ * • its outgoing queue, as it was when the panel left (`leave`);
360
+ * • prompts and slash commands that failed to go out after the panel had
361
+ * left (`park`). They go in front: they were on their way before the rest.
362
+ * Nothing here ever goes to the session on screen: `take(sessionId)` hands a
363
+ * session's entry out when the panel is bound to that session again, and the
364
+ * composer puts it in its queue, paused, with the reason.
365
+ * Nothing is ever evicted: every entry is prompts the user has not sent yet.
366
+ * The bound (`max` sessions with something waiting) is kept where refusing
367
+ * loses nothing: `isFull()` tells the composer not to start one more queue
368
+ * (the prompt stays in the box, and the user is told). What is already
369
+ * queued or on its way is always taken (`leave`, `park`).
370
+ * An entry leaves only through `take` (the panel is back) or `forget` (the
371
+ * session's tab was closed, or the session is gone). `forget` hands back what
372
+ * it drops and remembers the session as gone, so a send of it that fails
373
+ * afterwards is not kept for a session nobody can return to (`park` is
374
+ * false; `goneAs` says whether the user closed it or it was lost, and the
375
+ * composer keeps the prompts of the latter for the user: keptPromptNotice);
376
+ * `reopen` (the panel is bound to it again) lifts that.
377
+ * Only so many gone sessions are remembered. `hasTab(sessionId)` (the panel
378
+ * says which sessions have a tab) covers the rest: nothing is kept here for a
379
+ * session without a tab, and one nothing remembers the end of counts as lost.
380
+ * Without `hasTab` (a sub-agent panel, which is never rebound) every session
381
+ * counts as having one.
382
+ */
383
+ export const PARKED_SESSIONS_MAX = 64;
384
+ const GONE_SESSIONS_MAX = 256;
385
+
386
+ const parkedItem = (item) => {
387
+ const out = {
388
+ text: String(item.text || ''),
389
+ images: Array.isArray(item.images) ? [...item.images] : [],
390
+ paths: Array.isArray(item.paths) ? [...item.paths] : [],
391
+ mentions: Array.isArray(item.mentions) ? [...item.mentions] : [],
392
+ };
393
+ if (item.command?.command) out.command = { command: String(item.command.command), args: String(item.command.args || '') };
394
+ return out;
395
+ };
396
+
397
+ export function createParkedPrompts({ max = PARKED_SESSIONS_MAX, hasTab = () => true } = {}) {
398
+ const bySession = new Map(); // sessionId → { failed, queued, errors }
399
+ const gone = new Map(); // sessionId → 'closed' | 'lost', oldest first
400
+ // Nobody can return to a session that is gone or has no tab.
401
+ const nobodyReturnsTo = (key) => gone.has(key) || !hasTab(key);
402
+ const entryFor = (key) => {
403
+ let entry = bySession.get(key);
404
+ if (!entry) { entry = { failed: [], queued: [], errors: [] }; bySession.set(key, entry); }
405
+ return entry;
406
+ };
407
+ const itemsOf = (entry) => [...entry.failed, ...entry.queued];
408
+ return {
409
+ /**
410
+ * A prompt (or command) of `sessionId` that could not be delivered. Always
411
+ * kept, also past the bound (it has no other place to be). False only for
412
+ * a session that is gone or has no tab: there is nobody to hand it back to.
413
+ */
414
+ park(sessionId, { item, error = '' } = {}) {
415
+ const key = String(sessionId || '');
416
+ if (!key || !item || nobodyReturnsTo(key)) return false;
417
+ const entry = entryFor(key);
418
+ entry.failed.push(parkedItem(item));
419
+ if (error && !entry.errors.includes(error)) entry.errors.push(String(error));
420
+ return true;
421
+ },
422
+ /**
423
+ * The panel leaves `sessionId` with these prompts still in its queue.
424
+ * Always kept, except for a session that is gone or has no tab (0: the
425
+ * composer keeps them for the user, see onRebound).
426
+ */
427
+ leave(sessionId, items) {
428
+ const key = String(sessionId || '');
429
+ const list = (Array.isArray(items) ? items : []).filter(Boolean);
430
+ if (!key || !list.length || nobodyReturnsTo(key)) return 0;
431
+ entryFor(key).queued.push(...list.map(parkedItem));
432
+ return list.length;
433
+ },
434
+ /**
435
+ * What is kept for `sessionId`, removed from here: `{ items, failed,
436
+ * queued, errors }` (`items` is failed first, then the queue, each in its
437
+ * order). null when nothing is.
438
+ */
439
+ take(sessionId) {
440
+ const key = String(sessionId || '');
441
+ const entry = bySession.get(key);
442
+ if (!entry) return null;
443
+ bySession.delete(key);
444
+ return { items: itemsOf(entry), failed: entry.failed.length, queued: entry.queued.length, errors: entry.errors };
445
+ },
446
+ /** What is kept for `sessionId`, left where it is (to ask before a tab is closed). */
447
+ peek(sessionId) {
448
+ const entry = bySession.get(String(sessionId || ''));
449
+ return entry ? itemsOf(entry) : [];
450
+ },
451
+ /**
452
+ * The session's tab was closed, or the session is gone: what was kept for
453
+ * it cannot be delivered any more. Returns the items that were dropped
454
+ * (the caller asked the user first, or keeps them for the user: keptPromptNotice).
455
+ * `lost`: nobody closed it (it was deleted elsewhere, OpenCode lost it).
456
+ */
457
+ forget(sessionId, { lost = false } = {}) {
458
+ const key = String(sessionId || '');
459
+ if (!key) return [];
460
+ gone.delete(key);
461
+ gone.set(key, lost ? 'lost' : 'closed');
462
+ while (gone.size > GONE_SESSIONS_MAX) gone.delete(gone.keys().next().value);
463
+ const entry = bySession.get(key);
464
+ if (!entry) return [];
465
+ bySession.delete(key);
466
+ return itemsOf(entry);
467
+ },
468
+ /** The panel is bound to `sessionId` (again): it is not gone. */
469
+ reopen(sessionId) { gone.delete(String(sessionId || '')); },
470
+ /**
471
+ * '' while the session is not known to be gone, else how it went: 'closed'
472
+ * (by the user) or 'lost'. A session without a tab that nothing remembers
473
+ * the end of (its marker aged out) is 'lost': what fails for it is kept
474
+ * for the user, never parked for nobody and never dropped without a word.
475
+ */
476
+ goneAs(sessionId) {
477
+ const key = String(sessionId || '');
478
+ return gone.get(key) || (key && !hasTab(key) ? 'lost' : '');
479
+ },
480
+ /**
481
+ * As many sessions have prompts waiting as the bound allows: no further
482
+ * queue should be started (see the composer's enqueue). Nothing is evicted.
483
+ */
484
+ isFull: () => bySession.size >= max,
485
+ has: (sessionId) => bySession.has(String(sessionId || '')),
486
+ size: () => bySession.size,
487
+ };
488
+ }
489
+
490
+ /** The tray note for what came back from the parking (`take`'s answer). */
491
+ export function parkedPromptsNotice(entry) {
492
+ const count = entry?.items?.length || 0;
493
+ if (!count) return '';
494
+ // Entries made before the queue itself was parked carry no counts: all failed.
495
+ const failed = typeof entry.failed === 'number' ? entry.failed : count;
496
+ const queued = typeof entry.queued === 'number' ? entry.queued : 0;
497
+ const why = (entry.errors || []).filter(Boolean).join('; ');
498
+ const parts = [];
499
+ if (failed) {
500
+ parts.push(`${failed === 1 ? 'A prompt sent to this session was' : `${failed} prompts sent to this session were`} not delivered${why ? ` (${why})` : ''}.`);
501
+ }
502
+ if (queued) {
503
+ parts.push(`${queued === 1 ? 'A prompt was' : `${queued} prompts were`} waiting in this session's queue when you left it.`);
504
+ }
505
+ parts.push(`${count === 1 ? 'It is' : 'They are'} in the queue, paused: resume to send, or remove.`);
506
+ return parts.join(' ');
507
+ }
508
+
509
+ const promptExcerpt = (item) => {
510
+ const text = String(item?.text || '').replace(/\s+/g, ' ').trim();
511
+ if (!text) return '(attachments)';
512
+ return text.length > 60 ? `“${text.slice(0, 60)}…”` : `“${text}”`;
513
+ };
514
+
515
+ /**
516
+ * The notice for one prompt whose session went away by itself (it was deleted
517
+ * somewhere else, OpenCode lost it) or has no tab left: there was no moment to
518
+ * ask, and there is no session to deliver it to. The prompt is kept on the
519
+ * notice, whole (text, attachments, referenced paths, picked mentions), until
520
+ * the user puts it back into the box or discards it. The notice says what it
521
+ * holds and how long: it is in memory, like the queue it came from.
522
+ */
523
+ export function keptPromptNotice({ item, label = '' } = {}) {
524
+ if (!item) return '';
525
+ const count = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
526
+ const files = (item.images?.length || 0) + (item.paths?.length || 0);
527
+ const mentions = item.mentions?.length || 0;
528
+ const extras = [files ? count(files, 'attachment') : '', mentions ? count(mentions, 'mention') : ''].filter(Boolean);
529
+ const what = `${promptExcerpt(item)}${item.command?.command ? ' (command)' : ''}${extras.length ? ` with ${extras.join(' and ')}` : ''}`;
530
+ const where = label ? `“${label}”, and that session is gone` : 'a session that is gone';
531
+ return `Not sent: ${what} was waiting for ${where}. It is kept here until you put it back into the box or discard it; reloading the page drops it.`;
532
+ }
533
+
534
+ /**
535
+ * What putting a kept prompt back adds to the draft, which is never written
536
+ * over: its text goes under what is being typed (prefilledDraft), its
537
+ * attachments and referenced paths go back to the strips, and its mentions
538
+ * become picked mentions again, so the next send rebuilds the very same file
539
+ * parts (mentionItemOfPart). `lost` names a mention part that does not say
540
+ * which `@token` it was typed as (the composer makes none like that).
541
+ */
542
+ export function keptPromptDraft(item, draft = '') {
543
+ const mentions = [];
544
+ const lost = [];
545
+ for (const part of Array.isArray(item?.mentions) ? item.mentions : []) {
546
+ const picked = mentionItemOfPart(part);
547
+ if (picked) mentions.push(picked);
548
+ else lost.push(String(part?.filename || part?.url || 'a mention'));
549
+ }
550
+ return {
551
+ text: prefilledDraft(draft, item?.text),
552
+ images: Array.isArray(item?.images) ? [...item.images] : [],
553
+ paths: Array.isArray(item?.paths) ? [...item.paths] : [],
554
+ mentions,
555
+ lost,
556
+ };
557
+ }
558
+
559
+ // A prompt by what it is (the queue's own id of it is not part of that).
560
+ const promptIdentity = (item) => JSON.stringify([
561
+ String(item?.text || ''), item?.images || [], item?.paths || [], item?.mentions || [],
562
+ item?.command?.command ? [String(item.command.command), String(item.command.args || '')] : null,
563
+ ]);
564
+
565
+ /**
566
+ * True when two lists hold exactly the same prompts (text, attachments,
567
+ * paths, mentions, command), in any order. What the user was asked about
568
+ * before a tab closes is compared with this, not by how many there are.
569
+ */
570
+ export function samePrompts(a, b) {
571
+ const identities = (list) => (Array.isArray(list) ? list : []).map(promptIdentity).sort();
572
+ const left = identities(a);
573
+ const right = identities(b);
574
+ return left.length === right.length && left.every((identity, i) => identity === right[i]);
575
+ }
576
+
577
+ /**
578
+ * The question asked before a tab is closed while prompts are still waiting
579
+ * for its session (its queue, a prompt that could not be delivered): closing
580
+ * discards them, so the user decides. '' when nothing is waiting (no question).
581
+ * `lead` replaces the first sentence (the session menu's Delete has its own).
582
+ */
583
+ export function closeWithPromptsConfirm({ items = [], label = '', lead = '' } = {}) {
584
+ const count = items.length;
585
+ if (!count) return '';
586
+ const shown = items.slice(0, 5).map((item) => `• ${promptExcerpt(item)}${item?.command?.command ? ' (command)' : ''}`);
587
+ if (count > shown.length) shown.push(`• and ${count - shown.length} more`);
588
+ const first = lead || `Close ${label ? `“${label}”` : 'this session'}?`;
589
+ const what = count === 1 ? 'A prompt is still waiting to be sent there and will be discarded' : `${count} prompts are still waiting to be sent there and will be discarded`;
590
+ return `${first}\n\n${what}:\n${shown.join('\n')}`;
591
+ }
592
+
593
+ /** The banner when one more queue cannot be started (createParkedPrompts `isFull`). */
594
+ export function parkingFullNotice(max = PARKED_SESSIONS_MAX) {
595
+ return `Not queued: ${max} other tabs already hold prompts that are waiting. Resume or clear some of those first.`;
596
+ }
597
+
598
+ /**
599
+ * A prompt another part of the app hands to the composer (a handoff) goes
600
+ * into the box for the user to send. A draft that is already being written
601
+ * there is kept: the handed-over text is added under it, never put in its place.
602
+ */
603
+ export function prefilledDraft(current, incoming) {
604
+ const draft = String(current || '');
605
+ const text = String(incoming || '');
606
+ if (!draft.trim()) return text;
607
+ if (!text.trim() || draft.includes(text)) return draft;
608
+ return `${draft.replace(/\s+$/, '')}\n\n${text}`;
609
+ }
610
+
611
+ /** True when the next queued prompt may go out now. */
612
+ export function queueCanDrain(queue, state) {
613
+ if (!queue.size() || queue.isPaused()) return false;
614
+ if (!state.sessionId || state.running) return false;
615
+ if ((state.pendingPermissions || []).length || (state.pendingQuestions || []).length) return false;
616
+ if (state.showPostPlanActions || state.planTurnActive) return false;
617
+ if (state.sessionStatus?.type === 'busy' || state.sessionStatus?.type === 'retry') return false;
618
+ return true;
619
+ }
620
+
621
+ // ── Prompt history (ArrowUp / ArrowDown) ────────────────────────────────────
622
+
623
+ export const PROMPT_HISTORY_MAX = 50;
624
+
625
+ export function createPromptHistory(initial = []) {
626
+ let entries = (Array.isArray(initial) ? initial : []).filter((e) => typeof e === 'string' && e).slice(-PROMPT_HISTORY_MAX);
627
+ let cursor = -1; // -1 = the draft; 0 = newest entry
628
+ let draft = '';
629
+ return {
630
+ entries: () => entries.slice(),
631
+ push(text) {
632
+ const value = String(text || '').trim();
633
+ cursor = -1;
634
+ draft = '';
635
+ if (!value || entries[entries.length - 1] === value) return;
636
+ entries = [...entries, value].slice(-PROMPT_HISTORY_MAX);
637
+ },
638
+ /** One step back in time; `current` is what the box holds now. null = nothing older. */
639
+ prev(current) {
640
+ if (cursor + 1 >= entries.length) return null;
641
+ if (cursor === -1) draft = String(current || '');
642
+ cursor += 1;
643
+ return entries[entries.length - 1 - cursor];
644
+ },
645
+ /** One step forward; returns the draft when it runs off the newest entry. null = already at the draft. */
646
+ next() {
647
+ if (cursor === -1) return null;
648
+ cursor -= 1;
649
+ return cursor === -1 ? draft : entries[entries.length - 1 - cursor];
650
+ },
651
+ reset() { cursor = -1; draft = ''; },
652
+ browsing: () => cursor !== -1,
653
+ };
654
+ }
655
+
656
+ /** ArrowUp recalls history only where it would not move the caret inside the text. */
657
+ export function historyKeyApplies({ key, value = '', selectionStart = 0, selectionEnd = 0, browsing = false }) {
658
+ if (selectionStart !== selectionEnd) return false;
659
+ if (key === 'ArrowUp') return value === '' || browsing || !value.slice(0, selectionStart).includes('\n') && selectionStart === 0;
660
+ if (key === 'ArrowDown') return browsing && !value.slice(selectionEnd).includes('\n');
661
+ return false;
662
+ }
663
+
664
+ // ── Stop with Escape ────────────────────────────────────────────────────────
665
+ // One Escape is too easy to hit by accident while a turn runs: the first press
666
+ // arms, a second within the window stops.
667
+
668
+ export const ESCAPE_STOP_WINDOW_MS = 2000;
669
+
670
+ export function escapeStop(armedAt, now, running) {
671
+ if (!running) return { stop: false, armedAt: 0 };
672
+ if (armedAt && now - armedAt <= ESCAPE_STOP_WINDOW_MS) return { stop: true, armedAt: 0 };
673
+ return { stop: false, armedAt: now };
674
+ }
675
+
676
+ // ── Agents ──────────────────────────────────────────────────────────────────
677
+
678
+ /** Agents the user can pick: primary or all-mode, not hidden; build and plan first. */
679
+ export function selectableAgents(list) {
680
+ const rank = (name) => (name === 'build' ? 0 : name === 'plan' ? 1 : 2);
681
+ const seen = new Set();
682
+ return (Array.isArray(list) ? list : [])
683
+ .filter((a) => a && typeof a.name === 'string' && a.name && !a.hidden && (a.mode === 'primary' || a.mode === 'all'))
684
+ .filter((a) => (seen.has(a.name) ? false : seen.add(a.name)))
685
+ .map((a) => ({ name: a.name, description: typeof a.description === 'string' ? a.description : '', color: a.color || '' }))
686
+ .sort((a, b) => rank(a.name) - rank(b.name) || a.name.localeCompare(b.name));
687
+ }
688
+
689
+ // What the toggle shows before (or without) the agent list: the two built-ins.
690
+ export const DEFAULT_AGENTS = Object.freeze([
691
+ { name: 'build', description: 'The default agent', color: '' },
692
+ { name: 'plan', description: 'Plan mode: no edits', color: '' },
693
+ ]);
694
+
695
+ /**
696
+ * The agent list by project directory. `fetchAgents(cwd)` resolves the raw
697
+ * agents, or nothing when OpenCode is not up yet. An empty answer is never
698
+ * remembered: the composer mounts before the server is ready, and the next
699
+ * load (after `ready`, a reconnect, a project change) asks again.
700
+ */
701
+ export function createAgentCatalog(fetchAgents) {
702
+ const loaded = new Map(); // cwd → selectable agents (never empty)
703
+ const inflight = new Map();
704
+ const keyOf = (cwd) => String(cwd || '');
705
+ return {
706
+ get(cwd) { return loaded.get(keyOf(cwd)) || null; },
707
+ load(cwd, { force = false } = {}) {
708
+ const key = keyOf(cwd);
709
+ if (!force && loaded.has(key)) return Promise.resolve(loaded.get(key));
710
+ if (inflight.has(key)) return inflight.get(key);
711
+ const promise = Promise.resolve()
712
+ .then(() => fetchAgents(key))
713
+ .then((raw) => {
714
+ const agents = selectableAgents(raw);
715
+ if (agents.length) loaded.set(key, agents);
716
+ return agents.length ? agents : (loaded.get(key) || []);
717
+ })
718
+ .catch(() => loaded.get(key) || [])
719
+ .finally(() => { if (inflight.get(key) === promise) inflight.delete(key); });
720
+ inflight.set(key, promise);
721
+ return promise;
722
+ },
723
+ clear() { loaded.clear(); inflight.clear(); },
724
+ };
725
+ }
726
+
727
+ /** The agent to use when `current` is not in the list any more. */
728
+ export function resolveAgent(current, agents) {
729
+ const names = (agents || []).map((a) => a.name);
730
+ if (current && names.includes(current)) return current;
731
+ return names.includes('build') ? 'build' : (names[0] || 'build');
732
+ }
733
+
734
+ export function nextAgent(current, agents) {
735
+ const names = (agents || []).map((a) => a.name);
736
+ if (!names.length) return current || 'build';
737
+ return names[(names.indexOf(current) + 1) % names.length];
738
+ }
739
+
740
+ /**
741
+ * Which agents the toggle offers and whether the selected one may stay, for a
742
+ * composer that moves between project directories.
743
+ *
744
+ * The agent list belongs to a directory. After a directory change the list on
745
+ * hand is the previous project's until the new one arrives, so nothing is
746
+ * validated in between: an agent that exists only in the new project (the one
747
+ * a session of that project last ran with) would be thrown out by a list that
748
+ * does not apply to it. An agent that was replaced by the fallback is
749
+ * remembered and put back when the list of the directory the composer is in
750
+ * turns out to offer it, unless the selection changed again meanwhile.
751
+ */
752
+ export function createAgentChoice() {
753
+ const keyOf = (cwd) => String(cwd || '');
754
+ let listCwd = null; // directory `agents` came from; null while the built-ins show
755
+ let agents = [...DEFAULT_AGENTS];
756
+ let wanted = null; // { agent, fallback }: an agent the fallback replaced
757
+ const offers = (name) => agents.some((a) => a.name === name);
758
+ const loadedFor = (cwd) => listCwd !== null && listCwd === keyOf(cwd);
759
+ return {
760
+ agents: () => agents,
761
+ /** True when the list on hand is the server's list for `cwd`. */
762
+ isLoadedFor: loadedFor,
763
+ /** The composer is in `cwd` now; `cached` is that directory's list when it is already known. */
764
+ enter(cwd, cached) {
765
+ if (Array.isArray(cached) && cached.length) { agents = cached; listCwd = keyOf(cwd); }
766
+ else if (listCwd !== keyOf(cwd)) { agents = [...DEFAULT_AGENTS]; listCwd = null; }
767
+ },
768
+ /** The server's list for `cwd` arrived. False (ignored) when the composer is elsewhere or it is empty. */
769
+ accept(cwd, list, currentCwd) {
770
+ if (keyOf(cwd) !== keyOf(currentCwd) || !Array.isArray(list) || !list.length) return false;
771
+ agents = list;
772
+ listCwd = keyOf(cwd);
773
+ return true;
774
+ },
775
+ /**
776
+ * The agent the store should hold instead of `current`, or null to leave
777
+ * it. Only the list of the directory the composer is in decides.
778
+ */
779
+ settle(current, currentCwd) {
780
+ if (!loadedFor(currentCwd)) return null;
781
+ if (wanted && current === wanted.fallback && offers(wanted.agent)) {
782
+ const back = wanted.agent;
783
+ wanted = null;
784
+ return back === current ? null : back;
785
+ }
786
+ if (wanted && current !== wanted.fallback) wanted = null; // the selection moved on
787
+ const resolved = resolveAgent(current, agents);
788
+ if (resolved === current) return null;
789
+ wanted = { agent: current, fallback: resolved };
790
+ return resolved;
791
+ },
792
+ /** The user picked an agent, or the composer moved to another session. */
793
+ forget() { wanted = null; },
794
+ };
795
+ }
796
+
797
+ /** The plan lifecycle (PLAN COMPLETE card, async turn) is keyed on the plan agent. */
798
+ export const modeForAgent = (agent) => (agent === 'plan' ? 'plan' : 'build');
799
+
800
+ export const agentLabel = (name) => String(name || '').replace(/[-_]+/g, ' ').replace(/^./, (c) => c.toUpperCase());
801
+
802
+ // ── Attachments ─────────────────────────────────────────────────────────────
803
+
804
+ export const MAX_ATTACHMENTS = 10;
805
+ export const MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
806
+
807
+ const IMAGE_EXT = { png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp', bmp: 'image/bmp', avif: 'image/avif' };
808
+
809
+ /** The mime to announce for a path sent as a file part. */
810
+ export function mimeForPath(path) {
811
+ const value = String(path || '');
812
+ if (/[\\/]$/.test(value)) return 'application/x-directory';
813
+ const ext = (/\.([A-Za-z0-9]+)$/.exec(value)?.[1] || '').toLowerCase();
814
+ if (IMAGE_EXT[ext]) return IMAGE_EXT[ext];
815
+ if (ext === 'pdf') return 'application/pdf';
816
+ // OpenCode reads a text/plain file:// part with its Read tool, which also
817
+ // lists a directory given without a trailing slash.
818
+ return 'text/plain';
819
+ }
820
+
821
+ const baseName = (path) => String(path || '').split(/[\\/]/).filter(Boolean).pop() || String(path || '');
822
+
823
+ export function fileUrlForPath(path) {
824
+ const value = String(path || '').replace(/\\/g, '/');
825
+ const absolute = value.startsWith('/') ? value : `/${value}`;
826
+ return `file://${absolute.split('/').map((seg, i) => (i === 0 ? seg : encodeURIComponent(seg))).join('/')}`;
827
+ }
828
+
829
+ export function joinPath(cwd, relative) {
830
+ const rel = String(relative || '');
831
+ if (/^([A-Za-z]:[\\/]|\/)/.test(rel) || !cwd) return rel;
832
+ return `${String(cwd).replace(/[\\/]+$/, '')}/${rel}`;
833
+ }
834
+
835
+ /** Referenced paths as file parts (OpenCode inlines the content itself). */
836
+ export function pathFileParts(paths) {
837
+ return (Array.isArray(paths) ? paths : []).filter(Boolean).map((path) => ({
838
+ type: 'file',
839
+ mime: mimeForPath(path),
840
+ filename: baseName(path),
841
+ url: fileUrlForPath(path),
842
+ }));
843
+ }
844
+
845
+ /** Attachments from the strip ({ name, mime, dataUrl }) as file parts. */
846
+ export function imageFileParts(images) {
847
+ return (Array.isArray(images) ? images : []).filter((img) => img && img.dataUrl).map((img) => ({
848
+ type: 'file',
849
+ mime: img.mime || 'image/png',
850
+ filename: img.name || 'attachment',
851
+ url: img.dataUrl,
852
+ }));
853
+ }
854
+
855
+ /**
856
+ * A mention's part for a command turn. The `source` stays: it is what tells
857
+ * OpenCode how to read the part (an MCP resource is read through
858
+ * `source.clientName` / `source.uri`; without it the URL is stored and never
859
+ * read, seen on 1.18.34). Only the position goes: the command's template
860
+ * rewrites the text, so where the mention stood in the typed line means
861
+ * nothing in the prompt that is sent. The token itself (`value`) is kept.
862
+ */
863
+ export function commandMentionPart(part) {
864
+ if (!part?.source || typeof part.source !== 'object') return part;
865
+ const value = typeof part.source.text?.value === 'string' ? part.source.text.value : '';
866
+ return { ...part, source: { ...part.source, text: { value, start: 0, end: 0 } } };
867
+ }
868
+
869
+ /**
870
+ * File parts of a slash-command turn: what is attached, the referenced paths
871
+ * and the @ mentions, the same three sources a prompt sends.
872
+ */
873
+ export function commandFileParts({ images = [], paths = [], mentions = [] } = {}) {
874
+ const seen = new Set();
875
+ return [
876
+ ...imageFileParts(images),
877
+ ...pathFileParts(paths),
878
+ ...(Array.isArray(mentions) ? mentions : []).map(commandMentionPart),
879
+ ].filter((part) => part && part.url && (seen.has(part.url) ? false : seen.add(part.url)));
880
+ }
881
+
882
+ /** image | pdf | text | other, from a File-like { type, name }. */
883
+ export function attachmentKind(file) {
884
+ const type = String(file?.type || '').toLowerCase();
885
+ const name = String(file?.name || '').toLowerCase();
886
+ if (type.startsWith('image/')) return 'image';
887
+ if (type === 'application/pdf' || name.endsWith('.pdf')) return 'pdf';
888
+ if (type.startsWith('text/') || /\.(txt|md|markdown|json|ya?ml|toml|csv|tsv|log|xml|html?|css|s?css|jsx?|tsx?|mjs|cjs|py|rb|go|rs|java|kt|swift|c|h|cpp|hpp|cs|php|sh|zsh|sql|env|ini|conf)$/.test(name)
889
+ || type === 'application/json') return 'text';
890
+ return 'other';
891
+ }
892
+
893
+ /**
894
+ * May this file go to this model?
895
+ * capabilities: Model.capabilities from the provider list, or null when unknown
896
+ * An unknown model is given the benefit of the doubt for images (the provider
897
+ * will answer); a known one that takes no images or PDFs is refused up front.
898
+ */
899
+ export function attachmentVerdict(file, capabilities, { count = 0 } = {}) {
900
+ if (count >= MAX_ATTACHMENTS) return { ok: false, reason: `At most ${MAX_ATTACHMENTS} attachments per message.` };
901
+ if (Number(file?.size) > MAX_ATTACHMENT_BYTES) return { ok: false, reason: `${file.name || 'File'} is larger than 10 MB.` };
902
+ const kind = attachmentKind(file);
903
+ if (kind === 'other') return { ok: false, reason: `${file?.name || 'This file'} is not an image, a PDF or a text file.` };
904
+ const input = capabilities?.input;
905
+ if (kind === 'image' && input && input.image === false) return { ok: false, reason: 'The selected model does not take images.' };
906
+ if (kind === 'pdf' && input && input.pdf === false) return { ok: false, reason: 'The selected model does not take PDFs.' };
907
+ return { ok: true, kind };
908
+ }
909
+
910
+ /** Model.capabilities for `{ providerID, modelID }` out of the provider list. */
911
+ export function modelCapabilitiesOf(providers, model) {
912
+ if (!model?.providerID || !model?.modelID) return null;
913
+ const provider = (Array.isArray(providers) ? providers : []).find((p) => p?.id === model.providerID);
914
+ const entry = provider?.models?.[model.modelID];
915
+ return entry?.capabilities && typeof entry.capabilities === 'object' ? entry.capabilities : null;
916
+ }
917
+
918
+ // ── @ mentions ──────────────────────────────────────────────────────────────
919
+
920
+ /** The `@token` under the caret → { start, end, query }, else null. */
921
+ export function detectMentionToken(value, caret) {
922
+ const text = String(value || '');
923
+ const pos = Number.isInteger(caret) ? caret : text.length;
924
+ let start = pos;
925
+ while (start > 0 && !/\s/.test(text[start - 1])) start -= 1;
926
+ const token = text.slice(start, pos);
927
+ if (!token.startsWith('@')) return null;
928
+ // An e-mail address or a handle glued to a word is not a mention.
929
+ if (token.slice(1).includes('@')) return null;
930
+ return { start, end: pos, query: token.slice(1) };
931
+ }
932
+
933
+ /** Replace the token with `@path ` → { value, caret }. */
934
+ export function applyMention(value, token, path) {
935
+ const text = String(value || '');
936
+ const insert = `@${path} `;
937
+ const next = text.slice(0, token.start) + insert + text.slice(token.end);
938
+ return { value: next, caret: token.start + insert.length };
939
+ }
940
+
941
+ // What can be picked after `@`:
942
+ // file a project file or directory (find.files)
943
+ // symbol a workspace symbol from the LSP (find.symbols)
944
+ // resource a resource of a connected MCP server (experimental.resource.list)
945
+ // reference a reference from the OpenCode config (v2.reference.list)
946
+ // A pick inserts `@token ` and is remembered as an item; when the prompt is
947
+ // sent, every item whose token is still in the text becomes a file part with
948
+ // the matching FilePartSource (file / symbol / resource).
949
+
950
+ export const MENTION_GROUP_LIMIT = 12;
951
+ export const MENTION_GROUPS = Object.freeze([
952
+ { id: 'file', label: 'Files' },
953
+ { id: 'symbol', label: 'Symbols' },
954
+ { id: 'resource', label: 'MCP resources' },
955
+ { id: 'reference', label: 'References' },
956
+ ]);
957
+
958
+ // A token is one word: it ends at the first whitespace when the text is read back.
959
+ const tokenWord = (value) => String(value || '').trim().replace(/\s+/g, '-');
960
+
961
+ /** `path` relative to `cwd` when it is inside it, else as given. */
962
+ export function relativeTo(cwd, path) {
963
+ const base = String(cwd || '').replace(/[\\/]+$/, '');
964
+ const value = String(path || '');
965
+ return base && value.startsWith(`${base}/`) ? value.slice(base.length + 1) : value;
966
+ }
967
+
968
+ // An MCP resource as a picker item (also rebuilt from a sent prompt, see draftRestorePlan).
969
+ const resourceItem = (res) => ({
970
+ kind: 'resource', token: `${tokenWord(res.client)}:${tokenWord(res.name || res.uri)}`, label: res.name || res.uri,
971
+ detail: `${res.client} · ${res.uri}`, uri: res.uri, client: res.client, mimeType: res.mimeType || '',
972
+ });
973
+
974
+ const matchesQuery = (query, ...fields) => {
975
+ const needle = String(query || '').trim().toLowerCase();
976
+ if (!needle) return true;
977
+ return fields.some((field) => typeof field === 'string' && field.toLowerCase().includes(needle));
978
+ };
979
+
980
+ /**
981
+ * The picker's groups for `query`: `[{ id, label, items }]`, empty groups left
982
+ * out. Files and symbols were searched by the server; resources and references
983
+ * are whole lists, filtered here. Every item has `{ kind, token, label, detail }`
984
+ * plus what its file part needs.
985
+ */
986
+ export function mentionGroups({ query = '', cwd = '', files = [], symbols = [], resources = [], references = [] } = {}) {
987
+ const items = {
988
+ file: (Array.isArray(files) ? files : [])
989
+ .filter((path) => typeof path === 'string' && path)
990
+ .map((path) => ({ kind: 'file', token: path, label: path, detail: '', path })),
991
+ symbol: (Array.isArray(symbols) ? symbols : [])
992
+ .filter((sym) => sym && typeof sym.name === 'string' && sym.name && typeof sym.path === 'string' && sym.path)
993
+ .map((sym) => {
994
+ const rel = relativeTo(cwd, sym.path);
995
+ const line = (Number(sym.range?.start?.line) || 0) + 1;
996
+ return {
997
+ kind: 'symbol', token: `${tokenWord(rel)}#${tokenWord(sym.name)}`, label: sym.name, detail: `${rel}:${line}`,
998
+ path: sym.path, name: sym.name, symbolKind: Number(sym.kind) || 0,
999
+ range: sym.range || { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
1000
+ };
1001
+ }),
1002
+ resource: (Array.isArray(resources) ? resources : [])
1003
+ .filter((res) => res && typeof res.uri === 'string' && res.uri && typeof res.client === 'string' && res.client)
1004
+ .filter((res) => matchesQuery(query, res.name, res.uri, res.client, res.description))
1005
+ .map(resourceItem),
1006
+ reference: (Array.isArray(references) ? references : [])
1007
+ .filter((ref) => ref && typeof ref.name === 'string' && ref.name && typeof ref.path === 'string' && ref.path)
1008
+ .filter((ref) => matchesQuery(query, ref.name, ref.description, ref.path))
1009
+ .map((ref) => ({
1010
+ kind: 'reference', token: `ref:${tokenWord(ref.name)}`, label: ref.name, detail: ref.description || ref.path, path: ref.path,
1011
+ })),
1012
+ };
1013
+ return MENTION_GROUPS
1014
+ .map((group) => {
1015
+ const seen = new Set();
1016
+ const unique = items[group.id].filter((item) => (seen.has(item.token) ? false : seen.add(item.token)));
1017
+ return { ...group, items: unique.slice(0, MENTION_GROUP_LIMIT) };
1018
+ })
1019
+ .filter((group) => group.items.length);
1020
+ }
1021
+
1022
+ /** Where `@token` stands in the text as a word of its own, or -1. */
1023
+ function mentionPosition(value, needle) {
1024
+ let from = 0;
1025
+ let at = -1;
1026
+ while ((at = value.indexOf(needle, from)) !== -1) {
1027
+ const before = at === 0 ? ' ' : value[at - 1];
1028
+ const after = value[at + needle.length] ?? ' ';
1029
+ if (/\s/.test(before) && /\s/.test(after)) return at;
1030
+ from = at + needle.length;
1031
+ }
1032
+ return -1;
1033
+ }
1034
+
1035
+ /** The file part of one picked item; `text` is where its token stands in the prompt. */
1036
+ export function mentionPart(item, text, cwd) {
1037
+ // A part that was sent before (put back by Undo) goes out as it was: same
1038
+ // URL (a symbol's line range is in it), same source; only where its token
1039
+ // stands in the text is read again.
1040
+ if (item.part) return { ...item.part, source: { ...item.part.source, text } };
1041
+ if (item.kind === 'symbol') {
1042
+ const start = Number(item.range?.start?.line) || 0;
1043
+ const end = Math.max(start, Number(item.range?.end?.line) || 0);
1044
+ return {
1045
+ type: 'file',
1046
+ mime: 'text/plain',
1047
+ filename: baseName(item.path),
1048
+ // OpenCode reads the lines `start`..`end` of the file for a ranged URL.
1049
+ url: `${fileUrlForPath(item.path)}?start=${start + 1}&end=${end + 1}`,
1050
+ source: { type: 'symbol', path: item.path, range: item.range, name: item.name, kind: item.symbolKind || 0, text },
1051
+ };
1052
+ }
1053
+ if (item.kind === 'resource') {
1054
+ return {
1055
+ type: 'file',
1056
+ mime: item.mimeType || 'text/plain',
1057
+ filename: item.label || item.uri,
1058
+ url: item.uri,
1059
+ source: { type: 'resource', clientName: item.client, uri: item.uri, text },
1060
+ };
1061
+ }
1062
+ // A file, a directory or a reference (a path OpenCode keeps for the project).
1063
+ const absolute = joinPath(cwd, item.path);
1064
+ return {
1065
+ type: 'file',
1066
+ mime: mimeForPath(item.path),
1067
+ filename: item.kind === 'reference' ? (item.label || baseName(item.path)) : baseName(item.path),
1068
+ url: fileUrlForPath(absolute),
1069
+ source: { type: 'file', path: absolute, text },
1070
+ };
1071
+ }
1072
+
1073
+ /**
1074
+ * File parts for the mentions still present in the text. `picked` holds what
1075
+ * was chosen from the picker: a Map of token → item (see mentionGroups), or a
1076
+ * Set of file paths. An `@word` the user merely typed is text.
1077
+ */
1078
+ export function mentionFileParts(text, picked, cwd) {
1079
+ const value = String(text || '');
1080
+ const parts = [];
1081
+ const seen = new Set();
1082
+ const entries = picked instanceof Map
1083
+ ? [...picked.entries()]
1084
+ : [...(picked || [])].map((path) => [path, { kind: 'file', token: path, path }]);
1085
+ for (const [token, item] of entries) {
1086
+ if (!token || !item || seen.has(token)) continue;
1087
+ const needle = `@${token}`;
1088
+ const at = mentionPosition(value, needle);
1089
+ if (at === -1) continue;
1090
+ seen.add(token);
1091
+ parts.push(mentionPart(item, { value: needle, start: at, end: at + needle.length }, cwd));
1092
+ }
1093
+ return parts;
1094
+ }
1095
+
1096
+ // ── Putting a sent prompt back in the composer (Undo, "edit and resend") ────
1097
+ // What a prompt carried besides its text comes back as what it was:
1098
+ // a pasted image / PDF / text file (data URL) → the attachment strip
1099
+ // an @ mention (a file part with a `source`) → a picked mention again: its
1100
+ // token is still in the text, so the next send rebuilds the very same
1101
+ // part, a symbol's line range and source included
1102
+ // an MCP resource mention → a picked mention, rebuilt
1103
+ // from the session's resource list: OpenCode stores a resource it read as
1104
+ // text ("Reading MCP resource: name (uri)"), not as a file part
1105
+ // a referenced path (file:// without a source) → a path chip
1106
+ // Anything that cannot be put back as what it was is named in `lost`, never
1107
+ // turned into something else (a ranged part is not a whole-file path).
1108
+
1109
+ /** A sent file part as a picker item, when its source names the `@token` it was typed as. */
1110
+ export function mentionItemOfPart(part) {
1111
+ const source = part?.source;
1112
+ const value = typeof source?.text?.value === 'string' ? source.text.value : '';
1113
+ if (!source || typeof source.type !== 'string' || typeof part.url !== 'string' || !part.url) return null;
1114
+ if (!value.startsWith('@') || value.length < 2 || /\s/.test(value)) return null;
1115
+ const token = value.slice(1);
1116
+ return {
1117
+ kind: source.type,
1118
+ token,
1119
+ label: source.name || part.filename || token,
1120
+ detail: '',
1121
+ part: { type: 'file', mime: part.mime || 'text/plain', filename: part.filename || baseName(source.path || token), url: part.url, source },
1122
+ };
1123
+ }
1124
+
1125
+ // A file:// URL that names a whole file or directory → its path; '' for a
1126
+ // ranged URL (`?start=…`) or anything that is not a file URL.
1127
+ function plainFilePath(url) {
1128
+ if (typeof url !== 'string' || !url.startsWith('file://')) return '';
1129
+ try {
1130
+ const parsed = new URL(url);
1131
+ return parsed.search || parsed.hash ? '' : decodeURIComponent(parsed.pathname);
1132
+ } catch { return ''; }
1133
+ }
1134
+
1135
+ /**
1136
+ * Where each thing a prompt carried goes when the prompt is put back.
1137
+ * text the prompt's text (already in the composer)
1138
+ * files its file parts (replayablePrompt)
1139
+ * resources `{ name, uri }` of the MCP resources it read (replayablePrompt)
1140
+ * resourceList the session's resources now (resource:list); [] when unknown
1141
+ * → { attachments, paths, mentions, lost }
1142
+ */
1143
+ export function draftRestorePlan({ text = '', files = [], resources = [], resourceList = [] } = {}) {
1144
+ const value = String(text || '');
1145
+ const plan = { attachments: [], paths: [], mentions: [], lost: [] };
1146
+ const inText = (token) => mentionPosition(value, `@${token}`) !== -1;
1147
+ const tokens = new Set();
1148
+ const addMention = (item) => { if (!tokens.has(item.token)) { tokens.add(item.token); plan.mentions.push(item); } };
1149
+ for (const file of Array.isArray(files) ? files : []) {
1150
+ const url = typeof file?.url === 'string' ? file.url : '';
1151
+ if (!url) continue;
1152
+ if (url.startsWith('data:')) {
1153
+ plan.attachments.push({ name: file.filename, mime: file.mime, dataUrl: url });
1154
+ continue;
1155
+ }
1156
+ const item = mentionItemOfPart(file);
1157
+ if (item && inText(item.token)) { addMention(item); continue; }
1158
+ const path = plainFilePath(url);
1159
+ if (path) plan.paths.push(path);
1160
+ else plan.lost.push(file.filename || url);
1161
+ }
1162
+ for (const ref of Array.isArray(resources) ? resources : []) {
1163
+ if (!ref || typeof ref.uri !== 'string' || !ref.uri) continue;
1164
+ const item = (Array.isArray(resourceList) ? resourceList : [])
1165
+ .filter((res) => res && res.uri === ref.uri && typeof res.client === 'string' && res.client)
1166
+ .map(resourceItem)
1167
+ .find((candidate) => inText(candidate.token));
1168
+ if (item) addMention(item);
1169
+ else plan.lost.push(`${ref.name || ref.uri} (MCP resource)`);
1170
+ }
1171
+ return plan;
1172
+ }
1173
+
1174
+ /**
1175
+ * The file parts for the MCP resources a prompt read, to send it again
1176
+ * (Retry): rebuilt from the session's resource list, with the source OpenCode
1177
+ * needs to read them. `{ parts, lost }`.
1178
+ */
1179
+ export function resourceReplayParts({ text = '', resources = [], resourceList = [] } = {}) {
1180
+ const plan = draftRestorePlan({ text, resources, resourceList });
1181
+ const picked = new Map(plan.mentions.map((item) => [item.token, item]));
1182
+ return { parts: mentionFileParts(text, picked, ''), lost: plan.lost };
1183
+ }
1184
+
1185
+ /**
1186
+ * A cache with a lifetime, by key: the resource and reference lists are asked
1187
+ * once per session and directory, then reused while the picker is typed in.
1188
+ */
1189
+ export function createKeyedCache(load, { ttlMs = 30_000, now = () => Date.now() } = {}) {
1190
+ const entries = new Map(); // key → { at, promise }
1191
+ return {
1192
+ get(key) {
1193
+ const hit = entries.get(key);
1194
+ if (hit && now() - hit.at < ttlMs) return hit.promise;
1195
+ const promise = Promise.resolve().then(() => load(key)).catch(() => {
1196
+ if (entries.get(key)?.promise === promise) entries.delete(key);
1197
+ return [];
1198
+ });
1199
+ entries.set(key, { at: now(), promise });
1200
+ return promise;
1201
+ },
1202
+ clear() { entries.clear(); },
1203
+ };
1204
+ }