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,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
- });