@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,673 @@
1
+ import { test, expect } from 'vitest';
2
+ import { unnestCodeBlocksFromLists } from './unnest-code-blocks.js';
3
+ // Discord markdown quirks (as of 2026-02):
4
+ // - Fenced code blocks nested inside list indentation often don't render at all.
5
+ // (Discord effectively wants fences at the start of the line.)
6
+ // - If a list line is accidentally concatenated with the next list marker or a fence,
7
+ // e.g. `**Title**- bullet` or `Text:```ts`, Discord won't parse it as a list/code block.
8
+ // These tests lock down that `unnestCodeBlocksFromLists()` produces Discord-friendly output.
9
+ test('basic - single item with code block', () => {
10
+ const input = `- Item 1
11
+ \`\`\`js
12
+ const x = 1
13
+ \`\`\``;
14
+ const result = unnestCodeBlocksFromLists(input);
15
+ expect(result).toMatchInlineSnapshot(`
16
+ "- Item 1
17
+
18
+ \`\`\`js
19
+ const x = 1
20
+ \`\`\`"
21
+ `);
22
+ });
23
+ test('multiple items - code in middle item only', () => {
24
+ const input = `- Item 1
25
+ - Item 2
26
+ \`\`\`js
27
+ const x = 1
28
+ \`\`\`
29
+ - Item 3`;
30
+ const result = unnestCodeBlocksFromLists(input);
31
+ expect(result).toMatchInlineSnapshot(`
32
+ "- Item 1
33
+ - Item 2
34
+
35
+ \`\`\`js
36
+ const x = 1
37
+ \`\`\`
38
+ - Item 3"
39
+ `);
40
+ });
41
+ test('multiple code blocks in one item', () => {
42
+ const input = `- Item with two code blocks
43
+ \`\`\`js
44
+ const a = 1
45
+ \`\`\`
46
+ \`\`\`python
47
+ b = 2
48
+ \`\`\``;
49
+ const result = unnestCodeBlocksFromLists(input);
50
+ expect(result).toMatchInlineSnapshot(`
51
+ "- Item with two code blocks
52
+
53
+ \`\`\`js
54
+ const a = 1
55
+ \`\`\`
56
+ \`\`\`python
57
+ b = 2
58
+ \`\`\`"
59
+ `);
60
+ });
61
+ test('nested list with code', () => {
62
+ const input = `- Item 1
63
+ - Nested item
64
+ \`\`\`js
65
+ const x = 1
66
+ \`\`\`
67
+ - Item 2`;
68
+ const result = unnestCodeBlocksFromLists(input);
69
+ expect(result).toMatchInlineSnapshot(`
70
+ "- Item 1
71
+ - Nested item
72
+
73
+ \`\`\`js
74
+ const x = 1
75
+ \`\`\`
76
+ - Item 2"
77
+ `);
78
+ });
79
+ test('ordered list preserves numbering', () => {
80
+ const input = `1. First item
81
+ \`\`\`js
82
+ const a = 1
83
+ \`\`\`
84
+ 2. Second item
85
+ 3. Third item`;
86
+ const result = unnestCodeBlocksFromLists(input);
87
+ expect(result).toMatchInlineSnapshot(`
88
+ "1. First item
89
+
90
+ \`\`\`js
91
+ const a = 1
92
+ \`\`\`
93
+ 2. Second item
94
+ 3. Third item"
95
+ `);
96
+ });
97
+ test('list without code blocks unchanged', () => {
98
+ const input = `- Item 1
99
+ - Item 2
100
+ - Item 3`;
101
+ const result = unnestCodeBlocksFromLists(input);
102
+ expect(result).toMatchInlineSnapshot(`
103
+ "- Item 1
104
+ - Item 2
105
+ - Item 3"
106
+ `);
107
+ });
108
+ test('mixed - some items have code, some dont', () => {
109
+ const input = `- Normal item
110
+ - Item with code
111
+ \`\`\`js
112
+ const x = 1
113
+ \`\`\`
114
+ - Another normal item
115
+ - Another with code
116
+ \`\`\`python
117
+ y = 2
118
+ \`\`\``;
119
+ const result = unnestCodeBlocksFromLists(input);
120
+ expect(result).toMatchInlineSnapshot(`
121
+ "- Normal item
122
+ - Item with code
123
+
124
+ \`\`\`js
125
+ const x = 1
126
+ \`\`\`
127
+ - Another normal item
128
+ - Another with code
129
+
130
+ \`\`\`python
131
+ y = 2
132
+ \`\`\`"
133
+ `);
134
+ });
135
+ test('text before and after code in same item', () => {
136
+ const input = `- Start text
137
+ \`\`\`js
138
+ const x = 1
139
+ \`\`\`
140
+ End text`;
141
+ const result = unnestCodeBlocksFromLists(input);
142
+ expect(result).toMatchInlineSnapshot(`
143
+ "- Start text
144
+
145
+ \`\`\`js
146
+ const x = 1
147
+ \`\`\`
148
+ - End text"
149
+ `);
150
+ });
151
+ test('preserves content outside lists', () => {
152
+ const input = `# Heading
153
+
154
+ Some paragraph text.
155
+
156
+ - List item
157
+ \`\`\`js
158
+ const x = 1
159
+ \`\`\`
160
+
161
+ More text after.`;
162
+ const result = unnestCodeBlocksFromLists(input);
163
+ expect(result).toMatchInlineSnapshot(`
164
+ "# Heading
165
+
166
+ Some paragraph text.
167
+
168
+ - List item
169
+
170
+ \`\`\`js
171
+ const x = 1
172
+ \`\`\`
173
+
174
+ More text after."
175
+ `);
176
+ });
177
+ test('code block at root level unchanged', () => {
178
+ const input = `\`\`\`js
179
+ const x = 1
180
+ \`\`\``;
181
+ const result = unnestCodeBlocksFromLists(input);
182
+ expect(result).toMatchInlineSnapshot(`
183
+ "\`\`\`js
184
+ const x = 1
185
+ \`\`\`"
186
+ `);
187
+ });
188
+ test('handles code block without language', () => {
189
+ const input = `- Item
190
+ \`\`\`
191
+ plain code
192
+ \`\`\``;
193
+ const result = unnestCodeBlocksFromLists(input);
194
+ expect(result).toMatchInlineSnapshot(`
195
+ "- Item
196
+
197
+ \`\`\`
198
+ plain code
199
+ \`\`\`"
200
+ `);
201
+ });
202
+ test('handles empty list item with code', () => {
203
+ const input = `- \`\`\`js
204
+ const x = 1
205
+ \`\`\``;
206
+ const result = unnestCodeBlocksFromLists(input);
207
+ expect(result).toMatchInlineSnapshot(`
208
+ "\`\`\`js
209
+ const x = 1
210
+ \`\`\`"
211
+ `);
212
+ });
213
+ test('numbered list with text after code block', () => {
214
+ const input = `1. First item
215
+ \`\`\`js
216
+ const a = 1
217
+ \`\`\`
218
+ Text after the code
219
+ 2. Second item`;
220
+ const result = unnestCodeBlocksFromLists(input);
221
+ expect(result).toMatchInlineSnapshot(`
222
+ "1. First item
223
+
224
+ \`\`\`js
225
+ const a = 1
226
+ \`\`\`
227
+ - Text after the code
228
+ 2. Second item"
229
+ `);
230
+ });
231
+ test('numbered list with multiple code blocks and text between', () => {
232
+ const input = `1. First item
233
+ \`\`\`js
234
+ const a = 1
235
+ \`\`\`
236
+ Middle text
237
+ \`\`\`python
238
+ b = 2
239
+ \`\`\`
240
+ Final text
241
+ 2. Second item`;
242
+ const result = unnestCodeBlocksFromLists(input);
243
+ expect(result).toMatchInlineSnapshot(`
244
+ "1. First item
245
+
246
+ \`\`\`js
247
+ const a = 1
248
+ \`\`\`
249
+ - Middle text
250
+
251
+ \`\`\`python
252
+ b = 2
253
+ \`\`\`
254
+ - Final text
255
+ 2. Second item"
256
+ `);
257
+ });
258
+ test('unordered list with multiple code blocks and text between', () => {
259
+ const input = `- First item
260
+ \`\`\`js
261
+ const a = 1
262
+ \`\`\`
263
+ Middle text
264
+ \`\`\`python
265
+ b = 2
266
+ \`\`\`
267
+ Final text
268
+ - Second item`;
269
+ const result = unnestCodeBlocksFromLists(input);
270
+ expect(result).toMatchInlineSnapshot(`
271
+ "- First item
272
+
273
+ \`\`\`js
274
+ const a = 1
275
+ \`\`\`
276
+ - Middle text
277
+
278
+ \`\`\`python
279
+ b = 2
280
+ \`\`\`
281
+ - Final text
282
+ - Second item"
283
+ `);
284
+ });
285
+ test('numbered list starting from 5', () => {
286
+ const input = `5. Fifth item
287
+ \`\`\`js
288
+ code
289
+ \`\`\`
290
+ Text after
291
+ 6. Sixth item`;
292
+ const result = unnestCodeBlocksFromLists(input);
293
+ expect(result).toMatchInlineSnapshot(`
294
+ "5. Fifth item
295
+
296
+ \`\`\`js
297
+ code
298
+ \`\`\`
299
+ - Text after
300
+ 6. Sixth item"
301
+ `);
302
+ });
303
+ test('deeply nested list with code', () => {
304
+ const input = `- Level 1
305
+ - Level 2
306
+ - Level 3
307
+ \`\`\`js
308
+ deep code
309
+ \`\`\`
310
+ Text after deep code
311
+ - Another level 3
312
+ - Back to level 2`;
313
+ const result = unnestCodeBlocksFromLists(input);
314
+ expect(result).toMatchInlineSnapshot(`
315
+ "- Level 1
316
+ - Level 2
317
+ - Level 3
318
+
319
+ \`\`\`js
320
+ deep code
321
+ \`\`\`
322
+ - Text after deep code
323
+ - Another level 3
324
+ - Back to level 2"
325
+ `);
326
+ });
327
+ test('nested numbered list inside unordered with code', () => {
328
+ const input = `- Unordered item
329
+ 1. Nested numbered
330
+ \`\`\`js
331
+ code
332
+ \`\`\`
333
+ Text after
334
+ 2. Second nested
335
+ - Another unordered`;
336
+ const result = unnestCodeBlocksFromLists(input);
337
+ expect(result).toMatchInlineSnapshot(`
338
+ "- Unordered item
339
+ 1. Nested numbered
340
+
341
+ \`\`\`js
342
+ code
343
+ \`\`\`
344
+ - Text after
345
+ 2. Second nested
346
+ - Another unordered"
347
+ `);
348
+ });
349
+ test('code block at end of numbered item no text after', () => {
350
+ const input = `1. First with text
351
+ \`\`\`js
352
+ code here
353
+ \`\`\`
354
+ 2. Second item
355
+ 3. Third item`;
356
+ const result = unnestCodeBlocksFromLists(input);
357
+ expect(result).toMatchInlineSnapshot(`
358
+ "1. First with text
359
+
360
+ \`\`\`js
361
+ code here
362
+ \`\`\`
363
+ 2. Second item
364
+ 3. Third item"
365
+ `);
366
+ });
367
+ test('multiple items each with code and text after', () => {
368
+ const input = `1. First
369
+ \`\`\`js
370
+ code1
371
+ \`\`\`
372
+ After first
373
+ 2. Second
374
+ \`\`\`python
375
+ code2
376
+ \`\`\`
377
+ After second
378
+ 3. Third no code`;
379
+ const result = unnestCodeBlocksFromLists(input);
380
+ expect(result).toMatchInlineSnapshot(`
381
+ "1. First
382
+
383
+ \`\`\`js
384
+ code1
385
+ \`\`\`
386
+ - After first
387
+ 2. Second
388
+
389
+ \`\`\`python
390
+ code2
391
+ \`\`\`
392
+ - After second
393
+ 3. Third no code"
394
+ `);
395
+ });
396
+ test('code block immediately after list marker', () => {
397
+ const input = `1. \`\`\`js
398
+ immediate code
399
+ \`\`\`
400
+ 2. Normal item`;
401
+ const result = unnestCodeBlocksFromLists(input);
402
+ expect(result).toMatchInlineSnapshot(`
403
+ "\`\`\`js
404
+ immediate code
405
+ \`\`\`
406
+ 2. Normal item"
407
+ `);
408
+ });
409
+ test('code block with filename metadata', () => {
410
+ const input = `- Item with code
411
+ \`\`\`tsx filename=example.tsx
412
+ const x = 1
413
+ \`\`\``;
414
+ const result = unnestCodeBlocksFromLists(input);
415
+ expect(result).toMatchInlineSnapshot(`
416
+ "- Item with code
417
+
418
+ \`\`\`tsx filename=example.tsx
419
+ const x = 1
420
+ \`\`\`"
421
+ `);
422
+ });
423
+ test('numbered list with filename metadata code block', () => {
424
+ const input = `1. First item
425
+ \`\`\`tsx filename=app.tsx
426
+ export default function App() {}
427
+ \`\`\`
428
+ 2. Second item`;
429
+ const result = unnestCodeBlocksFromLists(input);
430
+ expect(result).toMatchInlineSnapshot(`
431
+ "1. First item
432
+
433
+ \`\`\`tsx filename=app.tsx
434
+ export default function App() {}
435
+ \`\`\`
436
+ 2. Second item"
437
+ `);
438
+ });
439
+ test('inline fence in list item stays inline (discord formatting issue)', () => {
440
+ const input = `- File: playwriter/src/aria-snapshot.ts
441
+ - Add helper function (~line 477, after isTextRole):\`\`\`ts
442
+ function isSubstringOfAny(needle: string, haystack: Set<string>): boolean {
443
+ for (const str of haystack) {
444
+ if (str.includes(needle)) {
445
+ return true
446
+ }
447
+ }
448
+ return false
449
+ }
450
+ \`\`\``;
451
+ const result = unnestCodeBlocksFromLists(input);
452
+ expect(result).toMatchInlineSnapshot(`
453
+ "- File: playwriter/src/aria-snapshot.ts
454
+ - Add helper function (~line 477, after isTextRole):\`\`\`ts
455
+ function isSubstringOfAny(needle: string, haystack: Set<string>): boolean {
456
+ for (const str of haystack) {
457
+ if (str.includes(needle)) {
458
+ return true
459
+ }
460
+ }
461
+ return false
462
+ }
463
+ \`\`\`"
464
+ `);
465
+ });
466
+ test('numbered list with ) delimiter and code block after continuation text', () => {
467
+ const input = `What to test (no mocks, real processes):
468
+
469
+ 1) **"Older client must not kill newer server"**
470
+ - Start a tiny HTTP server on an ephemeral port that serves \`/version\` as something **higher than** our current version.
471
+ - Run \`ensureRelayServer({ restartOnVersionMismatch: true })\` pointed at that port.
472
+ - Assert:
473
+ - the server is **still listening** afterward (port not killed)
474
+ - no relay is spawned / no kill attempted
475
+ This directly exercises:
476
+ \`\`\`ts
477
+ if (serverVersion !== null && compareVersions(serverVersion, VERSION) > 0) return
478
+ \`\`\`
479
+
480
+ 2) **"Newer client may restart older server (when allowed)"**
481
+ - Start a tiny HTTP server that returns a **lower** \`/version\`.
482
+ - Call \`ensureRelayServer({ restartOnVersionMismatch: true })\`.
483
+ - Assert the old server gets killed (port frees).`;
484
+ const result = unnestCodeBlocksFromLists(input);
485
+ expect('\n' + result).toMatchInlineSnapshot(`
486
+ "
487
+ What to test (no mocks, real processes):
488
+
489
+ 1) **"Older client must not kill newer server"**
490
+ - Start a tiny HTTP server on an ephemeral port that serves \`/version\` as something **higher than** our current version.
491
+ - Run \`ensureRelayServer({ restartOnVersionMismatch: true })\` pointed at that port.
492
+ - Assert:
493
+ - the server is **still listening** afterward (port not killed)
494
+ - no relay is spawned / no kill attempted
495
+ This directly exercises:
496
+ \`\`\`ts
497
+ if (serverVersion !== null && compareVersions(serverVersion, VERSION) > 0) return
498
+ \`\`\`
499
+
500
+ 2) **"Newer client may restart older server (when allowed)"**
501
+ - Start a tiny HTTP server that returns a **lower** \`/version\`.
502
+ - Call \`ensureRelayServer({ restartOnVersionMismatch: true })\`.
503
+ - Assert the old server gets killed (port frees)."
504
+ `);
505
+ // Desired Discord formatting:
506
+ // - Preserve newline between the "1) ..." line and the nested "- Start..." list
507
+ // - Keep fenced code blocks on their own lines (not glued to surrounding text)
508
+ expect(result).toContain('1) **"Older client must not kill newer server"**\n');
509
+ expect(result).toContain('\n- Start a tiny HTTP server');
510
+ expect(result).toContain('\nThis directly exercises:\n');
511
+ expect(result).toMatch(/\n```ts\n[\s\S]*\n```\n/);
512
+ // Regression: these are the two failure modes seen in the session message
513
+ expect(result).not.toContain('**"Older client must not kill newer server"**- Start');
514
+ expect(result).not.toContain('exercises:```');
515
+ });
516
+ test('unordered list with blank line before fenced code block', () => {
517
+ const input = `- Item with spacing
518
+
519
+ \`\`\`ts
520
+ const x = 1
521
+ \`\`\`
522
+ - Next item`;
523
+ const result = unnestCodeBlocksFromLists(input);
524
+ expect('\n' + result).toMatchInlineSnapshot(`
525
+ "
526
+ - Item with spacing
527
+
528
+ \`\`\`ts
529
+ const x = 1
530
+ \`\`\`
531
+ - Next item"
532
+ `);
533
+ });
534
+ test('ordered list item containing fenced code and trailing paragraph', () => {
535
+ const input = `1) Item title
536
+ \`\`\`js
537
+ console.log('hi')
538
+ \`\`\`
539
+ trailing text
540
+ 2) Second item`;
541
+ const result = unnestCodeBlocksFromLists(input);
542
+ expect('\n' + result).toMatchInlineSnapshot(`
543
+ "
544
+ 1. Item title
545
+
546
+ \`\`\`js
547
+ console.log('hi')
548
+ \`\`\`
549
+ - trailing text
550
+ 2) Second item"
551
+ `);
552
+ });
553
+ test('two top-level lists back-to-back (ensure newline between them)', () => {
554
+ const input = `- a
555
+ - b
556
+ 1) c
557
+ - d`;
558
+ const result = unnestCodeBlocksFromLists(input);
559
+ expect('\n' + result).toMatchInlineSnapshot(`
560
+ "
561
+ - a
562
+ - b
563
+ 1) c
564
+ - d"
565
+ `);
566
+ });
567
+ test('top-level list followed by top-level fenced code then paragraph', () => {
568
+ const input = `- Item
569
+ \`\`\`ts
570
+ type X = { a: 1 }
571
+ \`\`\`
572
+ After.`;
573
+ const result = unnestCodeBlocksFromLists(input);
574
+ expect('\n' + result).toMatchInlineSnapshot(`
575
+ "
576
+ - Item
577
+ \`\`\`ts
578
+ type X = { a: 1 }
579
+ \`\`\`
580
+ After."
581
+ `);
582
+ });
583
+ test('task list item with fenced code', () => {
584
+ const input = `- [ ] Do thing
585
+ \`\`\`sh
586
+ echo hi
587
+ \`\`\``;
588
+ const result = unnestCodeBlocksFromLists(input);
589
+ expect('\n' + result).toMatchInlineSnapshot(`
590
+ "
591
+ - [ ] Do thing
592
+
593
+ \`\`\`sh
594
+ echo hi
595
+ \`\`\`"
596
+ `);
597
+ });
598
+ test('checked task list item keeps a single checkbox marker', () => {
599
+ const input = `- [x] Ship fix
600
+ \`\`\`ts
601
+ console.log('done')
602
+ \`\`\``;
603
+ const result = unnestCodeBlocksFromLists(input);
604
+ expect('\n' + result).toMatchInlineSnapshot(`
605
+ "
606
+ - [x] Ship fix
607
+
608
+ \`\`\`ts
609
+ console.log('done')
610
+ \`\`\`"
611
+ `);
612
+ });
613
+ test('task list item with trailing text keeps one checkbox marker after hoisting code', () => {
614
+ const input = `- [ ] Do thing
615
+ \`\`\`sh
616
+ echo hi
617
+ \`\`\`
618
+ then report back`;
619
+ const result = unnestCodeBlocksFromLists(input);
620
+ expect('\n' + result).toMatchInlineSnapshot(`
621
+ "
622
+ - [ ] Do thing
623
+
624
+ \`\`\`sh
625
+ echo hi
626
+ \`\`\`
627
+ - then report back"
628
+ `);
629
+ });
630
+ test('fenced code block indented more than list marker', () => {
631
+ const input = `- Item
632
+ \`\`\`ts
633
+ const x = 1
634
+ \`\`\`
635
+ - Next`;
636
+ const result = unnestCodeBlocksFromLists(input);
637
+ expect('\n' + result).toMatchInlineSnapshot(`
638
+ "
639
+ - Item
640
+
641
+ \`\`\`ts
642
+ const x = 1
643
+ \`\`\`
644
+ - Next"
645
+ `);
646
+ });
647
+ test('ordered list with multiple paragraphs and code', () => {
648
+ const input = `1) Title
649
+ First paragraph.
650
+
651
+ Second paragraph.
652
+
653
+ \`\`\`ts
654
+ const x = 1
655
+ \`\`\`
656
+
657
+ After code.
658
+ 2) Next`;
659
+ const result = unnestCodeBlocksFromLists(input);
660
+ expect('\n' + result).toMatchInlineSnapshot(`
661
+ "
662
+ 1. Title
663
+ First paragraph.
664
+
665
+ Second paragraph.
666
+
667
+ \`\`\`ts
668
+ const x = 1
669
+ \`\`\`
670
+ - After code.
671
+ 2) Next"
672
+ `);
673
+ });