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
@@ -12,7 +12,9 @@ import { sendGzipCompressed } from '../lib/request-compression.mjs';
12
12
  const API_URL = 'https://api.anthropic.com/v1/messages';
13
13
  export { ANTHROPIC_VERSION };
14
14
 
15
- export async function postMessages({ accessToken, requestBody, betaHeaders, signal, compress }) {
15
+ // `plain`: exactly one uncompressed POST (a cache warm), so no compression
16
+ // rejection resend can follow.
17
+ export async function postMessages({ accessToken, requestBody, betaHeaders, signal, compress, plain = false }) {
16
18
  const headers = {
17
19
  Authorization: `Bearer ${accessToken}`,
18
20
  'anthropic-version': ANTHROPIC_VERSION,
@@ -22,17 +24,15 @@ export async function postMessages({ accessToken, requestBody, betaHeaders, sign
22
24
  'x-app': 'cli',
23
25
  'Content-Type': 'application/json',
24
26
  };
25
- return sendGzipCompressed({
26
- raw: Buffer.from(JSON.stringify(requestBody)),
27
- headers,
28
- compress,
29
- send: (body, sendHeaders) =>
30
- fetch(API_URL, {
31
- method: 'POST',
32
- headers: sendHeaders,
33
- body,
34
- signal,
35
- dispatcher: getLlmDispatcher(),
36
- }),
37
- });
27
+ const raw = Buffer.from(JSON.stringify(requestBody));
28
+ const send = (body, sendHeaders) =>
29
+ fetch(API_URL, {
30
+ method: 'POST',
31
+ headers: sendHeaders,
32
+ body,
33
+ signal,
34
+ dispatcher: getLlmDispatcher(),
35
+ });
36
+ if (plain) return send(raw, headers);
37
+ return sendGzipCompressed({ raw, headers, compress, send });
38
38
  }
@@ -43,16 +43,17 @@ import {
43
43
  } from './anthropic-oauth-credentials.mjs';
44
44
  import { claudeCliUserAgent, learnRequiredCliVersion, warmCliVersion } from './anthropic-oauth-client-version.mjs';
45
45
  import { createPassthroughSignal } from '../stall-policy.mjs';
46
- import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
47
46
  import { ANTHROPIC_MAX_MIDSTREAM_RETRIES, parseSSEStream, _classifyMidstreamError } from './anthropic-sse.mjs';
48
47
  import { buildAnthropicBetaHeaders, supportsAnthropicFastMode } from './anthropic-betas.mjs';
49
48
  import { applyAnthropicServerFallback } from './anthropic-server-fallback.mjs';
50
49
  import { fastModeAvailable, fastModeDowngraded, noteFastModeCapacityError } from './anthropic-fast-mode.mjs';
51
50
  import { ANTHROPIC_VERSION, anthropicQuotaError, createAnthropicOAuthRequest } from './anthropic-oauth-request.mjs';
52
51
  import { isRefreshableAuthRejection } from './anthropic-oauth-request/initial-status.mjs';
52
+ import { postMessages } from './anthropic-oauth-request/gzip-post.mjs';
53
+ import { sendAnthropicCacheWarm } from './anthropic-cache-warm.mjs';
53
54
  import { applyAnthropicErrorContract } from './anthropic-error-contract.mjs';
54
55
  import { adjustMaxTokensForContextOverflow } from './anthropic-max-tokens-recovery.mjs';
55
- import { announceAnthropicModelFallback, createAnthropicOverloadTracker } from './anthropic-overload-fallback.mjs';
56
+ import { createAnthropicOverloadTracker, takeAnthropicModelFallback } from './anthropic-overload-fallback.mjs';
56
57
  import { createAnthropicOAuthRecovery } from './anthropic-oauth-recovery.mjs';
57
58
  import { createMidState, createMidstreamRecovery } from './anthropic-oauth-midstream.mjs';
58
59
  import { assertAnthropicStreamNotEmpty } from './anthropic-midstream-recovery.mjs';
@@ -302,6 +303,28 @@ function buildRequestBody(requestMessages, model, tools, sendOpts) {
302
303
  return markAnthropicEffortBody(body, effortProjection);
303
304
  }
304
305
 
306
+ // Known tool names for the leaked-tool-call guard in parseSSEStream:
307
+ // recovered leaked calls are only synthesized when they name a tool
308
+ // actually offered to this request (native + lowered). Derived from the
309
+ // final request body so it matches exactly what the model was given.
310
+ function requestToolNames(body) {
311
+ return new Set(
312
+ (Array.isArray(body.tools) ? body.tools : [])
313
+ .map((t) => (t && typeof t.name === 'string' ? t.name : null))
314
+ .filter(Boolean)
315
+ );
316
+ }
317
+
318
+ // The number of mid-stream retries a streamed turn took, as a hidden field of
319
+ // the result.
320
+ function tagMidstreamRetries(result, attemptIndex) {
321
+ try {
322
+ Object.defineProperty(result, '__midstreamRetries', { value: attemptIndex, enumerable: false });
323
+ } catch {
324
+ /* ignore non-extensible result */
325
+ }
326
+ }
327
+
305
328
  export function _buildRequestBodyForCacheSmoke(messages, model, tools = [], sendOpts = {}) {
306
329
  return buildRequestBody(messages, model, tools, sendOpts);
307
330
  }
@@ -466,6 +489,41 @@ export class AnthropicOAuthProvider {
466
489
  );
467
490
  }
468
491
 
492
+ /**
493
+ * Cache-warm mode (anthropic-cache-warm.mjs): the body `send` would build
494
+ * for these arguments, POSTed once — no retry, credential replay, fallback
495
+ * model or re-send — and cut at the first content block. `onUsage` receives
496
+ * the reported usage as it arrives; resolves the final usage.
497
+ */
498
+ async sendCacheWarm(messages, model, tools, sendOpts = {}, { onUsage, accountId = '' } = {}) {
499
+ await warmCliVersion();
500
+ const opts = sendOpts || {};
501
+ const creds = await this.ensureAuth();
502
+ const body = buildRequestBody(sanitizeToolPairs(messages), model, tools, opts);
503
+ body.messages = await prepareAnthropicImages(body.messages, { signal: opts.signal });
504
+ return sendAnthropicCacheWarm({
505
+ providerName: this.name,
506
+ instance: this,
507
+ accountId,
508
+ model,
509
+ opts,
510
+ onUsage,
511
+ post: (signal) =>
512
+ postMessages({
513
+ accessToken: creds.accessToken,
514
+ requestBody: body,
515
+ betaHeaders: buildOAuthBetaHeaders(body, {
516
+ fastMode: this.fastModeBetaHeaderLatched,
517
+ toolSearch: Array.isArray(body.tools) && body.tools.some((t) => t?.defer_loading === true),
518
+ model,
519
+ opts,
520
+ }),
521
+ signal,
522
+ plain: true,
523
+ }),
524
+ });
525
+ }
526
+
469
527
  async _sendOnce(messages, model, tools, sendOpts) {
470
528
  await warmCliVersion();
471
529
  // Re-warm the kept-alive socket before the turn. preconnect() is a
@@ -505,15 +563,7 @@ export class AnthropicOAuthProvider {
505
563
  // actually carries deferred (defer_loading) tools — gate the header on
506
564
  // that instead of sending it unconditionally on every request.
507
565
  const hasDeferredTools = Array.isArray(body.tools) && body.tools.some((t) => t && t.defer_loading === true);
508
- // Known tool names for the leaked-tool-call guard in parseSSEStream:
509
- // recovered leaked calls are only synthesized when they name a tool
510
- // actually offered to this request (native + lowered). Derived from the
511
- // final request body so it matches exactly what the model was given.
512
- const knownToolNames = new Set(
513
- (Array.isArray(body.tools) ? body.tools : [])
514
- .map((t) => (t && typeof t.name === 'string' ? t.name : null))
515
- .filter(Boolean)
516
- );
566
+ const knownToolNames = requestToolNames(body);
517
567
  const sessionId = opts.sessionId || null;
518
568
  const iteration = Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : null;
519
569
  // Option A: no absolute wall-clock cap on streaming generation. A stream
@@ -530,51 +580,17 @@ export class AnthropicOAuthProvider {
530
580
  // totalSignal is therefore a pure pass-through of externalSignal with no timer.
531
581
  const totalTimeout = createPassthroughSignal(externalSignal);
532
582
  const totalSignal = totalTimeout.signal;
533
- // Request-loop 529s and SSE overload events share one counter; at the
534
- // limit the configured fallback model takes over the whole send.
535
- const overload = createAnthropicOverloadTracker({
536
- model: useModel,
537
- fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
538
- });
539
- const { requestWithRetry, cleanupCancelHandler } = createAnthropicOAuthRequest({
540
- provider: this,
583
+ const { requestWithRetry, cleanupCancelHandler, midstream } = this._createSendRecovery({
541
584
  opts,
542
585
  body,
543
586
  useModel,
544
587
  sessionId,
545
- totalSignal,
546
- overload,
547
- betaHeadersFor: (requestBody) =>
548
- buildOAuthBetaHeaders(requestBody, {
549
- fastMode: this.fastModeBetaHeaderLatched,
550
- toolSearch: hasDeferredTools,
551
- model: useModel,
552
- opts,
553
- }),
554
- onStageChange,
555
- });
556
- const recovery = createAnthropicOAuthRecovery({
557
- provider: this,
558
- opts,
559
- body,
560
- useModel,
561
588
  auth,
562
589
  totalSignal,
563
- requestWithRetry,
564
- cleanupCancelHandler,
590
+ hasDeferredTools,
565
591
  onStageChange,
566
592
  onTextReset,
567
593
  });
568
- // Bounded mid-stream retries for transient stream loss; jittered backoff
569
- // between attempts (anthropic-oauth-midstream.mjs).
570
- const midstream = createMidstreamRecovery({
571
- maxRetries: ANTHROPIC_MAX_MIDSTREAM_RETRIES,
572
- totalSignal,
573
- recovery,
574
- onStageChange,
575
- retry529: opts.retry529 !== false,
576
- overload,
577
- });
578
594
 
579
595
  let maxTokensAdjusted = false;
580
596
  try {
@@ -605,7 +621,9 @@ export class AnthropicOAuthProvider {
605
621
  if (SSE_VERBOSE) process.stderr.write(`[anthropic-oauth] Response ${response.status}, parsing SSE...\n`);
606
622
  try {
607
623
  onStageChange?.('streaming');
608
- } catch {}
624
+ } catch {
625
+ // the stage observer is advisory
626
+ }
609
627
 
610
628
  const midState = createMidState(attemptIndex);
611
629
  try {
@@ -623,7 +641,9 @@ export class AnthropicOAuthProvider {
623
641
  );
624
642
  try {
625
643
  controller?.abort?.('Anthropic SSE complete');
626
- } catch {}
644
+ } catch {
645
+ // the stream is complete; the controller may already be aborted
646
+ }
627
647
  this._settleStreamedTurn({
628
648
  result,
629
649
  midState,
@@ -633,11 +653,7 @@ export class AnthropicOAuthProvider {
633
653
  useModel,
634
654
  requestKind: opts.requestKind || null,
635
655
  });
636
- try {
637
- Object.defineProperty(result, '__midstreamRetries', { value: attemptIndex, enumerable: false });
638
- } catch {
639
- /* ignore non-extensible result */
640
- }
656
+ tagMidstreamRetries(result, attemptIndex);
641
657
  return result;
642
658
  } catch (err) {
643
659
  const decision = await midstream.onStreamError({ err, midState, controller, response, attemptIndex });
@@ -649,22 +665,81 @@ export class AnthropicOAuthProvider {
649
665
  }
650
666
  throw midstream.exhaustedError();
651
667
  } catch (err) {
652
- // The fallback trigger can surface from the request loop, a mid-stream
653
- // overload, or the non-streaming recovery: handle it around the whole
654
- // operation, not just the first request.
655
- if (!(err instanceof AnthropicFallbackTriggeredError)) throw err;
656
- process.stderr.write(`[anthropic-oauth] ${err.message}\n`);
657
- announceAnthropicModelFallback(onStageChange, _displayModel(useModel), _displayModel(err.fallbackModel));
658
- return await this.send(messages, err.fallbackModel, tools, {
659
- ...opts,
660
- fallbackModel: undefined,
661
- _fallbackTriggered: true,
668
+ const fallbackOpts = takeAnthropicModelFallback(err, {
669
+ tag: 'anthropic-oauth',
670
+ model: useModel,
671
+ opts,
672
+ onStageChange,
673
+ displayModel: _displayModel,
662
674
  });
675
+ return await this.send(messages, err.fallbackModel, tools, fallbackOpts);
663
676
  } finally {
664
677
  totalTimeout.cleanup();
665
678
  }
666
679
  }
667
680
 
681
+ // The request and recovery pipeline of one send over its fixed body: the
682
+ // shared overload counter, the retried request with its per-request beta
683
+ // headers, the non-streaming recovery and the bounded mid-stream retries.
684
+ _createSendRecovery({
685
+ opts,
686
+ body,
687
+ useModel,
688
+ sessionId,
689
+ auth,
690
+ totalSignal,
691
+ hasDeferredTools,
692
+ onStageChange,
693
+ onTextReset,
694
+ }) {
695
+ // Request-loop 529s and SSE overload events share one counter; at the
696
+ // limit the configured fallback model takes over the whole send.
697
+ const overload = createAnthropicOverloadTracker({
698
+ model: useModel,
699
+ fallbackModel: opts._fallbackTriggered ? undefined : opts.fallbackModel,
700
+ });
701
+ const { requestWithRetry, cleanupCancelHandler } = createAnthropicOAuthRequest({
702
+ provider: this,
703
+ opts,
704
+ body,
705
+ useModel,
706
+ sessionId,
707
+ totalSignal,
708
+ overload,
709
+ betaHeadersFor: (requestBody) =>
710
+ buildOAuthBetaHeaders(requestBody, {
711
+ fastMode: this.fastModeBetaHeaderLatched,
712
+ toolSearch: hasDeferredTools,
713
+ model: useModel,
714
+ opts,
715
+ }),
716
+ onStageChange,
717
+ });
718
+ const recovery = createAnthropicOAuthRecovery({
719
+ provider: this,
720
+ opts,
721
+ body,
722
+ useModel,
723
+ auth,
724
+ totalSignal,
725
+ requestWithRetry,
726
+ cleanupCancelHandler,
727
+ onStageChange,
728
+ onTextReset,
729
+ });
730
+ // Bounded mid-stream retries for transient stream loss; jittered backoff
731
+ // between attempts (anthropic-oauth-midstream.mjs).
732
+ const midstream = createMidstreamRecovery({
733
+ maxRetries: ANTHROPIC_MAX_MIDSTREAM_RETRIES,
734
+ totalSignal,
735
+ recovery,
736
+ onStageChange,
737
+ retry529: opts.retry529 !== false,
738
+ overload,
739
+ });
740
+ return { requestWithRetry, cleanupCancelHandler, midstream };
741
+ }
742
+
668
743
  // One attempt's initial response: a 401 (or Anthropic's exact revoked-token
669
744
  // 403) refreshes the credentials in `auth` and replays once; a context
670
745
  // overflow 400 shrinks `body.max_tokens` and replays once per send
@@ -687,7 +762,9 @@ export class AnthropicOAuthProvider {
687
762
  // resources before the credential-refresh replay.
688
763
  try {
689
764
  controller?.abort?.();
690
- } catch {}
765
+ } catch {
766
+ // the controller may already be aborted before the credential-refresh replay
767
+ }
691
768
  auth.creds = await this.ensureAuth({ forceRefresh: true, reason: String(response.status) });
692
769
  ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
693
770
  rejectedAuthBody = null;
@@ -702,7 +779,9 @@ export class AnthropicOAuthProvider {
702
779
  cleanupCancelHandler(cancelHandler);
703
780
  try {
704
781
  controller?.abort?.();
705
- } catch {}
782
+ } catch {
783
+ // the controller may already be aborted before the max-tokens replay
784
+ }
706
785
  ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
707
786
  rejectedAuthBody = null;
708
787
  }
@@ -12,11 +12,15 @@
12
12
  import { ANTHROPIC_MAX_CONSECUTIVE_529 } from './retry-backoff.mjs';
13
13
  import { AnthropicFallbackTriggeredError } from './retry-classifier.mjs';
14
14
 
15
- export const OVERLOADED_ERROR_CODE = 'overloaded_error';
15
+ const OVERLOADED_ERROR_CODE = 'overloaded_error';
16
16
 
17
17
  /** Consecutive 529s end the request only for API-key (non-subscriber) first-party Opus. */
18
18
  export function isTerminalOverloadModel(model) {
19
- return /^claude-opus-/.test(String(model || '').trim().toLowerCase());
19
+ return /^claude-opus-/.test(
20
+ String(model || '')
21
+ .trim()
22
+ .toLowerCase()
23
+ );
20
24
  }
21
25
 
22
26
  export function isAnthropicOverloadError(err) {
@@ -56,8 +60,26 @@ export function createAnthropicOverloadTracker({ model, fallbackModel, terminalA
56
60
  };
57
61
  }
58
62
 
63
+ /**
64
+ * The catch around a whole Anthropic send. The fallback trigger can surface
65
+ * from the request loop, a mid-stream overload, or the non-streaming
66
+ * recovery; any other error is rethrown unchanged. For the trigger it logs,
67
+ * announces the model switch, and returns the options of the single re-send
68
+ * on `err.fallbackModel` (no second hop).
69
+ */
70
+ export function takeAnthropicModelFallback(err, { tag, model, opts, onStageChange, displayModel = (id) => id }) {
71
+ if (!(err instanceof AnthropicFallbackTriggeredError)) throw err;
72
+ process.stderr.write(`[${tag}] ${err.message}\n`);
73
+ announceAnthropicModelFallback(onStageChange, displayModel(model), displayModel(err.fallbackModel));
74
+ return {
75
+ ...opts,
76
+ fallbackModel: undefined,
77
+ _fallbackTriggered: true,
78
+ };
79
+ }
80
+
59
81
  /** Visible model-switch notice on the existing display-only stage channel. */
60
- export function announceAnthropicModelFallback(onStageChange, fromLabel, toLabel) {
82
+ function announceAnthropicModelFallback(onStageChange, fromLabel, toLabel) {
61
83
  if (typeof onStageChange !== 'function') return;
62
84
  try {
63
85
  onStageChange('reconnecting', {
@@ -97,7 +97,7 @@ function _statusForAnthropicSseError(event, payload) {
97
97
  return 0;
98
98
  }
99
99
 
100
- function _anthropicSseError(event, labels) {
100
+ export function _anthropicSseError(event, labels) {
101
101
  const payload = event?.error && typeof event.error === 'object' ? event.error : event;
102
102
  const type = payload?.type || event?.type || 'error';
103
103
  const message = payload?.message || 'Anthropic SSE error';
@@ -198,7 +198,9 @@ export async function parseSSEStream(
198
198
  const progress = (kind) => {
199
199
  try {
200
200
  onStreamDelta?.(kind);
201
- } catch {}
201
+ } catch {
202
+ // the stream-delta observer is advisory
203
+ }
202
204
  };
203
205
  const turn = createAnthropicSseTurn({
204
206
  state,
@@ -222,12 +224,7 @@ export async function parseSSEStream(
222
224
  _captureMidstreamAbort(state, signal.reason);
223
225
  return signal.reason instanceof Error ? signal.reason : new Error(`${labels.display} SSE stream aborted`);
224
226
  };
225
- const onAbort = () => {
226
- try {
227
- const _c = reader.cancel('SSE aborted');
228
- if (_c && typeof _c.catch === 'function') _c.catch(() => {});
229
- } catch {}
230
- };
227
+ const onAbort = () => cancelReaderOnAbort(reader);
231
228
  let buffer = '';
232
229
  let currentEvent = '';
233
230
  retainProviderSseStream(streamKey);
@@ -239,24 +236,12 @@ export async function parseSSEStream(
239
236
  signal.addEventListener('abort', onAbort, { once: true });
240
237
  }
241
238
  watchdogs.armFirstMessageTimer();
242
- streamLoop: while (true) {
239
+ while (true) {
243
240
  let chunk;
244
241
  try {
245
242
  chunk = await watchdogs.read();
246
243
  } catch (err) {
247
- const stopped = watchdogs.readFailure() ?? (signal?.aborted ? abortedError() : null);
248
- if (stopped) {
249
- // The caller's cancellation reason may be shared: never stamp this
250
- // request's usage on it; the usage reported so far goes to this send.
251
- if (signal?.aborted && state?.sawMessageStart) {
252
- const reported = turn.reportedUsage();
253
- noteAbandonedUsage(reported.usage, reported.model);
254
- }
255
- throw stopped;
256
- }
257
- // The connection dropped mid-body: carry what this turn completed so
258
- // the loop can continue from finished tool calls instead of failing.
259
- throw state?.sawMessageStart ? turn.attachTransportPartial(err) : err;
244
+ throw readFailureError(err, { watchdogs, signal, state, turn, abortedError });
260
245
  }
261
246
  const { done, value } = chunk;
262
247
  if (done) break;
@@ -277,10 +262,7 @@ export async function parseSSEStream(
277
262
  const framed = frameProviderSseChunk(region, { currentEvent, streamKey });
278
263
  const framedChunk = typeof framed?.then === 'function' ? await framed : framed;
279
264
  currentEvent = framedChunk.currentEvent;
280
- for (const framedEvent of framedChunk.events) {
281
- if (framedEvent.error) continue;
282
- if (applyAnthropicSseEvent(framedEvent, { turn, watchdogs, state, progress, labels })) break streamLoop;
283
- }
265
+ if (applyAnthropicSseChunk(framedChunk.events, { turn, watchdogs, state, progress, labels })) break;
284
266
  }
285
267
  turn.flushLeak();
286
268
  if (state?.sawMessageStart && !state?.sawCompleted) throw turn.truncatedError();
@@ -289,20 +271,69 @@ export async function parseSSEStream(
289
271
  watchdogs.dispose();
290
272
  try {
291
273
  releaseProviderSseStream(streamKey);
292
- } catch {}
274
+ } catch {
275
+ // releasing the stream registration is bookkeeping only
276
+ }
293
277
  if (signal) signal.removeEventListener('abort', onAbort);
294
278
  // message_stop deliberately exits before EOF because Anthropic may keep
295
279
  // sending pings. Cancel the reader so the successful response body and
296
280
  // underlying keep-alive connection are not stranded.
297
281
  try {
298
282
  await reader.cancel('Anthropic SSE complete');
299
- } catch {}
283
+ } catch {
284
+ // the reader may already be closed after the stream ended
285
+ }
286
+ releaseReaderLock(reader, labels);
287
+ }
288
+ }
289
+
290
+ // Caller cancellation of a live stream: cancel the reader without awaiting it.
291
+ function cancelReaderOnAbort(reader) {
292
+ try {
293
+ const _c = reader.cancel('SSE aborted');
294
+ if (_c && typeof _c.catch === 'function') _c.catch(() => {});
295
+ } catch {
296
+ // the reader may already be closed or released
297
+ }
298
+ }
299
+
300
+ // The error a failed reader.read() ends the stream with: a watchdog stop or the
301
+ // caller's abort as-is, otherwise the transport failure carrying the partial
302
+ // turn once the message started.
303
+ function readFailureError(err, { watchdogs, signal, state, turn, abortedError }) {
304
+ const stopped = watchdogs.readFailure() ?? (signal?.aborted ? abortedError() : null);
305
+ if (stopped) {
306
+ // The caller's cancellation reason may be shared: never stamp this
307
+ // request's usage on it; the usage reported so far goes to this send.
308
+ if (signal?.aborted && state?.sawMessageStart) {
309
+ const reported = turn.reportedUsage();
310
+ noteAbandonedUsage(reported.usage, reported.model);
311
+ }
312
+ return stopped;
313
+ }
314
+ // The connection dropped mid-body: carry what this turn completed so
315
+ // the loop can continue from finished tool calls instead of failing.
316
+ return state?.sawMessageStart ? turn.attachTransportPartial(err) : err;
317
+ }
318
+
319
+ // Fold one network chunk's framed records into the turn, in order. Returns true
320
+ // once a terminal record ends the stream; a framing error skips its record.
321
+ function applyAnthropicSseChunk(events, context) {
322
+ for (const framedEvent of events) {
323
+ if (framedEvent.error) continue;
324
+ if (applyAnthropicSseEvent(framedEvent, context)) return true;
325
+ }
326
+ return false;
327
+ }
328
+
329
+ function releaseReaderLock(reader, labels) {
330
+ try {
331
+ reader.releaseLock();
332
+ } catch (err) {
300
333
  try {
301
- reader.releaseLock();
302
- } catch (err) {
303
- try {
304
- process.stderr.write(`[${labels.tag}] reader releaseLock failed: ${err?.message ?? String(err)}\n`);
305
- } catch {}
334
+ process.stderr.write(`[${labels.tag}] reader releaseLock failed: ${err?.message ?? String(err)}\n`);
335
+ } catch {
336
+ // stderr may be closed; the releaseLock failure is only reported
306
337
  }
307
338
  }
308
339
  }