@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,334 @@
1
+ // Discord channel and category management.
2
+ // Creates and manages Kimaki project channels (text + voice pairs),
3
+ // extracts channel metadata from topic tags, and ensures category structure.
4
+
5
+ import {
6
+ ChannelType,
7
+ type CategoryChannel,
8
+ type Guild,
9
+ type TextChannel,
10
+ } from 'discord.js'
11
+ import fs from 'node:fs'
12
+ import path from 'node:path'
13
+ import {
14
+ getChannelDirectory,
15
+ setChannelDirectory,
16
+ findChannelsByDirectory,
17
+ } from './database.js'
18
+ import { getProjectsDir } from './config.js'
19
+ import { execAsync } from './worktrees.js'
20
+ import { createLogger, LogPrefix } from './logger.js'
21
+
22
+ const logger = createLogger(LogPrefix.CHANNEL)
23
+
24
+ export async function ensureKimakiCategory(
25
+ guild: Guild,
26
+ botName?: string,
27
+ ): Promise<CategoryChannel> {
28
+ // Skip appending bot name if it's already "kimaki" to avoid "Kimaki kimaki"
29
+ const isKimakiBot = botName?.toLowerCase() === 'kimaki'
30
+ const categoryName = botName && !isKimakiBot ? `Kimaki ${botName}` : 'Kimaki'
31
+
32
+ const existingCategory = guild.channels.cache.find(
33
+ (channel): channel is CategoryChannel => {
34
+ if (channel.type !== ChannelType.GuildCategory) {
35
+ return false
36
+ }
37
+
38
+ return channel.name.toLowerCase() === categoryName.toLowerCase()
39
+ },
40
+ )
41
+
42
+ if (existingCategory) {
43
+ return existingCategory
44
+ }
45
+
46
+ return guild.channels.create({
47
+ name: categoryName,
48
+ type: ChannelType.GuildCategory,
49
+ })
50
+ }
51
+
52
+ export async function ensureKimakiAudioCategory(
53
+ guild: Guild,
54
+ botName?: string,
55
+ ): Promise<CategoryChannel> {
56
+ // Skip appending bot name if it's already "kimaki" to avoid "Kimaki Audio kimaki"
57
+ const isKimakiBot = botName?.toLowerCase() === 'kimaki'
58
+ const categoryName =
59
+ botName && !isKimakiBot ? `Kimaki Audio ${botName}` : 'Kimaki Audio'
60
+
61
+ const existingCategory = guild.channels.cache.find(
62
+ (channel): channel is CategoryChannel => {
63
+ if (channel.type !== ChannelType.GuildCategory) {
64
+ return false
65
+ }
66
+
67
+ return channel.name.toLowerCase() === categoryName.toLowerCase()
68
+ },
69
+ )
70
+
71
+ if (existingCategory) {
72
+ return existingCategory
73
+ }
74
+
75
+ return guild.channels.create({
76
+ name: categoryName,
77
+ type: ChannelType.GuildCategory,
78
+ })
79
+ }
80
+
81
+ export async function createProjectChannels({
82
+ guild,
83
+ projectDirectory,
84
+ botName,
85
+ enableVoiceChannels = false,
86
+ }: {
87
+ guild: Guild
88
+ projectDirectory: string
89
+ botName?: string
90
+ enableVoiceChannels?: boolean
91
+ }): Promise<{
92
+ textChannelId: string
93
+ voiceChannelId: string | null
94
+ channelName: string
95
+ }> {
96
+ const baseName = path.basename(projectDirectory)
97
+ const channelName = `${baseName}`
98
+ .toLowerCase()
99
+ .replace(/[^a-z0-9-]/g, '-')
100
+ .slice(0, 100)
101
+
102
+ const kimakiCategory = await ensureKimakiCategory(guild, botName)
103
+
104
+ const textChannel = await guild.channels.create({
105
+ name: channelName,
106
+ type: ChannelType.GuildText,
107
+ parent: kimakiCategory,
108
+ // Channel configuration is stored in SQLite, not in the topic
109
+ })
110
+
111
+ await setChannelDirectory({
112
+ channelId: textChannel.id,
113
+ directory: projectDirectory,
114
+ channelType: 'text',
115
+ })
116
+
117
+ let voiceChannelId: string | null = null
118
+
119
+ if (enableVoiceChannels) {
120
+ const kimakiAudioCategory = await ensureKimakiAudioCategory(guild, botName)
121
+
122
+ const voiceChannel = await guild.channels.create({
123
+ name: channelName,
124
+ type: ChannelType.GuildVoice,
125
+ parent: kimakiAudioCategory,
126
+ })
127
+
128
+ await setChannelDirectory({
129
+ channelId: voiceChannel.id,
130
+ directory: projectDirectory,
131
+ channelType: 'voice',
132
+ })
133
+
134
+ voiceChannelId = voiceChannel.id
135
+ }
136
+
137
+ return {
138
+ textChannelId: textChannel.id,
139
+ voiceChannelId,
140
+ channelName,
141
+ }
142
+ }
143
+
144
+ export type ChannelWithTags = {
145
+ id: string
146
+ name: string
147
+ description: string | null
148
+ kimakiDirectory?: string
149
+ }
150
+
151
+ export async function getChannelsWithDescriptions(
152
+ guild: Guild,
153
+ ): Promise<ChannelWithTags[]> {
154
+ const channels: ChannelWithTags[] = []
155
+
156
+ const textChannels = guild.channels.cache.filter((channel) =>
157
+ channel.isTextBased(),
158
+ )
159
+
160
+ for (const channel of textChannels.values()) {
161
+ const textChannel = channel as TextChannel
162
+ const description = textChannel.topic || null
163
+
164
+ // Get channel config from database instead of parsing XML from topic
165
+ const channelConfig = await getChannelDirectory(textChannel.id)
166
+
167
+ channels.push({
168
+ id: textChannel.id,
169
+ name: textChannel.name,
170
+ description,
171
+ kimakiDirectory: channelConfig?.directory,
172
+ })
173
+ }
174
+
175
+ return channels
176
+ }
177
+
178
+ const DEFAULT_GITIGNORE = `node_modules/
179
+ dist/
180
+ .env
181
+ .env.*
182
+ !.env.example
183
+ .DS_Store
184
+ tmp/
185
+ *.log
186
+ __pycache__/
187
+ *.pyc
188
+ .venv/
189
+ *.egg-info/
190
+ `
191
+
192
+ const DEFAULT_CHANNEL_TOPIC =
193
+ 'General channel for misc tasks with Kimaki. Not connected to a specific OpenCode project or repository.'
194
+
195
+ /**
196
+ * Create (or find) the default "kimaki" channel for general-purpose tasks.
197
+ * Channel name is "kimaki-{botName}" for self-hosted bots, "kimaki" for gateway.
198
+ * Directory is ~/.kimaki/projects/kimaki, git-initialized with a .gitignore.
199
+ *
200
+ * Idempotency: checks the database for an existing channel mapped to the
201
+ * kimaki projects directory. Also scans guild channels by name+category
202
+ * as a fallback for channels created before DB mapping existed.
203
+ */
204
+ export async function createDefaultKimakiChannel({
205
+ guild,
206
+ botName,
207
+ appId,
208
+ isGatewayMode,
209
+ }: {
210
+ guild: Guild
211
+ botName?: string
212
+ appId: string
213
+ isGatewayMode: boolean
214
+ }): Promise<{
215
+ textChannel: TextChannel
216
+ textChannelId: string
217
+ channelName: string
218
+ projectDirectory: string
219
+ } | null> {
220
+ const projectDirectory = path.join(getProjectsDir(), 'kimaki')
221
+
222
+ // Ensure the default kimaki project directory exists before any DB mapping
223
+ // restoration or git setup. Custom data dirs may not have <dataDir>/projects
224
+ // created yet, and later writes assume the full path is present.
225
+ if (!fs.existsSync(projectDirectory)) {
226
+ fs.mkdirSync(projectDirectory, { recursive: true })
227
+ logger.log(`Created default kimaki directory: ${projectDirectory}`)
228
+ }
229
+
230
+ // Hydrate guild channels from API so the cache scan is complete
231
+ try {
232
+ await guild.channels.fetch()
233
+ } catch (error) {
234
+ logger.warn(
235
+ `Could not fetch guild channels for ${guild.name}: ${error instanceof Error ? error.stack : String(error)}`,
236
+ )
237
+ }
238
+
239
+ // 1. Check database for existing channel mapped to this directory.
240
+ // Check ALL mappings (not just the first) since the same directory could
241
+ // have stale rows from deleted channels or other guilds.
242
+ const existingMappings = await findChannelsByDirectory({
243
+ directory: projectDirectory,
244
+ channelType: 'text',
245
+ })
246
+ const mappedChannelInGuild = existingMappings
247
+ .map((row) => guild.channels.cache.get(row.channel_id))
248
+ .find((ch): ch is TextChannel => ch?.type === ChannelType.GuildText)
249
+ if (mappedChannelInGuild) {
250
+ logger.log(`Default kimaki channel already exists: ${mappedChannelInGuild.id}`)
251
+ return null
252
+ }
253
+
254
+ // 2. Fallback: detect existing channel by name+category
255
+ const kimakiCategory = await ensureKimakiCategory(guild, botName)
256
+ const existingByName = guild.channels.cache.find((ch): ch is TextChannel => {
257
+ if (ch.type !== ChannelType.GuildText) {
258
+ return false
259
+ }
260
+ if (ch.parentId !== kimakiCategory.id) {
261
+ return false
262
+ }
263
+ return ch.name === 'kimaki' || ch.name.startsWith('kimaki-')
264
+ })
265
+ if (existingByName) {
266
+ logger.log(
267
+ `Found existing default kimaki channel by name: ${existingByName.id}, restoring DB mapping`,
268
+ )
269
+ await setChannelDirectory({
270
+ channelId: existingByName.id,
271
+ directory: projectDirectory,
272
+ channelType: 'text',
273
+ skipIfExists: true,
274
+ })
275
+ return null
276
+ }
277
+
278
+ // Git init — gracefully skip if git is not installed
279
+ const gitDir = path.join(projectDirectory, '.git')
280
+ if (!fs.existsSync(gitDir)) {
281
+ try {
282
+ await execAsync('git init', { cwd: projectDirectory, timeout: 10_000 })
283
+ logger.log(`Initialized git in: ${projectDirectory}`)
284
+ } catch (error) {
285
+ logger.warn(
286
+ `Could not initialize git in ${projectDirectory}: ${error instanceof Error ? error.stack : String(error)}`,
287
+ )
288
+ }
289
+ }
290
+
291
+ // Write .gitignore if it doesn't exist
292
+ const gitignorePath = path.join(projectDirectory, '.gitignore')
293
+ if (!fs.existsSync(gitignorePath)) {
294
+ fs.writeFileSync(gitignorePath, DEFAULT_GITIGNORE)
295
+ }
296
+
297
+ // Channel name: "kimaki-{botName}" for self-hosted, "kimaki" for gateway
298
+ const channelName = (() => {
299
+ if (isGatewayMode || !botName) {
300
+ return 'kimaki'
301
+ }
302
+ const sanitized = botName
303
+ .toLowerCase()
304
+ .replace(/[^a-z0-9-]/g, '-')
305
+ .replace(/-+/g, '-')
306
+ .replace(/^-|-$/g, '')
307
+ if (!sanitized || sanitized === 'kimaki') {
308
+ return 'kimaki'
309
+ }
310
+ return `kimaki-${sanitized}`.slice(0, 100)
311
+ })()
312
+
313
+ const textChannel = await guild.channels.create({
314
+ name: channelName,
315
+ type: ChannelType.GuildText,
316
+ parent: kimakiCategory,
317
+ topic: DEFAULT_CHANNEL_TOPIC,
318
+ })
319
+
320
+ await setChannelDirectory({
321
+ channelId: textChannel.id,
322
+ directory: projectDirectory,
323
+ channelType: 'text',
324
+ })
325
+
326
+ logger.log(`Created default kimaki channel: #${channelName} (${textChannel.id})`)
327
+
328
+ return {
329
+ textChannel,
330
+ textChannelId: textChannel.id,
331
+ channelName,
332
+ projectDirectory,
333
+ }
334
+ }
@@ -0,0 +1,195 @@
1
+ // Regression tests for CLI argument parsing around Discord ID string preservation.
2
+ import { describe, expect, test } from 'vitest'
3
+ import { goke } from 'goke'
4
+
5
+ function createCliForIdParsing() {
6
+ const cli = goke('kimaki')
7
+
8
+ cli
9
+ .command('send', 'Send a message')
10
+ .option('-c, --channel <channelId>', 'Discord channel ID')
11
+ .option('--thread <threadId>', 'Thread ID')
12
+ .option('--session <sessionId>', 'Session ID')
13
+ .option('--send-at <schedule>', 'Schedule')
14
+
15
+ cli.command('session archive <threadId>', 'Archive a thread')
16
+ cli
17
+ .command('session search <query>', 'Search sessions')
18
+ .option('--channel <channelId>', 'Discord channel ID')
19
+ .option('--project <path>', 'Project path')
20
+ cli
21
+ .command('session export-events-jsonl', 'Export in-memory events to JSONL')
22
+ .option('--session <sessionId>', 'Session ID')
23
+ .option('--out <file>', 'Output path')
24
+
25
+ cli
26
+ .command('add-project', 'Add a project')
27
+ .option('-g, --guild <guildId>', 'Discord guild/server ID')
28
+
29
+ cli.command('task delete <id>', 'Delete task')
30
+ cli.command('anthropic-accounts list', 'List stored Anthropic accounts').hidden()
31
+ cli.command('anthropic-accounts remove <index>', 'Remove stored Anthropic account').hidden()
32
+
33
+ return cli
34
+ }
35
+
36
+ describe('goke CLI ID parsing', () => {
37
+ test('keeps large Discord IDs as strings', () => {
38
+ const cli = createCliForIdParsing()
39
+ const channelId = '1234567890123456789'
40
+ const threadId = '9876543210987654321'
41
+ const sessionId = '1111222233334444555'
42
+
43
+ const channelResult = cli.parse(
44
+ ['node', 'kimaki', 'send', '--channel', channelId],
45
+ {
46
+ run: false,
47
+ },
48
+ )
49
+ expect(channelResult.options.channel).toBe(channelId)
50
+ expect(typeof channelResult.options.channel).toBe('string')
51
+
52
+ const threadResult = cli.parse(
53
+ ['node', 'kimaki', 'send', '--thread', threadId],
54
+ { run: false },
55
+ )
56
+ expect(threadResult.options.thread).toBe(threadId)
57
+ expect(typeof threadResult.options.thread).toBe('string')
58
+
59
+ const sessionResult = cli.parse(
60
+ ['node', 'kimaki', 'send', '--session', sessionId],
61
+ {
62
+ run: false,
63
+ },
64
+ )
65
+ expect(sessionResult.options.session).toBe(sessionId)
66
+ expect(typeof sessionResult.options.session).toBe('string')
67
+ })
68
+
69
+ test('preserves leading zeros in Discord IDs', () => {
70
+ const cli = createCliForIdParsing()
71
+ const guildId = '001230045600789'
72
+
73
+ const result = cli.parse(
74
+ ['node', 'kimaki', 'add-project', '--guild', guildId],
75
+ { run: false },
76
+ )
77
+
78
+ expect(result.options.guild).toBe(guildId)
79
+ expect(typeof result.options.guild).toBe('string')
80
+ })
81
+
82
+ test('keeps session archive thread ID as string', () => {
83
+ const cli = createCliForIdParsing()
84
+ const threadId = '0098765432109876543'
85
+
86
+ const result = cli.parse(
87
+ ['node', 'kimaki', 'session', 'archive', threadId],
88
+ {
89
+ run: false,
90
+ },
91
+ )
92
+
93
+ expect(result.args[0]).toBe(threadId)
94
+ expect(typeof result.args[0]).toBe('string')
95
+ })
96
+
97
+ test('keeps session search regex and channel ID as strings', () => {
98
+ const cli = createCliForIdParsing()
99
+ const channelId = '0012345678901234567'
100
+ const query = '/error\\s+42/i'
101
+
102
+ const result = cli.parse(
103
+ ['node', 'kimaki', 'session', 'search', query, '--channel', channelId],
104
+ {
105
+ run: false,
106
+ },
107
+ )
108
+
109
+ expect(result.args[0]).toBe(query)
110
+ expect(typeof result.args[0]).toBe('string')
111
+ expect(result.options.channel).toBe(channelId)
112
+ expect(typeof result.options.channel).toBe('string')
113
+ })
114
+
115
+ test('keeps session export options as strings', () => {
116
+ const cli = createCliForIdParsing()
117
+ const sessionId = '001111222233334444'
118
+ const outPath = './tmp/session-events.jsonl'
119
+
120
+ const result = cli.parse(
121
+ [
122
+ 'node',
123
+ 'kimaki',
124
+ 'session',
125
+ 'export-events-jsonl',
126
+ '--session',
127
+ sessionId,
128
+ '--out',
129
+ outPath,
130
+ ],
131
+ {
132
+ run: false,
133
+ },
134
+ )
135
+
136
+ expect(result.options.session).toBe(sessionId)
137
+ expect(typeof result.options.session).toBe('string')
138
+ expect(result.options.out).toBe(outPath)
139
+ expect(typeof result.options.out).toBe('string')
140
+ })
141
+
142
+ test('keeps --send-at cron string intact', () => {
143
+ const cli = createCliForIdParsing()
144
+ const cron = '0 9 * * 1'
145
+
146
+ const result = cli.parse(['node', 'kimaki', 'send', '--send-at', cron], {
147
+ run: false,
148
+ })
149
+
150
+ expect(result.options.sendAt).toBe(cron)
151
+ expect(typeof result.options.sendAt).toBe('string')
152
+ })
153
+
154
+ test('keeps task delete ID as string before validation', () => {
155
+ const cli = createCliForIdParsing()
156
+ const taskId = '0012345'
157
+
158
+ const result = cli.parse(['node', 'kimaki', 'task', 'delete', taskId], {
159
+ run: false,
160
+ })
161
+
162
+ expect(result.args[0]).toBe(taskId)
163
+ expect(typeof result.args[0]).toBe('string')
164
+ })
165
+
166
+ test('hidden anthropic account commands still parse', () => {
167
+ const cli = createCliForIdParsing()
168
+
169
+ const result = cli.parse(
170
+ ['node', 'kimaki', 'anthropic-accounts', 'remove', '2'],
171
+ { run: false },
172
+ )
173
+
174
+ expect(result.args[0]).toBe('2')
175
+ expect(typeof result.args[0]).toBe('string')
176
+ })
177
+
178
+ test('hidden anthropic account commands are excluded from help output', () => {
179
+ const stdout = {
180
+ text: '',
181
+ write(data: string | Uint8Array) {
182
+ this.text += String(data)
183
+ },
184
+ }
185
+
186
+ const cli = goke('kimaki', { stdout: stdout as never })
187
+ cli.command('send', 'Send a message')
188
+ cli.command('anthropic-accounts list', 'List stored Anthropic accounts').hidden()
189
+ cli.help()
190
+ cli.parse(['node', 'kimaki', '--help'], { run: false })
191
+
192
+ expect(stdout.text).toContain('send')
193
+ expect(stdout.text).not.toContain('anthropic-accounts')
194
+ })
195
+ })