mixdog 0.9.62 → 0.9.64

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 (403) 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/update-checker.mjs +1 -1
  187. package/src/runtime/shared/user-cwd.mjs +3 -3
  188. package/src/runtime/shared/user-data-guard.mjs +4 -4
  189. package/src/runtime/shared/workspace-router.mjs +5 -5
  190. package/src/session-runtime/config-helpers.mjs +2 -3
  191. package/src/session-runtime/config-lifecycle.mjs +1 -1
  192. package/src/session-runtime/hitch-profile.mjs +1 -1
  193. package/src/session-runtime/lifecycle-api.mjs +6 -4
  194. package/src/session-runtime/model-recency.mjs +5 -5
  195. package/src/session-runtime/plugin-mcp.mjs +1 -1
  196. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  197. package/src/session-runtime/session-text.mjs +4 -4
  198. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  199. package/src/session-runtime/tool-catalog.mjs +6 -483
  200. package/src/session-runtime/tool-defs.mjs +1 -1
  201. package/src/standalone/agent-task-status.mjs +1 -1
  202. package/src/standalone/agent-tool/helpers.mjs +2 -2
  203. package/src/standalone/channel-admin.mjs +4 -4
  204. package/src/standalone/explore-tool.mjs +3 -3
  205. package/src/standalone/hook-bus/config.mjs +1 -1
  206. package/src/standalone/hook-bus/constants.mjs +1 -1
  207. package/src/standalone/hook-bus/rules.mjs +1 -1
  208. package/src/standalone/projects.mjs +1 -1
  209. package/src/standalone/provider-admin.mjs +2 -2
  210. package/src/tui/app/app-format.mjs +2 -2
  211. package/src/tui/app/clipboard.mjs +3 -3
  212. package/src/tui/app/input-parsers.mjs +1 -1
  213. package/src/tui/app/text-layout.mjs +1 -1
  214. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  215. package/src/tui/app/transcript-window.mjs +5 -347
  216. package/src/tui/app/use-mouse-input.mjs +1 -1
  217. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  218. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  219. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  220. package/src/tui/components/tool-output-format.mjs +7 -7
  221. package/src/tui/display-width.mjs +4 -4
  222. package/src/tui/dist/index.mjs +63 -97
  223. package/src/tui/engine/agent-envelope.mjs +5 -5
  224. package/src/tui/engine/notification-plan.mjs +1 -1
  225. package/src/tui/engine/queue-helpers.mjs +3 -3
  226. package/src/tui/engine/render-timing.mjs +4 -5
  227. package/src/tui/engine/session-api-ext.mjs +3 -3
  228. package/src/tui/engine/tool-result-status.mjs +5 -5
  229. package/src/tui/engine/tool-result-text.mjs +1 -1
  230. package/src/tui/engine/transcript-spill.mjs +585 -0
  231. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  232. package/src/tui/engine.mjs +2 -474
  233. package/src/tui/lib/voice-setup.mjs +1 -1
  234. package/src/tui/markdown/format-token.mjs +1 -1
  235. package/src/tui/markdown/render-ansi.mjs +1 -1
  236. package/src/tui/markdown/table-layout.mjs +4 -4
  237. package/src/tui/paste-attachments.mjs +1 -1
  238. package/src/tui/prompt-history-store.mjs +3 -3
  239. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  240. package/src/tui/theme.mjs +1 -1
  241. package/src/tui/transcript-tool-failures.mjs +2 -2
  242. package/src/ui/ansi.mjs +0 -9
  243. package/src/ui/statusline-format.mjs +2 -4
  244. package/scripts/_bench-cwc.json +0 -20
  245. package/scripts/_jitter-fuzz.jsx +0 -42
  246. package/scripts/_jitter-fuzz2.jsx +0 -30
  247. package/scripts/_jitter-probe.jsx +0 -35
  248. package/scripts/_jp2.jsx +0 -16
  249. package/scripts/_smoke_wd.mjs +0 -7
  250. package/scripts/abort-recovery-test.mjs +0 -175
  251. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  252. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  253. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  254. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  255. package/scripts/agent-loop-policy-test.mjs +0 -37
  256. package/scripts/agent-model-liveness-test.mjs +0 -763
  257. package/scripts/agent-parallel-smoke.mjs +0 -435
  258. package/scripts/agent-route-batch-test.mjs +0 -40
  259. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  260. package/scripts/agent-terminal-reap-test.mjs +0 -252
  261. package/scripts/agent-trace-io-test.mjs +0 -133
  262. package/scripts/ansi-color-capability-test.mjs +0 -90
  263. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  264. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  265. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  266. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  267. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  268. package/scripts/arg-guard-test.mjs +0 -93
  269. package/scripts/async-notify-settlement-test.mjs +0 -99
  270. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  271. package/scripts/background-task-meta-smoke.mjs +0 -38
  272. package/scripts/bench-run.mjs +0 -508
  273. package/scripts/boot-smoke.mjs +0 -137
  274. package/scripts/build-runtime-windows.ps1 +0 -242
  275. package/scripts/channel-daemon-smoke.mjs +0 -1103
  276. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  277. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  278. package/scripts/code-graph-dispatch-test.mjs +0 -96
  279. package/scripts/code-graph-root-federation-test.mjs +0 -273
  280. package/scripts/compact-active-turn-test.mjs +0 -68
  281. package/scripts/compact-file-reattach-test.mjs +0 -88
  282. package/scripts/compact-pressure-test.mjs +0 -788
  283. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  284. package/scripts/compact-recall-digest-test.mjs +0 -57
  285. package/scripts/compact-smoke.mjs +0 -1017
  286. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  287. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  288. package/scripts/context-mcp-metering-test.mjs +0 -1406
  289. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  290. package/scripts/deferred-tool-loading-test.mjs +0 -250
  291. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  292. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  293. package/scripts/embedding-worker-exit-test.mjs +0 -76
  294. package/scripts/execution-completion-dedup-test.mjs +0 -205
  295. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  296. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  297. package/scripts/explore-bench-tmp.mjs +0 -36
  298. package/scripts/explore-bench.mjs +0 -248
  299. package/scripts/explore-prompt-policy-test.mjs +0 -256
  300. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  301. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  302. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  303. package/scripts/freevar-smoke.mjs +0 -98
  304. package/scripts/gemini-provider-test.mjs +0 -1448
  305. package/scripts/generate-oc-icons.mjs +0 -11
  306. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  307. package/scripts/headless-pristine-execution-test.mjs +0 -614
  308. package/scripts/hook-bus-test.mjs +0 -552
  309. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  310. package/scripts/internal-comms-bench-test.mjs +0 -226
  311. package/scripts/internal-comms-bench.mjs +0 -853
  312. package/scripts/internal-comms-smoke.mjs +0 -277
  313. package/scripts/internal-tools-normalization-test.mjs +0 -62
  314. package/scripts/interrupted-turn-history-test.mjs +0 -399
  315. package/scripts/lead-workflow-smoke.mjs +0 -592
  316. package/scripts/lifecycle-api-test.mjs +0 -137
  317. package/scripts/live-share-test.mjs +0 -148
  318. package/scripts/live-worker-smoke.mjs +0 -333
  319. package/scripts/log-writer-guard-smoke.mjs +0 -131
  320. package/scripts/maintenance-default-routes-test.mjs +0 -164
  321. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  322. package/scripts/max-output-recovery-test.mjs +0 -368
  323. package/scripts/mcp-client-normalization-test.mjs +0 -45
  324. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  325. package/scripts/memory-core-input-test.mjs +0 -167
  326. package/scripts/memory-cycle-routing-test.mjs +0 -111
  327. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  328. package/scripts/memory-pg-recovery-test.mjs +0 -59
  329. package/scripts/memory-rule-contract-test.mjs +0 -93
  330. package/scripts/model-list-sanitize-test.mjs +0 -37
  331. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  332. package/scripts/native-edit-wire-test.mjs +0 -152
  333. package/scripts/notify-completion-mirror-test.mjs +0 -73
  334. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  335. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  336. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  337. package/scripts/output-style-bench.mjs +0 -292
  338. package/scripts/output-style-smoke.mjs +0 -145
  339. package/scripts/parent-abort-link-test.mjs +0 -90
  340. package/scripts/patch-binary-cache-test.mjs +0 -181
  341. package/scripts/path-suffix-test.mjs +0 -57
  342. package/scripts/pending-completion-drop-test.mjs +0 -239
  343. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  344. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  345. package/scripts/process-lifecycle-test.mjs +0 -461
  346. package/scripts/prompt-immediate-render-test.mjs +0 -89
  347. package/scripts/prompt-input-parity-test.mjs +0 -145
  348. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  349. package/scripts/provider-contract-test.mjs +0 -632
  350. package/scripts/provider-stream-stall-test.mjs +0 -276
  351. package/scripts/provider-toolcall-test.mjs +0 -3063
  352. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  353. package/scripts/recall-bench.mjs +0 -359
  354. package/scripts/resource-admission-test.mjs +0 -792
  355. package/scripts/result-classification-test.mjs +0 -75
  356. package/scripts/rg-runner-test.mjs +0 -240
  357. package/scripts/routing-corpus-test.mjs +0 -349
  358. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  359. package/scripts/session-bench-cache-break-test.mjs +0 -102
  360. package/scripts/session-bench.mjs +0 -1708
  361. package/scripts/session-context-bench.mjs +0 -344
  362. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  363. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  364. package/scripts/session-ingest-smoke.mjs +0 -177
  365. package/scripts/session-orphan-sweep-test.mjs +0 -109
  366. package/scripts/set-effort-config-test.mjs +0 -41
  367. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  368. package/scripts/shell-hardening-test.mjs +0 -209
  369. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  370. package/scripts/ship-mode-test.mjs +0 -98
  371. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  372. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  373. package/scripts/smoke-loop-report.mjs +0 -221
  374. package/scripts/smoke-loop.mjs +0 -202
  375. package/scripts/smoke-runtime-negative.ps1 +0 -106
  376. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  377. package/scripts/steering-drain-buckets-test.mjs +0 -467
  378. package/scripts/steering-fold-provenance-test.mjs +0 -71
  379. package/scripts/streaming-tail-window-test.mjs +0 -175
  380. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  381. package/scripts/task-bench.mjs +0 -205
  382. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  383. package/scripts/termio-input-smoke.mjs +0 -199
  384. package/scripts/tool-result-hook-test.mjs +0 -48
  385. package/scripts/tool-smoke.mjs +0 -2453
  386. package/scripts/tool-tui-presentation-test.mjs +0 -286
  387. package/scripts/toolcall-args-test.mjs +0 -239
  388. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  389. package/scripts/tui-background-failure-smoke.mjs +0 -73
  390. package/scripts/tui-perf-run.ps1 +0 -26
  391. package/scripts/tui-render-smoke.mjs +0 -90
  392. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  393. package/scripts/tui-runtime-load-bench.mjs +0 -50
  394. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  395. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  396. package/scripts/tui-transcript-perf-test.mjs +0 -934
  397. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  398. package/scripts/verify-release-assets-test.mjs +0 -281
  399. package/scripts/web-fetch-routing-test.mjs +0 -158
  400. package/scripts/webhook-smoke.mjs +0 -201
  401. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  402. package/scripts/worker-notify-rejection-test.mjs +0 -51
  403. package/scripts/write-backpressure-test.mjs +0 -147
@@ -1,792 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
- import { EventEmitter } from 'node:events';
4
- import {
5
- RESOURCE_ADMISSION_DEFAULTS,
6
- ResourceAdmissionController,
7
- } from '../src/runtime/shared/resource-admission.mjs';
8
- import { execShellCommand } from '../src/runtime/agent/orchestrator/tools/shell-command.mjs';
9
- import { executeBashTool } from '../src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs';
10
- import {
11
- attachShellJobResourceLease,
12
- killShellJob,
13
- startBackgroundShellJob,
14
- waitForShellJob,
15
- } from '../src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs';
16
- import { trackProcessTreeQuiescence } from '../src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs';
17
- import { makeAgentDispatch } from '../src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs';
18
-
19
- const healthy = () => ({
20
- rssBytes: 100 * 1024 * 1024,
21
- freeMemoryBytes: 8 * 1024 * 1024 * 1024,
22
- });
23
-
24
- function fakeChild({ pid = 424242, event = 'spawn', killMode = 'exit' } = {}) {
25
- const child = new EventEmitter();
26
- child.pid = pid;
27
- child.exitCode = null;
28
- child.signalCode = null;
29
- child.killCalls = 0;
30
- child.kill = () => {
31
- child.killCalls += 1;
32
- if (killMode === 'error-exit') {
33
- queueMicrotask(() => child.emit('error', new Error('kill-time child error')));
34
- }
35
- if (killMode === 'none') return;
36
- queueMicrotask(() => {
37
- child.signalCode = 'SIGKILL';
38
- child.emit('exit', null, 'SIGKILL');
39
- child.emit('close', null, 'SIGKILL');
40
- });
41
- };
42
- queueMicrotask(() => {
43
- if (event === 'error') child.emit('error', Object.assign(new Error('async spawn denied'), { code: 'EACCES' }));
44
- else child.emit('spawn');
45
- });
46
- return child;
47
- }
48
-
49
- const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
50
- async function waitUntil(predicate, timeoutMs = 5000) {
51
- const deadline = Date.now() + timeoutMs;
52
- while (!predicate()) {
53
- if (Date.now() >= deadline) assert.fail(`condition not met within ${timeoutMs}ms`);
54
- await delay(25);
55
- }
56
- }
57
-
58
- test('safe defaults bound agents, shells, combined work, queue, and memory', () => {
59
- assert.deepEqual(RESOURCE_ADMISSION_DEFAULTS, {
60
- maxAgents: 4,
61
- maxShells: 4,
62
- maxHighLoad: 6,
63
- maxQueue: 32,
64
- minFreeMemoryMb: 1024,
65
- maxRssMb: 3072,
66
- });
67
- });
68
-
69
- test('combined and per-kind limits queue without revoking in-flight work', async () => {
70
- let now = 10;
71
- const admission = new ResourceAdmissionController({
72
- limits: { maxAgents: 2, maxShells: 2, maxHighLoad: 2, maxQueue: 3 },
73
- metrics: healthy,
74
- now: () => now,
75
- });
76
- const agent = await admission.acquire('agent');
77
- const shell = await admission.acquire('shell');
78
- let thirdStarted = false;
79
- const third = admission.acquire('agent').then((lease) => {
80
- thirdStarted = true;
81
- return lease;
82
- });
83
- await new Promise((resolve) => setImmediate(resolve));
84
- assert.equal(thirdStarted, false);
85
- assert.deepEqual(admission.snapshot().active, { agent: 1, shell: 1 });
86
- now = 25;
87
- shell.release();
88
- const queuedLease = await third;
89
- assert.equal(queuedLease.queuedMs, 15);
90
- assert.deepEqual(admission.snapshot().active, { agent: 2, shell: 0 });
91
- agent.release();
92
- queuedLease.release();
93
- });
94
-
95
- test('saturated parent agents yield their permits while awaited nested work progresses', async () => {
96
- const admission = new ResourceAdmissionController({
97
- limits: { maxAgents: 2, maxShells: 2, maxHighLoad: 2, maxQueue: 4 },
98
- metrics: healthy,
99
- });
100
- const parents = await Promise.all([
101
- admission.acquire('agent'),
102
- admission.acquire('agent'),
103
- ]);
104
- const gates = [Promise.withResolvers(), Promise.withResolvers()];
105
- let nestedStarted = 0;
106
- const nested = parents.map((parent, index) => admission.runWithLease(parent, async () => {
107
- const child = await admission.acquire('agent');
108
- nestedStarted += 1;
109
- await gates[index].promise;
110
- await child.release();
111
- }));
112
- await new Promise((resolve) => setImmediate(resolve));
113
- assert.equal(nestedStarted, 2);
114
- assert.deepEqual(admission.snapshot().active, { agent: 2, shell: 0 });
115
- gates.forEach((gate) => gate.resolve());
116
- await Promise.all(nested);
117
- assert.deepEqual(admission.snapshot().active, { agent: 2, shell: 0 });
118
- parents.forEach((lease) => lease.release());
119
- });
120
-
121
- test('yielded parent waits for bounded re-admission after a sibling steals its slot', async () => {
122
- const admission = new ResourceAdmissionController({
123
- limits: { maxAgents: 2, maxHighLoad: 2, maxQueue: 4 },
124
- metrics: healthy,
125
- });
126
- const parent = await admission.acquire('agent');
127
- const blocker = await admission.acquire('agent');
128
- let parentContinued = false;
129
- const childReady = Promise.withResolvers();
130
- const releaseChild = Promise.withResolvers();
131
- const dependency = admission.runWithLease(parent, async () => {
132
- const child = await admission.acquire('agent');
133
- childReady.resolve();
134
- await releaseChild.promise;
135
- await child.release();
136
- }).then(() => { parentContinued = true; });
137
- await childReady.promise;
138
- const sibling = admission.acquire('agent');
139
- releaseChild.resolve();
140
- const siblingLease = await sibling;
141
- await new Promise((resolve) => setImmediate(resolve));
142
- assert.equal(parentContinued, false);
143
- await siblingLease.release();
144
- await dependency;
145
- assert.equal(parentContinued, true);
146
- assert.ok(admission.snapshot().active.agent <= 2);
147
- await blocker.release();
148
- await parent.release();
149
- });
150
-
151
- test('cancellation rejects a queued parent restoration without over-counting', async () => {
152
- const admission = new ResourceAdmissionController({
153
- limits: { maxAgents: 2, maxHighLoad: 2, maxQueue: 4 },
154
- metrics: healthy,
155
- });
156
- const controller = new AbortController();
157
- const parent = await admission.acquire('agent', { signal: controller.signal });
158
- const blocker = await admission.acquire('agent');
159
- const childReady = Promise.withResolvers();
160
- const releaseChild = Promise.withResolvers();
161
- const outcome = admission.runWithLease(parent, async () => {
162
- const child = await admission.acquire('agent');
163
- childReady.resolve();
164
- await releaseChild.promise;
165
- await child.release();
166
- });
167
- await childReady.promise;
168
- const sibling = admission.acquire('agent');
169
- releaseChild.resolve();
170
- const siblingLease = await sibling;
171
- controller.abort(new Error('parent restoration cancelled'));
172
- await assert.rejects(outcome, /parent restoration cancelled/);
173
- await siblingLease.release();
174
- await blocker.release();
175
- await parent.release();
176
- assert.ok(admission.snapshot().active.agent <= 2);
177
- });
178
-
179
- test('detached nested lifetime keeps parent admitted or refuses without capacity', async () => {
180
- const admission = new ResourceAdmissionController({
181
- limits: { maxAgents: 2, maxShells: 1, maxHighLoad: 2 },
182
- metrics: healthy,
183
- });
184
- const parent = await admission.acquire('agent');
185
- await admission.runWithLease(parent, async () => {
186
- const detached = await admission.acquire('shell', { dependency: 'detached' });
187
- assert.deepEqual(admission.snapshot().active, { agent: 1, shell: 1 });
188
- await detached.release();
189
- });
190
- const blocker = await admission.acquire('agent');
191
- await admission.runWithLease(parent, async () => {
192
- await assert.rejects(
193
- admission.acquire('shell', { dependency: 'detached' }),
194
- (error) => error?.code === 'ERESOURCEDEPENDENCY',
195
- );
196
- assert.equal(parent.counted, true);
197
- });
198
- await blocker.release();
199
- await parent.release();
200
- });
201
-
202
- test('memory pressure rejects only new and queued work with actionable errors', async () => {
203
- let sample = healthy();
204
- const admission = new ResourceAdmissionController({
205
- limits: { maxAgents: 1, maxHighLoad: 1, maxQueue: 2, minFreeMemoryMb: 1024, maxRssMb: 512 },
206
- metrics: () => sample,
207
- });
208
- const running = await admission.acquire('agent');
209
- const queued = admission.acquire('agent');
210
- sample = { rssBytes: 600 * 1024 * 1024, freeMemoryBytes: 8 * 1024 * 1024 * 1024 };
211
- running.release();
212
- await assert.rejects(queued, (error) => (
213
- error?.code === 'ERESOURCEPRESSURE' && /RSS 600 MB reached 512 MB limit/.test(error.message)
214
- ));
215
- assert.deepEqual(admission.snapshot().active, { agent: 0, shell: 0 });
216
- await assert.rejects(admission.acquire('shell'), /resource pressure: Mixdog RSS/);
217
- });
218
-
219
- test('queued cancellation removes work without affecting the running lease', async () => {
220
- const admission = new ResourceAdmissionController({
221
- limits: { maxAgents: 1, maxHighLoad: 1, maxQueue: 2 },
222
- metrics: healthy,
223
- });
224
- const running = await admission.acquire('agent');
225
- const controller = new AbortController();
226
- const queued = admission.acquire('agent', { signal: controller.signal });
227
- controller.abort(new Error('caller cancelled'));
228
- await assert.rejects(queued, /caller cancelled/);
229
- assert.equal(admission.snapshot().active.agent, 1);
230
- assert.equal(admission.snapshot().queued, 0);
231
- running.release();
232
- });
233
-
234
- test('shell refusal is a normal tool result and starts no child process', async () => {
235
- let acquireCalls = 0;
236
- const refusal = Object.assign(new Error('resource pressure: test limit reached'), {
237
- code: 'ERESOURCEPRESSURE',
238
- });
239
- const result = await execShellCommand({
240
- shell: process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
241
- shellArg: process.platform === 'win32' ? '/c' : '-c',
242
- command: 'echo must-not-run',
243
- env: process.env,
244
- cwd: process.cwd(),
245
- timeoutMs: 1000,
246
- admission: {
247
- acquire() {
248
- acquireCalls += 1;
249
- return Promise.reject(refusal);
250
- },
251
- },
252
- });
253
- assert.equal(acquireCalls, 1);
254
- assert.equal(result.failureReason, 'resource pressure');
255
- assert.equal(result.exitCode, 1);
256
- assert.match(result.stderr, /resource pressure: test limit reached/);
257
- });
258
-
259
- test('real shell lifetime holds and releases one lease', async () => {
260
- const admission = new ResourceAdmissionController({
261
- limits: { maxShells: 1, maxHighLoad: 1 },
262
- metrics: healthy,
263
- });
264
- const isWindows = process.platform === 'win32';
265
- const result = await execShellCommand({
266
- shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
267
- shellArg: isWindows ? '/c' : '-c',
268
- command: 'echo admitted',
269
- env: process.env,
270
- cwd: process.cwd(),
271
- timeoutMs: 5000,
272
- admission,
273
- });
274
- assert.equal(result.exitCode, 0);
275
- assert.deepEqual(admission.snapshot().active, { agent: 0, shell: 0 });
276
- });
277
-
278
- test('process-tree quiescence tracker retains ownership and confirms exactly once', async () => {
279
- let alive = true;
280
- let confirmed = 0;
281
- const tracker = trackProcessTreeQuiescence(123, () => { confirmed += 1; }, {
282
- pollMs: 5,
283
- probe: () => alive,
284
- });
285
- assert.equal(tracker.pending, true);
286
- await delay(15);
287
- assert.equal(confirmed, 0);
288
- alive = false;
289
- await waitUntil(() => confirmed === 1);
290
- await delay(15);
291
- assert.equal(confirmed, 1);
292
- assert.equal(tracker.pending, false);
293
- });
294
-
295
- test('Windows tree tracking seeds ownership before an absent root snapshot', async () => {
296
- let rows = [
297
- { pid: 124, parentPid: 123, identity: 'child-a' },
298
- { pid: 125, parentPid: 124, identity: 'grandchild-a' },
299
- ];
300
- let confirmed = 0;
301
- const tracker = trackProcessTreeQuiescence(123, () => { confirmed += 1; }, {
302
- platform: 'win32',
303
- pollMs: 5,
304
- waitForRootExit: true,
305
- windowsSnapshot: () => rows,
306
- });
307
- tracker.rootExited();
308
- await delay(20);
309
- assert.equal(confirmed, 0);
310
- rows = [];
311
- await waitUntil(() => confirmed === 1);
312
- await delay(15);
313
- assert.equal(confirmed, 1);
314
- });
315
-
316
- test('delayed asynchronous Windows snapshots keep timers moving and coalesce trackers onto one CIM request', async () => {
317
- let cimRequests = 0;
318
- let timerProgressed = false;
319
- const spawnFn = (_file, args) => {
320
- cimRequests += 1;
321
- assert.match(args.at(-1), /Get-CimInstance Win32_Process/);
322
- const child = new EventEmitter();
323
- child.stdout = new EventEmitter();
324
- child.stdout.setEncoding = () => {};
325
- child.kill = () => {};
326
- setTimeout(() => {
327
- child.stdout.emit('data', '123\t1\troot-a\n');
328
- child.emit('close', 0);
329
- }, 40);
330
- return child;
331
- };
332
- const first = trackProcessTreeQuiescence(123, () => assert.fail('root is present'), {
333
- platform: 'win32',
334
- pollMs: 5,
335
- windowsSnapshotSpawn: spawnFn,
336
- waitForRootExit: true,
337
- });
338
- const second = trackProcessTreeQuiescence(123, () => assert.fail('root is present'), {
339
- platform: 'win32',
340
- pollMs: 5,
341
- windowsSnapshotSpawn: spawnFn,
342
- waitForRootExit: true,
343
- });
344
- first.rootExited();
345
- second.rootExited();
346
- setTimeout(() => { timerProgressed = true; }, 5);
347
- await delay(20);
348
- assert.equal(timerProgressed, true);
349
- assert.equal(cimRequests, 1);
350
- await delay(40);
351
- assert.equal(cimRequests, 1);
352
- assert.equal(first.cancel(), true);
353
- assert.equal(second.cancel(), true);
354
- });
355
-
356
- test('quiescence cleanup rejection is consumed after one callback', async () => {
357
- let callbacks = 0;
358
- let unhandled = 0;
359
- const onUnhandled = () => { unhandled += 1; };
360
- process.on('unhandledRejection', onUnhandled);
361
- try {
362
- trackProcessTreeQuiescence(123, async () => {
363
- callbacks += 1;
364
- throw new Error('fixture cleanup rejection');
365
- }, { probe: () => false });
366
- await delay(20);
367
- assert.equal(callbacks, 1);
368
- assert.equal(unhandled, 0);
369
- } finally {
370
- process.removeListener('unhandledRejection', onUnhandled);
371
- }
372
- });
373
-
374
- test('rejected foreground lease cleanup still returns one shell result', async () => {
375
- let releases = 0;
376
- const isWindows = process.platform === 'win32';
377
- const result = await execShellCommand({
378
- shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
379
- shellArg: isWindows ? '/c' : '-c',
380
- command: 'echo cleanup-result',
381
- env: process.env,
382
- cwd: process.cwd(),
383
- timeoutMs: 5000,
384
- admission: {
385
- async acquire() {
386
- return {
387
- release() {
388
- releases += 1;
389
- return Promise.reject(new Error('fixture release rejection'));
390
- },
391
- };
392
- },
393
- },
394
- });
395
- assert.equal(result.exitCode, 0);
396
- assert.equal(releases, 1);
397
- });
398
-
399
- test('spawn failure contains rejected lease cleanup and returns one result', async () => {
400
- let releases = 0;
401
- const result = await execShellCommand({
402
- shell: `mixdog-missing-shell-${process.pid}`,
403
- shellArg: '-c',
404
- command: 'echo unreachable',
405
- env: process.env,
406
- cwd: process.cwd(),
407
- timeoutMs: 1000,
408
- admission: {
409
- async acquire() {
410
- return {
411
- release() {
412
- releases += 1;
413
- return Promise.reject(new Error('spawn cleanup rejected'));
414
- },
415
- };
416
- },
417
- },
418
- });
419
- assert.equal(result.exitCode, 1);
420
- assert.equal(result.failureReason, 'spawn failed');
421
- assert.match(result.stderr, /resource cleanup failed: spawn cleanup rejected/);
422
- assert.equal(releases, 1);
423
- });
424
-
425
- for (const trigger of ['threshold', 'timeout']) {
426
- test(`background ${trigger} promotion contains detach rejection and returns once`, async () => {
427
- let releases = 0;
428
- let detachCalls = 0;
429
- const isWindows = process.platform === 'win32';
430
- const result = await execShellCommand({
431
- shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
432
- shellArg: isWindows ? '/c' : '-c',
433
- command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
434
- env: process.env,
435
- cwd: process.cwd(),
436
- timeoutMs: trigger === 'timeout' ? 20 : 5000,
437
- autoBackgroundMs: trigger === 'threshold' ? 20 : undefined,
438
- backgroundOnTimeout: trigger === 'timeout',
439
- admission: {
440
- async acquire() {
441
- return {
442
- release() { releases += 1; },
443
- detachDependency() {
444
- detachCalls += 1;
445
- return Promise.reject(new Error('detach restoration rejected'));
446
- },
447
- };
448
- },
449
- },
450
- });
451
- assert.equal(result.backgrounded, false);
452
- assert.equal(result.failureReason, 'resource cleanup failed');
453
- assert.match(result.stderr, /detach restoration rejected/);
454
- assert.equal(detachCalls, 1);
455
- await waitUntil(() => releases === 1);
456
- });
457
- }
458
-
459
- test('abort during adoption detaches once, resolves once, and releases once', async () => {
460
- const controller = new AbortController();
461
- const signal = controller.signal;
462
- let abortedReads = 0;
463
- Object.defineProperty(signal, 'aborted', {
464
- configurable: true,
465
- get() {
466
- abortedReads += 1;
467
- // Read #1: the bounded admission-wait wrapper (_acquireShellLeaseBounded).
468
- // Read #2: the pre-spawn abort check. Read #3: the _autoBackground
469
- // adoption re-check — the abort must land exactly there.
470
- if (abortedReads === 3) controller.abort(new Error('cancelled during adoption'));
471
- return abortedReads >= 3;
472
- },
473
- });
474
- let detachCalls = 0;
475
- let releases = 0;
476
- let results = 0;
477
- const isWindows = process.platform === 'win32';
478
- const pending = execShellCommand({
479
- shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
480
- shellArg: isWindows ? '/c' : '-c',
481
- command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
482
- env: process.env,
483
- cwd: process.cwd(),
484
- timeoutMs: 5000,
485
- autoBackgroundMs: 20,
486
- abortSignal: signal,
487
- admission: {
488
- async acquire() {
489
- return {
490
- detachDependency() { detachCalls += 1; },
491
- release() { releases += 1; },
492
- };
493
- },
494
- },
495
- }).then((result) => {
496
- results += 1;
497
- return result;
498
- });
499
- const result = await pending;
500
- assert.equal(result.backgrounded, false);
501
- assert.equal(result.killed, true);
502
- assert.equal(result.killCause, 'cancellation');
503
- assert.equal(results, 1);
504
- assert.equal(detachCalls, 1);
505
- await waitUntil(() => releases === 1);
506
- await delay(30);
507
- assert.equal(results, 1);
508
- assert.equal(detachCalls, 1);
509
- assert.equal(releases, 1);
510
- });
511
-
512
- test('foreground shell lease follows a surviving POSIX process group', {
513
- skip: process.platform === 'win32' ? 'Node cannot prove Windows descendant quiescence after root exit' : false,
514
- }, async () => {
515
- const admission = new ResourceAdmissionController({
516
- limits: { maxShells: 1, maxHighLoad: 1 },
517
- metrics: healthy,
518
- });
519
- const result = await execShellCommand({
520
- shell: '/bin/sh',
521
- shellArg: '-c',
522
- command: 'sleep 1 &',
523
- env: process.env,
524
- cwd: process.cwd(),
525
- timeoutMs: 5000,
526
- admission,
527
- });
528
- assert.equal(result.exitCode, 0);
529
- assert.equal(admission.snapshot().active.shell, 1);
530
- await waitUntil(() => admission.snapshot().active.shell === 0, 3000);
531
- });
532
-
533
- test('background completion marker does not release before POSIX group quiescence', {
534
- skip: process.platform === 'win32' ? 'Node cannot prove Windows descendant quiescence after root exit' : false,
535
- }, async () => {
536
- const job = await startBackgroundShellJob({
537
- command: 'sleep 1 &',
538
- timeoutMs: 5000,
539
- workDir: process.cwd(),
540
- mergeStderr: false,
541
- spawnEnv: process.env,
542
- shell: '/bin/sh',
543
- shellArg: '-c',
544
- shellType: 'posix',
545
- });
546
- let releases = 0;
547
- assert.equal(attachShellJobResourceLease(job.jobId, { release() { releases += 1; } }), true);
548
- const completed = await waitForShellJob(job.jobId, { timeoutMs: 2000, pollMs: 20 });
549
- assert.equal(completed.status, 'completed');
550
- assert.equal(releases, 0);
551
- await waitUntil(() => releases === 1, 3000);
552
- await delay(50);
553
- assert.equal(releases, 1);
554
- });
555
-
556
- test('promotion detaches caller abort listener and holds lease until child close', async () => {
557
- const admission = new ResourceAdmissionController({
558
- limits: { maxShells: 1, maxHighLoad: 1 },
559
- metrics: healthy,
560
- });
561
- const controller = new AbortController();
562
- let listeners = 0;
563
- const signal = controller.signal;
564
- const add = signal.addEventListener.bind(signal);
565
- const remove = signal.removeEventListener.bind(signal);
566
- signal.addEventListener = (...args) => { listeners += 1; return add(...args); };
567
- signal.removeEventListener = (...args) => { listeners -= 1; return remove(...args); };
568
- const isWindows = process.platform === 'win32';
569
- const result = await execShellCommand({
570
- shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
571
- shellArg: isWindows ? '/c' : '-c',
572
- command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
573
- env: process.env,
574
- cwd: process.cwd(),
575
- timeoutMs: 5000,
576
- autoBackgroundMs: 25,
577
- abortSignal: signal,
578
- admission,
579
- });
580
- assert.equal(result.backgrounded, true);
581
- assert.equal(listeners, 0);
582
- assert.equal(admission.snapshot().active.shell, 1);
583
- killShellJob(result.jobId);
584
- await waitUntil(() => admission.snapshot().active.shell === 0, 3000);
585
- });
586
-
587
- test('explicit async shell is admitted for its background lifetime', async () => {
588
- const admission = new ResourceAdmissionController({
589
- limits: { maxShells: 1, maxHighLoad: 1 },
590
- metrics: healthy,
591
- });
592
- const isWindows = process.platform === 'win32';
593
- const output = await executeBashTool({
594
- command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
595
- mode: 'async',
596
- shell: isWindows ? 'powershell' : 'bash',
597
- }, process.cwd(), { resourceAdmission: admission });
598
- const jobId = /task_id:\s*(\S+)/i.exec(String(output))?.[1];
599
- assert.ok(jobId, String(output));
600
- assert.equal(admission.snapshot().active.shell, 1);
601
- killShellJob(jobId);
602
- await waitUntil(() => admission.snapshot().active.shell === 0);
603
- });
604
-
605
- for (const shellType of ['posix', 'powershell']) {
606
- test(`${shellType} background spawn handles asynchronous child errors`, async () => {
607
- const child = fakeChild({ event: 'error' });
608
- const result = await startBackgroundShellJob({
609
- command: 'echo unreachable',
610
- timeoutMs: 1000,
611
- workDir: process.cwd(),
612
- mergeStderr: false,
613
- spawnEnv: process.env,
614
- shell: shellType === 'powershell' ? 'pwsh' : '/bin/sh',
615
- shellArg: '-c',
616
- shellType,
617
- spawnFn: () => child,
618
- });
619
- assert.equal(result.status, 'failed');
620
- assert.match(result.error, /async spawn denied/);
621
- assert.equal(child.listenerCount('error'), 0);
622
- });
623
- }
624
-
625
- for (const shellType of ['posix', 'powershell']) {
626
- test(`${shellType} post-readiness process error retains admission until confirmed exit`, async () => {
627
- const child = fakeChild({ killMode: 'none' });
628
- const result = await startBackgroundShellJob({
629
- command: 'echo runtime-error',
630
- timeoutMs: 1000,
631
- workDir: process.cwd(),
632
- mergeStderr: false,
633
- spawnEnv: process.env,
634
- shell: shellType === 'powershell' ? 'pwsh' : '/bin/sh',
635
- shellArg: '-c',
636
- shellType,
637
- spawnFn: () => child,
638
- rollbackTimeoutMs: 100,
639
- });
640
- let releases = 0;
641
- assert.equal(attachShellJobResourceLease(result.jobId, { release() { releases += 1; } }), true);
642
- child.emit('error', new Error('runtime pipe failure'));
643
- await new Promise((resolve) => setImmediate(resolve));
644
- assert.equal(releases, 0);
645
- child.signalCode = 'SIGKILL';
646
- child.emit('exit', null, 'SIGKILL');
647
- child.emit('close', null, 'SIGKILL');
648
- await waitUntil(() => releases === 1);
649
- });
650
- }
651
-
652
- for (const shellType of ['posix', 'powershell']) {
653
- test(`${shellType} persistence failure kills and awaits the spawned child`, async () => {
654
- const child = fakeChild();
655
- const result = await startBackgroundShellJob({
656
- command: 'echo rollback',
657
- timeoutMs: 1000,
658
- workDir: process.cwd(),
659
- mergeStderr: false,
660
- spawnEnv: process.env,
661
- shell: shellType === 'powershell' ? 'pwsh' : '/bin/sh',
662
- shellArg: '-c',
663
- shellType,
664
- spawnFn: () => child,
665
- writeDetailFn: () => { throw new Error('detail disk full'); },
666
- });
667
- assert.equal(result.status, 'failed');
668
- assert.match(result.error, /failed to persist/);
669
- assert.ok(child.killCalls >= 1);
670
- assert.ok(child.signalCode || child.exitCode != null);
671
- });
672
- }
673
-
674
- test('rollback consumes kill-time child errors', async () => {
675
- const child = fakeChild({ killMode: 'error-exit' });
676
- const result = await startBackgroundShellJob({
677
- command: 'echo rollback-error',
678
- timeoutMs: 1000,
679
- workDir: process.cwd(),
680
- mergeStderr: false,
681
- spawnEnv: process.env,
682
- shell: '/bin/sh',
683
- shellArg: '-c',
684
- shellType: 'posix',
685
- spawnFn: () => child,
686
- writeDetailFn: () => { throw new Error('persist failed'); },
687
- });
688
- assert.match(result.error, /failed to persist/);
689
- assert.equal(result.rollbackPending, undefined);
690
- });
691
-
692
- test('rollback timeout returns a tracked diagnosable failure within its bound', async () => {
693
- const child = fakeChild({ killMode: 'none' });
694
- const started = Date.now();
695
- const result = await startBackgroundShellJob({
696
- command: 'echo rollback-timeout',
697
- timeoutMs: 1000,
698
- workDir: process.cwd(),
699
- mergeStderr: false,
700
- spawnEnv: process.env,
701
- shell: '/bin/sh',
702
- shellArg: '-c',
703
- shellType: 'posix',
704
- spawnFn: () => child,
705
- writeDetailFn: () => { throw new Error('persist failed'); },
706
- rollbackTimeoutMs: 20,
707
- });
708
- assert.equal(result.rollbackPending, true);
709
- assert.match(result.error, /termination unconfirmed after 20ms .*remains tracked/);
710
- assert.ok(Date.now() - started < 1000);
711
- let releases = 0;
712
- assert.equal(attachShellJobResourceLease(
713
- result.jobId,
714
- { release() { releases += 1; } },
715
- { allowUnpersisted: true },
716
- ), true);
717
- child.signalCode = 'SIGKILL';
718
- child.emit('exit', null, 'SIGKILL');
719
- child.emit('close', null, 'SIGKILL');
720
- await waitUntil(() => releases === 1);
721
- });
722
-
723
- test('explicit async cancellation after admission prevents spawn and releases once', async () => {
724
- const controller = new AbortController();
725
- let releases = 0;
726
- let spawns = 0;
727
- const output = await executeBashTool({
728
- command: 'echo must-not-spawn',
729
- mode: 'async',
730
- shell: process.platform === 'win32' ? 'powershell' : 'bash',
731
- }, process.cwd(), {
732
- abortSignal: controller.signal,
733
- resourceAdmission: {
734
- async acquire() {
735
- controller.abort(new Error('cancelled after admission'));
736
- return { release() { releases += 1; } };
737
- },
738
- },
739
- shellJobRuntime: {
740
- spawnFn() {
741
- spawns += 1;
742
- return fakeChild();
743
- },
744
- },
745
- });
746
- assert.match(String(output), /cancelled after admission/);
747
- assert.equal(spawns, 0);
748
- assert.equal(releases, 1);
749
- });
750
-
751
- test('persistent shell holds one lease across commands and releases on close', {
752
- skip: process.platform === 'win32' ? 'persistent shells are disabled on Windows' : false,
753
- }, async () => {
754
- const admission = new ResourceAdmissionController({
755
- limits: { maxShells: 1, maxHighLoad: 1 },
756
- metrics: healthy,
757
- });
758
- const sessionId = `resource-test-${process.pid}`;
759
- const first = await executeBashTool({
760
- command: 'printf persistent',
761
- persistent: true,
762
- session_id: sessionId,
763
- create: true,
764
- }, process.cwd(), { resourceAdmission: admission });
765
- assert.match(String(first), /persistent/);
766
- assert.equal(admission.snapshot().active.shell, 1);
767
- await executeBashTool({ command: '', session_id: sessionId, close: true }, process.cwd(), {
768
- resourceAdmission: admission,
769
- });
770
- assert.equal(admission.snapshot().active.shell, 0);
771
- });
772
-
773
- test('hidden agent admission combines factory and call cancellation sources', async () => {
774
- const admission = new ResourceAdmissionController({
775
- limits: { maxAgents: 1, maxHighLoad: 1 },
776
- metrics: healthy,
777
- });
778
- const occupied = await admission.acquire('agent');
779
- const factory = new AbortController();
780
- const call = new AbortController();
781
- const dispatch = makeAgentDispatch({
782
- agent: 'explorer',
783
- config: {},
784
- parentSignal: factory.signal,
785
- resourceAdmission: admission,
786
- });
787
- const pending = dispatch({ prompt: 'locate', parentSignal: call.signal });
788
- factory.abort(new Error('factory parent cancelled'));
789
- await assert.rejects(pending, /factory parent cancelled/);
790
- assert.equal(admission.snapshot().queued, 0);
791
- occupied.release();
792
- });