@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,164 @@
1
+ // Main thread interface for the GenAI worker.
2
+ // Spawns and manages the worker thread, handling message passing for
3
+ // audio input/output, tool call completions, and graceful shutdown.
4
+
5
+ import { Worker } from 'node:worker_threads'
6
+ import type { WorkerInMessage, WorkerOutMessage } from './worker-types.js'
7
+ import { createLogger, LogPrefix } from './logger.js'
8
+ import { notifyError } from './sentry.js'
9
+
10
+ const genaiWorkerLogger = createLogger(LogPrefix.GENAI_WORKER)
11
+ const genaiWrapperLogger = createLogger(LogPrefix.GENAI_WORKER)
12
+
13
+ export interface GenAIWorkerOptions {
14
+ directory: string
15
+ systemMessage?: string
16
+ guildId: string
17
+ channelId: string
18
+ appId: string
19
+ geminiApiKey?: string | null
20
+ onAssistantOpusPacket: (packet: ArrayBuffer) => void
21
+ onAssistantStartSpeaking?: () => void
22
+ onAssistantStopSpeaking?: () => void
23
+ onAssistantInterruptSpeaking?: () => void
24
+ onToolCallCompleted?: (params: {
25
+ sessionId: string
26
+ messageId: string
27
+ data?: unknown
28
+ error?: unknown
29
+ markdown?: string
30
+ }) => void
31
+ onError?: (error: string) => void
32
+ }
33
+
34
+ export interface GenAIWorker {
35
+ sendRealtimeInput(params: {
36
+ audio?: { mimeType: string; data: string }
37
+ audioStreamEnd?: boolean
38
+ }): void
39
+ sendTextInput(text: string): void
40
+ interrupt(): void
41
+ stop(): Promise<void>
42
+ }
43
+
44
+ export function createGenAIWorker(
45
+ options: GenAIWorkerOptions,
46
+ ): Promise<GenAIWorker> {
47
+ return new Promise((resolve, reject) => {
48
+ const worker = new Worker(
49
+ new URL('../dist/genai-worker.js', import.meta.url),
50
+ )
51
+
52
+ // Handle messages from worker
53
+ worker.on('message', (message: WorkerOutMessage) => {
54
+ switch (message.type) {
55
+ case 'assistantOpusPacket':
56
+ options.onAssistantOpusPacket(message.packet)
57
+ break
58
+ case 'assistantStartSpeaking':
59
+ options.onAssistantStartSpeaking?.()
60
+ break
61
+ case 'assistantStopSpeaking':
62
+ options.onAssistantStopSpeaking?.()
63
+ break
64
+ case 'assistantInterruptSpeaking':
65
+ options.onAssistantInterruptSpeaking?.()
66
+ break
67
+ case 'toolCallCompleted':
68
+ options.onToolCallCompleted?.(message)
69
+ break
70
+ case 'error':
71
+ genaiWorkerLogger.error('Error:', message.error)
72
+ options.onError?.(message.error)
73
+ break
74
+ case 'ready':
75
+ genaiWorkerLogger.log('Ready')
76
+ // Resolve with the worker interface
77
+ resolve({
78
+ sendRealtimeInput({ audio, audioStreamEnd }) {
79
+ worker.postMessage({
80
+ type: 'sendRealtimeInput',
81
+ audio,
82
+ audioStreamEnd,
83
+ } satisfies WorkerInMessage)
84
+ },
85
+ sendTextInput(text) {
86
+ worker.postMessage({
87
+ type: 'sendTextInput',
88
+ text,
89
+ } satisfies WorkerInMessage)
90
+ },
91
+ interrupt() {
92
+ worker.postMessage({
93
+ type: 'interrupt',
94
+ } satisfies WorkerInMessage)
95
+ },
96
+ async stop() {
97
+ genaiWrapperLogger.log('Stopping worker...')
98
+ // Send stop message to trigger graceful shutdown
99
+ worker.postMessage({ type: 'stop' } satisfies WorkerInMessage)
100
+
101
+ // Wait for worker to exit gracefully (with timeout)
102
+ await new Promise<void>((resolve) => {
103
+ let resolved = false
104
+
105
+ // Listen for worker exit
106
+ worker.once('exit', (code) => {
107
+ if (!resolved) {
108
+ resolved = true
109
+ genaiWrapperLogger.log(
110
+ `[GENAI WORKER WRAPPER] Worker exited with code ${code}`,
111
+ )
112
+ resolve()
113
+ }
114
+ })
115
+
116
+ // Timeout after 5 seconds and force terminate
117
+ setTimeout(() => {
118
+ if (!resolved) {
119
+ resolved = true
120
+ genaiWrapperLogger.log(
121
+ '[GENAI WORKER WRAPPER] Worker did not exit gracefully, terminating...',
122
+ )
123
+ worker.terminate().then(() => {
124
+ genaiWrapperLogger.log('Worker terminated')
125
+ resolve()
126
+ })
127
+ }
128
+ }, 5000)
129
+ })
130
+ },
131
+ })
132
+ break
133
+ }
134
+ })
135
+
136
+ // Handle worker errors
137
+ worker.on('error', (error) => {
138
+ genaiWorkerLogger.error('Worker error:', error)
139
+ reject(error)
140
+ })
141
+
142
+ worker.on('exit', (code) => {
143
+ if (code !== 0) {
144
+ genaiWorkerLogger.error(`Worker stopped with exit code ${code}`)
145
+ void notifyError(
146
+ new Error(`GenAI worker exited with code ${code}`),
147
+ 'GenAI worker non-zero exit after init',
148
+ )
149
+ }
150
+ })
151
+
152
+ // Send initialization message
153
+ const initMessage: WorkerInMessage = {
154
+ type: 'init',
155
+ directory: options.directory,
156
+ systemMessage: options.systemMessage,
157
+ guildId: options.guildId,
158
+ channelId: options.channelId,
159
+ appId: options.appId,
160
+ geminiApiKey: options.geminiApiKey,
161
+ }
162
+ worker.postMessage(initMessage)
163
+ })
164
+ }
@@ -0,0 +1,386 @@
1
+ // Worker thread for GenAI voice processing.
2
+ // Runs in a separate thread to handle audio encoding/decoding without blocking.
3
+ // Resamples 24kHz GenAI output to 48kHz stereo Opus packets for Discord.
4
+
5
+ import { parentPort, threadId } from 'node:worker_threads'
6
+ import { createWriteStream, type WriteStream } from 'node:fs'
7
+ import path from 'node:path'
8
+ import * as errore from 'errore'
9
+ import { Resampler } from '@purinton/resampler'
10
+ import * as prism from 'prism-media'
11
+ import { startGenAiSession } from './genai.js'
12
+ import type { Session } from '@google/genai'
13
+ import { getTools } from './tools.js'
14
+ import { mkdir } from 'node:fs/promises'
15
+ import type { WorkerInMessage, WorkerOutMessage } from './worker-types.js'
16
+ import { createLogger, formatErrorWithStack, LogPrefix } from './logger.js'
17
+ import { initSentry, notifyError } from './sentry.js'
18
+
19
+ if (!parentPort) {
20
+ throw new Error('This module must be run as a worker thread')
21
+ }
22
+
23
+ const workerLogger = createLogger(`${LogPrefix.WORKER}_${threadId}`)
24
+ workerLogger.log('GenAI worker started')
25
+
26
+ // Initialize Sentry in worker thread (inherits KIMAKI_SENTRY_DSN from parent)
27
+ initSentry()
28
+
29
+ // Define sendError early so it can be used by global handlers
30
+ function sendError(error: string) {
31
+ if (parentPort) {
32
+ parentPort.postMessage({
33
+ type: 'error',
34
+ error,
35
+ } satisfies WorkerOutMessage)
36
+ }
37
+ }
38
+
39
+ // Add global error handlers for the worker thread
40
+ process.on('uncaughtException', (error) => {
41
+ workerLogger.error('Uncaught exception in worker:', error)
42
+ void notifyError(error, 'Uncaught exception in GenAI worker')
43
+ sendError(`Worker crashed: ${error.message}`)
44
+ process.exit(1)
45
+ })
46
+
47
+ process.on('unhandledRejection', (reason, promise) => {
48
+ const formattedReason = formatErrorWithStack(reason)
49
+ workerLogger.error(
50
+ 'Unhandled rejection in worker:',
51
+ formattedReason,
52
+ 'at promise:',
53
+ promise,
54
+ )
55
+ const error =
56
+ reason instanceof Error
57
+ ? reason
58
+ : new Error(formattedReason)
59
+ void notifyError(error, 'Unhandled rejection in GenAI worker')
60
+ sendError(`Worker unhandled rejection: ${formattedReason}`)
61
+ })
62
+
63
+ // Audio configuration
64
+ const AUDIO_CONFIG = {
65
+ inputSampleRate: 24000, // GenAI output
66
+ inputChannels: 1,
67
+ outputSampleRate: 48000, // Discord expects
68
+ outputChannels: 2,
69
+ opusFrameSize: 960, // 20ms at 48kHz
70
+ }
71
+
72
+ // Initialize audio processing components
73
+ const resampler = new Resampler({
74
+ inRate: AUDIO_CONFIG.inputSampleRate,
75
+ outRate: AUDIO_CONFIG.outputSampleRate,
76
+ inChannels: AUDIO_CONFIG.inputChannels,
77
+ outChannels: AUDIO_CONFIG.outputChannels,
78
+ volume: 1,
79
+ filterWindow: 8,
80
+ })
81
+
82
+ const opusEncoder = new prism.opus.Encoder({
83
+ rate: AUDIO_CONFIG.outputSampleRate,
84
+ channels: AUDIO_CONFIG.outputChannels,
85
+ frameSize: AUDIO_CONFIG.opusFrameSize,
86
+ })
87
+
88
+ // Pipe resampler to encoder with error handling
89
+ resampler.pipe(opusEncoder).on('error', (error) => {
90
+ workerLogger.error('Pipe error between resampler and encoder:', error)
91
+ void notifyError(error, 'GenAI worker audio pipeline error')
92
+ sendError(`Audio pipeline error: ${error.message}`)
93
+ })
94
+
95
+ // Opus packet queue and interval for 20ms packet sending
96
+ const opusPacketQueue: Buffer[] = []
97
+ let packetInterval: NodeJS.Timeout | null = null
98
+
99
+ // Send packets every 20ms
100
+ function startPacketSending() {
101
+ if (packetInterval) return
102
+
103
+ packetInterval = setInterval(() => {
104
+ const packet = opusPacketQueue.shift()
105
+ if (!packet) return
106
+
107
+ // Transfer packet as ArrayBuffer
108
+ const arrayBuffer = packet.buffer.slice(
109
+ packet.byteOffset,
110
+ packet.byteOffset + packet.byteLength,
111
+ ) as ArrayBuffer
112
+
113
+ parentPort!.postMessage(
114
+ {
115
+ type: 'assistantOpusPacket',
116
+ packet: arrayBuffer,
117
+ } satisfies WorkerOutMessage,
118
+ [arrayBuffer], // Transfer ownership
119
+ )
120
+ }, 20)
121
+ }
122
+
123
+ function stopPacketSending() {
124
+ if (packetInterval) {
125
+ clearInterval(packetInterval)
126
+ packetInterval = null
127
+ }
128
+ opusPacketQueue.length = 0
129
+ }
130
+
131
+ // Session state
132
+ let session: { session: Session; stop: () => void } | null = null
133
+
134
+ // Audio log stream for assistant audio
135
+ let audioLogStream: WriteStream | null = null
136
+
137
+ // Create assistant audio log stream for debugging
138
+ async function createAssistantAudioLogStream(
139
+ guildId: string,
140
+ channelId: string,
141
+ ): Promise<WriteStream | null> {
142
+ if (!process.env.DEBUG) return null
143
+
144
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
145
+ const audioDir = path.join(
146
+ process.cwd(),
147
+ 'discord-audio-logs',
148
+ guildId,
149
+ channelId,
150
+ )
151
+
152
+ const mkdirError = await errore.tryAsync({
153
+ try: () => mkdir(audioDir, { recursive: true }),
154
+ catch: (e) => e as Error,
155
+ })
156
+ if (mkdirError instanceof Error) {
157
+ workerLogger.error(
158
+ `Failed to create audio log directory:`,
159
+ mkdirError.message,
160
+ )
161
+ return null
162
+ }
163
+
164
+ // Create stream for assistant audio (24kHz mono s16le PCM)
165
+ const outputFileName = `assistant_${timestamp}.24.pcm`
166
+ const outputFilePath = path.join(audioDir, outputFileName)
167
+ const outputAudioStream = createWriteStream(outputFilePath)
168
+
169
+ // Add error handler to prevent crashes
170
+ outputAudioStream.on('error', (error) => {
171
+ workerLogger.error(`Assistant audio log stream error:`, error)
172
+ })
173
+
174
+ workerLogger.log(`Created assistant audio log: ${outputFilePath}`)
175
+
176
+ return outputAudioStream
177
+ }
178
+
179
+ // Handle encoded Opus packets
180
+ opusEncoder.on('data', (packet: Buffer) => {
181
+ opusPacketQueue.push(packet)
182
+ })
183
+
184
+ // Handle stream end events
185
+ opusEncoder.on('end', () => {
186
+ workerLogger.log('Opus encoder stream ended')
187
+ })
188
+
189
+ resampler.on('end', () => {
190
+ workerLogger.log('Resampler stream ended')
191
+ })
192
+
193
+ // Handle errors
194
+ resampler.on('error', (error: unknown) => {
195
+ workerLogger.error(`Resampler error:`, error)
196
+ void notifyError(error, 'GenAI worker resampler error')
197
+ sendError(`Resampler error: ${(error as Error).message}`)
198
+ })
199
+
200
+ opusEncoder.on('error', (error: unknown) => {
201
+ workerLogger.error(`Encoder error:`, error)
202
+ const errMsg = (error as Error).message || ''
203
+ // Check for specific corrupted data errors
204
+ if (errMsg.includes('The compressed data passed is corrupted')) {
205
+ workerLogger.warn('Received corrupted audio data in opus encoder')
206
+ } else {
207
+ void notifyError(error, 'GenAI worker encoder error')
208
+ sendError(`Encoder error: ${errMsg}`)
209
+ }
210
+ })
211
+
212
+ async function cleanupAsync(): Promise<void> {
213
+ workerLogger.log(`Starting async cleanup`)
214
+
215
+ stopPacketSending()
216
+
217
+ if (session) {
218
+ workerLogger.log(`Stopping GenAI session`)
219
+ session.stop()
220
+ session = null
221
+ }
222
+
223
+ // Wait for audio log stream to finish writing
224
+ if (audioLogStream) {
225
+ workerLogger.log(`Closing assistant audio log stream`)
226
+ await new Promise<void>((resolve, reject) => {
227
+ audioLogStream!.end(() => {
228
+ workerLogger.log(`Assistant audio log stream closed`)
229
+ resolve()
230
+ })
231
+ audioLogStream!.on('error', reject)
232
+ // Add timeout to prevent hanging
233
+ setTimeout(() => {
234
+ workerLogger.log(`Audio stream close timeout, continuing`)
235
+ resolve()
236
+ }, 3000)
237
+ })
238
+ audioLogStream = null
239
+ }
240
+
241
+ // Unpipe and end the encoder first
242
+ resampler.unpipe(opusEncoder)
243
+
244
+ // End the encoder stream
245
+ await new Promise<void>((resolve) => {
246
+ opusEncoder.end(() => {
247
+ workerLogger.log(`Opus encoder ended`)
248
+ resolve()
249
+ })
250
+ // Add timeout
251
+ setTimeout(resolve, 1000)
252
+ })
253
+
254
+ // End the resampler stream
255
+ await new Promise<void>((resolve) => {
256
+ resampler.end(() => {
257
+ workerLogger.log(`Resampler ended`)
258
+ resolve()
259
+ })
260
+ // Add timeout
261
+ setTimeout(resolve, 1000)
262
+ })
263
+
264
+ workerLogger.log(`Async cleanup complete`)
265
+ }
266
+
267
+ // Handle messages from main thread
268
+ parentPort.on('message', async (message: WorkerInMessage) => {
269
+ try {
270
+ switch (message.type) {
271
+ case 'init': {
272
+ workerLogger.log(`Initializing with directory:`, message.directory)
273
+
274
+ // Create audio log stream for assistant audio
275
+ audioLogStream = await createAssistantAudioLogStream(
276
+ message.guildId,
277
+ message.channelId,
278
+ )
279
+
280
+ // Start packet sending interval
281
+ startPacketSending()
282
+
283
+ // Get tools for the directory
284
+ const { tools } = await getTools({
285
+ directory: message.directory,
286
+ onMessageCompleted: (params) => {
287
+ parentPort!.postMessage({
288
+ type: 'toolCallCompleted',
289
+ ...params,
290
+ } satisfies WorkerOutMessage)
291
+ },
292
+ })
293
+
294
+ // Start GenAI session
295
+ session = await startGenAiSession({
296
+ tools,
297
+ systemMessage: message.systemMessage,
298
+ geminiApiKey: message.geminiApiKey,
299
+ onAssistantAudioChunk({ data }) {
300
+ // Write to audio log if enabled
301
+ if (audioLogStream && !audioLogStream.destroyed) {
302
+ audioLogStream.write(data, (err) => {
303
+ if (err) {
304
+ workerLogger.error('Error writing to audio log:', err)
305
+ }
306
+ })
307
+ }
308
+
309
+ // Write PCM data to resampler which will output Opus packets
310
+ if (!resampler.destroyed) {
311
+ resampler.write(data, (err) => {
312
+ if (err) {
313
+ workerLogger.error('Error writing to resampler:', err)
314
+ sendError(`Failed to process audio: ${err.message}`)
315
+ }
316
+ })
317
+ }
318
+ },
319
+ onAssistantStartSpeaking() {
320
+ parentPort!.postMessage({
321
+ type: 'assistantStartSpeaking',
322
+ } satisfies WorkerOutMessage)
323
+ },
324
+ onAssistantStopSpeaking() {
325
+ parentPort!.postMessage({
326
+ type: 'assistantStopSpeaking',
327
+ } satisfies WorkerOutMessage)
328
+ },
329
+ onAssistantInterruptSpeaking() {
330
+ parentPort!.postMessage({
331
+ type: 'assistantInterruptSpeaking',
332
+ } satisfies WorkerOutMessage)
333
+ },
334
+ })
335
+
336
+ // Notify main thread we're ready
337
+ parentPort!.postMessage({
338
+ type: 'ready',
339
+ } satisfies WorkerOutMessage)
340
+ break
341
+ }
342
+
343
+ case 'sendRealtimeInput': {
344
+ if (!session) {
345
+ sendError('Session not initialized')
346
+ return
347
+ }
348
+ session.session.sendRealtimeInput({
349
+ audio: message.audio,
350
+ audioStreamEnd: message.audioStreamEnd,
351
+ })
352
+ break
353
+ }
354
+
355
+ case 'sendTextInput': {
356
+ if (!session) {
357
+ sendError('Session not initialized')
358
+ return
359
+ }
360
+ session.session.sendRealtimeInput({
361
+ text: message.text,
362
+ })
363
+ break
364
+ }
365
+
366
+ case 'interrupt': {
367
+ workerLogger.log(`Interrupting playback`)
368
+ // Clear the opus packet queue
369
+ opusPacketQueue.length = 0
370
+ break
371
+ }
372
+
373
+ case 'stop': {
374
+ workerLogger.log(`Stopping worker`)
375
+ await cleanupAsync()
376
+ // process.exit(0)
377
+ break
378
+ }
379
+ }
380
+ } catch (error) {
381
+ workerLogger.error(`Error handling message:`, error)
382
+ sendError(
383
+ error instanceof Error ? error.message : 'Unknown error in worker',
384
+ )
385
+ }
386
+ })