mixdog 0.7.18 → 0.8.1

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 (847) hide show
  1. package/README.md +37 -331
  2. package/package.json +67 -99
  3. package/scripts/boot-smoke.mjs +94 -0
  4. package/scripts/build-tui.mjs +52 -0
  5. package/scripts/compact-smoke.mjs +199 -0
  6. package/scripts/lead-workflow-smoke.mjs +598 -0
  7. package/scripts/live-worker-smoke.mjs +239 -0
  8. package/scripts/output-style-smoke.mjs +101 -0
  9. package/scripts/session-context-bench.mjs +172 -0
  10. package/scripts/smoke-loop-report.mjs +221 -0
  11. package/scripts/smoke-loop.mjs +201 -0
  12. package/scripts/smoke.mjs +113 -0
  13. package/scripts/tool-failures.mjs +143 -0
  14. package/scripts/tool-smoke.mjs +452 -0
  15. package/src/agents/debugger/AGENT.md +3 -0
  16. package/src/agents/debugger/agent.json +6 -0
  17. package/src/agents/explore/AGENT.md +4 -0
  18. package/src/agents/explore/agent.json +6 -0
  19. package/src/agents/heavy-worker/AGENT.md +3 -0
  20. package/src/agents/heavy-worker/agent.json +6 -0
  21. package/src/agents/maintainer/AGENT.md +3 -0
  22. package/src/agents/maintainer/agent.json +6 -0
  23. package/src/agents/reviewer/AGENT.md +3 -0
  24. package/src/agents/reviewer/agent.json +6 -0
  25. package/src/agents/scheduler-task.md +3 -0
  26. package/src/agents/web-researcher/AGENT.md +3 -0
  27. package/src/agents/web-researcher/agent.json +6 -0
  28. package/src/agents/webhook-handler.md +3 -0
  29. package/src/agents/worker/AGENT.md +3 -0
  30. package/src/agents/worker/agent.json +6 -0
  31. package/src/app.mjs +90 -0
  32. package/src/cli.mjs +11 -0
  33. package/src/defaults/hidden-roles.json +72 -0
  34. package/src/defaults/mixdog-config.template.json +15 -0
  35. package/src/hooks/lib/permission-evaluator.cjs +488 -0
  36. package/src/hooks/lib/settings-loader.cjs +112 -0
  37. package/src/lib/keychain-cjs.cjs +332 -0
  38. package/src/lib/plugin-paths.cjs +28 -0
  39. package/src/lib/rules-builder.cjs +315 -0
  40. package/src/mixdog-session-runtime.mjs +3813 -0
  41. package/src/output-styles/default.md +38 -0
  42. package/src/output-styles/extreme-simple.md +17 -0
  43. package/src/output-styles/simple.md +17 -0
  44. package/src/repl.mjs +330 -0
  45. package/src/rules/bridge/00-common.md +5 -0
  46. package/src/rules/bridge/20-skip-protocol.md +11 -0
  47. package/src/rules/bridge/30-explorer.md +4 -0
  48. package/src/rules/bridge/40-cycle1-agent.md +28 -0
  49. package/src/rules/bridge/41-cycle2-agent.md +59 -0
  50. package/src/rules/lead/00-tool-lead.md +5 -0
  51. package/src/rules/lead/01-general.md +5 -0
  52. package/src/rules/lead/02-channels.md +3 -0
  53. package/src/rules/lead/04-workflow.md +12 -0
  54. package/src/rules/shared/00-language.md +3 -0
  55. package/src/rules/shared/01-tool.md +3 -0
  56. package/src/runtime/agent/orchestrator/bridge-trace.mjs +814 -0
  57. package/src/runtime/agent/orchestrator/cache-mtime.mjs +60 -0
  58. package/src/runtime/agent/orchestrator/config.mjs +446 -0
  59. package/src/runtime/agent/orchestrator/context/collect.mjs +796 -0
  60. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +417 -0
  61. package/src/runtime/agent/orchestrator/internal-roles.mjs +188 -0
  62. package/src/runtime/agent/orchestrator/internal-tools.mjs +88 -0
  63. package/src/runtime/agent/orchestrator/mcp/client.mjs +345 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2104 -0
  65. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +784 -0
  66. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +341 -0
  67. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1679 -0
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +959 -0
  69. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +213 -0
  70. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +38 -0
  71. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +471 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +615 -0
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +808 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +1719 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2587 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1953 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +136 -0
  78. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +317 -0
  79. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +109 -0
  80. package/src/runtime/agent/orchestrator/providers/registry.mjs +247 -0
  81. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +332 -0
  82. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +11 -0
  83. package/src/runtime/agent/orchestrator/providers/trace-utils.mjs +50 -0
  84. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  85. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  86. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +318 -0
  87. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +367 -0
  88. package/src/runtime/agent/orchestrator/session/compact.mjs +882 -0
  89. package/src/runtime/agent/orchestrator/session/context-utils.mjs +233 -0
  90. package/src/runtime/agent/orchestrator/session/loop.mjs +2269 -0
  91. package/src/runtime/agent/orchestrator/session/manager.mjs +2972 -0
  92. package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
  93. package/src/runtime/agent/orchestrator/session/store.mjs +870 -0
  94. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  95. package/src/runtime/agent/orchestrator/smart-bridge/bridge-llm.mjs +339 -0
  96. package/src/runtime/agent/orchestrator/smart-bridge/cache-strategy.mjs +419 -0
  97. package/src/runtime/agent/orchestrator/stall-policy.mjs +227 -0
  98. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +235 -0
  99. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +723 -0
  100. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +389 -0
  102. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +637 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +165 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +104 -0
  105. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +194 -0
  106. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +596 -0
  107. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +110 -0
  108. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +153 -0
  109. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  110. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +189 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +731 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +168 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +465 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +160 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +982 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1087 -0
  118. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +231 -0
  119. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +223 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin.mjs +478 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  122. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4102 -0
  123. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +154 -0
  125. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +26 -0
  126. package/src/runtime/agent/orchestrator/tools/patch-binary-fetcher.mjs +143 -0
  127. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +18 -0
  128. package/src/runtime/agent/orchestrator/tools/patch.mjs +2772 -0
  129. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +114 -0
  130. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +880 -0
  131. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +312 -0
  132. package/src/runtime/channels/backends/discord.mjs +781 -0
  133. package/src/runtime/channels/data/voice-runtime-manifest.json +138 -0
  134. package/src/runtime/channels/index.mjs +3309 -0
  135. package/src/runtime/channels/lib/config.mjs +285 -0
  136. package/src/runtime/channels/lib/drop-trace.mjs +71 -0
  137. package/src/runtime/channels/lib/event-pipeline.mjs +81 -0
  138. package/src/runtime/channels/lib/holidays.mjs +138 -0
  139. package/src/runtime/channels/lib/hook-pipe-server.mjs +671 -0
  140. package/src/runtime/channels/lib/output-forwarder.mjs +765 -0
  141. package/src/runtime/channels/lib/runtime-paths.mjs +497 -0
  142. package/src/runtime/channels/lib/scheduler.mjs +710 -0
  143. package/src/runtime/channels/lib/session-discovery.mjs +102 -0
  144. package/src/runtime/channels/lib/state-file.mjs +68 -0
  145. package/src/runtime/channels/lib/status-snapshot.mjs +224 -0
  146. package/src/runtime/channels/lib/tool-format.mjs +122 -0
  147. package/src/runtime/channels/lib/transcript-discovery.mjs +195 -0
  148. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +734 -0
  149. package/src/runtime/channels/lib/webhook.mjs +1288 -0
  150. package/src/runtime/channels/tool-defs.mjs +177 -0
  151. package/src/runtime/lib/keychain-cjs.cjs +289 -0
  152. package/src/runtime/memory/data/runtime-manifest.json +40 -0
  153. package/src/runtime/memory/index.mjs +3706 -0
  154. package/src/runtime/memory/lib/core-memory-store.mjs +336 -0
  155. package/src/runtime/memory/lib/embedding-provider.mjs +275 -0
  156. package/src/runtime/memory/lib/embedding-worker.mjs +331 -0
  157. package/src/runtime/memory/lib/memory-cycle-requests.mjs +276 -0
  158. package/src/runtime/memory/lib/memory-cycle1.mjs +783 -0
  159. package/src/runtime/memory/lib/memory-cycle2.mjs +1389 -0
  160. package/src/runtime/memory/lib/memory-cycle3.mjs +646 -0
  161. package/src/runtime/memory/lib/memory-embed.mjs +300 -0
  162. package/src/runtime/memory/lib/memory-ops-policy.mjs +149 -0
  163. package/src/runtime/memory/lib/memory-recall-store.mjs +644 -0
  164. package/src/runtime/memory/lib/memory.mjs +418 -0
  165. package/src/runtime/memory/lib/pg/adapter.mjs +328 -0
  166. package/src/runtime/memory/lib/pg/process.mjs +366 -0
  167. package/src/runtime/memory/lib/pg/supervisor.mjs +495 -0
  168. package/src/runtime/memory/lib/runtime-fetcher.mjs +464 -0
  169. package/src/runtime/memory/lib/trace-store.mjs +734 -0
  170. package/src/runtime/memory/tool-defs.mjs +79 -0
  171. package/src/runtime/search/index.mjs +925 -0
  172. package/src/runtime/search/lib/config.mjs +61 -0
  173. package/src/runtime/search/lib/web-tools.mjs +1278 -0
  174. package/src/runtime/search/tool-defs.mjs +64 -0
  175. package/src/runtime/shared/atomic-file.mjs +435 -0
  176. package/src/runtime/shared/background-tasks.mjs +376 -0
  177. package/src/runtime/shared/child-guardian.mjs +98 -0
  178. package/src/runtime/shared/config.mjs +393 -0
  179. package/src/runtime/shared/err-text.mjs +121 -0
  180. package/src/runtime/shared/launcher-control.mjs +259 -0
  181. package/src/runtime/shared/llm/http-agent.mjs +129 -0
  182. package/src/runtime/shared/open-url.mjs +37 -0
  183. package/src/runtime/shared/plugin-paths.mjs +25 -0
  184. package/src/runtime/shared/process-shutdown.mjs +147 -0
  185. package/src/runtime/shared/schedules-store.mjs +70 -0
  186. package/src/runtime/shared/tool-execution-contract.mjs +104 -0
  187. package/src/runtime/shared/tool-surface.mjs +947 -0
  188. package/src/runtime/shared/user-cwd.mjs +221 -0
  189. package/src/runtime/shared/user-data-guard.mjs +232 -0
  190. package/src/runtime/shared/workspace-router.mjs +259 -0
  191. package/src/standalone/bridge-tool.mjs +1414 -0
  192. package/src/standalone/channel-admin.mjs +366 -0
  193. package/src/standalone/channel-worker-preload.cjs +3 -0
  194. package/src/standalone/channel-worker.mjs +353 -0
  195. package/src/standalone/explore-tool.mjs +233 -0
  196. package/src/standalone/hook-bus.mjs +246 -0
  197. package/src/standalone/plugin-admin.mjs +247 -0
  198. package/src/standalone/provider-admin.mjs +338 -0
  199. package/src/standalone/seeds.mjs +94 -0
  200. package/src/standalone/usage-dashboard.mjs +510 -0
  201. package/src/tui/App.jsx +5446 -0
  202. package/src/tui/components/AnsiText.jsx +199 -0
  203. package/src/tui/components/ContextPanel.jsx +265 -0
  204. package/src/tui/components/Markdown.jsx +205 -0
  205. package/src/tui/components/MarkdownTable.jsx +204 -0
  206. package/src/tui/components/Message.jsx +103 -0
  207. package/src/tui/components/Picker.jsx +317 -0
  208. package/src/tui/components/PromptInput.jsx +584 -0
  209. package/src/tui/components/QueuedCommands.jsx +47 -0
  210. package/src/tui/components/SlashCommandPalette.jsx +114 -0
  211. package/src/tui/components/Spinner.jsx +317 -0
  212. package/src/tui/components/StatusLine.jsx +87 -0
  213. package/src/tui/components/TextEntryPanel.jsx +323 -0
  214. package/src/tui/components/ToolExecution.jsx +791 -0
  215. package/src/tui/components/TurnDone.jsx +78 -0
  216. package/src/tui/components/UsagePanel.jsx +331 -0
  217. package/src/tui/dist/index.mjs +12420 -0
  218. package/src/tui/engine.mjs +2410 -0
  219. package/src/tui/figures.mjs +50 -0
  220. package/src/tui/hooks/useEngine.mjs +16 -0
  221. package/src/tui/index.jsx +254 -0
  222. package/src/tui/input-editing.mjs +242 -0
  223. package/src/tui/markdown/format-token.mjs +194 -0
  224. package/src/tui/paste-attachments.mjs +198 -0
  225. package/src/tui/runtime/shared/process-shutdown.mjs +1 -0
  226. package/src/tui/spinner-verbs.mjs +45 -0
  227. package/src/tui/theme.mjs +67 -0
  228. package/src/tui/time-format.mjs +53 -0
  229. package/src/ui/ansi.mjs +115 -0
  230. package/src/ui/markdown.mjs +195 -0
  231. package/src/ui/statusline.mjs +730 -0
  232. package/src/ui/tool-card.mjs +99 -0
  233. package/src/vendor/statusline/bin/statusline-lib.mjs +805 -0
  234. package/src/vendor/statusline/bin/statusline-route.mjs +596 -0
  235. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +285 -0
  236. package/src/vendor/statusline/src/gateway/claude-current.mjs +320 -0
  237. package/src/vendor/statusline/src/gateway/route-meta.mjs +753 -0
  238. package/src/vendor/statusline/src/gateway/session-routes.mjs +244 -0
  239. package/src/workflows/default/WORKFLOW.md +7 -0
  240. package/src/workflows/default/workflow.json +14 -0
  241. package/vendor/ink/build/ansi-tokenizer.d.ts +38 -0
  242. package/vendor/ink/build/ansi-tokenizer.js +316 -0
  243. package/vendor/ink/build/ansi-tokenizer.js.map +1 -0
  244. package/vendor/ink/build/colorize.d.ts +3 -0
  245. package/vendor/ink/build/colorize.js +48 -0
  246. package/vendor/ink/build/colorize.js.map +1 -0
  247. package/vendor/ink/build/components/AccessibilityContext.d.ts +3 -0
  248. package/vendor/ink/build/components/AccessibilityContext.js +5 -0
  249. package/vendor/ink/build/components/AccessibilityContext.js.map +1 -0
  250. package/vendor/ink/build/components/AnimationContext.d.ts +9 -0
  251. package/vendor/ink/build/components/AnimationContext.js +13 -0
  252. package/vendor/ink/build/components/AnimationContext.js.map +1 -0
  253. package/vendor/ink/build/components/App.d.ts +24 -0
  254. package/vendor/ink/build/components/App.js +554 -0
  255. package/vendor/ink/build/components/App.js.map +1 -0
  256. package/vendor/ink/build/components/AppContext.d.ts +80 -0
  257. package/vendor/ink/build/components/AppContext.js +25 -0
  258. package/vendor/ink/build/components/AppContext.js.map +1 -0
  259. package/vendor/ink/build/components/BackgroundContext.d.ts +4 -0
  260. package/vendor/ink/build/components/BackgroundContext.js +3 -0
  261. package/vendor/ink/build/components/BackgroundContext.js.map +1 -0
  262. package/vendor/ink/build/components/Box.d.ts +130 -0
  263. package/vendor/ink/build/components/Box.js +34 -0
  264. package/vendor/ink/build/components/Box.js.map +1 -0
  265. package/vendor/ink/build/components/CursorContext.d.ts +11 -0
  266. package/vendor/ink/build/components/CursorContext.js +8 -0
  267. package/vendor/ink/build/components/CursorContext.js.map +1 -0
  268. package/vendor/ink/build/components/ErrorBoundary.d.ts +18 -0
  269. package/vendor/ink/build/components/ErrorBoundary.js +23 -0
  270. package/vendor/ink/build/components/ErrorBoundary.js.map +1 -0
  271. package/vendor/ink/build/components/ErrorOverview.d.ts +6 -0
  272. package/vendor/ink/build/components/ErrorOverview.js +90 -0
  273. package/vendor/ink/build/components/ErrorOverview.js.map +1 -0
  274. package/vendor/ink/build/components/FocusContext.d.ts +16 -0
  275. package/vendor/ink/build/components/FocusContext.js +17 -0
  276. package/vendor/ink/build/components/FocusContext.js.map +1 -0
  277. package/vendor/ink/build/components/Newline.d.ts +13 -0
  278. package/vendor/ink/build/components/Newline.js +8 -0
  279. package/vendor/ink/build/components/Newline.js.map +1 -0
  280. package/vendor/ink/build/components/Spacer.d.ts +7 -0
  281. package/vendor/ink/build/components/Spacer.js +11 -0
  282. package/vendor/ink/build/components/Spacer.js.map +1 -0
  283. package/vendor/ink/build/components/Static.d.ts +24 -0
  284. package/vendor/ink/build/components/Static.js +28 -0
  285. package/vendor/ink/build/components/Static.js.map +1 -0
  286. package/vendor/ink/build/components/StderrContext.d.ts +15 -0
  287. package/vendor/ink/build/components/StderrContext.js +13 -0
  288. package/vendor/ink/build/components/StderrContext.js.map +1 -0
  289. package/vendor/ink/build/components/StdinContext.d.ts +28 -0
  290. package/vendor/ink/build/components/StdinContext.js +20 -0
  291. package/vendor/ink/build/components/StdinContext.js.map +1 -0
  292. package/vendor/ink/build/components/StdoutContext.d.ts +15 -0
  293. package/vendor/ink/build/components/StdoutContext.js +13 -0
  294. package/vendor/ink/build/components/StdoutContext.js.map +1 -0
  295. package/vendor/ink/build/components/Text.d.ts +55 -0
  296. package/vendor/ink/build/components/Text.js +50 -0
  297. package/vendor/ink/build/components/Text.js.map +1 -0
  298. package/vendor/ink/build/components/Transform.d.ts +16 -0
  299. package/vendor/ink/build/components/Transform.js +15 -0
  300. package/vendor/ink/build/components/Transform.js.map +1 -0
  301. package/vendor/ink/build/cursor-helpers.d.ts +39 -0
  302. package/vendor/ink/build/cursor-helpers.js +62 -0
  303. package/vendor/ink/build/cursor-helpers.js.map +1 -0
  304. package/vendor/ink/build/devtools-window-polyfill.d.ts +1 -0
  305. package/vendor/ink/build/devtools-window-polyfill.js +68 -0
  306. package/vendor/ink/build/devtools-window-polyfill.js.map +1 -0
  307. package/vendor/ink/build/devtools.d.ts +1 -0
  308. package/vendor/ink/build/devtools.js +36 -0
  309. package/vendor/ink/build/devtools.js.map +1 -0
  310. package/vendor/ink/build/dom.d.ts +62 -0
  311. package/vendor/ink/build/dom.js +143 -0
  312. package/vendor/ink/build/dom.js.map +1 -0
  313. package/vendor/ink/build/get-max-width.d.ts +3 -0
  314. package/vendor/ink/build/get-max-width.js +10 -0
  315. package/vendor/ink/build/get-max-width.js.map +1 -0
  316. package/vendor/ink/build/hooks/use-animation.d.ts +49 -0
  317. package/vendor/ink/build/hooks/use-animation.js +87 -0
  318. package/vendor/ink/build/hooks/use-animation.js.map +1 -0
  319. package/vendor/ink/build/hooks/use-app.d.ts +5 -0
  320. package/vendor/ink/build/hooks/use-app.js +8 -0
  321. package/vendor/ink/build/hooks/use-app.js.map +1 -0
  322. package/vendor/ink/build/hooks/use-box-metrics.d.ts +59 -0
  323. package/vendor/ink/build/hooks/use-box-metrics.js +81 -0
  324. package/vendor/ink/build/hooks/use-box-metrics.js.map +1 -0
  325. package/vendor/ink/build/hooks/use-cursor.d.ts +12 -0
  326. package/vendor/ink/build/hooks/use-cursor.js +29 -0
  327. package/vendor/ink/build/hooks/use-cursor.js.map +1 -0
  328. package/vendor/ink/build/hooks/use-focus-manager.d.ts +43 -0
  329. package/vendor/ink/build/hooks/use-focus-manager.js +18 -0
  330. package/vendor/ink/build/hooks/use-focus-manager.js.map +1 -0
  331. package/vendor/ink/build/hooks/use-focus.d.ts +30 -0
  332. package/vendor/ink/build/hooks/use-focus.js +43 -0
  333. package/vendor/ink/build/hooks/use-focus.js.map +1 -0
  334. package/vendor/ink/build/hooks/use-input.d.ts +132 -0
  335. package/vendor/ink/build/hooks/use-input.js +126 -0
  336. package/vendor/ink/build/hooks/use-input.js.map +1 -0
  337. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.d.ts +6 -0
  338. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js +12 -0
  339. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -0
  340. package/vendor/ink/build/hooks/use-paste.d.ts +35 -0
  341. package/vendor/ink/build/hooks/use-paste.js +62 -0
  342. package/vendor/ink/build/hooks/use-paste.js.map +1 -0
  343. package/vendor/ink/build/hooks/use-stderr.d.ts +5 -0
  344. package/vendor/ink/build/hooks/use-stderr.js +8 -0
  345. package/vendor/ink/build/hooks/use-stderr.js.map +1 -0
  346. package/vendor/ink/build/hooks/use-stdin.d.ts +7 -0
  347. package/vendor/ink/build/hooks/use-stdin.js +9 -0
  348. package/vendor/ink/build/hooks/use-stdin.js.map +1 -0
  349. package/vendor/ink/build/hooks/use-stdout.d.ts +5 -0
  350. package/vendor/ink/build/hooks/use-stdout.js +8 -0
  351. package/vendor/ink/build/hooks/use-stdout.js.map +1 -0
  352. package/vendor/ink/build/hooks/use-window-size.d.ts +18 -0
  353. package/vendor/ink/build/hooks/use-window-size.js +22 -0
  354. package/vendor/ink/build/hooks/use-window-size.js.map +1 -0
  355. package/vendor/ink/build/index.d.ts +42 -0
  356. package/vendor/ink/build/index.js +24 -0
  357. package/vendor/ink/build/index.js.map +1 -0
  358. package/vendor/ink/build/ink.d.ts +146 -0
  359. package/vendor/ink/build/ink.js +1022 -0
  360. package/vendor/ink/build/ink.js.map +1 -0
  361. package/vendor/ink/build/input-parser.d.ts +10 -0
  362. package/vendor/ink/build/input-parser.js +194 -0
  363. package/vendor/ink/build/input-parser.js.map +1 -0
  364. package/vendor/ink/build/instances.d.ts +3 -0
  365. package/vendor/ink/build/instances.js +8 -0
  366. package/vendor/ink/build/instances.js.map +1 -0
  367. package/vendor/ink/build/kitty-keyboard.d.ts +23 -0
  368. package/vendor/ink/build/kitty-keyboard.js +32 -0
  369. package/vendor/ink/build/kitty-keyboard.js.map +1 -0
  370. package/vendor/ink/build/log-update.d.ts +20 -0
  371. package/vendor/ink/build/log-update.js +261 -0
  372. package/vendor/ink/build/log-update.js.map +1 -0
  373. package/vendor/ink/build/measure-element.d.ts +20 -0
  374. package/vendor/ink/build/measure-element.js +13 -0
  375. package/vendor/ink/build/measure-element.js.map +1 -0
  376. package/vendor/ink/build/measure-text.d.ts +6 -0
  377. package/vendor/ink/build/measure-text.js +21 -0
  378. package/vendor/ink/build/measure-text.js.map +1 -0
  379. package/vendor/ink/build/output.d.ts +35 -0
  380. package/vendor/ink/build/output.js +328 -0
  381. package/vendor/ink/build/output.js.map +1 -0
  382. package/vendor/ink/build/parse-keypress.d.ts +20 -0
  383. package/vendor/ink/build/parse-keypress.js +495 -0
  384. package/vendor/ink/build/parse-keypress.js.map +1 -0
  385. package/vendor/ink/build/reconciler.d.ts +4 -0
  386. package/vendor/ink/build/reconciler.js +306 -0
  387. package/vendor/ink/build/reconciler.js.map +1 -0
  388. package/vendor/ink/build/render-background.d.ts +4 -0
  389. package/vendor/ink/build/render-background.js +25 -0
  390. package/vendor/ink/build/render-background.js.map +1 -0
  391. package/vendor/ink/build/render-border.d.ts +4 -0
  392. package/vendor/ink/build/render-border.js +84 -0
  393. package/vendor/ink/build/render-border.js.map +1 -0
  394. package/vendor/ink/build/render-node-to-output.d.ts +14 -0
  395. package/vendor/ink/build/render-node-to-output.js +162 -0
  396. package/vendor/ink/build/render-node-to-output.js.map +1 -0
  397. package/vendor/ink/build/render-to-string.d.ts +38 -0
  398. package/vendor/ink/build/render-to-string.js +116 -0
  399. package/vendor/ink/build/render-to-string.js.map +1 -0
  400. package/vendor/ink/build/render.d.ts +176 -0
  401. package/vendor/ink/build/render.js +71 -0
  402. package/vendor/ink/build/render.js.map +1 -0
  403. package/vendor/ink/build/renderer.d.ts +8 -0
  404. package/vendor/ink/build/renderer.js +64 -0
  405. package/vendor/ink/build/renderer.js.map +1 -0
  406. package/vendor/ink/build/sanitize-ansi.d.ts +2 -0
  407. package/vendor/ink/build/sanitize-ansi.js +27 -0
  408. package/vendor/ink/build/sanitize-ansi.js.map +1 -0
  409. package/vendor/ink/build/squash-text-nodes.d.ts +3 -0
  410. package/vendor/ink/build/squash-text-nodes.js +36 -0
  411. package/vendor/ink/build/squash-text-nodes.js.map +1 -0
  412. package/vendor/ink/build/styles.d.ts +302 -0
  413. package/vendor/ink/build/styles.js +303 -0
  414. package/vendor/ink/build/styles.js.map +1 -0
  415. package/vendor/ink/build/utils.d.ts +9 -0
  416. package/vendor/ink/build/utils.js +19 -0
  417. package/vendor/ink/build/utils.js.map +1 -0
  418. package/vendor/ink/build/wrap-text.d.ts +3 -0
  419. package/vendor/ink/build/wrap-text.js +38 -0
  420. package/vendor/ink/build/wrap-text.js.map +1 -0
  421. package/vendor/ink/build/write-synchronized.d.ts +4 -0
  422. package/vendor/ink/build/write-synchronized.js +9 -0
  423. package/vendor/ink/build/write-synchronized.js.map +1 -0
  424. package/vendor/ink/license +10 -0
  425. package/vendor/ink/package.json +137 -0
  426. package/.claude-plugin/marketplace.json +0 -34
  427. package/.claude-plugin/plugin.json +0 -20
  428. package/.gitattributes +0 -34
  429. package/.mcp.json +0 -14
  430. package/ARCHITECTURE.md +0 -77
  431. package/CHANGELOG.md +0 -30
  432. package/CONTRIBUTING.md +0 -45
  433. package/DATA-FLOW.md +0 -79
  434. package/LICENSE +0 -21
  435. package/SECURITY.md +0 -138
  436. package/UNINSTALL.md +0 -115
  437. package/agents/maintenance.md +0 -5
  438. package/agents/memory-classification.md +0 -30
  439. package/agents/scheduler-task.md +0 -18
  440. package/agents/webhook-handler.md +0 -27
  441. package/agents/worker.md +0 -24
  442. package/bin/bridge +0 -133
  443. package/bin/statusline-launcher.mjs +0 -82
  444. package/bin/statusline-lib.mjs +0 -581
  445. package/bin/statusline-route.mjs +0 -273
  446. package/bin/statusline.mjs +0 -638
  447. package/bun.lock +0 -927
  448. package/commands/config.md +0 -16
  449. package/commands/doctor.md +0 -13
  450. package/commands/model.md +0 -61
  451. package/commands/setup.md +0 -17
  452. package/defaults/hidden-roles.json +0 -68
  453. package/defaults/memory-chunk-prompt.md +0 -63
  454. package/defaults/mixdog-config.template.json +0 -27
  455. package/defaults/user-workflow.json +0 -8
  456. package/defaults/user-workflow.md +0 -17
  457. package/hooks/hooks.json +0 -73
  458. package/hooks/lib/active-instance.cjs +0 -77
  459. package/hooks/lib/permission-evaluator.cjs +0 -411
  460. package/hooks/lib/permission-route.cjs +0 -63
  461. package/hooks/lib/settings-loader.cjs +0 -117
  462. package/hooks/post-tool-use.cjs +0 -84
  463. package/hooks/pre-mcp-sandbox.cjs +0 -158
  464. package/hooks/pre-tool-subagent.cjs +0 -258
  465. package/hooks/session-start.cjs +0 -1493
  466. package/hooks/shim-launcher.cjs +0 -65
  467. package/hooks/turn-timer.cjs +0 -82
  468. package/lib/claude-md-writer.cjs +0 -386
  469. package/lib/keychain-cjs.cjs +0 -290
  470. package/lib/plugin-paths.cjs +0 -69
  471. package/lib/rules-builder.cjs +0 -241
  472. package/native/README.md +0 -117
  473. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  474. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  475. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  476. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  477. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  478. package/prompts/code-review.txt +0 -16
  479. package/prompts/security-audit.txt +0 -17
  480. package/rules/bridge/00-common.md +0 -39
  481. package/rules/bridge/20-skip-protocol.md +0 -18
  482. package/rules/bridge/30-explorer.md +0 -33
  483. package/rules/bridge/40-cycle1-agent.md +0 -52
  484. package/rules/bridge/41-cycle2-agent.md +0 -62
  485. package/rules/lead/00-tool-lead.md +0 -61
  486. package/rules/lead/01-general.md +0 -26
  487. package/rules/lead/02-channels.md +0 -49
  488. package/rules/lead/03-team.md +0 -27
  489. package/rules/lead/04-workflow.md +0 -20
  490. package/rules/shared/00-language.md +0 -14
  491. package/rules/shared/01-tool.md +0 -138
  492. package/scripts/bootstrap.mjs +0 -130
  493. package/scripts/bridge-unify-smoke.mjs +0 -308
  494. package/scripts/build-runtime-linux.sh +0 -348
  495. package/scripts/build-runtime-macos.sh +0 -217
  496. package/scripts/build-runtime-windows.ps1 +0 -242
  497. package/scripts/builtin-utils-smoke.mjs +0 -398
  498. package/scripts/bump.mjs +0 -80
  499. package/scripts/check-json.mjs +0 -45
  500. package/scripts/check-syntax-changed.mjs +0 -102
  501. package/scripts/check-syntax.mjs +0 -58
  502. package/scripts/code-graph-batch.test.mjs +0 -33
  503. package/scripts/config-preserve-smoke.mjs +0 -180
  504. package/scripts/doctor.mjs +0 -489
  505. package/scripts/edit-normalize-fuzz.mjs +0 -130
  506. package/scripts/edit-normalize-smoke.mjs +0 -401
  507. package/scripts/edit-operation-smoke.mjs +0 -369
  508. package/scripts/edit2-smoke.mjs +0 -63
  509. package/scripts/ensure-deps.mjs +0 -259
  510. package/scripts/fuzzy-e2e.mjs +0 -28
  511. package/scripts/fuzzy-smoke.mjs +0 -26
  512. package/scripts/gateway-model.mjs +0 -596
  513. package/scripts/generate-runtime-manifest.mjs +0 -166
  514. package/scripts/guard-smoke.mjs +0 -66
  515. package/scripts/hidden-role-schema-smoke.mjs +0 -162
  516. package/scripts/hook-routing-smoke.mjs +0 -29
  517. package/scripts/inject-input.ps1 +0 -204
  518. package/scripts/io-complex-smoke.mjs +0 -667
  519. package/scripts/io-explore-bench.mjs +0 -424
  520. package/scripts/io-guardrails-smoke.mjs +0 -205
  521. package/scripts/io-mini-bench-baseline.json +0 -11
  522. package/scripts/io-mini-bench.mjs +0 -216
  523. package/scripts/io-route-harness.mjs +0 -933
  524. package/scripts/io-telemetry-report.mjs +0 -691
  525. package/scripts/lib/gateway-inventory.mjs +0 -178
  526. package/scripts/lib/gateway-settings.mjs +0 -78
  527. package/scripts/mutation-bench.mjs +0 -564
  528. package/scripts/mutation-io-smoke.mjs +0 -1097
  529. package/scripts/native-patch-bridge-smoke.mjs +0 -288
  530. package/scripts/native-patch-smoke.mjs +0 -304
  531. package/scripts/openai-oauth-catalog-smoke.mjs +0 -53
  532. package/scripts/patch-interior-context-smoke.mjs +0 -49
  533. package/scripts/patch-newline-utf8-smoke.mjs +0 -157
  534. package/scripts/perf-hook-smoke.mjs +0 -71
  535. package/scripts/permission-eval-smoke.mjs +0 -443
  536. package/scripts/prep-patch.mjs +0 -53
  537. package/scripts/prep-shim.mjs +0 -96
  538. package/scripts/provider-cache-smoke.mjs +0 -687
  539. package/scripts/report-runtime-health.mjs +0 -132
  540. package/scripts/resolve-bun.mjs +0 -60
  541. package/scripts/run-mcp.mjs +0 -1473
  542. package/scripts/salvage-v4a-shatter.test.mjs +0 -58
  543. package/scripts/scoped-cache-io-smoke.mjs +0 -103
  544. package/scripts/shell-policy-round3-smoke.mjs +0 -46
  545. package/scripts/smoke-runtime-negative.ps1 +0 -100
  546. package/scripts/smoke-runtime-negative.sh +0 -95
  547. package/scripts/stall-policy-smoke.mjs +0 -50
  548. package/scripts/start-memory-worker.mjs +0 -23
  549. package/scripts/statusline-launcher-smoke.mjs +0 -235
  550. package/scripts/stress-atomic-write.mjs +0 -1028
  551. package/scripts/test-fault-inject.mjs +0 -164
  552. package/scripts/test-large-file.mjs +0 -174
  553. package/scripts/tool-edge-smoke.mjs +0 -209
  554. package/scripts/uninstall.mjs +0 -238
  555. package/scripts/webhook-selfheal-smoke.mjs +0 -27
  556. package/scripts/write-overwrite-guard-smoke.mjs +0 -56
  557. package/server-main.mjs +0 -3350
  558. package/server.mjs +0 -468
  559. package/setup/config-merge.mjs +0 -246
  560. package/setup/install.mjs +0 -574
  561. package/setup/launch-core.mjs +0 -617
  562. package/setup/launch.mjs +0 -101
  563. package/setup/locate-claude.mjs +0 -56
  564. package/setup/mixdog-cli.mjs +0 -122
  565. package/setup/setup-server.mjs +0 -3305
  566. package/setup/setup.html +0 -3740
  567. package/setup/tui.mjs +0 -325
  568. package/skills/retro-skill-proposer/SKILL.md +0 -92
  569. package/skills/schedule-add/SKILL.md +0 -77
  570. package/skills/setup/SKILL.md +0 -356
  571. package/skills/webhook-add/SKILL.md +0 -81
  572. package/src/agent/bridge-stall-watchdog.mjs +0 -337
  573. package/src/agent/index.mjs +0 -2229
  574. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +0 -1010
  575. package/src/agent/orchestrator/bridge-retry.mjs +0 -220
  576. package/src/agent/orchestrator/bridge-trace.mjs +0 -601
  577. package/src/agent/orchestrator/cache-mtime.mjs +0 -58
  578. package/src/agent/orchestrator/config.mjs +0 -405
  579. package/src/agent/orchestrator/context/collect.mjs +0 -651
  580. package/src/agent/orchestrator/dispatch-persist.mjs +0 -549
  581. package/src/agent/orchestrator/drain-registry.mjs +0 -50
  582. package/src/agent/orchestrator/explore-validator.mjs +0 -8
  583. package/src/agent/orchestrator/internal-roles.mjs +0 -118
  584. package/src/agent/orchestrator/internal-tools.mjs +0 -88
  585. package/src/agent/orchestrator/jobs.mjs +0 -116
  586. package/src/agent/orchestrator/mcp/client.mjs +0 -364
  587. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +0 -1884
  588. package/src/agent/orchestrator/providers/anthropic.mjs +0 -598
  589. package/src/agent/orchestrator/providers/gemini.mjs +0 -1530
  590. package/src/agent/orchestrator/providers/grok-oauth.mjs +0 -779
  591. package/src/agent/orchestrator/providers/model-catalog.mjs +0 -374
  592. package/src/agent/orchestrator/providers/openai-compat-stream.mjs +0 -366
  593. package/src/agent/orchestrator/providers/openai-compat.mjs +0 -1511
  594. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -1891
  595. package/src/agent/orchestrator/providers/openai-oauth.mjs +0 -1456
  596. package/src/agent/orchestrator/providers/openai-ws.mjs +0 -127
  597. package/src/agent/orchestrator/providers/registry.mjs +0 -192
  598. package/src/agent/orchestrator/providers/retry-classifier.mjs +0 -325
  599. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +0 -42
  600. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +0 -142
  601. package/src/agent/orchestrator/session/cache/read-cache.mjs +0 -319
  602. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +0 -361
  603. package/src/agent/orchestrator/session/loop.mjs +0 -1619
  604. package/src/agent/orchestrator/session/manager.mjs +0 -1991
  605. package/src/agent/orchestrator/session/result-classification.mjs +0 -65
  606. package/src/agent/orchestrator/session/store.mjs +0 -632
  607. package/src/agent/orchestrator/session/stream-watchdog.mjs +0 -130
  608. package/src/agent/orchestrator/session/tool-result-offload.mjs +0 -166
  609. package/src/agent/orchestrator/session/trim.mjs +0 -491
  610. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +0 -115
  611. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +0 -331
  612. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +0 -150
  613. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +0 -228
  614. package/src/agent/orchestrator/smart-bridge/index.mjs +0 -215
  615. package/src/agent/orchestrator/smart-bridge/profiles.mjs +0 -37
  616. package/src/agent/orchestrator/smart-bridge/registry.mjs +0 -348
  617. package/src/agent/orchestrator/stall-policy.mjs +0 -201
  618. package/src/agent/orchestrator/tool-loop-guard.mjs +0 -75
  619. package/src/agent/orchestrator/tools/bash-session.mjs +0 -722
  620. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +0 -171
  621. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +0 -511
  622. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -480
  623. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +0 -256
  624. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +0 -58
  625. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +0 -240
  626. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +0 -113
  627. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +0 -74
  628. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +0 -242
  629. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +0 -211
  630. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +0 -1364
  631. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +0 -126
  632. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +0 -141
  633. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +0 -194
  634. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +0 -60
  635. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +0 -168
  636. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +0 -173
  637. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +0 -48
  638. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -99
  639. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +0 -193
  640. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +0 -597
  641. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  642. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +0 -300
  643. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +0 -152
  644. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +0 -118
  645. package/src/agent/orchestrator/tools/builtin/read-open.mjs +0 -190
  646. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +0 -728
  647. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +0 -173
  648. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +0 -602
  649. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +0 -196
  650. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +0 -422
  651. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +0 -158
  652. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +0 -869
  653. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +0 -962
  654. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +0 -223
  655. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +0 -206
  656. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +0 -401
  657. package/src/agent/orchestrator/tools/builtin.mjs +0 -503
  658. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +0 -24
  659. package/src/agent/orchestrator/tools/code-graph.mjs +0 -4095
  660. package/src/agent/orchestrator/tools/cwd-tool.mjs +0 -298
  661. package/src/agent/orchestrator/tools/destructive-warning.mjs +0 -323
  662. package/src/agent/orchestrator/tools/edit-normalize.mjs +0 -603
  663. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +0 -154
  664. package/src/agent/orchestrator/tools/graph-manifest.json +0 -26
  665. package/src/agent/orchestrator/tools/host-input.mjs +0 -204
  666. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +0 -143
  667. package/src/agent/orchestrator/tools/patch-manifest.json +0 -26
  668. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +0 -20
  669. package/src/agent/orchestrator/tools/patch.mjs +0 -2754
  670. package/src/agent/orchestrator/tools/progress-message.mjs +0 -118
  671. package/src/agent/orchestrator/tools/shell-command.mjs +0 -865
  672. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +0 -7
  673. package/src/agent/orchestrator/tools/shell-snapshot.mjs +0 -313
  674. package/src/agent/orchestrator/workflow-store.mjs +0 -93
  675. package/src/agent/tool-defs.mjs +0 -110
  676. package/src/channels/backends/discord.mjs +0 -784
  677. package/src/channels/data/voice-runtime-manifest.json +0 -138
  678. package/src/channels/index.mjs +0 -3268
  679. package/src/channels/lib/config.mjs +0 -292
  680. package/src/channels/lib/drop-trace.mjs +0 -71
  681. package/src/channels/lib/event-pipeline.mjs +0 -81
  682. package/src/channels/lib/holidays.mjs +0 -138
  683. package/src/channels/lib/hook-pipe-server.mjs +0 -822
  684. package/src/channels/lib/output-forwarder.mjs +0 -765
  685. package/src/channels/lib/runtime-paths.mjs +0 -552
  686. package/src/channels/lib/scheduler.mjs +0 -723
  687. package/src/channels/lib/session-discovery.mjs +0 -103
  688. package/src/channels/lib/state-file.mjs +0 -68
  689. package/src/channels/lib/status-snapshot.mjs +0 -219
  690. package/src/channels/lib/tool-format.mjs +0 -140
  691. package/src/channels/lib/transcript-discovery.mjs +0 -195
  692. package/src/channels/lib/voice-runtime-fetcher.mjs +0 -734
  693. package/src/channels/lib/webhook.mjs +0 -1318
  694. package/src/channels/tool-defs.mjs +0 -170
  695. package/src/daemon/host.mjs +0 -118
  696. package/src/daemon/mcp-transport.mjs +0 -47
  697. package/src/daemon/session.mjs +0 -100
  698. package/src/daemon/thin-client.mjs +0 -71
  699. package/src/daemon/transport.mjs +0 -163
  700. package/src/gateway/claude-current.mjs +0 -255
  701. package/src/gateway/oauth-usage.mjs +0 -598
  702. package/src/gateway/route-meta.mjs +0 -629
  703. package/src/gateway/server.mjs +0 -713
  704. package/src/memory/data/runtime-manifest.json +0 -40
  705. package/src/memory/index.mjs +0 -3332
  706. package/src/memory/lib/core-memory-store.mjs +0 -330
  707. package/src/memory/lib/embedding-provider.mjs +0 -269
  708. package/src/memory/lib/embedding-worker.mjs +0 -323
  709. package/src/memory/lib/memory-cycle1.mjs +0 -645
  710. package/src/memory/lib/memory-cycle2.mjs +0 -1284
  711. package/src/memory/lib/memory-cycle3.mjs +0 -540
  712. package/src/memory/lib/memory-embed.mjs +0 -299
  713. package/src/memory/lib/memory-ops-policy.mjs +0 -190
  714. package/src/memory/lib/memory-recall-store.mjs +0 -638
  715. package/src/memory/lib/memory.mjs +0 -412
  716. package/src/memory/lib/pg/adapter.mjs +0 -308
  717. package/src/memory/lib/pg/process.mjs +0 -360
  718. package/src/memory/lib/pg/supervisor.mjs +0 -396
  719. package/src/memory/lib/runtime-fetcher.mjs +0 -458
  720. package/src/memory/lib/trace-store.mjs +0 -728
  721. package/src/memory/tool-defs.mjs +0 -79
  722. package/src/search/index.mjs +0 -1173
  723. package/src/search/lib/backends/anthropic-oauth.mjs +0 -98
  724. package/src/search/lib/backends/exa.mjs +0 -50
  725. package/src/search/lib/backends/firecrawl.mjs +0 -61
  726. package/src/search/lib/backends/gemini-api.mjs +0 -83
  727. package/src/search/lib/backends/grok-oauth.mjs +0 -86
  728. package/src/search/lib/backends/index.mjs +0 -150
  729. package/src/search/lib/backends/openai-api.mjs +0 -144
  730. package/src/search/lib/backends/openai-oauth.mjs +0 -102
  731. package/src/search/lib/backends/openai-web-search.mjs +0 -76
  732. package/src/search/lib/backends/tavily.mjs +0 -55
  733. package/src/search/lib/backends/xai-api.mjs +0 -113
  734. package/src/search/lib/config.mjs +0 -192
  735. package/src/search/lib/provider-usage.mjs +0 -67
  736. package/src/search/lib/providers.mjs +0 -47
  737. package/src/search/lib/search-intent.mjs +0 -109
  738. package/src/search/lib/setup-handler.mjs +0 -261
  739. package/src/search/lib/web-tools.mjs +0 -1219
  740. package/src/search/tool-defs.mjs +0 -83
  741. package/src/setup/defender-exclusion.mjs +0 -183
  742. package/src/shared/atomic-file.mjs +0 -436
  743. package/src/shared/config.mjs +0 -372
  744. package/src/shared/daemon-recycle.mjs +0 -108
  745. package/src/shared/disable-claude-builtins.mjs +0 -91
  746. package/src/shared/err-text.mjs +0 -12
  747. package/src/shared/llm/http-agent.mjs +0 -123
  748. package/src/shared/open-url.mjs +0 -62
  749. package/src/shared/plugin-paths.mjs +0 -58
  750. package/src/shared/schedules-store.mjs +0 -70
  751. package/src/shared/seed.mjs +0 -161
  752. package/src/shared/user-cwd.mjs +0 -225
  753. package/src/shared/user-data-guard.mjs +0 -244
  754. package/src/status/aggregator.mjs +0 -584
  755. package/src/status/server.mjs +0 -413
  756. package/tools.json +0 -1671
  757. /package/{defaults → src/defaults}/cycle3-review-prompt.md +0 -0
  758. /package/{defaults → src/defaults}/memory-promote-prompt.md +0 -0
  759. /package/{hooks → src/hooks}/lib/permission-rules.cjs +0 -0
  760. /package/{lib → src/lib}/config-cjs.cjs +0 -0
  761. /package/{lib → src/lib}/hook-pipe-path.cjs +0 -0
  762. /package/{lib → src/lib}/mixdog-debug.cjs +0 -0
  763. /package/{lib → src/lib}/text-utils.cjs +0 -0
  764. /package/{rules → src/rules}/bridge/42-cycle3-agent.md +0 -0
  765. /package/src/{agent → runtime/agent}/orchestrator/activity-bus.mjs +0 -0
  766. /package/src/{agent → runtime/agent}/orchestrator/providers/anthropic-betas.mjs +0 -0
  767. /package/src/{agent → runtime/agent}/orchestrator/session/abort-lookup.mjs +0 -0
  768. /package/src/{agent → runtime/agent}/orchestrator/session/cache/scoped-cache-outcome.mjs +0 -0
  769. /package/src/{agent → runtime/agent}/orchestrator/session/cache/util.mjs +0 -0
  770. /package/src/{agent → runtime/agent}/orchestrator/session/read-dedup.mjs +0 -0
  771. /package/src/{agent → runtime/agent}/orchestrator/session/save-session-worker.mjs +0 -0
  772. /package/src/{agent → runtime/agent}/orchestrator/smart-bridge/session-builder.mjs +0 -0
  773. /package/src/{agent → runtime/agent}/orchestrator/tools/bash-policy-scan.mjs +0 -0
  774. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/atomic-write.mjs +0 -0
  775. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/binary-file.mjs +0 -0
  776. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cache-layers.mjs +0 -0
  777. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cwd-utils.mjs +0 -0
  778. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/device-paths.mjs +0 -0
  779. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -0
  780. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diff-utils.mjs +0 -0
  781. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/fs-reachability.mjs +0 -0
  782. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/grep-formatting.mjs +0 -0
  783. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/hash-utils.mjs +0 -0
  784. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/list-formatting.mjs +0 -0
  785. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/open-config-tool.mjs +0 -0
  786. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-locks.mjs +0 -0
  787. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-utils.mjs +0 -0
  788. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-args.mjs +0 -0
  789. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-batch.mjs +0 -0
  790. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-constants.mjs +0 -0
  791. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image-resize.mjs +0 -0
  792. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image.mjs +0 -0
  793. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-lines.mjs +0 -0
  794. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-mode-tool.mjs +0 -0
  795. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-range-index.mjs +0 -0
  796. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-ranges.mjs +0 -0
  797. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-special-files.mjs +0 -0
  798. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-tool.mjs +0 -0
  799. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-windows.mjs +0 -0
  800. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-analysis.mjs +0 -0
  801. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-output.mjs +0 -0
  802. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-helpers.mjs +0 -0
  803. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-validation.mjs +0 -0
  804. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/text-stats.mjs +0 -0
  805. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/windows-roots.mjs +0 -0
  806. /package/src/{agent → runtime/agent}/orchestrator/tools/code-graph-prewarm-worker.mjs +0 -0
  807. /package/src/{agent → runtime/agent}/orchestrator/tools/env-scrub.mjs +0 -0
  808. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-content-cache.mjs +0 -0
  809. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-planner.mjs +0 -0
  810. /package/src/{agent → runtime/agent}/orchestrator/tools/next-call-utils.mjs +0 -0
  811. /package/src/{agent → runtime/agent}/orchestrator/tools/result-compression.mjs +0 -0
  812. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-exec-policy.mjs +0 -0
  813. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy-danger-target.mjs +0 -0
  814. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy.mjs +0 -0
  815. /package/src/{channels → runtime/channels}/lib/cli-worker-host.mjs +0 -0
  816. /package/src/{channels → runtime/channels}/lib/config-lock.mjs +0 -0
  817. /package/src/{channels → runtime/channels}/lib/event-queue.mjs +0 -0
  818. /package/src/{channels → runtime/channels}/lib/executor.mjs +0 -0
  819. /package/src/{channels → runtime/channels}/lib/format.mjs +0 -0
  820. /package/src/{channels → runtime/channels}/lib/interaction-workflows.mjs +0 -0
  821. /package/src/{channels → runtime/channels}/lib/memory-client.mjs +0 -0
  822. /package/src/{channels → runtime/channels}/lib/session-control.mjs +0 -0
  823. /package/src/{channels → runtime/channels}/lib/settings.mjs +0 -0
  824. /package/src/{channels → runtime/channels}/lib/whisper-server.mjs +0 -0
  825. /package/src/{memory → runtime/memory}/lib/agent-ipc.mjs +0 -0
  826. /package/src/{memory → runtime/memory}/lib/bridge-trace-queries.mjs +0 -0
  827. /package/src/{memory → runtime/memory}/lib/llm-worker-host.mjs +0 -0
  828. /package/src/{memory → runtime/memory}/lib/memory-cycle.mjs +0 -0
  829. /package/src/{memory → runtime/memory}/lib/memory-extraction.mjs +0 -0
  830. /package/src/{memory → runtime/memory}/lib/memory-maintenance-store.mjs +0 -0
  831. /package/src/{memory → runtime/memory}/lib/memory-recall-id-patch.mjs +0 -0
  832. /package/src/{memory → runtime/memory}/lib/memory-recall-read-query.mjs +0 -0
  833. /package/src/{memory → runtime/memory}/lib/memory-recall-scope-filter.mjs +0 -0
  834. /package/src/{memory → runtime/memory}/lib/memory-retrievers.mjs +0 -0
  835. /package/src/{memory → runtime/memory}/lib/memory-score.mjs +0 -0
  836. /package/src/{memory → runtime/memory}/lib/memory-text-utils.mjs +0 -0
  837. /package/src/{memory → runtime/memory}/lib/model-profile.mjs +0 -0
  838. /package/src/{memory → runtime/memory}/lib/project-id-resolver.mjs +0 -0
  839. /package/src/{search → runtime/search}/lib/cache.mjs +0 -0
  840. /package/src/{search → runtime/search}/lib/formatter.mjs +0 -0
  841. /package/src/{search → runtime/search}/lib/state.mjs +0 -0
  842. /package/src/{shared → runtime/shared}/abort-controller.mjs +0 -0
  843. /package/src/{shared → runtime/shared}/llm/cost.mjs +0 -0
  844. /package/src/{shared → runtime/shared}/llm/index.mjs +0 -0
  845. /package/src/{shared → runtime/shared}/llm/pid-cleanup.mjs +0 -0
  846. /package/src/{shared → runtime/shared}/llm/usage-log.mjs +0 -0
  847. /package/src/{shared → runtime/shared}/wsl.mjs +0 -0
@@ -0,0 +1,1087 @@
1
+ import { spawn } from 'child_process';
2
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, statSync, watch as fsWatch, writeFileSync } from 'fs';
3
+ import * as fsPromises from 'fs/promises';
4
+ import { basename, join } from 'path';
5
+ import { getPluginData } from '../../config.mjs';
6
+ import { stripAnsi } from '../shell-command.mjs';
7
+ import { scrubLoaderVars, scrubProviderSecrets } from '../env-scrub.mjs';
8
+ import {
9
+ normalizeToolNotifyContext,
10
+ notifyToolCompletion,
11
+ } from '../../../../shared/tool-execution-contract.mjs';
12
+ import {
13
+ completeBackgroundTask,
14
+ notifyBackgroundTaskProgress,
15
+ } from '../../../../shared/background-tasks.mjs';
16
+ import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
17
+
18
+ function sleep(ms) {
19
+ return new Promise((resolve) => setTimeout(resolve, ms));
20
+ }
21
+
22
+ // One-shot sweep of stale shell-job artefacts. Each backgrounded `bash`
23
+ // emits five files (.json/.done/.exit/.stdout.log/.stderr.log); the .done
24
+ // flag is written when the job exits, so a .done file older than
25
+ // SHELL_JOB_STALE_MS is the invariant proof its sibling files are also
26
+ // safe to remove. Active and recently-completed jobs are kept so
27
+ // `task` status/read/wait readers still find them. Runs once per mcp child
28
+ // lifetime on first getShellJobsDir() call. Async dirent walk + parallel
29
+ // stat/unlink keeps the main event loop free; fire-and-forget so the
30
+ // synchronous caller receives `dir` immediately.
31
+ const SHELL_JOB_STALE_MS = 24 * 60 * 60 * 1000;
32
+ let shellJobsSwept = false;
33
+ async function sweepStaleShellJobs(dir) {
34
+ if (shellJobsSwept) return;
35
+ shellJobsSwept = true;
36
+ const cutoff = Date.now() - SHELL_JOB_STALE_MS;
37
+ let names;
38
+ try { names = await fsPromises.readdir(dir); } catch { return; }
39
+ const expired = [];
40
+ await Promise.all(names.map(async (name) => {
41
+ if (!name.endsWith('.done')) return;
42
+ const p = join(dir, name);
43
+ try {
44
+ const st = await fsPromises.stat(p);
45
+ if (st.mtimeMs < cutoff) expired.push(name.slice(0, -5));
46
+ } catch {}
47
+ }));
48
+ // Orphan reclaim: a crashed wrapper leaves <id>.json with no .done —
49
+ // forever. Invariant proofs of death (either suffices, both gated on the
50
+ // stale cutoff so a young orphan can't race its own spawn):
51
+ // a) deadline: the wrapper enforces timeoutMs, so an entry older than
52
+ // timeoutMs + grace cannot still be running — pid-reuse-proof. Only
53
+ // trusted on runtime proof: detail.timeoutEnforced:true (PS wrapper,
54
+ // unconditional) or the <id>.enforced marker the posix wrapper
55
+ // touches when its `timeout` branch actually runs.
56
+ // b) ESRCH: the recorded pid no longer exists. Alone this misses pids
57
+ // recycled by unrelated live processes, hence (a).
58
+ const ORPHAN_DEADLINE_GRACE_MS = 30 * 60_000;
59
+ const doneSet = new Set(names.filter(n => n.endsWith('.done')).map(n => n.slice(0, -5)));
60
+ await Promise.all(names.map(async (name) => {
61
+ if (!name.endsWith('.json')) return;
62
+ const jobId = name.slice(0, -5);
63
+ if (doneSet.has(jobId)) return;
64
+ const p = join(dir, name);
65
+ try {
66
+ const st = await fsPromises.stat(p);
67
+ if (st.mtimeMs >= cutoff) return;
68
+ const detail = JSON.parse(await fsPromises.readFile(p, 'utf-8'));
69
+ const tmo = Number(detail?.timeoutMs);
70
+ const enforced = detail?.timeoutEnforced === true
71
+ || existsSync(join(dir, `${jobId}.enforced`));
72
+ const deadlinePassed = enforced
73
+ && Number.isFinite(tmo) && tmo > 0
74
+ && (Date.now() - st.mtimeMs) > tmo + ORPHAN_DEADLINE_GRACE_MS;
75
+ if (!deadlinePassed) {
76
+ const pid = Number(detail?.pid);
77
+ if (Number.isFinite(pid) && pid > 0) {
78
+ try { process.kill(pid, 0); return; } // alive (or EPERM → treated dead only via ESRCH below)
79
+ catch (e) { if (e?.code !== 'ESRCH') return; }
80
+ }
81
+ }
82
+ expired.push(jobId);
83
+ } catch {}
84
+ }));
85
+ // Owner sidecar markers carry a dynamic `.owner-<pid>` suffix, so they
86
+ // can't sit in the fixed-extension list — map each expired jobId to the
87
+ // marker name(s) actually present in this listing and unlink those too.
88
+ const expiredSet = new Set(expired);
89
+ const ownerMarkers = names.filter((n) => {
90
+ const i = n.lastIndexOf('.owner-');
91
+ return i > 0 && expiredSet.has(n.slice(0, i));
92
+ });
93
+ await Promise.all([
94
+ ...expired.flatMap((jobId) =>
95
+ ['.json', '.done', '.exit', '.enforced', '.exit.cmd.sh', '.exit.cmd.ps1', '.stdout.log', '.stderr.log'].map((ext) =>
96
+ fsPromises.unlink(join(dir, jobId + ext)).catch(() => {}),
97
+ ),
98
+ ),
99
+ ...ownerMarkers.map((n) => fsPromises.unlink(join(dir, n)).catch(() => {})),
100
+ ]);
101
+ }
102
+
103
+ function getShellJobsDir() {
104
+ const dir = join(getPluginData(), 'shell-jobs');
105
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
106
+ sweepStaleShellJobs(dir);
107
+ return dir;
108
+ }
109
+
110
+ function shellJobDetailPath(jobId) { return join(getShellJobsDir(), `${jobId}.json`); }
111
+ function shellJobStdoutPath(jobId) { return join(getShellJobsDir(), `${jobId}.stdout.log`); }
112
+ function shellJobStderrPath(jobId) { return join(getShellJobsDir(), `${jobId}.stderr.log`); }
113
+ function shellJobExitPath(jobId) { return join(getShellJobsDir(), `${jobId}.exit`); }
114
+ function shellJobDonePath(jobId) { return join(getShellJobsDir(), `${jobId}.done`); }
115
+ // Runtime proof that the posix wrapper's `timeout` branch actually ran: the
116
+ // wrapper touches this marker immediately before exec'ing `timeout`, so its
117
+ // existence is never optimistic (no spawn-time probe can guarantee the
118
+ // wrapper's own env/cwd resolution). PS jobs don't need it — their wrapper
119
+ // enforces unconditionally and records detail.timeoutEnforced:true.
120
+ function shellJobEnforcedPath(jobId) { return join(getShellJobsDir(), `${jobId}.enforced`); }
121
+ // Owner sidecar marker: a zero-byte file whose NAME encodes the owning CC host
122
+ // (claude.exe) pid — `<jobId>.owner-<pid>`. It lets the statusline owner-filter
123
+ // jobs from a SINGLE directory listing (no per-job JSON read) so the filter can
124
+ // precede its per-tick scan cap — otherwise other sessions' newer jobs evict
125
+ // this session's live jobs before filtering. Swept alongside the other
126
+ // artefacts.
127
+ function shellJobOwnerPath(jobId, pid) { return join(getShellJobsDir(), `${jobId}.owner-${pid}`); }
128
+
129
+ const JOB_STATUS_PREVIEW_MAX_BYTES = 4096;
130
+ const JOB_STATUS_PREVIEW_MAX_LINES = 20;
131
+ const JOB_STATUS_PREVIEW_MAX_CHARS = 1200;
132
+
133
+ // Resolve the CLI host pid that owns a freshly-spawned job. Standalone CLI
134
+ // execution is process-owned: background tasks are scoped to this CLI lifetime,
135
+ // and the pid stamp is only for statusline filtering / legacy sidecars.
136
+ function resolveJobOwnerHostPid(clientHostPid) {
137
+ const explicit = Number(clientHostPid);
138
+ if (Number.isInteger(explicit) && explicit > 0) return explicit;
139
+ const envPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
140
+ if (Number.isInteger(envPid) && envPid > 0) return envPid;
141
+ return null;
142
+ }
143
+
144
+ function writeShellJobDetail(detail) {
145
+ // Session scope stamp: every job record is tagged with the CC host pid
146
+ // that owns it (the claude.exe pid). This is the SAME physical pid the
147
+ // statusline shim passes as --client-host-pid, so the statusline can count
148
+ // only its own session's jobs by exact pid equality (no heuristic). The
149
+ // spawn sites set detail.ownerHostPid from the per-request threaded
150
+ // clientHostPid (resolveJobOwnerHostPid); this fallback only stamps the
151
+ // single-client env value when no spawn-site pid was set, and NEVER
152
+ // overwrites an existing stamp — so the field round-trips through disk on
153
+ // refresh/kill and a correct per-terminal stamp is preserved across rewrites.
154
+ if (detail && detail.ownerHostPid == null) {
155
+ const hostPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
156
+ if (Number.isInteger(hostPid) && hostPid > 0) detail.ownerHostPid = hostPid;
157
+ }
158
+ writeFileSync(shellJobDetailPath(detail.jobId), JSON.stringify(detail, null, 2), 'utf-8');
159
+ // Owner sidecar: encode the resolved owner pid in the marker filename so the
160
+ // statusline can owner-filter from the directory listing alone (before its
161
+ // scan cap). Idempotent zero-byte write — safe to repeat on refresh/kill
162
+ // rewrites. Skipped when no owner is known (legacy/unattributed jobs).
163
+ if (detail && Number.isInteger(detail.ownerHostPid) && detail.ownerHostPid > 0) {
164
+ try { writeFileSync(shellJobOwnerPath(detail.jobId, detail.ownerHostPid), '', 'utf-8'); }
165
+ catch { /* best-effort marker */ }
166
+ }
167
+ }
168
+
169
+ function readShellJobDetail(jobId) {
170
+ try {
171
+ const p = shellJobDetailPath(jobId);
172
+ if (!existsSync(p)) return null;
173
+ return JSON.parse(readFileSync(p, 'utf-8'));
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ export function buildJobNotFoundMessage(jobId) {
180
+ return `Error: task not found: ${jobId}`;
181
+ }
182
+
183
+ function isPidAlive(pid) {
184
+ if (!Number.isFinite(pid) || pid <= 0) return false;
185
+ try {
186
+ process.kill(pid, 0);
187
+ return true;
188
+ } catch {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ function killProcessTree(pid, signal = 'SIGTERM') {
194
+ if (!Number.isFinite(pid) || pid <= 0) return false;
195
+ try {
196
+ if (process.platform === 'win32') {
197
+ spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
198
+ } else {
199
+ try { process.kill(-pid, signal); }
200
+ catch { process.kill(pid, signal); }
201
+ // SIGKILL escalation: a background child that ignores SIGTERM must
202
+ // not survive (foreground treeKill / persistent _killProcessTree
203
+ // already do this). After a 3s grace, force-kill the group/pid.
204
+ // unref so this backstop never holds the host process open.
205
+ if (signal === 'SIGTERM') {
206
+ const t = setTimeout(() => {
207
+ try { process.kill(-pid, 'SIGKILL'); }
208
+ catch { try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ } }
209
+ }, 3000);
210
+ if (t.unref) t.unref();
211
+ }
212
+ }
213
+ return true;
214
+ } catch {
215
+ return false;
216
+ }
217
+ }
218
+
219
+ // Module-level tracking of live background-job pids so CLI shutdown can reap
220
+ // owned children. Async jobs are intentionally CLI-owned; no restart replay or
221
+ // daemon handoff is attempted.
222
+ const _liveJobPids = new Set();
223
+ let _shellJobsExitHookInstalled = false;
224
+ function _registerLiveJobPid(pid) {
225
+ if (Number.isFinite(pid) && pid > 0) _liveJobPids.add(pid);
226
+ }
227
+ function _unregisterLiveJobPid(pid) {
228
+ if (Number.isFinite(pid) && pid > 0) _liveJobPids.delete(pid);
229
+ }
230
+ function _sweepLiveJobsSync() {
231
+ for (const pid of _liveJobPids) {
232
+ try {
233
+ if (process.platform === 'win32') {
234
+ spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
235
+ } else {
236
+ try { process.kill(-pid, 'SIGKILL'); }
237
+ catch { try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ } }
238
+ }
239
+ } catch { /* ignore */ }
240
+ }
241
+ _liveJobPids.clear();
242
+ }
243
+ function _installShellJobsExitHook() {
244
+ if (_shellJobsExitHookInstalled) return;
245
+ _shellJobsExitHookInstalled = true;
246
+ try { process.on('exit', _sweepLiveJobsSync); } catch { /* ignore */ }
247
+ try { process.on('SIGTERM', _sweepLiveJobsSync); } catch { /* ignore */ }
248
+ try { process.on('SIGINT', _sweepLiveJobsSync); } catch { /* ignore */ }
249
+ try { process.on('SIGHUP', _sweepLiveJobsSync); } catch { /* ignore */ }
250
+ }
251
+
252
+ function shellQuoteSingle(s) {
253
+ return `'${String(s).replace(/'/g, `'\"'\"'`)}'`;
254
+ }
255
+
256
+ function psSingleQuote(s) {
257
+ return `'${String(s).replace(/'/g, "''")}'`;
258
+ }
259
+
260
+ function powerShellEncodedCommand(command) {
261
+ return Buffer.from(String(command || ''), 'utf16le').toString('base64');
262
+ }
263
+
264
+ function isPowerShellShell(shell, shellType) {
265
+ if (shellType === 'powershell') return true;
266
+ const stem = basename(String(shell || '')).toLowerCase().replace(/\.exe$/, '');
267
+ return stem === 'pwsh' || stem === 'powershell';
268
+ }
269
+
270
+ function readTailPreviewSync(filePath, { maxBytes = JOB_STATUS_PREVIEW_MAX_BYTES, maxLines = JOB_STATUS_PREVIEW_MAX_LINES, maxChars = JOB_STATUS_PREVIEW_MAX_CHARS } = {}) {
271
+ try {
272
+ if (!filePath || !existsSync(filePath)) return null;
273
+ const st = statSync(filePath);
274
+ if (!st.isFile()) return null;
275
+ const size = st.size;
276
+ if (size <= 0) return { bytes: 0, preview: '' };
277
+ const readBytes = Math.min(size, maxBytes);
278
+ const fd = openSync(filePath, 'r');
279
+ try {
280
+ const buf = Buffer.alloc(readBytes);
281
+ readSync(fd, buf, 0, readBytes, size - readBytes);
282
+ let text = buf.toString('utf8');
283
+ if (size > readBytes) {
284
+ const nl = text.indexOf('\n');
285
+ if (nl !== -1) text = text.slice(nl + 1);
286
+ }
287
+ let lines = text.split(/\r?\n/);
288
+ if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
289
+ let truncated = size > readBytes;
290
+ if (lines.length > maxLines) {
291
+ lines = lines.slice(-maxLines);
292
+ truncated = true;
293
+ }
294
+ let preview = lines.join('\n');
295
+ if (preview.length > maxChars) {
296
+ preview = preview.slice(preview.length - maxChars);
297
+ const nl = preview.indexOf('\n');
298
+ if (nl !== -1) preview = preview.slice(nl + 1);
299
+ truncated = true;
300
+ }
301
+ return {
302
+ bytes: size,
303
+ preview,
304
+ truncated,
305
+ };
306
+ } finally {
307
+ try { closeSync(fd); } catch { /* ignore */ }
308
+ }
309
+ } catch {
310
+ return null;
311
+ }
312
+ }
313
+
314
+ function attachJobPreview(detail) {
315
+ if (!detail || typeof detail !== 'object') return detail;
316
+ const withPreview = { ...detail };
317
+ const stdoutInfo = readTailPreviewSync(detail.stdoutPath);
318
+ if (stdoutInfo) {
319
+ withPreview.stdoutBytes = stdoutInfo.bytes;
320
+ if (stdoutInfo.preview) withPreview.stdoutPreview = stdoutInfo.preview;
321
+ if (stdoutInfo.truncated) withPreview.stdoutPreviewTruncated = true;
322
+ }
323
+ if (detail.mergeStderr !== true) {
324
+ const stderrInfo = readTailPreviewSync(detail.stderrPath);
325
+ if (stderrInfo) {
326
+ withPreview.stderrBytes = stderrInfo.bytes;
327
+ if (stderrInfo.preview) withPreview.stderrPreview = stderrInfo.preview;
328
+ if (stderrInfo.truncated) withPreview.stderrPreviewTruncated = true;
329
+ }
330
+ }
331
+ return withPreview;
332
+ }
333
+
334
+ function summarizeJobPreviewText(text, maxChars = 160) {
335
+ if (typeof text !== 'string' || !text.trim()) return '';
336
+ const lines = text
337
+ .split(/\r?\n/)
338
+ .map((line) => stripAnsi(line).replace(/\s+/g, ' ').trim())
339
+ .filter(Boolean);
340
+ if (lines.length === 0) return '';
341
+ let summary = lines[lines.length - 1];
342
+ if (summary.length > maxChars) summary = `${summary.slice(0, maxChars - 1)}…`;
343
+ return summary;
344
+ }
345
+
346
+ const SHELL_JOB_PROMPT_STALL_MS = 45_000;
347
+ const SHELL_JOB_PROMPT_TAIL_BYTES = 1024;
348
+ const SHELL_JOB_PROMPT_TAIL_LINES = 16;
349
+ const SHELL_JOB_PROMPT_TAIL_CHARS = 1024;
350
+ const SHELL_JOB_PROMPT_PATTERNS = [
351
+ /\((?:y|yes)\/(?:n|no)\)\s*[:?]?\s*$/i,
352
+ /\[(?:y|yes)\/(?:n|no)\]\s*[:?]?\s*$/i,
353
+ /\b(?:continue|proceed|confirm|overwrite|replace)\b[^\n]*[?:]\s*$/i,
354
+ /\bpress\s+(?:enter|return)\b[^\n]*$/i,
355
+ /\bdo you (?:want|wish|agree|accept)\b[^\n]*\?\s*$/i,
356
+ /\b(?:password|passphrase|otp|verification code)\b[^\n]*[:?]\s*$/i,
357
+ ];
358
+
359
+ function looksLikeInteractivePrompt(text) {
360
+ const tail = stripAnsi(String(text || '')).trim();
361
+ if (!tail) return false;
362
+ const last = tail.split(/\r?\n/).slice(-4).join('\n').trim();
363
+ return SHELL_JOB_PROMPT_PATTERNS.some((pattern) => pattern.test(last));
364
+ }
365
+
366
+ function readPromptTail(detail) {
367
+ if (!detail || typeof detail !== 'object') return { bytes: 0, text: '' };
368
+ const stdoutInfo = readTailPreviewSync(detail.stdoutPath, {
369
+ maxBytes: SHELL_JOB_PROMPT_TAIL_BYTES,
370
+ maxLines: SHELL_JOB_PROMPT_TAIL_LINES,
371
+ maxChars: SHELL_JOB_PROMPT_TAIL_CHARS,
372
+ });
373
+ const stderrInfo = detail.mergeStderr === true ? null : readTailPreviewSync(detail.stderrPath, {
374
+ maxBytes: SHELL_JOB_PROMPT_TAIL_BYTES,
375
+ maxLines: SHELL_JOB_PROMPT_TAIL_LINES,
376
+ maxChars: SHELL_JOB_PROMPT_TAIL_CHARS,
377
+ });
378
+ const bytes = (stdoutInfo?.bytes || 0) + (stderrInfo?.bytes || 0);
379
+ const parts = [
380
+ stdoutInfo?.preview ? `[stdout tail]\n${stdoutInfo.preview}` : '',
381
+ stderrInfo?.preview ? `[stderr tail]\n${stderrInfo.preview}` : '',
382
+ ].filter(Boolean);
383
+ return { bytes, text: parts.join('\n\n') };
384
+ }
385
+
386
+ function attachJobInsights(detail) {
387
+ const withPreview = attachJobPreview(detail);
388
+ if (!withPreview || typeof withPreview !== 'object') return withPreview;
389
+ let summary = '';
390
+ let summarySource = '';
391
+ if (withPreview.status === 'completed') {
392
+ summary = summarizeJobPreviewText(withPreview.stdoutPreview)
393
+ || summarizeJobPreviewText(withPreview.stderrPreview);
394
+ summarySource = summary ? (withPreview.stdoutPreview ? 'stdout' : 'stderr') : '';
395
+ } else if (withPreview.status === 'failed') {
396
+ summary = summarizeJobPreviewText(withPreview.stderrPreview)
397
+ || summarizeJobPreviewText(withPreview.stdoutPreview)
398
+ || String(withPreview.error || '').trim();
399
+ summarySource = summary ? (withPreview.stderrPreview ? 'stderr' : (withPreview.stdoutPreview ? 'stdout' : 'status')) : '';
400
+ } else if (withPreview.status === 'cancelled') {
401
+ summary = 'cancelled before completion';
402
+ summarySource = 'status';
403
+ } else if (withPreview.status === 'running') {
404
+ summary = summarizeJobPreviewText(withPreview.stdoutPreview)
405
+ || summarizeJobPreviewText(withPreview.stderrPreview);
406
+ summarySource = summary ? (withPreview.stdoutPreview ? 'stdout' : 'stderr') : '';
407
+ }
408
+ if (summary) {
409
+ withPreview.summary = summary;
410
+ withPreview.summarySource = summarySource;
411
+ }
412
+ return withPreview;
413
+ }
414
+
415
+ export function shellJobPublicTaskResult(detail) {
416
+ if (!detail || typeof detail !== 'object') return detail;
417
+ const result = {
418
+ task_id: detail.jobId || detail.task_id || null,
419
+ shell: detail.shellType || null,
420
+ status: detail.status || null,
421
+ cwd: detail.cwd || null,
422
+ pid: detail.pid || null,
423
+ exit_code: (typeof detail.exitCode === 'number') ? detail.exitCode : null,
424
+ signal: detail.signal || null,
425
+ timed_out: detail.timedOut === true ? true : null,
426
+ killed: detail.killed === true ? true : null,
427
+ stdout_bytes: (typeof detail.stdoutBytes === 'number') ? detail.stdoutBytes : null,
428
+ stderr_bytes: (typeof detail.stderrBytes === 'number') ? detail.stderrBytes : null,
429
+ stdout_preview: detail.stdoutPreview || null,
430
+ stderr_preview: detail.stderrPreview || null,
431
+ summary: detail.summary || null,
432
+ summary_source: detail.summarySource || null,
433
+ waited_ms: (typeof detail.waitedMs === 'number') ? detail.waitedMs : null,
434
+ wait_timed_out: detail.waitTimedOut === true ? true : null,
435
+ started_at: detail.startedAt || null,
436
+ finished_at: detail.finishedAt || null,
437
+ error: detail.error || null,
438
+ };
439
+ for (const [key, value] of Object.entries(result)) {
440
+ if (value == null || value === '') delete result[key];
441
+ }
442
+ return result;
443
+ }
444
+
445
+ export async function waitForShellJob(jobId, { timeoutMs = 30_000, pollMs = 250 } = {}) {
446
+ const started = Date.now();
447
+ const deadline = started + Math.max(0, timeoutMs);
448
+ let detail = refreshShellJob(jobId);
449
+ if (!detail) return null;
450
+ while (detail && detail.status === 'running' && Date.now() < deadline) {
451
+ await sleep(Math.max(25, pollMs));
452
+ detail = refreshShellJob(jobId);
453
+ }
454
+ const withInsights = attachJobInsights(detail);
455
+ if (!withInsights) return null;
456
+ withInsights.waitedMs = Date.now() - started;
457
+ if (withInsights.status === 'running') withInsights.waitTimedOut = true;
458
+ return withInsights;
459
+ }
460
+
461
+ // Non-blocking peek at a background task (CC BashOutput analogue): refresh its
462
+ // status and return current stdout/stderr tail preview WITHOUT waiting for
463
+ // completion. Returns null if the job id is unknown.
464
+ export function peekShellJob(jobId) {
465
+ const detail = refreshShellJob(jobId);
466
+ if (!detail) return null;
467
+ return attachJobInsights(detail);
468
+ }
469
+
470
+ // Terminate a running background task (CC KillShell analogue): kill the process
471
+ // tree and mark the job failed/137. Returns null if unknown; a detail with
472
+ // killed:false if it had already finished.
473
+ export function killShellJob(jobId) {
474
+ const detail = readShellJobDetail(jobId);
475
+ if (!detail) return null;
476
+ if (detail.status !== 'running') {
477
+ return { ...detail, killed: false, note: `task already ${detail.status}` };
478
+ }
479
+ killProcessTree(detail.pid, 'SIGTERM');
480
+ detail.status = 'failed';
481
+ detail.exitCode = 137;
482
+ detail.error = 'killed by user (KillShell)';
483
+ detail.finishedAt = new Date().toISOString();
484
+ writeShellJobDetail(detail);
485
+ _unregisterLiveJobPid(detail.pid);
486
+ return { ...attachJobInsights(detail), killed: true };
487
+ }
488
+
489
+ function refreshShellJob(jobId) {
490
+ const detail = readShellJobDetail(jobId);
491
+ if (!detail) return null;
492
+ if (detail.status !== 'running') return detail;
493
+ const exitPath = shellJobExitPath(jobId);
494
+ const donePath = shellJobDonePath(jobId);
495
+ // Gate completion on donePath existence. The wrapper writes the
496
+ // exit-code file FIRST and `touch donePath` strictly AFTER, so a
497
+ // visible donePath proves the exit file is fully flushed. Reading
498
+ // exit before donePath landed produced empty-string -> NaN ->
499
+ // exitCode=null -> spurious 'failed' status for processes that
500
+ // actually exited 0.
501
+ if (existsSync(donePath)) {
502
+ let exitCode = null;
503
+ try {
504
+ const raw = readFileSync(exitPath, 'utf-8').trim();
505
+ const parsed = parseInt(raw, 10);
506
+ exitCode = Number.isFinite(parsed) ? parsed : null;
507
+ } catch { /* ignore */ }
508
+ let finishedAt = new Date().toISOString();
509
+ try {
510
+ finishedAt = new Date(statSync(donePath).mtimeMs).toISOString();
511
+ } catch { /* ignore */ }
512
+ detail.status = exitCode === 0 ? 'completed' : 'failed';
513
+ detail.exitCode = exitCode;
514
+ detail.finishedAt = finishedAt;
515
+ writeShellJobDetail(detail);
516
+ _unregisterLiveJobPid(detail.pid);
517
+ return detail;
518
+ }
519
+ const timeoutMs = Number(detail.timeoutMs || 0);
520
+ const startedAtMs = Date.parse(detail.startedAt || '');
521
+ if (timeoutMs > 0 && Number.isFinite(startedAtMs) && Date.now() - startedAtMs > timeoutMs) {
522
+ killProcessTree(detail.pid, 'SIGTERM');
523
+ detail.status = 'failed';
524
+ detail.exitCode = 124;
525
+ detail.finishedAt = new Date().toISOString();
526
+ detail.error = `timed out after ${timeoutMs} ms`;
527
+ writeShellJobDetail(detail);
528
+ _unregisterLiveJobPid(detail.pid);
529
+ return detail;
530
+ }
531
+ if (detail.pid && !isPidAlive(detail.pid)) {
532
+ detail.status = 'failed';
533
+ detail.finishedAt = new Date().toISOString();
534
+ detail.error = 'process exited without completion marker';
535
+ writeShellJobDetail(detail);
536
+ _unregisterLiveJobPid(detail.pid);
537
+ }
538
+ return detail;
539
+ }
540
+
541
+ export function startBackgroundShellJob({ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, shellArg, shellType, clientHostPid }) {
542
+ return _startBackgroundShellJobImpl({ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, shellArg, shellType, clientHostPid });
543
+ }
544
+
545
+ // In-process completion watcher. After a background shell task is spawned the
546
+ // Lead session has no way to learn the task finished (no polling tool is
547
+ // auto-driven), so this registers an fs.watch on the shell-jobs dir filtered
548
+ // to `<jobId>.done` plus a ~2s polling fallback (fs.watch misses on some
549
+ // network / overlay filesystems) and a hard stop at timeoutMs + grace. When
550
+ // the job completes it reads the finished detail and delivers a shared async
551
+ // execution notification with type 'shell_task_result'.
552
+ //
553
+ // CLI-owned by design. If the CLI/runtime process exits, no notification is
554
+ // replayed or recovered later; shutdown cancels live jobs instead of handing
555
+ // them to a daemon.
556
+ //
557
+ // All timers are unref()'d and fs.watch is closed on completion/stop, so the
558
+ // watcher never keeps the host process alive.
559
+ const SHELL_JOB_WATCH_POLL_MS = 2000;
560
+ const SHELL_JOB_WATCH_GRACE_MS = 5000;
561
+ // Registry of armed background-job watchers keyed by jobId. task wait
562
+ // and `kill` actions already hold the completed outcome, so they cancel the
563
+ // armed watcher here to prevent a double-notify when its next poll fires.
564
+ const backgroundShellJobWatchers = new Map();
565
+ // Persistent notify ctx per jobId, set at FIRST arm and surviving cancel — so a
566
+ // re-arm after a task wait timeout can reconstruct the notify wiring without the
567
+ // caller threading the ctx back in. Deleted only in the watcher's cleanup() on
568
+ // settle (and explicitly in the kill path) so it cannot leak for entries that
569
+ // never complete.
570
+ const jobNotifyCtxByJobId = new Map();
571
+ // Live task waiter count per jobId. While >0 a synchronous caller is
572
+ // consuming the outcome, so the watcher must stay cancelled; the last waiter to
573
+ // leave (count===0) owns the decision to re-arm a still-running job.
574
+ const jobWaitWaiterCountByJobId = new Map();
575
+ // Register a synchronous task waiter. Paired with endShellJobWait in a
576
+ // finally so the count can't leak on throw.
577
+ export function beginShellJobWait(jobId) {
578
+ jobWaitWaiterCountByJobId.set(jobId, (jobWaitWaiterCountByJobId.get(jobId) || 0) + 1);
579
+ }
580
+ // Deregister a synchronous task waiter; returns the POST-decrement count so
581
+ // the last leaver (0) can decide whether to re-arm.
582
+ export function endShellJobWait(jobId) {
583
+ const next = (jobWaitWaiterCountByJobId.get(jobId) || 0) - 1;
584
+ if (next <= 0) { jobWaitWaiterCountByJobId.delete(jobId); return 0; }
585
+ jobWaitWaiterCountByJobId.set(jobId, next);
586
+ return next;
587
+ }
588
+ // Drop the persistent notify ctx for a jobId. Called from the kill path after
589
+ // cancel so a killed-but-never-fired entry can't leak its ctx.
590
+ export function clearShellJobNotifyCtx(jobId) {
591
+ jobNotifyCtxByJobId.delete(jobId);
592
+ }
593
+ // Cancel (and unregister) an armed watcher without notifying. Idempotent: a
594
+ // no-op when no watcher is armed, and the per-watcher cancel respects the
595
+ // `settled` guard so it cannot race a real completion notify. The persistent
596
+ // notify ctx survives cancel (see jobNotifyCtxByJobId) so a re-arm can recover
597
+ // it; return value is no longer relied upon by callers.
598
+ export function cancelBackgroundShellJobWatch(jobId) {
599
+ const entry = backgroundShellJobWatchers.get(jobId);
600
+ if (!entry) return null;
601
+ if (typeof entry.cancel === 'function') entry.cancel();
602
+ return entry.notifyCtx || null;
603
+ }
604
+ // notifyCtx may be omitted on RE-ARM — it then falls back to the persistent
605
+ // ctx captured at first arm (jobNotifyCtxByJobId).
606
+ export function watchBackgroundShellJob(jobId, notifyCtx) {
607
+ const ctx = (notifyCtx && typeof notifyCtx.notifyFn === 'function')
608
+ ? normalizeToolNotifyContext(notifyCtx)
609
+ : (jobId ? jobNotifyCtxByJobId.get(jobId) : null);
610
+ if (!jobId || !ctx || typeof ctx.notifyFn !== 'function') {
611
+ // Direct/non-dispatch callers have no push target; the task still runs
612
+ // and remains pollable via task control, so this is not a failure.
613
+ return;
614
+ }
615
+ // Idempotent arm: if a watcher is already registered for this jobId, leave
616
+ // it in place. Lets task wait's re-arm-after-timeout path call this
617
+ // unconditionally without stacking duplicate watchers.
618
+ if (backgroundShellJobWatchers.has(jobId)) return;
619
+ // Persist the notify ctx on FIRST arm so a later re-arm can recover it even
620
+ // after cancel cleared the live watcher entry.
621
+ jobNotifyCtxByJobId.set(jobId, ctx);
622
+ let settled = false;
623
+ // Distinguishes a bare cancel (keep ctx for re-arm) from a real settle
624
+ // (fire/timeout/hard-stop → drop ctx). cleanup() reads this.
625
+ let cancelled = false;
626
+ let watcher = null;
627
+ let pollTimer = null;
628
+ let hardStopTimer = null;
629
+ let lastOutputBytes = null;
630
+ let lastOutputAtMs = Date.now();
631
+ let promptStallNotified = false;
632
+ const cleanup = () => {
633
+ if (watcher) { try { watcher.close(); } catch { /* ignore */ } watcher = null; }
634
+ if (pollTimer) { try { clearInterval(pollTimer); } catch { /* ignore */ } pollTimer = null; }
635
+ if (hardStopTimer) { try { clearTimeout(hardStopTimer); } catch { /* ignore */ } hardStopTimer = null; }
636
+ backgroundShellJobWatchers.delete(jobId);
637
+ // Drop the persistent ctx only on a real settle (fire/timeout/hard-stop)
638
+ // — NOT on a bare cancel, which keeps it for a possible re-arm.
639
+ if (settled && !cancelled) jobNotifyCtxByJobId.delete(jobId);
640
+ };
641
+ // Cancel without notifying — used by task wait/cancel paths, which
642
+ // already hold the completed outcome. Idempotent via the `settled` guard
643
+ // so it can never race or double-fire against a real completion notify.
644
+ const cancel = () => {
645
+ if (settled) return;
646
+ settled = true;
647
+ cancelled = true;
648
+ cleanup();
649
+ };
650
+ backgroundShellJobWatchers.set(jobId, { cancel, notifyCtx: ctx });
651
+ const fire = (reason) => {
652
+ if (settled) return;
653
+ settled = true;
654
+ cleanup();
655
+ try {
656
+ const detail = attachJobInsights(refreshShellJob(jobId)) || readShellJobDetail(jobId);
657
+ if (!detail) return;
658
+ const startedAtMs = Date.parse(detail.startedAt || '');
659
+ const finishedAtMs = Date.parse(detail.finishedAt || '') || Date.now();
660
+ const elapsedMs = Number.isFinite(startedAtMs) ? Math.max(0, finishedAtMs - startedAtMs) : null;
661
+ const exitCode = (typeof detail.exitCode === 'number') ? detail.exitCode : null;
662
+ const status = detail.status || (reason === 'timeout' ? 'running' : 'unknown');
663
+ const lines = [
664
+ `[task_id: ${jobId}]`,
665
+ `[status: ${status}]`,
666
+ `[exit: ${exitCode === null ? 'n/a' : exitCode}]`,
667
+ elapsedMs !== null ? `[elapsed: ${elapsedMs} ms]` : null,
668
+ detail.command ? `[command: ${detail.command}]` : null,
669
+ '',
670
+ detail.summary ? `Summary: ${detail.summary}` : null,
671
+ detail.stdoutPreview ? `\n[stdout preview]\n${detail.stdoutPreview}` : null,
672
+ (detail.mergeStderr !== true && detail.stderrPreview) ? `\n[stderr preview]\n${detail.stderrPreview}` : null,
673
+ ].filter((l) => l !== null && l !== '');
674
+ const body = lines.join('\n');
675
+ const taskStatus = status === 'completed'
676
+ ? 'completed'
677
+ : (status === 'cancelled' ? 'cancelled' : 'failed');
678
+ const instruction = `The background shell task ${jobId} you started earlier has finished (${status}, exit ${exitCode === null ? 'n/a' : exitCode}) - review this result in your next step.`;
679
+ const completedTask = completeBackgroundTask(jobId, {
680
+ status: taskStatus,
681
+ result: shellJobPublicTaskResult(detail),
682
+ resultText: body,
683
+ error: taskStatus === 'failed' ? (detail.error || (status === 'running' ? 'background shell watcher deadline reached' : null)) : null,
684
+ resultType: 'shell_task_result',
685
+ instruction,
686
+ });
687
+ if (completedTask) return;
688
+ notifyToolCompletion({
689
+ surface: 'shell',
690
+ id: jobId,
691
+ status: taskStatus,
692
+ text: body,
693
+ resultType: 'shell_task_result',
694
+ instruction,
695
+ context: ctx,
696
+ logPrefix: 'shell-jobs',
697
+ });
698
+ } catch (err) {
699
+ try { process.stderr.write(`[shell-jobs] watchBackgroundShellJob fire failed: jobId=${jobId} err=${err?.message ?? String(err)}\n`); } catch { /* ignore */ }
700
+ }
701
+ };
702
+ const maybeNotifyPromptStall = (detail) => {
703
+ if (settled || promptStallNotified || !detail || detail.status !== 'running') return;
704
+ const tail = readPromptTail(detail);
705
+ const now = Date.now();
706
+ if (lastOutputBytes === null || tail.bytes !== lastOutputBytes) {
707
+ lastOutputBytes = tail.bytes;
708
+ lastOutputAtMs = now;
709
+ return;
710
+ }
711
+ if (now - lastOutputAtMs < SHELL_JOB_PROMPT_STALL_MS) return;
712
+ if (!looksLikeInteractivePrompt(tail.text)) return;
713
+ const elapsedMs = now - (Date.parse(detail.startedAt || '') || now);
714
+ const body = [
715
+ `[task_id: ${jobId}]`,
716
+ '[status: running]',
717
+ `[stalled: no output growth for ${now - lastOutputAtMs} ms]`,
718
+ elapsedMs >= 0 ? `[elapsed: ${elapsedMs} ms]` : null,
719
+ detail.command ? `[command: ${detail.command}]` : null,
720
+ '',
721
+ 'This background shell task appears to be waiting for interactive input. Background tasks cannot answer prompts automatically; cancel it or rerun with non-interactive flags/input.',
722
+ tail.text ? `\n${tail.text}` : null,
723
+ ].filter((line) => line !== null && line !== '').join('\n');
724
+ const instruction = `The background shell task ${jobId} appears to be waiting for interactive input; inspect the prompt, then cancel or rerun it non-interactively.`;
725
+ const sent = notifyBackgroundTaskProgress(jobId, {
726
+ text: body,
727
+ resultType: 'shell_task_progress',
728
+ instruction,
729
+ key: 'interactive-prompt-stall',
730
+ status: null,
731
+ }) || notifyToolCompletion({
732
+ surface: 'shell',
733
+ id: jobId,
734
+ status: null,
735
+ text: body,
736
+ resultType: 'shell_task_progress',
737
+ instruction,
738
+ context: ctx,
739
+ logPrefix: 'shell-jobs',
740
+ });
741
+ if (sent) promptStallNotified = true;
742
+ };
743
+ const checkDone = (reason) => {
744
+ if (settled) return;
745
+ const detail = refreshShellJob(jobId);
746
+ // refreshShellJob flips status off 'running' once donePath/exit/timeout
747
+ // is observed; only fire once the job is no longer running.
748
+ if (!detail || detail.status !== 'running') fire(reason);
749
+ else maybeNotifyPromptStall(detail);
750
+ };
751
+ try {
752
+ const donePath = shellJobDonePath(jobId);
753
+ // Already finished between spawn and watcher arm — fire immediately.
754
+ if (existsSync(donePath)) { fire('already-done'); return; }
755
+ const dir = getShellJobsDir();
756
+ const doneName = `${jobId}.done`;
757
+ try {
758
+ watcher = fsWatch(dir, (_event, filename) => {
759
+ if (!filename) { checkDone('watch'); return; }
760
+ if (String(filename) === doneName) checkDone('watch');
761
+ });
762
+ // Don't let the FSWatcher pin the event loop — the poll fallback
763
+ // and hard-stop timer are already unref()'d, so the watcher must
764
+ // be too or the host process can't exit until the job completes.
765
+ if (watcher && typeof watcher.unref === 'function') watcher.unref();
766
+ // A watcher error (e.g. dir removed) must not crash the host; rely
767
+ // on the poll fallback instead.
768
+ if (watcher && typeof watcher.on === 'function') {
769
+ watcher.on('error', () => { try { watcher.close(); } catch { /* ignore */ } watcher = null; });
770
+ }
771
+ } catch { watcher = null; }
772
+ pollTimer = setInterval(() => checkDone('poll'), SHELL_JOB_WATCH_POLL_MS);
773
+ if (typeof pollTimer.unref === 'function') pollTimer.unref();
774
+ const startedAtMs = Date.parse(readShellJobDetail(jobId)?.startedAt || '') || Date.now();
775
+ const timeoutMs = Number(readShellJobDetail(jobId)?.timeoutMs || 0);
776
+ const hardStopMs = Math.max(0, (startedAtMs + timeoutMs + SHELL_JOB_WATCH_GRACE_MS) - Date.now());
777
+ hardStopTimer = setTimeout(() => fire('timeout'), hardStopMs);
778
+ if (typeof hardStopTimer.unref === 'function') hardStopTimer.unref();
779
+ } catch (err) {
780
+ cleanup();
781
+ try { process.stderr.write(`[shell-jobs] watchBackgroundShellJob arm failed: jobId=${jobId} err=${err?.message ?? String(err)}\n`); } catch { /* ignore */ }
782
+ }
783
+ }
784
+
785
+ // Adopt a still-running FOREGROUND child into the shell-jobs registry. Used
786
+ // by execShellCommand's auto-background transition: the foreground one-shot
787
+ // path spawned a piped child whose stdout/stderr were already being captured
788
+ // to TaskOutput spill files. When the auto-background timer fires we do NOT
789
+ // re-spawn or wrap — the child keeps running as-is — we only publish a job
790
+ // detail so task control / refreshShellJob can track it to completion.
791
+ //
792
+ // The caller owns the child.on('close') lifecycle wiring (writing the exit
793
+ // file FIRST, donePath AFTER) so the ordering invariant refreshShellJob()
794
+ // depends on holds for adopted jobs exactly as it does for staged wrappers.
795
+ // This function only allocates the jobId/paths and writes the initial
796
+ // 'running' detail.
797
+ export function adoptForegroundShellJob({ command, cwd, pid, timeoutMs, mergeStderr, stdoutPath, stderrPath, clientHostPid }) {
798
+ const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
799
+ const exitPath = shellJobExitPath(jobId);
800
+ const donePath = shellJobDonePath(jobId);
801
+ const detail = {
802
+ jobId,
803
+ kind: 'bash',
804
+ status: 'running',
805
+ adopted: true,
806
+ command,
807
+ cwd,
808
+ pid,
809
+ mergeStderr: mergeStderr === true,
810
+ timeoutMs: Number(timeoutMs) || 0,
811
+ // Point the registry at the live TaskOutput spill files so
812
+ // peek/wait previews read the same bytes the foreground capture is
813
+ // still appending. mergeStderr collapses both onto stdoutPath.
814
+ stdoutPath: stdoutPath || null,
815
+ stderrPath: mergeStderr === true ? (stdoutPath || null) : (stderrPath || null),
816
+ exitPath,
817
+ donePath,
818
+ // Per-terminal session stamp: the threaded clientHostPid is the
819
+ // dispatching terminal's claude.exe pid (resolveJobOwnerHostPid falls
820
+ // back to the single-client env only when unset).
821
+ ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
822
+ startedAt: new Date().toISOString(),
823
+ };
824
+ writeShellJobDetail(detail);
825
+ if (Number.isFinite(pid) && pid > 0) {
826
+ _installShellJobsExitHook();
827
+ _registerLiveJobPid(pid);
828
+ }
829
+ return { ...detail, exitPath, donePath };
830
+ }
831
+
832
+ function _startBackgroundShellJobImpl({ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, shellArg, shellType, clientHostPid }) {
833
+ // Route ANY PowerShell shell to the PS wrapper, regardless of platform.
834
+ // Gating on win32 sent shell:'powershell' on macOS/Linux down the POSIX
835
+ // path below, which spawns `pwsh <wrapper>.sh` — pwsh then tries to run a
836
+ // bash script. isPowerShellShell already matches pwsh/powershell by stem.
837
+ if (isPowerShellShell(shell, shellType)) {
838
+ return startBackgroundPowerShellJob({ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, clientHostPid });
839
+ }
840
+
841
+ // POSIX-shell wrapper path. On Windows this runs for shell:'bash' (Git
842
+ // Bash): the previous hard rejection here was a policy choice, NOT a real
843
+ // invariant — every piece of this path's plumbing is shell-neutral.
844
+ // The wrapper uses `command -v timeout`, `if ... fi`, single-quote escape
845
+ // and POSIX exit-code propagation, all of which Git Bash executes; output
846
+ // and exit-code plumbing flows through the staged script's own
847
+ // `exec > … 2> …` redirect plus `printf … > exitPath` / `touch donePath`
848
+ // (filesystem ops, not shell features); and kill goes through
849
+ // killProcessTree(), which on win32 uses `taskkill /pid /t /f` regardless
850
+ // of which shell spawned the tree. So Git Bash background tasks cancel,
851
+ // capture output, and report exit codes correctly.
852
+ const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
853
+ const stdoutPath = shellJobStdoutPath(jobId);
854
+ const stderrPath = shellJobStderrPath(jobId);
855
+ const exitPath = shellJobExitPath(jobId);
856
+ const donePath = shellJobDonePath(jobId);
857
+ const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));
858
+ // P2 fix: wrap with POSIX `timeout` so the kernel terminates the process
859
+ // at deadline even if the JS-side timer is interrupted. --preserve-status
860
+ // keeps the user command's exit code on success; on timeout the wrapper
861
+ // exits 124.
862
+ // `timeout` ships with GNU coreutils on Linux. On macOS, Homebrew
863
+ // coreutils installs it as `gtimeout` (the un-prefixed name is NOT created
864
+ // by default), so the wrapper picks `timeout` if present else `gtimeout`.
865
+ // When neither exists it falls through to the inner command (the parent
866
+ // setTimeout still calls refreshShellJob to clean up).
867
+ const userCmdQuoted = shellQuoteSingle(command);
868
+ // P2 fix: invoke the resolved shell (not bash -c) so zsh / dash /
869
+ // alternate shells run snapshot-aware commands correctly. Drop
870
+ // --preserve-status so timeout returns 124 unambiguously, making
871
+ // it trivial to distinguish a timeout (124) from a user-side
872
+ // SIGTERM exit (143).
873
+ const innerShellQ = shellQuoteSingle(shell);
874
+ const innerArgQ = shellQuoteSingle(shellArg);
875
+ // Runtime enforcement proof: the wrapper touches <jobId>.enforced right
876
+ // before exec'ing `timeout`, so the marker exists iff the timeout branch
877
+ // actually ran under the wrapper's own env/cwd. A spawn-time probe can't
878
+ // guarantee that (env is scrubbed, cwd differs) — see shellJobEnforcedPath.
879
+ const enforcedPath = shellJobEnforcedPath(jobId);
880
+ // Lifecycle ordering invariant: write the exit-code file FIRST and
881
+ // `touch donePath` strictly AFTER. refreshShellJob() gates completion
882
+ // on donePath existence and only then trusts the exit file — without
883
+ // this strict ordering, readFileSync on a partially-flushed exit file
884
+ // returned '' -> parseInt NaN -> exitCode null -> spurious 'failed'
885
+ // status for processes that actually exited 0. `rm -- "$0"` removes
886
+ // the staged wrapper .cmd.sh after donePath is published so a host
887
+ // crash before this point still leaves the file for the sweep to GC.
888
+ const wrapped = `{ 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} ${innerShellQ} ${innerArgQ} ${userCmdQuoted}; else ${innerShellQ} ${innerArgQ} ${userCmdQuoted}; fi; rc=$?; printf '%s' "$rc" > ${shellQuoteSingle(exitPath)}; touch ${shellQuoteSingle(donePath)}; rm -- "$0" 2>/dev/null; exit $rc; }`;
889
+ // Stage the wrapped command to a .sh and let the script open its own
890
+ // output files via `exec > … 2> …`. The parent does NOT pass file
891
+ // descriptors via stdio inheritance (`stdio: 'ignore'` for all three).
892
+ //
893
+ // Let the shell own redirects via `exec > ... 2> ...` inside the staged
894
+ // script. The child remains referenced by this CLI process; detached here
895
+ // is only used on POSIX to create a process group for tree-kill.
896
+ const outRedirect = mergeStderr
897
+ ? `> ${shellQuoteSingle(stdoutPath)} 2>&1`
898
+ : `> ${shellQuoteSingle(stdoutPath)} 2> ${shellQuoteSingle(stderrPath)}`;
899
+ const scriptBody = `#!/usr/bin/env bash\nexec ${outRedirect}\n${wrapped}\n`;
900
+ const wrappedTempPath = `${exitPath}.cmd.sh`;
901
+ try {
902
+ writeFileSync(wrappedTempPath, scriptBody);
903
+ } catch (e) {
904
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to stage shell background task: ${e?.message || e}` };
905
+ }
906
+ // R11: scrub loader/execution vars even though bash-tool.mjs already
907
+ // scrubs upstream — defense-in-depth at the spawn site catches future
908
+ // callers that build their own spawnEnv.
909
+ const child = spawn(shell, [wrappedTempPath], {
910
+ cwd: workDir,
911
+ env: scrubLoaderVars(scrubProviderSecrets({ ...spawnEnv })),
912
+ detached: true,
913
+ stdio: 'ignore',
914
+ windowsHide: true,
915
+ });
916
+ startChildGuardian({
917
+ childPid: child.pid,
918
+ childGroupPid: child.pid,
919
+ label: 'shell-job',
920
+ });
921
+ _installShellJobsExitHook();
922
+ _registerLiveJobPid(child.pid);
923
+ const detail = {
924
+ jobId,
925
+ kind: 'bash',
926
+ status: 'running',
927
+ command,
928
+ cwd: workDir,
929
+ pid: child.pid,
930
+ mergeStderr,
931
+ timeoutMs,
932
+ timeoutSeconds,
933
+ stdoutPath,
934
+ stderrPath: mergeStderr ? stdoutPath : stderrPath,
935
+ exitPath,
936
+ donePath,
937
+ // Per-terminal session stamp (see resolveJobOwnerHostPid).
938
+ ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
939
+ startedAt: new Date().toISOString(),
940
+ };
941
+ writeShellJobDetail(detail);
942
+ const timer = setTimeout(() => { refreshShellJob(jobId); }, timeoutMs + 25);
943
+ if (typeof timer.unref === 'function') timer.unref();
944
+ return detail;
945
+ }
946
+
947
+ function startBackgroundPowerShellJob({ command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, clientHostPid }) {
948
+ const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
949
+ const stdoutPath = shellJobStdoutPath(jobId);
950
+ const rawStderrPath = shellJobStderrPath(jobId);
951
+ const exitPath = shellJobExitPath(jobId);
952
+ const donePath = shellJobDonePath(jobId);
953
+ const wrappedTempPath = `${exitPath}.cmd.ps1`;
954
+ const encodedCommand = powerShellEncodedCommand(command);
955
+ const mergeLiteral = mergeStderr ? '$true' : '$false';
956
+ const wrapper = [
957
+ "$ErrorActionPreference = 'Continue'",
958
+ '[Console]::OutputEncoding=[System.Text.Encoding]::UTF8',
959
+ '$OutputEncoding=[System.Text.Encoding]::UTF8',
960
+ '$exe = (Get-Process -Id $PID).Path',
961
+ `$encoded = ${psSingleQuote(encodedCommand)}`,
962
+ `$stdoutPath = ${psSingleQuote(stdoutPath)}`,
963
+ `$stderrPath = ${psSingleQuote(rawStderrPath)}`,
964
+ `$exitPath = ${psSingleQuote(exitPath)}`,
965
+ `$donePath = ${psSingleQuote(donePath)}`,
966
+ `$mergeStderr = ${mergeLiteral}`,
967
+ `$timeoutMs = ${Math.max(1, Math.floor(timeoutMs || 0))}`,
968
+ '$code = 1',
969
+ 'try {',
970
+ " $argList = @('-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', $encoded)",
971
+ // -WindowStyle is a Windows-only Start-Process parameter; pwsh on
972
+ // macOS/Linux throws "not supported on this platform". Add it only on win32.
973
+ ' $spArgs = @{ FilePath = $exe; ArgumentList = $argList; RedirectStandardOutput = $stdoutPath; RedirectStandardError = $stderrPath; PassThru = $true }',
974
+ ' if ($IsWindows -or $null -eq $IsWindows) { $spArgs[\'WindowStyle\'] = \'Hidden\' }',
975
+ ' $p = Start-Process @spArgs',
976
+ ' if ($timeoutMs -gt 0 -and -not $p.WaitForExit($timeoutMs)) {',
977
+ // Kill the whole process TREE, not just the direct child: Start-Process
978
+ // launches an intermediate pwsh that spawns the user command, so
979
+ // Stop-Process on $p.Id alone orphans the grandchildren. On Windows use
980
+ // `taskkill /T /F` (tree-terminate); on macOS/Linux pwsh, fall back to
981
+ // Stop-Process (no taskkill there).
982
+ ' try {',
983
+ ' if ($IsWindows -or $null -eq $IsWindows) {',
984
+ ' & taskkill.exe /pid $p.Id /t /f 2>$null | Out-Null',
985
+ ' } else {',
986
+ ' try { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue } catch {}',
987
+ ' }',
988
+ ' } catch {}',
989
+ ' $code = 124',
990
+ ' } else {',
991
+ ' try { $p.WaitForExit() } catch {}',
992
+ ' $code = if ($null -ne $p.ExitCode) { [int]$p.ExitCode } else { 0 }',
993
+ ' }',
994
+ '} catch {',
995
+ ' try { Add-Content -LiteralPath $stderrPath -Value ($_ | Out-String) -Encoding utf8 } catch {}',
996
+ ' $code = 1',
997
+ '}',
998
+ 'if ($mergeStderr) {',
999
+ ' try {',
1000
+ ' if (Test-Path -LiteralPath $stderrPath) {',
1001
+ ' $err = Get-Content -LiteralPath $stderrPath -Raw -ErrorAction SilentlyContinue',
1002
+ ' if ($err) { Add-Content -LiteralPath $stdoutPath -Value $err -Encoding utf8 }',
1003
+ ' Remove-Item -LiteralPath $stderrPath -Force -ErrorAction SilentlyContinue',
1004
+ ' }',
1005
+ ' } catch {}',
1006
+ '}',
1007
+ 'try { Set-Content -LiteralPath $exitPath -Value ([string]$code) -NoNewline -Encoding ascii } catch {}',
1008
+ 'try { Set-Content -LiteralPath $donePath -Value "" -NoNewline -Encoding ascii } catch {}',
1009
+ 'try { Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue } catch {}',
1010
+ 'exit $code',
1011
+ '',
1012
+ ].join('\n');
1013
+ try {
1014
+ writeFileSync(wrappedTempPath, wrapper, 'utf-8');
1015
+ } catch (e) {
1016
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to stage PowerShell background task: ${e?.message || e}` };
1017
+ }
1018
+
1019
+ const shellStem = basename(String(shell || '')).toLowerCase().replace(/\.exe$/, '');
1020
+ // `-WindowStyle Hidden` is a Windows-only CLI switch; pwsh on macOS/Linux
1021
+ // rejects it. `-ExecutionPolicy` likewise only applies to Windows
1022
+ // PowerShell. Build args per-platform so cross-OS pwsh background tasks run.
1023
+ const isWin = process.platform === 'win32';
1024
+ const wrapperArgs = ['-NoLogo', '-NoProfile', '-NonInteractive'];
1025
+ if (isWin) wrapperArgs.push('-WindowStyle', 'Hidden');
1026
+ if (isWin && shellStem === 'powershell') wrapperArgs.push('-ExecutionPolicy', 'Bypass');
1027
+ wrapperArgs.push('-File', wrappedTempPath);
1028
+ // Spawn the staged wrapper directly. detached MUST be false on Windows:
1029
+ // a native pwsh launched with detached:true + stdio:'ignore' exits
1030
+ // immediately without running -File (verified — the detached child dies
1031
+ // even while the parent stays alive; the non-detached child runs to
1032
+ // completion). windowsHide:true gives CREATE_NO_WINDOW so no console
1033
+ // window flashes on screen. The wrapper owns its own stdout/stderr file
1034
+ // redirect (exec-equivalent Set-Content paths above), so stdio:'ignore'
1035
+ // drops no output. The child remains referenced so it is owned by the CLI
1036
+ // process; runtime.close()/exit hooks reap it instead of daemonizing it.
1037
+ let child;
1038
+ try {
1039
+ child = spawn(shell, wrapperArgs, {
1040
+ cwd: workDir,
1041
+ env: scrubLoaderVars(scrubProviderSecrets({ ...spawnEnv })),
1042
+ detached: false,
1043
+ stdio: 'ignore',
1044
+ windowsHide: true,
1045
+ });
1046
+ startChildGuardian({
1047
+ childPid: child.pid,
1048
+ childGroupPid: child.pid,
1049
+ label: 'shell-job-powershell',
1050
+ });
1051
+ } catch (e) {
1052
+ return { jobId, kind: 'bash', status: 'failed', error: `failed to spawn PowerShell background task: ${e?.message || e}` };
1053
+ }
1054
+ const childPid = child.pid;
1055
+ if (!Number.isFinite(childPid) || childPid <= 0) {
1056
+ return { jobId, kind: 'bash', status: 'failed', error: 'PowerShell background task spawn returned no pid' };
1057
+ }
1058
+ _installShellJobsExitHook();
1059
+ _registerLiveJobPid(childPid);
1060
+ const detail = {
1061
+ jobId,
1062
+ kind: 'bash',
1063
+ shellType: 'powershell',
1064
+ status: 'running',
1065
+ command,
1066
+ cwd: workDir,
1067
+ pid: childPid,
1068
+ mergeStderr,
1069
+ timeoutMs,
1070
+ timeoutSeconds: Math.max(1, Math.ceil(timeoutMs / 1000)),
1071
+ stdoutPath,
1072
+ stderrPath: mergeStderr ? stdoutPath : rawStderrPath,
1073
+ exitPath,
1074
+ donePath,
1075
+ // The PS wrapper enforces timeoutMs unconditionally in-wrapper
1076
+ // (WaitForExit($timeoutMs) → Stop-Process → 124), so the deadline
1077
+ // invariant always holds for PowerShell jobs.
1078
+ timeoutEnforced: true,
1079
+ // Per-terminal session stamp (see resolveJobOwnerHostPid).
1080
+ ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
1081
+ startedAt: new Date().toISOString(),
1082
+ };
1083
+ writeShellJobDetail(detail);
1084
+ const timer = setTimeout(() => { refreshShellJob(jobId); }, timeoutMs + 25);
1085
+ if (typeof timer.unref === 'function') timer.unref();
1086
+ return detail;
1087
+ }