@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,373 @@
1
+ // Measures time-to-ready for the kimaki Discord bot startup.
2
+ // Used as a baseline to track startup performance and guide optimizations
3
+ // for scale-to-zero deployments where cold start time is critical.
4
+ //
5
+ // Measures each phase independently:
6
+ // 1. Hrana server start (DB + lock port)
7
+ // 2. Database init (Prisma connect via HTTP)
8
+ // 3. Discord.js client creation + login (Gateway READY)
9
+ // 4. startDiscordBot (event handlers + markDiscordGatewayReady)
10
+ // 5. OpenCode server startup (spawn + health poll)
11
+ // 6. Total wall-clock time from zero to "bot ready"
12
+ //
13
+ // Uses discord-digital-twin so Gateway READY is instant (no real Discord).
14
+ // OpenCode startup uses deterministic provider (no real LLM).
15
+
16
+ import fs from 'node:fs'
17
+ import path from 'node:path'
18
+ import url from 'node:url'
19
+ import { describe, test, expect, afterAll } from 'vitest'
20
+ import { ChannelType, Client, GatewayIntentBits, Partials } from 'discord.js'
21
+ import { DigitalDiscord } from 'discord-digital-twin/src'
22
+ import {
23
+ buildDeterministicOpencodeConfig,
24
+ type DeterministicMatcher,
25
+ } from 'opencode-deterministic-provider'
26
+ import { setDataDir } from './config.js'
27
+ import { startDiscordBot } from './discord-bot.js'
28
+ import {
29
+ setBotToken,
30
+ initDatabase,
31
+ closeDatabase,
32
+ setChannelDirectory,
33
+ } from './database.js'
34
+ import { startHranaServer, stopHranaServer } from './hrana-server.js'
35
+ import { initializeOpencodeForDirectory, stopOpencodeServer } from './opencode.js'
36
+ import { chooseLockPort, cleanupTestSessions, initTestGitRepo } from './test-utils.js'
37
+
38
+ interface PhaseTimings {
39
+ hranaServerMs: number
40
+ databaseInitMs: number
41
+ discordLoginMs: number
42
+ startDiscordBotMs: number
43
+ opencodeServerMs: number
44
+ totalMs: number
45
+ }
46
+
47
+ function createRunDirectories() {
48
+ const root = path.resolve(process.cwd(), 'tmp', 'startup-time-e2e')
49
+ fs.mkdirSync(root, { recursive: true })
50
+
51
+ const dataDir = fs.mkdtempSync(path.join(root, 'data-'))
52
+ const projectDirectory = path.join(root, 'project')
53
+ fs.mkdirSync(projectDirectory, { recursive: true })
54
+ initTestGitRepo(projectDirectory)
55
+
56
+ return { root, dataDir, projectDirectory }
57
+ }
58
+
59
+ function createDiscordJsClient({ restUrl }: { restUrl: string }) {
60
+ return new Client({
61
+ intents: [
62
+ GatewayIntentBits.Guilds,
63
+ GatewayIntentBits.GuildMessages,
64
+ GatewayIntentBits.MessageContent,
65
+ GatewayIntentBits.GuildVoiceStates,
66
+ ],
67
+ partials: [
68
+ Partials.Channel,
69
+ Partials.Message,
70
+ Partials.User,
71
+ Partials.ThreadMember,
72
+ ],
73
+ rest: {
74
+ api: restUrl,
75
+ version: '10',
76
+ },
77
+ })
78
+ }
79
+
80
+ function createMinimalMatchers(): DeterministicMatcher[] {
81
+ return [
82
+ {
83
+ id: 'startup-test-reply',
84
+ priority: 10,
85
+ when: {
86
+ lastMessageRole: 'user',
87
+ rawPromptIncludes: 'startup-test',
88
+ },
89
+ then: {
90
+ parts: [
91
+ { type: 'stream-start', warnings: [] },
92
+ { type: 'text-start', id: 'startup-reply' },
93
+ { type: 'text-delta', id: 'startup-reply', delta: 'ok' },
94
+ { type: 'text-end', id: 'startup-reply' },
95
+ {
96
+ type: 'finish',
97
+ finishReason: 'stop',
98
+ usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
99
+ },
100
+ ],
101
+ },
102
+ },
103
+ ]
104
+ }
105
+
106
+ const TEST_USER_ID = '900000000000000777'
107
+ const TEXT_CHANNEL_ID = '900000000000000778'
108
+
109
+ describe('startup time measurement', () => {
110
+ let directories: ReturnType<typeof createRunDirectories>
111
+ let discord: DigitalDiscord
112
+ let botClient: Client | null = null
113
+ const testStartTime = Date.now()
114
+
115
+ afterAll(async () => {
116
+ if (directories) {
117
+ await cleanupTestSessions({
118
+ projectDirectory: directories.projectDirectory,
119
+ testStartTime,
120
+ })
121
+ }
122
+
123
+ if (botClient) {
124
+ botClient.destroy()
125
+ }
126
+
127
+ await Promise.all([
128
+ stopOpencodeServer().catch(() => {}),
129
+ closeDatabase().catch(() => {}),
130
+ stopHranaServer().catch(() => {}),
131
+ discord?.stop().catch(() => {}),
132
+ ])
133
+
134
+ delete process.env['KIMAKI_LOCK_PORT']
135
+ delete process.env['KIMAKI_DB_URL']
136
+
137
+ if (directories) {
138
+ fs.rmSync(directories.dataDir, { recursive: true, force: true })
139
+ }
140
+ })
141
+
142
+ test('measures per-phase startup timings', async () => {
143
+ directories = createRunDirectories()
144
+ const lockPort = chooseLockPort({ key: 'startup-time-e2e' })
145
+
146
+ process.env['KIMAKI_LOCK_PORT'] = String(lockPort)
147
+ setDataDir(directories.dataDir)
148
+
149
+ const digitalDiscordDbPath = path.join(
150
+ directories.dataDir,
151
+ 'digital-discord.db',
152
+ )
153
+
154
+ discord = new DigitalDiscord({
155
+ guild: {
156
+ name: 'Startup Time Guild',
157
+ ownerId: TEST_USER_ID,
158
+ },
159
+ channels: [
160
+ {
161
+ id: TEXT_CHANNEL_ID,
162
+ name: 'startup-time',
163
+ type: ChannelType.GuildText,
164
+ },
165
+ ],
166
+ users: [
167
+ {
168
+ id: TEST_USER_ID,
169
+ username: 'startup-tester',
170
+ },
171
+ ],
172
+ dbUrl: `file:${digitalDiscordDbPath}`,
173
+ })
174
+
175
+ await discord.start()
176
+
177
+ // Write deterministic opencode config
178
+ const providerNpm = url
179
+ .pathToFileURL(
180
+ path.resolve(
181
+ process.cwd(),
182
+ '..',
183
+ 'opencode-deterministic-provider',
184
+ 'src',
185
+ 'index.ts',
186
+ ),
187
+ )
188
+ .toString()
189
+
190
+ const opencodeConfig = buildDeterministicOpencodeConfig({
191
+ providerName: 'deterministic-provider',
192
+ providerNpm,
193
+ model: 'deterministic-v2',
194
+ smallModel: 'deterministic-v2',
195
+ settings: {
196
+ strict: false,
197
+ matchers: createMinimalMatchers(),
198
+ },
199
+ })
200
+ fs.writeFileSync(
201
+ path.join(directories.projectDirectory, 'opencode.json'),
202
+ JSON.stringify(opencodeConfig, null, 2),
203
+ )
204
+
205
+ // ── Phase timings ──
206
+ const totalStart = performance.now()
207
+
208
+ // Phase 1: Hrana server
209
+ const hranaStart = performance.now()
210
+ const dbPath = path.join(directories.dataDir, 'discord-sessions.db')
211
+ const hranaResult = await startHranaServer({ dbPath })
212
+ if (hranaResult instanceof Error) {
213
+ throw hranaResult
214
+ }
215
+ process.env['KIMAKI_DB_URL'] = hranaResult
216
+ const hranaMs = performance.now() - hranaStart
217
+
218
+ // Phase 2: Database init
219
+ const dbStart = performance.now()
220
+ await initDatabase()
221
+ await setBotToken(discord.botUserId, discord.botToken)
222
+ await setChannelDirectory({
223
+ channelId: TEXT_CHANNEL_ID,
224
+ directory: directories.projectDirectory,
225
+ channelType: 'text',
226
+ })
227
+ const dbMs = performance.now() - dbStart
228
+
229
+ // Phase 3+4: Discord.js login + startDiscordBot
230
+ // In the real cli.ts flow, login happens first (line 2077), then
231
+ // startDiscordBot is called with the already-logged-in client (line 2130).
232
+ // startDiscordBot calls login() again internally (line 1069) which is
233
+ // a no-op on already-connected clients. We measure them together since
234
+ // that's the real critical path.
235
+ const loginStart = performance.now()
236
+ botClient = createDiscordJsClient({ restUrl: discord.restUrl })
237
+ // Don't pre-login — let startDiscordBot handle login internally.
238
+ // This avoids the double-login overhead that inflates measurements.
239
+ const loginMs = Math.round(performance.now() - loginStart)
240
+
241
+ const botStart = performance.now()
242
+ await startDiscordBot({
243
+ token: discord.botToken,
244
+ appId: discord.botUserId,
245
+ discordClient: botClient,
246
+ })
247
+ const botMs = performance.now() - botStart
248
+
249
+ // Phase 5: OpenCode server startup (biggest bottleneck)
250
+ const opencodeStart = performance.now()
251
+ const opencodeResult = await initializeOpencodeForDirectory(
252
+ directories.projectDirectory,
253
+ )
254
+ if (opencodeResult instanceof Error) {
255
+ throw opencodeResult
256
+ }
257
+ const opencodeMs = performance.now() - opencodeStart
258
+
259
+ const totalMs = performance.now() - totalStart
260
+
261
+ const timings: PhaseTimings = {
262
+ hranaServerMs: Math.round(hranaMs),
263
+ databaseInitMs: Math.round(dbMs),
264
+ discordLoginMs: Math.round(loginMs),
265
+ startDiscordBotMs: Math.round(botMs),
266
+ opencodeServerMs: Math.round(opencodeMs),
267
+ totalMs: Math.round(totalMs),
268
+ }
269
+
270
+ // Print timings for CI/local visibility
271
+ console.log('\n┌─────────────────────────────────────────────┐')
272
+ console.log('│ Kimaki Startup Time Breakdown │')
273
+ console.log('├─────────────────────────────────────────────┤')
274
+ console.log(`│ Hrana server: ${String(timings.hranaServerMs).padStart(6)} ms │`)
275
+ console.log(`│ Database init: ${String(timings.databaseInitMs).padStart(6)} ms │`)
276
+ console.log(`│ Discord.js login: ${String(timings.discordLoginMs).padStart(6)} ms │`)
277
+ console.log(`│ startDiscordBot: ${String(timings.startDiscordBotMs).padStart(6)} ms │`)
278
+ console.log(`│ OpenCode server: ${String(timings.opencodeServerMs).padStart(6)} ms │`)
279
+ console.log('├─────────────────────────────────────────────┤')
280
+ console.log(`│ TOTAL: ${String(timings.totalMs).padStart(6)} ms │`)
281
+ console.log('└─────────────────────────────────────────────┘\n')
282
+
283
+ // Sanity assertions — these are baselines, not targets yet.
284
+ // Each phase should complete (no infinite hang).
285
+ expect(timings.hranaServerMs).toBeLessThan(5_000)
286
+ expect(timings.databaseInitMs).toBeLessThan(5_000)
287
+ expect(timings.discordLoginMs).toBeLessThan(10_000)
288
+ expect(timings.startDiscordBotMs).toBeLessThan(5_000)
289
+ expect(timings.opencodeServerMs).toBeLessThan(30_000)
290
+ expect(timings.totalMs).toBeLessThan(60_000)
291
+
292
+ // Verify the bot is actually functional by sending a message
293
+ // and getting a response (validates the full pipeline works)
294
+ await discord.channel(TEXT_CHANNEL_ID).user(TEST_USER_ID).sendMessage({
295
+ content: 'startup-test ping',
296
+ })
297
+
298
+ const thread = await discord.channel(TEXT_CHANNEL_ID).waitForThread({
299
+ timeout: 10_000,
300
+ })
301
+
302
+ const reply = await discord.thread(thread.id).waitForBotReply({
303
+ timeout: 30_000,
304
+ })
305
+
306
+ expect(reply.content.length).toBeGreaterThan(0)
307
+ expect(thread.id.length).toBeGreaterThan(0)
308
+ }, 120_000)
309
+
310
+ test('measures parallel startup (discord + opencode simultaneously)', async () => {
311
+ // This test reuses the infrastructure from test 1 (hrana, db already up)
312
+ // to measure what happens when we run Discord login + OpenCode in parallel.
313
+ // In a fresh cold start, hrana+db init would add ~50ms on top.
314
+
315
+ // Stop opencode server from test 1 so we get a fresh measurement
316
+ await stopOpencodeServer().catch(() => {})
317
+
318
+ // Destroy and recreate bot client for a clean login measurement
319
+ if (botClient) {
320
+ botClient.destroy()
321
+ botClient = null
322
+ }
323
+
324
+ // ── Parallel phase: Discord login + OpenCode server simultaneously ──
325
+ const parallelStart = performance.now()
326
+
327
+ const [discordResult, opencodeResult] = await Promise.all([
328
+ // Discord path: create client, login, start bot
329
+ (async () => {
330
+ const loginStart = performance.now()
331
+ const client = createDiscordJsClient({ restUrl: discord.restUrl })
332
+ await startDiscordBot({
333
+ token: discord.botToken,
334
+ appId: discord.botUserId,
335
+ discordClient: client,
336
+ })
337
+ return {
338
+ client,
339
+ totalMs: Math.round(performance.now() - loginStart),
340
+ }
341
+ })(),
342
+ // OpenCode path: spawn server + wait for health
343
+ (async () => {
344
+ const start = performance.now()
345
+ const result = await initializeOpencodeForDirectory(
346
+ directories.projectDirectory,
347
+ )
348
+ if (result instanceof Error) {
349
+ throw result
350
+ }
351
+ return { ms: Math.round(performance.now() - start) }
352
+ })(),
353
+ ])
354
+
355
+ const parallelMs = Math.round(performance.now() - parallelStart)
356
+ botClient = discordResult.client
357
+
358
+ console.log('\n┌─────────────────────────────────────────────┐')
359
+ console.log('│ Parallel Startup Time Breakdown │')
360
+ console.log('├─────────────────────────────────────────────┤')
361
+ console.log(`│ Discord login+bot: ${String(discordResult.totalMs).padStart(6)} ms │`)
362
+ console.log(`│ OpenCode server: ${String(opencodeResult.ms).padStart(6)} ms │`)
363
+ console.log('├─────────────────────────────────────────────┤')
364
+ console.log(`│ PARALLEL TOTAL: ${String(parallelMs).padStart(6)} ms │`)
365
+ console.log(`│ (vs sequential: ${String(discordResult.totalMs + opencodeResult.ms).padStart(6)} ms) │`)
366
+ console.log('└─────────────────────────────────────────────┘\n')
367
+
368
+ // Parallel total should be dominated by the slower path,
369
+ // not the sum of both.
370
+ const maxSingle = Math.max(discordResult.totalMs, opencodeResult.ms)
371
+ expect(parallelMs).toBeLessThan(maxSingle + 500)
372
+ }, 120_000)
373
+ })
package/src/store.ts ADDED
@@ -0,0 +1,122 @@
1
+ // Centralized zustand/vanilla store for global bot state.
2
+ // Replaces scattered module-level `let` variables, process.env mutations,
3
+ // and mutable arrays with a single immutable state atom.
4
+ // See cli/skills/zustand-centralized-state/SKILL.md for the pattern.
5
+
6
+ import { createStore } from 'zustand/vanilla'
7
+ import type { VerbosityLevel } from './generated/client.js'
8
+ import type { ThreadRunState } from './session-handler/thread-runtime-state.js'
9
+
10
+ // Registered user commands, populated by registerCommands() in cli.ts.
11
+ // discordCommandName is the full sanitized Discord slash command name
12
+ // (including -cmd or -skill suffix), while name is the original OpenCode
13
+ // command name (may contain :, /, etc).
14
+ export type RegisteredUserCommand = {
15
+ name: string
16
+ discordCommandName: string
17
+ description: string
18
+ source?: 'command' | 'mcp' | 'skill'
19
+ }
20
+
21
+ // Deterministic transcription config for e2e tests.
22
+ // When set, processVoiceAttachment() skips the real AI model call and
23
+ // returns this canned result after sleeping for delayMs. This lets tests
24
+ // control transcription output, timing, and queue behavior deterministically.
25
+ export type DeterministicTranscriptionConfig = {
26
+ transcription: string
27
+ queueMessage: boolean
28
+ /** Agent name extracted from voice message. Only set if user explicitly requested an agent. */
29
+ agent?: string
30
+ /** Artificial delay before returning the result (ms). Default 0. */
31
+ delayMs?: number
32
+ }
33
+
34
+ export type KimakiState = {
35
+ // ── Config state (set once at CLI startup, read everywhere) ──────────
36
+
37
+ // Path to the kimaki data directory (default ~/.kimaki).
38
+ // Changes: set once at startup by setDataDir() or auto-created on first
39
+ // getDataDir() call. Under vitest, auto-creates a temp dir.
40
+ // Read by: database paths, heap snapshot dir, log file path, hrana server.
41
+ dataDir: string | null
42
+
43
+ // Custom projects directory override (default: <dataDir>/projects).
44
+ // When set via --projects-dir CLI flag, project create commands will
45
+ // create new project folders here instead of ~/.kimaki/projects/.
46
+ // Changes: set once at startup from --projects-dir CLI flag.
47
+ // Read by: config.ts getProjectsDir().
48
+ projectsDir: string | null
49
+
50
+ // Default output verbosity for sessions when no channel-level override
51
+ // exists in the DB. Controls which tool outputs are shown in Discord.
52
+ // Changes: set once at startup from --verbosity CLI flag.
53
+ // Read by: database.ts (fallback in getChannelVerbosity), message formatting.
54
+ defaultVerbosity: VerbosityLevel
55
+
56
+ // When true, the bot only responds to messages that @mention it in text
57
+ // channels (threads are unaffected). Fallback when no channel override in DB.
58
+ // Changes: set once at startup from --mention-mode CLI flag.
59
+ // Read by: database.ts (fallback in getChannelMentionMode), discord-bot.ts guard.
60
+ defaultMentionMode: boolean
61
+
62
+ // Whether critique.work diff URL generation is enabled. When false,
63
+ // the system message omits critique instructions from the AI context.
64
+ // Changes: set once at startup from --no-critique CLI flag.
65
+ // Read by: system-message.ts (conditionally appends critique instructions).
66
+ critiqueEnabled: boolean
67
+
68
+ // Base URL for Discord REST API calls (default https://discord.com).
69
+ // Overridden when using a gateway-proxy or gateway Discord mode.
70
+ // Changes: set by getBotTokenWithMode() which runs at startup and on
71
+ // multiple runtime paths (CLI init, opencode spawn). May be updated
72
+ // whenever bot credentials are re-read from the DB.
73
+ // Read by: discord-urls.ts (getDiscordRestApiUrl), REST client construction.
74
+ discordBaseUrl: string
75
+
76
+ // Service auth token (client_id:client_secret) used to authenticate
77
+ // control-plane requests like /kimaki/wake. Always set at startup in all
78
+ // modes so localhost and internet paths share one auth model.
79
+ // Changes: set in cli.ts after credential resolution and persisted in sqlite.
80
+ // Read by: hrana-server.ts to validate Authorization bearer token.
81
+ gatewayToken: string | null
82
+
83
+ // User-defined slash commands registered with Discord, populated after
84
+ // registerCommands() completes during startup. Maps sanitized Discord
85
+ // command names back to original OpenCode command names.
86
+ // Changes: set once during startup after Discord API registration.
87
+ // Read by: /queue-command autocomplete, user-command handler dispatch.
88
+ registeredUserCommands: RegisteredUserCommand[]
89
+
90
+ // ── Per-thread runtime state ────────────────────────────────────────
91
+ // The main mutable state at runtime. One ThreadRunState per active thread.
92
+ // All mutations are immutable: each updateThread() creates a new Map + new
93
+ // ThreadRunState object via store.setState(). See thread-runtime-state.ts.
94
+ // Changes: on every message enqueue, prompt dispatch, phase transition,
95
+ // abort, and finish.
96
+ // Read by: runtime state helpers (isRunActive, canDispatchNext), session
97
+ // orchestration in ThreadSessionRuntime, /abort and /queue via runtime APIs.
98
+ threads: Map<string, ThreadRunState>
99
+
100
+ // ── Test-only state ─────────────────────────────────────────────────
101
+ test: {
102
+ // When set, processVoiceAttachment() skips the real AI transcription
103
+ // call and returns this canned result after sleeping delayMs.
104
+ // Lets e2e tests control transcription output and timing.
105
+ // Changes: set/cleared by e2e test setup/teardown only.
106
+ // Read by: voice-handler.ts processVoiceAttachment().
107
+ deterministicTranscription: DeterministicTranscriptionConfig | null
108
+ }
109
+ }
110
+
111
+ export const store = createStore<KimakiState>(() => ({
112
+ dataDir: null,
113
+ projectsDir: null,
114
+ defaultVerbosity: 'text_and_essential_tools',
115
+ defaultMentionMode: false,
116
+ critiqueEnabled: true,
117
+ discordBaseUrl: 'https://discord.com',
118
+ gatewayToken: null,
119
+ registeredUserCommands: [],
120
+ threads: new Map(),
121
+ test: { deterministicTranscription: null },
122
+ }))