@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,294 @@
1
+ // Discord -> filesystem sync.
2
+ // Fetches forum threads from Discord and writes them as markdown files.
3
+ // Handles incremental sync (skip unchanged threads) and stale file cleanup.
4
+
5
+ import fs from 'node:fs'
6
+ import path from 'node:path'
7
+ import type { ForumChannel, ThreadChannel } from 'discord.js'
8
+ import { createLogger } from '../logger.js'
9
+ import {
10
+ buildMessageSections,
11
+ extractProjectChannelFromContent,
12
+ formatMessageSection,
13
+ getStringValue,
14
+ stringifyFrontmatter,
15
+ } from './markdown.js'
16
+ import {
17
+ ensureDirectory,
18
+ fetchForumThreads,
19
+ fetchThreadMessages,
20
+ getCanonicalThreadFilePath,
21
+ loadExistingForumFiles,
22
+ resolveForumChannel,
23
+ } from './discord-operations.js'
24
+ import {
25
+ DEFAULT_RATE_LIMIT_DELAY_MS,
26
+ ForumSyncOperationError,
27
+ addIgnoredPath,
28
+ delay,
29
+ type ForumMarkdownFrontmatter,
30
+ type ForumMessageSection,
31
+ type ForumRuntimeState,
32
+ type ForumSyncResult,
33
+ type SyncForumToFilesOptions,
34
+ } from './types.js'
35
+
36
+ const forumLogger = createLogger('FORUM')
37
+
38
+ function resolveTagNames({
39
+ thread,
40
+ forumChannel,
41
+ }: {
42
+ thread: ThreadChannel
43
+ forumChannel: ForumChannel
44
+ }): string[] {
45
+ const availableTagsById = new Map(
46
+ forumChannel.availableTags.map((tag) => [tag.id, tag.name] as const),
47
+ )
48
+ return thread.appliedTags
49
+ .map((tagId) => availableTagsById.get(tagId))
50
+ .filter((tagName): tagName is string => Boolean(tagName))
51
+ }
52
+
53
+ function resolveSubfolderForThread({
54
+ existingSubfolder,
55
+ thread,
56
+ forumChannel,
57
+ }: {
58
+ existingSubfolder?: string
59
+ thread: ThreadChannel
60
+ forumChannel: ForumChannel
61
+ }) {
62
+ const hasGlobalTag = resolveTagNames({ thread, forumChannel }).some(
63
+ (tagName) => tagName.toLowerCase().trim() === 'global',
64
+ )
65
+ if (hasGlobalTag) return 'global'
66
+ if (existingSubfolder) return existingSubfolder
67
+ return undefined
68
+ }
69
+
70
+ function buildFrontmatter({
71
+ thread,
72
+ forumChannel,
73
+ sections,
74
+ project,
75
+ projectChannelId,
76
+ }: {
77
+ thread: ThreadChannel
78
+ forumChannel: ForumChannel
79
+ sections: ForumMessageSection[]
80
+ project?: string
81
+ projectChannelId?: string
82
+ }): ForumMarkdownFrontmatter {
83
+ const firstSection = sections[0]
84
+ const createdTimestamp = thread.createdTimestamp ?? Date.now()
85
+
86
+ const latestTimestamp = sections.reduce((latest, section) => {
87
+ const created = Date.parse(section.createdAt)
88
+ const edited = section.editedAt ? Date.parse(section.editedAt) : 0
89
+ return Math.max(latest, created, edited)
90
+ }, createdTimestamp)
91
+
92
+ return {
93
+ title: thread.name,
94
+ threadId: thread.id,
95
+ forumChannelId: forumChannel.id,
96
+ tags: resolveTagNames({ thread, forumChannel }),
97
+ author: firstSection?.authorName || '',
98
+ authorId: firstSection?.authorId || '',
99
+ createdAt:
100
+ thread.createdAt?.toISOString() ||
101
+ new Date(createdTimestamp).toISOString(),
102
+ lastUpdated: new Date(latestTimestamp).toISOString(),
103
+ lastMessageId: thread.lastMessageId,
104
+ lastSyncedAt: new Date().toISOString(),
105
+ messageCount: sections.length,
106
+ ...(project && { project }),
107
+ ...(projectChannelId && { projectChannelId }),
108
+ }
109
+ }
110
+
111
+ export async function syncSingleThreadToFile({
112
+ thread,
113
+ forumChannel,
114
+ outputDir,
115
+ runtimeState,
116
+ previousFilePath,
117
+ subfolder,
118
+ project,
119
+ projectChannelId,
120
+ }: {
121
+ thread: ThreadChannel
122
+ forumChannel: ForumChannel
123
+ outputDir: string
124
+ runtimeState?: ForumRuntimeState
125
+ previousFilePath?: string
126
+ subfolder?: string
127
+ project?: string
128
+ projectChannelId?: string
129
+ }): Promise<void | ForumSyncOperationError> {
130
+ const messages = await fetchThreadMessages({ thread })
131
+ if (messages instanceof Error) return messages
132
+
133
+ // Extract projectChannelId from the starter message footer if not already known.
134
+ // This allows Discord -> file sync to reconstruct the correct subfolder
135
+ // even when no local .md file exists (e.g. fresh machine, deleted files).
136
+ let resolvedProjectChannelId = projectChannelId
137
+ let resolvedSubfolder = subfolder
138
+ const sections = buildMessageSections({ messages })
139
+ const firstSection = sections[0]
140
+ if (firstSection) {
141
+ const { cleanContent, projectChannelId: footerChannelId } =
142
+ extractProjectChannelFromContent({ content: firstSection.content })
143
+ firstSection.content = cleanContent
144
+ if (footerChannelId && !resolvedProjectChannelId) {
145
+ resolvedProjectChannelId = footerChannelId
146
+ }
147
+ if (resolvedProjectChannelId && !resolvedSubfolder) {
148
+ resolvedSubfolder = resolvedProjectChannelId
149
+ }
150
+ }
151
+
152
+ // Ensure subfolder directory exists when writing into a nested path
153
+ if (resolvedSubfolder) {
154
+ const subDir = path.join(outputDir, resolvedSubfolder)
155
+ const ensureResult = await ensureDirectory({ directory: subDir })
156
+ if (ensureResult instanceof Error) return ensureResult
157
+ }
158
+ const body = sections
159
+ .map((section) => formatMessageSection({ section }))
160
+ .join('\n\n---\n\n')
161
+ const frontmatter = buildFrontmatter({
162
+ thread,
163
+ forumChannel,
164
+ sections,
165
+ project,
166
+ projectChannelId: resolvedProjectChannelId,
167
+ })
168
+ const markdown = stringifyFrontmatter({ frontmatter, body })
169
+ const targetPath = getCanonicalThreadFilePath({
170
+ outputDir,
171
+ threadId: thread.id,
172
+ subfolder: resolvedSubfolder,
173
+ })
174
+
175
+ addIgnoredPath({ runtimeState, filePath: targetPath })
176
+ const writeResult = await fs.promises
177
+ .writeFile(targetPath, markdown, 'utf8')
178
+ .catch((cause) => {
179
+ return new ForumSyncOperationError({
180
+ forumChannelId: forumChannel.id,
181
+ reason: `failed to write ${targetPath}`,
182
+ cause,
183
+ })
184
+ })
185
+ if (writeResult instanceof Error) return writeResult
186
+
187
+ // Clean up old file if thread was renamed (file path changed)
188
+ if (
189
+ previousFilePath &&
190
+ previousFilePath !== targetPath &&
191
+ fs.existsSync(previousFilePath)
192
+ ) {
193
+ addIgnoredPath({ runtimeState, filePath: previousFilePath })
194
+ await fs.promises.unlink(previousFilePath).catch((cause) => {
195
+ forumLogger.warn(
196
+ `Failed to remove old forum file ${previousFilePath}:`,
197
+ cause,
198
+ )
199
+ })
200
+ }
201
+ }
202
+
203
+ export async function syncForumToFiles({
204
+ discordClient,
205
+ forumChannelId,
206
+ outputDir,
207
+ forceFullRefresh = false,
208
+ forceThreadIds,
209
+ runtimeState,
210
+ }: SyncForumToFilesOptions) {
211
+ const ensureResult = await ensureDirectory({ directory: outputDir })
212
+ if (ensureResult instanceof Error) {
213
+ return new ForumSyncOperationError({
214
+ forumChannelId,
215
+ reason: `failed to create output directory ${outputDir}`,
216
+ cause: ensureResult,
217
+ })
218
+ }
219
+
220
+ const forumChannel = await resolveForumChannel({
221
+ discordClient,
222
+ forumChannelId,
223
+ })
224
+ if (forumChannel instanceof Error) return forumChannel
225
+
226
+ const threads = await fetchForumThreads({ forumChannel })
227
+ if (threads instanceof Error) return threads
228
+
229
+ const existingFiles = await loadExistingForumFiles({ outputDir })
230
+ const existingByThreadId = new Map(
231
+ existingFiles.map((entry) => [entry.threadId, entry] as const),
232
+ )
233
+
234
+ const result: ForumSyncResult = { synced: 0, skipped: 0, deleted: 0 }
235
+
236
+ for (const thread of threads) {
237
+ const existing = existingByThreadId.get(thread.id)
238
+ const savedLastMessageId =
239
+ getStringValue({ value: existing?.frontmatter.lastMessageId }) || null
240
+ const isForced = forceFullRefresh || Boolean(forceThreadIds?.has(thread.id))
241
+
242
+ if (
243
+ !isForced &&
244
+ savedLastMessageId &&
245
+ savedLastMessageId === thread.lastMessageId
246
+ ) {
247
+ result.skipped += 1
248
+ continue
249
+ }
250
+
251
+ const syncResult = await syncSingleThreadToFile({
252
+ thread,
253
+ forumChannel,
254
+ outputDir,
255
+ runtimeState,
256
+ previousFilePath: existing?.filePath,
257
+ subfolder: resolveSubfolderForThread({
258
+ existingSubfolder: existing?.subfolder,
259
+ thread,
260
+ forumChannel,
261
+ }),
262
+ project: getStringValue({ value: existing?.frontmatter.project }),
263
+ projectChannelId: getStringValue({
264
+ value: existing?.frontmatter.projectChannelId,
265
+ }),
266
+ })
267
+ if (syncResult instanceof Error) return syncResult
268
+
269
+ result.synced += 1
270
+ await delay({ ms: DEFAULT_RATE_LIMIT_DELAY_MS })
271
+ }
272
+
273
+ // Delete files for threads that no longer exist in Discord
274
+ const liveThreadIds = new Set(threads.map((thread) => thread.id))
275
+ for (const existing of existingFiles) {
276
+ if (liveThreadIds.has(existing.threadId)) continue
277
+ if (!fs.existsSync(existing.filePath)) continue
278
+
279
+ addIgnoredPath({ runtimeState, filePath: existing.filePath })
280
+ const deleteResult = await fs.promises
281
+ .unlink(existing.filePath)
282
+ .catch((cause) => {
283
+ return new ForumSyncOperationError({
284
+ forumChannelId,
285
+ reason: `failed deleting stale file ${existing.filePath}`,
286
+ cause,
287
+ })
288
+ })
289
+ if (deleteResult instanceof Error) return deleteResult
290
+ result.deleted += 1
291
+ }
292
+
293
+ return result
294
+ }
@@ -0,0 +1,175 @@
1
+ // Type definitions, tagged errors, and constants for forum sync.
2
+ // All shared types and error classes live here to avoid circular dependencies
3
+ // between the sync modules.
4
+
5
+ import * as errore from 'errore'
6
+ import type { Client } from 'discord.js'
7
+
8
+ // ═══════════════════════════════════════════════════════════════════════════
9
+ // CONSTANTS
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+
12
+ export const DEFAULT_DEBOUNCE_MS = 800
13
+ export const DEFAULT_RATE_LIMIT_DELAY_MS = 250
14
+ export const WRITE_IGNORE_TTL_MS = 2_000
15
+
16
+ // ═══════════════════════════════════════════════════════════════════════════
17
+ // TAGGED ERRORS
18
+ // ═══════════════════════════════════════════════════════════════════════════
19
+
20
+ export class ForumChannelResolveError extends errore.createTaggedError({
21
+ name: 'ForumChannelResolveError',
22
+ message: 'Could not resolve forum channel $forumChannelId',
23
+ }) {}
24
+
25
+ export class ForumSyncOperationError extends errore.createTaggedError({
26
+ name: 'ForumSyncOperationError',
27
+ message: 'Forum sync operation failed for forum $forumChannelId: $reason',
28
+ }) {}
29
+
30
+ export class ForumFrontmatterParseError extends errore.createTaggedError({
31
+ name: 'ForumFrontmatterParseError',
32
+ message: 'Failed to parse frontmatter: $reason',
33
+ }) {}
34
+
35
+ // ═══════════════════════════════════════════════════════════════════════════
36
+ // DATA TYPES
37
+ // ═══════════════════════════════════════════════════════════════════════════
38
+
39
+ export type ForumSyncDirection = 'discord-to-files' | 'bidirectional'
40
+
41
+ export type ForumSyncEntry = {
42
+ forumChannelId: string
43
+ outputDir: string
44
+ direction: ForumSyncDirection
45
+ }
46
+
47
+ export type ForumMessageSection = {
48
+ messageId: string
49
+ authorName: string
50
+ authorId: string
51
+ createdAt: string
52
+ editedAt: string | null
53
+ content: string
54
+ }
55
+
56
+ export type ForumMarkdownFrontmatter = {
57
+ title: string
58
+ threadId: string
59
+ forumChannelId: string
60
+ tags: string[]
61
+ author: string
62
+ authorId: string
63
+ createdAt: string
64
+ lastUpdated: string
65
+ lastMessageId: string | null
66
+ lastSyncedAt: string
67
+ messageCount: number
68
+ project?: string
69
+ projectChannelId?: string
70
+ }
71
+
72
+ export type ParsedMarkdownFile = {
73
+ frontmatter: Record<string, unknown>
74
+ body: string
75
+ }
76
+
77
+ export type ExistingForumFile = {
78
+ filePath: string
79
+ threadId: string
80
+ frontmatter: Record<string, unknown>
81
+ /** Relative subfolder path from outputDir (e.g. channelId) */
82
+ subfolder?: string
83
+ }
84
+
85
+ export type ForumSyncResult = {
86
+ synced: number
87
+ skipped: number
88
+ deleted: number
89
+ }
90
+
91
+ export type ForumFileSyncResult = {
92
+ created: number
93
+ updated: number
94
+ skipped: number
95
+ deleted: number
96
+ }
97
+
98
+ export type ForumRuntimeState = {
99
+ forumChannelId: string
100
+ outputDir: string
101
+ direction: ForumSyncDirection
102
+ dirtyThreadIds: Set<string>
103
+ ignoredPaths: Map<string, number>
104
+ queuedFileEvents: Map<string, 'create' | 'update' | 'delete'>
105
+ discordDebounceTimer: NodeJS.Timeout | null
106
+ fileDebounceTimer: NodeJS.Timeout | null
107
+ }
108
+
109
+ export type StartForumSyncOptions = {
110
+ discordClient: Client
111
+ appId?: string
112
+ }
113
+
114
+ export type SyncForumToFilesOptions = {
115
+ discordClient: Client
116
+ forumChannelId: string
117
+ outputDir: string
118
+ forceFullRefresh?: boolean
119
+ forceThreadIds?: Set<string>
120
+ runtimeState?: ForumRuntimeState
121
+ }
122
+
123
+ export type SyncFilesToForumOptions = {
124
+ discordClient: Client
125
+ forumChannelId: string
126
+ outputDir: string
127
+ runtimeState?: ForumRuntimeState
128
+ changedFilePaths?: string[]
129
+ deletedFilePaths?: string[]
130
+ }
131
+
132
+ export type LoadedForumConfig = {
133
+ forumChannelId: string
134
+ outputDir: string
135
+ direction: ForumSyncDirection
136
+ }
137
+
138
+ // ═══════════════════════════════════════════════════════════════════════════
139
+ // SHARED UTILITIES
140
+ // ═══════════════════════════════════════════════════════════════════════════
141
+
142
+ export function delay({ ms }: { ms: number }) {
143
+ return new Promise<void>((resolve) => {
144
+ setTimeout(resolve, ms)
145
+ })
146
+ }
147
+
148
+ /** Mark a file path as recently written so the file watcher ignores it. */
149
+ export function addIgnoredPath({
150
+ runtimeState,
151
+ filePath,
152
+ }: {
153
+ runtimeState?: ForumRuntimeState
154
+ filePath: string
155
+ }) {
156
+ if (!runtimeState) return
157
+ runtimeState.ignoredPaths.set(filePath, Date.now() + WRITE_IGNORE_TTL_MS)
158
+ }
159
+
160
+ /** Check if a file path was recently written by us and should be ignored. */
161
+ export function shouldIgnorePath({
162
+ runtimeState,
163
+ filePath,
164
+ }: {
165
+ runtimeState: ForumRuntimeState
166
+ filePath: string
167
+ }) {
168
+ const expiresAt = runtimeState.ignoredPaths.get(filePath)
169
+ if (!expiresAt) return false
170
+ if (expiresAt < Date.now()) {
171
+ runtimeState.ignoredPaths.delete(filePath)
172
+ return false
173
+ }
174
+ return true
175
+ }