kimaki 0.31.0 → 1.1.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 (1001) hide show
  1. package/dist/agent-ui.d.ts +33 -0
  2. package/dist/agent-ui.e2e.test.d.ts +1 -0
  3. package/dist/agent-ui.e2e.test.js +140 -0
  4. package/dist/agent-ui.e2e.test.js.map +1 -0
  5. package/dist/agent-ui.js +199 -0
  6. package/dist/agent-ui.js.map +1 -0
  7. package/dist/analytics.d.ts +42 -0
  8. package/dist/analytics.e2e.test.d.ts +1 -0
  9. package/dist/analytics.e2e.test.js +112 -0
  10. package/dist/analytics.e2e.test.js.map +1 -0
  11. package/dist/analytics.js +162 -223
  12. package/dist/analytics.js.map +1 -0
  13. package/dist/analytics.test.d.ts +1 -0
  14. package/dist/analytics.test.js +121 -157
  15. package/dist/analytics.test.js.map +1 -0
  16. package/dist/autostart.d.ts +7 -0
  17. package/dist/autostart.js +87 -0
  18. package/dist/autostart.js.map +1 -0
  19. package/dist/bot.d.ts +91 -0
  20. package/dist/bot.js +120 -0
  21. package/dist/bot.js.map +1 -0
  22. package/dist/btw.e2e.test.d.ts +1 -0
  23. package/dist/btw.e2e.test.js +102 -0
  24. package/dist/btw.e2e.test.js.map +1 -0
  25. package/dist/builtin-skills.d.ts +18 -0
  26. package/dist/builtin-skills.js +51 -0
  27. package/dist/builtin-skills.js.map +1 -0
  28. package/dist/builtin-skills.test.d.ts +1 -0
  29. package/dist/builtin-skills.test.js +28 -0
  30. package/dist/builtin-skills.test.js.map +1 -0
  31. package/dist/catalog-refresh.e2e.test.d.ts +1 -0
  32. package/dist/catalog-refresh.e2e.test.js +90 -0
  33. package/dist/catalog-refresh.e2e.test.js.map +1 -0
  34. package/dist/cli/bot.d.ts +5 -0
  35. package/dist/cli/bot.js +434 -0
  36. package/dist/cli/bot.js.map +1 -0
  37. package/dist/cli/discord.d.ts +3 -0
  38. package/dist/cli/discord.js +118 -0
  39. package/dist/cli/discord.js.map +1 -0
  40. package/dist/cli/project.d.ts +5 -0
  41. package/dist/cli/project.js +143 -0
  42. package/dist/cli/project.js.map +1 -0
  43. package/dist/cli/schedule.d.ts +3 -0
  44. package/dist/cli/schedule.js +156 -0
  45. package/dist/cli/schedule.js.map +1 -0
  46. package/dist/cli/session.d.ts +4 -0
  47. package/dist/cli/session.js +245 -0
  48. package/dist/cli/session.js.map +1 -0
  49. package/dist/cli/shared.d.ts +55 -0
  50. package/dist/cli/shared.js +138 -0
  51. package/dist/cli/shared.js.map +1 -0
  52. package/dist/cli-discord.e2e.test.d.ts +1 -0
  53. package/dist/cli-discord.e2e.test.js +82 -0
  54. package/dist/cli-discord.e2e.test.js.map +1 -0
  55. package/dist/cli-lock.e2e.test.d.ts +1 -0
  56. package/dist/cli-lock.e2e.test.js +520 -0
  57. package/dist/cli-lock.e2e.test.js.map +1 -0
  58. package/dist/cli-read.e2e.test.d.ts +1 -0
  59. package/dist/cli-read.e2e.test.js +86 -0
  60. package/dist/cli-read.e2e.test.js.map +1 -0
  61. package/dist/cli-sessions.e2e.test.d.ts +1 -0
  62. package/dist/cli-sessions.e2e.test.js +116 -0
  63. package/dist/cli-sessions.e2e.test.js.map +1 -0
  64. package/dist/cli.d.ts +2 -0
  65. package/dist/cli.js +34 -263
  66. package/dist/cli.js.map +1 -0
  67. package/dist/commands/login-commands.d.ts +59 -0
  68. package/dist/commands/login-commands.js +341 -0
  69. package/dist/commands/login-commands.js.map +1 -0
  70. package/dist/commands/preference-commands.d.ts +44 -0
  71. package/dist/commands/preference-commands.js +565 -0
  72. package/dist/commands/preference-commands.js.map +1 -0
  73. package/dist/commands/session-commands.d.ts +8 -0
  74. package/dist/commands/session-commands.js +267 -0
  75. package/dist/commands/session-commands.js.map +1 -0
  76. package/dist/commands/thread-commands.d.ts +16 -0
  77. package/dist/commands/thread-commands.js +285 -0
  78. package/dist/commands/thread-commands.js.map +1 -0
  79. package/dist/commands/worktree-commands.d.ts +41 -0
  80. package/dist/commands/worktree-commands.js +279 -0
  81. package/dist/commands/worktree-commands.js.map +1 -0
  82. package/dist/commands.e2e.test.d.ts +1 -0
  83. package/dist/commands.e2e.test.js +255 -0
  84. package/dist/commands.e2e.test.js.map +1 -0
  85. package/dist/credentials.d.ts +84 -0
  86. package/dist/credentials.js +320 -0
  87. package/dist/credentials.js.map +1 -0
  88. package/dist/db.d.ts +1532 -0
  89. package/dist/db.js +57 -245
  90. package/dist/db.js.map +1 -0
  91. package/dist/db.test.d.ts +1 -0
  92. package/dist/db.test.js +351 -682
  93. package/dist/db.test.js.map +1 -0
  94. package/dist/duration.d.ts +2 -0
  95. package/dist/duration.js +18 -0
  96. package/dist/duration.js.map +1 -0
  97. package/dist/effects.d.ts +15 -0
  98. package/dist/effects.js +315 -0
  99. package/dist/effects.js.map +1 -0
  100. package/dist/errors.d.ts +38 -0
  101. package/dist/errors.js +40 -175
  102. package/dist/errors.js.map +1 -0
  103. package/dist/event-loop.d.ts +41 -0
  104. package/dist/event-loop.js +418 -0
  105. package/dist/event-loop.js.map +1 -0
  106. package/dist/file-edit-log.d.ts +41 -0
  107. package/dist/file-edit-log.js +122 -164
  108. package/dist/file-edit-log.js.map +1 -0
  109. package/dist/file-edit-log.test.d.ts +1 -0
  110. package/dist/file-edit-log.test.js +68 -244
  111. package/dist/file-edit-log.test.js.map +1 -0
  112. package/dist/format-parts.d.ts +137 -0
  113. package/dist/format-parts.js +300 -0
  114. package/dist/format-parts.js.map +1 -0
  115. package/dist/format-parts.test.d.ts +1 -0
  116. package/dist/format-parts.test.js +144 -0
  117. package/dist/format-parts.test.js.map +1 -0
  118. package/dist/gateway-replay.e2e.test.d.ts +1 -0
  119. package/dist/gateway-replay.e2e.test.js +95 -0
  120. package/dist/gateway-replay.e2e.test.js.map +1 -0
  121. package/dist/harness.e2e.test.d.ts +1 -0
  122. package/dist/harness.e2e.test.js +107 -0
  123. package/dist/harness.e2e.test.js.map +1 -0
  124. package/dist/hello-thread.e2e.test.d.ts +1 -0
  125. package/dist/hello-thread.e2e.test.js +194 -0
  126. package/dist/hello-thread.e2e.test.js.map +1 -0
  127. package/dist/ingress.d.ts +7 -0
  128. package/dist/ingress.js +337 -0
  129. package/dist/ingress.js.map +1 -0
  130. package/dist/interaction-context.d.ts +45 -0
  131. package/dist/interaction-context.js +77 -0
  132. package/dist/interaction-context.js.map +1 -0
  133. package/dist/interrupt.e2e.test.d.ts +1 -0
  134. package/dist/interrupt.e2e.test.js +124 -0
  135. package/dist/interrupt.e2e.test.js.map +1 -0
  136. package/dist/legacy.e2e.test.d.ts +1 -0
  137. package/dist/legacy.e2e.test.js +85 -0
  138. package/dist/legacy.e2e.test.js.map +1 -0
  139. package/dist/live-call.d.ts +51 -0
  140. package/dist/live-call.js +207 -0
  141. package/dist/live-call.js.map +1 -0
  142. package/dist/lock-routes.d.ts +812 -0
  143. package/dist/lock-routes.js +635 -0
  144. package/dist/lock-routes.js.map +1 -0
  145. package/dist/lock-server.d.ts +36 -0
  146. package/dist/lock-server.js +223 -0
  147. package/dist/lock-server.js.map +1 -0
  148. package/dist/logger.d.ts +13 -0
  149. package/dist/logger.js +112 -162
  150. package/dist/logger.js.map +1 -0
  151. package/dist/login.e2e.test.d.ts +1 -0
  152. package/dist/login.e2e.test.js +83 -0
  153. package/dist/login.e2e.test.js.map +1 -0
  154. package/dist/main.d.ts +33 -0
  155. package/dist/main.js +261 -0
  156. package/dist/main.js.map +1 -0
  157. package/dist/markdown/components.d.ts +10 -0
  158. package/dist/markdown/components.js +77 -0
  159. package/dist/markdown/components.js.map +1 -0
  160. package/dist/markdown/markdown.test.d.ts +1 -0
  161. package/dist/markdown/markdown.test.js +393 -0
  162. package/dist/markdown/markdown.test.js.map +1 -0
  163. package/dist/markdown/render-markdown.d.ts +30 -0
  164. package/dist/markdown/render-markdown.js +265 -0
  165. package/dist/markdown/render-markdown.js.map +1 -0
  166. package/dist/message-tool.e2e.test.d.ts +1 -0
  167. package/dist/message-tool.e2e.test.js +39 -0
  168. package/dist/message-tool.e2e.test.js.map +1 -0
  169. package/dist/migrations.d.ts +8 -0
  170. package/dist/migrations.js +187 -0
  171. package/dist/migrations.js.map +1 -0
  172. package/dist/onboarding-cli.e2e.test.d.ts +1 -0
  173. package/dist/onboarding-cli.e2e.test.js +178 -0
  174. package/dist/onboarding-cli.e2e.test.js.map +1 -0
  175. package/dist/onboarding.d.ts +36 -0
  176. package/dist/onboarding.e2e.test.d.ts +1 -0
  177. package/dist/onboarding.e2e.test.js +148 -0
  178. package/dist/onboarding.e2e.test.js.map +1 -0
  179. package/dist/onboarding.js +261 -0
  180. package/dist/onboarding.js.map +1 -0
  181. package/dist/opencode-server.d.ts +54 -0
  182. package/dist/opencode-server.js +417 -0
  183. package/dist/opencode-server.js.map +1 -0
  184. package/dist/opencode-server.test.d.ts +1 -0
  185. package/dist/opencode-server.test.js +21 -0
  186. package/dist/opencode-server.test.js.map +1 -0
  187. package/dist/permissions.d.ts +31 -0
  188. package/dist/permissions.e2e.test.d.ts +1 -0
  189. package/dist/permissions.e2e.test.js +166 -0
  190. package/dist/permissions.e2e.test.js.map +1 -0
  191. package/dist/permissions.js +95 -0
  192. package/dist/permissions.js.map +1 -0
  193. package/dist/plugin/anthropic/index.d.ts +13 -0
  194. package/dist/plugin/anthropic/index.js +201 -0
  195. package/dist/plugin/anthropic/index.js.map +1 -0
  196. package/dist/plugin/anthropic/index.test.d.ts +1 -0
  197. package/dist/plugin/anthropic/index.test.js +39 -0
  198. package/dist/plugin/anthropic/index.test.js.map +1 -0
  199. package/dist/plugin/index.d.ts +3 -0
  200. package/dist/plugin/index.js +124 -0
  201. package/dist/plugin/index.js.map +1 -0
  202. package/dist/plugin/read-media.d.ts +23 -0
  203. package/dist/plugin/read-media.js +72 -0
  204. package/dist/plugin/read-media.js.map +1 -0
  205. package/dist/plugin/session-id/index.d.ts +3 -0
  206. package/dist/plugin/session-id/index.js +18 -0
  207. package/dist/plugin/session-id/index.js.map +1 -0
  208. package/dist/plugin.e2e.test.d.ts +1 -0
  209. package/dist/plugin.e2e.test.js +92 -0
  210. package/dist/plugin.e2e.test.js.map +1 -0
  211. package/dist/preference-commands.e2e.test.d.ts +1 -0
  212. package/dist/preference-commands.e2e.test.js +555 -0
  213. package/dist/preference-commands.e2e.test.js.map +1 -0
  214. package/dist/probe-live.tmp.d.ts +1 -0
  215. package/dist/probe-live.tmp.js +82 -0
  216. package/dist/probe-live.tmp.js.map +1 -0
  217. package/dist/project.d.ts +58 -0
  218. package/dist/project.js +169 -0
  219. package/dist/project.js.map +1 -0
  220. package/dist/prompt.d.ts +112 -0
  221. package/dist/prompt.js +577 -0
  222. package/dist/prompt.js.map +1 -0
  223. package/dist/questions.d.ts +59 -0
  224. package/dist/questions.e2e.test.d.ts +1 -0
  225. package/dist/questions.e2e.test.js +290 -0
  226. package/dist/questions.e2e.test.js.map +1 -0
  227. package/dist/questions.js +231 -0
  228. package/dist/questions.js.map +1 -0
  229. package/dist/queue.d.ts +45 -0
  230. package/dist/queue.e2e.test.d.ts +1 -0
  231. package/dist/queue.e2e.test.js +220 -0
  232. package/dist/queue.e2e.test.js.map +1 -0
  233. package/dist/queue.js +132 -0
  234. package/dist/queue.js.map +1 -0
  235. package/dist/remote-send.d.ts +32 -0
  236. package/dist/remote-send.e2e.test.d.ts +1 -0
  237. package/dist/remote-send.e2e.test.js +89 -0
  238. package/dist/remote-send.e2e.test.js.map +1 -0
  239. package/dist/remote-send.js +105 -0
  240. package/dist/remote-send.js.map +1 -0
  241. package/dist/renderer.e2e.test.d.ts +1 -0
  242. package/dist/renderer.e2e.test.js +357 -0
  243. package/dist/renderer.e2e.test.js.map +1 -0
  244. package/dist/restart.e2e.test.d.ts +1 -0
  245. package/dist/restart.e2e.test.js +68 -0
  246. package/dist/restart.e2e.test.js.map +1 -0
  247. package/dist/routes.d.ts +24 -0
  248. package/dist/routes.js +42 -0
  249. package/dist/routes.js.map +1 -0
  250. package/dist/routes.test.d.ts +1 -0
  251. package/dist/routes.test.js +79 -0
  252. package/dist/routes.test.js.map +1 -0
  253. package/dist/routines.e2e.test.d.ts +1 -0
  254. package/dist/routines.e2e.test.js +188 -0
  255. package/dist/routines.e2e.test.js.map +1 -0
  256. package/dist/scheduler.d.ts +116 -0
  257. package/dist/scheduler.e2e.test.d.ts +1 -0
  258. package/dist/scheduler.e2e.test.js +163 -0
  259. package/dist/scheduler.e2e.test.js.map +1 -0
  260. package/dist/scheduler.js +602 -0
  261. package/dist/scheduler.js.map +1 -0
  262. package/dist/scheduler.test.d.ts +1 -0
  263. package/dist/scheduler.test.js +180 -0
  264. package/dist/scheduler.test.js.map +1 -0
  265. package/dist/schema-sql.d.ts +1 -0
  266. package/dist/schema-sql.js +3 -0
  267. package/dist/schema-sql.js.map +1 -0
  268. package/dist/schema.d.ts +2968 -0
  269. package/dist/schema.js +95 -252
  270. package/dist/schema.js.map +1 -0
  271. package/dist/session-commands.e2e.test.d.ts +1 -0
  272. package/dist/session-commands.e2e.test.js +285 -0
  273. package/dist/session-commands.e2e.test.js.map +1 -0
  274. package/dist/session-events.d.ts +83 -0
  275. package/dist/session-events.js +260 -0
  276. package/dist/session-events.js.map +1 -0
  277. package/dist/session-ops.e2e.test.d.ts +1 -0
  278. package/dist/session-ops.e2e.test.js +114 -0
  279. package/dist/session-ops.e2e.test.js.map +1 -0
  280. package/dist/sessions.d.ts +121 -0
  281. package/dist/sessions.js +663 -0
  282. package/dist/sessions.js.map +1 -0
  283. package/dist/shell.e2e.test.d.ts +1 -0
  284. package/dist/shell.e2e.test.js +147 -0
  285. package/dist/shell.e2e.test.js.map +1 -0
  286. package/dist/slash-commands.d.ts +62 -0
  287. package/dist/slash-commands.js +476 -0
  288. package/dist/slash-commands.js.map +1 -0
  289. package/dist/slash-commands.test.d.ts +1 -0
  290. package/dist/slash-commands.test.js +87 -0
  291. package/dist/slash-commands.test.js.map +1 -0
  292. package/dist/sleep.e2e.test.d.ts +1 -0
  293. package/dist/sleep.e2e.test.js +115 -487
  294. package/dist/sleep.e2e.test.js.map +1 -0
  295. package/dist/sleeps.d.ts +37 -0
  296. package/dist/sleeps.js +220 -0
  297. package/dist/sleeps.js.map +1 -0
  298. package/dist/store.d.ts +10 -0
  299. package/dist/store.js +8 -28
  300. package/dist/store.js.map +1 -0
  301. package/dist/system-prompt.d.ts +76 -0
  302. package/dist/system-prompt.js +852 -0
  303. package/dist/system-prompt.js.map +1 -0
  304. package/dist/tasks.e2e.test.d.ts +1 -0
  305. package/dist/tasks.e2e.test.js +173 -0
  306. package/dist/tasks.e2e.test.js.map +1 -0
  307. package/dist/test/auth-plugin/index.d.ts +3 -0
  308. package/dist/test/auth-plugin/index.js +18 -0
  309. package/dist/test/auth-plugin/index.js.map +1 -0
  310. package/dist/test/fake-mcp-server.d.ts +1 -0
  311. package/dist/test/fake-mcp-server.js +45 -0
  312. package/dist/test/fake-mcp-server.js.map +1 -0
  313. package/dist/test/harness.d.ts +246 -0
  314. package/dist/test/harness.js +704 -0
  315. package/dist/test/harness.js.map +1 -0
  316. package/dist/test/replay.d.ts +14 -0
  317. package/dist/test/replay.js +75 -0
  318. package/dist/test/replay.js.map +1 -0
  319. package/dist/thread-reducer.d.ts +172 -0
  320. package/dist/thread-reducer.fixtures.test.d.ts +1 -0
  321. package/dist/thread-reducer.fixtures.test.js +360 -0
  322. package/dist/thread-reducer.fixtures.test.js.map +1 -0
  323. package/dist/thread-reducer.js +497 -0
  324. package/dist/thread-reducer.js.map +1 -0
  325. package/dist/thread-reducer.test.d.ts +1 -0
  326. package/dist/thread-reducer.test.js +369 -0
  327. package/dist/thread-reducer.test.js.map +1 -0
  328. package/dist/tts.test.d.ts +1 -0
  329. package/dist/tts.test.js +147 -0
  330. package/dist/tts.test.js.map +1 -0
  331. package/dist/voice-agent.d.ts +33 -0
  332. package/dist/voice-agent.js +159 -0
  333. package/dist/voice-agent.js.map +1 -0
  334. package/dist/voice-attachments.e2e.test.d.ts +1 -0
  335. package/dist/voice-attachments.e2e.test.js +256 -0
  336. package/dist/voice-attachments.e2e.test.js.map +1 -0
  337. package/dist/voice-call.d.ts +59 -0
  338. package/dist/voice-call.e2e.test.d.ts +1 -0
  339. package/dist/voice-call.e2e.test.js +259 -0
  340. package/dist/voice-call.e2e.test.js.map +1 -0
  341. package/dist/voice-call.js +760 -0
  342. package/dist/voice-call.js.map +1 -0
  343. package/dist/voice-openai.e2e.test.d.ts +1 -0
  344. package/dist/voice-openai.e2e.test.js +161 -0
  345. package/dist/voice-openai.e2e.test.js.map +1 -0
  346. package/dist/voice.d.ts +116 -0
  347. package/dist/voice.js +619 -717
  348. package/dist/voice.js.map +1 -0
  349. package/dist/working-directory.e2e.test.d.ts +1 -0
  350. package/dist/working-directory.e2e.test.js +231 -0
  351. package/dist/working-directory.e2e.test.js.map +1 -0
  352. package/dist/worktrees.d.ts +54 -0
  353. package/dist/worktrees.js +247 -709
  354. package/dist/worktrees.js.map +1 -0
  355. package/dist/worktrees.test.d.ts +1 -0
  356. package/dist/worktrees.test.js +97 -952
  357. package/dist/worktrees.test.js.map +1 -0
  358. package/package.json +38 -73
  359. package/skills/kimaki-debug/SKILL.md +44 -0
  360. package/skills/kimaki-tunnel/SKILL.md +34 -0
  361. package/src/agent-ui.e2e.test.ts +134 -0
  362. package/src/agent-ui.ts +216 -0
  363. package/src/analytics.e2e.test.ts +138 -0
  364. package/src/analytics.test.ts +122 -182
  365. package/src/analytics.ts +206 -258
  366. package/src/autostart.ts +90 -0
  367. package/src/bot.ts +196 -0
  368. package/src/btw.e2e.test.ts +130 -0
  369. package/src/builtin-skills.test.ts +29 -0
  370. package/src/builtin-skills.ts +59 -0
  371. package/src/catalog-refresh.e2e.test.ts +112 -0
  372. package/src/cli/bot.ts +425 -0
  373. package/src/cli/discord.ts +116 -0
  374. package/src/cli/project.ts +147 -0
  375. package/src/cli/schedule.ts +151 -0
  376. package/src/cli/session.ts +252 -0
  377. package/src/cli/shared.ts +139 -0
  378. package/src/cli-discord.e2e.test.ts +79 -0
  379. package/src/cli-lock.e2e.test.ts +530 -0
  380. package/src/cli-read.e2e.test.ts +83 -0
  381. package/src/cli-sessions.e2e.test.ts +120 -0
  382. package/src/cli.ts +35 -418
  383. package/src/commands/login-commands.ts +360 -0
  384. package/src/commands/preference-commands.ts +599 -0
  385. package/src/commands/session-commands.ts +289 -0
  386. package/src/commands/thread-commands.ts +324 -0
  387. package/src/commands/worktree-commands.ts +288 -0
  388. package/src/commands.e2e.test.ts +290 -0
  389. package/src/credentials.ts +425 -0
  390. package/src/db.test.ts +344 -759
  391. package/src/db.ts +66 -284
  392. package/src/duration.ts +18 -0
  393. package/src/effects.ts +337 -0
  394. package/src/errors.ts +39 -228
  395. package/src/event-loop.ts +471 -0
  396. package/src/file-edit-log.test.ts +68 -272
  397. package/src/file-edit-log.ts +108 -231
  398. package/src/fixtures/v1-schema.sql +241 -0
  399. package/src/fixtures/voice.ogg +0 -0
  400. package/src/format-parts.test.ts +150 -0
  401. package/src/format-parts.ts +405 -0
  402. package/src/gateway-replay.e2e.test.ts +116 -0
  403. package/src/harness.e2e.test.ts +124 -0
  404. package/src/hello-thread.e2e.test.ts +223 -0
  405. package/src/ingress.ts +334 -0
  406. package/src/interaction-context.ts +131 -0
  407. package/src/interrupt.e2e.test.ts +151 -0
  408. package/src/legacy.e2e.test.ts +105 -0
  409. package/src/live-call.ts +249 -0
  410. package/src/lock-routes.ts +625 -0
  411. package/src/lock-server.ts +233 -0
  412. package/src/logger.ts +120 -207
  413. package/src/login.e2e.test.ts +86 -0
  414. package/src/main.ts +238 -0
  415. package/src/markdown/components.ts +102 -0
  416. package/src/markdown/markdown.test.ts +414 -0
  417. package/src/markdown/render-markdown.ts +288 -0
  418. package/src/migrations.ts +198 -0
  419. package/src/onboarding-cli.e2e.test.ts +215 -0
  420. package/src/onboarding.e2e.test.ts +171 -0
  421. package/src/onboarding.ts +297 -0
  422. package/src/opencode-server.test.ts +24 -0
  423. package/src/opencode-server.ts +464 -0
  424. package/src/permissions.e2e.test.ts +191 -0
  425. package/src/permissions.ts +121 -0
  426. package/src/plugin/anthropic/index.test.ts +39 -0
  427. package/src/plugin/anthropic/index.ts +213 -0
  428. package/src/plugin/index.ts +112 -0
  429. package/src/plugin.e2e.test.ts +117 -0
  430. package/src/preference-commands.e2e.test.ts +597 -0
  431. package/src/project.ts +216 -0
  432. package/src/prompt.ts +664 -0
  433. package/src/questions.e2e.test.ts +328 -0
  434. package/src/questions.ts +322 -0
  435. package/src/queue.e2e.test.ts +253 -0
  436. package/src/queue.ts +158 -0
  437. package/src/remote-send.e2e.test.ts +91 -0
  438. package/src/remote-send.ts +110 -0
  439. package/src/renderer.e2e.test.ts +390 -0
  440. package/src/restart.e2e.test.ts +90 -0
  441. package/src/routes.test.ts +81 -0
  442. package/src/routes.ts +48 -0
  443. package/src/routines.e2e.test.ts +210 -0
  444. package/src/scheduler.e2e.test.ts +182 -0
  445. package/src/scheduler.test.ts +183 -0
  446. package/src/scheduler.ts +648 -0
  447. package/src/schema-sql.ts +3 -0
  448. package/src/schema.ts +95 -271
  449. package/src/session-commands.e2e.test.ts +326 -0
  450. package/src/session-events.ts +301 -0
  451. package/src/session-ops.e2e.test.ts +141 -0
  452. package/src/sessions.ts +804 -0
  453. package/src/shell.e2e.test.ts +173 -0
  454. package/src/slash-commands.test.ts +89 -0
  455. package/src/slash-commands.ts +540 -0
  456. package/src/sleep.e2e.test.ts +135 -552
  457. package/src/sleeps.ts +230 -0
  458. package/src/store.ts +19 -217
  459. package/src/system-prompt.ts +923 -0
  460. package/src/test/auth-plugin/index.ts +17 -0
  461. package/src/test/fake-mcp-server.ts +49 -0
  462. package/src/test/harness.ts +957 -0
  463. package/src/test/replay.ts +90 -0
  464. package/src/thread-reducer.fixtures.test.ts +374 -0
  465. package/src/thread-reducer.test.ts +380 -0
  466. package/src/thread-reducer.ts +671 -0
  467. package/src/tts.test.ts +150 -0
  468. package/src/voice-agent.ts +196 -0
  469. package/src/voice-attachments.e2e.test.ts +301 -0
  470. package/src/voice-call.e2e.test.ts +293 -0
  471. package/src/voice-call.ts +764 -0
  472. package/src/voice-openai.e2e.test.ts +191 -0
  473. package/src/voice.ts +668 -988
  474. package/src/working-directory.e2e.test.ts +226 -0
  475. package/src/worktrees.test.ts +89 -1115
  476. package/src/worktrees.ts +195 -957
  477. package/bin.js +0 -2
  478. package/dist/agent-model.e2e.test.js +0 -1223
  479. package/dist/ai-tool-to-genai.js +0 -233
  480. package/dist/ai-tool-to-genai.test.js +0 -267
  481. package/dist/ai-tool.js +0 -6
  482. package/dist/anthropic-account-identity.js +0 -70
  483. package/dist/anthropic-account-identity.test.js +0 -38
  484. package/dist/anthropic-auth-plugin.js +0 -958
  485. package/dist/anthropic-auth-plugin.test.js +0 -115
  486. package/dist/anthropic-auth-state.js +0 -186
  487. package/dist/anthropic-auth-state.test.js +0 -203
  488. package/dist/bash-tool-schema-plugin.js +0 -50
  489. package/dist/bash-tool-schema-plugin.test.js +0 -68
  490. package/dist/bin.js +0 -151
  491. package/dist/bot-lifecycle.test.js +0 -206
  492. package/dist/btw-prefix-detection.js +0 -24
  493. package/dist/btw-prefix-detection.test.js +0 -130
  494. package/dist/cache-drift-plugin.js +0 -183
  495. package/dist/cache-rewrite.js +0 -39
  496. package/dist/cache-rewrite.test.js +0 -41
  497. package/dist/channel-management.js +0 -347
  498. package/dist/cli-commands/bot.js +0 -254
  499. package/dist/cli-commands/maintenance.js +0 -171
  500. package/dist/cli-commands/misc.js +0 -153
  501. package/dist/cli-commands/project.js +0 -510
  502. package/dist/cli-commands/send.js +0 -659
  503. package/dist/cli-commands/session.js +0 -889
  504. package/dist/cli-commands/task.js +0 -277
  505. package/dist/cli-commands/thread.js +0 -63
  506. package/dist/cli-commands/user.js +0 -145
  507. package/dist/cli-parsing.test.js +0 -226
  508. package/dist/cli-runner.js +0 -1591
  509. package/dist/cli-runner.test.js +0 -119
  510. package/dist/cli-send-thread.e2e.test.js +0 -470
  511. package/dist/cloudflare-transcription.js +0 -56
  512. package/dist/commands/abort.js +0 -84
  513. package/dist/commands/action-buttons.js +0 -248
  514. package/dist/commands/add-project.js +0 -111
  515. package/dist/commands/agent.js +0 -658
  516. package/dist/commands/ask-question.js +0 -371
  517. package/dist/commands/ask-question.test.js +0 -150
  518. package/dist/commands/btw.js +0 -275
  519. package/dist/commands/compact.js +0 -117
  520. package/dist/commands/context-usage.js +0 -194
  521. package/dist/commands/context-usage.test.js +0 -49
  522. package/dist/commands/create-new-project.js +0 -138
  523. package/dist/commands/diff.js +0 -63
  524. package/dist/commands/file-upload.js +0 -280
  525. package/dist/commands/fork-subagent.js +0 -175
  526. package/dist/commands/fork.js +0 -291
  527. package/dist/commands/gemini-apikey.js +0 -123
  528. package/dist/commands/last-sessions.js +0 -129
  529. package/dist/commands/login.js +0 -893
  530. package/dist/commands/mcp.js +0 -239
  531. package/dist/commands/memory-snapshot.js +0 -24
  532. package/dist/commands/mention-mode.js +0 -42
  533. package/dist/commands/merge-worktree.js +0 -205
  534. package/dist/commands/model-variant.js +0 -390
  535. package/dist/commands/model.js +0 -1012
  536. package/dist/commands/multioauth.js +0 -425
  537. package/dist/commands/new-worktree.js +0 -625
  538. package/dist/commands/paginated-select.js +0 -57
  539. package/dist/commands/permissions.js +0 -327
  540. package/dist/commands/queue.js +0 -347
  541. package/dist/commands/queue.test.js +0 -15
  542. package/dist/commands/remove-project.js +0 -111
  543. package/dist/commands/restart-opencode-server.js +0 -133
  544. package/dist/commands/resume.js +0 -148
  545. package/dist/commands/run-command.js +0 -79
  546. package/dist/commands/screenshare.js +0 -307
  547. package/dist/commands/screenshare.test.js +0 -20
  548. package/dist/commands/session-id.js +0 -78
  549. package/dist/commands/session.js +0 -248
  550. package/dist/commands/share.js +0 -80
  551. package/dist/commands/tasks.js +0 -296
  552. package/dist/commands/types.js +0 -2
  553. package/dist/commands/undo-redo.js +0 -313
  554. package/dist/commands/unset-model.js +0 -116
  555. package/dist/commands/upgrade.js +0 -41
  556. package/dist/commands/user-command.js +0 -196
  557. package/dist/commands/verbosity.js +0 -124
  558. package/dist/commands/vscode.js +0 -295
  559. package/dist/commands/worktrees.js +0 -548
  560. package/dist/condense-memory.js +0 -33
  561. package/dist/config.js +0 -126
  562. package/dist/context-awareness-plugin.js +0 -357
  563. package/dist/context-awareness-plugin.test.js +0 -126
  564. package/dist/cpu-profiler.js +0 -211
  565. package/dist/cpu-profiler.test.js +0 -160
  566. package/dist/critique-utils.js +0 -95
  567. package/dist/database.js +0 -1074
  568. package/dist/debounce-timeout.js +0 -28
  569. package/dist/debounced-process-flush.js +0 -77
  570. package/dist/default-channel-provisioning.test.js +0 -149
  571. package/dist/discord-bot.js +0 -1432
  572. package/dist/discord-command-registration.js +0 -519
  573. package/dist/discord-command-registration.test.js +0 -17
  574. package/dist/discord-urls.js +0 -81
  575. package/dist/discord-utils.js +0 -789
  576. package/dist/discord-utils.test.js +0 -400
  577. package/dist/escape-backticks.test.js +0 -429
  578. package/dist/event-stream-real-capture.e2e.test.js +0 -533
  579. package/dist/eventsource-parser.test.js +0 -327
  580. package/dist/exec-async.js +0 -38
  581. package/dist/external-directory-permissions.e2e.test.js +0 -86
  582. package/dist/external-directory-project-deny.e2e.test.js +0 -70
  583. package/dist/external-opencode-sync.js +0 -620
  584. package/dist/external-opencode-sync.test.js +0 -206
  585. package/dist/extract-sdk-error-message.test.js +0 -13
  586. package/dist/format-tables.js +0 -728
  587. package/dist/format-tables.test.js +0 -915
  588. package/dist/forum-sync/config.js +0 -79
  589. package/dist/forum-sync/discord-operations.js +0 -157
  590. package/dist/forum-sync/index.js +0 -5
  591. package/dist/forum-sync/markdown.js +0 -110
  592. package/dist/forum-sync/sync-to-discord.js +0 -417
  593. package/dist/forum-sync/sync-to-files.js +0 -190
  594. package/dist/forum-sync/types.js +0 -53
  595. package/dist/forum-sync/watchers.js +0 -322
  596. package/dist/gateway-proxy-reconnect.e2e.test.js +0 -414
  597. package/dist/gateway-proxy.e2e.test.js +0 -488
  598. package/dist/genai-worker-wrapper.js +0 -111
  599. package/dist/genai-worker.js +0 -309
  600. package/dist/genai.js +0 -232
  601. package/dist/git-worktree-core.js +0 -461
  602. package/dist/guild-categories.test.js +0 -225
  603. package/dist/heap-monitor.js +0 -122
  604. package/dist/hrana-server.js +0 -322
  605. package/dist/hrana-server.test.js +0 -99
  606. package/dist/html-actions.js +0 -123
  607. package/dist/html-actions.test.js +0 -70
  608. package/dist/html-components.js +0 -115
  609. package/dist/html-components.test.js +0 -34
  610. package/dist/image-optimizer-plugin.js +0 -153
  611. package/dist/image-utils.js +0 -112
  612. package/dist/interaction-handler.js +0 -578
  613. package/dist/ipc-polling.js +0 -245
  614. package/dist/ipc-tools-plugin.js +0 -251
  615. package/dist/kimaki-digital-twin.e2e.test.js +0 -161
  616. package/dist/kimaki-opencode-plugin-loading.e2e.test.js +0 -158
  617. package/dist/kimaki-opencode-plugin.js +0 -33
  618. package/dist/kimaki-opencode-plugin.test.js +0 -126
  619. package/dist/kimaki-workspace-adaptor.js +0 -118
  620. package/dist/limit-heading-depth.js +0 -25
  621. package/dist/limit-heading-depth.test.js +0 -105
  622. package/dist/list-channel-threads.js +0 -163
  623. package/dist/list-channel-threads.test.js +0 -80
  624. package/dist/markdown.js +0 -455
  625. package/dist/markdown.test.js +0 -411
  626. package/dist/memory-overview-plugin.js +0 -123
  627. package/dist/message-formatting.js +0 -788
  628. package/dist/message-formatting.test.js +0 -1132
  629. package/dist/message-preprocessing.js +0 -419
  630. package/dist/oauth-rotation-shared.js +0 -253
  631. package/dist/onboarding-tutorial.js +0 -163
  632. package/dist/onboarding-welcome.js +0 -37
  633. package/dist/openai-auth-plugin.js +0 -125
  634. package/dist/openai-auth-state.js +0 -237
  635. package/dist/openai-realtime.js +0 -224
  636. package/dist/opencode-command-detection.js +0 -65
  637. package/dist/opencode-command-detection.test.js +0 -240
  638. package/dist/opencode-command.js +0 -181
  639. package/dist/opencode-command.test.js +0 -218
  640. package/dist/opencode-interrupt-plugin.js +0 -253
  641. package/dist/opencode-interrupt-plugin.test.js +0 -293
  642. package/dist/opencode.js +0 -1217
  643. package/dist/parse-permission-rules.test.js +0 -117
  644. package/dist/patch-text-parser.js +0 -97
  645. package/dist/plugin-logger.js +0 -105
  646. package/dist/plugin-opencode-client.js +0 -54
  647. package/dist/privacy-sanitizer.js +0 -105
  648. package/dist/project-add.test.js +0 -62
  649. package/dist/question-abort-clears-dropdown.e2e.test.js +0 -89
  650. package/dist/question-abort-resume.e2e.test.js +0 -100
  651. package/dist/question-abort-retry-text-before-dropdown.e2e.test.js +0 -150
  652. package/dist/question-text-before-dropdown.e2e.test.js +0 -52
  653. package/dist/queue-advanced-abort.e2e.test.js +0 -348
  654. package/dist/queue-advanced-action-buttons.e2e.test.js +0 -209
  655. package/dist/queue-advanced-e2e-setup.js +0 -1043
  656. package/dist/queue-advanced-footer.e2e.test.js +0 -286
  657. package/dist/queue-advanced-model-switch.e2e.test.js +0 -297
  658. package/dist/queue-advanced-permissions-typing.e2e.test.js +0 -189
  659. package/dist/queue-advanced-question.e2e.test.js +0 -266
  660. package/dist/queue-advanced-typing-interrupt.e2e.test.js +0 -113
  661. package/dist/queue-advanced-typing.e2e.test.js +0 -151
  662. package/dist/queue-delete-message.e2e.test.js +0 -216
  663. package/dist/queue-drain-after-interactive-ui.e2e.test.js +0 -120
  664. package/dist/queue-interrupt-drain.e2e.test.js +0 -256
  665. package/dist/queue-question-select-drain.e2e.test.js +0 -162
  666. package/dist/runtime-idle-sweeper.js +0 -52
  667. package/dist/runtime-lifecycle.e2e.test.js +0 -570
  668. package/dist/sentry.js +0 -23
  669. package/dist/session-handler/agent-utils.js +0 -65
  670. package/dist/session-handler/event-stream-state.js +0 -1225
  671. package/dist/session-handler/event-stream-state.test.js +0 -3160
  672. package/dist/session-handler/global-event-listener.js +0 -212
  673. package/dist/session-handler/model-utils.js +0 -311
  674. package/dist/session-handler/model-utils.test.js +0 -445
  675. package/dist/session-handler/opencode-session-event-log.js +0 -121
  676. package/dist/session-handler/thread-ingress.test.js +0 -41
  677. package/dist/session-handler/thread-runtime-state.js +0 -209
  678. package/dist/session-handler/thread-session-runtime-summary.test.js +0 -121
  679. package/dist/session-handler/thread-session-runtime.js +0 -4320
  680. package/dist/session-handler.js +0 -9
  681. package/dist/session-search.js +0 -219
  682. package/dist/session-search.test.js +0 -287
  683. package/dist/session-sleep.test.js +0 -224
  684. package/dist/session-title-rename.test.js +0 -128
  685. package/dist/skill-filter.js +0 -43
  686. package/dist/skill-filter.test.js +0 -86
  687. package/dist/startup-service.js +0 -153
  688. package/dist/startup-time.e2e.test.js +0 -297
  689. package/dist/system-message.js +0 -806
  690. package/dist/system-message.test.js +0 -917
  691. package/dist/task-id-plugin.js +0 -16
  692. package/dist/task-runner.js +0 -554
  693. package/dist/task-schedule.js +0 -298
  694. package/dist/task-schedule.test.js +0 -366
  695. package/dist/test-utils.js +0 -385
  696. package/dist/thinking-utils.js +0 -45
  697. package/dist/thinking-utils.test.js +0 -35
  698. package/dist/thread-message-queue.e2e.test.js +0 -1227
  699. package/dist/tools.js +0 -357
  700. package/dist/undo-redo.e2e.test.js +0 -171
  701. package/dist/unnest-code-blocks.js +0 -146
  702. package/dist/unnest-code-blocks.test.js +0 -673
  703. package/dist/upgrade.js +0 -114
  704. package/dist/utils.js +0 -148
  705. package/dist/voice-attachment.js +0 -56
  706. package/dist/voice-handler.js +0 -727
  707. package/dist/voice-message.e2e.test.js +0 -1254
  708. package/dist/voice-routing-input.e2e.test.js +0 -195
  709. package/dist/voice-routing-question.e2e.test.js +0 -182
  710. package/dist/voice.test.js +0 -500
  711. package/dist/wait-session.js +0 -193
  712. package/dist/websockify.js +0 -69
  713. package/dist/worker-types.js +0 -4
  714. package/dist/worktree-clone-isolation.e2e.test.js +0 -188
  715. package/dist/worktree-lifecycle.e2e.test.js +0 -735
  716. package/dist/worktree-utils.js +0 -3
  717. package/dist/xai-auth-plugin.js +0 -238
  718. package/dist/xai-auth-state.js +0 -234
  719. package/dist/xai-auth-state.test.js +0 -183
  720. package/dist/xml.js +0 -92
  721. package/dist/xml.test.js +0 -32
  722. package/skills/bump-holocron-consumers/SKILL.md +0 -137
  723. package/skills/critique/SKILL.md +0 -116
  724. package/skills/egaki/SKILL.md +0 -86
  725. package/skills/errore/SKILL.md +0 -501
  726. package/skills/goke/SKILL.md +0 -695
  727. package/skills/holocron/SKILL.md +0 -942
  728. package/skills/lintcn/SKILL.md +0 -873
  729. package/skills/new-skill/SKILL.md +0 -339
  730. package/skills/npm-package/SKILL.md +0 -692
  731. package/skills/opensrc/SKILL.md +0 -102
  732. package/skills/playwriter/SKILL.md +0 -35
  733. package/skills/profano/SKILL.md +0 -33
  734. package/skills/readme.md +0 -20
  735. package/skills/sigillo/SKILL.md +0 -370
  736. package/skills/spiceflow/SKILL.md +0 -80
  737. package/skills/strada/SKILL.md +0 -104
  738. package/skills/termcast/SKILL.md +0 -133
  739. package/skills/tuistory/SKILL.md +0 -237
  740. package/skills/usecomputer/SKILL.md +0 -264
  741. package/skills/zele/SKILL.md +0 -81
  742. package/src/agent-model.e2e.test.ts +0 -1557
  743. package/src/ai-tool-to-genai.test.ts +0 -296
  744. package/src/ai-tool-to-genai.ts +0 -283
  745. package/src/ai-tool.ts +0 -39
  746. package/src/anthropic-account-identity.test.ts +0 -52
  747. package/src/anthropic-account-identity.ts +0 -96
  748. package/src/anthropic-auth-plugin.test.ts +0 -135
  749. package/src/anthropic-auth-plugin.ts +0 -1180
  750. package/src/anthropic-auth-state.test.ts +0 -259
  751. package/src/anthropic-auth-state.ts +0 -272
  752. package/src/bash-tool-schema-plugin.test.ts +0 -84
  753. package/src/bash-tool-schema-plugin.ts +0 -77
  754. package/src/bin.ts +0 -173
  755. package/src/bot-lifecycle.test.ts +0 -252
  756. package/src/btw-prefix-detection.test.ts +0 -142
  757. package/src/btw-prefix-detection.ts +0 -30
  758. package/src/cache-drift-plugin.ts +0 -249
  759. package/src/cache-rewrite.test.ts +0 -49
  760. package/src/cache-rewrite.ts +0 -96
  761. package/src/channel-management.ts +0 -494
  762. package/src/cli-commands/bot.ts +0 -374
  763. package/src/cli-commands/maintenance.ts +0 -237
  764. package/src/cli-commands/misc.ts +0 -208
  765. package/src/cli-commands/project.ts +0 -680
  766. package/src/cli-commands/send.ts +0 -885
  767. package/src/cli-commands/session.ts +0 -1181
  768. package/src/cli-commands/task.ts +0 -358
  769. package/src/cli-commands/thread.ts +0 -86
  770. package/src/cli-commands/user.ts +0 -195
  771. package/src/cli-parsing.test.ts +0 -305
  772. package/src/cli-runner.test.ts +0 -166
  773. package/src/cli-runner.ts +0 -2157
  774. package/src/cli-send-thread.e2e.test.ts +0 -609
  775. package/src/cloudflare-transcription.ts +0 -76
  776. package/src/commands/abort.ts +0 -113
  777. package/src/commands/action-buttons.ts +0 -371
  778. package/src/commands/add-project.ts +0 -146
  779. package/src/commands/agent.ts +0 -911
  780. package/src/commands/ask-question.test.ts +0 -177
  781. package/src/commands/ask-question.ts +0 -532
  782. package/src/commands/btw.ts +0 -356
  783. package/src/commands/compact.ts +0 -154
  784. package/src/commands/context-usage.test.ts +0 -57
  785. package/src/commands/context-usage.ts +0 -267
  786. package/src/commands/create-new-project.ts +0 -203
  787. package/src/commands/diff.ts +0 -91
  788. package/src/commands/file-upload.ts +0 -398
  789. package/src/commands/fork-subagent.ts +0 -262
  790. package/src/commands/fork.ts +0 -421
  791. package/src/commands/gemini-apikey.ts +0 -205
  792. package/src/commands/last-sessions.ts +0 -176
  793. package/src/commands/login.ts +0 -1182
  794. package/src/commands/mcp.ts +0 -307
  795. package/src/commands/memory-snapshot.ts +0 -30
  796. package/src/commands/mention-mode.ts +0 -66
  797. package/src/commands/merge-worktree.ts +0 -282
  798. package/src/commands/model-variant.ts +0 -522
  799. package/src/commands/model.ts +0 -1377
  800. package/src/commands/multioauth.ts +0 -508
  801. package/src/commands/new-worktree.ts +0 -835
  802. package/src/commands/paginated-select.ts +0 -81
  803. package/src/commands/permissions.ts +0 -458
  804. package/src/commands/queue.test.ts +0 -19
  805. package/src/commands/queue.ts +0 -471
  806. package/src/commands/remove-project.ts +0 -152
  807. package/src/commands/restart-opencode-server.ts +0 -173
  808. package/src/commands/resume.ts +0 -229
  809. package/src/commands/run-command.ts +0 -123
  810. package/src/commands/screenshare.test.ts +0 -30
  811. package/src/commands/screenshare.ts +0 -388
  812. package/src/commands/session-id.ts +0 -109
  813. package/src/commands/session.ts +0 -319
  814. package/src/commands/share.ts +0 -106
  815. package/src/commands/tasks.ts +0 -399
  816. package/src/commands/types.ts +0 -25
  817. package/src/commands/undo-redo.ts +0 -394
  818. package/src/commands/unset-model.ts +0 -152
  819. package/src/commands/upgrade.ts +0 -51
  820. package/src/commands/user-command.ts +0 -260
  821. package/src/commands/verbosity.ts +0 -172
  822. package/src/commands/vscode.ts +0 -378
  823. package/src/commands/worktrees.ts +0 -762
  824. package/src/condense-memory.ts +0 -36
  825. package/src/config.ts +0 -147
  826. package/src/context-awareness-plugin.test.ts +0 -144
  827. package/src/context-awareness-plugin.ts +0 -508
  828. package/src/cpu-profiler.test.ts +0 -183
  829. package/src/cpu-profiler.ts +0 -293
  830. package/src/critique-utils.ts +0 -139
  831. package/src/database.ts +0 -1484
  832. package/src/debounce-timeout.ts +0 -43
  833. package/src/debounced-process-flush.ts +0 -104
  834. package/src/default-channel-provisioning.test.ts +0 -156
  835. package/src/discord-bot.ts +0 -1852
  836. package/src/discord-command-registration.test.ts +0 -20
  837. package/src/discord-command-registration.ts +0 -704
  838. package/src/discord-urls.ts +0 -88
  839. package/src/discord-utils.test.ts +0 -470
  840. package/src/discord-utils.ts +0 -1079
  841. package/src/escape-backticks.test.ts +0 -469
  842. package/src/event-stream-real-capture.e2e.test.ts +0 -692
  843. package/src/eventsource-parser.test.ts +0 -351
  844. package/src/exec-async.ts +0 -44
  845. package/src/external-directory-permissions.e2e.test.ts +0 -103
  846. package/src/external-directory-project-deny.e2e.test.ts +0 -84
  847. package/src/external-opencode-sync.test.ts +0 -248
  848. package/src/external-opencode-sync.ts +0 -867
  849. package/src/extract-sdk-error-message.test.ts +0 -18
  850. package/src/format-tables.test.ts +0 -988
  851. package/src/format-tables.ts +0 -1031
  852. package/src/forum-sync/config.ts +0 -92
  853. package/src/forum-sync/discord-operations.ts +0 -245
  854. package/src/forum-sync/index.ts +0 -9
  855. package/src/forum-sync/markdown.ts +0 -172
  856. package/src/forum-sync/sync-to-discord.ts +0 -595
  857. package/src/forum-sync/sync-to-files.ts +0 -294
  858. package/src/forum-sync/types.ts +0 -175
  859. package/src/forum-sync/watchers.ts +0 -474
  860. package/src/gateway-proxy-reconnect.e2e.test.ts +0 -553
  861. package/src/gateway-proxy.e2e.test.ts +0 -648
  862. package/src/genai-worker-wrapper.ts +0 -164
  863. package/src/genai-worker.ts +0 -385
  864. package/src/genai.ts +0 -318
  865. package/src/git-worktree-core.ts +0 -616
  866. package/src/guild-categories.test.ts +0 -260
  867. package/src/heap-monitor.ts +0 -152
  868. package/src/hrana-server.test.ts +0 -130
  869. package/src/hrana-server.ts +0 -399
  870. package/src/html-actions.test.ts +0 -87
  871. package/src/html-actions.ts +0 -174
  872. package/src/html-components.test.ts +0 -38
  873. package/src/html-components.ts +0 -177
  874. package/src/image-optimizer-plugin.ts +0 -194
  875. package/src/image-utils.ts +0 -150
  876. package/src/interaction-handler.ts +0 -775
  877. package/src/ipc-polling.ts +0 -325
  878. package/src/ipc-tools-plugin.ts +0 -306
  879. package/src/kimaki-digital-twin.e2e.test.ts +0 -199
  880. package/src/kimaki-opencode-plugin-loading.e2e.test.ts +0 -208
  881. package/src/kimaki-opencode-plugin.test.ts +0 -142
  882. package/src/kimaki-opencode-plugin.ts +0 -34
  883. package/src/kimaki-workspace-adaptor.ts +0 -144
  884. package/src/limit-heading-depth.test.ts +0 -116
  885. package/src/limit-heading-depth.ts +0 -26
  886. package/src/list-channel-threads.test.ts +0 -90
  887. package/src/list-channel-threads.ts +0 -252
  888. package/src/markdown.test.ts +0 -498
  889. package/src/markdown.ts +0 -577
  890. package/src/memory-overview-plugin.ts +0 -159
  891. package/src/message-formatting.test.ts +0 -1401
  892. package/src/message-formatting.ts +0 -999
  893. package/src/message-preprocessing.ts +0 -557
  894. package/src/oauth-rotation-shared.ts +0 -332
  895. package/src/onboarding-tutorial.ts +0 -167
  896. package/src/onboarding-welcome.ts +0 -49
  897. package/src/openai-auth-plugin.ts +0 -158
  898. package/src/openai-auth-state.ts +0 -297
  899. package/src/openai-realtime.ts +0 -358
  900. package/src/opencode-command-detection.test.ts +0 -307
  901. package/src/opencode-command-detection.ts +0 -76
  902. package/src/opencode-command.test.ts +0 -290
  903. package/src/opencode-command.ts +0 -251
  904. package/src/opencode-interrupt-plugin.test.ts +0 -419
  905. package/src/opencode-interrupt-plugin.ts +0 -306
  906. package/src/opencode.ts +0 -1590
  907. package/src/parse-permission-rules.test.ts +0 -127
  908. package/src/patch-text-parser.ts +0 -107
  909. package/src/plugin-logger.ts +0 -139
  910. package/src/plugin-opencode-client.ts +0 -71
  911. package/src/privacy-sanitizer.ts +0 -142
  912. package/src/project-add.test.ts +0 -80
  913. package/src/question-abort-clears-dropdown.e2e.test.ts +0 -109
  914. package/src/question-abort-resume.e2e.test.ts +0 -121
  915. package/src/question-abort-retry-text-before-dropdown.e2e.test.ts +0 -172
  916. package/src/question-text-before-dropdown.e2e.test.ts +0 -67
  917. package/src/queue-advanced-abort.e2e.test.ts +0 -449
  918. package/src/queue-advanced-action-buttons.e2e.test.ts +0 -271
  919. package/src/queue-advanced-e2e-setup.ts +0 -1164
  920. package/src/queue-advanced-footer.e2e.test.ts +0 -357
  921. package/src/queue-advanced-model-switch.e2e.test.ts +0 -382
  922. package/src/queue-advanced-permissions-typing.e2e.test.ts +0 -256
  923. package/src/queue-advanced-question.e2e.test.ts +0 -322
  924. package/src/queue-advanced-typing-interrupt.e2e.test.ts +0 -147
  925. package/src/queue-advanced-typing.e2e.test.ts +0 -199
  926. package/src/queue-delete-message.e2e.test.ts +0 -262
  927. package/src/queue-drain-after-interactive-ui.e2e.test.ts +0 -152
  928. package/src/queue-interrupt-drain.e2e.test.ts +0 -299
  929. package/src/queue-question-select-drain.e2e.test.ts +0 -209
  930. package/src/runtime-idle-sweeper.ts +0 -76
  931. package/src/runtime-lifecycle.e2e.test.ts +0 -714
  932. package/src/schema.sql +0 -251
  933. package/src/sentry.ts +0 -26
  934. package/src/session-handler/agent-utils.ts +0 -104
  935. package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +0 -45
  936. package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +0 -40
  937. package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +0 -23
  938. package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +0 -22
  939. package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +0 -277
  940. package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +0 -46
  941. package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +0 -21
  942. package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +0 -56
  943. package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +0 -44
  944. package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +0 -29
  945. package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +0 -29
  946. package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +0 -50
  947. package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +0 -59
  948. package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +0 -52
  949. package/src/session-handler/event-stream-state.test.ts +0 -3431
  950. package/src/session-handler/event-stream-state.ts +0 -1792
  951. package/src/session-handler/global-event-listener.ts +0 -258
  952. package/src/session-handler/model-utils.test.ts +0 -537
  953. package/src/session-handler/model-utils.ts +0 -489
  954. package/src/session-handler/opencode-session-event-log.ts +0 -159
  955. package/src/session-handler/thread-ingress.test.ts +0 -54
  956. package/src/session-handler/thread-runtime-state.ts +0 -358
  957. package/src/session-handler/thread-session-runtime-summary.test.ts +0 -169
  958. package/src/session-handler/thread-session-runtime.ts +0 -5696
  959. package/src/session-handler.ts +0 -15
  960. package/src/session-search.test.ts +0 -331
  961. package/src/session-search.ts +0 -382
  962. package/src/session-sleep.test.ts +0 -294
  963. package/src/session-title-rename.test.ts +0 -186
  964. package/src/skill-filter.test.ts +0 -110
  965. package/src/skill-filter.ts +0 -63
  966. package/src/startup-service.ts +0 -200
  967. package/src/startup-time.e2e.test.ts +0 -377
  968. package/src/system-message.test.ts +0 -1013
  969. package/src/system-message.ts +0 -1043
  970. package/src/task-id-plugin.ts +0 -17
  971. package/src/task-runner.ts +0 -768
  972. package/src/task-schedule.test.ts +0 -424
  973. package/src/task-schedule.ts +0 -470
  974. package/src/test-utils.ts +0 -549
  975. package/src/thinking-utils.test.ts +0 -46
  976. package/src/thinking-utils.ts +0 -82
  977. package/src/thread-message-queue.e2e.test.ts +0 -1468
  978. package/src/tools.ts +0 -430
  979. package/src/undici.d.ts +0 -12
  980. package/src/undo-redo.e2e.test.ts +0 -219
  981. package/src/unnest-code-blocks.test.ts +0 -713
  982. package/src/unnest-code-blocks.ts +0 -185
  983. package/src/upgrade.ts +0 -127
  984. package/src/utils.ts +0 -216
  985. package/src/voice-attachment.ts +0 -79
  986. package/src/voice-handler.ts +0 -1000
  987. package/src/voice-message.e2e.test.ts +0 -1513
  988. package/src/voice-routing-input.e2e.test.ts +0 -203
  989. package/src/voice-routing-question.e2e.test.ts +0 -188
  990. package/src/voice.test.ts +0 -565
  991. package/src/wait-session.ts +0 -291
  992. package/src/websockify.ts +0 -101
  993. package/src/worker-types.ts +0 -64
  994. package/src/worktree-clone-isolation.e2e.test.ts +0 -213
  995. package/src/worktree-lifecycle.e2e.test.ts +0 -900
  996. package/src/worktree-utils.ts +0 -4
  997. package/src/xai-auth-plugin.ts +0 -303
  998. package/src/xai-auth-state.test.ts +0 -224
  999. package/src/xai-auth-state.ts +0 -294
  1000. package/src/xml.test.ts +0 -38
  1001. package/src/xml.ts +0 -121
@@ -1,1468 +0,0 @@
1
- // E2e tests for basic per-thread message queue ordering.
2
- // Advanced interrupt/abort/retry tests are in thread-queue-advanced.e2e.test.ts.
3
- //
4
- // Uses opencode-deterministic-provider which returns canned responses instantly
5
- // (no real LLM calls), so poll timeouts can be aggressive (4s). The only real
6
- // latency is OpenCode server startup (beforeAll) and intentional partDelaysMs
7
- // in matchers (100ms for user-reply).
8
- //
9
- // If total duration of a file exceeds ~10s, split into a new test file
10
- // so vitest can parallelize across files.
11
-
12
- import fs from 'node:fs'
13
- import path from 'node:path'
14
- import url from 'node:url'
15
- import { describe, beforeAll, afterAll, test, expect } from 'vitest'
16
- import { ChannelType, Client, GatewayIntentBits, Partials } from 'discord.js'
17
- import { DigitalDiscord } from 'discord-digital-twin/src'
18
- import {
19
- buildDeterministicOpencodeConfig,
20
- type DeterministicMatcher,
21
- } from 'opencode-deterministic-provider'
22
- import {
23
- setDataDir,
24
- } from './config.js'
25
- import { store } from './store.js'
26
- import { startDiscordBot } from './discord-bot.js'
27
- import {
28
- setBotToken,
29
- initDatabase,
30
- closeDatabase,
31
- setChannelDirectory,
32
- setChannelVerbosity,
33
- getChannelVerbosity,
34
- type VerbosityLevel,
35
- } from './database.js'
36
- import { startHranaServer, stopHranaServer } from './hrana-server.js'
37
- import { initializeOpencodeForDirectory, stopOpencodeServer } from './opencode.js'
38
- import {
39
- chooseLockPort,
40
- cleanupTestSessions,
41
- initTestGitRepo,
42
- waitForFooterMessage,
43
- waitForBotMessageContaining,
44
- waitForMessageById,
45
- waitForBotMessageCount,
46
- waitForBotReplyAfterUserMessage,
47
- waitForBotReplyTo,
48
- waitForThreadState,
49
- getMessageVisibleText,
50
- } from './test-utils.js'
51
-
52
-
53
- const e2eTest = describe
54
-
55
- function createRunDirectories() {
56
- const root = path.resolve(process.cwd(), 'tmp', 'thread-queue-e2e')
57
- fs.mkdirSync(root, { recursive: true })
58
-
59
- const dataDir = fs.mkdtempSync(path.join(root, 'data-'))
60
- const projectDirectory = path.join(root, 'project')
61
- fs.mkdirSync(projectDirectory, { recursive: true })
62
- initTestGitRepo(projectDirectory)
63
-
64
- return { root, dataDir, projectDirectory }
65
- }
66
-
67
- function createDiscordJsClient({ restUrl }: { restUrl: string }) {
68
- return new Client({
69
- intents: [
70
- GatewayIntentBits.Guilds,
71
- GatewayIntentBits.GuildMessages,
72
- GatewayIntentBits.MessageContent,
73
- GatewayIntentBits.GuildVoiceStates,
74
- ],
75
- partials: [
76
- Partials.Channel,
77
- Partials.Message,
78
- Partials.User,
79
- Partials.ThreadMember,
80
- ],
81
- rest: {
82
- api: restUrl,
83
- version: '10',
84
- },
85
- })
86
- }
87
-
88
- function createDeterministicMatchers() {
89
- const bashCreateFileMatcher: DeterministicMatcher = {
90
- id: 'bash-create-file',
91
- priority: 130,
92
- when: {
93
- lastMessageRole: 'user',
94
- rawPromptIncludes: 'BASH_TOOL_FILE_MARKER',
95
- },
96
- then: {
97
- parts: [
98
- { type: 'stream-start', warnings: [] },
99
- { type: 'text-start', id: 'bash-create-file' },
100
- {
101
- type: 'text-delta',
102
- id: 'bash-create-file',
103
- delta: 'running create file',
104
- },
105
- { type: 'text-end', id: 'bash-create-file' },
106
- { type: 'text-start', id: 'bash-create-file-status' },
107
- {
108
- type: 'text-delta',
109
- id: 'bash-create-file-status',
110
- delta: 'creating marker',
111
- },
112
- { type: 'text-end', id: 'bash-create-file-status' },
113
- {
114
- type: 'tool-call',
115
- toolCallId: 'bash-create-file-call',
116
- toolName: 'bash',
117
- input: JSON.stringify({
118
- command: 'mkdir -p tmp && printf "created" > tmp/bash-tool-executed.txt',
119
- description: 'Create marker file for e2e test',
120
- hasSideEffect: true,
121
- }),
122
- },
123
- {
124
- type: 'finish',
125
- finishReason: 'tool-calls',
126
- usage: {
127
- inputTokens: 1,
128
- outputTokens: 1,
129
- totalTokens: 2,
130
- },
131
- },
132
- ],
133
- },
134
- }
135
-
136
- const bashCreateFileFollowupMatcher: DeterministicMatcher = {
137
- id: 'bash-create-file-followup',
138
- priority: 120,
139
- when: {
140
- lastMessageRole: 'tool',
141
- rawPromptIncludes: 'BASH_TOOL_FILE_MARKER',
142
- },
143
- then: {
144
- parts: [
145
- { type: 'stream-start', warnings: [] },
146
- { type: 'text-start', id: 'bash-create-file-followup' },
147
- {
148
- type: 'text-delta',
149
- id: 'bash-create-file-followup',
150
- delta: 'file created',
151
- },
152
- { type: 'text-end', id: 'bash-create-file-followup' },
153
- {
154
- type: 'finish',
155
- finishReason: 'stop',
156
- usage: {
157
- inputTokens: 1,
158
- outputTokens: 1,
159
- totalTokens: 2,
160
- },
161
- },
162
- ],
163
- },
164
- }
165
-
166
- const raceFinalReplyMatcher: DeterministicMatcher = {
167
- id: 'race-final-reply',
168
- priority: 110,
169
- when: {
170
- latestUserTextIncludes: 'Reply with exactly: race-final',
171
- },
172
- then: {
173
- parts: [
174
- { type: 'stream-start', warnings: [] },
175
- { type: 'text-start', id: 'race-final' },
176
- { type: 'text-delta', id: 'race-final', delta: 'race-final' },
177
- { type: 'text-end', id: 'race-final' },
178
- {
179
- type: 'finish',
180
- finishReason: 'stop',
181
- usage: {
182
- inputTokens: 1,
183
- outputTokens: 1,
184
- totalTokens: 2,
185
- },
186
- },
187
- ],
188
- // Delay first output to widen the stale-idle window. The race happens
189
- // in <1ms; 500ms is plenty to keep the window reliably open.
190
- partDelaysMs: [0, 500, 0, 0, 0],
191
- },
192
- }
193
-
194
- // Slow matcher for "hotel" so the 200ms sleep in the queueing test
195
- // guarantees "india" arrives while hotel is still streaming.
196
- const hotelSlowMatcher: DeterministicMatcher = {
197
- id: 'hotel-slow-reply',
198
- priority: 20,
199
- when: {
200
- latestUserTextIncludes: 'Reply with exactly: hotel',
201
- },
202
- then: {
203
- parts: [
204
- { type: 'stream-start', warnings: [] },
205
- { type: 'text-start', id: 'hotel-reply' },
206
- { type: 'text-delta', id: 'hotel-reply', delta: 'ok' },
207
- { type: 'text-end', id: 'hotel-reply' },
208
- {
209
- type: 'finish',
210
- finishReason: 'stop',
211
- usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
212
- },
213
- ],
214
- partDelaysMs: [0, 100, 300, 0, 0],
215
- },
216
- }
217
-
218
- const userReplyMatcher: DeterministicMatcher = {
219
- id: 'user-reply',
220
- priority: 10,
221
- when: {
222
- lastMessageRole: 'user',
223
- rawPromptIncludes: 'Reply with exactly:',
224
- },
225
- then: {
226
- parts: [
227
- { type: 'stream-start', warnings: [] },
228
- { type: 'text-start', id: 'default-reply' },
229
- { type: 'text-delta', id: 'default-reply', delta: 'ok' },
230
- { type: 'text-end', id: 'default-reply' },
231
- {
232
- type: 'finish',
233
- finishReason: 'stop',
234
- usage: {
235
- inputTokens: 1,
236
- outputTokens: 1,
237
- totalTokens: 2,
238
- },
239
- },
240
- ],
241
- partDelaysMs: [0, 100, 0, 0, 0],
242
- },
243
- }
244
-
245
- // Matcher that keeps the session busy for 2s after the first text part.
246
- // Used by queue-edit tests to ensure the follow-up message lands in the
247
- // local queue before the session finishes.
248
- const slowBusyMatcher: DeterministicMatcher = {
249
- id: 'slow-busy-reply',
250
- priority: 115,
251
- when: {
252
- latestUserTextIncludes: 'SLOW_BUSY_MARKER',
253
- },
254
- then: {
255
- parts: [
256
- { type: 'stream-start', warnings: [] },
257
- { type: 'text-start', id: 'slow-busy' },
258
- { type: 'text-delta', id: 'slow-busy', delta: 'slow-busy-reply' },
259
- { type: 'text-end', id: 'slow-busy' },
260
- {
261
- type: 'finish',
262
- finishReason: 'stop',
263
- usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
264
- },
265
- ],
266
- partDelaysMs: [0, 100, 0, 0, 2_000],
267
- },
268
- }
269
-
270
- return [
271
- bashCreateFileMatcher,
272
- bashCreateFileFollowupMatcher,
273
- raceFinalReplyMatcher,
274
- slowBusyMatcher,
275
- hotelSlowMatcher,
276
- userReplyMatcher,
277
- ]
278
- }
279
-
280
- const TEST_USER_ID = '200000000000000777'
281
- const TEXT_CHANNEL_ID = '200000000000000778'
282
-
283
- e2eTest('thread message queue ordering', () => {
284
- let directories: ReturnType<typeof createRunDirectories>
285
- let discord: DigitalDiscord
286
- let botClient: Client
287
- let previousDefaultVerbosity: VerbosityLevel | null =
288
- null
289
- let testStartTime = Date.now()
290
-
291
- beforeAll(async () => {
292
- testStartTime = Date.now()
293
- directories = createRunDirectories()
294
- const lockPort = chooseLockPort({ key: TEXT_CHANNEL_ID })
295
-
296
- process.env['KIMAKI_LOCK_PORT'] = String(lockPort)
297
- setDataDir(directories.dataDir)
298
- previousDefaultVerbosity = store.getState().defaultVerbosity
299
- store.setState({ defaultVerbosity: 'tools_and_text' })
300
-
301
- const digitalDiscordDbPath = path.join(
302
- directories.dataDir,
303
- 'digital-discord.db',
304
- )
305
-
306
- discord = new DigitalDiscord({
307
- guild: {
308
- name: 'Queue E2E Guild',
309
- ownerId: TEST_USER_ID,
310
- },
311
- channels: [
312
- {
313
- id: TEXT_CHANNEL_ID,
314
- name: 'queue-e2e',
315
- type: ChannelType.GuildText,
316
- },
317
- ],
318
- users: [
319
- {
320
- id: TEST_USER_ID,
321
- username: 'queue-tester',
322
- },
323
- ],
324
- dbUrl: `file:${digitalDiscordDbPath}`,
325
- })
326
-
327
- await discord.start()
328
-
329
- const providerNpm = url
330
- .pathToFileURL(
331
- path.resolve(
332
- process.cwd(),
333
- '..',
334
- 'opencode-deterministic-provider',
335
- 'src',
336
- 'index.ts',
337
- ),
338
- )
339
- .toString()
340
-
341
- const opencodeConfig = buildDeterministicOpencodeConfig({
342
- providerName: 'deterministic-provider',
343
- providerNpm,
344
- model: 'deterministic-v2',
345
- smallModel: 'deterministic-v2',
346
- settings: {
347
- strict: false,
348
- matchers: createDeterministicMatchers(),
349
- },
350
- })
351
- fs.writeFileSync(
352
- path.join(directories.projectDirectory, 'opencode.json'),
353
- JSON.stringify(opencodeConfig, null, 2),
354
- )
355
-
356
- const dbPath = path.join(directories.dataDir, 'discord-sessions.db')
357
- const hranaResult = await startHranaServer({ dbPath })
358
- if (hranaResult instanceof Error) {
359
- throw hranaResult
360
- }
361
- process.env['KIMAKI_DB_URL'] = hranaResult
362
- await initDatabase()
363
- await setBotToken(discord.botUserId, discord.botToken)
364
-
365
- await setChannelDirectory({
366
- channelId: TEXT_CHANNEL_ID,
367
- directory: directories.projectDirectory,
368
- channelType: 'text',
369
- })
370
- await setChannelVerbosity(TEXT_CHANNEL_ID, 'tools_and_text')
371
- const channelVerbosity = await getChannelVerbosity(TEXT_CHANNEL_ID)
372
- expect(channelVerbosity).toBe('tools_and_text')
373
-
374
- botClient = createDiscordJsClient({ restUrl: discord.restUrl })
375
- await startDiscordBot({
376
- token: discord.botToken,
377
- appId: discord.botUserId,
378
- discordClient: botClient,
379
- })
380
-
381
- // Pre-warm the opencode server so the first test doesn't include
382
- // server startup time (~3-4s) inside its 4s poll timeouts.
383
- const warmup = await initializeOpencodeForDirectory(
384
- directories.projectDirectory,
385
- )
386
- if (warmup instanceof Error) {
387
- throw warmup
388
- }
389
- }, 60_000)
390
-
391
- afterAll(async () => {
392
- if (directories) {
393
- await cleanupTestSessions({
394
- projectDirectory: directories.projectDirectory,
395
- testStartTime,
396
- })
397
- }
398
-
399
- if (botClient) {
400
- void botClient.destroy()
401
- }
402
-
403
- await stopOpencodeServer()
404
- await Promise.all([
405
- closeDatabase().catch(() => {
406
- return
407
- }),
408
- stopHranaServer().catch(() => {
409
- return
410
- }),
411
- discord?.stop().catch(() => {
412
- return
413
- }),
414
- ])
415
-
416
- delete process.env['KIMAKI_LOCK_PORT']
417
- delete process.env['KIMAKI_DB_URL']
418
- if (previousDefaultVerbosity) {
419
- store.setState({ defaultVerbosity: previousDefaultVerbosity })
420
- }
421
- if (directories) {
422
- fs.rmSync(directories.dataDir, { recursive: true, force: true })
423
- }
424
- }, 20_000)
425
-
426
- test(
427
- 'first prompt after cold opencode server start still streams text parts',
428
- async () => {
429
- // Reproduce cold-start path: clear in-memory server/client registry so
430
- // runtime startEventListener() runs once before initialize and exits with
431
- // "No OpenCode client". The first prompt must still show text parts.
432
- await stopOpencodeServer()
433
-
434
- const prompt = 'Reply with exactly: cold-start-stream'
435
-
436
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
437
- content: prompt,
438
- })
439
-
440
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
441
- timeout: 4_000,
442
- predicate: (t) => {
443
- return t.name === prompt
444
- },
445
- })
446
-
447
- await waitForBotMessageContaining({
448
- discord,
449
- threadId: thread.id,
450
- userId: TEST_USER_ID,
451
- text: 'ok',
452
- timeout: 10_000,
453
- })
454
-
455
- await waitForFooterMessage({
456
- discord,
457
- threadId: thread.id,
458
- timeout: 4_000,
459
- })
460
-
461
- expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
462
- "--- from: user (queue-tester)
463
- Reply with exactly: cold-start-stream
464
- --- from: assistant (TestBot)
465
- -# *using deterministic-provider/deterministic-v2*
466
- ok
467
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
468
- `)
469
- },
470
- 12_000,
471
- )
472
-
473
- test(
474
- 'text message during active session gets processed',
475
- async () => {
476
- // 1. Send initial message to text channel → thread created + session established
477
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
478
- content: 'Reply with exactly: alpha',
479
- })
480
-
481
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
482
- timeout: 4_000,
483
- predicate: (t) => {
484
- return t.name === 'Reply with exactly: alpha'
485
- },
486
- })
487
-
488
- const th = discord.thread(thread.id)
489
-
490
- // Wait for the first bot reply so session is fully established in DB
491
- const firstReply = await th.waitForBotReply({
492
- timeout: 4_000,
493
- })
494
- expect(getMessageVisibleText(firstReply).trim().length).toBeGreaterThan(0)
495
-
496
- // Snapshot bot message count before sending follow-up
497
- const before = await th.getMessages()
498
- const beforeBotCount = before.filter((m) => {
499
- return m.author.id === discord.botUserId
500
- }).length
501
-
502
- // 2. Send follow-up message B into the thread — serialized by runtime's enqueueIncoming
503
- await th.user(TEST_USER_ID).sendMessage({
504
- content: 'Reply with exactly: beta',
505
- })
506
-
507
- // 3. Wait for exactly 1 new bot message (the response to B)
508
- const after = await waitForBotMessageCount({
509
- discord,
510
- threadId: thread.id,
511
- count: beforeBotCount + 1,
512
- timeout: 4_000,
513
- })
514
-
515
- // 4. Verify at least 1 new bot message appeared for the follow-up.
516
- // The bot may send additional messages per session (error reactions,
517
- // session notifications) so we check >= not exact equality.
518
- const afterBotMessages = after.filter((m) => {
519
- return m.author.id === discord.botUserId
520
- })
521
- expect(afterBotMessages.length).toBeGreaterThanOrEqual(beforeBotCount + 1)
522
-
523
- await waitForFooterMessage({
524
- discord,
525
- threadId: thread.id,
526
- timeout: 8_000,
527
- afterMessageIncludes: 'beta',
528
- afterAuthorId: TEST_USER_ID,
529
- })
530
-
531
- const timeline = await th.text()
532
- expect(timeline).toContain('Reply with exactly: alpha')
533
- expect(timeline).toContain('Reply with exactly: beta')
534
- expect(timeline).toContain('ok')
535
- expect(timeline).toContain('*project ⋅ main ⋅')
536
- // User B's message must appear before the new bot response
537
- const userBIndex = after.findIndex((m) => {
538
- return (
539
- m.author.id === TEST_USER_ID &&
540
- m.content.includes('beta')
541
- )
542
- })
543
- const lastBotIndex = after.findLastIndex((m) => {
544
- return m.author.id === discord.botUserId
545
- })
546
-
547
- expect(userBIndex).toBeGreaterThan(-1)
548
- expect(lastBotIndex).toBeGreaterThan(-1)
549
- expect(userBIndex).toBeLessThan(lastBotIndex)
550
-
551
- // New bot response has non-empty content
552
- const newBotReply = afterBotMessages[afterBotMessages.length - 1]!
553
- expect(getMessageVisibleText(newBotReply).trim().length).toBeGreaterThan(0)
554
- },
555
- 12_000,
556
- )
557
-
558
- test('btw queue forks after the current turn and includes its answer', async () => {
559
- const start = 'Reply with exactly: SLOW_BUSY_MARKER queued-fork-source'
560
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({ content: start })
561
- const source = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
562
- timeout: 4_000,
563
- predicate: (thread) => thread.name === start,
564
- })
565
- const th = discord.thread(source.id)
566
- await waitForBotMessageContaining({
567
- discord, threadId: source.id, userId: TEST_USER_ID,
568
- text: 'slow-busy-reply', timeout: 10_000,
569
- })
570
- await th.user(TEST_USER_ID).sendMessage({
571
- content: 'Reply with exactly: earlier-queue. queue',
572
- })
573
- await th.user(TEST_USER_ID).sendMessage({
574
- content: 'Reply with exactly: queued-fork-answer. btw queue',
575
- })
576
-
577
- const before = await th.text()
578
- expect(before).not.toContain('Session forked!')
579
-
580
- const fork = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
581
- timeout: 8_000,
582
- predicate: (thread) => thread.id !== source.id && (thread.name?.startsWith('btw:') ?? false),
583
- })
584
- await waitForBotMessageContaining({
585
- discord, threadId: fork.id, userId: TEST_USER_ID,
586
- text: 'ok', timeout: 8_000,
587
- })
588
- await waitForFooterMessage({ discord, threadId: fork.id, timeout: 8_000 })
589
- await waitForBotMessageContaining({
590
- discord, threadId: source.id, userId: TEST_USER_ID,
591
- text: 'Session forked!', timeout: 8_000,
592
- })
593
- const sourceText = await th.text()
594
- expect(sourceText.replace(/<#[0-9]+>/g, '<#FORK_THREAD>')).toMatchInlineSnapshot(`
595
- "--- from: user (queue-tester)
596
- Reply with exactly: SLOW_BUSY_MARKER queued-fork-source
597
- --- from: assistant (TestBot)
598
- -# *using deterministic-provider/deterministic-v2*
599
- slow-busy-reply
600
- --- from: user (queue-tester)
601
- Reply with exactly: earlier-queue. queue
602
- Reply with exactly: queued-fork-answer. btw queue
603
- --- from: assistant (TestBot)
604
- -# Queued at position 1. Edit or delete your message to update the queue
605
- -# Queued at position 2. Edit or delete your message to update the queue
606
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
607
- -# Executing queued prompt
608
- ok
609
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
610
- -# Executing queued prompt
611
- Session forked! Continue in <#FORK_THREAD>"
612
- `)
613
- expect(sourceText.indexOf('*project ⋅ main ⋅')).toBeLessThan(sourceText.indexOf('Session forked!'))
614
- expect(sourceText.indexOf('earlier-queue')).toBeLessThan(sourceText.indexOf('Session forked!'))
615
- expect((await discord.thread(fork.id).text()).replace(/<#[0-9]+>/g, '<#SOURCE_THREAD>')).toMatchInlineSnapshot(`
616
- "--- from: assistant (TestBot)
617
- Reusing context from <#SOURCE_THREAD> to answer prompt...
618
- Reply with exactly: queued-fork-answer
619
- ok
620
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
621
- `)
622
-
623
- await discord.thread(fork.id).user(TEST_USER_ID).runSlashCommand({
624
- name: 'queue',
625
- options: [{ name: 'message', type: 3, value: 'Reply with exactly: nested-fork. btw' }],
626
- })
627
- const nested = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
628
- timeout: 8_000,
629
- predicate: (thread) => thread.id !== source.id && thread.id !== fork.id && (thread.name?.startsWith('btw:') ?? false),
630
- })
631
- await waitForBotMessageContaining({
632
- discord, threadId: nested.id, userId: TEST_USER_ID,
633
- text: 'ok', timeout: 8_000,
634
- })
635
- await waitForFooterMessage({ discord, threadId: nested.id, timeout: 8_000 })
636
- expect((await discord.thread(nested.id).text()).replace(/<#[0-9]+>/g, '<#SOURCE_THREAD>')).toMatchInlineSnapshot(`
637
- "--- from: assistant (TestBot)
638
- Reusing context from <#SOURCE_THREAD> to answer prompt...
639
- Reply with exactly: nested-fork
640
- ok
641
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
642
- `)
643
- }, 16_000)
644
-
645
- test(
646
- 'two rapid text messages in thread — both processed in order',
647
- async () => {
648
- // 1. Send initial message to text channel → thread + session established
649
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
650
- content: 'Reply with exactly: one',
651
- })
652
-
653
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
654
- timeout: 4_000,
655
- predicate: (t) => {
656
- return t.name === 'Reply with exactly: one'
657
- },
658
- })
659
-
660
- const th = discord.thread(thread.id)
661
-
662
- // Wait for the first bot reply AND its footer so the first response
663
- // cycle is fully complete before sending follow-ups. Without this,
664
- // the footer for "one" can still be in-flight when the snapshot runs.
665
- const firstReply = await th.waitForBotReply({
666
- timeout: 4_000,
667
- })
668
- expect(getMessageVisibleText(firstReply).trim().length).toBeGreaterThan(0)
669
-
670
- await waitForFooterMessage({
671
- discord,
672
- threadId: thread.id,
673
- timeout: 4_000,
674
- afterMessageIncludes: 'one',
675
- afterAuthorId: TEST_USER_ID,
676
- })
677
-
678
- // Snapshot bot message count before sending follow-ups
679
- const before = await th.getMessages()
680
- const beforeBotCount = before.filter((m) => {
681
- return m.author.id === discord.botUserId
682
- }).length
683
-
684
- // 2. Rapidly send messages B and C. With opencode queue mode,
685
- // both messages are serialized by opencode's per-session loop.
686
- await th.user(TEST_USER_ID).sendMessage({
687
- content: 'Reply with exactly: two',
688
- })
689
- await th.user(TEST_USER_ID).sendMessage({
690
- content: 'Reply with exactly: three',
691
- })
692
-
693
- // 3. Wait for a bot reply after message C.
694
- const after = await waitForBotReplyAfterUserMessage({
695
- discord,
696
- threadId: thread.id,
697
- userId: TEST_USER_ID,
698
- userMessageIncludes: 'three',
699
- timeout: 4_000,
700
- })
701
-
702
- // 4. Verify the latest user message got a bot reply.
703
- const afterBotMessages = after.filter((m) => {
704
- return m.author.id === discord.botUserId
705
- })
706
- expect(afterBotMessages.length).toBeGreaterThanOrEqual(beforeBotCount + 1)
707
-
708
- expect((await th.text()).replace(/\nok(?=\nok)/g, '')).toMatchInlineSnapshot(`
709
- "--- from: user (queue-tester)
710
- Reply with exactly: one
711
- --- from: assistant (TestBot)
712
- -# *using deterministic-provider/deterministic-v2*
713
- ok
714
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
715
- --- from: user (queue-tester)
716
- Reply with exactly: two
717
- Reply with exactly: three
718
- --- from: assistant (TestBot)
719
- ok"
720
- `)
721
- const userThreeIndex = after.findIndex((message) => {
722
- return (
723
- message.author.id === TEST_USER_ID &&
724
- message.content.includes('three')
725
- )
726
- })
727
- expect(userThreeIndex).toBeGreaterThan(-1)
728
-
729
- const botAfterThreeIndex = after.findIndex((message, index) => {
730
- return index > userThreeIndex && message.author.id === discord.botUserId
731
- })
732
- expect(botAfterThreeIndex).toBeGreaterThan(userThreeIndex)
733
-
734
- const newBotReplies = afterBotMessages.slice(beforeBotCount)
735
- expect(newBotReplies.some((reply) => {
736
- return getMessageVisibleText(reply).trim().length > 0
737
- })).toBe(true)
738
-
739
- const finalState = await waitForThreadState({
740
- threadId: thread.id,
741
- predicate: (state) => {
742
- return state.queueItems.length === 0
743
- },
744
- timeout: 4_000,
745
- description: 'queue empty after rapid interrupts',
746
- })
747
- expect(finalState.queueItems.length).toBe(0)
748
- },
749
- 8_000,
750
- )
751
-
752
- test(
753
- 'bash tool-call actually executes and creates file in project directory',
754
- async () => {
755
- const markerRelativePath = path.join('tmp', 'bash-tool-executed.txt')
756
- const markerPath = path.join(directories.projectDirectory, markerRelativePath)
757
- fs.rmSync(markerPath, { force: true })
758
- const existingThreadIds = new Set(
759
- (await discord.channel(TEXT_CHANNEL_ID).getThreads()).map((thread) => {
760
- return thread.id
761
- }),
762
- )
763
-
764
- const prompt = 'Reply with exactly: BASH_TOOL_FILE_MARKER'
765
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
766
- content: prompt,
767
- })
768
-
769
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
770
- timeout: 6_000,
771
- predicate: (t) => {
772
- return !existingThreadIds.has(t.id)
773
- },
774
- })
775
-
776
- await waitForBotMessageContaining({
777
- discord,
778
- threadId: thread.id,
779
- userId: TEST_USER_ID,
780
- text: 'running create file',
781
- timeout: 6_000,
782
- })
783
-
784
- await waitForFooterMessage({
785
- discord,
786
- threadId: thread.id,
787
- timeout: 6_000,
788
- })
789
-
790
- const deadline = Date.now() + 4_000
791
- while (!fs.existsSync(markerPath) && Date.now() < deadline) {
792
- await new Promise((resolve) => {
793
- setTimeout(resolve, 100)
794
- })
795
- }
796
-
797
- expect(await discord.thread(thread.id).text()).toMatchInlineSnapshot(`
798
- "--- from: user (queue-tester)
799
- Reply with exactly: BASH_TOOL_FILE_MARKER
800
- --- from: assistant (TestBot)
801
- -# *using deterministic-provider/deterministic-v2*
802
- > running create file
803
- > creating marker
804
-
805
- -# ┣ bash _Create marker file for e2e test_
806
-
807
- file created
808
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
809
- `)
810
- expect(fs.existsSync(markerPath)).toBe(true)
811
- const markerContents = fs.readFileSync(markerPath, 'utf8')
812
- expect(markerContents).toBe('created')
813
- },
814
- 8_000,
815
- )
816
-
817
- test(
818
- '/queue shows queued status first, then dispatch indicator when dequeued',
819
- async () => {
820
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
821
- content: 'Reply with exactly: queue-slash-setup',
822
- })
823
-
824
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
825
- timeout: 4_000,
826
- predicate: (t) => {
827
- return t.name === 'Reply with exactly: queue-slash-setup'
828
- },
829
- })
830
-
831
- const th = discord.thread(thread.id)
832
- const firstReply = await th.waitForBotReply({ timeout: 4_000 })
833
- expect(getMessageVisibleText(firstReply).trim().length).toBeGreaterThan(0)
834
-
835
- // Ensure the setup run is fully settled before slash-queue checks.
836
- // Otherwise the first /queue call can race with a still-busy run window.
837
- await waitForFooterMessage({
838
- discord,
839
- threadId: thread.id,
840
- timeout: 4_000,
841
- })
842
-
843
- // Start a non-interrupting queued slash message while idle so it
844
- // dispatches immediately and keeps the runtime active.
845
- const { id: firstQueueInteractionId } = await th.user(TEST_USER_ID)
846
- .runSlashCommand({
847
- name: 'queue',
848
- options: [{ name: 'message', type: 3, value: 'Reply with exactly: race-final' }],
849
- })
850
-
851
- const firstQueueAck = await th.waitForInteractionAck({
852
- interactionId: firstQueueInteractionId,
853
- timeout: 4_000,
854
- })
855
- if (!firstQueueAck.messageId) {
856
- throw new Error('Expected first /queue response message id')
857
- }
858
-
859
- const firstQueueAckMessage = await waitForMessageById({
860
- discord,
861
- threadId: thread.id,
862
- messageId: firstQueueAck.messageId,
863
- timeout: 4_000,
864
- })
865
- expect(firstQueueAckMessage.content).toContain('» **queue-tester:** Reply with exactly: race-final')
866
-
867
- const queuedPrompt = 'Reply with exactly: queued-from-slash'
868
- const { id: interactionId } = await th.user(TEST_USER_ID).runSlashCommand({
869
- name: 'queue',
870
- options: [{ name: 'message', type: 3, value: queuedPrompt }],
871
- })
872
-
873
- const queuedAck = await th.waitForInteractionAck({ interactionId, timeout: 4_000 })
874
- if (!queuedAck.messageId) {
875
- throw new Error('Expected queued /queue response message id')
876
- }
877
-
878
- const queuedStatusMessage = await waitForMessageById({
879
- discord,
880
- threadId: thread.id,
881
- messageId: queuedAck.messageId,
882
- timeout: 4_000,
883
- })
884
- expect(queuedStatusMessage.content.startsWith('-# Queued message')).toBe(true)
885
-
886
- // The drain indicator is a silent reply to the /queue ack.
887
- const dispatchIndicatorMessage = await waitForBotReplyTo({
888
- discord,
889
- threadId: thread.id,
890
- replyToMessageId: queuedStatusMessage.id,
891
- timeout: 8_000,
892
- })
893
- expect(dispatchIndicatorMessage.content).toBe('-# Executing queued prompt')
894
-
895
- await waitForBotMessageContaining({
896
- discord,
897
- threadId: thread.id,
898
- text: 'ok',
899
- afterMessageId: dispatchIndicatorMessage.id,
900
- timeout: 8_000,
901
- })
902
-
903
- await waitForFooterMessage({
904
- discord,
905
- threadId: thread.id,
906
- timeout: 8_000,
907
- afterMessageIncludes: 'ok',
908
- afterAuthorId: discord.botUserId,
909
- })
910
-
911
- expect(await th.text()).toMatchInlineSnapshot(`
912
- "--- from: user (queue-tester)
913
- Reply with exactly: queue-slash-setup
914
- --- from: assistant (TestBot)
915
- -# *using deterministic-provider/deterministic-v2*
916
- ok
917
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
918
- » **queue-tester:** Reply with exactly: race-final
919
- -# Queued message (position 2)
920
- race-final
921
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
922
- -# Executing queued prompt
923
- ok
924
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
925
- `)
926
- },
927
- 12_000,
928
- )
929
-
930
- test(
931
- 'Remove from queue button clears only that queued message',
932
- async () => {
933
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
934
- content: 'Reply with exactly: clear-queue-setup',
935
- })
936
-
937
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
938
- timeout: 4_000,
939
- predicate: (t) => {
940
- return t.name === 'Reply with exactly: clear-queue-setup'
941
- },
942
- })
943
-
944
- const th = discord.thread(thread.id)
945
- await th.waitForBotReply({ timeout: 4_000 })
946
- await waitForFooterMessage({
947
- discord,
948
- threadId: thread.id,
949
- timeout: 4_000,
950
- })
951
-
952
- await th.user(TEST_USER_ID).runSlashCommand({
953
- name: 'queue',
954
- options: [{ name: 'message', type: 3, value: 'Reply with exactly: race-final' }],
955
- })
956
-
957
- const { id: secondQueueInteractionId } = await th.user(TEST_USER_ID)
958
- .runSlashCommand({
959
- name: 'queue',
960
- options: [{ name: 'message', type: 3, value: 'Reply with exactly: removed-queued-message' }],
961
- })
962
- const secondQueueAck = await th.waitForInteractionAck({
963
- interactionId: secondQueueInteractionId,
964
- timeout: 4_000,
965
- })
966
- if (!secondQueueAck.messageId) {
967
- throw new Error('Expected second /queue response message id')
968
- }
969
-
970
- const secondQueueAckMessage = await waitForMessageById({
971
- discord,
972
- threadId: thread.id,
973
- messageId: secondQueueAck.messageId,
974
- timeout: 4_000,
975
- })
976
- // The first /queue item is still in flight, so it holds position 1.
977
- expect(secondQueueAckMessage.content).toContain('Queued message (position 2)')
978
-
979
- const { id: thirdQueueInteractionId } = await th.user(TEST_USER_ID).runSlashCommand({
980
- name: 'queue',
981
- options: [{ name: 'message', type: 3, value: 'Reply with exactly: kept-queued-message' }],
982
- })
983
- const thirdQueueAck = await th.waitForInteractionAck({
984
- interactionId: thirdQueueInteractionId,
985
- timeout: 4_000,
986
- })
987
- if (!thirdQueueAck.messageId) {
988
- throw new Error('Expected third /queue response message id')
989
- }
990
-
991
- const thirdQueueAckMessage = await waitForMessageById({
992
- discord,
993
- threadId: thread.id,
994
- messageId: thirdQueueAck.messageId,
995
- timeout: 4_000,
996
- })
997
- expect(thirdQueueAckMessage.content).toContain('Queued message (position 3)')
998
-
999
- const serializedComponents = JSON.stringify(secondQueueAckMessage.components)
1000
- const customIdMatch = serializedComponents.match(
1001
- /"custom_id"\s*:\s*"(queue_remove:[^"]+)"/,
1002
- )
1003
- if (!customIdMatch?.[1]) {
1004
- throw new Error(
1005
- `Expected Remove from queue button on queue ack: ${serializedComponents}`,
1006
- )
1007
- }
1008
-
1009
- const removeInteraction = await th.user(TEST_USER_ID).clickButton({
1010
- messageId: secondQueueAckMessage.id,
1011
- customId: customIdMatch[1],
1012
- })
1013
- await th.waitForInteractionAck({
1014
- interactionId: removeInteraction.id,
1015
- timeout: 4_000,
1016
- })
1017
-
1018
- const removeAckMessage = await waitForMessageById({
1019
- discord,
1020
- threadId: thread.id,
1021
- messageId: secondQueueAckMessage.id,
1022
- timeout: 4_000,
1023
- })
1024
- expect(removeAckMessage.content).toContain('Removed queued message')
1025
- expect(removeAckMessage.content).toContain('removed-queued-message')
1026
-
1027
- const indicator = await waitForBotReplyTo({
1028
- discord,
1029
- threadId: thread.id,
1030
- replyToMessageId: thirdQueueAckMessage.id,
1031
- timeout: 8_000,
1032
- })
1033
- expect(indicator.content).toBe('-# Executing queued prompt')
1034
-
1035
- await waitForFooterMessage({
1036
- discord,
1037
- threadId: thread.id,
1038
- timeout: 8_000,
1039
- afterMessageId: indicator.id,
1040
- })
1041
-
1042
- const threadText = await th.text()
1043
- expect(threadText).toMatchInlineSnapshot(`
1044
- "--- from: user (queue-tester)
1045
- Reply with exactly: clear-queue-setup
1046
- --- from: assistant (TestBot)
1047
- -# *using deterministic-provider/deterministic-v2*
1048
- ok
1049
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
1050
- -# Removed queued message: Reply with exactly: removed-queued-message
1051
- » **queue-tester:** Reply with exactly: race-final
1052
- -# Queued message (position 3)
1053
- race-final
1054
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
1055
- -# Executing queued prompt
1056
- ok
1057
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
1058
- `)
1059
- // The removed item never drained, so nothing replies to its ack.
1060
- const finalMessages = await th.getMessages()
1061
- expect(finalMessages.some((message) => {
1062
- return message.message_reference?.message_id === secondQueueAckMessage.id
1063
- })).toBe(false)
1064
- },
1065
- 12_000,
1066
- )
1067
-
1068
- test(
1069
- 'slow stream still processes queued next message after completion',
1070
- async () => {
1071
- // A message sent mid-stream queues and runs after the in-flight request
1072
- // completes (no auto-interrupt).
1073
-
1074
- // 1. Fast setup: establish session
1075
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
1076
- content: 'Reply with exactly: golf',
1077
- })
1078
-
1079
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
1080
- timeout: 4_000,
1081
- predicate: (t) => {
1082
- return t.name === 'Reply with exactly: golf'
1083
- },
1084
- })
1085
-
1086
- const th = discord.thread(thread.id)
1087
- const firstReply = await th.waitForBotReply({ timeout: 4_000 })
1088
- expect(getMessageVisibleText(firstReply).trim().length).toBeGreaterThan(0)
1089
-
1090
- // Wait for golf's footer so the golf→hotel transition is deterministic
1091
- await waitForFooterMessage({
1092
- discord,
1093
- threadId: thread.id,
1094
- timeout: 4_000,
1095
- afterMessageIncludes: 'ok',
1096
- afterAuthorId: discord.botUserId,
1097
- })
1098
-
1099
- const before = await th.getMessages()
1100
- const beforeBotCount = before.filter((m) => {
1101
- return m.author.id === discord.botUserId
1102
- }).length
1103
-
1104
- // 2. Start request B (hotel, slow matcher ~400ms), then send C while B
1105
- // is still in progress.
1106
- await th.user(TEST_USER_ID).sendMessage({
1107
- content: 'Reply with exactly: hotel',
1108
- })
1109
-
1110
- // 3. Wait briefly for B to start, then send C to queue behind it
1111
- await new Promise((r) => {
1112
- setTimeout(r, 200)
1113
- })
1114
- await th.user(TEST_USER_ID).sendMessage({
1115
- content: 'Reply with exactly: india',
1116
- })
1117
-
1118
- // 4. B completes, then C gets processed.
1119
- // Poll until india's user message has a bot reply after it.
1120
- const after = await waitForBotReplyAfterUserMessage({
1121
- discord,
1122
- threadId: thread.id,
1123
- userId: TEST_USER_ID,
1124
- userMessageIncludes: 'india',
1125
- timeout: 4_000,
1126
- })
1127
-
1128
- // C's user message appears before its bot response.
1129
- // We assert on india's reply existence.
1130
- expect(await th.text()).toMatchInlineSnapshot(`
1131
- "--- from: user (queue-tester)
1132
- Reply with exactly: golf
1133
- --- from: assistant (TestBot)
1134
- -# *using deterministic-provider/deterministic-v2*
1135
- ok
1136
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
1137
- --- from: user (queue-tester)
1138
- Reply with exactly: hotel
1139
- Reply with exactly: india
1140
- --- from: assistant (TestBot)
1141
- > ok"
1142
- `)
1143
- const userIndiaIndex = after.findIndex((m) => {
1144
- return m.author.id === TEST_USER_ID && m.content.includes('india')
1145
- })
1146
- expect(userIndiaIndex).toBeGreaterThan(-1)
1147
- const botAfterIndia = after.findIndex((m, i) => {
1148
- return i > userIndiaIndex && m.author.id === discord.botUserId
1149
- })
1150
- expect(botAfterIndia).toBeGreaterThan(userIndiaIndex)
1151
- },
1152
- 8_000,
1153
- )
1154
-
1155
- test(
1156
- 'queue drains correctly after bursty queued messages',
1157
- async () => {
1158
- // Verifies the queue doesn't get stuck after multiple rapid messages.
1159
-
1160
- // 1. Fast setup: establish session
1161
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
1162
- content: 'Reply with exactly: juliet',
1163
- })
1164
-
1165
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
1166
- timeout: 4_000,
1167
- predicate: (t) => {
1168
- return t.name === 'Reply with exactly: juliet'
1169
- },
1170
- })
1171
-
1172
- const th = discord.thread(thread.id)
1173
- const firstReply = await th.waitForBotReply({ timeout: 4_000 })
1174
- expect(getMessageVisibleText(firstReply).trim().length).toBeGreaterThan(0)
1175
-
1176
- const before = await th.getMessages()
1177
- const beforeBotCount = before.filter((m) => {
1178
- return m.author.id === discord.botUserId
1179
- }).length
1180
-
1181
- // 2. Rapidly send B, C, D back-to-back to avoid timing windows where
1182
- // one run can finish between sends and reorder transcript lines.
1183
- await th.user(TEST_USER_ID).sendMessage({
1184
- content: 'Reply with exactly: kilo',
1185
- })
1186
- await th.user(TEST_USER_ID).sendMessage({
1187
- content: 'Reply with exactly: lima',
1188
- })
1189
- await th.user(TEST_USER_ID).sendMessage({
1190
- content: 'Reply with exactly: mike',
1191
- })
1192
-
1193
- // 3. Wait until the last burst message (mike) has a bot reply after it.
1194
- const afterBurst = await waitForBotReplyAfterUserMessage({
1195
- discord,
1196
- threadId: thread.id,
1197
- userId: TEST_USER_ID,
1198
- userMessageIncludes: 'mike',
1199
- timeout: 4_000,
1200
- })
1201
-
1202
- // 4. Queue should be clean — send E and verify it also gets processed
1203
- await th.user(TEST_USER_ID).sendMessage({
1204
- content: 'Reply with exactly: november',
1205
- })
1206
-
1207
- const afterE = await waitForBotReplyAfterUserMessage({
1208
- discord,
1209
- threadId: thread.id,
1210
- userId: TEST_USER_ID,
1211
- userMessageIncludes: 'november',
1212
- timeout: 4_000,
1213
- })
1214
-
1215
- const textWithoutFooters = (await th.text())
1216
- .split('\n')
1217
- .filter((line) => {
1218
- return !line.startsWith('*project ⋅') && !line.startsWith('> *project ⋅') && !line.startsWith('-# *project ⋅')
1219
- })
1220
- .join('\n')
1221
-
1222
- const normalizedTextWithoutFooters = textWithoutFooters.replace(
1223
- [
1224
- '--- from: assistant (TestBot)',
1225
- 'ok',
1226
- '--- from: user (queue-tester)',
1227
- 'Reply with exactly: november',
1228
- ].join('\n'),
1229
- [
1230
- '--- from: assistant (TestBot)',
1231
- '--- from: user (queue-tester)',
1232
- 'Reply with exactly: november',
1233
- ].join('\n'),
1234
- )
1235
-
1236
- expect(normalizedTextWithoutFooters).toMatchInlineSnapshot(`
1237
- "--- from: user (queue-tester)
1238
- Reply with exactly: juliet
1239
- --- from: assistant (TestBot)
1240
- -# *using deterministic-provider/deterministic-v2*
1241
- --- from: user (queue-tester)
1242
- Reply with exactly: kilo
1243
- Reply with exactly: lima
1244
- Reply with exactly: mike
1245
- --- from: assistant (TestBot)
1246
- --- from: user (queue-tester)
1247
- Reply with exactly: november
1248
- --- from: assistant (TestBot)"
1249
- `)
1250
- // E's user message appears before the final bot response
1251
- const userNovemberIndex = afterE.findIndex((m) => {
1252
- return m.author.id === TEST_USER_ID && m.content.includes('november')
1253
- })
1254
- expect(userNovemberIndex).toBeGreaterThan(-1)
1255
- const lastBotIndex = afterE.findLastIndex((m) => {
1256
- return m.author.id === discord.botUserId
1257
- })
1258
- expect(userNovemberIndex).toBeLessThan(lastBotIndex)
1259
- },
1260
- 8_000,
1261
- )
1262
-
1263
- test(
1264
- 'editing a queued message updates its prompt before dispatch',
1265
- async () => {
1266
- // 1. Start a session with a slow matcher (2s busy) to keep it busy.
1267
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
1268
- content: 'SLOW_BUSY_MARKER Reply with exactly: edit-queue-setup',
1269
- })
1270
-
1271
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
1272
- timeout: 4_000,
1273
- predicate: (t) => {
1274
- return t.name === 'SLOW_BUSY_MARKER Reply with exactly: edit-queue-setup'
1275
- },
1276
- })
1277
-
1278
- const th = discord.thread(thread.id)
1279
-
1280
- // 2. Wait until the bot has replied, then queue while the slow stream is busy.
1281
- await th.waitForBotReply({ timeout: 4_000 })
1282
- const queuedMsg = await th.user(TEST_USER_ID).sendMessage({
1283
- content: 'Reply with exactly: original-queued. queue',
1284
- })
1285
-
1286
- // 3. Verify the message landed in the local queue.
1287
- const queuedState = await waitForThreadState({
1288
- threadId: thread.id,
1289
- predicate: (state) => {
1290
- return state.queueItems.length > 0
1291
- },
1292
- timeout: 4_000,
1293
- description: 'queue has item from suffix message',
1294
- })
1295
- expect(queuedState.queueItems.length).toBeGreaterThanOrEqual(1)
1296
- const queueItem = queuedState.queueItems.find((item) => {
1297
- return item.sourceMessageId === queuedMsg.id
1298
- })
1299
- expect(queueItem).toBeTruthy()
1300
- expect(queueItem!.prompt).toContain('original-queued')
1301
-
1302
- // 4. Edit the message while it's still in the queue.
1303
- await th.user(TEST_USER_ID).editMessage({
1304
- messageId: queuedMsg.id,
1305
- content: 'Reply with exactly: edited-queued. queue',
1306
- })
1307
-
1308
- // 5. Verify the queue item was updated.
1309
- const updatedState = await waitForThreadState({
1310
- threadId: thread.id,
1311
- predicate: (state) => {
1312
- return state.queueItems.some((item) => {
1313
- return item.prompt.includes('edited-queued')
1314
- })
1315
- },
1316
- timeout: 4_000,
1317
- description: 'queue item updated after edit',
1318
- })
1319
- const updatedItem = updatedState.queueItems.find((item) => {
1320
- return item.sourceMessageId === queuedMsg.id
1321
- })
1322
- expect(updatedItem).toBeTruthy()
1323
- expect(updatedItem!.prompt).toContain('edited-queued')
1324
- expect(updatedItem!.prompt).not.toContain('original-queued')
1325
-
1326
- // 6. Wait for the queue to drain and verify the edited prompt was dispatched.
1327
- // The drain indicator is a silent reply to the user's queued message.
1328
- const indicator = await waitForBotReplyTo({
1329
- discord,
1330
- threadId: thread.id,
1331
- replyToMessageId: queuedMsg.id,
1332
- timeout: 8_000,
1333
- })
1334
- expect(indicator.content).toBe('-# Executing queued prompt')
1335
-
1336
- await waitForFooterMessage({
1337
- discord,
1338
- threadId: thread.id,
1339
- timeout: 8_000,
1340
- afterMessageId: indicator.id,
1341
- })
1342
-
1343
- expect(await th.text()).toMatchInlineSnapshot(`
1344
- "--- from: user (queue-tester)
1345
- SLOW_BUSY_MARKER Reply with exactly: edit-queue-setup
1346
- --- from: assistant (TestBot)
1347
- -# *using deterministic-provider/deterministic-v2*
1348
- --- from: user (queue-tester)
1349
- Reply with exactly: edited-queued. queue
1350
- --- from: assistant (TestBot)
1351
- -# Queued at position 1. Edit or delete your message to update the queue
1352
- -# **queue-tester** edited queued message
1353
- slow-busy-reply
1354
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*
1355
- -# Executing queued prompt
1356
- ok
1357
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
1358
- `)
1359
-
1360
- const finalText = await th.text()
1361
- expect(finalText).toContain('edited-queued')
1362
- expect(finalText).not.toContain('original-queued')
1363
- },
1364
- 12_000,
1365
- )
1366
-
1367
- test(
1368
- 'editing a queued message to remove queue suffix removes it from queue',
1369
- async () => {
1370
- // 1. Start a session with a slow matcher (2s busy) to keep it busy.
1371
- await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
1372
- content: 'SLOW_BUSY_MARKER Reply with exactly: remove-queue-setup',
1373
- })
1374
-
1375
- const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
1376
- timeout: 4_000,
1377
- predicate: (t) => {
1378
- return t.name === 'SLOW_BUSY_MARKER Reply with exactly: remove-queue-setup'
1379
- },
1380
- })
1381
-
1382
- const th = discord.thread(thread.id)
1383
-
1384
- // Wait for the bot to start replying so the session is busy and
1385
- // the user message appears after the first bot message in the thread.
1386
- await th.waitForBotReply({ timeout: 4_000 })
1387
-
1388
- // 2. Queue a message with queue suffix while session is busy.
1389
- const queuedMsg = await th.user(TEST_USER_ID).sendMessage({
1390
- content: 'Reply with exactly: will-be-removed. queue',
1391
- })
1392
-
1393
- // 3. Verify the message is in the queue.
1394
- await waitForThreadState({
1395
- threadId: thread.id,
1396
- predicate: (state) => {
1397
- return state.queueItems.some((item) => {
1398
- return item.sourceMessageId === queuedMsg.id
1399
- })
1400
- },
1401
- timeout: 4_000,
1402
- description: 'queue has item to be removed',
1403
- })
1404
-
1405
- // 4. Edit the message to remove the queue suffix.
1406
- await th.user(TEST_USER_ID).editMessage({
1407
- messageId: queuedMsg.id,
1408
- content: 'Reply with exactly: will-be-removed',
1409
- })
1410
-
1411
- // 5. Verify the item was removed from the queue.
1412
- // Poll briefly since the MessageUpdate event is async.
1413
- for (let i = 0; i < 20; i++) {
1414
- const state = await waitForThreadState({
1415
- threadId: thread.id,
1416
- predicate: () => true,
1417
- timeout: 100,
1418
- description: 'check queue after edit',
1419
- })
1420
- const stillQueued = state.queueItems.some((item) => {
1421
- return item.sourceMessageId === queuedMsg.id
1422
- })
1423
- if (!stillQueued) break
1424
- await new Promise((r) => setTimeout(r, 50))
1425
- }
1426
-
1427
- const finalState = await waitForThreadState({
1428
- threadId: thread.id,
1429
- predicate: () => true,
1430
- timeout: 100,
1431
- description: 'final queue check',
1432
- })
1433
- const removedItem = finalState.queueItems.find((item) => {
1434
- return item.sourceMessageId === queuedMsg.id
1435
- })
1436
- expect(removedItem).toBeUndefined()
1437
-
1438
- // 6. Wait for the slow session to finish and verify the removed
1439
- // message was never dispatched as a queue drain (no drain indicator).
1440
- await waitForFooterMessage({
1441
- discord,
1442
- threadId: thread.id,
1443
- timeout: 8_000,
1444
- })
1445
-
1446
- const finalText = await th.text()
1447
- // The user message text appears in the thread, but the queue dispatch
1448
- // indicator should NOT appear because the item was removed from the
1449
- // queue before drain.
1450
- expect(finalText).not.toContain('Executing queued prompt')
1451
- expect(finalText).toMatchInlineSnapshot(`
1452
- "--- from: user (queue-tester)
1453
- SLOW_BUSY_MARKER Reply with exactly: remove-queue-setup
1454
- --- from: assistant (TestBot)
1455
- -# *using deterministic-provider/deterministic-v2*
1456
- --- from: user (queue-tester)
1457
- Reply with exactly: will-be-removed
1458
- --- from: assistant (TestBot)
1459
- -# Queued at position 1. Edit or delete your message to update the queue
1460
- -# **queue-tester** removed message from queue
1461
- slow-busy-reply
1462
- -# *project ⋅ main ⋅ Ns ⋅ N% ⋅ deterministic-v2*"
1463
- `)
1464
- },
1465
- 12_000,
1466
- )
1467
-
1468
- })