mixdog 1.3.5 → 1.4.0

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 (341) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/daemon-stub.mjs +1 -1
  5. package/scripts/gate-local.mjs +4 -2
  6. package/scripts/native-release-status.mjs +2 -2
  7. package/scripts/recall-history-cases.json +1 -1
  8. package/scripts/suite-health.mjs +10 -1
  9. package/scripts/test.mjs +7 -11
  10. package/src/agents/reviewer/AGENT.md +7 -11
  11. package/src/agents/reviewer/agent.json +1 -1
  12. package/src/defaults/shipped-history.json +17 -1
  13. package/src/defaults/skills/browser-use/SKILL.md +59 -11
  14. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  15. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  16. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  17. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  18. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  19. package/src/defaults/skills/setup/SKILL.md +22 -0
  20. package/src/defaults/skills/setup/references/actions.md +39 -5
  21. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  22. package/src/defaults/skills/visualize/SKILL.md +2 -1
  23. package/src/lib/rules-builder.cjs +10 -8
  24. package/src/rules/agent/AGENT.md +6 -6
  25. package/src/rules/lead/LEAD.md +18 -20
  26. package/src/rules/shared/05-tool-calls.md +5 -6
  27. package/src/rules/shared/10-tool-workflow.md +2 -0
  28. package/src/rules/shared/60-verification.md +7 -5
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  30. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  31. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  32. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  33. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  35. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  36. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  39. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +181 -133
  40. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  41. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  42. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  45. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +145 -66
  46. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  47. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  48. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  49. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  50. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  52. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
  53. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  54. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  55. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  56. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  57. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  58. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  64. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  65. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +26 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  68. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  69. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  71. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  72. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  73. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  76. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  77. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
  78. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  79. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  80. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  82. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  83. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  84. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  86. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  87. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  88. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  90. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -2
  91. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +3 -2
  93. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  94. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  95. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  97. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  98. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  99. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  100. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  101. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  102. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  103. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  104. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  105. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  106. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  107. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  108. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  109. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  110. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  112. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  113. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  114. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  115. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  116. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  117. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  118. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  120. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  121. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  122. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  123. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  124. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  125. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  126. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  127. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  128. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  129. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  130. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  131. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  132. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  133. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  134. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  135. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +18 -1
  136. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  137. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  138. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  141. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  142. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  143. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  144. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  145. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  146. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  147. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  148. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  149. package/src/runtime/browser-bridge/client.mjs +73 -8
  150. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  151. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  152. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  153. package/src/runtime/channels/lib/config.mjs +6 -61
  154. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  155. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  156. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  157. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  158. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  159. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  160. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  161. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  162. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  163. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  164. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  165. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  166. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  167. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  168. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  169. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  170. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  171. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  172. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  173. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  174. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  175. package/src/runtime/computer-bridge/actions.mjs +9 -1
  176. package/src/runtime/computer-bridge/client.mjs +180 -8
  177. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  178. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  179. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  180. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  181. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  182. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  183. package/src/runtime/connectors/google-mcp.mjs +10 -12
  184. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  185. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  186. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  187. package/src/runtime/session-search/session-search-db.mjs +73 -9
  188. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  189. package/src/runtime/shared/abort-race.mjs +89 -0
  190. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  191. package/src/runtime/shared/agent-kind.mjs +4 -3
  192. package/src/runtime/shared/bounded-download.mjs +8 -3
  193. package/src/runtime/shared/config.mjs +2 -13
  194. package/src/runtime/shared/connector-accounts.mjs +20 -10
  195. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  196. package/src/runtime/shared/connector-catalog.mjs +150 -0
  197. package/src/runtime/shared/connector-triggers.mjs +22 -19
  198. package/src/runtime/shared/defaults-separation.mjs +2 -14
  199. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  200. package/src/runtime/shared/err-text.mjs +24 -15
  201. package/src/runtime/shared/file-permissions.d.mts +0 -8
  202. package/src/runtime/shared/file-permissions.mjs +0 -11
  203. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  204. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  205. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  206. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  207. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  208. package/src/runtime/shared/mixy-presence.mjs +22 -0
  209. package/src/runtime/shared/orchestration.d.mts +2 -0
  210. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  211. package/src/runtime/shared/profile-config.cjs +1 -1
  212. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  213. package/src/runtime/shared/prompt-origin.mjs +21 -0
  214. package/src/runtime/shared/provider-accounts.mjs +7 -1
  215. package/src/runtime/shared/provider-display-name.mjs +29 -0
  216. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  217. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  218. package/src/runtime/shared/routine-trigger.mjs +10 -0
  219. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  220. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  221. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  222. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  223. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  224. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  225. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  226. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  227. package/src/runtime/terminal-bridge/client.mjs +10 -1
  228. package/src/runtime/visualize-bridge/client.mjs +27 -8
  229. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  230. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  231. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  232. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  233. package/src/session-runtime/agent-home-delivery.mjs +448 -185
  234. package/src/session-runtime/agent-task-sessions.mjs +329 -180
  235. package/src/session-runtime/boot/apis.mjs +9 -0
  236. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  237. package/src/session-runtime/boot/providers.mjs +3 -0
  238. package/src/session-runtime/boot/tools.mjs +6 -1
  239. package/src/session-runtime/config-lifecycle.mjs +6 -0
  240. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  241. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  242. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  243. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  244. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  245. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  246. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  247. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  248. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  249. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  250. package/src/session-runtime/notification-bus.mjs +31 -117
  251. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  252. package/src/session-runtime/notification-listeners.mjs +76 -0
  253. package/src/session-runtime/owned-automation-run.mjs +2 -1
  254. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  255. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  256. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  257. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  258. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  259. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  260. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  261. package/src/session-runtime/routine-owner.mjs +40 -9
  262. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  263. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  264. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  265. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  266. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  267. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  268. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  269. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  270. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  271. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  272. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  273. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  274. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  275. package/src/session-runtime/services/channel-admin.mjs +7 -13
  276. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  277. package/src/session-runtime/services/seeds.mjs +0 -28
  278. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  279. package/src/session-runtime/session-create/session-options.mjs +1 -0
  280. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  281. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  282. package/src/session-runtime/settings-system-api.mjs +13 -1
  283. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  284. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  285. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  286. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  287. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  288. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  289. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  290. package/src/session-runtime/tool-policy/policy-stage.mjs +16 -2
  291. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  292. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  293. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  294. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  295. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  296. package/src/standalone/channel-transport.mjs +1 -1
  297. package/src/standalone/daemon-channels-loader.mjs +10 -8
  298. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  299. package/src/standalone/daemon-session-control/request.mjs +51 -0
  300. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  301. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  302. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  303. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  304. package/src/standalone/daemon-session-control.mjs +124 -188
  305. package/src/standalone/daemon.mjs +26 -3
  306. package/src/standalone/session-client.mjs +7 -3
  307. package/src/standalone/session-protocol.mjs +5 -0
  308. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  309. package/src/standalone/session-service/quota-resume.mjs +3 -0
  310. package/src/standalone/session-service/retention/busy.mjs +65 -1
  311. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  312. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  313. package/src/standalone/session-tool-output.mjs +15 -7
  314. package/src/tui/app/model-options.mjs +3 -28
  315. package/src/tui/dist/index.mjs +58 -64
  316. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  317. package/src/tui/session/initial-state.mjs +3 -0
  318. package/src/tui/session/notice-surface.mjs +7 -1
  319. package/src/tui/session/queue-helpers.mjs +3 -42
  320. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  321. package/src/tui/session/session-api/routes-write.mjs +5 -1
  322. package/src/tui/session/session-api/routes.mjs +1 -0
  323. package/src/tui/session/session-api/settings.mjs +17 -1
  324. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  325. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  326. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  327. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  328. package/src/tui/session/session-flow/drain.mjs +118 -149
  329. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  330. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  331. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  332. package/src/tui/session/tool-approval-request.mjs +39 -0
  333. package/src/tui/session/tool-approval-settle.mjs +62 -0
  334. package/src/tui/session/tool-approval.mjs +69 -115
  335. package/src/tui/session/tui-steering-persist.mjs +8 -1
  336. package/src/tui/session/turn-stream.mjs +19 -2
  337. package/src/tui/session/turn.mjs +1 -0
  338. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  339. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  340. package/scripts/code-graph-description-contract.mjs +0 -188
  341. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -2,10 +2,11 @@ import { reloadProviderApiKey, resolveProviderApiKey } from '../../../shared/pro
2
2
  import { sanitizeToolPairs } from '../session/context-utils.mjs';
3
3
  import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
4
4
  import {
5
- announceAnthropicModelFallback,
6
5
  createAnthropicOverloadTracker,
7
6
  isTerminalOverloadModel,
7
+ takeAnthropicModelFallback,
8
8
  } from './anthropic-overload-fallback.mjs';
9
+ import { removeAbortListener } from './anthropic-oauth-request/abort-wiring.mjs';
9
10
  import { readStreamOutcome } from './lib/stream-outcome.mjs';
10
11
  import { createPassthroughSignal } from '../stall-policy.mjs';
11
12
  import { createAbortController } from '../../../shared/abort-controller.mjs';
@@ -18,6 +19,7 @@ import { ANTHROPIC_EFFORT_LEVELS, withEffortRejectionFallback } from './effort-r
18
19
  import { armFirstByteWatchdog, createAnthropicApiTransport } from './anthropic-api-transport.mjs';
19
20
  import { createAnthropicApiRecovery } from './anthropic-api-recovery.mjs';
20
21
  import { buildAnthropicTurnResult } from './anthropic-turn-result.mjs';
22
+ import { sendAnthropicCacheWarm } from './anthropic-cache-warm.mjs';
21
23
  import {
22
24
  assertAnthropicStreamNotEmpty,
23
25
  createAnthropicMidState,
@@ -38,6 +40,29 @@ import { streamCallbacks } from './lib/send-callbacks.mjs';
38
40
  import { preparePdfTextForProvider } from './media-normalization.mjs';
39
41
  export { _test, _toAnthropicMessagesForTest } from './anthropic-messages.mjs';
40
42
 
43
+ // One stream attempt's controller, aborted with the caller's reason when the
44
+ // send's signal aborts. An already-aborted send fails before the attempt.
45
+ function linkAttemptAbort(totalSignal) {
46
+ const streamController = createAbortController();
47
+ let cancelHandler = null;
48
+
49
+ if (totalSignal) {
50
+ if (totalSignal.aborted) {
51
+ const reason = totalSignal.reason;
52
+ throw reason instanceof Error ? reason : new Error('Anthropic request aborted');
53
+ }
54
+ cancelHandler = () => {
55
+ try {
56
+ streamController.abort(totalSignal.reason);
57
+ } catch {
58
+ // best-effort propagation of caller cancellation
59
+ }
60
+ };
61
+ totalSignal.addEventListener('abort', cancelHandler, { once: true });
62
+ }
63
+ return { streamController, cancelHandler };
64
+ }
65
+
41
66
  export class AnthropicProvider {
42
67
  // Takes a PDF as a native document block (decided by this adapter's wire protocol).
43
68
  nativePdf = true;
@@ -143,6 +168,38 @@ export class AnthropicProvider {
143
168
  throw err;
144
169
  }
145
170
  }
171
+ /**
172
+ * Cache-warm mode (anthropic-cache-warm.mjs): the request `send` would
173
+ * build for these arguments, sent once on the retry-free client — no
174
+ * key reload, fallback model or re-send — and cut at the first content
175
+ * block. `onUsage` receives the reported usage as it arrives.
176
+ */
177
+ async sendCacheWarm(messages, model, tools, opts = {}, { onUsage, accountId = '' } = {}) {
178
+ const prepared = await preparePdfTextForProvider(sanitizeToolPairs(messages), this);
179
+ const { params, requestHeaders } = await buildAnthropicApiRequest({
180
+ name: this.name,
181
+ config: this.config,
182
+ messages: prepared,
183
+ useModel: model,
184
+ tools,
185
+ opts,
186
+ fastModeLatched: this.fastModeBetaHeaderLatched,
187
+ modelInfo: isCustomProviderName(this.name) ? this.getCachedModelInfo?.(model) || null : null,
188
+ capabilityModel: this._capabilityModel(model),
189
+ });
190
+ return sendAnthropicCacheWarm({
191
+ providerName: this.name,
192
+ instance: this,
193
+ accountId,
194
+ model,
195
+ opts,
196
+ onUsage,
197
+ post: (signal) =>
198
+ this._currentClient()
199
+ .messages.create(params, { signal, ...(requestHeaders ? { headers: requestHeaders } : {}) })
200
+ .asResponse(),
201
+ });
202
+ }
146
203
  // The `anthropic` API-key provider and every custom `anthropic`-protocol
147
204
  // provider (a subclass of this one) share this send path.
148
205
  async _doSend(messages, model, tools, sendOpts) {
@@ -188,89 +245,20 @@ export class AnthropicProvider {
188
245
  const totalTimeout = createPassthroughSignal(externalSignal);
189
246
  const totalSignal = totalTimeout.signal;
190
247
 
191
- const cleanupCancelHandler = (handler) => {
192
- if (!handler) return;
193
- try {
194
- totalSignal.removeEventListener('abort', handler);
195
- } catch {}
196
- };
197
-
198
- // Request-loop 529s and SSE overload events share one counter; at the
199
- // limit the configured fallback model takes over the whole send.
200
- const overload = createAnthropicOverloadTracker({
201
- model: useModel,
202
- fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
203
- terminalAtLimit: !isCustomProviderName(this.name) && isTerminalOverloadModel(useModel),
204
- });
205
- // The SDK calls and their initial-response retry policy
206
- // (anthropic-api-transport.mjs).
207
- const transport = createAnthropicApiTransport({
208
- client: this._currentClient(),
209
- label: this.name,
248
+ const { transport, buildTurnResult, recovery, midstream } = this._createSendRecovery({
210
249
  opts,
211
250
  useModel,
212
251
  params,
213
252
  requestHeaders,
214
253
  totalSignal,
215
- overload,
216
- });
217
- // Usage accounting + caller-visible projection, shared by the streaming
218
- // success path and the non-streaming fallback (anthropic-turn-result.mjs).
219
- const buildTurnResult = (parseResult) =>
220
- buildAnthropicTurnResult(parseResult, { provider: this.name, useModel, opts });
221
- // Non-streaming re-issue of a dead stream, behind the transport-recovery
222
- // budget and the exposed-output retraction handshake
223
- // (anthropic-api-recovery.mjs).
224
- const recovery = createAnthropicApiRecovery({
225
- label: this.name,
226
- opts,
227
- useModel,
228
- params,
229
- totalSignal,
230
- transport,
231
- buildTurnResult,
232
254
  onStageChange,
233
255
  onTextReset,
234
256
  });
235
- // Bounded mid-stream retries for transient stream loss; jittered backoff
236
- // between attempts (anthropic-midstream-recovery.mjs, shared with
237
- // anthropic-oauth).
238
- const midstream = createAnthropicMidstreamRecovery({
239
- label: this.name,
240
- displayName: 'Anthropic',
241
- outcomeProvider: 'anthropic',
242
- midstreamOwner: `${this.name}-midstream`,
243
- unreachableMessage: 'Anthropic mid-stream retry: unreachable',
244
- // The transport's withRetry already exhausted the full request-level
245
- // budget for a non-OK initial response, so it must not earn an
246
- // additional SSE retry budget here.
247
- initialResponseErrorTerminal: true,
248
- maxRetries: ANTHROPIC_MAX_MIDSTREAM_RETRIES,
249
- totalSignal,
250
- recovery,
251
- onStageChange,
252
- retry529: opts.retry529 !== false,
253
- overload,
254
- });
255
257
 
256
258
  let streamingFallbackTried = false;
257
259
  try {
258
260
  for (let attemptIndex = 0; attemptIndex <= ANTHROPIC_MAX_MIDSTREAM_RETRIES; attemptIndex++) {
259
- const streamController = createAbortController();
260
- let cancelHandler = null;
261
-
262
- if (totalSignal) {
263
- if (totalSignal.aborted) {
264
- const reason = totalSignal.reason;
265
- throw reason instanceof Error ? reason : new Error('Anthropic request aborted');
266
- }
267
- cancelHandler = () => {
268
- try {
269
- streamController.abort(totalSignal.reason);
270
- } catch {}
271
- };
272
- totalSignal.addEventListener('abort', cancelHandler, { once: true });
273
- }
261
+ const { streamController, cancelHandler } = linkAttemptAbort(totalSignal);
274
262
 
275
263
  const midState = createAnthropicMidState(attemptIndex);
276
264
 
@@ -280,13 +268,17 @@ export class AnthropicProvider {
280
268
  try {
281
269
  try {
282
270
  onStageChange?.('requesting');
283
- } catch {}
271
+ } catch {
272
+ // the stage observer is advisory
273
+ }
284
274
 
285
275
  response = await transport.requestStreamingResponse();
286
276
 
287
277
  try {
288
278
  onStageChange?.('streaming');
289
- } catch {}
279
+ } catch {
280
+ // the stage observer is advisory
281
+ }
290
282
 
291
283
  firstByte = armFirstByteWatchdog(streamController, midState);
292
284
 
@@ -307,7 +299,9 @@ export class AnthropicProvider {
307
299
  );
308
300
  try {
309
301
  streamController.abort?.('Anthropic SSE complete');
310
- } catch {}
302
+ } catch {
303
+ // the stream is complete; the controller may already be aborted
304
+ }
311
305
 
312
306
  firstByte.cleanup();
313
307
  assertAnthropicStreamNotEmpty(midState, parseResult, 'Anthropic');
@@ -336,26 +330,80 @@ export class AnthropicProvider {
336
330
  return decision.value;
337
331
  } finally {
338
332
  firstByte?.cleanup();
339
- cleanupCancelHandler(cancelHandler);
333
+ removeAbortListener(totalSignal, cancelHandler);
340
334
  }
341
335
  }
342
336
  throw midstream.exhaustedError();
343
337
  } catch (err) {
344
- // The fallback trigger can surface from the request loop, a mid-stream
345
- // overload, or the non-streaming recovery: handle it around the whole
346
- // operation, not just the first request.
347
- if (!(err instanceof AnthropicFallbackTriggeredError)) throw err;
348
- process.stderr.write(`[${this.name}] ${err.message}\n`);
349
- announceAnthropicModelFallback(onStageChange, useModel, err.fallbackModel);
350
- return await this._doSend(messages, err.fallbackModel, tools, {
351
- ...opts,
352
- fallbackModel: undefined,
353
- _fallbackTriggered: true,
354
- });
338
+ const fallbackOpts = takeAnthropicModelFallback(err, { tag: this.name, model: useModel, opts, onStageChange });
339
+ return await this._doSend(messages, err.fallbackModel, tools, fallbackOpts);
355
340
  } finally {
356
341
  totalTimeout.cleanup();
357
342
  }
358
343
  }
344
+ // The recovery pipeline of one send over its fixed request: the shared
345
+ // overload counter, the transport, the turn-result projection, the
346
+ // non-streaming recovery and the bounded mid-stream retries.
347
+ _createSendRecovery({ opts, useModel, params, requestHeaders, totalSignal, onStageChange, onTextReset }) {
348
+ // Request-loop 529s and SSE overload events share one counter; at the
349
+ // limit the configured fallback model takes over the whole send.
350
+ const overload = createAnthropicOverloadTracker({
351
+ model: useModel,
352
+ fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
353
+ terminalAtLimit: !isCustomProviderName(this.name) && isTerminalOverloadModel(useModel),
354
+ });
355
+ // The SDK calls and their initial-response retry policy
356
+ // (anthropic-api-transport.mjs).
357
+ const transport = createAnthropicApiTransport({
358
+ client: this._currentClient(),
359
+ label: this.name,
360
+ opts,
361
+ useModel,
362
+ params,
363
+ requestHeaders,
364
+ totalSignal,
365
+ overload,
366
+ });
367
+ // Usage accounting + caller-visible projection, shared by the streaming
368
+ // success path and the non-streaming fallback (anthropic-turn-result.mjs).
369
+ const buildTurnResult = (parseResult) =>
370
+ buildAnthropicTurnResult(parseResult, { provider: this.name, useModel, opts });
371
+ // Non-streaming re-issue of a dead stream, behind the transport-recovery
372
+ // budget and the exposed-output retraction handshake
373
+ // (anthropic-api-recovery.mjs).
374
+ const recovery = createAnthropicApiRecovery({
375
+ label: this.name,
376
+ opts,
377
+ useModel,
378
+ params,
379
+ totalSignal,
380
+ transport,
381
+ buildTurnResult,
382
+ onStageChange,
383
+ onTextReset,
384
+ });
385
+ // Bounded mid-stream retries for transient stream loss; jittered backoff
386
+ // between attempts (anthropic-midstream-recovery.mjs, shared with
387
+ // anthropic-oauth).
388
+ const midstream = createAnthropicMidstreamRecovery({
389
+ label: this.name,
390
+ displayName: 'Anthropic',
391
+ outcomeProvider: 'anthropic',
392
+ midstreamOwner: `${this.name}-midstream`,
393
+ unreachableMessage: 'Anthropic mid-stream retry: unreachable',
394
+ // The transport's withRetry already exhausted the full request-level
395
+ // budget for a non-OK initial response, so it must not earn an
396
+ // additional SSE retry budget here.
397
+ initialResponseErrorTerminal: true,
398
+ maxRetries: ANTHROPIC_MAX_MIDSTREAM_RETRIES,
399
+ totalSignal,
400
+ recovery,
401
+ onStageChange,
402
+ retry529: opts.retry529 !== false,
403
+ overload,
404
+ });
405
+ return { transport, buildTurnResult, recovery, midstream };
406
+ }
359
407
  async listModels() {
360
408
  const apiKey = resolveProviderApiKey(this.name, this.config);
361
409
  if (!apiKey) return MODELS;
@@ -132,103 +132,12 @@ export function createAntigravityRequest({
132
132
  firstByte = ANTIGRAVITY_UNCLASSED_FIRST_BYTE,
133
133
  afterToolResult = false,
134
134
  }) {
135
- const log = (attempt, lastErr, delayMs) => {
136
- // A first-byte timeout runs on its own, shorter retry budget.
137
- const firstByteLimited = isFirstByteTimeout(lastErr) && firstByte.retries != null;
138
- emitProviderRetryStage(opts.onStageChange, {
139
- attempt: attempt + 1,
140
- maxAttempts: firstByteLimited
141
- ? Math.min(ANTIGRAVITY_RETRY.maxAttempts, firstByte.retries + 1)
142
- : ANTIGRAVITY_RETRY.maxAttempts,
143
- lastErr,
144
- delayMs,
145
- });
146
- process.stderr.write(`[antigravity] retry ${attempt + 1} after ${lastErr?.message || 'transient error'}\n`);
147
- };
148
- // Stamps what the attempt tells the user: how many attempts ran and, for a
149
- // first-byte timeout, how long the model was given in total. A timeout past
150
- // the first-byte retry budget ends the ladder here. A caller's cancellation
151
- // reason may be shared by other requests and is never stamped.
152
- const noteFailure = (err, { attemptSignal, windowMs, send }) => {
153
- if (!err || typeof err !== 'object' || attemptSignal?.aborted) return err;
154
- send.attempts += 1;
155
- err.attempts = send.attempts;
156
- if (!isFirstByteTimeout(err)) return err;
157
- send.firstByteTimeouts += 1;
158
- send.firstByteWaitedMs += windowMs;
159
- err.waitedMs = send.firstByteWaitedMs;
160
- err.providerLabel = 'Antigravity';
161
- if (firstByte.retries != null && send.firstByteTimeouts > firstByte.retries) {
162
- markProviderRecoveryExhausted(err, { owner: 'antigravity-first-byte', attempts: send.attempts, terminal: true });
163
- }
164
- return err;
165
- };
135
+ const log = (attempt, lastErr, delayMs) =>
136
+ logAntigravityRetry(opts.onStageChange, firstByte, attempt, lastErr, delayMs);
166
137
  const attemptOnce = (send) =>
167
138
  withRetry(
168
- async ({ signal: attemptSignal }) => {
169
- try {
170
- opts.onStageChange?.('requesting');
171
- } catch {
172
- /* heartbeat */
173
- }
174
- const windowMs = firstByteWindowMs(firstByte, send.firstByteTimeouts);
175
- try {
176
- const firstByteTimer = createTimeoutSignal(attemptSignal, windowMs, 'Antigravity first byte');
177
- let res;
178
- try {
179
- const response = fetchFn(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
180
- method: 'POST',
181
- headers,
182
- body,
183
- signal: firstByteTimer.signal,
184
- dispatcher: getLlmDispatcher(),
185
- });
186
- // The request is on the wire and its headers are pending: the
187
- // gateway sends them only with the first chunk, so this wait is
188
- // the model's queueing and thinking time.
189
- try {
190
- opts.onStageChange?.(MODEL_THINKING_STAGE);
191
- } catch {
192
- /* heartbeat */
193
- }
194
- res = await response;
195
- } catch (err) {
196
- // Fetch surfaces AbortError; rethrow the timer/parent
197
- // reason so same-host retry sees EPROVIDERTIMEOUT and
198
- // a caller cancel stays a cancel.
199
- if (firstByteTimer.signal.aborted && firstByteTimer.signal.reason instanceof Error) {
200
- const reason = firstByteTimer.signal.reason;
201
- if (!attemptSignal?.aborted) reason.timeoutPhase = TIMEOUT_PHASE.HEADERS;
202
- throw reason;
203
- }
204
- throw err;
205
- } finally {
206
- firstByteTimer.cleanup();
207
- }
208
- // Headers arrived: the connection is live even while the model
209
- // has not produced its first chunk.
210
- try {
211
- opts.onStreamDelta?.('transport');
212
- } catch {}
213
- if (!res.ok) {
214
- const text = await res.text().catch(() => '');
215
- const failure = antigravityError(res, text, endpoint);
216
- // Capacity is paced by the local schedule below; the generic loop
217
- // would sleep whatever wait the server names.
218
- if (isCapacityFailure(failure)) markProviderRecoveryExhausted(failure, { owner: 'antigravity-capacity' });
219
- throw failure;
220
- }
221
- try {
222
- opts.onStageChange?.('streaming');
223
- } catch {
224
- /* heartbeat */
225
- }
226
- collector.beginAttempt();
227
- return await collector.consume(res, attemptSignal, { firstByteTimeoutMs: windowMs });
228
- } catch (err) {
229
- throw noteFailure(err, { attemptSignal, windowMs, send });
230
- }
231
- },
139
+ ({ signal: attemptSignal }) =>
140
+ streamAttempt({ fetchFn, endpoint, headers, body, opts, collector, firstByte, send, attemptSignal }),
232
141
  {
233
142
  ...ANTIGRAVITY_RETRY,
234
143
  finalOnExhaust: true,
@@ -261,16 +170,128 @@ export function createAntigravityRequest({
261
170
  streamAttempt += 1;
262
171
  await sleepWithAbort(reask.waitMs, signal, sleepFn, 'Antigravity retry wait aborted');
263
172
  } catch (err) {
264
- const waitMs = antigravity429WaitMs(err, state);
265
- if (waitMs == null) {
266
- if (isCapacityFailure(err)) {
267
- markProviderRecoveryExhausted(err, { owner: 'antigravity-capacity', attempts: tried });
268
- }
269
- throw err;
270
- }
173
+ const waitMs = scheduledRetryWaitMs(err, state, tried);
271
174
  log(tried, err, waitMs);
272
175
  await sleepWithAbort(waitMs, signal, sleepFn, 'Antigravity retry wait aborted');
273
176
  }
274
177
  }
275
178
  };
276
179
  }
180
+
181
+ // The retry status and log line of one scheduled retry. A first-byte timeout
182
+ // runs on its own, shorter retry budget.
183
+ function logAntigravityRetry(onStageChange, firstByte, attempt, lastErr, delayMs) {
184
+ const firstByteLimited = isFirstByteTimeout(lastErr) && firstByte.retries != null;
185
+ emitProviderRetryStage(onStageChange, {
186
+ attempt: attempt + 1,
187
+ maxAttempts: firstByteLimited
188
+ ? Math.min(ANTIGRAVITY_RETRY.maxAttempts, firstByte.retries + 1)
189
+ : ANTIGRAVITY_RETRY.maxAttempts,
190
+ lastErr,
191
+ delayMs,
192
+ providerLabel: 'Antigravity',
193
+ });
194
+ process.stderr.write(`[antigravity] retry ${attempt + 1} after ${lastErr?.message || 'transient error'}\n`);
195
+ }
196
+
197
+ // The local wait before re-sending a failed request (429 / capacity schedule);
198
+ // a failure the schedule does not retry is rethrown, a capacity one marked
199
+ // exhausted after `tried` sends.
200
+ function scheduledRetryWaitMs(err, state, tried) {
201
+ const waitMs = antigravity429WaitMs(err, state);
202
+ if (waitMs == null) {
203
+ if (isCapacityFailure(err)) {
204
+ markProviderRecoveryExhausted(err, { owner: 'antigravity-capacity', attempts: tried });
205
+ }
206
+ throw err;
207
+ }
208
+ return waitMs;
209
+ }
210
+
211
+ // One POST under the first-byte window: headers pending is the model's
212
+ // thinking time, a non-OK body becomes the typed error, and a live stream is
213
+ // handed to the collector for this attempt.
214
+ async function streamAttempt({ fetchFn, endpoint, headers, body, opts, collector, firstByte, send, attemptSignal }) {
215
+ try {
216
+ opts.onStageChange?.('requesting');
217
+ } catch {
218
+ /* heartbeat */
219
+ }
220
+ const windowMs = firstByteWindowMs(firstByte, send.firstByteTimeouts);
221
+ try {
222
+ const firstByteTimer = createTimeoutSignal(attemptSignal, windowMs, 'Antigravity first byte');
223
+ let res;
224
+ try {
225
+ const response = fetchFn(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
226
+ method: 'POST',
227
+ headers,
228
+ body,
229
+ signal: firstByteTimer.signal,
230
+ dispatcher: getLlmDispatcher(),
231
+ });
232
+ // The request is on the wire and its headers are pending: the
233
+ // gateway sends them only with the first chunk, so this wait is
234
+ // the model's queueing and thinking time.
235
+ try {
236
+ opts.onStageChange?.(MODEL_THINKING_STAGE);
237
+ } catch {
238
+ /* heartbeat */
239
+ }
240
+ res = await response;
241
+ } catch (err) {
242
+ // Fetch surfaces AbortError; rethrow the timer/parent
243
+ // reason so same-host retry sees EPROVIDERTIMEOUT and
244
+ // a caller cancel stays a cancel.
245
+ if (firstByteTimer.signal.aborted && firstByteTimer.signal.reason instanceof Error) {
246
+ const reason = firstByteTimer.signal.reason;
247
+ if (!attemptSignal?.aborted) reason.timeoutPhase = TIMEOUT_PHASE.HEADERS;
248
+ throw reason;
249
+ }
250
+ throw err;
251
+ } finally {
252
+ firstByteTimer.cleanup();
253
+ }
254
+ // Headers arrived: the connection is live even while the model
255
+ // has not produced its first chunk.
256
+ try {
257
+ opts.onStreamDelta?.('transport');
258
+ } catch {
259
+ // the stream-delta observer is advisory
260
+ }
261
+ if (!res.ok) {
262
+ const text = await res.text().catch(() => '');
263
+ const failure = antigravityError(res, text, endpoint);
264
+ // Capacity is paced by the local schedule; the generic loop
265
+ // would sleep whatever wait the server names.
266
+ if (isCapacityFailure(failure)) markProviderRecoveryExhausted(failure, { owner: 'antigravity-capacity' });
267
+ throw failure;
268
+ }
269
+ try {
270
+ opts.onStageChange?.('streaming');
271
+ } catch {
272
+ /* heartbeat */
273
+ }
274
+ collector.beginAttempt();
275
+ return await collector.consume(res, attemptSignal, { firstByteTimeoutMs: windowMs });
276
+ } catch (err) {
277
+ throw noteAttemptFailure(err, { attemptSignal, windowMs, send, firstByte });
278
+ }
279
+ }
280
+
281
+ // Stamps what the attempt tells the user: how many attempts ran and, for a
282
+ // first-byte timeout, how long the model was given in total. A timeout past
283
+ // the first-byte retry budget ends the ladder here. A caller's cancellation
284
+ // reason may be shared by other requests and is never stamped.
285
+ function noteAttemptFailure(err, { attemptSignal, windowMs, send, firstByte }) {
286
+ if (!err || typeof err !== 'object' || attemptSignal?.aborted) return err;
287
+ send.attempts += 1;
288
+ err.attempts = send.attempts;
289
+ if (!isFirstByteTimeout(err)) return err;
290
+ send.firstByteTimeouts += 1;
291
+ send.firstByteWaitedMs += windowMs;
292
+ err.waitedMs = send.firstByteWaitedMs;
293
+ if (firstByte.retries != null && send.firstByteTimeouts > firstByte.retries) {
294
+ markProviderRecoveryExhausted(err, { owner: 'antigravity-first-byte', attempts: send.attempts, terminal: true });
295
+ }
296
+ return err;
297
+ }
@@ -0,0 +1,90 @@
1
+ // cache-warm-admission.mjs — the account gate of a cache warm
2
+ // (anthropic-cache-warm.mjs). A warm never waits, queues, rotates accounts or
3
+ // retries: right before its POST it reads the state a normal send consults
4
+ // for the account it goes to — the Developer option, the account's admission
5
+ // lane (rate-limit cooldown, a lane throttled after a 429), the account
6
+ // roster's blocks and quota windows, and the session's quota-resume pause —
7
+ // and is skipped when any of them holds or when no fresh quota reading says
8
+ // the account has room. A warm's own refusal is recorded like a send's: a 429
9
+ // in the lane cooldown, and the roster's verdict through the account pool.
10
+ import {
11
+ ACCOUNT_PROVIDERS,
12
+ providerAccountExhausted,
13
+ quotaReadingFresh,
14
+ readProviderAccountPool,
15
+ staleQuotaWindows,
16
+ } from '../../../shared/provider-accounts.mjs';
17
+ import { quotaResumeEntry } from '../../../shared/quota-resume-store.mjs';
18
+ import { recordAccountRefusal } from './account-pool.mjs';
19
+ import { providerAdmissionKey, providerAdmissionScheduler } from './admission-scheduler.mjs';
20
+ import { readCachedOAuthUsageSnapshot } from './oauth-usage.mjs';
21
+ import { providerAvailable } from './provider-availability.mjs';
22
+
23
+ function readingBlock(providerName, windows, checkedAt, now, exhausted) {
24
+ if (!Array.isArray(windows) || !quotaReadingFresh(checkedAt, now)) return 'no fresh quota reading';
25
+ if (staleQuotaWindows(providerName, windows)) return 'no fresh quota reading';
26
+ return exhausted ? 'account blocked or quota exhausted' : null;
27
+ }
28
+
29
+ function rosterBlock(providerName, accountId, model, now) {
30
+ if (!ACCOUNT_PROVIDERS.includes(providerName)) return null;
31
+ const pool = readProviderAccountPool(providerName);
32
+ const useCredits = pool.useCredits === true;
33
+ if (!pool.accounts.length) {
34
+ if (useCredits) return null;
35
+ const stored = readCachedOAuthUsageSnapshot({ provider: providerName, model: '' });
36
+ const row = stored ? { usage: { windows: stored.quotaWindows, checkedAt: stored.cachedAt } } : null;
37
+ return readingBlock(
38
+ providerName,
39
+ stored?.quotaWindows,
40
+ stored?.cachedAt,
41
+ now,
42
+ row && providerAccountExhausted(row, now, model)
43
+ );
44
+ }
45
+ if (!accountId) return 'account holding the cache unknown';
46
+ const row = pool.accounts.find((entry) => entry.id === accountId);
47
+ if (!row) return 'account not connected';
48
+ const exhausted = providerAccountExhausted(row, now, model, { useCredits });
49
+ if (useCredits) return exhausted ? 'account blocked' : null;
50
+ return readingBlock(providerName, row.usage?.windows, row.usage?.checkedAt, now, exhausted);
51
+ }
52
+
53
+ /**
54
+ * Why a warm on `instance` (account `accountId` of `providerName`; no id for
55
+ * a provider without an account roster) may not be sent now, or null. The
56
+ * instance's admission lane — the one its ordinary sends are admitted on —
57
+ * cooling down or throttled blocks it. Never waits.
58
+ */
59
+ export function cacheWarmAccountBlock(
60
+ providerName,
61
+ instance,
62
+ { accountId = '', model = '', sessionId = '', scheduler = providerAdmissionScheduler, now = Date.now() } = {}
63
+ ) {
64
+ if (!providerAvailable(providerName)) return 'provider disabled';
65
+ const lane = scheduler.immediateBlock(providerAdmissionKey(providerName, instance));
66
+ if (lane) return lane;
67
+ if (sessionId && quotaResumeEntry(sessionId)) return 'session paused until its quota resets';
68
+ return rosterBlock(providerName, accountId, model, now);
69
+ }
70
+
71
+ /** The error a skipped warm rejects with (nothing was sent or billed). */
72
+ export function cacheWarmBlockedError(reason) {
73
+ return Object.assign(new Error(`cache warm skipped: ${reason}`), { code: 'ECACHEWARMBLOCKED', usage: null });
74
+ }
75
+
76
+ /** Feed a warm's refusal into the shared state a send's would reach (the
77
+ * lane keeps 429s only; the roster judges every refusal as the pool's send
78
+ * does). A warm the caller aborted is no refusal. */
79
+ export async function recordCacheWarmRefusal(
80
+ providerName,
81
+ instance,
82
+ error,
83
+ { accountId = '', model = '', aborted = false, scheduler = providerAdmissionScheduler } = {}
84
+ ) {
85
+ if (aborted || error?.code === 'ECACHEWARMBLOCKED') return;
86
+ scheduler.recordRateLimit(providerAdmissionKey(providerName, instance), error);
87
+ if (accountId && ACCOUNT_PROVIDERS.includes(providerName)) {
88
+ await recordAccountRefusal(providerName, accountId, model, error, instance);
89
+ }
90
+ }