mixdog 0.9.3 → 0.9.4

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 (329) hide show
  1. package/package.json +7 -3
  2. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  3. package/scripts/bench/lead-review-tasks.json +20 -0
  4. package/scripts/bench/r4-mixed-tasks.json +20 -0
  5. package/scripts/bench/review-tasks.json +20 -0
  6. package/scripts/bench/round-codex.json +114 -0
  7. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  8. package/scripts/bench/round-mixdog-lead.json +269 -0
  9. package/scripts/bench/round-mixdog.json +126 -0
  10. package/scripts/bench/round-r10-bigsample.json +679 -0
  11. package/scripts/bench/round-r11-codexalign.json +257 -0
  12. package/scripts/bench/round-r4-codex.json +114 -0
  13. package/scripts/bench/round-r4-mixed.json +225 -0
  14. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  15. package/scripts/bench/round-r6-codex.json +114 -0
  16. package/scripts/bench/round-r6-solo.json +257 -0
  17. package/scripts/bench/round-r7-full.json +254 -0
  18. package/scripts/bench/round-r8-fulldefault.json +255 -0
  19. package/scripts/bench-run.mjs +215 -29
  20. package/scripts/freevar-smoke.mjs +95 -0
  21. package/scripts/internal-comms-bench.mjs +1 -0
  22. package/scripts/internal-comms-smoke.mjs +10 -9
  23. package/scripts/mouse-probe.mjs +45 -0
  24. package/scripts/output-style-bench.mjs +13 -6
  25. package/scripts/output-style-smoke.mjs +4 -4
  26. package/scripts/provider-toolcall-test.mjs +7 -3
  27. package/scripts/recall-usecase-cases.json +18 -0
  28. package/scripts/recall-usecase-probe.json +6 -0
  29. package/scripts/session-bench.mjs +152 -6
  30. package/scripts/tool-smoke.mjs +23 -63
  31. package/scripts/tui-render-smoke.mjs +90 -0
  32. package/scripts/webhook-smoke.mjs +208 -0
  33. package/src/agents/debugger/AGENT.md +4 -1
  34. package/src/agents/heavy-worker/AGENT.md +6 -5
  35. package/src/agents/maintainer/AGENT.md +4 -0
  36. package/src/agents/reviewer/AGENT.md +2 -1
  37. package/src/agents/worker/AGENT.md +8 -4
  38. package/src/lib/rules-builder.cjs +4 -0
  39. package/src/mixdog-session-runtime.mjs +632 -2042
  40. package/src/output-styles/default.md +34 -9
  41. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  42. package/src/output-styles/minimal.md +4 -1
  43. package/src/output-styles/simple.md +22 -7
  44. package/src/rules/agent/00-common.md +2 -0
  45. package/src/rules/lead/lead-brief.md +12 -0
  46. package/src/rules/lead/lead-tool.md +0 -11
  47. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  49. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  50. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  51. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  52. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  53. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  54. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  55. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  60. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  62. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  63. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  65. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  66. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  67. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  68. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
  77. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  78. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  79. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  80. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  83. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  84. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  85. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  87. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  88. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  89. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  90. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  91. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  93. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  94. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  95. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  97. package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
  98. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  99. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  100. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  101. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  102. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  103. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  104. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  105. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  106. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  109. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
  110. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  118. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  120. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  122. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  123. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  125. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  126. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  127. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  128. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  129. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  130. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  131. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  132. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  133. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  134. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  135. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  136. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  137. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  138. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  139. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  140. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  141. package/src/runtime/channels/backends/discord.mjs +12 -292
  142. package/src/runtime/channels/index.mjs +229 -663
  143. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  144. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  145. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  146. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  147. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  148. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  149. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  150. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  151. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  152. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  153. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  154. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  155. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  156. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  157. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  158. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  159. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  160. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  161. package/src/runtime/channels/lib/webhook.mjs +36 -570
  162. package/src/runtime/channels/tool-defs.mjs +11 -130
  163. package/src/runtime/memory/index.mjs +201 -1948
  164. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  165. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  166. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  167. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  168. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  169. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  170. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  171. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  172. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  173. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  174. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  175. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  177. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  178. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  179. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  180. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  181. package/src/runtime/memory/tool-defs.mjs +5 -13
  182. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  183. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  184. package/src/runtime/search/lib/web-tools.mjs +24 -602
  185. package/src/runtime/shared/atomic-file.mjs +26 -1
  186. package/src/runtime/shared/launcher-control.mjs +2 -2
  187. package/src/runtime/shared/tool-primitives.mjs +308 -0
  188. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  189. package/src/runtime/shared/tool-surface.mjs +80 -898
  190. package/src/runtime/shared/transcript-writer.mjs +23 -0
  191. package/src/runtime/shared/update-checker.mjs +7 -4
  192. package/src/session-runtime/config-helpers.mjs +84 -2
  193. package/src/session-runtime/config-lifecycle.mjs +232 -0
  194. package/src/session-runtime/cwd-plugins.mjs +226 -0
  195. package/src/session-runtime/mcp-glue.mjs +177 -0
  196. package/src/session-runtime/model-recency.mjs +111 -0
  197. package/src/session-runtime/native-search.mjs +247 -0
  198. package/src/session-runtime/output-styles.mjs +11 -9
  199. package/src/session-runtime/prewarm.mjs +142 -0
  200. package/src/session-runtime/provider-models.mjs +278 -0
  201. package/src/session-runtime/provider-usage.mjs +120 -0
  202. package/src/session-runtime/quick-model-rows.mjs +170 -0
  203. package/src/session-runtime/quick-search-models.mjs +46 -0
  204. package/src/session-runtime/session-hooks.mjs +93 -0
  205. package/src/session-runtime/settings-api.mjs +319 -0
  206. package/src/session-runtime/tool-catalog.mjs +29 -29
  207. package/src/session-runtime/tool-defs.mjs +84 -0
  208. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  209. package/src/standalone/agent-tool/helpers.mjs +237 -0
  210. package/src/standalone/agent-tool/notify.mjs +107 -0
  211. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  212. package/src/standalone/agent-tool/render.mjs +152 -0
  213. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  214. package/src/standalone/agent-tool.mjs +110 -671
  215. package/src/standalone/channel-worker.mjs +4 -7
  216. package/src/standalone/explore-tool.mjs +30 -9
  217. package/src/standalone/hook-bus/config.mjs +207 -0
  218. package/src/standalone/hook-bus/constants.mjs +90 -0
  219. package/src/standalone/hook-bus/handlers.mjs +481 -0
  220. package/src/standalone/hook-bus/payload.mjs +31 -0
  221. package/src/standalone/hook-bus/rules.mjs +77 -0
  222. package/src/standalone/hook-bus.mjs +77 -870
  223. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  224. package/src/standalone/opencode-go-login.mjs +5 -1
  225. package/src/standalone/provider-admin.mjs +1 -16
  226. package/src/standalone/usage-dashboard.mjs +3 -1
  227. package/src/tui/App.jsx +945 -8094
  228. package/src/tui/app/app-format.mjs +206 -0
  229. package/src/tui/app/channel-pickers.mjs +510 -0
  230. package/src/tui/app/clipboard.mjs +67 -0
  231. package/src/tui/app/core-memory-picker.mjs +210 -0
  232. package/src/tui/app/extension-pickers.mjs +506 -0
  233. package/src/tui/app/input-parsers.mjs +193 -0
  234. package/src/tui/app/maintenance-pickers.mjs +324 -0
  235. package/src/tui/app/model-options.mjs +330 -0
  236. package/src/tui/app/model-picker.mjs +365 -0
  237. package/src/tui/app/onboarding-steps.mjs +400 -0
  238. package/src/tui/app/project-picker.mjs +247 -0
  239. package/src/tui/app/provider-setup-picker.mjs +580 -0
  240. package/src/tui/app/resume-picker.mjs +55 -0
  241. package/src/tui/app/route-pickers.mjs +419 -0
  242. package/src/tui/app/settings-picker.mjs +490 -0
  243. package/src/tui/app/slash-commands.mjs +101 -0
  244. package/src/tui/app/slash-dispatch.mjs +427 -0
  245. package/src/tui/app/text-layout.mjs +46 -0
  246. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  247. package/src/tui/app/transcript-window.mjs +671 -0
  248. package/src/tui/app/use-mouse-input.mjs +460 -0
  249. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  250. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  251. package/src/tui/app/use-transcript-window.mjs +589 -0
  252. package/src/tui/components/ConfirmBar.jsx +1 -1
  253. package/src/tui/components/Picker.jsx +32 -4
  254. package/src/tui/components/PromptInput.jsx +23 -101
  255. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  256. package/src/tui/components/StatusLine.jsx +63 -12
  257. package/src/tui/components/TextEntryPanel.jsx +11 -0
  258. package/src/tui/components/ToolExecution.jsx +52 -594
  259. package/src/tui/components/TranscriptItem.jsx +105 -0
  260. package/src/tui/components/UsagePanel.jsx +18 -4
  261. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  262. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  263. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  264. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  265. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  266. package/src/tui/display-width.mjs +20 -3
  267. package/src/tui/dist/index.mjs +19652 -18630
  268. package/src/tui/engine/agent-job-feed.mjs +133 -0
  269. package/src/tui/engine/notification-plan.mjs +76 -0
  270. package/src/tui/engine/render-timing.mjs +17 -0
  271. package/src/tui/engine/tool-approval.mjs +94 -0
  272. package/src/tui/engine/tool-card-results.mjs +234 -0
  273. package/src/tui/engine/tool-result-status.mjs +135 -0
  274. package/src/tui/engine.mjs +122 -562
  275. package/src/tui/figures.mjs +5 -0
  276. package/src/tui/index.jsx +105 -0
  277. package/src/tui/input-editing.mjs +2 -2
  278. package/src/tui/markdown/format-token.mjs +4 -1
  279. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  280. package/src/tui/theme.mjs +6 -0
  281. package/src/ui/statusline-agents.mjs +213 -0
  282. package/src/ui/statusline-format.mjs +146 -0
  283. package/src/ui/statusline-segments.mjs +148 -0
  284. package/src/ui/statusline.mjs +67 -501
  285. package/src/ui/tool-card.mjs +0 -1
  286. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  287. package/src/workflows/default/WORKFLOW.md +1 -1
  288. package/src/workflows/sequential/WORKFLOW.md +1 -1
  289. package/vendor/ink/build/display-width.js +19 -3
  290. package/vendor/ink/build/ink.js +103 -6
  291. package/vendor/ink/build/log-update.js +17 -3
  292. package/vendor/ink/build/wrap-text.js +125 -0
  293. package/scripts/_test-folder-dialog.mjs +0 -30
  294. package/scripts/fix-brief-fn.mjs +0 -35
  295. package/scripts/fix-format-tool-surface.mjs +0 -24
  296. package/scripts/fix-tool-exec-visible.mjs +0 -42
  297. package/scripts/patch-agent-brief.mjs +0 -48
  298. package/scripts/patch-app.mjs +0 -21
  299. package/scripts/patch-app2.mjs +0 -18
  300. package/scripts/patch-dist-brief.mjs +0 -96
  301. package/scripts/patch-tool-exec.mjs +0 -70
  302. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  303. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  304. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  305. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  306. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  307. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  308. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  309. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  310. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  311. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  312. package/src/standalone/agent-task-status.test.mjs +0 -76
  313. package/src/tui/components/tool-output-format.test.mjs +0 -399
  314. package/src/tui/display-width.test.mjs +0 -35
  315. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  316. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  317. package/src/tui/input-editing.selection.test.mjs +0 -75
  318. package/src/tui/markdown/format-token.test.mjs +0 -354
  319. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  320. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  321. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  322. package/src/tui/paste-fix.test.mjs +0 -119
  323. package/src/tui/prompt-history-store.test.mjs +0 -52
  324. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  325. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  326. package/src/ui/markdown.test.mjs +0 -70
  327. package/src/ui/statusline-context-label.test.mjs +0 -15
  328. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  329. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,505 @@
1
+ /**
2
+ * gemini-stream.mjs — Gemini stream consumption (REST + SDK) and stream guards.
3
+ *
4
+ * Extracted from gemini.mjs. Owns chunk aggregation, completion assertions,
5
+ * timeout/truncation error shapes and the text leak guard that recovers
6
+ * tool calls emitted as plain text. gemini.mjs imports the consumer entry
7
+ * points; parseToolCalls/emitGeminiToolCalls stay in gemini.mjs and are
8
+ * imported back (function-level, no init-time cycle).
9
+ */
10
+ import {
11
+ PROVIDER_FIRST_BYTE_TIMEOUT_MS,
12
+ PROVIDER_MAX_BEFORE_WARN_MS,
13
+ PROVIDER_SSE_IDLE_TIMEOUT_MS,
14
+ PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
15
+ providerTimeoutError,
16
+ resolveTimeoutMs,
17
+ } from '../stall-policy.mjs';
18
+ import { scanLeakedToolCalls } from './anthropic-leaked-toolcall.mjs';
19
+ import { traceHash, stableTraceStringify } from './trace-utils.mjs';
20
+ import { parseToolCalls, emitGeminiToolCalls, collectGeminiGroundingSources } from './gemini.mjs';
21
+
22
+ export const GEMINI_FIRST_BYTE_TIMEOUT_MS = resolveTimeoutMs(
23
+ 'MIXDOG_GEMINI_FIRST_BYTE_TIMEOUT_MS',
24
+ PROVIDER_FIRST_BYTE_TIMEOUT_MS,
25
+ { minMs: 30_000, maxMs: PROVIDER_MAX_BEFORE_WARN_MS },
26
+ );
27
+
28
+ export function geminiTimeoutError(label, timeoutMs) {
29
+ const err = providerTimeoutError(label, timeoutMs);
30
+ err.name = 'GeminiTimeoutError';
31
+ err.code = 'EGEMINITIMEOUT';
32
+ return err;
33
+ }
34
+
35
+ export function geminiTruncatedStreamError(message) {
36
+ return Object.assign(
37
+ new Error(message),
38
+ { name: 'TruncatedStreamError', code: 'TRUNCATED_STREAM', truncatedStream: true },
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Aggregate streamed GenerateContentResponse chunks into one response object
44
+ * (same shape as a non-streaming generateContent JSON body).
45
+ * Mirrors @google/generative-ai aggregateResponses().
46
+ */
47
+ export function aggregateGeminiStreamChunks(responses) {
48
+ const lastResponse = responses[responses.length - 1];
49
+ const aggregatedResponse = {
50
+ promptFeedback: lastResponse?.promptFeedback,
51
+ };
52
+ for (const response of responses) {
53
+ if (response?.candidates) {
54
+ let candidateIndex = 0;
55
+ for (const candidate of response.candidates) {
56
+ if (!aggregatedResponse.candidates) aggregatedResponse.candidates = [];
57
+ if (!aggregatedResponse.candidates[candidateIndex]) {
58
+ aggregatedResponse.candidates[candidateIndex] = { index: candidateIndex };
59
+ }
60
+ const aggCand = aggregatedResponse.candidates[candidateIndex];
61
+ aggCand.citationMetadata = candidate.citationMetadata;
62
+ aggCand.groundingMetadata = candidate.groundingMetadata;
63
+ aggCand.finishReason = candidate.finishReason;
64
+ aggCand.finishMessage = candidate.finishMessage;
65
+ aggCand.safetyRatings = candidate.safetyRatings;
66
+ if (candidate.content?.parts) {
67
+ if (!aggCand.content) {
68
+ aggCand.content = {
69
+ role: candidate.content.role || 'user',
70
+ parts: [],
71
+ };
72
+ }
73
+ for (const part of candidate.content.parts) {
74
+ const newPart = {};
75
+ if (part.text) newPart.text = part.text;
76
+ if (part.functionCall) newPart.functionCall = part.functionCall;
77
+ if (part.thoughtSignature) newPart.thoughtSignature = part.thoughtSignature;
78
+ if (part.thought_signature) newPart.thought_signature = part.thought_signature;
79
+ if (part.executableCode) newPart.executableCode = part.executableCode;
80
+ if (part.codeExecutionResult) newPart.codeExecutionResult = part.codeExecutionResult;
81
+ if (Object.keys(newPart).length === 0) newPart.text = '';
82
+ aggCand.content.parts.push(newPart);
83
+ }
84
+ }
85
+ candidateIndex++;
86
+ }
87
+ }
88
+ if (response?.usageMetadata) aggregatedResponse.usageMetadata = response.usageMetadata;
89
+ }
90
+ return aggregatedResponse;
91
+ }
92
+
93
+ export function assertGeminiStreamCompleted({ sawStreamChunk, finishReason, label }) {
94
+ if (!sawStreamChunk) {
95
+ throw geminiTruncatedStreamError(`${label} truncated: empty stream`);
96
+ }
97
+ if (!finishReason) {
98
+ throw geminiTruncatedStreamError(`${label} truncated: no finishReason`);
99
+ }
100
+ }
101
+
102
+ // Concatenate the text parts of a single streamed Gemini chunk. Used to feed
103
+ // the gateway live-text relay (onTextDelta) with the incremental text payload
104
+ // as each SSE/SDK chunk arrives. Returns '' for tool-call / thought-only /
105
+ // malformed chunks so the caller can skip empty emits.
106
+ export function geminiChunkText(chunk) {
107
+ const parts = chunk?.candidates?.[0]?.content?.parts;
108
+ if (!Array.isArray(parts)) return '';
109
+ let text = '';
110
+ for (const p of parts) {
111
+ if (p && typeof p.text === 'string') text += p.text;
112
+ }
113
+ return text;
114
+ }
115
+
116
+ export function relayGeminiStreamText(t, { onTextDelta, textLeakGuard }) {
117
+ if (!t) return;
118
+ if (textLeakGuard) textLeakGuard.feedText(t);
119
+ else if (onTextDelta) { try { onTextDelta(t); } catch {} }
120
+ }
121
+
122
+ /**
123
+ * Rolling scanner for tool calls leaked as plain XML/antml tags inside Gemini
124
+ * `part.text` streams. Mirrors the Anthropic OAuth guard: suppress tags from
125
+ * visible text, synthesize known-tool calls, dispatch via onToolCall.
126
+ */
127
+ export function createGeminiTextLeakGuard({ knownToolNames, onTextDelta, onToolCall, onStreamDelta }) {
128
+ const _knownTools = knownToolNames instanceof Set
129
+ ? knownToolNames
130
+ : new Set(Array.isArray(knownToolNames) ? knownToolNames : []);
131
+ const _enabled = _knownTools.size > 0;
132
+ const _isKnownTool = (name) => _knownTools.has(name);
133
+ let leakBuffer = '';
134
+ const leakedCalls = [];
135
+ const dispatchedFingerprints = new Set();
136
+
137
+ const toolCallFingerprint = (name, args) => {
138
+ let a = args;
139
+ if (a === null || typeof a !== 'object' || Array.isArray(a)) a = {};
140
+ return traceHash(stableTraceStringify({ name: name || '', args: a }));
141
+ };
142
+
143
+ const dispatchLeakedCall = (recovered) => {
144
+ let args = recovered?.arguments;
145
+ if (args === null || typeof args !== 'object' || Array.isArray(args)) args = {};
146
+ const fp = toolCallFingerprint(recovered.name, args);
147
+ if (dispatchedFingerprints.has(fp)) return;
148
+ dispatchedFingerprints.add(fp);
149
+ const idHash = traceHash(stableTraceStringify({
150
+ name: recovered.name,
151
+ args,
152
+ leak: true,
153
+ })).slice(0, 16);
154
+ const call = {
155
+ id: `gemini_leaked_${idHash}`,
156
+ name: recovered.name,
157
+ arguments: args,
158
+ };
159
+ leakedCalls.push(call);
160
+ try { onToolCall?.(call); } catch {}
161
+ try { onStreamDelta?.(); } catch {}
162
+ };
163
+
164
+ const pumpLeakBuffer = (final) => {
165
+ if (!_enabled) return;
166
+ if (!leakBuffer && !final) return;
167
+ const { emit, calls, rest } = scanLeakedToolCalls(leakBuffer, { isKnownTool: _isKnownTool, final });
168
+ leakBuffer = rest;
169
+ if (emit && onTextDelta) {
170
+ try { onTextDelta(emit); } catch {}
171
+ }
172
+ for (const c of calls) dispatchLeakedCall(c);
173
+ };
174
+
175
+ return {
176
+ get enabled() { return _enabled; },
177
+ feedText(text) {
178
+ if (!text) return;
179
+ if (!_enabled) {
180
+ try { onTextDelta?.(text); } catch {}
181
+ return;
182
+ }
183
+ leakBuffer += text;
184
+ pumpLeakBuffer(false);
185
+ },
186
+ finalize() {
187
+ pumpLeakBuffer(true);
188
+ },
189
+ scrubAssistantText(raw) {
190
+ if (!raw) return '';
191
+ if (!_enabled) return raw;
192
+ const { emit, calls, rest } = scanLeakedToolCalls(raw, { isKnownTool: _isKnownTool, final: true });
193
+ for (const c of calls) dispatchLeakedCall(c);
194
+ return emit + rest;
195
+ },
196
+ filterNativeToolCalls(nativeCalls) {
197
+ if (!_enabled || !nativeCalls?.length) return nativeCalls;
198
+ const kept = [];
199
+ for (const call of nativeCalls) {
200
+ const fp = toolCallFingerprint(call?.name, call?.arguments);
201
+ if (dispatchedFingerprints.has(fp)) continue;
202
+ dispatchedFingerprints.add(fp);
203
+ kept.push(call);
204
+ }
205
+ return kept.length ? kept : undefined;
206
+ },
207
+ getLeakedToolCalls() {
208
+ return leakedCalls.length ? [...leakedCalls] : [];
209
+ },
210
+ };
211
+ }
212
+
213
+ export async function consumeGeminiRestStreamResponse(response, { signal, onStreamDelta, onTextDelta, textLeakGuard, label }) {
214
+ if (!response?.body) throw new Error(`${label}: missing response body`);
215
+ const reader = response.body.getReader();
216
+ const decoder = new TextDecoder();
217
+ let buffer = '';
218
+ const allChunks = [];
219
+ let sawStreamChunk = false;
220
+ let idleTimedOut = false;
221
+ let idleTimer = null;
222
+ let idleReject = null;
223
+
224
+ let firstByteTimer = setTimeout(() => {
225
+ try { reader.cancel('first byte timeout'); } catch {}
226
+ if (idleReject) {
227
+ const e = geminiTimeoutError(`${label} first byte`, GEMINI_FIRST_BYTE_TIMEOUT_MS);
228
+ const r = idleReject; idleReject = null; r(e);
229
+ }
230
+ }, GEMINI_FIRST_BYTE_TIMEOUT_MS);
231
+ if (firstByteTimer.unref) firstByteTimer.unref();
232
+
233
+ const clearFirstByteTimer = () => {
234
+ if (firstByteTimer) {
235
+ clearTimeout(firstByteTimer);
236
+ firstByteTimer = null;
237
+ }
238
+ };
239
+
240
+ const resetIdleTimer = () => {
241
+ if (!PROVIDER_SSE_IDLE_WATCHDOG_ENABLED) return;
242
+ if (idleTimer) clearTimeout(idleTimer);
243
+ idleTimer = setTimeout(() => {
244
+ idleTimedOut = true;
245
+ try { reader.cancel('SSE idle timeout'); } catch {}
246
+ if (idleReject) {
247
+ const e = geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
248
+ const r = idleReject; idleReject = null; r(e);
249
+ }
250
+ }, PROVIDER_SSE_IDLE_TIMEOUT_MS);
251
+ if (idleTimer.unref) idleTimer.unref();
252
+ };
253
+
254
+ const onAbort = () => {
255
+ try {
256
+ const c = reader.cancel('aborted');
257
+ if (c && typeof c.catch === 'function') c.catch(() => {});
258
+ } catch {}
259
+ };
260
+
261
+ if (signal) {
262
+ if (signal.aborted) {
263
+ const reason = signal.reason;
264
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
265
+ }
266
+ signal.addEventListener('abort', onAbort, { once: true });
267
+ }
268
+
269
+ try {
270
+ resetIdleTimer();
271
+ while (true) {
272
+ let chunk;
273
+ try {
274
+ chunk = await new Promise((resolve, reject) => {
275
+ idleReject = reject;
276
+ reader.read().then(resolve, reject);
277
+ });
278
+ } catch (err) {
279
+ if (idleTimedOut) {
280
+ throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
281
+ }
282
+ if (signal?.aborted) {
283
+ const reason = signal.reason;
284
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
285
+ }
286
+ throw err;
287
+ } finally {
288
+ idleReject = null;
289
+ }
290
+ const { done, value } = chunk;
291
+ if (done) break;
292
+ resetIdleTimer();
293
+ buffer += decoder.decode(value, { stream: true });
294
+ let lineEnd;
295
+ while ((lineEnd = buffer.indexOf('\n')) >= 0) {
296
+ let line = buffer.slice(0, lineEnd);
297
+ buffer = buffer.slice(lineEnd + 1);
298
+ line = line.replace(/\r$/, '');
299
+ if (!line.startsWith('data: ')) continue;
300
+ const data = line.slice(6).trim();
301
+ if (!data || data === '[DONE]') continue;
302
+ let parsed;
303
+ try { parsed = JSON.parse(data); } catch { continue; }
304
+ if (!sawStreamChunk) {
305
+ sawStreamChunk = true;
306
+ clearFirstByteTimer();
307
+ }
308
+ allChunks.push(parsed);
309
+ try { onStreamDelta?.(); } catch {}
310
+ if (onTextDelta || textLeakGuard) {
311
+ const t = geminiChunkText(parsed);
312
+ relayGeminiStreamText(t, { onTextDelta, textLeakGuard });
313
+ }
314
+ }
315
+ }
316
+ if (buffer.trim()) {
317
+ const line = buffer.trim().replace(/\r$/, '');
318
+ if (line.startsWith('data: ')) {
319
+ const data = line.slice(6).trim();
320
+ if (data && data !== '[DONE]') {
321
+ try {
322
+ const parsed = JSON.parse(data);
323
+ if (!sawStreamChunk) {
324
+ sawStreamChunk = true;
325
+ clearFirstByteTimer();
326
+ }
327
+ allChunks.push(parsed);
328
+ try { onStreamDelta?.(); } catch {}
329
+ if (onTextDelta || textLeakGuard) {
330
+ const t = geminiChunkText(parsed);
331
+ relayGeminiStreamText(t, { onTextDelta, textLeakGuard });
332
+ }
333
+ } catch { /* skip malformed tail */ }
334
+ }
335
+ }
336
+ }
337
+ } finally {
338
+ clearFirstByteTimer();
339
+ if (idleTimer) clearTimeout(idleTimer);
340
+ if (signal) signal.removeEventListener('abort', onAbort);
341
+ try { reader.releaseLock(); } catch {}
342
+ try { textLeakGuard?.finalize(); } catch {}
343
+ }
344
+
345
+ const aggregated = aggregateGeminiStreamChunks(allChunks);
346
+ const finishReason = aggregated.candidates?.[0]?.finishReason || null;
347
+ assertGeminiStreamCompleted({ sawStreamChunk, finishReason, label });
348
+ return aggregated;
349
+ }
350
+
351
+ export async function consumeGeminiSdkStream(streamResult, { signal, onStreamDelta, onTextDelta, textLeakGuard, label }) {
352
+ let sawStreamChunk = false;
353
+ let idleTimedOut = false;
354
+ let idleTimer = null;
355
+ let firstByteReject = null;
356
+ let firstByteTimer = null;
357
+ let inFlightReject = null;
358
+
359
+ const armFirstByteTimer = () => {
360
+ if (firstByteTimer) clearTimeout(firstByteTimer);
361
+ firstByteTimer = setTimeout(() => {
362
+ if (firstByteReject) {
363
+ const e = geminiTimeoutError(`${label} first byte`, GEMINI_FIRST_BYTE_TIMEOUT_MS);
364
+ const r = firstByteReject; firstByteReject = null; r(e);
365
+ }
366
+ }, GEMINI_FIRST_BYTE_TIMEOUT_MS);
367
+ if (firstByteTimer.unref) firstByteTimer.unref();
368
+ };
369
+
370
+ const clearFirstByteTimer = () => {
371
+ if (firstByteTimer) {
372
+ clearTimeout(firstByteTimer);
373
+ firstByteTimer = null;
374
+ }
375
+ firstByteReject = null;
376
+ };
377
+
378
+ const resetIdleTimer = () => {
379
+ if (!PROVIDER_SSE_IDLE_WATCHDOG_ENABLED) return;
380
+ if (idleTimer) clearTimeout(idleTimer);
381
+ idleTimer = setTimeout(() => {
382
+ idleTimedOut = true;
383
+ if (inFlightReject) {
384
+ const e = geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
385
+ const r = inFlightReject; inFlightReject = null; r(e);
386
+ }
387
+ }, PROVIDER_SSE_IDLE_TIMEOUT_MS);
388
+ if (idleTimer.unref) idleTimer.unref();
389
+ };
390
+
391
+ if (signal?.aborted) {
392
+ const reason = signal.reason;
393
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
394
+ }
395
+
396
+ const iterator = streamResult.stream[Symbol.asyncIterator]();
397
+
398
+ // Wire the abort signal to actually CANCEL iteration (mirror of the REST
399
+ // consumer's onAbort -> reader.cancel). Without this, a parent / client /
400
+ // gateway abort that fires AFTER the first byte — with the SSE idle
401
+ // watchdog off by default — would leave iterator.next() hanging: the loop
402
+ // below only reads `signal` for error translation, never to interrupt the
403
+ // pending read. Rejecting the in-flight promise and returning the iterator
404
+ // releases it promptly even if the SDK is slow to propagate the underlying
405
+ // request abort.
406
+ let onSignalAbort = null;
407
+ if (signal) {
408
+ onSignalAbort = () => {
409
+ const reason = signal.reason;
410
+ const err = reason instanceof Error ? reason : new Error(`${label} aborted`);
411
+ if (inFlightReject) {
412
+ const r = inFlightReject;
413
+ inFlightReject = null;
414
+ firstByteReject = null;
415
+ r(err);
416
+ }
417
+ try {
418
+ const ret = iterator.return?.();
419
+ if (ret && typeof ret.catch === 'function') ret.catch(() => {});
420
+ } catch {}
421
+ };
422
+ signal.addEventListener('abort', onSignalAbort, { once: true });
423
+ }
424
+
425
+ try {
426
+ armFirstByteTimer();
427
+ resetIdleTimer();
428
+ while (true) {
429
+ if (idleTimedOut) {
430
+ throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
431
+ }
432
+ let step;
433
+ try {
434
+ step = await new Promise((resolve, reject) => {
435
+ inFlightReject = reject;
436
+ if (!sawStreamChunk) firstByteReject = reject;
437
+ iterator.next().then(
438
+ (value) => {
439
+ inFlightReject = null;
440
+ firstByteReject = null;
441
+ resolve(value);
442
+ },
443
+ (err) => {
444
+ inFlightReject = null;
445
+ firstByteReject = null;
446
+ reject(err);
447
+ },
448
+ );
449
+ });
450
+ } catch (err) {
451
+ if (idleTimedOut) {
452
+ throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
453
+ }
454
+ if (signal?.aborted) {
455
+ const reason = signal.reason;
456
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
457
+ }
458
+ throw err;
459
+ }
460
+ if (step.done) break;
461
+ if (!sawStreamChunk) {
462
+ sawStreamChunk = true;
463
+ clearFirstByteTimer();
464
+ }
465
+ resetIdleTimer();
466
+ try { onStreamDelta?.(); } catch {}
467
+ if (onTextDelta || textLeakGuard) {
468
+ const t = geminiChunkText(step.value);
469
+ relayGeminiStreamText(t, { onTextDelta, textLeakGuard });
470
+ }
471
+ }
472
+ if (idleTimedOut) {
473
+ throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
474
+ }
475
+ } catch (err) {
476
+ clearFirstByteTimer();
477
+ if (signal?.aborted) {
478
+ const reason = signal.reason;
479
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
480
+ }
481
+ throw err;
482
+ } finally {
483
+ clearFirstByteTimer();
484
+ if (idleTimer) clearTimeout(idleTimer);
485
+ if (signal && onSignalAbort) {
486
+ try { signal.removeEventListener('abort', onSignalAbort); } catch {}
487
+ }
488
+ try { textLeakGuard?.finalize(); } catch {}
489
+ }
490
+
491
+ let response;
492
+ try {
493
+ response = await streamResult.response;
494
+ } catch (err) {
495
+ if (signal?.aborted) {
496
+ const reason = signal.reason;
497
+ throw reason instanceof Error ? reason : new Error(`${label} aborted`);
498
+ }
499
+ throw err;
500
+ }
501
+ const raw = response?.candidates ? response : (response?.response || response);
502
+ const finishReason = raw?.candidates?.[0]?.finishReason || null;
503
+ assertGeminiStreamCompleted({ sawStreamChunk, finishReason, label });
504
+ return raw;
505
+ }