@otto-assistant/bridge 0.4.92

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 (483) hide show
  1. package/bin.js +2 -0
  2. package/dist/agent-model.e2e.test.js +755 -0
  3. package/dist/ai-tool-to-genai.js +233 -0
  4. package/dist/ai-tool-to-genai.test.js +267 -0
  5. package/dist/ai-tool.js +6 -0
  6. package/dist/anthropic-auth-plugin.js +728 -0
  7. package/dist/anthropic-auth-plugin.test.js +125 -0
  8. package/dist/anthropic-auth-state.js +231 -0
  9. package/dist/bin.js +90 -0
  10. package/dist/channel-management.js +227 -0
  11. package/dist/cli-parsing.test.js +137 -0
  12. package/dist/cli-send-thread.e2e.test.js +356 -0
  13. package/dist/cli.js +3276 -0
  14. package/dist/commands/abort.js +65 -0
  15. package/dist/commands/action-buttons.js +245 -0
  16. package/dist/commands/add-project.js +113 -0
  17. package/dist/commands/agent.js +335 -0
  18. package/dist/commands/ask-question.js +274 -0
  19. package/dist/commands/btw.js +116 -0
  20. package/dist/commands/compact.js +120 -0
  21. package/dist/commands/context-usage.js +140 -0
  22. package/dist/commands/create-new-project.js +130 -0
  23. package/dist/commands/diff.js +63 -0
  24. package/dist/commands/file-upload.js +275 -0
  25. package/dist/commands/fork.js +220 -0
  26. package/dist/commands/gemini-apikey.js +70 -0
  27. package/dist/commands/login.js +885 -0
  28. package/dist/commands/mcp.js +239 -0
  29. package/dist/commands/memory-snapshot.js +24 -0
  30. package/dist/commands/mention-mode.js +44 -0
  31. package/dist/commands/merge-worktree.js +159 -0
  32. package/dist/commands/model-variant.js +364 -0
  33. package/dist/commands/model.js +776 -0
  34. package/dist/commands/new-worktree.js +366 -0
  35. package/dist/commands/paginated-select.js +57 -0
  36. package/dist/commands/permissions.js +274 -0
  37. package/dist/commands/queue.js +206 -0
  38. package/dist/commands/remove-project.js +115 -0
  39. package/dist/commands/restart-opencode-server.js +127 -0
  40. package/dist/commands/resume.js +149 -0
  41. package/dist/commands/run-command.js +79 -0
  42. package/dist/commands/screenshare.js +303 -0
  43. package/dist/commands/screenshare.test.js +20 -0
  44. package/dist/commands/session-id.js +78 -0
  45. package/dist/commands/session.js +176 -0
  46. package/dist/commands/share.js +80 -0
  47. package/dist/commands/tasks.js +205 -0
  48. package/dist/commands/types.js +2 -0
  49. package/dist/commands/undo-redo.js +305 -0
  50. package/dist/commands/unset-model.js +138 -0
  51. package/dist/commands/upgrade.js +42 -0
  52. package/dist/commands/user-command.js +155 -0
  53. package/dist/commands/verbosity.js +125 -0
  54. package/dist/commands/worktree-settings.js +43 -0
  55. package/dist/commands/worktrees.js +410 -0
  56. package/dist/condense-memory.js +33 -0
  57. package/dist/config.js +94 -0
  58. package/dist/context-awareness-plugin.js +363 -0
  59. package/dist/context-awareness-plugin.test.js +124 -0
  60. package/dist/critique-utils.js +95 -0
  61. package/dist/database.js +1310 -0
  62. package/dist/db.js +251 -0
  63. package/dist/db.test.js +138 -0
  64. package/dist/debounce-timeout.js +28 -0
  65. package/dist/debounced-process-flush.js +77 -0
  66. package/dist/discord-bot.js +1008 -0
  67. package/dist/discord-command-registration.js +524 -0
  68. package/dist/discord-urls.js +81 -0
  69. package/dist/discord-utils.js +591 -0
  70. package/dist/discord-utils.test.js +134 -0
  71. package/dist/errors.js +157 -0
  72. package/dist/escape-backticks.test.js +429 -0
  73. package/dist/event-stream-real-capture.e2e.test.js +533 -0
  74. package/dist/eventsource-parser.test.js +327 -0
  75. package/dist/exec-async.js +26 -0
  76. package/dist/external-opencode-sync.js +480 -0
  77. package/dist/format-tables.js +302 -0
  78. package/dist/format-tables.test.js +308 -0
  79. package/dist/forum-sync/config.js +79 -0
  80. package/dist/forum-sync/discord-operations.js +154 -0
  81. package/dist/forum-sync/index.js +5 -0
  82. package/dist/forum-sync/markdown.js +113 -0
  83. package/dist/forum-sync/sync-to-discord.js +417 -0
  84. package/dist/forum-sync/sync-to-files.js +190 -0
  85. package/dist/forum-sync/types.js +53 -0
  86. package/dist/forum-sync/watchers.js +307 -0
  87. package/dist/gateway-proxy-reconnect.e2e.test.js +394 -0
  88. package/dist/gateway-proxy.e2e.test.js +483 -0
  89. package/dist/genai-worker-wrapper.js +111 -0
  90. package/dist/genai-worker.js +311 -0
  91. package/dist/genai.js +232 -0
  92. package/dist/generated/browser.js +17 -0
  93. package/dist/generated/client.js +37 -0
  94. package/dist/generated/commonInputTypes.js +10 -0
  95. package/dist/generated/enums.js +52 -0
  96. package/dist/generated/internal/class.js +49 -0
  97. package/dist/generated/internal/prismaNamespace.js +253 -0
  98. package/dist/generated/internal/prismaNamespaceBrowser.js +223 -0
  99. package/dist/generated/models/bot_api_keys.js +1 -0
  100. package/dist/generated/models/bot_tokens.js +1 -0
  101. package/dist/generated/models/channel_agents.js +1 -0
  102. package/dist/generated/models/channel_directories.js +1 -0
  103. package/dist/generated/models/channel_mention_mode.js +1 -0
  104. package/dist/generated/models/channel_models.js +1 -0
  105. package/dist/generated/models/channel_verbosity.js +1 -0
  106. package/dist/generated/models/channel_worktrees.js +1 -0
  107. package/dist/generated/models/forum_sync_configs.js +1 -0
  108. package/dist/generated/models/global_models.js +1 -0
  109. package/dist/generated/models/ipc_requests.js +1 -0
  110. package/dist/generated/models/part_messages.js +1 -0
  111. package/dist/generated/models/scheduled_tasks.js +1 -0
  112. package/dist/generated/models/session_agents.js +1 -0
  113. package/dist/generated/models/session_events.js +1 -0
  114. package/dist/generated/models/session_models.js +1 -0
  115. package/dist/generated/models/session_start_sources.js +1 -0
  116. package/dist/generated/models/thread_sessions.js +1 -0
  117. package/dist/generated/models/thread_worktrees.js +1 -0
  118. package/dist/generated/models.js +1 -0
  119. package/dist/heap-monitor.js +122 -0
  120. package/dist/hrana-server.js +263 -0
  121. package/dist/hrana-server.test.js +370 -0
  122. package/dist/html-actions.js +123 -0
  123. package/dist/html-actions.test.js +70 -0
  124. package/dist/html-components.js +117 -0
  125. package/dist/html-components.test.js +34 -0
  126. package/dist/image-optimizer-plugin.js +153 -0
  127. package/dist/image-utils.js +112 -0
  128. package/dist/interaction-handler.js +397 -0
  129. package/dist/ipc-polling.js +252 -0
  130. package/dist/ipc-tools-plugin.js +193 -0
  131. package/dist/kimaki-digital-twin.e2e.test.js +161 -0
  132. package/dist/kimaki-opencode-plugin-loading.e2e.test.js +87 -0
  133. package/dist/kimaki-opencode-plugin.js +17 -0
  134. package/dist/kimaki-opencode-plugin.test.js +98 -0
  135. package/dist/limit-heading-depth.js +25 -0
  136. package/dist/limit-heading-depth.test.js +105 -0
  137. package/dist/logger.js +165 -0
  138. package/dist/markdown.js +342 -0
  139. package/dist/markdown.test.js +257 -0
  140. package/dist/message-finish-field.e2e.test.js +165 -0
  141. package/dist/message-formatting.js +413 -0
  142. package/dist/message-formatting.test.js +73 -0
  143. package/dist/message-preprocessing.js +330 -0
  144. package/dist/onboarding-tutorial.js +172 -0
  145. package/dist/onboarding-welcome.js +37 -0
  146. package/dist/openai-realtime.js +224 -0
  147. package/dist/opencode-command-detection.js +65 -0
  148. package/dist/opencode-command-detection.test.js +240 -0
  149. package/dist/opencode-command.js +129 -0
  150. package/dist/opencode-command.test.js +48 -0
  151. package/dist/opencode-interrupt-plugin.js +361 -0
  152. package/dist/opencode-interrupt-plugin.test.js +458 -0
  153. package/dist/opencode.js +861 -0
  154. package/dist/otto/branding.js +22 -0
  155. package/dist/otto/index.js +21 -0
  156. package/dist/parse-permission-rules.test.js +117 -0
  157. package/dist/patch-text-parser.js +97 -0
  158. package/dist/plugin-logger.js +59 -0
  159. package/dist/privacy-sanitizer.js +105 -0
  160. package/dist/queue-advanced-abort.e2e.test.js +293 -0
  161. package/dist/queue-advanced-action-buttons.e2e.test.js +206 -0
  162. package/dist/queue-advanced-e2e-setup.js +786 -0
  163. package/dist/queue-advanced-footer.e2e.test.js +472 -0
  164. package/dist/queue-advanced-model-switch.e2e.test.js +299 -0
  165. package/dist/queue-advanced-permissions-typing.e2e.test.js +180 -0
  166. package/dist/queue-advanced-question.e2e.test.js +261 -0
  167. package/dist/queue-advanced-typing-interrupt.e2e.test.js +114 -0
  168. package/dist/queue-advanced-typing.e2e.test.js +153 -0
  169. package/dist/queue-drain-after-interactive-ui.e2e.test.js +119 -0
  170. package/dist/queue-interrupt-drain.e2e.test.js +135 -0
  171. package/dist/queue-question-select-drain.e2e.test.js +120 -0
  172. package/dist/runtime-idle-sweeper.js +52 -0
  173. package/dist/runtime-lifecycle.e2e.test.js +508 -0
  174. package/dist/sentry.js +23 -0
  175. package/dist/session-handler/agent-utils.js +67 -0
  176. package/dist/session-handler/event-stream-state.js +420 -0
  177. package/dist/session-handler/event-stream-state.test.js +563 -0
  178. package/dist/session-handler/model-utils.js +124 -0
  179. package/dist/session-handler/opencode-session-event-log.js +94 -0
  180. package/dist/session-handler/thread-runtime-state.js +104 -0
  181. package/dist/session-handler/thread-session-runtime.js +3258 -0
  182. package/dist/session-handler.js +9 -0
  183. package/dist/session-search.js +100 -0
  184. package/dist/session-search.test.js +40 -0
  185. package/dist/session-title-rename.test.js +80 -0
  186. package/dist/startup-service.js +153 -0
  187. package/dist/startup-time.e2e.test.js +296 -0
  188. package/dist/store.js +17 -0
  189. package/dist/system-message.js +613 -0
  190. package/dist/system-message.test.js +602 -0
  191. package/dist/task-runner.js +295 -0
  192. package/dist/task-schedule.js +209 -0
  193. package/dist/task-schedule.test.js +71 -0
  194. package/dist/test-utils.js +299 -0
  195. package/dist/thinking-utils.js +35 -0
  196. package/dist/thread-message-queue.e2e.test.js +999 -0
  197. package/dist/tools.js +357 -0
  198. package/dist/undo-redo.e2e.test.js +161 -0
  199. package/dist/unnest-code-blocks.js +146 -0
  200. package/dist/unnest-code-blocks.test.js +673 -0
  201. package/dist/upgrade.js +114 -0
  202. package/dist/utils.js +144 -0
  203. package/dist/voice-attachment.js +34 -0
  204. package/dist/voice-handler.js +646 -0
  205. package/dist/voice-message.e2e.test.js +1021 -0
  206. package/dist/voice.js +447 -0
  207. package/dist/voice.test.js +235 -0
  208. package/dist/wait-session.js +94 -0
  209. package/dist/websockify.js +69 -0
  210. package/dist/worker-types.js +4 -0
  211. package/dist/worktree-lifecycle.e2e.test.js +308 -0
  212. package/dist/worktree-utils.js +3 -0
  213. package/dist/worktrees.js +929 -0
  214. package/dist/worktrees.test.js +189 -0
  215. package/dist/xml.js +92 -0
  216. package/dist/xml.test.js +32 -0
  217. package/package.json +98 -0
  218. package/schema.prisma +295 -0
  219. package/skills/batch/SKILL.md +87 -0
  220. package/skills/critique/SKILL.md +112 -0
  221. package/skills/egaki/SKILL.md +100 -0
  222. package/skills/errore/SKILL.md +647 -0
  223. package/skills/event-sourcing-state/SKILL.md +252 -0
  224. package/skills/gitchamber/SKILL.md +93 -0
  225. package/skills/goke/SKILL.md +644 -0
  226. package/skills/jitter/EDITOR.md +219 -0
  227. package/skills/jitter/EXPORT-INTERNALS.md +309 -0
  228. package/skills/jitter/SKILL.md +158 -0
  229. package/skills/jitter/jitter-clipboard.json +1042 -0
  230. package/skills/jitter/package.json +14 -0
  231. package/skills/jitter/tsconfig.json +15 -0
  232. package/skills/jitter/utils/actions.ts +212 -0
  233. package/skills/jitter/utils/export.ts +114 -0
  234. package/skills/jitter/utils/index.ts +141 -0
  235. package/skills/jitter/utils/snapshot.ts +154 -0
  236. package/skills/jitter/utils/traverse.ts +246 -0
  237. package/skills/jitter/utils/types.ts +279 -0
  238. package/skills/jitter/utils/wait.ts +133 -0
  239. package/skills/lintcn/SKILL.md +873 -0
  240. package/skills/new-skill/SKILL.md +211 -0
  241. package/skills/npm-package/SKILL.md +239 -0
  242. package/skills/playwriter/SKILL.md +35 -0
  243. package/skills/proxyman/SKILL.md +215 -0
  244. package/skills/security-review/SKILL.md +208 -0
  245. package/skills/simplify/SKILL.md +58 -0
  246. package/skills/spiceflow/SKILL.md +14 -0
  247. package/skills/termcast/SKILL.md +945 -0
  248. package/skills/tuistory/SKILL.md +250 -0
  249. package/skills/usecomputer/SKILL.md +264 -0
  250. package/skills/x-articles/SKILL.md +554 -0
  251. package/skills/zele/SKILL.md +112 -0
  252. package/skills/zustand-centralized-state/SKILL.md +1004 -0
  253. package/src/agent-model.e2e.test.ts +976 -0
  254. package/src/ai-tool-to-genai.test.ts +296 -0
  255. package/src/ai-tool-to-genai.ts +283 -0
  256. package/src/ai-tool.ts +39 -0
  257. package/src/anthropic-auth-plugin.test.ts +159 -0
  258. package/src/anthropic-auth-plugin.ts +861 -0
  259. package/src/anthropic-auth-state.ts +282 -0
  260. package/src/bin.ts +111 -0
  261. package/src/channel-management.ts +334 -0
  262. package/src/cli-parsing.test.ts +195 -0
  263. package/src/cli-send-thread.e2e.test.ts +464 -0
  264. package/src/cli.ts +4581 -0
  265. package/src/commands/abort.ts +89 -0
  266. package/src/commands/action-buttons.ts +364 -0
  267. package/src/commands/add-project.ts +149 -0
  268. package/src/commands/agent.ts +473 -0
  269. package/src/commands/ask-question.ts +390 -0
  270. package/src/commands/btw.ts +164 -0
  271. package/src/commands/compact.ts +157 -0
  272. package/src/commands/context-usage.ts +199 -0
  273. package/src/commands/create-new-project.ts +190 -0
  274. package/src/commands/diff.ts +91 -0
  275. package/src/commands/file-upload.ts +389 -0
  276. package/src/commands/fork.ts +321 -0
  277. package/src/commands/gemini-apikey.ts +104 -0
  278. package/src/commands/login.ts +1173 -0
  279. package/src/commands/mcp.ts +307 -0
  280. package/src/commands/memory-snapshot.ts +30 -0
  281. package/src/commands/mention-mode.ts +68 -0
  282. package/src/commands/merge-worktree.ts +223 -0
  283. package/src/commands/model-variant.ts +483 -0
  284. package/src/commands/model.ts +1053 -0
  285. package/src/commands/new-worktree.ts +510 -0
  286. package/src/commands/paginated-select.ts +81 -0
  287. package/src/commands/permissions.ts +397 -0
  288. package/src/commands/queue.ts +271 -0
  289. package/src/commands/remove-project.ts +155 -0
  290. package/src/commands/restart-opencode-server.ts +162 -0
  291. package/src/commands/resume.ts +230 -0
  292. package/src/commands/run-command.ts +123 -0
  293. package/src/commands/screenshare.test.ts +30 -0
  294. package/src/commands/screenshare.ts +366 -0
  295. package/src/commands/session-id.ts +109 -0
  296. package/src/commands/session.ts +227 -0
  297. package/src/commands/share.ts +106 -0
  298. package/src/commands/tasks.ts +293 -0
  299. package/src/commands/types.ts +25 -0
  300. package/src/commands/undo-redo.ts +386 -0
  301. package/src/commands/unset-model.ts +173 -0
  302. package/src/commands/upgrade.ts +52 -0
  303. package/src/commands/user-command.ts +198 -0
  304. package/src/commands/verbosity.ts +173 -0
  305. package/src/commands/worktree-settings.ts +70 -0
  306. package/src/commands/worktrees.ts +552 -0
  307. package/src/condense-memory.ts +36 -0
  308. package/src/config.ts +111 -0
  309. package/src/context-awareness-plugin.test.ts +142 -0
  310. package/src/context-awareness-plugin.ts +510 -0
  311. package/src/critique-utils.ts +139 -0
  312. package/src/database.ts +1876 -0
  313. package/src/db.test.ts +162 -0
  314. package/src/db.ts +286 -0
  315. package/src/debounce-timeout.ts +43 -0
  316. package/src/debounced-process-flush.ts +104 -0
  317. package/src/discord-bot.ts +1330 -0
  318. package/src/discord-command-registration.ts +693 -0
  319. package/src/discord-urls.ts +88 -0
  320. package/src/discord-utils.test.ts +153 -0
  321. package/src/discord-utils.ts +800 -0
  322. package/src/errors.ts +201 -0
  323. package/src/escape-backticks.test.ts +469 -0
  324. package/src/event-stream-real-capture.e2e.test.ts +692 -0
  325. package/src/eventsource-parser.test.ts +351 -0
  326. package/src/exec-async.ts +35 -0
  327. package/src/external-opencode-sync.ts +685 -0
  328. package/src/format-tables.test.ts +335 -0
  329. package/src/format-tables.ts +445 -0
  330. package/src/forum-sync/config.ts +92 -0
  331. package/src/forum-sync/discord-operations.ts +241 -0
  332. package/src/forum-sync/index.ts +9 -0
  333. package/src/forum-sync/markdown.ts +172 -0
  334. package/src/forum-sync/sync-to-discord.ts +595 -0
  335. package/src/forum-sync/sync-to-files.ts +294 -0
  336. package/src/forum-sync/types.ts +175 -0
  337. package/src/forum-sync/watchers.ts +454 -0
  338. package/src/gateway-proxy-reconnect.e2e.test.ts +523 -0
  339. package/src/gateway-proxy.e2e.test.ts +640 -0
  340. package/src/genai-worker-wrapper.ts +164 -0
  341. package/src/genai-worker.ts +386 -0
  342. package/src/genai.ts +321 -0
  343. package/src/generated/browser.ts +114 -0
  344. package/src/generated/client.ts +138 -0
  345. package/src/generated/commonInputTypes.ts +736 -0
  346. package/src/generated/enums.ts +88 -0
  347. package/src/generated/internal/class.ts +384 -0
  348. package/src/generated/internal/prismaNamespace.ts +2386 -0
  349. package/src/generated/internal/prismaNamespaceBrowser.ts +326 -0
  350. package/src/generated/models/bot_api_keys.ts +1288 -0
  351. package/src/generated/models/bot_tokens.ts +1656 -0
  352. package/src/generated/models/channel_agents.ts +1256 -0
  353. package/src/generated/models/channel_directories.ts +1859 -0
  354. package/src/generated/models/channel_mention_mode.ts +1300 -0
  355. package/src/generated/models/channel_models.ts +1288 -0
  356. package/src/generated/models/channel_verbosity.ts +1228 -0
  357. package/src/generated/models/channel_worktrees.ts +1300 -0
  358. package/src/generated/models/forum_sync_configs.ts +1452 -0
  359. package/src/generated/models/global_models.ts +1288 -0
  360. package/src/generated/models/ipc_requests.ts +1485 -0
  361. package/src/generated/models/part_messages.ts +1302 -0
  362. package/src/generated/models/scheduled_tasks.ts +2320 -0
  363. package/src/generated/models/session_agents.ts +1086 -0
  364. package/src/generated/models/session_events.ts +1439 -0
  365. package/src/generated/models/session_models.ts +1114 -0
  366. package/src/generated/models/session_start_sources.ts +1408 -0
  367. package/src/generated/models/thread_sessions.ts +1781 -0
  368. package/src/generated/models/thread_worktrees.ts +1356 -0
  369. package/src/generated/models.ts +30 -0
  370. package/src/heap-monitor.ts +152 -0
  371. package/src/hrana-server.test.ts +434 -0
  372. package/src/hrana-server.ts +314 -0
  373. package/src/html-actions.test.ts +87 -0
  374. package/src/html-actions.ts +174 -0
  375. package/src/html-components.test.ts +38 -0
  376. package/src/html-components.ts +181 -0
  377. package/src/image-optimizer-plugin.ts +194 -0
  378. package/src/image-utils.ts +149 -0
  379. package/src/interaction-handler.ts +576 -0
  380. package/src/ipc-polling.ts +326 -0
  381. package/src/ipc-tools-plugin.ts +236 -0
  382. package/src/kimaki-digital-twin.e2e.test.ts +199 -0
  383. package/src/kimaki-opencode-plugin-loading.e2e.test.ts +109 -0
  384. package/src/kimaki-opencode-plugin.test.ts +108 -0
  385. package/src/kimaki-opencode-plugin.ts +18 -0
  386. package/src/limit-heading-depth.test.ts +116 -0
  387. package/src/limit-heading-depth.ts +26 -0
  388. package/src/logger.ts +208 -0
  389. package/src/markdown.test.ts +308 -0
  390. package/src/markdown.ts +410 -0
  391. package/src/message-finish-field.e2e.test.ts +192 -0
  392. package/src/message-formatting.test.ts +81 -0
  393. package/src/message-formatting.ts +533 -0
  394. package/src/message-preprocessing.ts +455 -0
  395. package/src/onboarding-tutorial.ts +176 -0
  396. package/src/onboarding-welcome.ts +49 -0
  397. package/src/openai-realtime.ts +358 -0
  398. package/src/opencode-command-detection.test.ts +307 -0
  399. package/src/opencode-command-detection.ts +76 -0
  400. package/src/opencode-command.test.ts +70 -0
  401. package/src/opencode-command.ts +188 -0
  402. package/src/opencode-interrupt-plugin.test.ts +677 -0
  403. package/src/opencode-interrupt-plugin.ts +477 -0
  404. package/src/opencode.ts +1110 -0
  405. package/src/otto/branding.ts +23 -0
  406. package/src/otto/index.ts +22 -0
  407. package/src/parse-permission-rules.test.ts +127 -0
  408. package/src/patch-text-parser.ts +107 -0
  409. package/src/plugin-logger.ts +68 -0
  410. package/src/privacy-sanitizer.ts +142 -0
  411. package/src/queue-advanced-abort.e2e.test.ts +382 -0
  412. package/src/queue-advanced-action-buttons.e2e.test.ts +268 -0
  413. package/src/queue-advanced-e2e-setup.ts +873 -0
  414. package/src/queue-advanced-footer.e2e.test.ts +576 -0
  415. package/src/queue-advanced-model-switch.e2e.test.ts +383 -0
  416. package/src/queue-advanced-permissions-typing.e2e.test.ts +245 -0
  417. package/src/queue-advanced-question.e2e.test.ts +316 -0
  418. package/src/queue-advanced-typing-interrupt.e2e.test.ts +146 -0
  419. package/src/queue-advanced-typing.e2e.test.ts +199 -0
  420. package/src/queue-drain-after-interactive-ui.e2e.test.ts +151 -0
  421. package/src/queue-interrupt-drain.e2e.test.ts +166 -0
  422. package/src/queue-question-select-drain.e2e.test.ts +152 -0
  423. package/src/runtime-idle-sweeper.ts +76 -0
  424. package/src/runtime-lifecycle.e2e.test.ts +641 -0
  425. package/src/schema.sql +173 -0
  426. package/src/sentry.ts +26 -0
  427. package/src/session-handler/agent-utils.ts +97 -0
  428. package/src/session-handler/event-stream-fixtures/real-session-action-buttons.jsonl +45 -0
  429. package/src/session-handler/event-stream-fixtures/real-session-footer-suppressed-on-pre-idle-interrupt.jsonl +40 -0
  430. package/src/session-handler/event-stream-fixtures/real-session-permission-external-file.jsonl +23 -0
  431. package/src/session-handler/event-stream-fixtures/real-session-task-normal.jsonl +22 -0
  432. package/src/session-handler/event-stream-fixtures/real-session-task-three-parallel-sleeps.jsonl +277 -0
  433. package/src/session-handler/event-stream-fixtures/real-session-task-user-interruption.jsonl +46 -0
  434. package/src/session-handler/event-stream-fixtures/session-abort-after-idle-race.jsonl +21 -0
  435. package/src/session-handler/event-stream-fixtures/session-concurrent-messages-serialized.jsonl +56 -0
  436. package/src/session-handler/event-stream-fixtures/session-explicit-abort.jsonl +44 -0
  437. package/src/session-handler/event-stream-fixtures/session-normal-completion.jsonl +29 -0
  438. package/src/session-handler/event-stream-fixtures/session-tool-call-noisy-stream.jsonl +29 -0
  439. package/src/session-handler/event-stream-fixtures/session-two-completions-same-session.jsonl +50 -0
  440. package/src/session-handler/event-stream-fixtures/session-user-interruption.jsonl +59 -0
  441. package/src/session-handler/event-stream-fixtures/session-voice-queued-followup.jsonl +52 -0
  442. package/src/session-handler/event-stream-state.test.ts +645 -0
  443. package/src/session-handler/event-stream-state.ts +608 -0
  444. package/src/session-handler/model-utils.ts +183 -0
  445. package/src/session-handler/opencode-session-event-log.ts +130 -0
  446. package/src/session-handler/thread-runtime-state.ts +212 -0
  447. package/src/session-handler/thread-session-runtime.ts +4281 -0
  448. package/src/session-handler.ts +15 -0
  449. package/src/session-search.test.ts +50 -0
  450. package/src/session-search.ts +148 -0
  451. package/src/session-title-rename.test.ts +112 -0
  452. package/src/startup-service.ts +200 -0
  453. package/src/startup-time.e2e.test.ts +373 -0
  454. package/src/store.ts +122 -0
  455. package/src/system-message.test.ts +612 -0
  456. package/src/system-message.ts +723 -0
  457. package/src/task-runner.ts +421 -0
  458. package/src/task-schedule.test.ts +84 -0
  459. package/src/task-schedule.ts +311 -0
  460. package/src/test-utils.ts +435 -0
  461. package/src/thinking-utils.ts +61 -0
  462. package/src/thread-message-queue.e2e.test.ts +1219 -0
  463. package/src/tools.ts +430 -0
  464. package/src/undici.d.ts +12 -0
  465. package/src/undo-redo.e2e.test.ts +209 -0
  466. package/src/unnest-code-blocks.test.ts +713 -0
  467. package/src/unnest-code-blocks.ts +185 -0
  468. package/src/upgrade.ts +127 -0
  469. package/src/utils.ts +212 -0
  470. package/src/voice-attachment.ts +51 -0
  471. package/src/voice-handler.ts +908 -0
  472. package/src/voice-message.e2e.test.ts +1255 -0
  473. package/src/voice.test.ts +281 -0
  474. package/src/voice.ts +627 -0
  475. package/src/wait-session.ts +147 -0
  476. package/src/websockify.ts +101 -0
  477. package/src/worker-types.ts +64 -0
  478. package/src/worktree-lifecycle.e2e.test.ts +391 -0
  479. package/src/worktree-utils.ts +4 -0
  480. package/src/worktrees.test.ts +223 -0
  481. package/src/worktrees.ts +1294 -0
  482. package/src/xml.test.ts +38 -0
  483. package/src/xml.ts +121 -0
@@ -0,0 +1,483 @@
1
+ // /model-variant command — quickly change the thinking level variant for the current model.
2
+ // Shows both the variant picker and scope picker in a single reply (two action rows)
3
+ // so the user can select both without waiting for sequential menus.
4
+ //
5
+ // Cross-menu state: Discord doesn't expose already-selected values on sibling
6
+ // select menus in the same message. We track partial selections in the context
7
+ // Map. Whichever menu fires second sees the first selection stored and applies.
8
+
9
+ import {
10
+ ChatInputCommandInteraction,
11
+ StringSelectMenuInteraction,
12
+ StringSelectMenuBuilder,
13
+ ActionRowBuilder,
14
+ ChannelType,
15
+ type ThreadChannel,
16
+ type TextChannel,
17
+ MessageFlags,
18
+ } from 'discord.js'
19
+ import crypto from 'node:crypto'
20
+ import {
21
+ setChannelModel,
22
+ setSessionModel,
23
+ getThreadSession,
24
+ setGlobalModel,
25
+ getVariantCascade,
26
+ } from '../database.js'
27
+ import { initializeOpencodeForDirectory } from '../opencode.js'
28
+ import { resolveTextChannel, getKimakiMetadata } from '../discord-utils.js'
29
+ import {
30
+ getCurrentModelInfo,
31
+ ensureSessionPreferencesSnapshot,
32
+ type CurrentModelInfo,
33
+ } from './model.js'
34
+ import { getRuntime } from '../session-handler/thread-session-runtime.js'
35
+ import { getThinkingValuesForModel } from '../thinking-utils.js'
36
+ import { createLogger, LogPrefix } from '../logger.js'
37
+
38
+ const logger = createLogger(LogPrefix.MODEL)
39
+
40
+ type PendingVariantContext = {
41
+ dir: string
42
+ channelId: string
43
+ sessionId?: string
44
+ isThread: boolean
45
+ thread?: ThreadChannel
46
+ appId: string
47
+ /** Full model ID (provider/model) that stays constant */
48
+ modelId: string
49
+ providerId: string
50
+ modelName: string
51
+ providerName: string
52
+ availableVariants: string[]
53
+ currentVariant?: string
54
+ /** Partial selection tracking — set when user picks variant before scope */
55
+ selectedVariant?: string | null
56
+ /** Partial selection tracking — set when user picks scope before variant */
57
+ selectedScope?: string
58
+ }
59
+
60
+ const pendingVariantContexts = new Map<string, PendingVariantContext>()
61
+
62
+ /** 10 minute TTL for pending contexts to prevent unbounded map growth */
63
+ const CONTEXT_TTL_MS = 10 * 60 * 1000
64
+
65
+ type VariantScope = 'session' | 'channel' | 'global'
66
+
67
+ function isVariantScope(value: string): value is VariantScope {
68
+ return value === 'session' || value === 'channel' || value === 'global'
69
+ }
70
+
71
+ function formatSourceLabel(info: CurrentModelInfo): string {
72
+ switch (info.type) {
73
+ case 'session':
74
+ return 'thread override'
75
+ case 'agent':
76
+ return `agent "${info.agentName}"`
77
+ case 'channel':
78
+ return 'channel override'
79
+ case 'global':
80
+ return 'global default'
81
+ case 'opencode-config':
82
+ case 'opencode-recent':
83
+ case 'opencode-provider-default':
84
+ return 'opencode default'
85
+ case 'none':
86
+ return 'none'
87
+ }
88
+ }
89
+
90
+ export async function handleModelVariantCommand({
91
+ interaction,
92
+ appId,
93
+ }: {
94
+ interaction: ChatInputCommandInteraction
95
+ appId: string
96
+ }): Promise<void> {
97
+ await interaction.deferReply({ flags: MessageFlags.Ephemeral })
98
+
99
+ const channel = interaction.channel
100
+ if (!channel) {
101
+ await interaction.editReply({
102
+ content: 'This command can only be used in a channel',
103
+ })
104
+ return
105
+ }
106
+
107
+ const isThread = [
108
+ ChannelType.PublicThread,
109
+ ChannelType.PrivateThread,
110
+ ChannelType.AnnouncementThread,
111
+ ].includes(channel.type)
112
+
113
+ let projectDirectory: string | undefined
114
+ let targetChannelId: string
115
+ let sessionId: string | undefined
116
+
117
+ if (isThread) {
118
+ const thread = channel as ThreadChannel
119
+ const [textChannel, threadSessionId] = await Promise.all([
120
+ resolveTextChannel(thread),
121
+ getThreadSession(thread.id),
122
+ ])
123
+ const metadata = await getKimakiMetadata(textChannel)
124
+ projectDirectory = metadata.projectDirectory
125
+ targetChannelId = textChannel?.id || channel.id
126
+ sessionId = threadSessionId
127
+ } else if (channel.type === ChannelType.GuildText) {
128
+ const textChannel = channel as TextChannel
129
+ const metadata = await getKimakiMetadata(textChannel)
130
+ projectDirectory = metadata.projectDirectory
131
+ targetChannelId = channel.id
132
+ } else {
133
+ await interaction.editReply({
134
+ content: 'This command can only be used in text channels or threads',
135
+ })
136
+ return
137
+ }
138
+
139
+ if (!projectDirectory) {
140
+ await interaction.editReply({
141
+ content: 'This channel is not configured with a project directory',
142
+ })
143
+ return
144
+ }
145
+
146
+ const getClient = await initializeOpencodeForDirectory(projectDirectory)
147
+ if (getClient instanceof Error) {
148
+ await interaction.editReply({ content: getClient.message })
149
+ return
150
+ }
151
+
152
+ if (isThread && sessionId) {
153
+ await ensureSessionPreferencesSnapshot({
154
+ sessionId,
155
+ channelId: targetChannelId,
156
+ appId,
157
+ getClient,
158
+ })
159
+ }
160
+
161
+ const [currentModelInfo, cascadeVariant, providersResponse] =
162
+ await Promise.all([
163
+ getCurrentModelInfo({
164
+ sessionId,
165
+ channelId: targetChannelId,
166
+ appId,
167
+ getClient,
168
+ }),
169
+ getVariantCascade({
170
+ sessionId,
171
+ channelId: targetChannelId,
172
+ appId,
173
+ }),
174
+ getClient().provider.list({ directory: projectDirectory }),
175
+ ])
176
+
177
+ if (currentModelInfo.type === 'none') {
178
+ await interaction.editReply({
179
+ content: 'No model configured. Use `/model` to set one first.',
180
+ })
181
+ return
182
+ }
183
+
184
+ if (!providersResponse.data) {
185
+ await interaction.editReply({ content: 'Failed to fetch providers' })
186
+ return
187
+ }
188
+
189
+ const { providerID, modelID, model: fullModelId } = currentModelInfo
190
+ const sourceLabel = formatSourceLabel(currentModelInfo)
191
+ const variantLabel = cascadeVariant ? ` (${cascadeVariant})` : ''
192
+
193
+ const provider = providersResponse.data.all.find((p) => {
194
+ return p.id === providerID
195
+ })
196
+ const providerName = provider?.name || providerID
197
+
198
+ const variants = getThinkingValuesForModel({
199
+ providers: providersResponse.data.all,
200
+ providerId: providerID,
201
+ modelId: modelID,
202
+ })
203
+
204
+ const statusText = `**Current model:** \`${fullModelId}\`${variantLabel} — ${sourceLabel}`
205
+
206
+ if (variants.length === 0) {
207
+ await interaction.editReply({
208
+ content: `${statusText}\nThis model doesn't support thinking level variants.`,
209
+ })
210
+ return
211
+ }
212
+
213
+ const contextHash = crypto.randomBytes(8).toString('hex')
214
+ pendingVariantContexts.set(contextHash, {
215
+ dir: projectDirectory,
216
+ channelId: targetChannelId,
217
+ sessionId,
218
+ isThread,
219
+ thread: isThread ? (channel as ThreadChannel) : undefined,
220
+ appId,
221
+ modelId: fullModelId,
222
+ providerId: providerID,
223
+ modelName: modelID,
224
+ providerName,
225
+ availableVariants: variants,
226
+ currentVariant: cascadeVariant,
227
+ })
228
+ setTimeout(() => {
229
+ pendingVariantContexts.delete(contextHash)
230
+ }, CONTEXT_TTL_MS)
231
+
232
+ const variantOptions = [
233
+ {
234
+ label: 'None (default)',
235
+ value: '__none__',
236
+ description: 'Use the model without a specific thinking level',
237
+ default: !cascadeVariant,
238
+ },
239
+ ...variants.slice(0, 24).map((v: string) => ({
240
+ label: v.slice(0, 100),
241
+ value: v,
242
+ description: `Use ${v} thinking`.slice(0, 100),
243
+ default: cascadeVariant === v,
244
+ })),
245
+ ]
246
+
247
+ const variantMenu = new StringSelectMenuBuilder()
248
+ .setCustomId(`variant_quick:${contextHash}`)
249
+ .setPlaceholder('Select a thinking level')
250
+ .addOptions(variantOptions)
251
+
252
+ const scopeOptions = [
253
+ ...(isThread && sessionId
254
+ ? [
255
+ {
256
+ label: 'This session only',
257
+ value: 'session',
258
+ description: 'Override for this thread session only',
259
+ },
260
+ ]
261
+ : []),
262
+ {
263
+ label: 'This channel',
264
+ value: 'channel',
265
+ description: 'Override for this channel (all new sessions)',
266
+ },
267
+ {
268
+ label: 'Global default',
269
+ value: 'global',
270
+ description: 'Set for this channel and as default for all others',
271
+ },
272
+ ]
273
+
274
+ const scopeMenu = new StringSelectMenuBuilder()
275
+ .setCustomId(`variant_scope:${contextHash}`)
276
+ .setPlaceholder('Apply to...')
277
+ .addOptions(scopeOptions)
278
+
279
+ const variantRow =
280
+ new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(variantMenu)
281
+ const scopeRow =
282
+ new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(scopeMenu)
283
+
284
+ await interaction.editReply({
285
+ content: `${statusText}\nSelect a thinking level and where to apply it:`,
286
+ components: [variantRow, scopeRow],
287
+ })
288
+ }
289
+
290
+ /**
291
+ * Handle the variant quick-select interaction.
292
+ * Stores the chosen variant in context. If scope was already picked, applies immediately.
293
+ */
294
+ export async function handleVariantQuickSelectMenu(
295
+ interaction: StringSelectMenuInteraction,
296
+ ): Promise<void> {
297
+ const contextHash = interaction.customId.replace('variant_quick:', '')
298
+ const context = pendingVariantContexts.get(contextHash)
299
+
300
+ if (!context) {
301
+ await interaction.reply({
302
+ content: 'Selection expired. Please run /model-variant again.',
303
+ flags: MessageFlags.Ephemeral,
304
+ })
305
+ return
306
+ }
307
+
308
+ await interaction.deferUpdate()
309
+
310
+ const selected = interaction.values[0]
311
+ if (!selected) {
312
+ return
313
+ }
314
+
315
+ const chosenVariant = selected === '__none__' ? null : selected
316
+ if (chosenVariant !== null && !context.availableVariants.includes(chosenVariant)) {
317
+ pendingVariantContexts.delete(contextHash)
318
+ await interaction.editReply({
319
+ content: 'Invalid variant selection. Please run /model-variant again.',
320
+ components: [],
321
+ })
322
+ return
323
+ }
324
+
325
+ context.selectedVariant = chosenVariant
326
+
327
+ if (context.selectedScope) {
328
+ await applyVariant({
329
+ interaction,
330
+ context,
331
+ variant: chosenVariant,
332
+ scope: context.selectedScope,
333
+ contextHash,
334
+ })
335
+ }
336
+ // Otherwise wait — scope select will see selectedVariant and trigger applyVariant
337
+ }
338
+
339
+ /**
340
+ * Handle the scope select interaction.
341
+ * Stores the chosen scope in context. If variant was already picked, applies immediately.
342
+ */
343
+ export async function handleVariantScopeSelectMenu(
344
+ interaction: StringSelectMenuInteraction,
345
+ ): Promise<void> {
346
+ const contextHash = interaction.customId.replace('variant_scope:', '')
347
+ const context = pendingVariantContexts.get(contextHash)
348
+
349
+ if (!context) {
350
+ await interaction.reply({
351
+ content: 'Selection expired. Please run /model-variant again.',
352
+ flags: MessageFlags.Ephemeral,
353
+ })
354
+ return
355
+ }
356
+
357
+ await interaction.deferUpdate()
358
+
359
+ const selected = interaction.values[0]
360
+ if (!selected) {
361
+ return
362
+ }
363
+
364
+ if (!isVariantScope(selected)) {
365
+ pendingVariantContexts.delete(contextHash)
366
+ await interaction.editReply({
367
+ content: 'Invalid scope selection. Please run /model-variant again.',
368
+ components: [],
369
+ })
370
+ return
371
+ }
372
+
373
+ context.selectedScope = selected
374
+
375
+ if (context.selectedVariant !== undefined) {
376
+ await applyVariant({
377
+ interaction,
378
+ context,
379
+ variant: context.selectedVariant,
380
+ scope: selected,
381
+ contextHash,
382
+ })
383
+ }
384
+ // Otherwise wait — variant select will see selectedScope and trigger applyVariant
385
+ }
386
+
387
+ async function applyVariant({
388
+ interaction,
389
+ context,
390
+ variant,
391
+ scope,
392
+ contextHash,
393
+ }: {
394
+ interaction: StringSelectMenuInteraction
395
+ context: PendingVariantContext
396
+ variant: string | null
397
+ scope: string
398
+ contextHash: string
399
+ }): Promise<void> {
400
+ const modelId = context.modelId
401
+ const variantSuffix = variant ? ` (${variant})` : ''
402
+ const agentTip =
403
+ '\n_Tip: create [agent .md files](https://github.com/remorses/kimaki/blob/main/docs/model-switching.md) in .opencode/agent/ for one-command model switching_'
404
+
405
+ try {
406
+ if (scope === 'session') {
407
+ if (!context.sessionId) {
408
+ pendingVariantContexts.delete(contextHash)
409
+ await interaction.editReply({
410
+ content:
411
+ 'No active session in this thread. Please run /model-variant in a thread with a session.',
412
+ components: [],
413
+ })
414
+ return
415
+ }
416
+ await setSessionModel({
417
+ sessionId: context.sessionId,
418
+ modelId,
419
+ variant,
420
+ })
421
+ logger.log(
422
+ `Set variant ${variant ?? 'none'} for session ${context.sessionId} (model ${modelId})`,
423
+ )
424
+
425
+ let retried = false
426
+ if (context.thread) {
427
+ const runtime = getRuntime(context.thread.id)
428
+ if (runtime) {
429
+ retried = await runtime.retryLastUserPrompt()
430
+ }
431
+ }
432
+
433
+ const retryNote = retried
434
+ ? '\n_Restarting current request with new variant..._'
435
+ : ''
436
+ await interaction.editReply({
437
+ content: `Variant set for this session:\n**${context.providerName}** / **${context.modelName}**${variantSuffix}\n\`${modelId}\`${retryNote}${agentTip}`,
438
+ flags: MessageFlags.SuppressEmbeds,
439
+ components: [],
440
+ })
441
+ } else if (scope === 'global') {
442
+ await setGlobalModel({ appId: context.appId, modelId, variant })
443
+ await setChannelModel({
444
+ channelId: context.channelId,
445
+ modelId,
446
+ variant,
447
+ })
448
+ logger.log(
449
+ `Set global variant ${variant ?? 'none'} for app ${context.appId} and channel ${context.channelId} (model ${modelId})`,
450
+ )
451
+
452
+ await interaction.editReply({
453
+ content: `Variant set for this channel and as global default:\n**${context.providerName}** / **${context.modelName}**${variantSuffix}\n\`${modelId}\`\nAll channels will use this variant (unless they have their own override).${agentTip}`,
454
+ flags: MessageFlags.SuppressEmbeds,
455
+ components: [],
456
+ })
457
+ } else {
458
+ // channel scope
459
+ await setChannelModel({
460
+ channelId: context.channelId,
461
+ modelId,
462
+ variant,
463
+ })
464
+ logger.log(
465
+ `Set channel variant ${variant ?? 'none'} for channel ${context.channelId} (model ${modelId})`,
466
+ )
467
+
468
+ await interaction.editReply({
469
+ content: `Variant set for this channel:\n**${context.providerName}** / **${context.modelName}**${variantSuffix}\n\`${modelId}\`\nAll new sessions in this channel will use this variant.${agentTip}`,
470
+ flags: MessageFlags.SuppressEmbeds,
471
+ components: [],
472
+ })
473
+ }
474
+
475
+ pendingVariantContexts.delete(contextHash)
476
+ } catch (error) {
477
+ logger.error('Error applying variant:', error)
478
+ await interaction.editReply({
479
+ content: `Failed to apply variant: ${error instanceof Error ? error.message : 'Unknown error'}`,
480
+ components: [],
481
+ })
482
+ }
483
+ }