mixdog 1.2.3 → 1.2.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 (465) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +6 -4
  3. package/scripts/lib/gate-scheduler.mjs +29 -11
  4. package/scripts/probe-provider-catalog.mjs +31 -0
  5. package/scripts/provider-pingpong-live.mjs +128 -38
  6. package/scripts/session-sweep.mjs +5 -25
  7. package/src/agents/maintainer/AGENT.md +1 -1
  8. package/src/agents/maintainer/agent.json +1 -1
  9. package/src/agents/reviewer/AGENT.md +2 -4
  10. package/src/agents/security/AGENT.md +1 -2
  11. package/src/agents/writer/AGENT.md +2 -3
  12. package/src/defaults/shipped-history.json +6 -1
  13. package/src/defaults/skills/image/SKILL.md +21 -23
  14. package/src/lib/keychain-cjs.cjs +13 -11
  15. package/src/rules/agent/AGENT.md +7 -5
  16. package/src/rules/lead/LEAD.md +7 -9
  17. package/src/rules/shared/45-destructive.md +4 -0
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  20. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  21. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  22. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  24. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  29. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  31. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  32. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  33. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  34. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  35. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  36. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  37. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  38. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  40. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  41. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  46. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  47. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  48. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  49. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  50. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  51. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  52. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  53. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  54. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  55. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  56. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  57. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  58. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  59. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  60. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  62. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  63. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  64. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  65. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  66. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  67. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  68. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  79. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  80. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  84. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  86. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  87. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  88. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  89. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  90. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  91. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  93. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  94. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  95. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  96. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  97. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  99. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  100. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  101. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  102. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  103. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  106. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  107. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  108. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  109. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  110. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  111. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  112. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  113. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  114. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  115. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  116. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  118. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  120. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  121. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  122. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  123. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  124. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  125. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  128. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  129. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  130. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  131. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  132. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  133. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  134. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  135. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  137. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  138. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  139. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  146. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  148. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  150. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  157. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  158. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  159. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  160. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  161. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  162. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  163. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  164. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  165. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  167. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  169. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  170. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  171. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  172. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  177. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  179. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +136 -196
  180. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  181. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  182. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  184. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  185. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  186. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  187. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  188. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  189. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  190. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  191. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  192. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  195. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  196. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  198. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  200. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  206. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  208. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  209. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  210. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  211. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  212. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  216. package/src/runtime/attachments/store.d.mts +9 -0
  217. package/src/runtime/attachments/store.mjs +80 -9
  218. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  219. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  220. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  221. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  222. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  223. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  224. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  225. package/src/runtime/github/tool.mjs +118 -17
  226. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  227. package/src/runtime/media/catalog.mjs +4 -4
  228. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  229. package/src/runtime/media/defaults.mjs +37 -0
  230. package/src/runtime/media/jobs.mjs +12 -2
  231. package/src/runtime/media/lanes.mjs +12 -0
  232. package/src/runtime/media/tool-defs.mjs +15 -4
  233. package/src/runtime/media/tool.mjs +41 -12
  234. package/src/runtime/media/upstream-error.mjs +72 -6
  235. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  236. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  237. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  238. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  239. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  240. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  242. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  243. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  244. package/src/runtime/office/index.mjs +2 -3
  245. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  246. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  247. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  248. package/src/runtime/shared/atomic-file.d.mts +1 -1
  249. package/src/runtime/shared/background-tasks.mjs +6 -1
  250. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  251. package/src/runtime/shared/browser-launch.mjs +15 -0
  252. package/src/runtime/shared/err-text.mjs +42 -4
  253. package/src/runtime/shared/file-lock.mjs +6 -5
  254. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  255. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  256. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  257. package/src/runtime/shared/model-route-chain.mjs +48 -0
  258. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  259. package/src/runtime/shared/pid-liveness.mjs +12 -0
  260. package/src/runtime/shared/provider-api-key.mjs +6 -0
  261. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  262. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  263. package/src/runtime/shared/quiet-progress.mjs +101 -0
  264. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  265. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  266. package/src/runtime/shared/remote-intent.mjs +2 -42
  267. package/src/runtime/shared/slow-span.mjs +8 -1
  268. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  269. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  270. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  271. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  272. package/src/runtime/shared/tool-status.mjs +3 -2
  273. package/src/runtime/shared/tool-surface.mjs +2 -3
  274. package/src/runtime/shared/tool-work-units.mjs +22 -15
  275. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  276. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  277. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  278. package/src/runtime/shared/transcript-writer.mjs +14 -4
  279. package/src/runtime/shared/turn-changes.mjs +2 -1
  280. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  281. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  282. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  283. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  284. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  285. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  286. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  287. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  288. package/src/runtime/shared/worktrees/include.mjs +125 -7
  289. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  290. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  291. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  292. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  293. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  294. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  295. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  296. package/src/runtime/terminal-bridge/client.mjs +14 -10
  297. package/src/session-runtime/boot/apis.mjs +7 -1
  298. package/src/session-runtime/boot/config.mjs +2 -0
  299. package/src/session-runtime/boot/facade.mjs +3 -0
  300. package/src/session-runtime/boot/session.mjs +3 -2
  301. package/src/session-runtime/boot/tools.mjs +3 -0
  302. package/src/session-runtime/conversation-import/service.mjs +1 -1
  303. package/src/session-runtime/goal-facade-api.mjs +2 -2
  304. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  305. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  306. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  307. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  308. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  309. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  310. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  311. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  312. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  313. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  314. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  315. package/src/session-runtime/media-api.mjs +8 -0
  316. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  317. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  318. package/src/session-runtime/model-route/set-route.mjs +114 -34
  319. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  320. package/src/session-runtime/model-route-api.mjs +4 -2
  321. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  322. package/src/session-runtime/native-web-search.mjs +9 -8
  323. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  324. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  325. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  326. package/src/session-runtime/provider-models.mjs +1 -0
  327. package/src/session-runtime/provider-usage.mjs +4 -0
  328. package/src/session-runtime/route-resolution.mjs +3 -1
  329. package/src/session-runtime/runtime-core.mjs +4 -0
  330. package/src/session-runtime/runtime-facade.mjs +6 -0
  331. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  332. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  333. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  334. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  335. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  336. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  337. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  338. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  339. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  340. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  341. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  343. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  344. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  345. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  346. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  347. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  348. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  349. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  350. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  351. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  352. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  353. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  354. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  355. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  356. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  357. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  358. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  359. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  360. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  361. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  362. package/src/session-runtime/services/agent-tool.mjs +4 -0
  363. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  364. package/src/session-runtime/services/provider-admin.mjs +77 -0
  365. package/src/session-runtime/session-create/session-options.mjs +7 -5
  366. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  367. package/src/session-runtime/session-transcript.mjs +4 -5
  368. package/src/session-runtime/session-turn-api.mjs +3 -9
  369. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  370. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  371. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  372. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  373. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  374. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  375. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  376. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  377. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  378. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  379. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  380. package/src/session-runtime/turn/turn-run.mjs +1 -36
  381. package/src/session-runtime/usage-stats-api.mjs +4 -1
  382. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  383. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  384. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  385. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  386. package/src/session-runtime/workspace/transitions.mjs +248 -68
  387. package/src/standalone/channel-transport.mjs +3 -4
  388. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  389. package/src/standalone/daemon-pg-stop.mjs +1 -3
  390. package/src/standalone/daemon-session-control.mjs +88 -0
  391. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  392. package/src/standalone/daemon.mjs +2 -0
  393. package/src/standalone/local-session-runtime.mjs +11 -12
  394. package/src/standalone/session-protocol.mjs +7 -0
  395. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  396. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  397. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  398. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  399. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  400. package/src/standalone/session-service/agent-tree.mjs +28 -2
  401. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  402. package/src/standalone/session-service/entries.mjs +1 -0
  403. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  404. package/src/standalone/session-service/quota-resume.mjs +28 -13
  405. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  406. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  407. package/src/standalone/session-tool-output.mjs +82 -0
  408. package/src/tui/app/app-view.jsx +2 -1
  409. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  410. package/src/tui/app/provider-setup-picker.mjs +15 -3
  411. package/src/tui/app/slash-commands.mjs +6 -0
  412. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  413. package/src/tui/components/Spinner.jsx +4 -0
  414. package/src/tui/components/SpinnerByline.jsx +21 -2
  415. package/src/tui/components/status-line/local-line.mjs +10 -2
  416. package/src/tui/dist/index.mjs +478 -251
  417. package/src/tui/session/active-tool-summary.mjs +1 -1
  418. package/src/tui/session/context-state.mjs +0 -3
  419. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  420. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  421. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  422. package/src/tui/session/quota-resume.mjs +140 -61
  423. package/src/tui/session/session-api/commands.mjs +4 -10
  424. package/src/tui/session/session-api/controls.mjs +12 -0
  425. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  426. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  427. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  428. package/src/tui/session/session-api/media.mjs +2 -0
  429. package/src/tui/session/session-api/routes.mjs +81 -57
  430. package/src/tui/session/session-api/settings.mjs +3 -3
  431. package/src/tui/session/session-api/worktree.mjs +3 -2
  432. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  433. package/src/tui/session/session-flow/drain.mjs +6 -4
  434. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  435. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  436. package/src/tui/session/session-flow/queue.mjs +1 -1
  437. package/src/tui/session/tool-approval.mjs +17 -8
  438. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  439. package/src/tui/session/tui-steering-persist.mjs +3 -5
  440. package/src/tui/session/turn-quiet.mjs +53 -0
  441. package/src/tui/session/turn-stream.mjs +5 -1
  442. package/src/tui/session/turn.mjs +38 -31
  443. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  444. package/src/tui/spinner-meta.mjs +10 -0
  445. package/src/tui/spinner-verbs.mjs +3 -0
  446. package/src/ui/statusline.mjs +13 -3
  447. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  448. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  449. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  450. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  451. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  452. package/src/session-runtime/turn/share-ops.mjs +0 -77
  453. package/src/tui/session/cross-surface-share.mjs +0 -406
  454. package/src/tui/session/live-share/delta.mjs +0 -153
  455. package/src/tui/session/live-share/mirror.mjs +0 -106
  456. package/src/tui/session/live-share/owner.mjs +0 -254
  457. package/src/tui/session/live-share/retry.mjs +0 -54
  458. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  459. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  460. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  461. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  462. package/src/tui/session/live-share/viewer.mjs +0 -99
  463. package/src/tui/session/live-share/wire.mjs +0 -44
  464. package/src/tui/session/live-share.mjs +0 -154
  465. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -19,21 +19,43 @@ function catalogThinkingLevels(model, provider) {
19
19
  // Documented ranges: 2.5 Pro 128–32768 (cannot disable), 2.5 Flash 0–24576,
20
20
  // 2.5 Flash-Lite 0 or 512–24576; -1 is dynamic thinking for all of them.
21
21
  function budgetSpec(model) {
22
- const bare = String(model || '').replace(/^models\//i, '').toLowerCase();
22
+ const bare = String(model || '')
23
+ .replace(/^models\//i, '')
24
+ .toLowerCase();
23
25
  if (!/^gemini-2\.5(?:-|$)/.test(bare)) return null;
24
26
  if (/^gemini-2\.5-pro/.test(bare)) {
25
- return { name: 'Gemini 2.5 Pro', min: 128, max: 32768, canDisable: false, efforts: { minimal: 128, low: 1024, medium: 8192, high: 32768 } };
27
+ return {
28
+ name: 'Gemini 2.5 Pro',
29
+ min: 128,
30
+ max: 32768,
31
+ canDisable: false,
32
+ efforts: { minimal: 128, low: 1024, medium: 8192, high: 32768 },
33
+ };
26
34
  }
27
35
  if (/^gemini-2\.5-flash-lite/.test(bare)) {
28
- return { name: 'Gemini 2.5 Flash-Lite', min: 512, max: 24576, canDisable: true, efforts: { minimal: 512, low: 1024, medium: 8192, high: 24576 } };
36
+ return {
37
+ name: 'Gemini 2.5 Flash-Lite',
38
+ min: 512,
39
+ max: 24576,
40
+ canDisable: true,
41
+ efforts: { minimal: 512, low: 1024, medium: 8192, high: 24576 },
42
+ };
29
43
  }
30
- return { name: 'Gemini 2.5 Flash', min: 0, max: 24576, canDisable: true, efforts: { minimal: 0, low: 1024, medium: 8192, high: 24576 } };
44
+ return {
45
+ name: 'Gemini 2.5 Flash',
46
+ min: 0,
47
+ max: 24576,
48
+ canDisable: true,
49
+ efforts: { minimal: 0, low: 1024, medium: 8192, high: 24576 },
50
+ };
31
51
  }
32
52
 
33
53
  // Levels documented for models whose ladder differs from the generic wire
34
54
  // vocabulary (a catalog ladder, when declared, still takes precedence).
35
55
  function documentedThinkingLevels(model) {
36
- const bare = String(model || '').replace(/^models\//i, '').toLowerCase();
56
+ const bare = String(model || '')
57
+ .replace(/^models\//i, '')
58
+ .toLowerCase();
37
59
  if (bare === 'gemini-4-argon' || bare === 'gemini-3.8-flash') return ['low', 'medium', 'high'];
38
60
  return null;
39
61
  }
@@ -47,7 +69,9 @@ function checkBudget(model, spec, budget) {
47
69
  throw new TypeError(`${model} (${spec.name}) cannot disable thinking; thinkingBudget 0 is not allowed.`);
48
70
  }
49
71
  if (budget !== 0 && (budget < spec.min || budget > spec.max)) {
50
- throw new TypeError(`${model} (${spec.name}) thinkingBudget must be -1, ${spec.canDisable ? '0, or ' : ''}${spec.min}–${spec.max}; received ${budget}.`);
72
+ throw new TypeError(
73
+ `${model} (${spec.name}) thinkingBudget must be -1, ${spec.canDisable ? '0, or ' : ''}${spec.min}–${spec.max}; received ${budget}.`
74
+ );
51
75
  }
52
76
  }
53
77
 
@@ -75,7 +99,9 @@ export function geminiThinkingConfig(model, opts = {}, { includeThoughts, provid
75
99
  }
76
100
  if (spec && level) {
77
101
  if (!Object.hasOwn(spec.efforts, level)) {
78
- throw new TypeError(`${model} supports thinking levels ${Object.keys(spec.efforts).join(', ')}; received ${level}.`);
102
+ throw new TypeError(
103
+ `${model} supports thinking levels ${Object.keys(spec.efforts).join(', ')}; received ${level}.`
104
+ );
79
105
  }
80
106
  config.thinkingBudget = spec.efforts[level];
81
107
  } else if (level) {
@@ -731,8 +731,17 @@ export class GeminiProvider {
731
731
  }
732
732
 
733
733
  async _streamViaSdk(stream) {
734
- const { opts, signal, useModel, systemInstruction, geminiTools, toolConfig, generationConfig, safetySettings, contents } =
735
- stream;
734
+ const {
735
+ opts,
736
+ signal,
737
+ useModel,
738
+ systemInstruction,
739
+ geminiTools,
740
+ toolConfig,
741
+ generationConfig,
742
+ safetySettings,
743
+ contents,
744
+ } = stream;
736
745
  const genModel = this._currentGenAI().getGenerativeModel({
737
746
  model: useModel,
738
747
  systemInstruction,
@@ -3,7 +3,7 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
3
3
  // Grok CLI client version for the cli-chat-proxy version gate (HTTP 426).
4
4
  // Effective = env override, else max(floor, learned-from-426, live npm latest
5
5
  // of the official @xai-official/grok CLI).
6
- export const GROK_CLI_VERSION_FLOOR = '1.0.60';
6
+ export const GROK_CLI_VERSION_FLOOR = '1.0.62';
7
7
  export const GROK_CLI_NPM_PACKAGE = '@xai-official/grok';
8
8
 
9
9
  const live = createNpmVersionSource(GROK_CLI_NPM_PACKAGE, { persistKey: 'grok-cli' });
@@ -37,7 +37,9 @@ function lastEligibleBlockIndex(content) {
37
37
  export function countAnthropicCacheControls(value) {
38
38
  if (Array.isArray(value)) return value.reduce((sum, item) => sum + countAnthropicCacheControls(item), 0);
39
39
  if (!value || typeof value !== 'object') return 0;
40
- return (value.cache_control ? 1 : 0) + (Array.isArray(value.content) ? countAnthropicCacheControls(value.content) : 0);
40
+ return (
41
+ (value.cache_control ? 1 : 0) + (Array.isArray(value.content) ? countAnthropicCacheControls(value.content) : 0)
42
+ );
41
43
  }
42
44
 
43
45
  function stripCacheControl(block, budget) {
@@ -546,7 +548,12 @@ function toAnthropicTools(tools, logTag, onToolExcluded) {
546
548
  // `supportsDisableParallelToolUse: false` (gateway capability).
547
549
  export function toAnthropicToolChoice(
548
550
  toolChoice,
549
- { thinking = false, disableParallelToolUse = false, supportsDisableParallelToolUse = true, onThinkingConflict = 'fallback' } = {}
551
+ {
552
+ thinking = false,
553
+ disableParallelToolUse = false,
554
+ supportsDisableParallelToolUse = true,
555
+ onThinkingConflict = 'fallback',
556
+ } = {}
550
557
  ) {
551
558
  let choice;
552
559
  if (toolChoice === 'none') choice = { type: 'none' };
@@ -203,7 +203,8 @@ function assertObjectRoot(schema, rule) {
203
203
  function buildSchema(inputSchema) {
204
204
  if (!isPlainObject(inputSchema)) throw new SchemaProblem('', 'the input schema must be a JSON object schema');
205
205
  const { $schema, ...rest } = inputSchema;
206
- const schema = dialectOf(inputSchema) === 'draft-07' ? translateDraft07(rest, '', true, DRAFT07) : structuredClone(rest);
206
+ const schema =
207
+ dialectOf(inputSchema) === 'draft-07' ? translateDraft07(rest, '', true, DRAFT07) : structuredClone(rest);
207
208
  assertValidDraft202012(schema);
208
209
  assertObjectRoot(schema, 'anthropic');
209
210
  return schema;
@@ -222,8 +223,7 @@ function repairRoot(schema) {
222
223
  if (Array.isArray(type) && type.includes('object')) return { ...schema, type: 'object' };
223
224
  if (type !== undefined || Object.hasOwn(schema, '$ref')) return schema;
224
225
  const out = { type: 'object', ...schema };
225
- if (!Object.hasOwn(out, 'properties') && !ROOT_FORBIDDEN_KEYS.some((k) => Object.hasOwn(out, k)))
226
- out.properties = {};
226
+ if (!Object.hasOwn(out, 'properties') && !ROOT_FORBIDDEN_KEYS.some((k) => Object.hasOwn(out, k))) out.properties = {};
227
227
  return out;
228
228
  }
229
229
 
@@ -232,7 +232,8 @@ function refsIntoDefinitions(node) {
232
232
  if (Array.isArray(node)) return node.some(refsIntoDefinitions);
233
233
  if (!isPlainObject(node)) return false;
234
234
  return Object.entries(node).some(
235
- ([key, value]) => (key === '$ref' && typeof value === 'string' && /^#\/definitions(\/|$)/.test(value)) || refsIntoDefinitions(value)
235
+ ([key, value]) =>
236
+ (key === '$ref' && typeof value === 'string' && /^#\/definitions(\/|$)/.test(value)) || refsIntoDefinitions(value)
236
237
  );
237
238
  }
238
239
 
@@ -348,7 +349,10 @@ export function claudeBridgeInputSchema(inputSchema) {
348
349
  // Not valid as written: lower undeclared draft-07/04 constructs and
349
350
  // fill the root type; only a schema still broken is excluded.
350
351
  const repaired = repairedToolInputSchema(inputSchema, 'anthropic');
351
- result = repaired && !wireProblem(repaired, 'anthropic') ? { schema: repaired } : { pointer: error.pointer, reason: error.message };
352
+ result =
353
+ repaired && !wireProblem(repaired, 'anthropic')
354
+ ? { schema: repaired }
355
+ : { pointer: error.pointer, reason: error.message };
352
356
  }
353
357
  if (key !== null) {
354
358
  if (cache.size >= CACHE_LIMIT) cache.clear();
@@ -0,0 +1,86 @@
1
+ // Shared request-body compression for provider POSTs (Anthropic gzip, Codex
2
+ // zstd). Policy in one place:
3
+ // - bodies below REQ_COMPRESSION_MIN_BYTES are never compressed (CPU + header
4
+ // cost outweighs the upload saving);
5
+ // - a request that already carries Content-Encoding is never double-encoded;
6
+ // - a compression failure fails open to the plain body;
7
+ // - a 400/415 on a compressed request is drained and the SAME request is
8
+ // resent once uncompressed; compression latches off process-wide only when
9
+ // that plain resend succeeds (the encoding was the cause). If the resend
10
+ // fails too, nothing latches and the resend's response is returned.
11
+ import { promisify } from 'node:util';
12
+ import { gzip } from 'node:zlib';
13
+
14
+ export const REQ_COMPRESSION_MIN_BYTES = 8 * 1024;
15
+
16
+ export function createCompressionLatch(envVar) {
17
+ let latched = false;
18
+ return {
19
+ disabled: () => latched || process.env[envVar] === '0',
20
+ latchOff: () => {
21
+ latched = true;
22
+ },
23
+ reset: () => {
24
+ latched = false;
25
+ },
26
+ };
27
+ }
28
+
29
+ // One latch for every api.anthropic.com /v1/messages path (OAuth + API key).
30
+ export const anthropicGzipLatch = createCompressionLatch('MIXDOG_ANTHROPIC_REQ_GZIP');
31
+ // Compress on the libuv threadpool so large turn bodies never block the main thread.
32
+ export const gzipAsync = promisify(gzip);
33
+
34
+ export const hasContentEncoding = (headers) => new Headers(headers || {}).has('content-encoding');
35
+
36
+ export function withContentEncoding(headers, encoding) {
37
+ if (headers instanceof Headers || Array.isArray(headers)) {
38
+ const next = new Headers(headers);
39
+ next.delete('content-length');
40
+ next.set('Content-Encoding', encoding);
41
+ return next;
42
+ }
43
+ const rest = Object.fromEntries(
44
+ Object.entries(headers || {}).filter(([name]) => name.toLowerCase() !== 'content-length')
45
+ );
46
+ return { ...rest, 'Content-Encoding': encoding };
47
+ }
48
+
49
+ // Fail open: any compression error yields the plain body.
50
+ export async function compressBody(raw, compress, encoding) {
51
+ try {
52
+ return { bytes: await compress(raw), encoding };
53
+ } catch {
54
+ return { bytes: raw, encoding: null };
55
+ }
56
+ }
57
+
58
+ export const isEncodingRejection = (response) => response.status === 400 || response.status === 415;
59
+
60
+ export async function resendIfEncodingRejected({ response, encoded, resend, latch }) {
61
+ if (!encoded || !isEncodingRejection(response)) return response;
62
+ try {
63
+ await response.arrayBuffer();
64
+ } catch {
65
+ /* drain best-effort */
66
+ }
67
+ const retry = await resend();
68
+ if (retry.ok) latch.latchOff();
69
+ return retry;
70
+ }
71
+
72
+ /**
73
+ * Send `raw` compressed when eligible, with the rejection resend above.
74
+ * `send(bytes, headers)` performs one HTTP request.
75
+ */
76
+ export async function sendWithCompression({ raw, headers, encoding, compress, latch, send }) {
77
+ const plain = () => send(raw, headers);
78
+ if (latch.disabled() || raw.length < REQ_COMPRESSION_MIN_BYTES || hasContentEncoding(headers)) return plain();
79
+ const packed = await compressBody(raw, compress, encoding);
80
+ if (!packed.encoding) return plain();
81
+ const response = await send(packed.bytes, withContentEncoding(headers, encoding));
82
+ return resendIfEncodingRejected({ response, encoded: true, resend: plain, latch });
83
+ }
84
+
85
+ export const sendGzipCompressed = ({ compress = gzipAsync, ...rest }) =>
86
+ sendWithCompression({ ...rest, encoding: 'gzip', compress, latch: anthropicGzipLatch });
@@ -29,8 +29,10 @@ function isObject(value) {
29
29
  // answered by the server inside the same response. It is never a client call
30
30
  // awaiting a client output, so the guard neither pairs nor synthesizes it.
31
31
  function isServerToolSearch(item) {
32
- return (item.type === 'tool_search_call' || item.type === 'tool_search_output') &&
33
- (item.execution === 'server' || item.call_id === null);
32
+ return (
33
+ (item.type === 'tool_search_call' || item.type === 'tool_search_output') &&
34
+ (item.execution === 'server' || item.call_id === null)
35
+ );
34
36
  }
35
37
 
36
38
  function responsesCallId(item) {
@@ -167,7 +167,11 @@ function officeTextForFile(file) {
167
167
  });
168
168
  if (file.ref) return memoized(officeTextMemo, `ref:${file.ref}\0${file.filename || ''}`, compute);
169
169
  if (file.part && inlineOfficeText.has(file.part)) return inlineOfficeText.get(file.part);
170
- const key = createHash('sha256').update(file.filename || '').update('\0').update(file.data).digest('hex');
170
+ const key = createHash('sha256')
171
+ .update(file.filename || '')
172
+ .update('\0')
173
+ .update(file.data)
174
+ .digest('hex');
171
175
  const text = memoized(officeTextMemo, key, compute);
172
176
  if (file.part) inlineOfficeText.set(file.part, text);
173
177
  return text;
@@ -403,15 +407,19 @@ export async function preparePdfTextForProvider(messages, provider) {
403
407
  continue;
404
408
  }
405
409
  if (file && fileKind(file) === 'pdf') found.push({ part, file });
406
- else if (noDocuments && (file || part.type === 'document')) texts.set(part, file ? textFormOfFilePart(part) : documentSourceText(part));
410
+ else if (noDocuments && (file || part.type === 'document'))
411
+ texts.set(part, file ? textFormOfFilePart(part) : documentSourceText(part));
407
412
  }
408
413
  }
409
414
  if (!found.length && !texts.size) return messages;
410
415
  const extracted = await Promise.all(found.map(({ file }) => pdfTextFor(file)));
411
- found.forEach(({ part, file }, i) => texts.set(part, `--- ${fileLabel(file)} ---\n${extracted[i]}`));
416
+ found.forEach(({ part, file }, i) => {
417
+ texts.set(part, `--- ${fileLabel(file)} ---\n${extracted[i]}`);
418
+ });
412
419
  return messages.map((message) => {
413
420
  // A message whose content is one bare part becomes a one-part array.
414
- if (texts.has(message?.content)) return { ...message, content: [{ type: 'text', text: texts.get(message.content) }] };
421
+ if (texts.has(message?.content))
422
+ return { ...message, content: [{ type: 'text', text: texts.get(message.content) }] };
415
423
  const content = replacePdfParts(message?.content, texts);
416
424
  return content === message?.content ? message : { ...message, content };
417
425
  });
@@ -681,7 +689,10 @@ export function contentMediaBytes(content, { nativePdf = true } = {}) {
681
689
  if (!part || typeof part !== 'object') continue;
682
690
  if (isAttachmentReference(part)) {
683
691
  if (part.type === 'image') bytes += Math.ceil((Number(part.sizeBytes) || 0) / 3) * 4;
684
- else if (part.type === 'file' && countsAsMedia(String(part.mimeType || part.mediaType || 'application/pdf'), '')) {
692
+ else if (
693
+ part.type === 'file' &&
694
+ countsAsMedia(String(part.mimeType || part.mediaType || 'application/pdf'), '')
695
+ ) {
685
696
  bytes += Math.ceil((Number(part.sizeBytes) || 0) / 3) * 4;
686
697
  }
687
698
  continue;
@@ -9,7 +9,7 @@
9
9
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
10
10
  import { fileMtimeMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
11
11
  import { OpenAICompatProvider, preloadOpenAICompatRuntime } from './openai-compat.mjs';
12
- import { COMPAT_RETRY_POLICY } from './openai-compat-retry-profile.mjs';
12
+ import { COMPAT_RETRY_POLICY, compatFirstResponseBudget } from './openai-compat-retry-profile.mjs';
13
13
  import { fetchCompatModelItems, listCompatModels } from './openai-compat-models.mjs';
14
14
  import { makeModelCache } from './model-cache.mjs';
15
15
  import {
@@ -152,6 +152,12 @@ export class MimoOAuthProvider {
152
152
  return this.tokens;
153
153
  }
154
154
 
155
+ // The inner client's chat profile sets a longer first-byte window; the
156
+ // budget needs no credentials, so it is read from the profile directly.
157
+ firstResponseBudget() {
158
+ return compatFirstResponseBudget(this.name, MIMO_CHAT_PROFILE);
159
+ }
160
+
155
161
  _ensureInner() {
156
162
  const { apiKey, baseUrl } = this.ensureAuth();
157
163
  const key = `${baseUrl}:${apiKey}`;
@@ -90,6 +90,15 @@ export function inlineModelProxyAttachments(messages, { maxBytes = MODEL_PROXY_I
90
90
  return out ?? messages;
91
91
  }
92
92
 
93
+ /** A first-response budget the PC reported, or null for anything malformed. */
94
+ function reportedBudget(value) {
95
+ const transportMs = Number(value?.transportMs);
96
+ const semanticMs = Number(value?.semanticMs);
97
+ return transportMs > 0 && semanticMs > 0 && Number.isFinite(transportMs) && Number.isFinite(semanticMs)
98
+ ? { transportMs, semanticMs }
99
+ : null;
100
+ }
101
+
93
102
  export class ModelProxyProvider {
94
103
  #channel;
95
104
  #modelInfo = new Map();
@@ -101,6 +110,7 @@ export class ModelProxyProvider {
101
110
  this.#channel = channel;
102
111
  // Adapter traits are reported by the PC's real instance.
103
112
  if (typeof this.config.modelProxy?.nativePdf === 'boolean') this.nativePdf = this.config.modelProxy.nativePdf;
113
+ if (this.config.modelProxy?.consumesReadWindows === true) this.consumesReadWindows = true;
104
114
  }
105
115
 
106
116
  #call(method, fields = {}, options = {}) {
@@ -110,18 +120,16 @@ export class ModelProxyProvider {
110
120
  async send(messages, model, tools, sendOpts) {
111
121
  const { options, callbacks } = splitModelProxySendOptions(sendOpts);
112
122
  const finalized = isFinalizedProviderRequestTools(tools);
113
- const result = await this.#call(
114
- 'send',
115
- {
116
- model: model ?? null,
117
- messages: inlineModelProxyAttachments(messages),
118
- tools: Array.isArray(tools) ? tools : null,
119
- toolsFinalized: finalized,
120
- nativePrefixCount: finalized ? providerNativeToolPrefixCount(tools) : 0,
121
- options,
122
- },
123
- { signal: sendOpts?.signal || null, callbacks }
124
- );
123
+ const fields = {
124
+ model: model ?? null,
125
+ messages: inlineModelProxyAttachments(messages),
126
+ tools: Array.isArray(tools) ? tools : null,
127
+ toolsFinalized: finalized,
128
+ nativePrefixCount: finalized ? providerNativeToolPrefixCount(tools) : 0,
129
+ options,
130
+ };
131
+ const signal = sendOpts?.signal || null;
132
+ const result = await this.#call('send', fields, { signal, callbacks });
125
133
  // The PC rerouted the wire (a clear "unsupported" refusal) and reports the
126
134
  // route it used: the daemon's own session adopts it, so the next proxied
127
135
  // turn starts on the new wire instead of repeating the refusal.
@@ -163,6 +171,16 @@ export class ModelProxyProvider {
163
171
  return this.#modelInfo.get(String(model ?? '')) ?? null;
164
172
  }
165
173
 
174
+ /** The PC adapter's first-response budget for `model`: the catalog's
175
+ * model-specific figure, else the adapter's own from its provider config;
176
+ * null keeps the agent watchdog default. */
177
+ firstResponseBudget(model) {
178
+ return (
179
+ reportedBudget(this.getCachedModelInfo(model)?.firstResponseBudget) ??
180
+ reportedBudget(this.config.modelProxy?.firstResponseBudget)
181
+ );
182
+ }
183
+
166
184
  /** The PC adapter's wire route for `model` (its config and roster decide);
167
185
  * null for a single-wire adapter, which is remembered so every later turn
168
186
  * skips the round trip. */
@@ -222,10 +240,12 @@ export function loadModelProxyProviderConfig({
222
240
  channel = getModelProxyChannel(),
223
241
  now = Date.now,
224
242
  waitMs = MODEL_PROXY_CONFIG_WAIT_MS,
243
+ force = false,
225
244
  } = {}) {
226
245
  const generation = channel.generation;
227
246
  const cached = _providerConfigCache;
228
247
  if (
248
+ !force &&
229
249
  cached &&
230
250
  cached.channel === channel &&
231
251
  cached.generation === generation &&
@@ -234,7 +254,16 @@ export function loadModelProxyProviderConfig({
234
254
  return Promise.resolve(cached.value);
235
255
  }
236
256
  const inFlight = _providerConfigInFlight;
237
- if (inFlight && inFlight.channel === channel && inFlight.generation === generation) return inFlight.promise;
257
+ // A forced read starts after the caller's change, never reuses an older
258
+ // snapshot. Short setup reads also cannot inherit a registry init's budget.
259
+ if (
260
+ !force &&
261
+ inFlight &&
262
+ inFlight.channel === channel &&
263
+ inFlight.generation === generation &&
264
+ inFlight.waitMs === waitMs
265
+ )
266
+ return inFlight.promise;
238
267
  const promise = (async () => {
239
268
  const deadline = Date.now() + waitMs;
240
269
  let delay = CONFIG_RETRY_FIRST_MS;
@@ -247,12 +276,27 @@ export function loadModelProxyProviderConfig({
247
276
  }
248
277
  }
249
278
  try {
250
- const reply = await channel.request({ provider: '', method: 'providerConfig' });
279
+ const remaining = deadline - Date.now();
280
+ if (remaining <= 0) throw new ModelProxyUnavailableError('the desktop provider request timed out');
281
+ const controller = new AbortController();
282
+ const timer = setTimeout(
283
+ () => controller.abort(new ModelProxyUnavailableError('the desktop provider request timed out')),
284
+ remaining
285
+ );
286
+ timer.unref?.();
287
+ let reply;
288
+ try {
289
+ reply = await channel.request({ provider: '', method: 'providerConfig' }, { signal: controller.signal });
290
+ } finally {
291
+ clearTimeout(timer);
292
+ }
251
293
  const raw = reply?.providers && typeof reply.providers === 'object' ? reply.providers : {};
252
294
  // Defense in depth: the PC already strips secrets before sending.
253
295
  const value = stripModelProxySecrets(raw);
254
296
  if (providerConfigReady(reply, value)) {
255
- if (channel.generation === generation) {
297
+ // An older read may finish after a forced refresh; only the newest
298
+ // request may publish to the shared cache.
299
+ if (channel.generation === generation && _providerConfigInFlight === entry) {
256
300
  _providerConfigCache = { at: now(), value, channel, generation };
257
301
  }
258
302
  return value;
@@ -268,7 +312,7 @@ export function loadModelProxyProviderConfig({
268
312
  delay = Math.min(delay * 2, CONFIG_RETRY_MAX_MS);
269
313
  }
270
314
  })();
271
- const entry = { channel, generation, promise };
315
+ const entry = { channel, generation, waitMs, promise };
272
316
  _providerConfigInFlight = entry;
273
317
  const clear = () => {
274
318
  if (_providerConfigInFlight === entry) _providerConfigInFlight = null;
@@ -56,11 +56,6 @@ function resolveProbeState(paths, evaluate) {
56
56
  return sawUnreadable ? 'unreadable' : 'absent';
57
57
  }
58
58
 
59
- function pushUnique(list, value) {
60
- if (!value || typeof value !== 'string') return;
61
- if (!list.includes(value)) list.push(value);
62
- }
63
-
64
59
  // Short-TTL memo. Under simultaneous multi-agent launch, buildDefaultConfig
65
60
  // and registry lazy-init call these probes in bursts; each probe does
66
61
  // synchronous existsSync + readFileSync disk work. Cache the STATE per key
@@ -113,9 +108,11 @@ function memoProbe(key, paths, compute, extraSignature = '') {
113
108
  }
114
109
 
115
110
  function anthropicOAuthState() {
116
- const paths = [];
117
- pushUnique(paths, process.env.ANTHROPIC_OAUTH_CREDENTIALS_PATH);
118
- pushUnique(paths, ANTHROPIC_DEFAULT_CREDENTIALS_PATH);
111
+ const paths = [
112
+ boundProviderAuthPath('anthropic-oauth') ||
113
+ process.env.ANTHROPIC_OAUTH_CREDENTIALS_PATH ||
114
+ ANTHROPIC_DEFAULT_CREDENTIALS_PATH,
115
+ ];
119
116
  return memoProbe('anthropic-oauth', paths, () => {
120
117
  return resolveProbeState(paths, (docs) => {
121
118
  const candidates = [];
@@ -176,9 +173,7 @@ function mimoOAuthState() {
176
173
  process.env.MIMO_OAUTH_CREDENTIALS_PATH ||
177
174
  join(resolvePluginData(), 'mimo-oauth.json'),
178
175
  ];
179
- return memoProbe('mimo-oauth', paths, () =>
180
- resolveProbeState(paths, (docs) => docs.some((own) => !!own?.apiKey))
181
- );
176
+ return memoProbe('mimo-oauth', paths, () => resolveProbeState(paths, (docs) => docs.some((own) => !!own?.apiKey)));
182
177
  }
183
178
 
184
179
  function commandCodeOAuthState() {
@@ -230,7 +225,10 @@ function cursorEnvTokenFingerprint() {
230
225
  }
231
226
 
232
227
  function cursorOAuthState() {
233
- const paths = [process.env.CURSOR_OAUTH_CREDENTIALS_PATH, join(resolvePluginData(), 'cursor-oauth.json')];
228
+ // Match the adapter: selected account, then explicit path, then legacy file.
229
+ // An environment token must not authenticate a different selected account.
230
+ const bound = boundProviderAuthPath('cursor-oauth');
231
+ const paths = [bound || process.env.CURSOR_OAUTH_CREDENTIALS_PATH || join(resolvePluginData(), 'cursor-oauth.json')];
234
232
  // The env token is the whole answer on this branch and has no file metadata
235
233
  // behind it, so its VALUE is what the memo signature has to track: a length
236
234
  // (or a bare present/absent flag) let a swap to an equal-length expired token
@@ -245,7 +243,7 @@ function cursorOAuthState() {
245
243
  // Expiry is read by the one shared JWT reader (cursor-auth.cursorTokenExpiry),
246
244
  // which answers 0 for a non-JWT or unparseable token — exactly the "assume
247
245
  // usable" outcome this branch already took for those shapes.
248
- if (process.env.CURSOR_ACCESS_TOKEN) {
246
+ if (process.env.CURSOR_ACCESS_TOKEN && !bound) {
249
247
  const expiresAt = cursorTokenExpiry(process.env.CURSOR_ACCESS_TOKEN);
250
248
  return expiresAt === 0 || expiresAt > Date.now() ? 'present' : 'absent';
251
249
  }
@@ -20,7 +20,8 @@ import { normalizeCompatChatResponse } from './openai-compat-response-normalizat
20
20
  import { applyCompatToolChoice, compatStreamRetryReporter } from './compat-request-policy.mjs';
21
21
  import { ensureChatToolPairs } from './lib/wire-pairing.mjs';
22
22
  import { xaiCacheRouting } from './openai-compat-xai.mjs';
23
- import { compatRetryOptions, withCompatRetryProfile } from './openai-compat-retry-profile.mjs';
23
+ import { compatFirstByteWindowMs, compatRetryOptions, withCompatRetryProfile } from './openai-compat-retry-profile.mjs';
24
+ import { TIMEOUT_PHASE } from '../../../shared/provider-timeout-phase.mjs';
24
25
  import { preparePdfTextForProvider } from './media-normalization.mjs';
25
26
  import { customWireFormat, isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
26
27
  import { geminiAcceptedTools } from './gemini-schema.mjs';
@@ -140,29 +141,39 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
140
141
  assembled = await withRetry(
141
142
  withCompatRetryProfile(
142
143
  provider.name,
143
- refineAttemptErrors(profile, async ({ signal: attemptSignal }) => {
144
+ refineAttemptErrors(profile, async ({ signal: attemptSignal, firstByteTimeouts = 0 }) => {
144
145
  try {
145
146
  opts.onStageChange?.('requesting');
146
147
  } catch {
147
148
  /* heartbeat best-effort */
148
149
  }
149
- const stream = await withRetry(
150
- ({ signal: openSignal }) =>
151
- provider.client.chat.completions.create(params, {
152
- signal: openSignal,
153
- ...(requestHeaders ? { headers: requestHeaders } : {}),
154
- }),
155
- {
156
- signal: attemptSignal,
157
- // Single attempt: this inner wrapper exists only to
158
- // apply the first-byte per-attempt timeout. Retry is
159
- // owned by the outer withRetry — nesting retry loops
160
- // here multiplied tail latency (5x5).
161
- maxAttempts: 1,
162
- perAttemptTimeoutMs: firstByteTimeoutMs,
163
- perAttemptLabel: `${provider.name} first byte`,
150
+ const windowMs = compatFirstByteWindowMs(provider.name, firstByteTimeoutMs, firstByteTimeouts);
151
+ let stream;
152
+ try {
153
+ stream = await withRetry(
154
+ ({ signal: openSignal }) =>
155
+ provider.client.chat.completions.create(params, {
156
+ signal: openSignal,
157
+ ...(requestHeaders ? { headers: requestHeaders } : {}),
158
+ }),
159
+ {
160
+ signal: attemptSignal,
161
+ // Single attempt: this inner wrapper exists only to
162
+ // apply the first-byte per-attempt timeout. Retry is
163
+ // owned by the outer withRetry — nesting retry loops
164
+ // here multiplied tail latency (5x5).
165
+ maxAttempts: 1,
166
+ perAttemptTimeoutMs: windowMs,
167
+ perAttemptLabel: `${provider.name} first byte`,
168
+ }
169
+ );
170
+ } catch (error) {
171
+ // The per-attempt window (the only timer here) expired before headers.
172
+ if (error?.code === 'EPROVIDERTIMEOUT' && !attemptSignal?.aborted) {
173
+ error.timeoutPhase = TIMEOUT_PHASE.HEADERS;
164
174
  }
165
- );
175
+ throw error;
176
+ }
166
177
  try {
167
178
  opts.onStageChange?.('streaming');
168
179
  } catch {
@@ -181,7 +192,7 @@ export async function sendCompatChat(provider, messages, useModel, tools, opts)
181
192
  // recovered leaked calls only synthesize when they name
182
193
  // a tool actually offered to this request.
183
194
  knownToolNames: knownToolNamesFromOpenAITools(params.tools),
184
- ...(profile?.firstByteTimeoutMs ? { firstByteTimeoutMs: profile.firstByteTimeoutMs } : {}),
195
+ ...(profile?.firstByteTimeoutMs ? { firstByteTimeoutMs: windowMs } : {}),
185
196
  ...(profile?.semanticIdleTimeoutMs ? { semanticIdleTimeoutMs: profile.semanticIdleTimeoutMs } : {}),
186
197
  });
187
198
  } catch (error) {
@@ -87,7 +87,9 @@ export function isIncompleteFinishReason(finishReason) {
87
87
  /** Whether `finish_reason` confirms a normal end of the turn: present, not an
88
88
  * incomplete outcome and not a gateway-reported failure (network_error/error). */
89
89
  export function isNormalFinishReason(finishReason) {
90
- return Boolean(finishReason) && !isIncompleteFinishReason(finishReason) && !failingFinishReasonError(finishReason, '');
90
+ return (
91
+ Boolean(finishReason) && !isIncompleteFinishReason(finishReason) && !failingFinishReasonError(finishReason, '')
92
+ );
91
93
  }
92
94
 
93
95
  /** Dispatch held leaked calls once a normal finish_reason confirms the turn
@@ -11,6 +11,7 @@ import {
11
11
  PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
12
12
  createTimeoutSignal,
13
13
  } from '../stall-policy.mjs';
14
+ import { TIMEOUT_PHASE } from '../../../shared/provider-timeout-phase.mjs';
14
15
  import {
15
16
  closeCompatStream,
16
17
  markErrorLiveTextEmitted,
@@ -86,6 +87,10 @@ export async function consumeCompatChatCompletionStream(
86
87
  }
87
88
  if (state.leakGuard.enabled) flushLeak(state);
88
89
  } catch (err) {
90
+ // The first-byte window (not a caller cancellation) ended the wait for the first chunk.
91
+ if (!signal?.aborted && err && typeof err === 'object' && err === firstByteTimeout.signal.reason) {
92
+ err.timeoutPhase = TIMEOUT_PHASE.FIRST_CHUNK;
93
+ }
89
94
  // Keep exposed partial text without permitting the request to replay;
90
95
  // every mid-flight failure carries what the stream completed (a
91
96
  // cancellation keeps the caller's own reason untouched).