mixdog 0.9.63 → 0.9.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (403) hide show
  1. package/README.md +3 -0
  2. package/package.json +9 -2
  3. package/scripts/code-graph-description-contract.mjs +3 -3
  4. package/scripts/verify-release-assets.mjs +2 -2
  5. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
  6. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
  7. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  8. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  9. package/src/runtime/agent/orchestrator/config.mjs +19 -124
  10. package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
  11. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
  12. package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
  13. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
  14. package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
  15. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
  16. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
  17. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
  18. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
  19. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
  20. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
  24. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
  25. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
  27. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
  30. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
  32. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
  37. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
  39. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
  41. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
  46. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
  49. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
  50. package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
  51. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
  54. package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
  57. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
  59. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
  60. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
  61. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
  62. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
  64. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
  66. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
  68. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
  69. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
  70. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
  73. package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
  74. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
  75. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
  77. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  79. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  81. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
  83. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
  91. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
  92. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
  93. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
  96. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
  97. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
  99. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
  102. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
  105. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
  106. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
  107. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
  108. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
  109. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
  110. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
  111. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
  112. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
  115. package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
  116. package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
  119. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
  120. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
  121. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  123. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
  124. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
  127. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
  129. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
  130. package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
  131. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  132. package/src/runtime/channels/lib/telegram-format.mjs +1 -1
  133. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
  134. package/src/runtime/channels/tool-defs.mjs +2 -3
  135. package/src/runtime/memory/index.mjs +1 -5
  136. package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
  137. package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
  138. package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
  139. package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
  140. package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
  141. package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
  142. package/src/runtime/memory/lib/ko-morph.mjs +2 -2
  143. package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
  144. package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
  145. package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
  146. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
  147. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
  148. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
  149. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
  150. package/src/runtime/memory/lib/memory-embed.mjs +2 -6
  151. package/src/runtime/memory/lib/memory-log.mjs +8 -0
  152. package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
  153. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
  154. package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
  155. package/src/runtime/memory/lib/memory-score.mjs +1 -1
  156. package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
  157. package/src/runtime/memory/lib/memory.mjs +2 -6
  158. package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
  159. package/src/runtime/memory/lib/pg/process.mjs +1 -5
  160. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
  161. package/src/runtime/memory/lib/recall-format.mjs +2 -2
  162. package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
  163. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
  164. package/src/runtime/memory/lib/trace-store.mjs +2 -6
  165. package/src/runtime/search/lib/http-fetch.mjs +6 -6
  166. package/src/runtime/search/lib/web-tools.mjs +2 -2
  167. package/src/runtime/shared/buffered-appender.mjs +1 -1
  168. package/src/runtime/shared/child-guardian.mjs +1 -1
  169. package/src/runtime/shared/child-spawn-gate.mjs +1 -1
  170. package/src/runtime/shared/config.mjs +4 -4
  171. package/src/runtime/shared/launcher-control.mjs +9 -10
  172. package/src/runtime/shared/llm/index.mjs +5 -20
  173. package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
  174. package/src/runtime/shared/memory-snapshot.mjs +4 -4
  175. package/src/runtime/shared/pristine-execution.mjs +1 -1
  176. package/src/runtime/shared/process-lifecycle.mjs +0 -1
  177. package/src/runtime/shared/process-shutdown.mjs +1 -1
  178. package/src/runtime/shared/resource-admission.mjs +2 -2
  179. package/src/runtime/shared/schedule-session-run.mjs +1 -1
  180. package/src/runtime/shared/schedules-db.mjs +1 -1
  181. package/src/runtime/shared/service-discovery.mjs +2 -2
  182. package/src/runtime/shared/staged-update.mjs +6 -6
  183. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  184. package/src/runtime/shared/tool-primitives.mjs +1 -1
  185. package/src/runtime/shared/tool-surface.mjs +2 -2
  186. package/src/runtime/shared/update-checker.mjs +1 -1
  187. package/src/runtime/shared/user-cwd.mjs +3 -3
  188. package/src/runtime/shared/user-data-guard.mjs +4 -4
  189. package/src/runtime/shared/workspace-router.mjs +5 -5
  190. package/src/session-runtime/config-helpers.mjs +2 -3
  191. package/src/session-runtime/config-lifecycle.mjs +1 -1
  192. package/src/session-runtime/hitch-profile.mjs +1 -1
  193. package/src/session-runtime/lifecycle-api.mjs +6 -4
  194. package/src/session-runtime/model-recency.mjs +5 -5
  195. package/src/session-runtime/plugin-mcp.mjs +1 -1
  196. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  197. package/src/session-runtime/session-text.mjs +4 -4
  198. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  199. package/src/session-runtime/tool-catalog.mjs +6 -483
  200. package/src/session-runtime/tool-defs.mjs +1 -1
  201. package/src/standalone/agent-task-status.mjs +1 -1
  202. package/src/standalone/agent-tool/helpers.mjs +2 -2
  203. package/src/standalone/channel-admin.mjs +4 -4
  204. package/src/standalone/explore-tool.mjs +3 -3
  205. package/src/standalone/hook-bus/config.mjs +1 -1
  206. package/src/standalone/hook-bus/constants.mjs +1 -1
  207. package/src/standalone/hook-bus/rules.mjs +1 -1
  208. package/src/standalone/projects.mjs +1 -1
  209. package/src/standalone/provider-admin.mjs +2 -2
  210. package/src/tui/app/app-format.mjs +2 -2
  211. package/src/tui/app/clipboard.mjs +3 -3
  212. package/src/tui/app/input-parsers.mjs +1 -1
  213. package/src/tui/app/text-layout.mjs +1 -1
  214. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  215. package/src/tui/app/transcript-window.mjs +5 -347
  216. package/src/tui/app/use-mouse-input.mjs +1 -1
  217. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  218. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  219. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  220. package/src/tui/components/tool-output-format.mjs +7 -7
  221. package/src/tui/display-width.mjs +4 -4
  222. package/src/tui/dist/index.mjs +63 -97
  223. package/src/tui/engine/agent-envelope.mjs +5 -5
  224. package/src/tui/engine/notification-plan.mjs +1 -1
  225. package/src/tui/engine/queue-helpers.mjs +3 -3
  226. package/src/tui/engine/render-timing.mjs +4 -5
  227. package/src/tui/engine/session-api-ext.mjs +3 -3
  228. package/src/tui/engine/tool-result-status.mjs +5 -5
  229. package/src/tui/engine/tool-result-text.mjs +1 -1
  230. package/src/tui/engine/transcript-spill.mjs +585 -0
  231. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  232. package/src/tui/engine.mjs +2 -474
  233. package/src/tui/lib/voice-setup.mjs +1 -1
  234. package/src/tui/markdown/format-token.mjs +1 -1
  235. package/src/tui/markdown/render-ansi.mjs +1 -1
  236. package/src/tui/markdown/table-layout.mjs +4 -4
  237. package/src/tui/paste-attachments.mjs +1 -1
  238. package/src/tui/prompt-history-store.mjs +3 -3
  239. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  240. package/src/tui/theme.mjs +1 -1
  241. package/src/tui/transcript-tool-failures.mjs +2 -2
  242. package/src/ui/ansi.mjs +0 -9
  243. package/src/ui/statusline-format.mjs +2 -4
  244. package/scripts/_bench-cwc.json +0 -20
  245. package/scripts/_jitter-fuzz.jsx +0 -42
  246. package/scripts/_jitter-fuzz2.jsx +0 -30
  247. package/scripts/_jitter-probe.jsx +0 -35
  248. package/scripts/_jp2.jsx +0 -16
  249. package/scripts/_smoke_wd.mjs +0 -7
  250. package/scripts/abort-recovery-test.mjs +0 -175
  251. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  252. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  253. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  254. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  255. package/scripts/agent-loop-policy-test.mjs +0 -37
  256. package/scripts/agent-model-liveness-test.mjs +0 -763
  257. package/scripts/agent-parallel-smoke.mjs +0 -435
  258. package/scripts/agent-route-batch-test.mjs +0 -40
  259. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  260. package/scripts/agent-terminal-reap-test.mjs +0 -252
  261. package/scripts/agent-trace-io-test.mjs +0 -133
  262. package/scripts/ansi-color-capability-test.mjs +0 -90
  263. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  264. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  265. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  266. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  267. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  268. package/scripts/arg-guard-test.mjs +0 -93
  269. package/scripts/async-notify-settlement-test.mjs +0 -99
  270. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  271. package/scripts/background-task-meta-smoke.mjs +0 -38
  272. package/scripts/bench-run.mjs +0 -508
  273. package/scripts/boot-smoke.mjs +0 -137
  274. package/scripts/build-runtime-windows.ps1 +0 -242
  275. package/scripts/channel-daemon-smoke.mjs +0 -1103
  276. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  277. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  278. package/scripts/code-graph-dispatch-test.mjs +0 -96
  279. package/scripts/code-graph-root-federation-test.mjs +0 -273
  280. package/scripts/compact-active-turn-test.mjs +0 -68
  281. package/scripts/compact-file-reattach-test.mjs +0 -88
  282. package/scripts/compact-pressure-test.mjs +0 -788
  283. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  284. package/scripts/compact-recall-digest-test.mjs +0 -57
  285. package/scripts/compact-smoke.mjs +0 -1017
  286. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  287. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  288. package/scripts/context-mcp-metering-test.mjs +0 -1406
  289. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  290. package/scripts/deferred-tool-loading-test.mjs +0 -250
  291. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  292. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  293. package/scripts/embedding-worker-exit-test.mjs +0 -76
  294. package/scripts/execution-completion-dedup-test.mjs +0 -205
  295. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  296. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  297. package/scripts/explore-bench-tmp.mjs +0 -36
  298. package/scripts/explore-bench.mjs +0 -248
  299. package/scripts/explore-prompt-policy-test.mjs +0 -256
  300. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  301. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  302. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  303. package/scripts/freevar-smoke.mjs +0 -98
  304. package/scripts/gemini-provider-test.mjs +0 -1448
  305. package/scripts/generate-oc-icons.mjs +0 -11
  306. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  307. package/scripts/headless-pristine-execution-test.mjs +0 -614
  308. package/scripts/hook-bus-test.mjs +0 -552
  309. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  310. package/scripts/internal-comms-bench-test.mjs +0 -226
  311. package/scripts/internal-comms-bench.mjs +0 -853
  312. package/scripts/internal-comms-smoke.mjs +0 -277
  313. package/scripts/internal-tools-normalization-test.mjs +0 -62
  314. package/scripts/interrupted-turn-history-test.mjs +0 -399
  315. package/scripts/lead-workflow-smoke.mjs +0 -592
  316. package/scripts/lifecycle-api-test.mjs +0 -137
  317. package/scripts/live-share-test.mjs +0 -148
  318. package/scripts/live-worker-smoke.mjs +0 -333
  319. package/scripts/log-writer-guard-smoke.mjs +0 -131
  320. package/scripts/maintenance-default-routes-test.mjs +0 -164
  321. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  322. package/scripts/max-output-recovery-test.mjs +0 -368
  323. package/scripts/mcp-client-normalization-test.mjs +0 -45
  324. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  325. package/scripts/memory-core-input-test.mjs +0 -167
  326. package/scripts/memory-cycle-routing-test.mjs +0 -111
  327. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  328. package/scripts/memory-pg-recovery-test.mjs +0 -59
  329. package/scripts/memory-rule-contract-test.mjs +0 -93
  330. package/scripts/model-list-sanitize-test.mjs +0 -37
  331. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  332. package/scripts/native-edit-wire-test.mjs +0 -152
  333. package/scripts/notify-completion-mirror-test.mjs +0 -73
  334. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  335. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  336. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  337. package/scripts/output-style-bench.mjs +0 -292
  338. package/scripts/output-style-smoke.mjs +0 -145
  339. package/scripts/parent-abort-link-test.mjs +0 -90
  340. package/scripts/patch-binary-cache-test.mjs +0 -181
  341. package/scripts/path-suffix-test.mjs +0 -57
  342. package/scripts/pending-completion-drop-test.mjs +0 -239
  343. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  344. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  345. package/scripts/process-lifecycle-test.mjs +0 -461
  346. package/scripts/prompt-immediate-render-test.mjs +0 -89
  347. package/scripts/prompt-input-parity-test.mjs +0 -145
  348. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  349. package/scripts/provider-contract-test.mjs +0 -632
  350. package/scripts/provider-stream-stall-test.mjs +0 -276
  351. package/scripts/provider-toolcall-test.mjs +0 -3063
  352. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  353. package/scripts/recall-bench.mjs +0 -359
  354. package/scripts/resource-admission-test.mjs +0 -792
  355. package/scripts/result-classification-test.mjs +0 -75
  356. package/scripts/rg-runner-test.mjs +0 -240
  357. package/scripts/routing-corpus-test.mjs +0 -349
  358. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  359. package/scripts/session-bench-cache-break-test.mjs +0 -102
  360. package/scripts/session-bench.mjs +0 -1708
  361. package/scripts/session-context-bench.mjs +0 -344
  362. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  363. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  364. package/scripts/session-ingest-smoke.mjs +0 -177
  365. package/scripts/session-orphan-sweep-test.mjs +0 -109
  366. package/scripts/set-effort-config-test.mjs +0 -41
  367. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  368. package/scripts/shell-hardening-test.mjs +0 -209
  369. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  370. package/scripts/ship-mode-test.mjs +0 -98
  371. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  372. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  373. package/scripts/smoke-loop-report.mjs +0 -221
  374. package/scripts/smoke-loop.mjs +0 -202
  375. package/scripts/smoke-runtime-negative.ps1 +0 -106
  376. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  377. package/scripts/steering-drain-buckets-test.mjs +0 -467
  378. package/scripts/steering-fold-provenance-test.mjs +0 -71
  379. package/scripts/streaming-tail-window-test.mjs +0 -175
  380. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  381. package/scripts/task-bench.mjs +0 -205
  382. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  383. package/scripts/termio-input-smoke.mjs +0 -199
  384. package/scripts/tool-result-hook-test.mjs +0 -48
  385. package/scripts/tool-smoke.mjs +0 -2453
  386. package/scripts/tool-tui-presentation-test.mjs +0 -286
  387. package/scripts/toolcall-args-test.mjs +0 -239
  388. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  389. package/scripts/tui-background-failure-smoke.mjs +0 -73
  390. package/scripts/tui-perf-run.ps1 +0 -26
  391. package/scripts/tui-render-smoke.mjs +0 -90
  392. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  393. package/scripts/tui-runtime-load-bench.mjs +0 -50
  394. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  395. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  396. package/scripts/tui-transcript-perf-test.mjs +0 -934
  397. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  398. package/scripts/verify-release-assets-test.mjs +0 -281
  399. package/scripts/web-fetch-routing-test.mjs +0 -158
  400. package/scripts/webhook-smoke.mjs +0 -201
  401. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  402. package/scripts/worker-notify-rejection-test.mjs +0 -51
  403. package/scripts/write-backpressure-test.mjs +0 -147
@@ -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
+ }