@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,181 @@
1
+ // HTML fragment parser for Discord-renderable components.
2
+ // Supports a small reusable subset today (text + button) so tables and other
3
+ // CV2 renderers can map inline HTML into Discord UI elements.
4
+
5
+ import { DomHandler, ElementType, Parser } from 'htmlparser2'
6
+ import type { ChildNode, Element, Text } from 'domhandler'
7
+
8
+ export type HtmlTextRenderable = {
9
+ type: 'text'
10
+ text: string
11
+ }
12
+
13
+ export type HtmlButtonRenderable = {
14
+ type: 'button'
15
+ id: string
16
+ label: string
17
+ variant: 'secondary' | 'primary' | 'success' | 'danger'
18
+ disabled: boolean
19
+ }
20
+
21
+ export type HtmlRenderable = HtmlTextRenderable | HtmlButtonRenderable
22
+
23
+ export function parseInlineHtmlRenderables({
24
+ html,
25
+ }: {
26
+ html: string
27
+ }): HtmlRenderable[] | Error {
28
+ let parseError: Error | undefined
29
+ let domNodes: ChildNode[] = []
30
+
31
+ const handler = new DomHandler(
32
+ (error, dom) => {
33
+ if (error) {
34
+ parseError = new Error('Failed to parse HTML fragment', {
35
+ cause: error,
36
+ })
37
+ return
38
+ }
39
+ domNodes = dom
40
+ },
41
+ {
42
+ withStartIndices: false,
43
+ withEndIndices: false,
44
+ },
45
+ )
46
+
47
+ const parser = new Parser(handler, {
48
+ xmlMode: false,
49
+ decodeEntities: false,
50
+ recognizeSelfClosing: true,
51
+ })
52
+ parser.write(html)
53
+ parser.end()
54
+
55
+ if (parseError) {
56
+ return parseError
57
+ }
58
+
59
+ return parseRenderableNodes({ nodes: domNodes })
60
+ }
61
+
62
+ function parseRenderableNodes({
63
+ nodes,
64
+ }: {
65
+ nodes: ChildNode[]
66
+ }): HtmlRenderable[] | Error {
67
+ const renderables: HtmlRenderable[] = []
68
+
69
+ for (const node of nodes) {
70
+ if (node.type === ElementType.Text) {
71
+ const textNode = node as Text
72
+ renderables.push({
73
+ type: 'text',
74
+ text: textNode.data,
75
+ })
76
+ continue
77
+ }
78
+
79
+ if (node.type === ElementType.Tag) {
80
+ const element = node as Element
81
+ if (element.name !== 'button') {
82
+ return new Error(`Unsupported HTML tag: <${element.name}>`)
83
+ }
84
+
85
+ const buttonRenderable = parseButtonElement({ element })
86
+ if (buttonRenderable instanceof Error) {
87
+ return buttonRenderable
88
+ }
89
+
90
+ renderables.push(buttonRenderable)
91
+ continue
92
+ }
93
+
94
+ if (node.type === ElementType.Comment) {
95
+ continue
96
+ }
97
+
98
+ return new Error(`Unsupported HTML node type: ${node.type}`)
99
+ }
100
+
101
+ return renderables
102
+ }
103
+
104
+ function parseButtonElement({
105
+ element,
106
+ }: {
107
+ element: Element
108
+ }): HtmlButtonRenderable | Error {
109
+ const id = element.attribs.id?.trim()
110
+ if (!id) {
111
+ return new Error('<button> is missing required id attribute')
112
+ }
113
+
114
+ const label = extractNodeText({ nodes: element.children })
115
+ .replace(/\s+/g, ' ')
116
+ .trim()
117
+ if (!label) {
118
+ return new Error(`<button id="${id}"> is missing label text`)
119
+ }
120
+
121
+ const variant = normalizeButtonVariant({
122
+ value: element.attribs.variant,
123
+ })
124
+ if (variant instanceof Error) {
125
+ return variant
126
+ }
127
+
128
+ return {
129
+ type: 'button',
130
+ id,
131
+ label,
132
+ variant,
133
+ disabled: 'disabled' in element.attribs,
134
+ }
135
+ }
136
+
137
+ function normalizeButtonVariant({
138
+ value,
139
+ }: {
140
+ value?: string
141
+ }): HtmlButtonRenderable['variant'] | Error {
142
+ if (!value) {
143
+ return 'secondary'
144
+ }
145
+
146
+ if (value === 'secondary') {
147
+ return value
148
+ }
149
+ if (value === 'primary') {
150
+ return value
151
+ }
152
+ if (value === 'success') {
153
+ return value
154
+ }
155
+ if (value === 'danger') {
156
+ return value
157
+ }
158
+
159
+ return new Error(`Unsupported <button> variant: ${value}`)
160
+ }
161
+
162
+ function extractNodeText({
163
+ nodes,
164
+ }: {
165
+ nodes: ChildNode[]
166
+ }): string {
167
+ const parts: string[] = []
168
+
169
+ for (const node of nodes) {
170
+ if (node.type === ElementType.Text) {
171
+ parts.push((node as Text).data)
172
+ continue
173
+ }
174
+
175
+ if (node.type === ElementType.Tag) {
176
+ parts.push(extractNodeText({ nodes: (node as Element).children }))
177
+ }
178
+ }
179
+
180
+ return parts.join('')
181
+ }
@@ -0,0 +1,194 @@
1
+ // Optimizes oversized images before they reach the LLM API.
2
+ // Prevents "image dimensions exceed max allowed" errors from Anthropic/Google/OpenAI.
3
+ // Hooks into tool.execute.after (read) and experimental.chat.messages.transform (clipboard paste).
4
+ // Uses sharp to resize images > 2000px and compress images > 4MB.
5
+ // Vendored from https://github.com/kargnas/opencode-large-image-optimizer, simplified to zero-config.
6
+
7
+ import type { Plugin } from '@opencode-ai/plugin'
8
+
9
+ // Conservative safe floor for Anthropic many-image requests (20+ images = 2000px limit).
10
+ // OpenCode resends history so image counts accumulate across turns — 2000px is safest.
11
+ const MAX_DIMENSION = 2000
12
+ // 4MB safe margin under Anthropic's 5MB limit
13
+ const MAX_FILE_SIZE = 4 * 1024 * 1024
14
+ const SUPPORTED_MIMES = new Set([
15
+ 'image/png',
16
+ 'image/jpeg',
17
+ 'image/jpg',
18
+ 'image/gif',
19
+ 'image/webp',
20
+ ])
21
+
22
+ // sharp is an optionalDependency — lazy-load to avoid breaking all plugins if missing
23
+ type SharpFn = (input?: Buffer | string) => import('sharp').Sharp
24
+
25
+ let sharpFactory: SharpFn | null | undefined
26
+
27
+ async function getSharp(): Promise<SharpFn | null> {
28
+ if (sharpFactory !== undefined) {
29
+ return sharpFactory
30
+ }
31
+ try {
32
+ const mod = await import('sharp')
33
+ // sharp uses `export =` so it lands on .default in ESM interop
34
+ const fn = typeof mod === 'function' ? mod : (mod as { default: SharpFn }).default
35
+ if (typeof fn === 'function') {
36
+ sharpFactory = fn
37
+ } else {
38
+ sharpFactory = null
39
+ }
40
+ } catch {
41
+ sharpFactory = null
42
+ }
43
+ return sharpFactory
44
+ }
45
+
46
+ function extractBase64Data(dataUrl: string): string | null {
47
+ const match = dataUrl.match(/^data:[^;]+;base64,(.+)$/s)
48
+ if (match?.[1]) {
49
+ return match[1]
50
+ }
51
+ // raw base64 string (no data: prefix)
52
+ if (/^[A-Za-z0-9+/]+={0,2}$/.test(dataUrl)) {
53
+ return dataUrl
54
+ }
55
+ return null
56
+ }
57
+
58
+ interface OptimizeResult {
59
+ dataUrl: string
60
+ mime: string
61
+ }
62
+
63
+ async function optimizeImage(
64
+ dataUrl: string,
65
+ mime: string,
66
+ ): Promise<OptimizeResult | null> {
67
+ const sharp = await getSharp()
68
+ if (!sharp) {
69
+ return null
70
+ }
71
+
72
+ const rawBase64 = extractBase64Data(dataUrl)
73
+ if (!rawBase64) {
74
+ return null
75
+ }
76
+
77
+ const inputBuffer = Buffer.from(rawBase64, 'base64')
78
+ if (inputBuffer.length === 0) {
79
+ return null
80
+ }
81
+
82
+ const metadata = await sharp(inputBuffer).metadata()
83
+ const width = metadata.width || 0
84
+ const height = metadata.height || 0
85
+ if (width === 0 || height === 0) {
86
+ return null
87
+ }
88
+
89
+ const needsResize = width > MAX_DIMENSION || height > MAX_DIMENSION
90
+ const needsCompress = inputBuffer.length > MAX_FILE_SIZE
91
+ if (!needsResize && !needsCompress) {
92
+ return null
93
+ }
94
+
95
+ let pipeline = sharp(inputBuffer)
96
+ let outputMime = mime
97
+
98
+ if (needsResize) {
99
+ pipeline = pipeline.resize(MAX_DIMENSION, MAX_DIMENSION, {
100
+ fit: 'inside',
101
+ withoutEnlargement: true,
102
+ })
103
+ }
104
+
105
+ let outputBuffer = await pipeline.toBuffer()
106
+
107
+ // if still over 4MB, convert to JPEG with progressive quality reduction
108
+ if (outputBuffer.length > MAX_FILE_SIZE) {
109
+ for (const quality of [100, 90, 80, 70]) {
110
+ outputBuffer = await sharp(outputBuffer)
111
+ .jpeg({ quality, mozjpeg: true })
112
+ .toBuffer()
113
+ outputMime = 'image/jpeg'
114
+ if (outputBuffer.length <= MAX_FILE_SIZE) {
115
+ break
116
+ }
117
+ }
118
+ }
119
+
120
+ return {
121
+ dataUrl: `data:${outputMime};base64,${outputBuffer.toString('base64')}`,
122
+ mime: outputMime,
123
+ }
124
+ }
125
+
126
+ // runtime guard — tool.execute.after output type doesn't declare attachments
127
+ function hasAttachments(
128
+ value: unknown,
129
+ ): value is { attachments: Array<{ mime?: string; url?: string }> } {
130
+ return (
131
+ typeof value === 'object' &&
132
+ value !== null &&
133
+ 'attachments' in value &&
134
+ Array.isArray((value as { attachments?: unknown }).attachments)
135
+ )
136
+ }
137
+
138
+ const imageOptimizerPlugin: Plugin = async () => {
139
+ return {
140
+ 'tool.execute.after': async (input, output) => {
141
+ const tool = input.tool.toLowerCase()
142
+
143
+ // read tool: optimize image attachments
144
+ if (tool === 'read' && hasAttachments(output)) {
145
+ for (const att of output.attachments) {
146
+ if (
147
+ !att.mime ||
148
+ !att.url ||
149
+ !SUPPORTED_MIMES.has(att.mime.toLowerCase())
150
+ ) {
151
+ continue
152
+ }
153
+ const result = await optimizeImage(att.url, att.mime).catch(
154
+ () => null,
155
+ )
156
+ if (result) {
157
+ att.url = result.dataUrl
158
+ att.mime = result.mime
159
+ }
160
+ }
161
+ }
162
+
163
+ },
164
+
165
+ // clipboard paste: optimize file parts in message history
166
+ 'experimental.chat.messages.transform': async (_input, output) => {
167
+ if (!output.messages || !Array.isArray(output.messages)) {
168
+ return
169
+ }
170
+ for (const msg of output.messages) {
171
+ if (!msg.parts || !Array.isArray(msg.parts)) {
172
+ continue
173
+ }
174
+ for (const part of msg.parts) {
175
+ if (part.type !== 'file') {
176
+ continue
177
+ }
178
+ if (!SUPPORTED_MIMES.has(part.mime.toLowerCase())) {
179
+ continue
180
+ }
181
+ const result = await optimizeImage(part.url, part.mime).catch(
182
+ () => null,
183
+ )
184
+ if (result) {
185
+ part.url = result.dataUrl
186
+ part.mime = result.mime
187
+ }
188
+ }
189
+ }
190
+ },
191
+ }
192
+ }
193
+
194
+ export { imageOptimizerPlugin }
@@ -0,0 +1,149 @@
1
+ // Image processing utilities for Discord attachments.
2
+ // Uses sharp (optional) to resize large images and heic-convert (optional) for HEIC support.
3
+ // Falls back gracefully if dependencies are not available.
4
+
5
+ import { createLogger, LogPrefix } from './logger.js'
6
+
7
+ const logger = createLogger(LogPrefix.FORMATTING)
8
+
9
+ const MAX_DIMENSION = 1500
10
+ const HEIC_MIME_TYPES = [
11
+ 'image/heic',
12
+ 'image/heif',
13
+ 'image/heic-sequence',
14
+ 'image/heif-sequence',
15
+ ]
16
+
17
+ type SharpModule = typeof import('sharp')
18
+ type HeicConvertFn = (options: {
19
+ buffer: ArrayBufferLike
20
+ format: 'JPEG' | 'PNG'
21
+ quality?: number
22
+ }) => Promise<ArrayBuffer>
23
+
24
+ let sharpModule: SharpModule | null | undefined = undefined
25
+ let heicConvertModule: HeicConvertFn | null | undefined = undefined
26
+
27
+ async function tryLoadSharp(): Promise<SharpModule | null> {
28
+ if (sharpModule !== undefined) {
29
+ return sharpModule
30
+ }
31
+ try {
32
+ sharpModule = (await import('sharp')).default as unknown as SharpModule
33
+ logger.log('sharp loaded successfully')
34
+ return sharpModule
35
+ } catch {
36
+ logger.log('sharp not available, images will be sent at original size')
37
+ sharpModule = null
38
+ return null
39
+ }
40
+ }
41
+
42
+ async function tryLoadHeicConvert(): Promise<HeicConvertFn | null> {
43
+ if (heicConvertModule !== undefined) {
44
+ return heicConvertModule
45
+ }
46
+ try {
47
+ const mod = await import('heic-convert')
48
+ heicConvertModule = mod.default as HeicConvertFn
49
+ logger.log('heic-convert loaded successfully')
50
+ return heicConvertModule
51
+ } catch {
52
+ logger.log('heic-convert not available, HEIC images will be sent as-is')
53
+ heicConvertModule = null
54
+ return null
55
+ }
56
+ }
57
+
58
+ function isHeicMime(mime: string): boolean {
59
+ return HEIC_MIME_TYPES.includes(mime.toLowerCase())
60
+ }
61
+
62
+ export async function processImage(
63
+ buffer: Buffer,
64
+ mime: string,
65
+ ): Promise<{ buffer: Buffer; mime: string }> {
66
+ // Skip non-images (PDFs, etc.)
67
+ if (!mime.startsWith('image/')) {
68
+ return { buffer, mime }
69
+ }
70
+
71
+ let workingBuffer = buffer
72
+ let workingMime = mime
73
+
74
+ // Handle HEIC conversion first (before sharp, since sharp doesn't support HEIC)
75
+ if (isHeicMime(mime)) {
76
+ const heicConvert = await tryLoadHeicConvert()
77
+ if (heicConvert) {
78
+ try {
79
+ const outputArrayBuffer = await heicConvert({
80
+ buffer: workingBuffer.buffer.slice(
81
+ workingBuffer.byteOffset,
82
+ workingBuffer.byteOffset + workingBuffer.byteLength,
83
+ ),
84
+ format: 'JPEG',
85
+ quality: 0.85,
86
+ })
87
+ workingBuffer = Buffer.from(outputArrayBuffer)
88
+ workingMime = 'image/jpeg'
89
+ logger.log(
90
+ `Converted HEIC to JPEG (${buffer.length} → ${workingBuffer.length} bytes)`,
91
+ )
92
+ } catch (error) {
93
+ logger.error('Failed to convert HEIC, sending original:', error)
94
+ return { buffer, mime }
95
+ }
96
+ } else {
97
+ // No heic-convert available, return original (LLM might not support it)
98
+ logger.log(
99
+ 'HEIC image detected but heic-convert not available, sending as-is',
100
+ )
101
+ return { buffer, mime }
102
+ }
103
+ }
104
+
105
+ // Now process with sharp (resize + ensure JPEG output)
106
+ const sharp = await tryLoadSharp()
107
+ if (!sharp) {
108
+ return { buffer: workingBuffer, mime: workingMime }
109
+ }
110
+
111
+ try {
112
+ const image = sharp(workingBuffer)
113
+ const metadata = await image.metadata()
114
+ const { width, height } = metadata
115
+
116
+ const needsResize =
117
+ width && height && (width > MAX_DIMENSION || height > MAX_DIMENSION)
118
+
119
+ if (!needsResize) {
120
+ // Still convert to JPEG for consistency (unless already JPEG from HEIC conversion)
121
+ const outputBuffer = await image.jpeg({ quality: 85 }).toBuffer()
122
+ logger.log(
123
+ `Converted image to JPEG: ${width}x${height} (${outputBuffer.length} bytes)`,
124
+ )
125
+ return { buffer: outputBuffer, mime: 'image/jpeg' }
126
+ }
127
+
128
+ // Resize and convert to JPEG
129
+ const outputBuffer = await image
130
+ .resize(MAX_DIMENSION, MAX_DIMENSION, {
131
+ fit: 'inside',
132
+ withoutEnlargement: true,
133
+ })
134
+ .jpeg({ quality: 85 })
135
+ .toBuffer()
136
+
137
+ logger.log(
138
+ `Resized image: ${width}x${height} → max ${MAX_DIMENSION}px (${outputBuffer.length} bytes)`,
139
+ )
140
+
141
+ return { buffer: outputBuffer, mime: 'image/jpeg' }
142
+ } catch (error) {
143
+ logger.error(
144
+ 'Failed to process image with sharp, using working buffer:',
145
+ error,
146
+ )
147
+ return { buffer: workingBuffer, mime: workingMime }
148
+ }
149
+ }