mixdog 0.9.63 → 0.9.65

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 (408) hide show
  1. package/README.md +3 -0
  2. package/package.json +9 -2
  3. package/scripts/code-graph-description-contract.mjs +3 -3
  4. package/scripts/verify-release-assets.mjs +2 -2
  5. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
  6. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
  7. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  8. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  9. package/src/runtime/agent/orchestrator/config.mjs +19 -124
  10. package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
  11. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
  12. package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
  13. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
  14. package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
  15. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
  16. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
  17. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
  18. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
  19. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
  20. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
  24. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
  25. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
  27. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
  30. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
  32. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
  37. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
  39. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
  41. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
  46. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
  49. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
  50. package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
  51. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
  54. package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
  57. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
  59. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
  60. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
  61. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
  62. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
  64. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
  66. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
  68. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
  69. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
  70. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
  73. package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
  74. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
  75. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
  77. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  79. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  81. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
  83. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
  91. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
  92. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
  93. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
  96. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
  97. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
  99. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
  102. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
  105. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
  106. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
  107. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
  108. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
  109. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
  110. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
  111. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
  112. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
  115. package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
  116. package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
  119. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
  120. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
  121. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  123. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
  124. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
  127. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
  129. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
  130. package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
  131. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  132. package/src/runtime/channels/lib/telegram-format.mjs +1 -1
  133. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
  134. package/src/runtime/channels/tool-defs.mjs +2 -3
  135. package/src/runtime/memory/index.mjs +1 -5
  136. package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
  137. package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
  138. package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
  139. package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
  140. package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
  141. package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
  142. package/src/runtime/memory/lib/ko-morph.mjs +2 -2
  143. package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
  144. package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
  145. package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
  146. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
  147. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
  148. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
  149. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
  150. package/src/runtime/memory/lib/memory-embed.mjs +2 -6
  151. package/src/runtime/memory/lib/memory-log.mjs +8 -0
  152. package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
  153. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
  154. package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
  155. package/src/runtime/memory/lib/memory-score.mjs +1 -1
  156. package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
  157. package/src/runtime/memory/lib/memory.mjs +2 -6
  158. package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
  159. package/src/runtime/memory/lib/pg/process.mjs +1 -5
  160. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
  161. package/src/runtime/memory/lib/recall-format.mjs +2 -2
  162. package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
  163. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
  164. package/src/runtime/memory/lib/trace-store.mjs +2 -6
  165. package/src/runtime/search/lib/http-fetch.mjs +6 -6
  166. package/src/runtime/search/lib/web-tools.mjs +2 -2
  167. package/src/runtime/shared/buffered-appender.mjs +1 -1
  168. package/src/runtime/shared/child-guardian.mjs +1 -1
  169. package/src/runtime/shared/child-spawn-gate.mjs +1 -1
  170. package/src/runtime/shared/config.mjs +4 -4
  171. package/src/runtime/shared/launcher-control.mjs +9 -10
  172. package/src/runtime/shared/llm/index.mjs +5 -20
  173. package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
  174. package/src/runtime/shared/memory-snapshot.mjs +4 -4
  175. package/src/runtime/shared/pristine-execution.mjs +1 -1
  176. package/src/runtime/shared/process-lifecycle.mjs +0 -1
  177. package/src/runtime/shared/process-shutdown.mjs +1 -1
  178. package/src/runtime/shared/resource-admission.mjs +2 -2
  179. package/src/runtime/shared/schedule-session-run.mjs +1 -1
  180. package/src/runtime/shared/schedules-db.mjs +1 -1
  181. package/src/runtime/shared/service-discovery.mjs +2 -2
  182. package/src/runtime/shared/staged-update.mjs +6 -6
  183. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  184. package/src/runtime/shared/tool-primitives.mjs +1 -1
  185. package/src/runtime/shared/tool-surface.mjs +2 -2
  186. package/src/runtime/shared/turn-snapshot.mjs +184 -0
  187. package/src/runtime/shared/update-checker.mjs +1 -1
  188. package/src/runtime/shared/user-cwd.mjs +3 -3
  189. package/src/runtime/shared/user-data-guard.mjs +4 -4
  190. package/src/runtime/shared/workspace-router.mjs +5 -5
  191. package/src/session-runtime/config-helpers.mjs +2 -3
  192. package/src/session-runtime/config-lifecycle.mjs +1 -1
  193. package/src/session-runtime/hitch-profile.mjs +1 -1
  194. package/src/session-runtime/lifecycle-api.mjs +6 -4
  195. package/src/session-runtime/model-recency.mjs +5 -5
  196. package/src/session-runtime/plugin-mcp.mjs +1 -1
  197. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  198. package/src/session-runtime/runtime-core.mjs +4 -0
  199. package/src/session-runtime/session-text.mjs +4 -4
  200. package/src/session-runtime/session-turn-api.mjs +7 -0
  201. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  202. package/src/session-runtime/tool-catalog.mjs +6 -483
  203. package/src/session-runtime/tool-defs.mjs +1 -1
  204. package/src/standalone/agent-task-status.mjs +1 -1
  205. package/src/standalone/agent-tool/helpers.mjs +2 -2
  206. package/src/standalone/channel-admin.mjs +4 -4
  207. package/src/standalone/explore-tool.mjs +3 -3
  208. package/src/standalone/hook-bus/config.mjs +1 -1
  209. package/src/standalone/hook-bus/constants.mjs +1 -1
  210. package/src/standalone/hook-bus/rules.mjs +1 -1
  211. package/src/standalone/projects.mjs +1 -1
  212. package/src/standalone/provider-admin.mjs +2 -2
  213. package/src/tui/App.jsx +1 -1
  214. package/src/tui/app/app-format.mjs +2 -2
  215. package/src/tui/app/clipboard.mjs +3 -3
  216. package/src/tui/app/input-parsers.mjs +1 -1
  217. package/src/tui/app/text-layout.mjs +1 -1
  218. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  219. package/src/tui/app/transcript-window.mjs +5 -347
  220. package/src/tui/app/use-mouse-input.mjs +1 -1
  221. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  222. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  223. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  224. package/src/tui/components/tool-output-format.mjs +7 -7
  225. package/src/tui/display-width.mjs +4 -4
  226. package/src/tui/dist/index.mjs +187 -99
  227. package/src/tui/engine/agent-envelope.mjs +5 -5
  228. package/src/tui/engine/live-share.mjs +100 -0
  229. package/src/tui/engine/notification-plan.mjs +1 -1
  230. package/src/tui/engine/queue-helpers.mjs +3 -3
  231. package/src/tui/engine/render-timing.mjs +4 -5
  232. package/src/tui/engine/session-api-ext.mjs +6 -3
  233. package/src/tui/engine/tool-result-status.mjs +5 -5
  234. package/src/tui/engine/tool-result-text.mjs +1 -1
  235. package/src/tui/engine/transcript-spill.mjs +585 -0
  236. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  237. package/src/tui/engine.mjs +29 -474
  238. package/src/tui/lib/voice-setup.mjs +1 -1
  239. package/src/tui/markdown/format-token.mjs +1 -1
  240. package/src/tui/markdown/render-ansi.mjs +1 -1
  241. package/src/tui/markdown/table-layout.mjs +4 -4
  242. package/src/tui/paste-attachments.mjs +1 -1
  243. package/src/tui/prompt-history-store.mjs +3 -3
  244. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  245. package/src/tui/theme.mjs +1 -1
  246. package/src/tui/transcript-tool-failures.mjs +2 -2
  247. package/src/ui/ansi.mjs +0 -9
  248. package/src/ui/statusline-format.mjs +2 -4
  249. package/scripts/_bench-cwc.json +0 -20
  250. package/scripts/_jitter-fuzz.jsx +0 -42
  251. package/scripts/_jitter-fuzz2.jsx +0 -30
  252. package/scripts/_jitter-probe.jsx +0 -35
  253. package/scripts/_jp2.jsx +0 -16
  254. package/scripts/_smoke_wd.mjs +0 -7
  255. package/scripts/abort-recovery-test.mjs +0 -175
  256. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  257. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  258. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  259. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  260. package/scripts/agent-loop-policy-test.mjs +0 -37
  261. package/scripts/agent-model-liveness-test.mjs +0 -763
  262. package/scripts/agent-parallel-smoke.mjs +0 -435
  263. package/scripts/agent-route-batch-test.mjs +0 -40
  264. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  265. package/scripts/agent-terminal-reap-test.mjs +0 -252
  266. package/scripts/agent-trace-io-test.mjs +0 -133
  267. package/scripts/ansi-color-capability-test.mjs +0 -90
  268. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  269. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  270. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  271. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  272. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  273. package/scripts/arg-guard-test.mjs +0 -93
  274. package/scripts/async-notify-settlement-test.mjs +0 -99
  275. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  276. package/scripts/background-task-meta-smoke.mjs +0 -38
  277. package/scripts/bench-run.mjs +0 -508
  278. package/scripts/boot-smoke.mjs +0 -137
  279. package/scripts/build-runtime-windows.ps1 +0 -242
  280. package/scripts/channel-daemon-smoke.mjs +0 -1103
  281. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  282. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  283. package/scripts/code-graph-dispatch-test.mjs +0 -96
  284. package/scripts/code-graph-root-federation-test.mjs +0 -273
  285. package/scripts/compact-active-turn-test.mjs +0 -68
  286. package/scripts/compact-file-reattach-test.mjs +0 -88
  287. package/scripts/compact-pressure-test.mjs +0 -788
  288. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  289. package/scripts/compact-recall-digest-test.mjs +0 -57
  290. package/scripts/compact-smoke.mjs +0 -1017
  291. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  292. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  293. package/scripts/context-mcp-metering-test.mjs +0 -1406
  294. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  295. package/scripts/deferred-tool-loading-test.mjs +0 -250
  296. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  297. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  298. package/scripts/embedding-worker-exit-test.mjs +0 -76
  299. package/scripts/execution-completion-dedup-test.mjs +0 -205
  300. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  301. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  302. package/scripts/explore-bench-tmp.mjs +0 -36
  303. package/scripts/explore-bench.mjs +0 -248
  304. package/scripts/explore-prompt-policy-test.mjs +0 -256
  305. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  306. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  307. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  308. package/scripts/freevar-smoke.mjs +0 -98
  309. package/scripts/gemini-provider-test.mjs +0 -1448
  310. package/scripts/generate-oc-icons.mjs +0 -11
  311. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  312. package/scripts/headless-pristine-execution-test.mjs +0 -614
  313. package/scripts/hook-bus-test.mjs +0 -552
  314. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  315. package/scripts/internal-comms-bench-test.mjs +0 -226
  316. package/scripts/internal-comms-bench.mjs +0 -853
  317. package/scripts/internal-comms-smoke.mjs +0 -277
  318. package/scripts/internal-tools-normalization-test.mjs +0 -62
  319. package/scripts/interrupted-turn-history-test.mjs +0 -399
  320. package/scripts/lead-workflow-smoke.mjs +0 -592
  321. package/scripts/lifecycle-api-test.mjs +0 -137
  322. package/scripts/live-share-test.mjs +0 -148
  323. package/scripts/live-worker-smoke.mjs +0 -333
  324. package/scripts/log-writer-guard-smoke.mjs +0 -131
  325. package/scripts/maintenance-default-routes-test.mjs +0 -164
  326. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  327. package/scripts/max-output-recovery-test.mjs +0 -368
  328. package/scripts/mcp-client-normalization-test.mjs +0 -45
  329. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  330. package/scripts/memory-core-input-test.mjs +0 -167
  331. package/scripts/memory-cycle-routing-test.mjs +0 -111
  332. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  333. package/scripts/memory-pg-recovery-test.mjs +0 -59
  334. package/scripts/memory-rule-contract-test.mjs +0 -93
  335. package/scripts/model-list-sanitize-test.mjs +0 -37
  336. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  337. package/scripts/native-edit-wire-test.mjs +0 -152
  338. package/scripts/notify-completion-mirror-test.mjs +0 -73
  339. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  340. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  341. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  342. package/scripts/output-style-bench.mjs +0 -292
  343. package/scripts/output-style-smoke.mjs +0 -145
  344. package/scripts/parent-abort-link-test.mjs +0 -90
  345. package/scripts/patch-binary-cache-test.mjs +0 -181
  346. package/scripts/path-suffix-test.mjs +0 -57
  347. package/scripts/pending-completion-drop-test.mjs +0 -239
  348. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  349. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  350. package/scripts/process-lifecycle-test.mjs +0 -461
  351. package/scripts/prompt-immediate-render-test.mjs +0 -89
  352. package/scripts/prompt-input-parity-test.mjs +0 -145
  353. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  354. package/scripts/provider-contract-test.mjs +0 -632
  355. package/scripts/provider-stream-stall-test.mjs +0 -276
  356. package/scripts/provider-toolcall-test.mjs +0 -3063
  357. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  358. package/scripts/recall-bench.mjs +0 -359
  359. package/scripts/resource-admission-test.mjs +0 -792
  360. package/scripts/result-classification-test.mjs +0 -75
  361. package/scripts/rg-runner-test.mjs +0 -240
  362. package/scripts/routing-corpus-test.mjs +0 -349
  363. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  364. package/scripts/session-bench-cache-break-test.mjs +0 -102
  365. package/scripts/session-bench.mjs +0 -1708
  366. package/scripts/session-context-bench.mjs +0 -344
  367. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  368. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  369. package/scripts/session-ingest-smoke.mjs +0 -177
  370. package/scripts/session-orphan-sweep-test.mjs +0 -109
  371. package/scripts/set-effort-config-test.mjs +0 -41
  372. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  373. package/scripts/shell-hardening-test.mjs +0 -209
  374. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  375. package/scripts/ship-mode-test.mjs +0 -98
  376. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  377. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  378. package/scripts/smoke-loop-report.mjs +0 -221
  379. package/scripts/smoke-loop.mjs +0 -202
  380. package/scripts/smoke-runtime-negative.ps1 +0 -106
  381. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  382. package/scripts/steering-drain-buckets-test.mjs +0 -467
  383. package/scripts/steering-fold-provenance-test.mjs +0 -71
  384. package/scripts/streaming-tail-window-test.mjs +0 -175
  385. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  386. package/scripts/task-bench.mjs +0 -205
  387. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  388. package/scripts/termio-input-smoke.mjs +0 -199
  389. package/scripts/tool-result-hook-test.mjs +0 -48
  390. package/scripts/tool-smoke.mjs +0 -2453
  391. package/scripts/tool-tui-presentation-test.mjs +0 -286
  392. package/scripts/toolcall-args-test.mjs +0 -239
  393. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  394. package/scripts/tui-background-failure-smoke.mjs +0 -73
  395. package/scripts/tui-perf-run.ps1 +0 -26
  396. package/scripts/tui-render-smoke.mjs +0 -90
  397. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  398. package/scripts/tui-runtime-load-bench.mjs +0 -50
  399. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  400. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  401. package/scripts/tui-transcript-perf-test.mjs +0 -934
  402. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  403. package/scripts/verify-release-assets-test.mjs +0 -281
  404. package/scripts/web-fetch-routing-test.mjs +0 -158
  405. package/scripts/webhook-smoke.mjs +0 -201
  406. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  407. package/scripts/worker-notify-rejection-test.mjs +0 -51
  408. package/scripts/write-backpressure-test.mjs +0 -147
@@ -1,726 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
-
4
- import {
5
- ANTHROPIC_RETRY_BACKOFF_MS,
6
- ANTHROPIC_RETRY_JITTER_RATIO,
7
- AnthropicFallbackTriggeredError,
8
- anthropicMaxAttempts,
9
- anthropicRequestTimeoutMs,
10
- classifyError,
11
- classifyMidstreamError,
12
- MAX_SAFE_TIMEOUT_MS,
13
- MIDSTREAM_RETRY_POLICY,
14
- withRetry,
15
- } from '../src/runtime/agent/orchestrator/providers/retry-classifier.mjs';
16
- import { _midstreamSleepWithAbort, parseSSEStream } from '../src/runtime/agent/orchestrator/providers/anthropic-sse.mjs';
17
- import { AnthropicProvider } from '../src/runtime/agent/orchestrator/providers/anthropic.mjs';
18
- import { AnthropicOAuthProvider } from '../src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs';
19
- import {
20
- ANTHROPIC_CACHE_TTL_STABLE,
21
- ANTHROPIC_CACHE_TTL_VOLATILE,
22
- resolveAnthropicCacheTtls,
23
- } from '../src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs';
24
-
25
- test('Anthropic cache defaults keep system/tier3 stable and messages volatile', () => {
26
- assert.deepEqual(resolveAnthropicCacheTtls(), {
27
- tools: null,
28
- system: ANTHROPIC_CACHE_TTL_STABLE,
29
- tier3: ANTHROPIC_CACHE_TTL_STABLE,
30
- messages: ANTHROPIC_CACHE_TTL_VOLATILE,
31
- });
32
- const descending = resolveAnthropicCacheTtls({
33
- cacheStrategy: { system: '5m', tier3: '1h', messages: '1h' },
34
- });
35
- assert.equal(descending.system, ANTHROPIC_CACHE_TTL_VOLATILE);
36
- assert.equal(descending.tier3, ANTHROPIC_CACHE_TTL_VOLATILE);
37
- assert.equal(descending.messages, ANTHROPIC_CACHE_TTL_VOLATILE);
38
- });
39
-
40
- test('Anthropic transport classifier covers every 5xx, 529, and nested connection errno', () => {
41
- for (const status of [500, 501, 505, 529, 599]) {
42
- assert.equal(classifyError({ status }), 'transient');
43
- }
44
- assert.equal(classifyError({ code: 'EWRAPPED', cause: { code: 'EUNKNOWN', cause: { code: 'ECONNRESET' } } }), 'transient');
45
- assert.equal(classifyError({ name: 'APIConnectionError' }), 'transient');
46
- assert.equal(classifyError({ code: 'TRUNCATED_STREAM', partialToolCall: true }), 'permanent');
47
- assert.equal(classifyError({ code: 'TRUNCATED_STREAM', emittedThinking: true }), 'permanent');
48
- assert.equal(classifyError({ status: 403 }), 'auth');
49
- assert.equal(classifyError({ code: 'TRUNCATED_STREAM', status: 401 }), 'auth');
50
- assert.equal(classifyError(Object.assign(new Error('canceled'), {
51
- name: 'AbortError',
52
- cause: { code: 'ECONNRESET' },
53
- })), 'permanent');
54
- assert.equal(classifyError({ status: 418, cause: { code: 'ECONNRESET' } }), 'permanent');
55
- assert.equal(classifyError({ status: 409, cause: { code: 'EUNKNOWN' } }), 'transient');
56
-
57
- const sdkAbort = Object.assign(new Error('Request was aborted.'), {
58
- cause: { code: 'ECONNRESET' },
59
- });
60
- Object.defineProperty(sdkAbort, 'constructor', { value: { name: 'APIUserAbortError' } });
61
- assert.equal(sdkAbort.name, 'Error');
62
- assert.equal(classifyError(sdkAbort), 'permanent');
63
- assert.equal(classifyError({
64
- name: 'Error',
65
- type: 'api_user_abort_error',
66
- cause: { code: 'ECONNRESET' },
67
- }), 'permanent');
68
- assert.equal(classifyError({
69
- name: 'Error',
70
- message: 'Request was aborted.',
71
- cause: { code: 'ECONNRESET' },
72
- }), 'transient', 'abort-like message alone is not an SDK type marker');
73
-
74
- const cyclicA = { code: 'EWRAPPED' };
75
- const cyclicB = { code: 'EUNKNOWN', cause: cyclicA };
76
- cyclicA.cause = cyclicB;
77
- assert.equal(classifyError(cyclicA), 'unknown');
78
- cyclicB.code = 'ECONNRESET';
79
- assert.equal(classifyError(cyclicA), 'transient');
80
- });
81
-
82
- test('API-key Anthropic reloads typed 401 once while ordinary 403 is terminal', async () => {
83
- const provider401 = Object.create(AnthropicProvider.prototype);
84
- let attempts401 = 0;
85
- let reloads401 = 0;
86
- provider401._doSend = async () => {
87
- attempts401 += 1;
88
- if (attempts401 === 1) throw Object.assign(new Error('typed auth'), { status: 401 });
89
- return 'ok';
90
- };
91
- provider401.reloadApiKey = () => { reloads401 += 1; };
92
- assert.equal(await provider401.send([], 'claude', [], {}), 'ok');
93
- assert.equal(attempts401, 2);
94
- assert.equal(reloads401, 1);
95
-
96
- const provider403 = Object.create(AnthropicProvider.prototype);
97
- let attempts403 = 0;
98
- let reloads403 = 0;
99
- const forbidden = Object.assign(new Error('ordinary forbidden'), { status: 403 });
100
- provider403._doSend = async () => { attempts403 += 1; throw forbidden; };
101
- provider403.reloadApiKey = () => { reloads403 += 1; };
102
- await assert.rejects(provider403.send([], 'claude', [], {}), (err) => err === forbidden);
103
- assert.equal(attempts403, 1);
104
- assert.equal(reloads403, 0);
105
-
106
- for (const marker of ['partialToolCall', 'emittedThinking']) {
107
- const provider = Object.create(AnthropicProvider.prototype);
108
- let attempts = 0;
109
- let reloads = 0;
110
- const exposed401 = Object.assign(new Error(`401 after ${marker}`), {
111
- status: 401,
112
- [marker]: true,
113
- });
114
- provider._doSend = async () => { attempts += 1; throw exposed401; };
115
- provider.reloadApiKey = () => { reloads += 1; };
116
- await assert.rejects(provider.send([], 'claude', [], {}), (err) => err === exposed401);
117
- assert.equal(attempts, 1, `${marker} must prevent 401 replay`);
118
- assert.equal(reloads, 0, `${marker} must prevent key reload`);
119
- }
120
- });
121
-
122
- test('permanent quota 429 is not retried by the generic rate-limit path', async () => {
123
- let attempts = 0;
124
- const quota = new Error('request failed: insufficient_quota');
125
- await assert.rejects(withRetry(async () => {
126
- attempts += 1;
127
- throw quota;
128
- }, { maxAttempts: 3, backoffMs: [0], retryJitterRatio: 0 }), (err) => err === quota);
129
- assert.equal(attempts, 1);
130
- assert.equal(classifyError(quota), 'permanent');
131
- });
132
-
133
- test('OAuth subscription 429 fails immediately without honoring Retry-After', async () => {
134
- const oauth = Object.create(AnthropicOAuthProvider.prototype);
135
- oauth.credentials = { accessToken: 'fixture', expiresAt: Date.now() + 60_000 };
136
- oauth.config = {};
137
- oauth.fastModeBetaHeaderLatched = false;
138
- oauth.ensureAuth = async () => oauth.credentials;
139
- oauth.scrubTokens = (text) => text;
140
- let attempts = 0;
141
- const limited = {
142
- status: 429,
143
- ok: false,
144
- headers: new Map([['retry-after', '14400']]),
145
- async text() { return 'subscription rate limit'; },
146
- };
147
- await assert.rejects(oauth.send([], 'claude-sonnet-4-6', [], {
148
- _doRequestFn: async () => {
149
- attempts += 1;
150
- return {
151
- response: limited,
152
- controller: { abort() {} },
153
- cancelHandler: null,
154
- };
155
- },
156
- }), (err) => err?.status === 429 && err?.retryAfterMs === 14_400_000);
157
- assert.equal(attempts, 1);
158
- });
159
-
160
- test('structured and nested permanent quota codes are terminal; rate-limit codes retry', async () => {
161
- const terminalCases = [
162
- Object.assign(new Error('generic 429'), { status: 429, code: 'insufficient_quota' }),
163
- Object.assign(new Error('generic 429'), { status: 429, error: { code: 'quota_exceeded' } }),
164
- Object.assign(new Error('generic 429'), {
165
- status: 429,
166
- cause: { code: 'EWRAPPED', cause: { error: { code: 'resource_exhausted' } } },
167
- }),
168
- ];
169
- for (const quota of terminalCases) {
170
- let attempts = 0;
171
- await assert.rejects(withRetry(async () => {
172
- attempts += 1;
173
- throw quota;
174
- }, { maxAttempts: 2, backoffMs: [0], retryJitterRatio: 0 }), (err) => err === quota);
175
- assert.equal(attempts, 1);
176
- }
177
-
178
- for (const code of ['rate_limit_error', 'rate_limit_exceeded']) {
179
- let attempts = 0;
180
- const result = await withRetry(async () => {
181
- attempts += 1;
182
- if (attempts === 1) throw Object.assign(new Error('generic 429'), { status: 429, code });
183
- return 'recovered';
184
- }, { maxAttempts: 2, backoffMs: [0], retryJitterRatio: 0 });
185
- assert.equal(result, 'recovered');
186
- assert.equal(attempts, 2);
187
- }
188
- });
189
-
190
- test('partial tool/thinking markers veto every request retry path, including 429', async () => {
191
- for (const status of [429, 529]) {
192
- for (const marker of ['partialToolCall', 'emittedThinking']) {
193
- let attempts = 0;
194
- const exposed = Object.assign(new Error(`${status} after ${marker}`), {
195
- status,
196
- [marker]: true,
197
- });
198
- await assert.rejects(withRetry(async () => {
199
- attempts += 1;
200
- throw exposed;
201
- }, {
202
- maxAttempts: 4,
203
- backoffMs: [0],
204
- retryJitterRatio: 0,
205
- model: 'claude-opus-primary',
206
- fallbackModel: 'claude-sonnet-fallback',
207
- }), (err) => err === exposed);
208
- assert.equal(attempts, 1, `${status}/${marker} must not replay`);
209
- }
210
- }
211
- });
212
-
213
- test('optional fallback triggers on the third 529 and never when absent', async () => {
214
- let attempts = 0;
215
- await assert.rejects(withRetry(async () => {
216
- attempts += 1;
217
- throw Object.assign(new Error('busy'), { status: 529, httpStatus: 529 });
218
- }, {
219
- maxAttempts: 5,
220
- backoffMs: [0],
221
- retryJitterRatio: 0,
222
- model: 'claude-opus-primary',
223
- fallbackModel: 'claude-sonnet-fallback',
224
- }), (err) => (
225
- err instanceof AnthropicFallbackTriggeredError
226
- && err.originalModel === 'claude-opus-primary'
227
- && err.fallbackModel === 'claude-sonnet-fallback'
228
- ));
229
- assert.equal(attempts, 3);
230
-
231
- attempts = 0;
232
- await assert.rejects(withRetry(async () => {
233
- attempts += 1;
234
- throw Object.assign(new Error('overloaded without fallback'), { status: 529 });
235
- }, {
236
- maxAttempts: 4,
237
- backoffMs: [0],
238
- retryJitterRatio: 0,
239
- model: 'claude-opus-primary',
240
- }), /without fallback/);
241
- assert.equal(attempts, 4);
242
- });
243
-
244
- function successfulAnthropicResponse(content = 'fallback-ok') {
245
- const bytes = new TextEncoder().encode([
246
- { type: 'message_start', message: { model: 'claude-sonnet-fallback', usage: { input_tokens: 1 } } },
247
- { type: 'content_block_start', index: 0, content_block: { type: 'text' } },
248
- { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: content } },
249
- { type: 'content_block_stop', index: 0 },
250
- { type: 'message_delta', delta: { stop_reason: 'end_turn' }, usage: { output_tokens: 1 } },
251
- { type: 'message_stop' },
252
- ].map((event) => `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`).join(''));
253
- let sent = false;
254
- return {
255
- ok: true,
256
- status: 200,
257
- headers: new Map(),
258
- body: { getReader: () => ({
259
- async read() {
260
- if (sent) return { done: true };
261
- sent = true;
262
- return { done: false, value: bytes };
263
- },
264
- async cancel() {},
265
- releaseLock() {},
266
- }) },
267
- };
268
- }
269
-
270
- function nonStreamingAnthropicMessage(content = 'replacement') {
271
- return {
272
- id: 'msg_fixture',
273
- model: 'claude-sonnet-4-6',
274
- content: [{ type: 'text', text: content }],
275
- stop_reason: 'end_turn',
276
- usage: { input_tokens: 2, output_tokens: 1 },
277
- };
278
- }
279
-
280
- test('session plumbing fail-closes stream replacement and preserves unique output', { concurrency: false }, async (t) => {
281
- const priorRetries = process.env.CLAUDE_CODE_MAX_RETRIES;
282
- process.env.CLAUDE_CODE_MAX_RETRIES = '0';
283
- try {
284
- process.env.MIXDOG_AGENT_TRACE_DISABLE = '1';
285
- const { initProviders, getProvider } = await import('../src/runtime/agent/orchestrator/providers/registry.mjs');
286
- await initProviders({ anthropic: { enabled: true, apiKey: 'fixture-key' } });
287
- const provider = getProvider('anthropic');
288
- const {
289
- abortSessionTurn,
290
- askSession,
291
- createSession,
292
- getSession,
293
- } = await import('../src/runtime/agent/orchestrator/session/manager.mjs');
294
-
295
- const makeDroppedResponse = () => {
296
- const partial = new TextEncoder().encode([
297
- { type: 'message_start', message: { model: 'claude-sonnet-4-6', usage: { input_tokens: 1 } } },
298
- { type: 'content_block_start', index: 0, content_block: { type: 'text' } },
299
- { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'partial' } },
300
- ].map((event) => `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`).join(''));
301
- let reads = 0;
302
- return {
303
- ok: true,
304
- status: 200,
305
- headers: new Map(),
306
- body: { getReader: () => ({
307
- async read() {
308
- if (reads++ === 0) return { done: false, value: partial };
309
- throw Object.assign(new Error('body stream terminated'), { code: 'ECONNRESET' });
310
- },
311
- async cancel() {},
312
- releaseLock() {},
313
- }) },
314
- };
315
- };
316
- const createTestSession = () => createSession({
317
- provider: 'anthropic',
318
- model: 'claude-sonnet-4-6',
319
- tools: [],
320
- cwd: process.cwd(),
321
- skipAgentRules: true,
322
- skipSkills: true,
323
- compaction: { auto: false },
324
- });
325
- const partialsInHistory = (session) => session.messages.filter((message) => (
326
- message.role === 'assistant' && message.content === 'partial'
327
- )).length;
328
- const runCase = async ({ reset, restartFails = false, cancelRestart = false }) => {
329
- const session = createTestSession();
330
- let calls = 0;
331
- let visible = '';
332
- let markRestartEntered;
333
- const restartEntered = new Promise((resolve) => { markRestartEntered = resolve; });
334
- provider.client = { messages: { create(params, requestOptions = {}) {
335
- calls += 1;
336
- if (params.stream === false) {
337
- if (restartFails) throw new Error('non-streaming restart failed');
338
- if (cancelRestart) {
339
- markRestartEntered();
340
- return new Promise((_resolve, reject) => {
341
- const signal = requestOptions.signal;
342
- const rejectFromAbort = () => reject(
343
- signal?.reason instanceof Error
344
- ? signal.reason
345
- : new Error('non-streaming restart canceled'),
346
- );
347
- if (signal?.aborted) rejectFromAbort();
348
- else signal?.addEventListener('abort', rejectFromAbort, { once: true });
349
- });
350
- }
351
- return Promise.resolve(nonStreamingAnthropicMessage('replacement'));
352
- }
353
- return { asResponse: async () => makeDroppedResponse() };
354
- } } };
355
- const options = {
356
- onTextDelta: (chunk) => { visible += chunk; },
357
- ...(reset ? {
358
- onTextReset: async ({ chars }) => reset({
359
- chars,
360
- getVisible: () => visible,
361
- setVisible: (value) => { visible = value; },
362
- }),
363
- } : {}),
364
- };
365
- const asking = askSession(
366
- session.id,
367
- 'fixture prompt',
368
- null,
369
- null,
370
- process.cwd(),
371
- null,
372
- options,
373
- );
374
- if (cancelRestart) {
375
- await restartEntered;
376
- assert.equal(abortSessionTurn(session.id, 'user-cancel'), true);
377
- }
378
- const outcome = await asking.then(
379
- (result) => ({ result }),
380
- (error) => ({ error }),
381
- );
382
- return {
383
- ...outcome,
384
- calls,
385
- visible,
386
- session: getSession(session.id),
387
- };
388
- };
389
-
390
- for (const [name, reset] of [
391
- ['absent acknowledgment', null],
392
- ['false acknowledgment', async () => false],
393
- ['throwing acknowledgment', async () => { throw new Error('reset failed'); }],
394
- ]) {
395
- await t.test(name, async () => {
396
- const outcome = await runCase({ reset });
397
- assert.match(outcome.error?.message || '', /body stream terminated/);
398
- assert.equal(outcome.calls, 1, 'must not start non-streaming request');
399
- assert.equal(outcome.visible, 'partial');
400
- assert.equal(partialsInHistory(outcome.session), 1);
401
- });
402
- }
403
-
404
- await t.test('late positive acknowledgment is awaited before one replacement', async () => {
405
- const outcome = await runCase({
406
- reset: async ({ chars, getVisible, setVisible }) => {
407
- await new Promise((resolve) => setTimeout(resolve, 15));
408
- setVisible(getVisible().slice(0, -chars));
409
- return true;
410
- },
411
- });
412
- assert.equal(outcome.error, undefined);
413
- assert.equal(outcome.calls, 2);
414
- assert.equal(outcome.visible, '');
415
- assert.equal(outcome.result.content, 'replacement');
416
- assert.equal(outcome.session.messages.filter((message) => (
417
- message.role === 'assistant' && message.content === 'replacement'
418
- )).length, 1);
419
- assert.equal(partialsInHistory(outcome.session), 0);
420
- });
421
-
422
- await t.test('failed restart restores one partial to interruption history', async () => {
423
- const outcome = await runCase({
424
- restartFails: true,
425
- reset: async ({ chars, getVisible, setVisible }) => {
426
- setVisible(getVisible().slice(0, -chars));
427
- return true;
428
- },
429
- });
430
- assert.match(outcome.error?.message || '', /non-streaming restart failed/);
431
- assert.equal(outcome.calls, 2);
432
- assert.equal(outcome.visible, '', 'acknowledged consumer keeps the tombstone');
433
- assert.equal(partialsInHistory(outcome.session), 1, 'history restores exactly one exposed partial');
434
- });
435
-
436
- await t.test('cancellation during restart restores the partial before finalization', async () => {
437
- const outcome = await runCase({
438
- cancelRestart: true,
439
- reset: async ({ chars, getVisible, setVisible }) => {
440
- setVisible(getVisible().slice(0, -chars));
441
- return true;
442
- },
443
- });
444
- assert.equal(outcome.error?.name, 'SessionClosedError');
445
- assert.equal(outcome.calls, 2);
446
- assert.equal(outcome.visible, '', 'acknowledged consumer keeps the tombstone');
447
- assert.equal(partialsInHistory(outcome.session), 1, 'cancel history preserves exactly one partial');
448
- });
449
- } finally {
450
- if (priorRetries == null) delete process.env.CLAUDE_CODE_MAX_RETRIES;
451
- else process.env.CLAUDE_CODE_MAX_RETRIES = priorRetries;
452
- }
453
- });
454
-
455
- test('OAuth and API-key providers switch to the opt-in fallback after three 529s', async () => {
456
- const priorRetries = process.env.CLAUDE_CODE_MAX_RETRIES;
457
- process.env.CLAUDE_CODE_MAX_RETRIES = '2';
458
- try {
459
- const oauth = Object.create(AnthropicOAuthProvider.prototype);
460
- oauth.credentials = { accessToken: 'fixture', expiresAt: Date.now() + 60_000 };
461
- oauth.config = {};
462
- oauth.fastModeBetaHeaderLatched = false;
463
- oauth.ensureAuth = async () => oauth.credentials;
464
- const oauthModels = [];
465
- const oauthSend = AnthropicOAuthProvider.prototype.send;
466
- oauth.send = function (messages, model, tools, opts) {
467
- oauthModels.push(model);
468
- return oauthSend.call(this, messages, model, tools, opts);
469
- };
470
- let oauthAttempts = 0;
471
- const oauthResult = await oauth.send([], 'claude-opus-primary', [], {
472
- fallbackModel: 'claude-sonnet-fallback',
473
- _doRequestFn: async () => {
474
- oauthAttempts += 1;
475
- const ok = oauthAttempts > 3;
476
- return {
477
- response: {
478
- status: ok ? 200 : 529,
479
- ok,
480
- headers: new Map([['retry-after', '0']]),
481
- async text() { return ''; },
482
- },
483
- controller: { abort() {} },
484
- cancelHandler: null,
485
- };
486
- },
487
- _parseSSEFn: async () => ({
488
- content: 'oauth-fallback-ok',
489
- model: 'claude-sonnet-fallback',
490
- usage: {},
491
- }),
492
- });
493
- assert.equal(oauthResult.content, 'oauth-fallback-ok');
494
- assert.equal(oauthAttempts, 4);
495
- assert.deepEqual(oauthModels, ['claude-opus-primary', 'claude-sonnet-fallback']);
496
-
497
- const direct = Object.create(AnthropicProvider.prototype);
498
- direct.name = 'anthropic';
499
- direct.config = {};
500
- direct.fastModeBetaHeaderLatched = false;
501
- const directModels = [];
502
- const directDoSend = AnthropicProvider.prototype._doSend;
503
- direct._doSend = function (messages, model, tools, opts) {
504
- directModels.push(model);
505
- return directDoSend.call(this, messages, model, tools, opts);
506
- };
507
- let directAttempts = 0;
508
- direct.client = { messages: { create() {
509
- directAttempts += 1;
510
- return { async asResponse() {
511
- if (directAttempts <= 3) {
512
- return { ok: false, status: 529, headers: new Map([['retry-after', '0']]), async text() { return ''; } };
513
- }
514
- return successfulAnthropicResponse('direct-fallback-ok');
515
- } };
516
- } } };
517
- const directResult = await direct._doSend([], 'claude-opus-primary', [], {
518
- fallbackModel: 'claude-sonnet-fallback',
519
- });
520
- assert.equal(directResult.content, 'direct-fallback-ok');
521
- assert.equal(directAttempts, 4);
522
- assert.deepEqual(directModels, ['claude-opus-primary', 'claude-sonnet-fallback']);
523
- } finally {
524
- if (priorRetries == null) delete process.env.CLAUDE_CODE_MAX_RETRIES;
525
- else process.env.CLAUDE_CODE_MAX_RETRIES = priorRetries;
526
- }
527
- });
528
-
529
- test('Anthropic retry budget defaults to ten retries and is configurable/bounded', () => {
530
- const prior = process.env.CLAUDE_CODE_MAX_RETRIES;
531
- try {
532
- delete process.env.CLAUDE_CODE_MAX_RETRIES;
533
- assert.equal(anthropicMaxAttempts(), 11);
534
- process.env.CLAUDE_CODE_MAX_RETRIES = '2';
535
- assert.equal(anthropicMaxAttempts(), 3);
536
- process.env.CLAUDE_CODE_MAX_RETRIES = '9999';
537
- assert.equal(anthropicMaxAttempts(), 101);
538
- } finally {
539
- if (prior == null) delete process.env.CLAUDE_CODE_MAX_RETRIES;
540
- else process.env.CLAUDE_CODE_MAX_RETRIES = prior;
541
- }
542
- });
543
-
544
- test('Anthropic request timeout and exponential backoff match Claude Code defaults', async () => {
545
- const priorTimeout = process.env.API_TIMEOUT_MS;
546
- const priorRandom = Math.random;
547
- try {
548
- delete process.env.API_TIMEOUT_MS;
549
- assert.equal(anthropicRequestTimeoutMs(), 600_000);
550
- process.env.API_TIMEOUT_MS = '123456';
551
- assert.equal(anthropicRequestTimeoutMs(), 123456);
552
-
553
- Math.random = () => 0;
554
- const waits = [];
555
- let attempts = 0;
556
- await assert.rejects(withRetry(async () => {
557
- attempts += 1;
558
- throw Object.assign(new Error('overloaded'), { status: 529 });
559
- }, {
560
- maxAttempts: 4,
561
- backoffMs: ANTHROPIC_RETRY_BACKOFF_MS,
562
- retryJitterRatio: ANTHROPIC_RETRY_JITTER_RATIO,
563
- retryJitterMode: 'positive',
564
- sleepFn: async (ms) => { waits.push(ms); },
565
- }), /overloaded/);
566
- assert.equal(attempts, 4);
567
- assert.deepEqual(waits, [500, 1000, 2000]);
568
- } finally {
569
- Math.random = priorRandom;
570
- if (priorTimeout == null) delete process.env.API_TIMEOUT_MS;
571
- else process.env.API_TIMEOUT_MS = priorTimeout;
572
- }
573
- });
574
-
575
- test('x-should-retry false vetoes retry before status defaults', async () => {
576
- for (const status of [429, 529]) {
577
- let attempts = 0;
578
- const denied = Object.assign(new Error(`denied ${status}`), {
579
- status,
580
- headers: new Map([['x-should-retry', 'false'], ['retry-after', '0']]),
581
- });
582
- await assert.rejects(withRetry(async () => {
583
- attempts += 1;
584
- throw denied;
585
- }, {
586
- maxAttempts: 3,
587
- backoffMs: [0],
588
- retryJitterRatio: 0,
589
- }), (err) => err === denied);
590
- assert.equal(attempts, 1);
591
- }
592
- });
593
-
594
- test('Anthropic x-should-retry true overrides a normally terminal status only for Anthropic', async () => {
595
- for (const provider of ['anthropic', 'gemini']) {
596
- let attempts = 0;
597
- const result = await withRetry(async () => {
598
- attempts += 1;
599
- if (attempts === 1) {
600
- throw Object.assign(new Error('bad request with server override'), {
601
- status: 400,
602
- headers: new Map([['x-should-retry', 'true']]),
603
- });
604
- }
605
- return 'recovered';
606
- }, {
607
- provider,
608
- maxAttempts: 2,
609
- backoffMs: [0],
610
- retryJitterRatio: 0,
611
- }).then((value) => value, (error) => error);
612
- if (provider === 'anthropic') {
613
- assert.equal(result, 'recovered');
614
- assert.equal(attempts, 2);
615
- } else {
616
- assert.equal(result?.status, 400);
617
- assert.equal(attempts, 1);
618
- }
619
- }
620
- });
621
-
622
- test('Retry-After is not capped or jittered and remains abortable', async () => {
623
- const ac = new AbortController();
624
- let observed;
625
- let calls = 0;
626
- await assert.rejects(withRetry(async () => {
627
- calls++;
628
- const err = Object.assign(new Error('busy'), {
629
- status: 529,
630
- headers: new Map([['retry-after-ms', '123456']]),
631
- });
632
- throw err;
633
- }, {
634
- maxAttempts: 2,
635
- signal: ac.signal,
636
- maxRetryAfterMs: 1,
637
- retryJitterRatio: 1,
638
- onRetry: ({ delayMs }) => {
639
- observed = delayMs;
640
- ac.abort(new Error('test stop'));
641
- },
642
- }), /test stop/);
643
- assert.equal(calls, 1);
644
- assert.equal(observed, 123456);
645
- });
646
-
647
- test('oversized HTTP Retry-After is chunked without Node timeout clamping', async () => {
648
- const chunks = [];
649
- let attempts = 0;
650
- const result = await withRetry(async () => {
651
- attempts += 1;
652
- if (attempts === 1) {
653
- throw Object.assign(new Error('busy'), {
654
- status: 429,
655
- headers: new Map([['retry-after-ms', '2147483648']]),
656
- });
657
- }
658
- return 'ok';
659
- }, {
660
- maxAttempts: 2,
661
- retryJitterRatio: 1,
662
- sleepFn: async (ms) => { chunks.push(ms); },
663
- });
664
- assert.equal(result, 'ok');
665
- assert.deepEqual(chunks, [MAX_SAFE_TIMEOUT_MS, 1]);
666
- });
667
-
668
- test('oversized Anthropic SSE Retry-After sleep uses the same safe chunks', async () => {
669
- const chunks = [];
670
- await _midstreamSleepWithAbort(
671
- 2_147_483_648,
672
- null,
673
- async (ms) => { chunks.push(ms); },
674
- );
675
- assert.deepEqual(chunks, [MAX_SAFE_TIMEOUT_MS, 1]);
676
- });
677
-
678
- test('SSE retries 429/5xx before output regardless of message_start, but not partial output', () => {
679
- const policy = { mode: 'sse', defaultRetries: 3, perClassifierGate: false };
680
- const clean = { attemptIndex: 0, sawCompleted: false, sawMessageStart: false };
681
- assert.equal(classifyMidstreamError({ status: 429 }, clean, policy), 'http_429');
682
- assert.equal(classifyMidstreamError({ status: 529 }, { ...clean, sawMessageStart: true }, policy), 'http_529');
683
- for (const exposed of ['emittedText', 'emittedToolCall', 'partialToolCall', 'emittedThinking']) {
684
- assert.equal(classifyMidstreamError({ status: 529 }, { ...clean, [exposed]: true }, policy), null);
685
- }
686
- assert.equal(MIDSTREAM_RETRY_POLICY.sse.defaultRetries, 3);
687
- });
688
-
689
- test('successful SSE parsing cancels and releases the reader', async () => {
690
- const encoded = new TextEncoder().encode(
691
- 'event: message_start\ndata: {"type":"message_start","message":{"model":"claude","usage":{}}}\n\n'
692
- + 'event: message_stop\ndata: {"type":"message_stop"}\n\n',
693
- );
694
- let reads = 0;
695
- let cancels = 0;
696
- let releases = 0;
697
- const response = { body: { getReader: () => ({
698
- read: async () => reads++ === 0 ? { done: false, value: encoded } : { done: true },
699
- cancel: async () => { cancels++; },
700
- releaseLock: () => { releases++; },
701
- }) } };
702
- const state = {};
703
- await parseSSEStream(response, null, () => {}, null, null, state);
704
- assert.equal(state.sawCompleted, true);
705
- assert.equal(cancels, 1);
706
- assert.equal(releases, 1);
707
- });
708
-
709
- test('pre-aborted SSE still cancels and releases its acquired reader', async () => {
710
- const controller = new AbortController();
711
- const reason = new Error('already canceled');
712
- controller.abort(reason);
713
- let cancels = 0;
714
- let releases = 0;
715
- const response = { body: { getReader: () => ({
716
- read: async () => ({ done: true }),
717
- cancel: async () => { cancels += 1; },
718
- releaseLock: () => { releases += 1; },
719
- }) } };
720
- await assert.rejects(
721
- parseSSEStream(response, controller.signal, () => {}, null, null, {}),
722
- (err) => err === reason,
723
- );
724
- assert.equal(cancels, 1);
725
- assert.equal(releases, 1);
726
- });