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
@@ -0,0 +1,519 @@
1
+ // Background shell-job spawn implementation, extracted from shell-jobs.mjs.
2
+ import { spawn } from 'child_process';
3
+ import { existsSync, readFileSync, statSync, unlinkSync, watch as fsWatch, writeFileSync } from 'fs';
4
+ import { basename } from 'path';
5
+ import { scrubLoaderVars, scrubProviderSecrets } from '../env-scrub.mjs';
6
+ import {
7
+ normalizeToolNotifyContext,
8
+ notifyToolCompletion,
9
+ } from '../../../../shared/tool-execution-contract.mjs';
10
+ import {
11
+ renderShellCompletionEnvelope,
12
+ shellCompletionInstruction,
13
+ renderShellPromptStallEnvelope,
14
+ shellPromptStallInstruction,
15
+ } from '../../../../shared/task-notification-envelope.mjs';
16
+ import {
17
+ completeBackgroundTask,
18
+ notifyBackgroundTaskProgress,
19
+ getBackgroundTask,
20
+ } from '../../../../shared/background-tasks.mjs';
21
+ import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
22
+ import { detachedSpawnOpts } from '../../../../shared/spawn-flags.mjs';
23
+ import {
24
+ getShellJobsDir,
25
+ shellJobStdoutPath,
26
+ shellJobStderrPath,
27
+ shellJobExitPath,
28
+ shellJobDonePath,
29
+ shellJobEnforcedPath,
30
+ resolveJobOwnerHostPid,
31
+ trimShellJobSpill,
32
+ writeShellJobDetail,
33
+ readShellJobDetail,
34
+ } from './shell-job-paths.mjs';
35
+ import {
36
+ isPidAlive,
37
+ trackProcessTreeQuiescence,
38
+ killProcessTree,
39
+ _unregisterLiveJobPid,
40
+ _installShellJobsExitHook,
41
+ _registerLiveJobPid,
42
+ _liveJobPids,
43
+ _liveJobIdsByPid,
44
+ _killLiveJobPid,
45
+ } from './shell-job-process.mjs';
46
+ import {
47
+ attachJobInsights,
48
+ looksLikeInteractivePrompt,
49
+ readPromptTail,
50
+ shellJobOutputBytes,
51
+ shellJobPublicTaskResult,
52
+ SHELL_JOB_OUTPUT_DISK_CAP,
53
+ } from './lib/shell-job-insights.mjs';
54
+ import {
55
+ sleep,
56
+ awaitSpawnReady,
57
+ adoptSpawnErrorHandler,
58
+ discardSpawnErrorGuard,
59
+ rollbackSpawnedChild,
60
+ shellQuoteSingle,
61
+ psSingleQuote,
62
+ isPowerShellShell,
63
+ } from './lib/shell-spawn-helpers.mjs';
64
+
65
+ // Facade re-exports: path/detail helpers and the job-not-found message moved
66
+ // to sibling modules; keep existing importers of shell-jobs.mjs resolving.
67
+
68
+ globalThis.__mixdogShellJobsRuntimeLoaded = true;
69
+
70
+
71
+ // Poll cadence for the adopted-job output-cap self-tick (mirrors the
72
+ // foreground sizeWatchdog in shell-command.mjs).
73
+
74
+ import { refreshShellJob, trackChildUntilConfirmedExit, releaseShellJobOwnershipWhenQuiescent, TIMER_MAX_MS } from './shell-jobs.mjs';
75
+
76
+ export async function _startBackgroundShellJobImpl({
77
+ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, shellArg, shellArgs,
78
+ shellType, clientHostPid, spawnFn = spawn, writeDetailFn = writeShellJobDetail,
79
+ rollbackTimeoutMs = 5000,
80
+ }) {
81
+ // Route ANY PowerShell shell to the PS wrapper, regardless of platform.
82
+ // Gating on win32 sent shell:'powershell' on macOS/Linux down the POSIX
83
+ // path below, which spawns `pwsh <wrapper>.sh` — pwsh then tries to run a
84
+ // bash script. isPowerShellShell already matches pwsh/powershell by stem.
85
+ if (isPowerShellShell(shell, shellType)) {
86
+ return startBackgroundPowerShellJob({
87
+ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, clientHostPid,
88
+ spawnFn, writeDetailFn,
89
+ });
90
+ }
91
+
92
+ // POSIX-shell wrapper path. On Windows this runs for shell:'bash' (Git
93
+ // Bash): the previous hard rejection here was a policy choice, NOT a real
94
+ // invariant — every piece of this path's plumbing is shell-neutral.
95
+ // The wrapper uses `command -v timeout`, `if ... fi`, single-quote escape
96
+ // and POSIX exit-code propagation, all of which Git Bash executes; output
97
+ // and exit-code plumbing flows through the staged script's own
98
+ // `exec > … 2> …` redirect plus `printf … > exitPath` / `touch donePath`
99
+ // (filesystem ops, not shell features); and kill goes through
100
+ // killProcessTree(), which on win32 uses `taskkill /pid /t /f` regardless
101
+ // of which shell spawned the tree. So Git Bash background tasks cancel,
102
+ // capture output, and report exit codes correctly.
103
+ const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
104
+ const stdoutPath = shellJobStdoutPath(jobId);
105
+ const stderrPath = shellJobStderrPath(jobId);
106
+ const exitPath = shellJobExitPath(jobId);
107
+ const donePath = shellJobDonePath(jobId);
108
+ // timeoutMs <= 0 means unlimited (async omitted default): no kernel
109
+ // `timeout` wrapper, no enforced marker — exec the inner shell directly.
110
+ const enforceTimeout = Number(timeoutMs) > 0;
111
+ const timeoutSeconds = enforceTimeout ? Math.max(1, Math.ceil(timeoutMs / 1000)) : 0;
112
+ // P2 fix: wrap with POSIX `timeout` so the kernel terminates the process
113
+ // at deadline even if the JS-side timer is interrupted. --preserve-status
114
+ // keeps the user command's exit code on success; on timeout the wrapper
115
+ // exits 124.
116
+ // `timeout` ships with GNU coreutils on Linux. On macOS, Homebrew
117
+ // coreutils installs it as `gtimeout` (the un-prefixed name is NOT created
118
+ // by default), so the wrapper picks `timeout` if present else `gtimeout`.
119
+ // When neither exists it falls through to the inner command (the parent
120
+ // setTimeout still calls refreshShellJob to clean up).
121
+ const userCmdQuoted = shellQuoteSingle(command);
122
+ // P2 fix: invoke the resolved shell (not bash -c) so zsh / dash /
123
+ // alternate shells run snapshot-aware commands correctly. Drop
124
+ // --preserve-status so timeout returns 124 unambiguously, making
125
+ // it trivial to distinguish a timeout (124) from a user-side
126
+ // SIGTERM exit (143).
127
+ const innerShellQ = shellQuoteSingle(shell);
128
+ const innerArgs = Array.isArray(shellArgs) && shellArgs.length > 0 ? shellArgs : [shellArg];
129
+ const innerArgsQ = innerArgs.filter((arg) => arg != null && String(arg).length > 0).map(shellQuoteSingle).join(' ');
130
+ // Runtime enforcement proof: the wrapper touches <jobId>.enforced right
131
+ // before exec'ing `timeout`, so the marker exists iff the timeout branch
132
+ // actually ran under the wrapper's own env/cwd. A spawn-time probe can't
133
+ // guarantee that (env is scrubbed, cwd differs) — see shellJobEnforcedPath.
134
+ const enforcedPath = shellJobEnforcedPath(jobId);
135
+ // Lifecycle ordering invariant: write the exit-code file FIRST and
136
+ // `touch donePath` strictly AFTER. refreshShellJob() gates completion
137
+ // on donePath existence and only then trusts the exit file — without
138
+ // this strict ordering, readFileSync on a partially-flushed exit file
139
+ // returned '' -> parseInt NaN -> exitCode null -> spurious 'failed'
140
+ // status for processes that actually exited 0. `rm -- "$0"` removes
141
+ // the staged wrapper .cmd.sh after donePath is published so a host
142
+ // crash before this point still leaves the file for the sweep to GC.
143
+ const _innerRun = `${innerShellQ} ${innerArgsQ} ${userCmdQuoted}`;
144
+ const _execPart = enforceTimeout
145
+ ? `if command -v timeout >/dev/null 2>&1; then _to=timeout; elif command -v gtimeout >/dev/null 2>&1; then _to=gtimeout; else _to=; fi; if [ -n "$_to" ]; then touch ${shellQuoteSingle(enforcedPath)}; "$_to" ${timeoutSeconds} ${_innerRun}; else ${_innerRun}; fi`
146
+ : _innerRun;
147
+ const wrapped = `{ ${_execPart}; rc=$?; printf '%s' "$rc" > ${shellQuoteSingle(exitPath)}; touch ${shellQuoteSingle(donePath)}; rm -- "$0" 2>/dev/null; exit $rc; }`;
148
+ // Stage the wrapped command to a .sh and let the script open its own
149
+ // output files via `exec > … 2> …`. The parent does NOT pass file
150
+ // descriptors via stdio inheritance (`stdio: 'ignore'` for all three).
151
+ //
152
+ // Let the shell own redirects via `exec > ... 2> ...` inside the staged
153
+ // script. The child remains referenced by this CLI process; detached here
154
+ // is only used on POSIX to create a process group for tree-kill.
155
+ const outRedirect = mergeStderr
156
+ ? `> ${shellQuoteSingle(stdoutPath)} 2>&1`
157
+ : `> ${shellQuoteSingle(stdoutPath)} 2> ${shellQuoteSingle(stderrPath)}`;
158
+ const scriptBody = `#!/usr/bin/env bash\nexec ${outRedirect}\n${wrapped}\n`;
159
+ const wrappedTempPath = `${exitPath}.cmd.sh`;
160
+ try {
161
+ writeFileSync(wrappedTempPath, scriptBody);
162
+ } catch (e) {
163
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to stage shell background task: ${e?.message || e}` };
164
+ }
165
+ // R11: scrub loader/execution vars even though bash-tool.mjs already
166
+ // scrubs upstream — defense-in-depth at the spawn site catches future
167
+ // callers that build their own spawnEnv.
168
+ let child;
169
+ try {
170
+ child = spawnFn(shell, [wrappedTempPath], {
171
+ cwd: workDir,
172
+ env: scrubLoaderVars(scrubProviderSecrets({ ...spawnEnv })),
173
+ stdio: 'ignore',
174
+ ...detachedSpawnOpts,
175
+ });
176
+ await awaitSpawnReady(child, 'POSIX background task');
177
+ } catch (e) {
178
+ try { unlinkSync(wrappedTempPath); } catch {}
179
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to spawn shell background task: ${e?.message || e}` };
180
+ }
181
+ const detail = {
182
+ jobId,
183
+ kind: 'bash',
184
+ status: 'running',
185
+ command,
186
+ cwd: workDir,
187
+ pid: child.pid,
188
+ mergeStderr,
189
+ timeoutMs,
190
+ timeoutSeconds,
191
+ stdoutPath,
192
+ stderrPath: mergeStderr ? stdoutPath : stderrPath,
193
+ exitPath,
194
+ donePath,
195
+ // Per-terminal session stamp (see resolveJobOwnerHostPid).
196
+ ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
197
+ startedAt: new Date().toISOString(),
198
+ };
199
+ let terminal = false;
200
+ let runtimeOwned = false;
201
+ const onRuntimeError = (error) => {
202
+ if (terminal) return;
203
+ terminal = true;
204
+ detail.status = 'failed';
205
+ detail.terminationPending = true;
206
+ detail.error = `shell background task process error: ${error?.message || error}`;
207
+ detail.finishedAt = new Date().toISOString();
208
+ try { writeDetailFn(detail); } catch {}
209
+ if (runtimeOwned) void (async () => {
210
+ const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
211
+ if (rollback.confirmed) {
212
+ releaseShellJobOwnershipWhenQuiescent(jobId, child.pid, {
213
+ onConfirmed: () => {
214
+ detail.terminationPending = false;
215
+ try { writeDetailFn(detail); } catch {}
216
+ },
217
+ });
218
+ } else {
219
+ detail.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
220
+ try { writeDetailFn(detail); } catch {}
221
+ trackChildUntilConfirmedExit(child, jobId, () => {
222
+ detail.terminationPending = false;
223
+ try { writeDetailFn(detail); } catch {}
224
+ });
225
+ }
226
+ })();
227
+ };
228
+ adoptSpawnErrorHandler(child, onRuntimeError);
229
+ if (terminal) {
230
+ const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
231
+ detail.terminationPending = !rollback.confirmed;
232
+ try { writeDetailFn(detail); } catch {}
233
+ try { unlinkSync(wrappedTempPath); } catch {}
234
+ return rollback.confirmed
235
+ ? detail
236
+ : {
237
+ ...detail,
238
+ rollbackPending: trackChildUntilConfirmedExit(child, jobId, () => {
239
+ detail.terminationPending = false;
240
+ try { writeDetailFn(detail); } catch {}
241
+ }),
242
+ error: `${detail.error}; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`,
243
+ };
244
+ }
245
+ try {
246
+ writeDetailFn(detail);
247
+ } catch (e) {
248
+ terminal = true;
249
+ const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
250
+ child.removeListener('error', onRuntimeError);
251
+ discardSpawnErrorGuard(child);
252
+ try { unlinkSync(wrappedTempPath); } catch {}
253
+ const failure = { jobId, kind: 'bash', status: 'failed', error: `failed to persist shell background task: ${e?.message || e}` };
254
+ if (!rollback.confirmed) {
255
+ failure.rollbackPending = trackChildUntilConfirmedExit(child, jobId);
256
+ failure.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
257
+ }
258
+ return failure;
259
+ }
260
+ startChildGuardian({
261
+ childPid: child.pid,
262
+ childGroupPid: child.pid,
263
+ label: 'shell-job',
264
+ });
265
+ _installShellJobsExitHook();
266
+ _registerLiveJobPid(child.pid, jobId);
267
+ runtimeOwned = true;
268
+ releaseShellJobOwnershipWhenQuiescent(jobId, child.pid, {
269
+ allowLateLease: true,
270
+ deferUntilRootExit: true,
271
+ });
272
+ // Deadline cleanup poke only when a timeout is enforced; an unlimited
273
+ // (timeoutMs<=0) job has no deadline — completion is observed via the
274
+ // fs.watch/poll watcher and refreshShellJob on task queries.
275
+ if (enforceTimeout) {
276
+ const timer = setTimeout(() => { refreshShellJob(jobId); }, Math.min(TIMER_MAX_MS, timeoutMs + 25));
277
+ if (typeof timer.unref === 'function') timer.unref();
278
+ }
279
+ return detail;
280
+ }
281
+
282
+ async function startBackgroundPowerShellJob({
283
+ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, clientHostPid,
284
+ spawnFn = spawn, writeDetailFn = writeShellJobDetail, rollbackTimeoutMs = 5000,
285
+ }) {
286
+ const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
287
+ const stdoutPath = shellJobStdoutPath(jobId);
288
+ const rawStderrPath = shellJobStderrPath(jobId);
289
+ const exitPath = shellJobExitPath(jobId);
290
+ const donePath = shellJobDonePath(jobId);
291
+ const wrappedTempPath = `${exitPath}.cmd.ps1`;
292
+ // Stage the USER command as its own .ps1 and run it via `-File` instead of
293
+ // `-EncodedCommand <base64>`: the base64 token landed on the grandchild's
294
+ // visible command line, which is a prime Defender obfuscation signature
295
+ // (same family as the PowhidSubExec false positive). A file path on the
296
+ // command line carries no such signature, and the payload stays opaque to
297
+ // outer-shell quoting exactly as base64 did. UTF-8 BOM so Windows
298
+ // PowerShell 5.1 doesn't misread non-ASCII as ANSI.
299
+ // Trailer mirrors -Command/-EncodedCommand exit semantics (-File alone
300
+ // does NOT propagate the last native command's exit code).
301
+ const innerTempPath = `${exitPath}.user.ps1`;
302
+ const innerScript = `\ufeff${command}\nif ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }\n`;
303
+ const mergeLiteral = mergeStderr ? '$true' : '$false';
304
+ const wrapper = [
305
+ "$ErrorActionPreference = 'Continue'",
306
+ '[Console]::OutputEncoding=[System.Text.Encoding]::UTF8',
307
+ '$OutputEncoding=[System.Text.Encoding]::UTF8',
308
+ '$exe = (Get-Process -Id $PID).Path',
309
+ `$innerPath = ${psSingleQuote(innerTempPath)}`,
310
+ `$stdoutPath = ${psSingleQuote(stdoutPath)}`,
311
+ `$stderrPath = ${psSingleQuote(rawStderrPath)}`,
312
+ `$exitPath = ${psSingleQuote(exitPath)}`,
313
+ `$donePath = ${psSingleQuote(donePath)}`,
314
+ `$mergeStderr = ${mergeLiteral}`,
315
+ // 0 (or negative) = unlimited: the wrapper's `if ($timeoutMs -gt 0 ...`
316
+ // guard falls through to a plain WaitForExit() with no deadline. Do NOT
317
+ // floor to 1 — that would enforce a 1ms timeout on unlimited jobs.
318
+ `$timeoutMs = ${Math.max(0, Math.floor(timeoutMs || 0))}`,
319
+ '$code = 1',
320
+ 'try {',
321
+ // -ExecutionPolicy Bypass: unlike -EncodedCommand, -File is subject to
322
+ // the execution policy on Windows PowerShell; pwsh on macOS/Linux
323
+ // accepts the parameter as a no-op, so it is safe unconditionally.
324
+ " $argList = @('-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $innerPath)",
325
+ // Do not set WindowStyle: Hidden creates a separate transient conhost.
326
+ // On Windows, explicitly reuse the wrapper's CREATE_NO_WINDOW console.
327
+ // NoNewWindow is unavailable on non-Windows pwsh, so only add it for
328
+ // Windows (where $IsWindows is absent/null in Windows PowerShell 5.1).
329
+ ' $spArgs = @{ FilePath = $exe; ArgumentList = $argList; RedirectStandardOutput = $stdoutPath; RedirectStandardError = $stderrPath; PassThru = $true }',
330
+ ' if ($IsWindows -or $null -eq $IsWindows) { $spArgs[\'NoNewWindow\'] = $true }',
331
+ ' $p = Start-Process @spArgs',
332
+ ' if ($timeoutMs -gt 0 -and -not $p.WaitForExit($timeoutMs)) {',
333
+ // Kill the whole process TREE, not just the direct child: Start-Process
334
+ // launches an intermediate pwsh that spawns the user command, so
335
+ // Stop-Process on $p.Id alone orphans the grandchildren. On Windows use
336
+ // `taskkill /T /F` (tree-terminate); on macOS/Linux pwsh, fall back to
337
+ // Stop-Process (no taskkill there).
338
+ ' try {',
339
+ ' if ($IsWindows -or $null -eq $IsWindows) {',
340
+ ' & taskkill.exe /pid $p.Id /t /f 2>$null | Out-Null',
341
+ ' } else {',
342
+ ' try { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue } catch {}',
343
+ ' }',
344
+ ' } catch {}',
345
+ ' $code = 124',
346
+ ' } else {',
347
+ ' try { $p.WaitForExit() } catch {}',
348
+ ' $code = if ($null -ne $p.ExitCode) { [int]$p.ExitCode } else { 0 }',
349
+ ' }',
350
+ '} catch {',
351
+ ' try { Add-Content -LiteralPath $stderrPath -Value ($_ | Out-String) -Encoding utf8 } catch {}',
352
+ ' $code = 1',
353
+ '}',
354
+ 'if ($mergeStderr) {',
355
+ ' try {',
356
+ ' if (Test-Path -LiteralPath $stderrPath) {',
357
+ ' $err = Get-Content -LiteralPath $stderrPath -Raw -ErrorAction SilentlyContinue',
358
+ ' if ($err) { Add-Content -LiteralPath $stdoutPath -Value $err -Encoding utf8 }',
359
+ ' Remove-Item -LiteralPath $stderrPath -Force -ErrorAction SilentlyContinue',
360
+ ' }',
361
+ ' } catch {}',
362
+ '}',
363
+ 'try { Set-Content -LiteralPath $exitPath -Value ([string]$code) -NoNewline -Encoding ascii } catch {}',
364
+ 'try { Set-Content -LiteralPath $donePath -Value "" -NoNewline -Encoding ascii } catch {}',
365
+ 'try { Remove-Item -LiteralPath $innerPath -Force -ErrorAction SilentlyContinue } catch {}',
366
+ 'try { Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue } catch {}',
367
+ 'exit $code',
368
+ '',
369
+ ].join('\n');
370
+ try {
371
+ writeFileSync(wrappedTempPath, wrapper, 'utf-8');
372
+ writeFileSync(innerTempPath, innerScript, 'utf-8');
373
+ } catch (e) {
374
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to stage PowerShell background task: ${e?.message || e}` };
375
+ }
376
+
377
+ const shellStem = basename(String(shell || '')).toLowerCase().replace(/\.exe$/, '');
378
+ // No `-WindowStyle Hidden` CLI switch: windowsHide:true on the spawn below
379
+ // already gives CREATE_NO_WINDOW, and the visible command-line token trips
380
+ // Defender's hidden-PowerShell dropper signature (PowhidSubExec). The
381
+ // in-wrapper Start-Process uses Windows-only NoNewWindow to reuse this
382
+ // hidden console rather than creating a transient conhost window.
383
+ // `-ExecutionPolicy` only applies to Windows PowerShell; build per-platform.
384
+ const isWin = process.platform === 'win32';
385
+ const wrapperArgs = ['-NoLogo', '-NoProfile', '-NonInteractive'];
386
+ if (isWin && shellStem === 'powershell') wrapperArgs.push('-ExecutionPolicy', 'Bypass');
387
+ wrapperArgs.push('-File', wrappedTempPath);
388
+ // Spawn the staged wrapper directly. detached MUST be false on Windows:
389
+ // a native pwsh launched with detached:true + stdio:'ignore' exits
390
+ // immediately without running -File (verified — the detached child dies
391
+ // even while the parent stays alive; the non-detached child runs to
392
+ // completion). windowsHide:true gives CREATE_NO_WINDOW so no console
393
+ // window flashes on screen. The wrapper owns its own stdout/stderr file
394
+ // redirect (exec-equivalent Set-Content paths above), so stdio:'ignore'
395
+ // drops no output. The child remains referenced so it is owned by the CLI
396
+ // process; runtime.close()/exit hooks reap it instead of daemonizing it.
397
+ let child;
398
+ try {
399
+ child = spawnFn(shell, wrapperArgs, {
400
+ cwd: workDir,
401
+ env: scrubLoaderVars(scrubProviderSecrets({ ...spawnEnv })),
402
+ detached: false,
403
+ stdio: 'ignore',
404
+ windowsHide: true,
405
+ });
406
+ await awaitSpawnReady(child, 'PowerShell background task');
407
+ } catch (e) {
408
+ try { unlinkSync(wrappedTempPath); } catch {}
409
+ try { unlinkSync(innerTempPath); } catch {}
410
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to spawn PowerShell background task: ${e?.message || e}` };
411
+ }
412
+ const childPid = child.pid;
413
+ if (!Number.isFinite(childPid) || childPid <= 0) {
414
+ return { jobId, kind: 'bash', status: 'failed', error: 'PowerShell background task spawn returned no pid' };
415
+ }
416
+ const detail = {
417
+ jobId,
418
+ kind: 'bash',
419
+ shellType: 'powershell',
420
+ status: 'running',
421
+ command,
422
+ cwd: workDir,
423
+ pid: childPid,
424
+ mergeStderr,
425
+ timeoutMs,
426
+ timeoutSeconds: Number(timeoutMs) > 0 ? Math.max(1, Math.ceil(timeoutMs / 1000)) : 0,
427
+ stdoutPath,
428
+ stderrPath: mergeStderr ? stdoutPath : rawStderrPath,
429
+ exitPath,
430
+ donePath,
431
+ // The PS wrapper enforces the deadline (WaitForExit($timeoutMs) →
432
+ // Stop-Process → 124) only when timeoutMs>0; an unlimited job waits
433
+ // with no deadline, so don't falsely claim enforcement.
434
+ timeoutEnforced: Number(timeoutMs) > 0,
435
+ // Per-terminal session stamp (see resolveJobOwnerHostPid).
436
+ ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
437
+ startedAt: new Date().toISOString(),
438
+ };
439
+ let terminal = false;
440
+ let runtimeOwned = false;
441
+ const onRuntimeError = (error) => {
442
+ if (terminal) return;
443
+ terminal = true;
444
+ detail.status = 'failed';
445
+ detail.terminationPending = true;
446
+ detail.error = `PowerShell background task process error: ${error?.message || error}`;
447
+ detail.finishedAt = new Date().toISOString();
448
+ try { writeDetailFn(detail); } catch {}
449
+ if (runtimeOwned) void (async () => {
450
+ const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
451
+ if (rollback.confirmed) {
452
+ releaseShellJobOwnershipWhenQuiescent(jobId, childPid, {
453
+ onConfirmed: () => {
454
+ detail.terminationPending = false;
455
+ try { writeDetailFn(detail); } catch {}
456
+ },
457
+ });
458
+ } else {
459
+ detail.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
460
+ try { writeDetailFn(detail); } catch {}
461
+ trackChildUntilConfirmedExit(child, jobId, () => {
462
+ detail.terminationPending = false;
463
+ try { writeDetailFn(detail); } catch {}
464
+ });
465
+ }
466
+ })();
467
+ };
468
+ adoptSpawnErrorHandler(child, onRuntimeError);
469
+ if (terminal) {
470
+ const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
471
+ detail.terminationPending = !rollback.confirmed;
472
+ try { writeDetailFn(detail); } catch {}
473
+ try { unlinkSync(wrappedTempPath); } catch {}
474
+ try { unlinkSync(innerTempPath); } catch {}
475
+ return rollback.confirmed
476
+ ? detail
477
+ : {
478
+ ...detail,
479
+ rollbackPending: trackChildUntilConfirmedExit(child, jobId, () => {
480
+ detail.terminationPending = false;
481
+ try { writeDetailFn(detail); } catch {}
482
+ }),
483
+ error: `${detail.error}; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`,
484
+ };
485
+ }
486
+ try {
487
+ writeDetailFn(detail);
488
+ } catch (e) {
489
+ terminal = true;
490
+ const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
491
+ child.removeListener('error', onRuntimeError);
492
+ discardSpawnErrorGuard(child);
493
+ try { unlinkSync(wrappedTempPath); } catch {}
494
+ try { unlinkSync(innerTempPath); } catch {}
495
+ const failure = { jobId, kind: 'bash', status: 'failed', error: `failed to persist PowerShell background task: ${e?.message || e}` };
496
+ if (!rollback.confirmed) {
497
+ failure.rollbackPending = trackChildUntilConfirmedExit(child, jobId);
498
+ failure.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
499
+ }
500
+ return failure;
501
+ }
502
+ startChildGuardian({
503
+ childPid,
504
+ childGroupPid: childPid,
505
+ label: 'shell-job-powershell',
506
+ });
507
+ _installShellJobsExitHook();
508
+ _registerLiveJobPid(childPid, jobId);
509
+ runtimeOwned = true;
510
+ releaseShellJobOwnershipWhenQuiescent(jobId, childPid, {
511
+ allowLateLease: true,
512
+ deferUntilRootExit: true,
513
+ });
514
+ if (Number(timeoutMs) > 0) {
515
+ const timer = setTimeout(() => { refreshShellJob(jobId); }, Math.min(TIMER_MAX_MS, timeoutMs + 25));
516
+ if (typeof timer.unref === 'function') timer.unref();
517
+ }
518
+ return detail;
519
+ }