mixdog 1.2.5 → 1.2.6

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 (290) hide show
  1. package/package.json +1 -1
  2. package/scripts/lib/gate-scheduler.mjs +2 -2
  3. package/scripts/lib/run-node-tests.mjs +5 -14
  4. package/scripts/provider-pingpong-live.mjs +9 -9
  5. package/src/agents/advisor/AGENT.md +4 -4
  6. package/src/agents/advisor/agent.json +1 -1
  7. package/src/agents/reviewer/AGENT.md +2 -3
  8. package/src/agents/reviewer/agent.json +1 -1
  9. package/src/defaults/shipped-history.json +10 -0
  10. package/src/defaults/skills/browser-use/SKILL.md +2 -1
  11. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  12. package/src/defaults/skills/visualize/SKILL.md +3 -2
  13. package/src/lib/rules-builder.cjs +1 -1
  14. package/src/output-styles/common.md +22 -6
  15. package/src/output-styles/detailed.md +7 -5
  16. package/src/output-styles/minimal.md +7 -3
  17. package/src/output-styles/simple.md +2 -3
  18. package/src/rules/agent/AGENT.md +3 -1
  19. package/src/rules/lead/LEAD.md +4 -3
  20. package/src/rules/shared/10-tool-workflow.md +2 -1
  21. package/src/rules/shared/60-verification.md +4 -5
  22. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  23. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  24. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  26. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +53 -60
  27. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  28. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  29. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +40 -19
  31. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  32. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  33. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  34. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  35. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  37. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +1 -2
  38. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  39. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  40. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  41. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  42. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  43. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  44. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  45. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  46. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  47. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  49. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  50. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  51. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  53. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  56. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  57. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  58. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  61. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  64. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  65. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  66. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  67. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  68. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  69. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  70. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +2 -3
  71. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +3 -8
  72. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  73. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  74. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  75. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  76. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  77. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  78. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  79. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +9 -19
  80. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -3
  81. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  83. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  84. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  85. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  86. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  87. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  88. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  89. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  90. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  91. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  92. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  93. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  94. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  95. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  96. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  97. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  98. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +306 -0
  99. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +70 -21
  100. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  101. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  102. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  103. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  104. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  105. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  106. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  109. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  111. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  112. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  113. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  115. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  117. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  119. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  120. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  121. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  122. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  123. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  124. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  125. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  126. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  128. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  129. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  130. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  132. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  133. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  134. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  135. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  136. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  137. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +119 -32
  138. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  139. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  140. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  141. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  142. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  143. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  144. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  145. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  146. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  147. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  148. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +40 -2
  149. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -2
  150. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  151. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -1
  153. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +35 -4
  155. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  156. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  157. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  158. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  159. package/src/runtime/attachments/office-extract.mjs +11 -9
  160. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  161. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  162. package/src/runtime/memory/lib/tool-call-handler.mjs +7 -5
  163. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  164. package/src/runtime/office/core/tabular.mjs +1 -1
  165. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  166. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  167. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  168. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  169. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  170. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  171. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  172. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  173. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  174. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  175. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  176. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  177. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  178. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  179. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  180. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  181. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  182. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  183. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  184. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  185. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  186. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  187. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  188. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  189. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  190. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  191. package/src/runtime/shared/atomic-file.mjs +22 -13
  192. package/src/runtime/shared/background-tasks.mjs +60 -20
  193. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  194. package/src/runtime/shared/config.mjs +0 -10
  195. package/src/runtime/shared/file-lock.mjs +7 -1
  196. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  197. package/src/runtime/shared/file-permissions.d.mts +19 -4
  198. package/src/runtime/shared/file-permissions.mjs +310 -4
  199. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  200. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  201. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  202. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  203. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  204. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  205. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  206. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  207. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  208. package/src/runtime/shared/notify-trace.mjs +8 -1
  209. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  210. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  211. package/src/runtime/shared/process-runner.mjs +38 -0
  212. package/src/runtime/shared/provider-accounts.mjs +2 -1
  213. package/src/runtime/shared/provider-api-key.mjs +0 -1
  214. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  215. package/src/runtime/shared/resource-admission.mjs +60 -18
  216. package/src/runtime/shared/run-child.mjs +84 -0
  217. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  218. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  219. package/src/runtime/shared/singleton-owner.mjs +126 -42
  220. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  221. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  222. package/src/runtime/shared/user-data-guard.mjs +28 -9
  223. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  224. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  225. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  226. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  227. package/src/runtime/tidy/runners/biome.mjs +3 -1
  228. package/src/runtime/tidy/shadow.mjs +2 -1
  229. package/src/runtime/web-search/index.mjs +5 -7
  230. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  231. package/src/runtime/web-search/tool-defs.mjs +6 -1
  232. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  233. package/src/session-runtime/lifecycle/session-switch/resume.mjs +7 -4
  234. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  235. package/src/session-runtime/lifecycle/shared.mjs +19 -2
  236. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  237. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  238. package/src/session-runtime/lifecycle-api.mjs +3 -1
  239. package/src/session-runtime/provider-auth/login.mjs +10 -6
  240. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  241. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  242. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  243. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  244. package/src/session-runtime/provider-models.mjs +76 -10
  245. package/src/session-runtime/provider-readiness.mjs +4 -0
  246. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  247. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  248. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  249. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  250. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  251. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  252. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  253. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  254. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  255. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  256. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  257. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  258. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  259. package/src/session-runtime/services/agent-tool.mjs +20 -0
  260. package/src/session-runtime/services/provider-admin.mjs +92 -20
  261. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  262. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  263. package/src/session-runtime/turn/session-ops.mjs +2 -2
  264. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  265. package/src/session-runtime/usage-stats-api.mjs +6 -1
  266. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  267. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  268. package/src/standalone/daemon-owner.mjs +117 -25
  269. package/src/standalone/daemon.mjs +43 -13
  270. package/src/standalone/session-call-cache.mjs +6 -0
  271. package/src/standalone/session-protocol.mjs +1 -1
  272. package/src/standalone/session-service/retention/busy.mjs +7 -2
  273. package/src/standalone/session-transport/routes.mjs +7 -6
  274. package/src/tui/app/model-options.mjs +39 -2
  275. package/src/tui/app/model-picker.mjs +16 -2
  276. package/src/tui/app/provider-setup-picker.mjs +1 -2
  277. package/src/tui/dist/index.mjs +385 -104
  278. package/src/tui/session/notice-text.mjs +0 -1
  279. package/src/tui/session/session-api/intake/submission.mjs +7 -13
  280. package/src/tui/session/session-api/integrations.mjs +1 -5
  281. package/src/tui/session/session-flow/drain.mjs +8 -0
  282. package/src/tui/session/session-flow/steering.mjs +46 -0
  283. package/src/tui/session/session-flow.mjs +2 -0
  284. package/src/tui/session/turn-stream.mjs +33 -0
  285. package/src/tui/session/turn.mjs +30 -3
  286. package/src/ui/statusline.mjs +1 -6
  287. package/src/ui/streaming-markdown-heal.mjs +24 -7
  288. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  289. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  290. package/src/workflows/default/WORKFLOW.md +9 -13
@@ -9,6 +9,7 @@
9
9
  // tree (taskkill /T /F on Windows, SIGTERM->SIGKILL of every descendant on
10
10
  // POSIX). No external dependencies.
11
11
  import { spawn } from 'node:child_process';
12
+ import { runProcessOffThread } from '../../../shared/process-runner.mjs';
12
13
  import { detachedSpawnOpts } from '../../../shared/spawn-flags.mjs';
13
14
 
14
15
  const isWin = process.platform === 'win32';
@@ -44,54 +45,26 @@ function transportPid(transport) {
44
45
  * settled, so enumerators never pile up whatever this value is. */
45
46
  export const ENUMERATION_DEADLINE_MS = 20_000;
46
47
 
47
- // Spawn a helper command and resolve its stdout (empty string on any error).
48
- // `timeoutMs` > 0: past the deadline the helper is killed (Node reaps it on
49
- // exit) and the call settles at once instead of waiting for it.
50
- function run(cmd, args, { strict = false, timeoutMs = 0 } = {}) {
51
- return new Promise((resolve, reject) => {
52
- let out = '';
53
- let cp;
54
- let timer = null;
55
- let settled = false;
56
- const settle = (finish) => {
57
- if (settled) return;
58
- settled = true;
59
- if (timer) clearTimeout(timer);
60
- finish();
61
- };
62
- try {
63
- cp = spawn(cmd, args, { windowsHide: true });
64
- } catch (error) {
65
- if (strict) {
66
- reject(error);
67
- return;
68
- }
69
- resolve('');
70
- return;
71
- }
72
- cp.stdout?.on('data', (d) => {
73
- out += String(d);
74
- });
75
- cp.on('error', (error) => settle(() => (strict ? reject(error) : resolve(out))));
76
- cp.on('close', (code) =>
77
- settle(() => (strict && code !== 0 ? reject(new Error(`Process enumeration failed (${code}).`)) : resolve(out)))
78
- );
79
- if (timeoutMs > 0) {
80
- timer = setTimeout(() => {
81
- try {
82
- cp.kill('SIGKILL');
83
- } catch {
84
- /* already gone */
85
- }
86
- cp.stdout?.destroy?.();
87
- cp.stderr?.destroy?.();
88
- settle(() =>
89
- strict ? reject(new Error(`Process enumeration timed out after ${timeoutMs} ms.`)) : resolve('')
90
- );
91
- }, timeoutMs);
92
- timer.unref?.();
48
+ // Output bound of one helper command: a process table is tens of KB.
49
+ const HELPER_MAX_BYTES = 64 * 1024 * 1024;
50
+
51
+ // Run a helper command off the event loop (process-runner.mjs: under load one
52
+ // PowerShell start blocked the daemon's loop for seconds) and resolve its
53
+ // stdout. Past `timeoutMs` (default ENUMERATION_DEADLINE_MS) the helper is
54
+ // killed and the call settles. Strict: a failed, non-zero or timed-out run
55
+ // rejects; otherwise it resolves '' (non-zero exits keep their stdout).
56
+ function run(cmd, args, { strict = false, timeoutMs = ENUMERATION_DEADLINE_MS } = {}) {
57
+ return runProcessOffThread(cmd, args, { timeoutMs, maxBytes: HELPER_MAX_BYTES }).then(
58
+ ({ code, stdout }) => {
59
+ if (strict && code !== 0) throw new Error(`Process enumeration failed (${code}).`);
60
+ return stdout;
61
+ },
62
+ (error) => {
63
+ if (!strict) return '';
64
+ if (error?.code === 'ETIMEDOUT') throw new Error(`Process enumeration timed out after ${timeoutMs} ms.`);
65
+ throw error;
93
66
  }
94
- });
67
+ );
95
68
  }
96
69
 
97
70
  // Snapshot the whole process table once: parent map + a per-pid identity
@@ -114,21 +87,25 @@ async function enumerate({ strict = false } = {}) {
114
87
  ],
115
88
  { strict, timeoutMs: ENUMERATION_DEADLINE_MS }
116
89
  )
117
- : await run('ps', ['-A', '-o', 'pid=,ppid=,pgid=,comm=,lstart='], {
90
+ : await run('ps', ['-A', '-o', 'pid=,ppid=,pgid=,stat=,comm=,lstart='], {
118
91
  strict,
119
92
  timeoutMs: ENUMERATION_DEADLINE_MS,
120
93
  });
121
- const row = isWin ? /^(\d+)\s+(\d+)\s+()(\S+)(?:\s+(.+))?$/ : /^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)(?:\s+(.+))?$/;
94
+ const row = isWin ? /^(\d+)\s+(\d+)\s+()()(\S+)(?:\s+(.+))?$/ : /^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)(?:\s+(.+))?$/;
122
95
  for (const line of out.split(/\r?\n/)) {
123
96
  const m = line.trim().match(row);
124
97
  if (!m) continue;
98
+ // A zombie runs nothing and holds no port or file; it only waits to be
99
+ // reaped (forever under an init that does not reap, e.g. a container
100
+ // without one). Counting it alive would hold a dead tree as running.
101
+ if (m[4].startsWith('Z')) continue;
125
102
  const pid = Number(m[1]);
126
103
  const ppid = Number(m[2]);
127
- const token = (m[5] || '').trim() || null;
104
+ const token = (m[6] || '').trim() || null;
128
105
  if (!childrenOf.has(ppid)) childrenOf.set(ppid, []);
129
106
  childrenOf.get(ppid).push(pid);
130
107
  tokenOf.set(pid, token);
131
- nameOf.set(pid, m[4]);
108
+ nameOf.set(pid, m[5]);
132
109
  if (m[3]) pgidOf.set(pid, Number(m[3]));
133
110
  }
134
111
  if (strict && !nameOf.has(process.pid)) throw new Error('The process snapshot could not be verified.');
@@ -142,17 +119,33 @@ async function enumerate({ strict = false } = {}) {
142
119
  * `shared: true` (pre-kill captures) joins a read already in flight, so many
143
120
  * jobs promoted together do not each spawn an enumerator; a capture taken
144
121
  * while its root runs is valid whenever that read began. An observation after
145
- * a kill never shares: a read that began before the kill would report the
146
- * killed tree alive and defer the confirmation by a whole re-check. */
122
+ * a kill or exit must see a table taken AFTER it asked: a read that began
123
+ * before the kill would report the killed tree alive and defer the
124
+ * confirmation by a whole re-check. Such a caller starts a read when none
125
+ * runs, else joins the one queued to begin when the running read settles —
126
+ * every observer asking meanwhile shares it. At most one enumerator runs at
127
+ * a time, however many shells settle or watch at once (each one is a
128
+ * PowerShell process on Windows; one per settle and watch pass compounded
129
+ * machine load into multi-second daemon stalls). */
147
130
  let tableInFlight = null;
131
+ let nextTable = null;
132
+ function startTableRead() {
133
+ const read = enumerate({ strict: true }).finally(() => {
134
+ if (tableInFlight === read) tableInFlight = null;
135
+ });
136
+ tableInFlight = read;
137
+ return read;
138
+ }
148
139
  export function readProcessTable({ shared = false } = {}) {
149
- if (!shared) return enumerate({ strict: true });
150
- if (!tableInFlight) {
151
- tableInFlight = enumerate({ strict: true }).finally(() => {
152
- tableInFlight = null;
153
- });
154
- }
155
- return tableInFlight;
140
+ if (shared && tableInFlight) return tableInFlight;
141
+ if (nextTable) return nextTable;
142
+ if (!tableInFlight) return startTableRead();
143
+ const settled = () => {};
144
+ nextTable = tableInFlight.then(settled, settled).then(() => {
145
+ nextTable = null;
146
+ return startTableRead();
147
+ });
148
+ return nextTable;
156
149
  }
157
150
 
158
151
  // Walk every descendant pid of rootPid (excludes rootPid).
@@ -14,7 +14,7 @@ import { basename, join, resolve } from 'node:path';
14
14
  import { pathToFileURL } from 'node:url';
15
15
  import { tmpdir } from 'node:os';
16
16
  import { createHash, randomUUID } from 'node:crypto';
17
- import { buildSmartReadTruncationMarker, smartReadTruncate } from '../tools/builtin/read-formatting.mjs';
17
+ import { smartReadTruncate } from '../tools/builtin/read-formatting.mjs';
18
18
  import { shutdownStdioChild, killStdioChildTreeFast } from './child-tree.mjs';
19
19
  import { makeToolEnvelope, normalizeToolEnvelope } from '../session/tool-envelope.mjs';
20
20
  import { classifyResultKind } from '../session/result-classification.mjs';
@@ -308,7 +308,11 @@ function callAdmissionFor(server) {
308
308
  const cfg = server?.cfg || {};
309
309
  gate = createOwnerFairGate({
310
310
  name: `MCP ${server.name}`,
311
- activeMax: positiveInt(cfg.maxConcurrency ?? cfg.max_concurrency ?? process.env.MIXDOG_MCP_MAX_INFLIGHT, 8),
311
+ // JSON-RPC multiplexes requests by id over one transport, so the client
312
+ // imposes no in-flight cap of its own; a server that needs one declares
313
+ // it (maxConcurrency) or the operator sets MIXDOG_MCP_MAX_INFLIGHT.
314
+ activeMax:
315
+ positiveInt(cfg.maxConcurrency ?? cfg.max_concurrency ?? process.env.MIXDOG_MCP_MAX_INFLIGHT, null) ?? Infinity,
312
316
  queueMax: positiveInt(cfg.maxQueue ?? cfg.max_queue ?? process.env.MIXDOG_MCP_MAX_QUEUE, 256),
313
317
  minOwnerQueue: 8,
314
318
  waitTimeoutMs: positiveInt(
@@ -1037,7 +1041,7 @@ function capMcpOutputParts(content) {
1037
1041
  const s = typeof content === 'string' ? content : String(content ?? '');
1038
1042
  const bodyBytes = Buffer.byteLength(s, 'utf8');
1039
1043
  const bodyLines = countTextLines(s);
1040
- const { text, truncated } = smartReadTruncate(s, bodyLines, bodyBytes);
1044
+ const { text, truncated, marker } = smartReadTruncate(s, bodyLines, bodyBytes);
1041
1045
  if (!truncated) return { text, spillNote: '', truncated: false };
1042
1046
  // Spill the full body to a tmp file so the caller can recover content
1043
1047
  // elided by the head/tail cap.
@@ -1056,7 +1060,6 @@ function capMcpOutputParts(content) {
1056
1060
  /* spill best-effort */
1057
1061
  }
1058
1062
  const spillNote = spillPath ? `\n\n... [full output spilled to ${spillPath}] ...` : '';
1059
- const marker = buildSmartReadTruncationMarker(bodyLines, bodyBytes);
1060
1063
  const split = text.indexOf(`\n${marker}\n`);
1061
1064
  return {
1062
1065
  text,
@@ -70,8 +70,7 @@ export function answerOAuthCallback(req, res, { redirectUrl, state, finish, serv
70
70
  });
71
71
  res.end(html);
72
72
  };
73
- const invalid = () =>
74
- page(400, oauthErrorHtml('This sign-in link is invalid or has expired.', 'Start the sign-in again from Mixdog.'));
73
+ const invalid = () => page(400, oauthErrorHtml('This sign-in link is invalid or has expired.'));
75
74
  let target;
76
75
  try {
77
76
  target = new URL(req.url || '/', redirectUrl);
@@ -94,7 +93,7 @@ export function answerOAuthCallback(req, res, { redirectUrl, state, finish, serv
94
93
  const server = serverName || 'the MCP server';
95
94
  if (target.searchParams.has('error')) {
96
95
  // The authorization server's error text is never reflected.
97
- page(400, oauthErrorHtml(`Sign-in to ${server} was not completed.`, 'Start the sign-in again from Mixdog.'));
96
+ page(400, oauthErrorHtml(`Sign-in to ${server} was not completed.`));
98
97
  finish(undefined);
99
98
  return;
100
99
  }
@@ -378,14 +378,21 @@ function poolSend(providerName, account) {
378
378
  // remaining wait budget is waited out; otherwise the send fails as before.
379
379
  const waitOrThrow = async (error, ids) => {
380
380
  const { rows, now, all } = exhaustion(ids);
381
- const wait = all ? null : earliestCooldownMs(rows, now);
382
- if (wait == null || waitedMs + wait > waitBoundMs(ids)) throw markExhausted(error, ids);
381
+ const bound = waitBoundMs(ids);
382
+ // Persisting a short cooldown can outlast its deadline. Release only
383
+ // attempts cooled by this send, and only under an enabled wait policy.
384
+ const expired = bound > 0 && rows.some((row) => cooled.has(row.id) && !(Number(row.blockedUntil) > now));
385
+ const wait = all ? null : expired ? 0 : earliestCooldownMs(rows, now);
386
+ if (wait == null || waitedMs + wait > bound) throw markExhausted(error, ids);
383
387
  options.onStageChange?.('reconnecting', {
384
388
  message: `Rate limited — every account is cooling down; retrying in ${Math.ceil(wait / 1000)}s`,
385
389
  reason: 'rate_limited',
386
390
  waitMs: wait,
387
391
  retryAt: now + wait,
388
392
  });
393
+ // A zero-duration sleep does not check cancellation, including on the
394
+ // final allowed send when no next loop iteration will check it either.
395
+ options.signal?.throwIfAborted();
389
396
  await sleepWithAbort(wait, options.signal);
390
397
  waitedMs += wait;
391
398
  const lifted = Date.now();
@@ -25,6 +25,7 @@ import {
25
25
  scrubOAuthSecrets,
26
26
  } from './lib/oauth-token-utils.mjs';
27
27
  import { createOAuthPkce, parseOAuthCodeInput } from './lib/oauth-pkce.mjs';
28
+ import { OAUTH_PAGE_CONTENT_TYPE, oauthErrorHtml, oauthSuccessHtml } from './lib/oauth-page.mjs';
28
29
 
29
30
  const SIGN_IN_EXPIRED_PREFIX = 'Anthropic sign-in expired. Sign in again from Providers.';
30
31
 
@@ -66,8 +67,6 @@ const OAUTH_CALLBACK_PATH = '/callback';
66
67
  const OAUTH_REDIRECT_URI = `http://${OAUTH_CALLBACK_HOST}:${OAUTH_CALLBACK_PORT}${OAUTH_CALLBACK_PATH}`;
67
68
  const OAUTH_MANUAL_REDIRECT_URI =
68
69
  process.env.ANTHROPIC_OAUTH_MANUAL_REDIRECT_URI || 'https://platform.claude.com/oauth/code/callback';
69
- const OAUTH_SUCCESS_REDIRECT_URL =
70
- process.env.ANTHROPIC_OAUTH_SUCCESS_REDIRECT_URL || 'https://platform.claude.com/oauth/code/success?app=claude-code';
71
70
  const OAUTH_LOGIN_TIMEOUT_MS = 5 * 60_000;
72
71
  const OAUTH_TOKEN_TIMEOUT_MS = 30_000;
73
72
 
@@ -110,7 +109,7 @@ function _loadCredentialsFile(path) {
110
109
  }
111
110
 
112
111
  // The token endpoint names the signed-in account next to the tokens.
113
- export function identityFromTokenResponse(json) {
112
+ function identityFromTokenResponse(json) {
114
113
  return normalizeAccountIdentity({
115
114
  id: json?.account?.uuid,
116
115
  email: json?.account?.email_address,
@@ -570,9 +569,11 @@ function _parseOAuthCodeInput(input) {
570
569
  return parseOAuthCodeInput(input, { allowHashState: true });
571
570
  }
572
571
 
573
- async function exchangeAuthorizationCode({ pkce, code, state, redirectUri }) {
572
+ async function exchangeAuthorizationCode({ pkce, code, state, redirectUri, signal = null }) {
573
+ signal?.throwIfAborted();
574
574
  const cleanCode = String(code || '').trim();
575
575
  if (!cleanCode) throw new Error('[anthropic-oauth] authorization code is required');
576
+ const timeoutSignal = AbortSignal.timeout(OAUTH_TOKEN_TIMEOUT_MS);
576
577
  const tokenRes = await fetch(TOKEN_URL, {
577
578
  method: 'POST',
578
579
  headers: tokenEndpointHeaders(),
@@ -585,7 +586,7 @@ async function exchangeAuthorizationCode({ pkce, code, state, redirectUri }) {
585
586
  state,
586
587
  }),
587
588
  redirect: 'error',
588
- signal: AbortSignal.timeout(OAUTH_TOKEN_TIMEOUT_MS),
589
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal,
589
590
  dispatcher: getLlmDispatcher(),
590
591
  });
591
592
  if (!tokenRes.ok) {
@@ -605,6 +606,8 @@ async function exchangeAuthorizationCode({ pkce, code, state, redirectUri }) {
605
606
  const expiresAt = expiresAtFromTokenResponse(json);
606
607
  const scopes = _oauthParseScopeField(json?.scope);
607
608
  const identity = identityFromTokenResponse(json);
609
+ // A cancelled login never stores the account it was exchanging.
610
+ signal?.throwIfAborted();
608
611
  const credPath = _oauthCredentialsWritePath();
609
612
  const raw = _updateCredentialsFile(credPath, (current) => {
610
613
  const base = current && typeof current === 'object' ? current : {};
@@ -664,14 +667,33 @@ export async function beginOAuthLogin({ openBrowserFn = null, openBrowser = true
664
667
  }
665
668
  };
666
669
 
670
+ const controller = new AbortController();
667
671
  let server = null;
668
672
  let timeout = null;
669
673
  let finish = null;
674
+ let exchangePromise = null;
675
+ // Browser and manual callbacks share one exchange: an authorization code is
676
+ // spent once, and a duplicate joins the first instead of failing the login.
677
+ const acceptCode = (code, redirectUri) => {
678
+ if (exchangePromise) return exchangePromise;
679
+ controller.signal.throwIfAborted();
680
+ // The browser deadline ends when a code arrives; the exchange has its own.
681
+ if (timeout) clearTimeout(timeout);
682
+ timeout = null;
683
+ exchangePromise = exchangeAuthorizationCode({ pkce, code, state, redirectUri, signal: controller.signal });
684
+ exchangePromise.then(
685
+ (tokens) => finish(tokens),
686
+ (error) => finish(null, error instanceof Error ? error : new Error(String(error)))
687
+ );
688
+ return exchangePromise;
689
+ };
670
690
  const waitForCallback = new Promise((resolve, reject) => {
671
691
  let settled = false;
672
692
  finish = (value, error = null) => {
673
693
  if (settled) return;
674
694
  settled = true;
695
+ // Work arriving after the login settled (or was cancelled) is rejected.
696
+ controller.abort();
675
697
  if (timeout) clearTimeout(timeout);
676
698
  try {
677
699
  server?.close();
@@ -691,20 +713,20 @@ export async function beginOAuthLogin({ openBrowserFn = null, openBrowser = true
691
713
  const code = u.searchParams.get('code');
692
714
  if (!code || u.searchParams.get('state') !== state) {
693
715
  // Reject this request only; the valid callback may still arrive.
694
- res.writeHead(400);
695
- res.end('Invalid');
716
+ res.writeHead(400, { 'Content-Type': OAUTH_PAGE_CONTENT_TYPE });
717
+ res.end(oauthErrorHtml('The sign-in callback was invalid or did not match this login.'));
696
718
  return;
697
719
  }
720
+ // The login settles (and its server closes) before this answer is sent,
721
+ // so the connection must not stay open to the closed login's handler.
698
722
  try {
699
- const tokens = await exchangeAuthorizationCode({ pkce, code, state, redirectUri: OAUTH_REDIRECT_URI });
700
- res.writeHead(302, { Location: OAUTH_SUCCESS_REDIRECT_URL });
701
- res.end();
702
- finish(tokens);
703
- } catch (err) {
704
- const error = err instanceof Error ? err : new Error(String(err));
705
- res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
706
- res.end(`Claude login failed: ${error.message}`);
707
- finish(null, error);
723
+ await acceptCode(code, OAUTH_REDIRECT_URI);
724
+ res.writeHead(200, { 'Content-Type': OAUTH_PAGE_CONTENT_TYPE, Connection: 'close' });
725
+ res.end(oauthSuccessHtml('Claude'));
726
+ } catch {
727
+ // The shared exchange reports its error through waitForCallback.
728
+ res.writeHead(500, { 'Content-Type': OAUTH_PAGE_CONTENT_TYPE, Connection: 'close' });
729
+ res.end(oauthErrorHtml('Claude could not complete the token exchange.'));
708
730
  }
709
731
  });
710
732
  timeout = setTimeout(() => finish(null), OAUTH_LOGIN_TIMEOUT_MS);
@@ -730,10 +752,9 @@ export async function beginOAuthLogin({ openBrowserFn = null, openBrowser = true
730
752
  completeCode: async (input) => {
731
753
  const parsed = _parseOAuthCodeInput(input);
732
754
  if (parsed.state && parsed.state !== state) throw new Error('[anthropic-oauth] OAuth state mismatch');
755
+ if (!String(parsed.code || '').trim()) throw new Error('[anthropic-oauth] authorization code is required');
733
756
  const redirectUri = parsed.redirectUri || (parsed.state ? OAUTH_MANUAL_REDIRECT_URI : OAUTH_REDIRECT_URI);
734
- const tokens = await exchangeAuthorizationCode({ pkce, code: parsed.code, state, redirectUri });
735
- finish?.(tokens);
736
- return tokens;
757
+ return await acceptCode(parsed.code, redirectUri);
737
758
  },
738
759
  cancel: () => {
739
760
  finish?.(null);
@@ -448,7 +448,8 @@ export class AnthropicOAuthProvider {
448
448
  // a hard 400. Pairing here closes the gap regardless of caller.
449
449
  messages = sanitizeToolPairs(messages);
450
450
  const opts = sendOpts || {};
451
- const { onStageChange, onStreamDelta, onToolCall, onTextDelta, onTextReset } = streamCallbacks(opts);
451
+ const { onStageChange, onStreamDelta, onToolCall, onTextDelta, onTextReset, onStreamItemStart } =
452
+ streamCallbacks(opts);
452
453
  const externalSignal = opts.signal || null;
453
454
  // Test seam: lets the retry harness drive stream outcomes without a
454
455
  // live OAuth session.
@@ -610,7 +611,7 @@ export class AnthropicOAuthProvider {
610
611
  midState,
611
612
  onTextDelta,
612
613
  knownToolNames,
613
- { relayProgressUpdates: body.thinking?.display === 'updates' }
614
+ { relayProgressUpdates: body.thinking?.display === 'updates', onStreamItemStart }
614
615
  );
615
616
  try {
616
617
  controller?.abort?.('Anthropic SSE complete');
@@ -8,6 +8,7 @@ import {
8
8
  NATIVE_SERVER_TOOL_RESULT_BLOCK_TYPES,
9
9
  } from '../../lib/anthropic-native-blocks.mjs';
10
10
  import { parseAnthropicFallbackBlock } from '../../anthropic-server-fallback.mjs';
11
+ import { reportStreamItemStart } from '../../lib/send-callbacks.mjs';
11
12
 
12
13
  // Detach the provider payload from the parser's event object without altering
13
14
  // any field (opaque server-tool result payloads must round-trip byte-exact).
@@ -19,7 +20,7 @@ function cloneNativeBlock(block) {
19
20
  }
20
21
  }
21
22
 
22
- export function createContentBlockStart({ turn, blocks, state, progress, seedText }) {
23
+ export function createContentBlockStart({ turn, blocks, state, progress, seedText, onStreamItemStart }) {
23
24
  const seedThinkingBlock = (index, block) => {
24
25
  if (block.type === 'redacted_thinking') {
25
26
  // Opaque redacted payload: real reasoning content, exposed as a block.
@@ -59,6 +60,8 @@ export function createContentBlockStart({ turn, blocks, state, progress, seedTex
59
60
  name: block.name || '',
60
61
  inputJson: '',
61
62
  });
63
+ // The call opened; its input still streams before onToolCall.
64
+ reportStreamItemStart(onStreamItemStart, { kind: 'tool' });
62
65
  }
63
66
  if (block?.type === 'text') {
64
67
  // Seed the ordered text slot. An empty block still needs its slot (it
@@ -19,6 +19,7 @@ export function createTurnEvents({
19
19
  onToolCall,
20
20
  relayProgressUpdates,
21
21
  labels,
22
+ onStreamItemStart,
22
23
  }) {
23
24
  const { onMessageStart, onMessageDelta, onMessageStop } = createMessageEvents({ turn, blocks, state });
24
25
  const onContentBlockDelta = createContentBlockDelta({
@@ -38,6 +39,7 @@ export function createTurnEvents({
38
39
  state,
39
40
  progress,
40
41
  seedText: onContentBlockDelta.seedText,
42
+ onStreamItemStart,
41
43
  }),
42
44
  onContentBlockDelta,
43
45
  onContentBlockStop: createContentBlockStop({ turn, blocks, state, leak, progress, onToolCall, labels }),
@@ -24,6 +24,7 @@ import { OAUTH_STREAM_LABELS } from './lib/anthropic-stream-labels.mjs';
24
24
  * @param {Iterable<string>} [deps.knownToolNames]
25
25
  * @param {boolean} [deps.relayProgressUpdates] request used thinking.display "updates"
26
26
  * @param {{ display: string, tag: string }} [deps.labels] provider name for errors and stderr lines
27
+ * @param {(detail: object) => void} [deps.onStreamItemStart] a client tool_use block opened
27
28
  */
28
29
  export function createAnthropicSseTurn({
29
30
  state,
@@ -33,6 +34,7 @@ export function createAnthropicSseTurn({
33
34
  knownToolNames,
34
35
  relayProgressUpdates = false,
35
36
  labels = OAUTH_STREAM_LABELS,
37
+ onStreamItemStart = null,
36
38
  }) {
37
39
  const progress = (kind) => {
38
40
  try {
@@ -64,6 +66,7 @@ export function createAnthropicSseTurn({
64
66
  onToolCall,
65
67
  relayProgressUpdates,
66
68
  labels,
69
+ onStreamItemStart,
67
70
  });
68
71
  const outcome = createTurnOutcome({ turn, blocks, state, labels });
69
72
 
@@ -186,7 +186,7 @@ export async function parseSSEStream(
186
186
  state,
187
187
  onTextDelta,
188
188
  knownToolNames,
189
- { relayProgressUpdates = false, labels = OAUTH_STREAM_LABELS } = {}
189
+ { relayProgressUpdates = false, labels = OAUTH_STREAM_LABELS, onStreamItemStart = null } = {}
190
190
  ) {
191
191
  // Anthropic/Claude parity: every received SSE byte proves transport
192
192
  // activity, including comment and named ping keepalives. Content kinds
@@ -208,6 +208,7 @@ export async function parseSSEStream(
208
208
  knownToolNames,
209
209
  relayProgressUpdates,
210
210
  labels,
211
+ onStreamItemStart,
211
212
  });
212
213
  const watchdogs = createAnthropicSseWatchdogs({
213
214
  state,
@@ -162,7 +162,8 @@ export class AnthropicProvider {
162
162
  });
163
163
  this.fastModeBetaHeaderLatched = fastModeLatched;
164
164
 
165
- const { onStageChange, onStreamDelta, onToolCall, onTextDelta, onTextReset } = streamCallbacks(opts);
165
+ const { onStageChange, onStreamDelta, onToolCall, onTextDelta, onTextReset, onStreamItemStart } =
166
+ streamCallbacks(opts);
166
167
 
167
168
  // No absolute wall-clock cap on streaming generation: a stream still
168
169
  // emitting SSE deltas must not be killed by a fixed total-lifetime timer.
@@ -288,6 +289,7 @@ export class AnthropicProvider {
288
289
  {
289
290
  relayProgressUpdates: params.thinking?.display === 'updates',
290
291
  labels: { display: 'Anthropic', tag: this.name },
292
+ onStreamItemStart,
291
293
  }
292
294
  );
293
295
  try {
@@ -36,8 +36,7 @@ import {
36
36
 
37
37
  const ONBOARD_INTERVAL_MS = 1_000;
38
38
  const CALLBACK_FAILURE_HTML = oauthErrorHtml(
39
- 'Antigravity sign-in was not completed.',
40
- 'Return to Mixdog for the error details and any account verification link.'
39
+ 'Antigravity sign-in was not completed. Return to Mixdog for the error details and any account verification link.'
41
40
  );
42
41
 
43
42
  function extractProjectId(payload) {