mixdog 1.3.5 → 1.4.1

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 (515) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/gate-local.mjs +16 -5
  7. package/scripts/latest-guard.mjs +21 -9
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +37 -37
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +123 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/native-release-status.mjs +2 -2
  14. package/scripts/post-release-check.mjs +1 -6
  15. package/scripts/pptx-kit-index.mjs +17 -4
  16. package/scripts/recall-history-cases.json +1 -1
  17. package/scripts/suite-health.mjs +20 -2
  18. package/scripts/test-data-dir.mjs +10 -7
  19. package/scripts/test.mjs +7 -11
  20. package/src/agents/reviewer/AGENT.md +7 -11
  21. package/src/agents/reviewer/agent.json +1 -1
  22. package/src/app.mjs +4 -0
  23. package/src/defaults/shipped-history.json +22 -1
  24. package/src/defaults/skills/browser-use/SKILL.md +62 -12
  25. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  26. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  27. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  28. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  29. package/src/defaults/skills/computer-use/SKILL.md +35 -9
  30. package/src/defaults/skills/setup/SKILL.md +22 -0
  31. package/src/defaults/skills/setup/references/actions.md +39 -5
  32. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  33. package/src/defaults/skills/visualize/SKILL.md +2 -1
  34. package/src/lib/rules-builder.cjs +39 -20
  35. package/src/output-styles/common.md +2 -2
  36. package/src/output-styles/simple.md +1 -1
  37. package/src/rules/agent/AGENT.md +11 -10
  38. package/src/rules/lead/LEAD.md +23 -20
  39. package/src/rules/shared/05-tool-calls.md +5 -6
  40. package/src/rules/shared/10-tool-workflow.md +4 -0
  41. package/src/rules/shared/45-destructive.md +0 -2
  42. package/src/rules/shared/50-execution.md +2 -0
  43. package/src/rules/shared/60-verification.md +7 -5
  44. package/src/rules/shared/70-delivery.md +7 -9
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  46. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  47. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  49. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  50. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  51. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
  53. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
  54. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  56. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  57. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  58. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
  59. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  60. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  63. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  70. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
  71. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  72. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
  73. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  75. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  76. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  77. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  78. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  79. package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  83. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  84. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  85. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  87. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  88. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  89. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  90. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  91. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  93. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  113. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  117. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  118. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  119. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
  120. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  121. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  122. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  131. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  132. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
  134. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  136. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  137. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  138. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
  139. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  140. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  141. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  142. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  143. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  144. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  145. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  146. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  147. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  148. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  149. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  150. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  151. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
  153. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
  154. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  155. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  158. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
  159. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
  162. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  163. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  164. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
  165. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  166. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  167. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  168. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  172. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  173. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  174. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
  175. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  176. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  177. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
  178. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  179. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
  182. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  183. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
  185. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  187. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
  188. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  189. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  191. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
  192. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  193. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  203. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
  209. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
  214. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  217. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  218. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  225. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
  226. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
  227. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  228. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  229. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  231. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  232. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  233. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  234. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  235. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  236. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  237. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  238. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  239. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  240. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  241. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  243. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  244. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  245. package/src/runtime/browser-bridge/client.mjs +73 -8
  246. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  247. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  248. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  249. package/src/runtime/channels/lib/config.mjs +6 -61
  250. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  251. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  252. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  253. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  254. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  255. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  256. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  257. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  258. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  259. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  260. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  261. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  262. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  263. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  264. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  265. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  266. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  267. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  268. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  269. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  270. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  271. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  272. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  273. package/src/runtime/computer-bridge/actions.mjs +9 -1
  274. package/src/runtime/computer-bridge/client.mjs +180 -8
  275. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  276. package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
  277. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  278. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  279. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  280. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  281. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  282. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  283. package/src/runtime/connectors/google-mcp.mjs +10 -12
  284. package/src/runtime/local-provider/server-process.mjs +2 -1
  285. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  286. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  287. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  288. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  289. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  290. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  291. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  292. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  293. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  294. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  295. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  296. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  297. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  298. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  299. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  300. package/src/runtime/office/quality/design-review.mjs +1 -5
  301. package/src/runtime/office/quality/document-brief.mjs +1 -5
  302. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  303. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  304. package/src/runtime/session-search/session-search-db.mjs +73 -9
  305. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  306. package/src/runtime/shared/abort-race.mjs +89 -0
  307. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  308. package/src/runtime/shared/agent-kind.mjs +4 -3
  309. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  310. package/src/runtime/shared/bounded-download.mjs +8 -3
  311. package/src/runtime/shared/config.mjs +2 -13
  312. package/src/runtime/shared/connector-accounts.mjs +20 -10
  313. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  314. package/src/runtime/shared/connector-catalog.mjs +150 -0
  315. package/src/runtime/shared/connector-triggers.mjs +22 -19
  316. package/src/runtime/shared/defaults-separation.mjs +2 -17
  317. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  318. package/src/runtime/shared/err-text.mjs +24 -15
  319. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  320. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  321. package/src/runtime/shared/file-permissions.d.mts +0 -8
  322. package/src/runtime/shared/file-permissions.mjs +0 -11
  323. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  324. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  325. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  326. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  327. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  328. package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
  329. package/src/runtime/shared/llm/cost.mjs +5 -1
  330. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  331. package/src/runtime/shared/loopback-listener.mjs +25 -2
  332. package/src/runtime/shared/mixy-presence.mjs +22 -0
  333. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  334. package/src/runtime/shared/model-route-chain.mjs +22 -4
  335. package/src/runtime/shared/open-url.mjs +10 -0
  336. package/src/runtime/shared/orchestration.d.mts +2 -0
  337. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  338. package/src/runtime/shared/profile-config.cjs +1 -1
  339. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  340. package/src/runtime/shared/prompt-origin.mjs +21 -0
  341. package/src/runtime/shared/provider-accounts.mjs +7 -1
  342. package/src/runtime/shared/provider-display-name.mjs +29 -0
  343. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  344. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  345. package/src/runtime/shared/routine-trigger.mjs +10 -0
  346. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  347. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  348. package/src/runtime/shared/session-activity-signal.mjs +48 -1
  349. package/src/runtime/shared/session-scratch.mjs +37 -0
  350. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  351. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  352. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  353. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  354. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  355. package/src/runtime/shared/tool-result-summary.mjs +2 -15
  356. package/src/runtime/shared/tool-surface.mjs +3 -0
  357. package/src/runtime/shared/tool-work-units.mjs +2 -0
  358. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  359. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  360. package/src/runtime/shared/worktrees/git.mjs +15 -0
  361. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  362. package/src/runtime/shared/worktrees/test-support.mjs +14 -13
  363. package/src/runtime/terminal-bridge/client.mjs +10 -1
  364. package/src/runtime/visualize-bridge/client.mjs +29 -8
  365. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  366. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  367. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  368. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  369. package/src/session-runtime/agent-home-delivery.mjs +489 -185
  370. package/src/session-runtime/agent-task-sessions.mjs +467 -229
  371. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  372. package/src/session-runtime/boot/apis.mjs +9 -0
  373. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  374. package/src/session-runtime/boot/providers.mjs +3 -0
  375. package/src/session-runtime/boot/tools.mjs +11 -2
  376. package/src/session-runtime/bot-status.mjs +12 -6
  377. package/src/session-runtime/bot-target.mjs +38 -0
  378. package/src/session-runtime/channel-config-api.mjs +6 -1
  379. package/src/session-runtime/config-lifecycle.mjs +6 -0
  380. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  381. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  382. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  383. package/src/session-runtime/conversation-import/service.mjs +26 -5
  384. package/src/session-runtime/handoff-attachments.mjs +82 -31
  385. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
  386. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
  387. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  388. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  389. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  390. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  391. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  392. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  393. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  394. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  395. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  396. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  397. package/src/session-runtime/model-route/set-route.mjs +11 -2
  398. package/src/session-runtime/notification-bus.mjs +31 -117
  399. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  400. package/src/session-runtime/notification-listeners.mjs +76 -0
  401. package/src/session-runtime/owned-automation-run.mjs +2 -1
  402. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  403. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  404. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  405. package/src/session-runtime/provider-models.mjs +4 -2
  406. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  407. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  408. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  409. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  410. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  411. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  412. package/src/session-runtime/resource-recall-api.mjs +6 -1
  413. package/src/session-runtime/routine-owner.mjs +40 -9
  414. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  415. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  416. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  417. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  418. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  419. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  420. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  422. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  423. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  424. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  425. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  426. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  427. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  428. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  429. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  430. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  431. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  432. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  433. package/src/session-runtime/services/channel-admin.mjs +7 -13
  434. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  435. package/src/session-runtime/services/provider-admin.mjs +2 -1
  436. package/src/session-runtime/services/seeds.mjs +0 -28
  437. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  438. package/src/session-runtime/session-create/session-options.mjs +1 -0
  439. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  440. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  441. package/src/session-runtime/settings-system-api.mjs +13 -1
  442. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  443. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  444. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  445. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  446. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  447. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  448. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  449. package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
  450. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  451. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  452. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  453. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  454. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  455. package/src/session-runtime/workspace/transitions.mjs +25 -0
  456. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  457. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  458. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  459. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  460. package/src/standalone/channel-transport.mjs +1 -1
  461. package/src/standalone/daemon-channels-loader.mjs +10 -8
  462. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  463. package/src/standalone/daemon-session-control/request.mjs +51 -0
  464. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  465. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  466. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  467. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  468. package/src/standalone/daemon-session-control.mjs +142 -190
  469. package/src/standalone/daemon.mjs +26 -3
  470. package/src/standalone/session-client.mjs +7 -3
  471. package/src/standalone/session-protocol.mjs +5 -0
  472. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  473. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  474. package/src/standalone/session-service/quota-resume.mjs +3 -0
  475. package/src/standalone/session-service/retention/busy.mjs +65 -1
  476. package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
  477. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  478. package/src/standalone/session-tool-output.mjs +15 -7
  479. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  480. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  481. package/src/tui/app/model-options.mjs +3 -28
  482. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  483. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  484. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  485. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  486. package/src/tui/app/provider-setup-picker.mjs +9 -5
  487. package/src/tui/components/QuestionPanel.jsx +13 -4
  488. package/src/tui/dist/index.mjs +133 -92
  489. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  490. package/src/tui/session/initial-state.mjs +3 -0
  491. package/src/tui/session/notice-surface.mjs +7 -1
  492. package/src/tui/session/queue-helpers.mjs +3 -42
  493. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  494. package/src/tui/session/session-api/routes-write.mjs +5 -1
  495. package/src/tui/session/session-api/routes.mjs +1 -0
  496. package/src/tui/session/session-api/settings.mjs +17 -1
  497. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  498. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  499. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  500. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  501. package/src/tui/session/session-flow/drain.mjs +118 -149
  502. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  503. package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
  504. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  505. package/src/tui/session/tool-approval-request.mjs +39 -0
  506. package/src/tui/session/tool-approval-settle.mjs +62 -0
  507. package/src/tui/session/tool-approval.mjs +69 -115
  508. package/src/tui/session/tui-steering-persist.mjs +8 -1
  509. package/src/tui/session/turn-stream.mjs +19 -2
  510. package/src/tui/session/turn.mjs +1 -0
  511. package/src/ui/statusline-agents.mjs +1 -3
  512. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  513. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  514. package/scripts/code-graph-description-contract.mjs +0 -188
  515. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -27,11 +27,22 @@ export function createWsEventSwitch({
27
27
  // already cleared the pre-stream watchdog and armed the single idle timer.
28
28
  // response.created is a MEANINGFUL frame, so it also satisfies the
29
29
  // first-meaningful watchdog (keepalive/metadata frames never reach here,
30
- // so they never clear it). Semantic idle is not armed until actual model
31
- // progress; transport-only reasoning heartbeats are bounded by the outer
32
- // first-visible ceiling instead.
30
+ // so they never clear it). It is still metadata, not model output: it
31
+ // neither ends the first-output phase nor arms the semantic idle, and the
32
+ // agent sees it as transport.
33
33
  watchdogs.clearFirstMeaningful();
34
- progress('semantic');
34
+ progress('transport');
35
+ }
36
+
37
+ // A tool item starting is model output: it resets the semantic-idle timer so
38
+ // long server-side tool latency after item-added (before any arg delta) is
39
+ // not mistaken for a silent stall. An empty reasoning or message item is
40
+ // progress only once the model has produced output; before that the silent
41
+ // thinking follows it.
42
+ function onItemAdded() {
43
+ const kind = response.toolInFlight ? 'tool' : watchdogs.semanticSeen() ? 'semantic' : null;
44
+ if (kind) watchdogs.bumpSemantic();
45
+ progress(kind ?? 'transport');
35
46
  }
36
47
 
37
48
  function onOutputTextDelta(delta) {
@@ -84,11 +95,7 @@ export function createWsEventSwitch({
84
95
  case 'response.output_item.added':
85
96
  response.noteItemAdded(event.item);
86
97
  reportStreamItemStart(onStreamItemStart, responsesItemStart(event.item));
87
- // Item lifecycle is genuine progress: reset the semantic-idle timer so
88
- // long server-side tool latency after item-added (before any arg
89
- // delta) is not mistaken for a silent stall.
90
- watchdogs.resetSemantic();
91
- progress(response.toolInFlight ? 'tool' : 'semantic');
98
+ onItemAdded();
92
99
  break;
93
100
  case 'response.function_call_arguments.delta':
94
101
  case 'response.custom_tool_call_input.delta':
@@ -115,9 +122,10 @@ export function createWsEventSwitch({
115
122
  const kind = response.completeOutputItem(event.item);
116
123
  // A stream that names the phase only on the finished message.
117
124
  if (event.item?.type === 'message') reportStreamItemStart(onStreamItemStart, responsesItemStart(event.item));
118
- // Item-done is genuine lifecycle progress — reset semantic idle so
119
- // latency before the next item/args does not stall.
120
- watchdogs.resetSemantic();
125
+ // A completed item (reasoning, message, tool call) is model output —
126
+ // it ends the first-output phase and resets semantic idle so latency
127
+ // before the next item/args does not stall.
128
+ watchdogs.bumpSemantic();
121
129
  progress(kind);
122
130
  break;
123
131
  }
@@ -24,11 +24,13 @@ import {
24
24
  PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
25
25
  PROVIDER_WS_FIRST_MEANINGFUL_TIMEOUT_MS,
26
26
  PROVIDER_WS_SEMANTIC_IDLE_TIMEOUT_MS,
27
+ providerFirstOutputTimeoutMs,
27
28
  } from '../stall-policy.mjs';
28
29
  import { _wsErrLabel, WS_MAX_INCOMING_FRAME_BYTES } from './openai-ws-pool.mjs';
29
30
  import { createWsResponseState } from './openai-ws-response-state.mjs';
30
31
  import { createWsTextRelay } from './openai-ws-text-relay.mjs';
31
32
  import { createWsStreamWatchdogs } from './openai-ws-watchdogs.mjs';
33
+ import { streamFirstOutputWindow } from './lib/first-output-window.mjs';
32
34
  import {
33
35
  enableSessionTransportTracking,
34
36
  disableSessionTransportTracking,
@@ -85,6 +87,7 @@ function resolveStreamTimeouts(_timeouts) {
85
87
  interChunkMs: _positiveInt(_timeouts?.interChunkMs, WS_INTER_CHUNK_MS),
86
88
  maxIncomingFrameBytes: _positiveInt(_timeouts?.maxIncomingFrameBytes, WS_MAX_INCOMING_FRAME_BYTES),
87
89
  firstMeaningfulMs: _positiveInt(_timeouts?.firstMeaningfulMs, PROVIDER_WS_FIRST_MEANINGFUL_TIMEOUT_MS),
90
+ firstOutputMs: _positiveInt(_timeouts?.firstOutputMs, providerFirstOutputTimeoutMs(semanticIdleMs)),
88
91
  semanticIdleMs,
89
92
  semanticIdleEnabled: PROVIDER_SSE_IDLE_WATCHDOG_ENABLED && semanticIdleMs > 0,
90
93
  };
@@ -112,7 +115,8 @@ export async function _streamResponse({
112
115
  if (socket?.readyState === WebSocket.OPEN) {
113
116
  markSessionTransportActivity(state?.sessionId);
114
117
  }
115
- const { maxIncomingFrameBytes, ...timeouts } = resolveStreamTimeouts(_timeouts);
118
+ const { maxIncomingFrameBytes, firstOutputMs, ...timeouts } = resolveStreamTimeouts(_timeouts);
119
+ const firstOutput = streamFirstOutputWindow({ sessionId: state?.sessionId, onStreamDelta, timeoutMs: firstOutputMs });
116
120
  const streamingStart = Date.now();
117
121
  // Mid-stream retry classifier needs to distinguish "stream died before we
118
122
  // even saw response.created" from "stream died after we had a partial
@@ -182,6 +186,7 @@ export async function _streamResponse({
182
186
  streamingStart,
183
187
  currentModel: () => response.model,
184
188
  timeouts,
189
+ firstOutput,
185
190
  attachPartialState,
186
191
  fail,
187
192
  });
@@ -256,6 +261,7 @@ export async function _streamResponse({
256
261
  if (!handlers.attach()) return;
257
262
  watchdogs.armPreStream();
258
263
  watchdogs.armFirstMeaningful();
264
+ watchdogs.armFirstOutput();
259
265
  // No proactive client ping: Codex's Responses WebSocket uses the library
260
266
  // default (automatic pong replies, no periodic client ping). Incoming
261
267
  // frames remain bounded by the 300s stream idle timeout.
@@ -1,14 +1,18 @@
1
1
  /**
2
- * openai-ws-watchdogs.mjs — the four timers that bound one Responses WS
2
+ * openai-ws-watchdogs.mjs — the five timers that bound one Responses WS
3
3
  * stream, and what each one proves when it fires:
4
4
  *
5
5
  * pre-stream — no server event at all after response.create (a wedged
6
6
  * post-upgrade socket); cleared by ANY frame.
7
7
  * first-meaningful — no response.created / content / tool-arg delta, even
8
8
  * while keepalive frames keep arriving; cleared once.
9
+ * first-output — no semantic output (reasoning, text, tool call) within
10
+ * the first-output window; metadata, empty items and
11
+ * keepalives never clear or extend it; cleared once.
9
12
  * inter-chunk — byte silence mid-stream; reset by EVERY frame.
10
- * semantic idle — deltas then silence (keepalives only); reset only by
11
- * meaningful output and item lifecycle.
13
+ * semantic idle — deltas then silence (keepalives only); armed by the
14
+ * first semantic output, then reset by meaningful output
15
+ * and item lifecycle.
12
16
  *
13
17
  * Every expiry hands a tagged error to `fail`, which owns terminalError, the
14
18
  * socket close and the settle; the timers never touch the Promise.
@@ -23,6 +27,8 @@ import { streamStalledError } from '../stall-policy.mjs';
23
27
  * @param {number} deps.streamingStart
24
28
  * @param {() => string} deps.currentModel
25
29
  * @param {{ preResponseCreatedMs: number, firstMeaningfulMs: number, interChunkMs: number, semanticIdleMs: number, semanticIdleEnabled: boolean }} deps.timeouts
30
+ * @param {ReturnType<typeof import('./lib/first-output-window.mjs').streamFirstOutputWindow>} deps.firstOutput
31
+ * the logical request's first-output window
26
32
  * @param {(err: Error) => Error} deps.attachPartialState
27
33
  * @param {(err: Error, closeReason: string) => void} deps.fail
28
34
  */
@@ -32,6 +38,7 @@ export function createWsStreamWatchdogs({
32
38
  streamingStart,
33
39
  currentModel,
34
40
  timeouts,
41
+ firstOutput,
35
42
  attachPartialState,
36
43
  fail,
37
44
  }) {
@@ -90,23 +97,25 @@ export function createWsStreamWatchdogs({
90
97
  fail(err, 'inter_chunk_timeout');
91
98
  }, interChunkMs);
92
99
  };
100
+ // A semantic stall (first-output or idle window): partial-final recovery
101
+ // attaches the streamed partial state so a wedged FINAL no-tool summary can
102
+ // be accepted as partial-final success by the loop. pendingToolUse gates
103
+ // out mid-flight tools.
104
+ const failStalled = (event, timeoutMs) => {
105
+ traceWsTimeout(event, timeoutMs);
106
+ const err = streamStalledError('Responses WS', timeoutMs, {
107
+ emittedToolCall: !!midState?.emittedToolCall,
108
+ });
109
+ attachPartialState(err);
110
+ try {
111
+ err.wsCloseCode = 4000;
112
+ } catch {}
113
+ fail(err, event);
114
+ };
93
115
  const resetSemanticIdle = () => {
94
116
  if (!semanticIdleEnabled) return;
95
117
  if (semanticIdleTimer) clearTimeout(semanticIdleTimer);
96
- semanticIdleTimer = setTimeout(() => {
97
- traceWsTimeout('semantic_idle_timeout', semanticIdleMs);
98
- const err = streamStalledError('Responses WS', semanticIdleMs, {
99
- emittedToolCall: !!midState?.emittedToolCall,
100
- });
101
- // Partial-final recovery: attach streamed partial state so a wedged
102
- // FINAL no-tool summary can be accepted as partial-final success by
103
- // the loop. pendingToolUse gates out mid-flight tools.
104
- attachPartialState(err);
105
- try {
106
- err.wsCloseCode = 4000;
107
- } catch {}
108
- fail(err, 'semantic_idle_timeout');
109
- }, semanticIdleMs);
118
+ semanticIdleTimer = setTimeout(() => failStalled('semantic_idle_timeout', semanticIdleMs), semanticIdleMs);
110
119
  try {
111
120
  semanticIdleTimer.unref?.();
112
121
  } catch {}
@@ -164,24 +173,41 @@ export function createWsStreamWatchdogs({
164
173
  firstMeaningfulTimer.unref?.();
165
174
  } catch {}
166
175
  },
176
+ /** Fires if the logical request produces no semantic output by its
177
+ * first-output deadline. Metadata (response.created / in_progress), empty
178
+ * item lifecycle and keepalive frames never clear or extend it, and the
179
+ * shorter semantic idle does not run before the first output, so a model
180
+ * may think silently past the idle window. A request that already had
181
+ * output has none: its stream runs under the semantic idle from its
182
+ * first frame (resetIdle). */
183
+ armFirstOutput() {
184
+ if (!firstOutput.outputSeen && semanticIdleEnabled && firstOutput.timeoutMs > 0) {
185
+ firstOutput.arm(() => failStalled('first_output_timeout', firstOutput.timeoutMs));
186
+ }
187
+ },
167
188
  /** Called on EVERY parsed server event: any frame proves the socket is
168
189
  * live. Also clears the pre-stream watchdog in case the first event is
169
- * not response.created. */
190
+ * not response.created, and starts the semantic idle (once) for a
191
+ * request that already had output. */
170
192
  resetIdle() {
171
193
  clearPreStream();
172
194
  resetInterChunk();
195
+ if (firstOutput.outputSeen && !semanticIdleTimer) resetSemanticIdle();
173
196
  },
174
- /** Meaningful-output progress: text/reasoning/tool-arg deltas. The
175
- * first one also satisfies the first-meaningful watchdog. */
197
+ /** Meaningful-output progress: text/reasoning/tool output. The first one
198
+ * also satisfies the first-meaningful watchdog and ends the first-output
199
+ * phase. */
176
200
  bumpSemantic() {
177
201
  clearFirstMeaningful();
202
+ firstOutput.noteOutput();
178
203
  resetSemanticIdle();
179
204
  },
180
- /** Item lifecycle progress (added/done) without a delta. */
181
- resetSemantic: resetSemanticIdle,
205
+ /** Whether the model has produced semantic output on this stream. */
206
+ semanticSeen: () => firstOutput.outputSeen,
182
207
  /** response.created is meaningful on its own. */
183
208
  clearFirstMeaningful,
184
209
  clearAll() {
210
+ firstOutput.clear();
185
211
  if (idleTimer) clearTimeout(idleTimer);
186
212
  if (interChunkTimer) {
187
213
  clearTimeout(interChunkTimer);
@@ -5,6 +5,7 @@
5
5
  // to the older fields (httpStatus, responseFailed, providerError, SDK error
6
6
  // bodies, RPC status strings). Message text is consulted only for untyped
7
7
  // errors and only for unambiguous wordings.
8
+ import { TIMEOUT_PHASE, isFirstByteTimeout } from '../../../shared/provider-timeout-phase.mjs';
8
9
 
9
10
  export const PROVIDER_ERROR_KINDS = Object.freeze([
10
11
  'overloaded',
@@ -391,6 +392,13 @@ const CODE_KINDS = new Map(
391
392
  }).flatMap(([kind, codes]) => codes.map((code) => [code, kind]))
392
393
  );
393
394
  const RESOURCE_EXHAUSTED = 'resource_exhausted';
395
+
396
+ /** True when `code` is one of the provider error codes this module classifies
397
+ * (rate/usage limits, quota, overload, server/transient, context length,
398
+ * model not found, auth, policy, …): a known vocabulary word, never free text. */
399
+ function isKnownProviderErrorCode(code) {
400
+ return typeof code === 'string' && (CODE_KINDS.has(code) || code === RESOURCE_EXHAUSTED);
401
+ }
394
402
  // When several typed codes are present (an envelope type plus a specific
395
403
  // code), the most specific verdict wins.
396
404
  const KIND_RANK = [
@@ -435,7 +443,7 @@ const GATEWAY_ERROR_TYPE_CODES = Object.freeze({
435
443
  });
436
444
 
437
445
  /** The typed code a gateway `error_type` names, or null for an absent/unmapped one. */
438
- export function gatewayErrorTypeCode(errorType) {
446
+ function gatewayErrorTypeCode(errorType) {
439
447
  const key = typeof errorType === 'string' ? errorType.trim().toLowerCase() : '';
440
448
  return key && Object.hasOwn(GATEWAY_ERROR_TYPE_CODES, key) ? GATEWAY_ERROR_TYPE_CODES[key] : null;
441
449
  }
@@ -718,6 +726,62 @@ export function providerErrorKind(err, options = {}) {
718
726
  return describe(kind, code || undefined);
719
727
  }
720
728
 
729
+ // ── Retry reason, without server text ───────────────────────────────────────
730
+ // A server error's free text can echo request content (prompt, tokens,
731
+ // account ids), so neither the retry trace nor the reconnect status carries
732
+ // it: only a fixed reason class derived from typed status/code, and a code
733
+ // only when it is a known provider error code (an identifier-shaped value can
734
+ // still be private, e.g. an account id in a `code` field).
735
+ const SERVER_ERROR_CODES = new Set([
736
+ 'server_error',
737
+ 'api_error',
738
+ 'internal',
739
+ 'internal_error',
740
+ 'unavailable',
741
+ 'service_unavailable',
742
+ ]);
743
+ // Responses failure codes the WS retry classifier acts on beyond the shared
744
+ // provider vocabulary.
745
+ const WS_RETRY_ERROR_CODES = new Set(['auth_context_expired', 'auth_expired']);
746
+
747
+ /** The typed error code when it is a known provider error code; null otherwise. */
748
+ export function safeRetryErrorCode(err) {
749
+ const code = providerErrorKind(err).code;
750
+ return isKnownProviderErrorCode(code) || WS_RETRY_ERROR_CODES.has(code) ? code : null;
751
+ }
752
+
753
+ const STALL_ERROR_NAMES = new Set(['StreamStalledError', 'StreamStalledAbortError']);
754
+
755
+ /**
756
+ * One of rate_limited / overloaded / server_error / auth / first_response_timeout /
757
+ * stalled / other. The timeout classes read our own typed timeout stamps
758
+ * (name, code, timeoutPhase), never message text.
759
+ */
760
+ export function retryReasonClass(err) {
761
+ const { kind, status } = providerErrorKind(err);
762
+ const code = safeRetryErrorCode(err);
763
+ if (kind === 'rate_limited' || kind === 'quota_exhausted' || status === 429) return 'rate_limited';
764
+ if (kind === 'overloaded' || status === 529) return 'overloaded';
765
+ if (kind === 'auth' || kind === 'permission' || status === 401 || status === 403) return 'auth';
766
+ // A token-refresh timeout is a sign-in problem, not a slow model.
767
+ if (err?.timeoutPhase === TIMEOUT_PHASE.AUTH) return 'auth';
768
+ if ((status >= 500 && status < 600) || (code && SERVER_ERROR_CODES.has(code))) return 'server_error';
769
+ if (err?.timeoutPhase === TIMEOUT_PHASE.IDLE || STALL_ERROR_NAMES.has(err?.name) || err?.code === 'ESTREAMSTALL') {
770
+ return 'stalled';
771
+ }
772
+ // `firstByteTimeout` is the stamp the Anthropic first-message and OpenAI
773
+ // WS/HTTP first-event watchdogs put on their errors.
774
+ if (
775
+ isFirstByteTimeout(err) ||
776
+ err?.firstByteTimeout === true ||
777
+ err?.name === 'ProviderTimeoutError' ||
778
+ err?.code === 'EPROVIDERTIMEOUT'
779
+ ) {
780
+ return 'first_response_timeout';
781
+ }
782
+ return 'other';
783
+ }
784
+
721
785
  /** Kinds whose refusal no wait or replay can change: quota, plan and billing. */
722
786
  export const QUOTA_KINDS = new Set(['quota_exhausted', 'entitlement', 'billing']);
723
787
  /** Kinds that are deterministic refusals of this request (never replayed as-is). */
@@ -13,6 +13,8 @@ import {
13
13
  } from '../../../shared/provider-accounts.mjs';
14
14
  import { fetchOAuthUsageSnapshot } from './oauth-usage.mjs';
15
15
  import { getProvider } from './registry.mjs';
16
+ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
17
+ import { getModelProxyChannel } from './model-proxy-channel.mjs';
16
18
 
17
19
  const forcedReading = (routeInfo, providerObj) =>
18
20
  fetchOAuthUsageSnapshot(routeInfo, providerObj, () => {}, { force: true }).catch(() => null);
@@ -40,15 +42,15 @@ function readingServesModel(reading, model, useCredits) {
40
42
  return measured.length > 0 && measured.every((window) => window.usedPct < 100);
41
43
  }
42
44
 
43
- export async function providerQuotaUsable(providerName, model = '', { provider = getProvider(providerName) } = {}) {
45
+ /** The model-independent, account-wide measurement: one forced usage reading
46
+ * per roster account (or the single credential). Null for a provider that
47
+ * reports no exhausted quota. */
48
+ export async function measureProviderQuota(providerName, { provider } = {}) {
44
49
  // Only an account-pooled provider reports exhausted quota at all.
45
- if (!ACCOUNT_PROVIDERS.includes(providerName)) return true;
50
+ if (!ACCOUNT_PROVIDERS.includes(providerName)) return null;
51
+ if (provider === undefined) provider = getProvider(providerName);
46
52
  const pool = readProviderAccountPool(providerName);
47
53
  const useCredits = pool.useCredits === true;
48
- // Only this reading counts as evidence: an account whose forced reading
49
- // failed or measured nothing for this model stays exhausted however its
50
- // older record reads; the pool's own choice (selection, blocks) decides
51
- // among the rest.
52
54
  if (pool.accounts.length > 0) {
53
55
  const readings = await Promise.all(
54
56
  pool.accounts.map((row) =>
@@ -58,13 +60,37 @@ export async function providerQuotaUsable(providerName, model = '', { provider =
58
60
  )
59
61
  )
60
62
  );
61
- const unproven = new Set(
62
- pool.accounts.filter((_, index) => !readingServesModel(readings[index], model, useCredits)).map((row) => row.id)
63
- );
64
- if (unproven.size === pool.accounts.length) return false;
65
- return chooseProviderAccount(readProviderAccountPool(providerName), unproven, Date.now(), model) !== null;
63
+ return {
64
+ providerName,
65
+ useCredits,
66
+ accounts: pool.accounts.map((row, index) => ({ id: row.id, reading: readings[index] })),
67
+ };
66
68
  }
67
69
  // No roster: the single credential's fresh meter decides.
68
70
  const reading = await forcedReading({ provider: providerName, model: '' }, provider);
69
- return readingServesModel(reading, model, useCredits);
71
+ return { providerName, useCredits, accounts: null, reading };
72
+ }
73
+
74
+ /** Whether `model` can be served per `measurement` (from measureProviderQuota).
75
+ * Only the measurement counts as evidence: an account whose forced reading
76
+ * failed or measured nothing for this model stays exhausted however its
77
+ * older record reads; the pool's own choice (selection, blocks) decides
78
+ * among the rest. */
79
+ export function quotaMeasurementServesModel(measurement, model = '') {
80
+ if (!measurement) return true;
81
+ const { providerName, useCredits, accounts } = measurement;
82
+ if (!accounts) return readingServesModel(measurement.reading, model, useCredits);
83
+ const unproven = new Set(
84
+ accounts.filter((row) => !readingServesModel(row.reading, model, useCredits)).map((row) => row.id)
85
+ );
86
+ if (unproven.size === accounts.length) return false;
87
+ return chooseProviderAccount(readProviderAccountPool(providerName), unproven, Date.now(), model) !== null;
88
+ }
89
+
90
+ export async function providerQuotaUsable(providerName, model = '', { provider } = {}) {
91
+ // The PC owns the accounts in proxy mode; do not resolve a server provider.
92
+ if (modelProxyClientMode()) {
93
+ return (await getModelProxyChannel().request({ provider: providerName, method: 'quotaUsable', model })) === true;
94
+ }
95
+ return quotaMeasurementServesModel(await measureProviderQuota(providerName, { provider }), model);
70
96
  }
@@ -70,6 +70,17 @@ let _initChain = Promise.resolve();
70
70
  let _inFlightPromise = null;
71
71
  let _inFlightSig = null;
72
72
  let _lastAppliedSig = null;
73
+ // Settles when an initialization by the owning runtime first applies.
74
+ let _markProvidersInitialized;
75
+ const _providersInitialized = new Promise((resolve) => {
76
+ _markProvidersInitialized = resolve;
77
+ });
78
+
79
+ /** Read-only readiness: resolves once the owning runtime's provider
80
+ * initialization has applied a config (never starts one). */
81
+ export function providersInitialized() {
82
+ return _providersInitialized;
83
+ }
73
84
  // Provider instances are process-global inside the daemon. Catalog
74
85
  // readers use this revision to share one raw model snapshot while still
75
86
  // rebuilding their cheap route/config projection after auth/config changes.
@@ -223,7 +234,14 @@ async function instantiateProvider(name, Ctor, cfg, proxyChannel = null) {
223
234
  return createAccountPoolProvider(name, () => wrapProviderAdmission(new Ctor(cfg), name));
224
235
  }
225
236
 
237
+ let _initProvidersCalls = 0;
238
+ /** Test seam: how many times initProviders was called in this process. */
239
+ export function _initProvidersCallCountForTest() {
240
+ return _initProvidersCalls;
241
+ }
242
+
226
243
  export async function initProviders(config, { signal = null } = {}) {
244
+ _initProvidersCalls += 1;
227
245
  throwIfAborted(signal);
228
246
  // Remote mode: provider availability and config are the PC's, never this
229
247
  // server's credential probes. No connected client fails here, clearly.
@@ -263,6 +281,7 @@ export async function initProviders(config, { signal = null } = {}) {
263
281
  if (_lastAppliedSig !== sig) _providerCatalogRevision += 1;
264
282
  _lastAppliedSig = sig;
265
283
  settle();
284
+ _markProvidersInitialized();
266
285
  return v;
267
286
  },
268
287
  (err) => {
@@ -452,6 +471,13 @@ function _scheduleOAuthSelfHeal(name) {
452
471
  // by its replacement account. When that account is not connected the lookup
453
472
  // misses like any other unconnected provider.
454
473
  const RETIRED_PROVIDER_ALIASES = Object.freeze({ 'opencode-go': 'opencode-go-oauth' });
474
+
475
+ /** The instance registered under `name` right now, or null — a read-only
476
+ * lookup (no lazy registration, unlike getProvider). */
477
+ export function registeredProvider(name) {
478
+ return providers.get(name) || null;
479
+ }
480
+
455
481
  export function getProvider(requestedName) {
456
482
  const name = Object.hasOwn(RETIRED_PROVIDER_ALIASES, requestedName)
457
483
  ? RETIRED_PROVIDER_ALIASES[requestedName]
@@ -658,6 +684,18 @@ export function refreshProviderCatalogsOnStartup() {
658
684
  return _startupCatalogRefreshPromise;
659
685
  }
660
686
 
687
+ // Force-refresh only the named providers' catalogs, each bounded like the full
688
+ // refresh. It leaves the 24h window, the catalog revision and every other
689
+ // provider's cache alone.
690
+ export function refreshProviderCatalogsFor(names) {
691
+ const pending = [];
692
+ for (const [name, provider] of availableProviderEntries()) {
693
+ const refreshFn = names.includes(name) ? providerCatalogRefreshFn(provider) : null;
694
+ if (refreshFn) pending.push(boundedCatalogRefresh(name, provider, refreshFn, 'catalog refresh'));
695
+ }
696
+ return Promise.allSettled(pending);
697
+ }
698
+
661
699
  // Refresh the complete catalog at most once per 24h process window. The timer
662
700
  // above calls this automatically when the window expires; callers may bypass
663
701
  // that window after an explicit auth/config/model-refresh action.
@@ -4,7 +4,7 @@ import { PROVIDER_RETRY_JITTER_RATIO, createTimeoutSignal } from '../stall-polic
4
4
  import { readStreamOutcome } from './lib/stream-outcome.mjs';
5
5
  import { recycleLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
6
6
  import { noteErrorUsage } from './lib/note-error-usage.mjs';
7
- import { retryReasonText, retryStatusLine } from '../../../shared/err-text.mjs';
7
+ import { retryStatusLine, retryTimeoutText } from '../../../shared/err-text.mjs';
8
8
  import {
9
9
  TERMINAL_EDGE_STATUSES,
10
10
  boundedCauseChain,
@@ -13,7 +13,14 @@ import {
13
13
  isToolInputCut,
14
14
  retryAfterMsFromError,
15
15
  } from './retry-classification.mjs';
16
- import { DETERMINISTIC_KINDS, QUOTA_KINDS, errorHeaderValue, providerErrorKind } from './provider-error-kind.mjs';
16
+ import {
17
+ DETERMINISTIC_KINDS,
18
+ QUOTA_KINDS,
19
+ errorHeaderValue,
20
+ providerErrorKind,
21
+ retryReasonClass,
22
+ safeRetryErrorCode,
23
+ } from './provider-error-kind.mjs';
17
24
  import { adviceGatedRetry } from './retry-midstream.mjs';
18
25
  import {
19
26
  ModelFallbackTriggeredError,
@@ -35,7 +42,9 @@ export function createStreamSafetyStamps() {
35
42
  try {
36
43
  e.liveTextEmitted = true;
37
44
  e.unsafeToRetry = true;
38
- } catch {}
45
+ } catch {
46
+ // the error object may be frozen; the live-text stamp is advisory
47
+ }
39
48
  }
40
49
  return e;
41
50
  };
@@ -44,7 +53,9 @@ export function createStreamSafetyStamps() {
44
53
  try {
45
54
  e.emittedToolCall = true;
46
55
  e.unsafeToRetry = true;
47
- } catch {}
56
+ } catch {
57
+ // the error object may be frozen; the tool-call stamp is advisory
58
+ }
48
59
  }
49
60
  return e;
50
61
  };
@@ -313,6 +324,19 @@ function retryWaitMs(
313
324
  return retryAfterJitterRatio > 0 ? jitterDelayMs(wait, retryAfterJitterRatio, 'positive') : wait;
314
325
  }
315
326
 
327
+ /** Fixed status phrases for a caller-classified retry reason. */
328
+ const RETRY_REASON_TEXT = Object.freeze({
329
+ rate_limited: 'Rate limited by the provider',
330
+ overloaded: 'Provider overloaded',
331
+ server_error: 'Provider server error',
332
+ auth: 'Provider rejected the sign-in',
333
+ other: 'Connection interrupted',
334
+ });
335
+
336
+ // Timeout classes read our own typed timeout stamps (retryTimeoutText), so
337
+ // their phrases carry the wait window.
338
+ const TIMEOUT_REASON_CLASSES = new Set(['first_response_timeout', 'stalled']);
339
+
316
340
  /**
317
341
  * Display-only 'reconnecting' stage for one retry wait, so it reads as a
318
342
  * reconnect instead of a silent spinner. `attempt` is the attempt about to run
@@ -321,20 +345,28 @@ function retryWaitMs(
321
345
  */
322
346
  export function emitProviderRetryStage(
323
347
  onStageChange,
324
- { attempt, maxAttempts, lastErr, delayMs, classifier = null, extra = null }
348
+ { attempt, maxAttempts, lastErr, delayMs, classifier = null, extra = null, reasonClass = null, providerLabel = null }
325
349
  ) {
326
350
  if (typeof onStageChange !== 'function') return;
327
351
  try {
328
- const reason = retryReasonText(lastErr);
352
+ // The reason is a fixed phrase for a class (the caller's, else derived from
353
+ // the error's typed status/code), never the error's own text; an
354
+ // unrecognized class reads as `other`.
355
+ const cls = reasonClass == null ? retryReasonClass(lastErr) : reasonClass;
356
+ const fixedClass = Object.hasOwn(RETRY_REASON_TEXT, cls) || TIMEOUT_REASON_CLASSES.has(cls) ? cls : 'other';
357
+ const reason = TIMEOUT_REASON_CLASSES.has(fixedClass)
358
+ ? retryTimeoutText(lastErr, fixedClass, providerLabel)
359
+ : RETRY_REASON_TEXT[fixedClass];
329
360
  const waitMs = Math.max(0, Number(delayMs) || 0);
330
361
  onStageChange('reconnecting', {
331
362
  ...extra,
363
+ reasonClass: fixedClass,
332
364
  attempt,
333
365
  max: maxAttempts,
334
366
  waitMs,
335
367
  retryAt: Date.now() + waitMs,
336
368
  reason,
337
- classifier: classifier || lastErr?.retryClassifier || lastErr?.code || null,
369
+ classifier: classifier || lastErr?.retryClassifier || safeRetryErrorCode(lastErr),
338
370
  message: retryStatusLine({ reason, attempt, maxAttempts, delayMs: waitMs }),
339
371
  });
340
372
  } catch {
@@ -34,6 +34,12 @@ export function recordGatewayUsageEvent(summary) {
34
34
  return requireRouteMeta().recordGatewayUsageEvent(summary);
35
35
  }
36
36
 
37
+ /** Resolves once every recorded usage event has been written; nothing was
38
+ * recorded while the module is not loaded. */
39
+ export function settleGatewayUsageWrites() {
40
+ return routeMeta ? routeMeta.settleGatewayUsageWrites() : Promise.resolve();
41
+ }
42
+
37
43
  export function summarizeGatewayUsage(routeInfo, providerOut, compact = null, durationMs = null) {
38
44
  return requireRouteMeta().summarizeGatewayUsage(routeInfo, providerOut, compact, durationMs);
39
45
  }