zidane 7.2.4 → 8.0.4

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 (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -6
  3. package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
  4. package/dist/acp-DQDhi2dj.js.map +1 -0
  5. package/dist/acp-cli.d.ts +1 -1
  6. package/dist/acp-cli.js +9 -66
  7. package/dist/acp-cli.js.map +1 -1
  8. package/dist/acp.d.ts +10 -590
  9. package/dist/acp.d.ts.map +1 -1
  10. package/dist/acp.js +3 -3
  11. package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
  12. package/dist/agent-DSLNmBp0.js.map +1 -0
  13. package/dist/agent.d.ts +1 -1
  14. package/dist/agent.js +3 -2
  15. package/dist/aliasing-lVFbm81n.js +217 -0
  16. package/dist/aliasing-lVFbm81n.js.map +1 -0
  17. package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
  18. package/dist/anthropic-CD00LhUP.js.map +1 -0
  19. package/dist/artifacts-CV2hUKnL.js.map +1 -1
  20. package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
  21. package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
  22. package/dist/call-tool-dHlbGOIY.js +70 -0
  23. package/dist/call-tool-dHlbGOIY.js.map +1 -0
  24. package/dist/chat/pure.d.ts +372 -6
  25. package/dist/chat/pure.d.ts.map +1 -1
  26. package/dist/chat/pure.js +35 -244
  27. package/dist/chat/pure.js.map +1 -1
  28. package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
  29. package/dist/chat-JPJJ8uu5.js.map +1 -0
  30. package/dist/chat.d.ts +4 -6
  31. package/dist/chat.js +23 -12
  32. package/dist/cli-shared-C-YCaglL.js +159 -0
  33. package/dist/cli-shared-C-YCaglL.js.map +1 -0
  34. package/dist/client-core-BQSyDALT.d.ts +2115 -0
  35. package/dist/client-core-BQSyDALT.d.ts.map +1 -0
  36. package/dist/codemode-worker.d.ts +1 -0
  37. package/dist/codemode-worker.js +155 -0
  38. package/dist/codemode-worker.js.map +1 -0
  39. package/dist/codemode.d.ts +352 -0
  40. package/dist/codemode.d.ts.map +1 -0
  41. package/dist/codemode.js +5 -0
  42. package/dist/connection-Dr-Js4yZ.d.ts +28 -0
  43. package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
  44. package/dist/connection-loop-gdh22LTI.js +112 -0
  45. package/dist/connection-loop-gdh22LTI.js.map +1 -0
  46. package/dist/context-segments-BHKlagL6.js +1016 -0
  47. package/dist/context-segments-BHKlagL6.js.map +1 -0
  48. package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
  49. package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
  50. package/dist/contexts/daytona.d.ts +19 -8
  51. package/dist/contexts/daytona.d.ts.map +1 -1
  52. package/dist/contexts/daytona.js +1 -440
  53. package/dist/contexts/docker.d.ts +36 -7
  54. package/dist/contexts/docker.d.ts.map +1 -1
  55. package/dist/contexts/docker.js +1 -456
  56. package/dist/contexts/e2b.d.ts +68 -13
  57. package/dist/contexts/e2b.d.ts.map +1 -1
  58. package/dist/contexts/e2b.js +1 -382
  59. package/dist/contexts/sandbox.d.ts +1 -1
  60. package/dist/contexts/sandbox.js +5 -0
  61. package/dist/contexts/sandbox.js.map +1 -1
  62. package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
  63. package/dist/contexts-d7UolYot.js.map +1 -0
  64. package/dist/contexts.d.ts +3 -3
  65. package/dist/contexts.js +1 -1
  66. package/dist/cred-keys-GpBg71cP.js +105 -0
  67. package/dist/cred-keys-GpBg71cP.js.map +1 -0
  68. package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
  69. package/dist/credentials-JttEuRON.js.map +1 -0
  70. package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
  71. package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
  72. package/dist/daemon-cli.d.ts +1 -1
  73. package/dist/daemon-cli.js +3 -2
  74. package/dist/daemon-cli.js.map +1 -1
  75. package/dist/daytona-DG3omipZ.js +513 -0
  76. package/dist/daytona-DG3omipZ.js.map +1 -0
  77. package/dist/defaults-Cx3zvDOV.js +23 -0
  78. package/dist/defaults-Cx3zvDOV.js.map +1 -0
  79. package/dist/dispatcher-BMlu2BmZ.js +4941 -0
  80. package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
  81. package/dist/docker-BZhMMJUp.js +727 -0
  82. package/dist/docker-BZhMMJUp.js.map +1 -0
  83. package/dist/e2b-CEVOdRib.js +480 -0
  84. package/dist/e2b-CEVOdRib.js.map +1 -0
  85. package/dist/edit-utils-EGosADZq.js +234 -0
  86. package/dist/edit-utils-EGosADZq.js.map +1 -0
  87. package/dist/environment-8wsKE2Ln.js +214 -0
  88. package/dist/environment-8wsKE2Ln.js.map +1 -0
  89. package/dist/eval-BNcvle_J.js +1595 -0
  90. package/dist/eval-BNcvle_J.js.map +1 -0
  91. package/dist/eval.d.ts +2 -2
  92. package/dist/eval.js +1 -1489
  93. package/dist/executor-Cx5-8yqi.js +621 -0
  94. package/dist/executor-Cx5-8yqi.js.map +1 -0
  95. package/dist/extension-authoring-DyGGmq9V.js +385 -0
  96. package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
  97. package/dist/extensions.d.ts +2 -2
  98. package/dist/extensions.js +3 -3
  99. package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
  100. package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
  101. package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
  102. package/dist/format-C2NQEVUH.js.map +1 -0
  103. package/dist/format-bytes-C7tfc3Zm.js +55 -0
  104. package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
  105. package/dist/headless-C_rNUuYC.js +960 -0
  106. package/dist/headless-C_rNUuYC.js.map +1 -0
  107. package/dist/headless.d.ts +1 -1
  108. package/dist/headless.js +1 -413
  109. package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
  110. package/dist/history-search-CcYzF8-0.js.map +1 -0
  111. package/dist/history-search-worker.d.ts +1 -1
  112. package/dist/history-search-worker.js +1 -1
  113. package/dist/host-D7NrI03V.js +378 -0
  114. package/dist/host-D7NrI03V.js.map +1 -0
  115. package/dist/host-signals-BcP5QKUD.js +118 -0
  116. package/dist/host-signals-BcP5QKUD.js.map +1 -0
  117. package/dist/identity-CwfO8K-t.js +26 -0
  118. package/dist/identity-CwfO8K-t.js.map +1 -0
  119. package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
  120. package/dist/index-BGe1fQT7.d.ts.map +1 -0
  121. package/dist/index-BVQsIKGn.d.ts +448 -0
  122. package/dist/index-BVQsIKGn.d.ts.map +1 -0
  123. package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
  124. package/dist/index-CHABJd-L.d.ts.map +1 -0
  125. package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
  126. package/dist/index-D6a7qXpg.d.ts.map +1 -0
  127. package/dist/index.d.ts +6 -6
  128. package/dist/index.js +27 -23
  129. package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
  130. package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
  131. package/dist/ipc/remote.d.ts +313 -0
  132. package/dist/ipc/remote.d.ts.map +1 -0
  133. package/dist/ipc/remote.js +4 -0
  134. package/dist/ipc.d.ts +6 -964
  135. package/dist/ipc.js +403 -4
  136. package/dist/ipc.js.map +1 -0
  137. package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
  138. package/dist/isolation-D9L650W5.d.ts.map +1 -0
  139. package/dist/json-rpc--HXQti1E.js +350 -0
  140. package/dist/json-rpc--HXQti1E.js.map +1 -0
  141. package/dist/links-j8rED1sR.js +603 -0
  142. package/dist/links-j8rED1sR.js.map +1 -0
  143. package/dist/local-terminal-D8QThwo_.js +252 -0
  144. package/dist/local-terminal-D8QThwo_.js.map +1 -0
  145. package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
  146. package/dist/logger-Gtqlayx8.js.map +1 -0
  147. package/dist/logs-view-CWG8aUAG.js.map +1 -1
  148. package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
  149. package/dist/mcp-_wecH9Qe.js.map +1 -0
  150. package/dist/mcp.d.ts +2 -2
  151. package/dist/mcp.js +2 -2
  152. package/dist/media-sniff-UKijEaiQ.js.map +1 -1
  153. package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
  154. package/dist/messages-BqiwcD2Q.js.map +1 -0
  155. package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
  156. package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
  157. package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
  158. package/dist/openai-compat-CFrpH7uq.js.map +1 -0
  159. package/dist/orchestration-BCGmB8Ps.js +511 -0
  160. package/dist/orchestration-BCGmB8Ps.js.map +1 -0
  161. package/dist/output/stream-json.d.ts +1 -2
  162. package/dist/output/stream-json.d.ts.map +1 -1
  163. package/dist/output/stream-json.js +1 -1
  164. package/dist/output/terminal.d.ts +1 -2
  165. package/dist/output/terminal.d.ts.map +1 -1
  166. package/dist/output/terminal.js +13 -7
  167. package/dist/output/terminal.js.map +1 -1
  168. package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
  169. package/dist/plugins-DS7mahzZ.js.map +1 -0
  170. package/dist/plugins.d.ts +1 -1
  171. package/dist/plugins.js +1 -2
  172. package/dist/prelude-Cxo4t74B.js +517 -0
  173. package/dist/prelude-Cxo4t74B.js.map +1 -0
  174. package/dist/presets.d.ts +1 -1
  175. package/dist/presets.js +1 -2
  176. package/dist/project-root-B8zADr4o.js +167 -0
  177. package/dist/project-root-B8zADr4o.js.map +1 -0
  178. package/dist/project-user-paths-BSgtrRxl.js +58 -0
  179. package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
  180. package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
  181. package/dist/prompts-DYcvqShf.d.ts.map +1 -0
  182. package/dist/protocol-DnhqqSmE.js +13 -0
  183. package/dist/protocol-DnhqqSmE.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +2 -2
  185. package/dist/providers/anthropic.js +1 -2
  186. package/dist/providers/arcee.d.ts +1 -1
  187. package/dist/providers/arcee.js +1 -1
  188. package/dist/providers/baseten.d.ts +1 -1
  189. package/dist/providers/baseten.js +1 -1
  190. package/dist/providers/cerebras.d.ts +1 -1
  191. package/dist/providers/cerebras.js +3 -2
  192. package/dist/providers/cerebras.js.map +1 -1
  193. package/dist/providers/local.d.ts +1 -1
  194. package/dist/providers/local.js +1 -1
  195. package/dist/providers/openai-compat.d.ts +1 -1
  196. package/dist/providers/openai-compat.js +1 -2
  197. package/dist/providers/openai.d.ts +1 -1
  198. package/dist/providers/openai.js +15 -12
  199. package/dist/providers/openai.js.map +1 -1
  200. package/dist/providers/openrouter.d.ts +1 -1
  201. package/dist/providers/openrouter.js +3 -2
  202. package/dist/providers/openrouter.js.map +1 -1
  203. package/dist/providers/xai.d.ts +1 -1
  204. package/dist/providers/xai.js +3 -3
  205. package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
  206. package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
  207. package/dist/providers.d.ts +1 -1
  208. package/dist/providers.js +4 -4
  209. package/dist/registry-D6ShBWJU.js +5234 -0
  210. package/dist/registry-D6ShBWJU.js.map +1 -0
  211. package/dist/remote-hosts-BtA_9ZGp.js +97 -0
  212. package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
  213. package/dist/restate.d.ts +2 -3
  214. package/dist/restate.d.ts.map +1 -1
  215. package/dist/restate.js +631 -572
  216. package/dist/restate.js.map +1 -1
  217. package/dist/run-summary-CklLiZ4y.js +288 -0
  218. package/dist/run-summary-CklLiZ4y.js.map +1 -0
  219. package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
  220. package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
  221. package/dist/session/sqlite.d.ts +1 -2
  222. package/dist/session/sqlite.d.ts.map +1 -1
  223. package/dist/session/sqlite.js +1 -616
  224. package/dist/session-factory-BUGY5P40.js +1576 -0
  225. package/dist/session-factory-BUGY5P40.js.map +1 -0
  226. package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
  227. package/dist/session-xh9JXu6i.js.map +1 -0
  228. package/dist/session.d.ts +1 -1
  229. package/dist/session.js +2 -2
  230. package/dist/settings-keys-Ckf29NeM.js +677 -0
  231. package/dist/settings-keys-Ckf29NeM.js.map +1 -0
  232. package/dist/shell-path-Dqaoow2J.js +412 -0
  233. package/dist/shell-path-Dqaoow2J.js.map +1 -0
  234. package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
  235. package/dist/skills-DKqZJm4X.js.map +1 -0
  236. package/dist/skills.d.ts +2 -2
  237. package/dist/skills.js +1 -2
  238. package/dist/source-mPExaUm8.js +381 -0
  239. package/dist/source-mPExaUm8.js.map +1 -0
  240. package/dist/sqlite-DV1DQzN2.js +698 -0
  241. package/dist/sqlite-DV1DQzN2.js.map +1 -0
  242. package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
  243. package/dist/src-BRMYRJib.js.map +1 -0
  244. package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
  245. package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
  246. package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
  247. package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
  248. package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
  249. package/dist/timeout-DEaqslLO.d.ts.map +1 -1
  250. package/dist/todo-edits-Dn251YUP.js +1790 -0
  251. package/dist/todo-edits-Dn251YUP.js.map +1 -0
  252. package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
  253. package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
  254. package/dist/tool-state-Dku6YqAB.js +989 -0
  255. package/dist/tool-state-Dku6YqAB.js.map +1 -0
  256. package/dist/tools/fetch-url.d.ts +1 -2
  257. package/dist/tools/fetch-url.d.ts.map +1 -1
  258. package/dist/tools/fetch-url.js +1 -2
  259. package/dist/tools/prompts.d.ts +1 -1
  260. package/dist/tools/prompts.js +7 -7
  261. package/dist/tools/prompts.js.map +1 -1
  262. package/dist/tools/web-search.d.ts +1 -2
  263. package/dist/tools/web-search.d.ts.map +1 -1
  264. package/dist/tools/web-search.js +2 -2
  265. package/dist/tools/web-search.js.map +1 -1
  266. package/dist/tools-_DnT3AGM.js +578 -0
  267. package/dist/tools-_DnT3AGM.js.map +1 -0
  268. package/dist/tools.d.ts +3 -3
  269. package/dist/tools.js +4 -2
  270. package/dist/tui.d.ts +534 -517
  271. package/dist/tui.d.ts.map +1 -1
  272. package/dist/tui.js +34933 -27242
  273. package/dist/tui.js.map +1 -1
  274. package/dist/turn-operations-B8sokwi5.js +333 -0
  275. package/dist/turn-operations-B8sokwi5.js.map +1 -0
  276. package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
  277. package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
  278. package/dist/types-C7ZFOztE.d.ts +132 -0
  279. package/dist/types-C7ZFOztE.d.ts.map +1 -0
  280. package/dist/types-CH5XpIHw.js +114 -0
  281. package/dist/types-CH5XpIHw.js.map +1 -0
  282. package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
  283. package/dist/types-Cu0ZGDu9.js.map +1 -0
  284. package/dist/types-IqegbJEE.d.ts +624 -0
  285. package/dist/types-IqegbJEE.d.ts.map +1 -0
  286. package/dist/types.d.ts +4 -4
  287. package/dist/types.js +1 -1
  288. package/dist/url-D8HCl65b.js +1259 -0
  289. package/dist/url-D8HCl65b.js.map +1 -0
  290. package/dist/wasm-BVT-j5cl.js +33 -0
  291. package/dist/wasm-BVT-j5cl.js.map +1 -0
  292. package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
  293. package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
  294. package/dist/ws-CIHMvlSh.js +952 -0
  295. package/dist/ws-CIHMvlSh.js.map +1 -0
  296. package/dist/ws-client-N7NxzvQp.js +230 -0
  297. package/dist/ws-client-N7NxzvQp.js.map +1 -0
  298. package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
  299. package/dist/xai-CvJTeLrI.js.map +1 -0
  300. package/dist/xdg-zweed0-t.js +117 -0
  301. package/dist/xdg-zweed0-t.js.map +1 -0
  302. package/docs/ACP.md +52 -0
  303. package/docs/ARCHITECTURE.md +103 -6
  304. package/docs/ARTIFACTS.md +8 -4
  305. package/docs/CHAT.md +165 -14
  306. package/docs/EXECUTION_CONTEXT.md +8 -0
  307. package/docs/EXTENSIONS.md +123 -2
  308. package/docs/HEADLESS.md +2 -0
  309. package/docs/IPC.md +775 -64
  310. package/docs/MACHINES.md +456 -0
  311. package/docs/PANELS.md +3 -0
  312. package/docs/README.md +4 -0
  313. package/docs/RESTATE.md +3 -2
  314. package/docs/RUN_IN_BACKGROUND.md +10 -2
  315. package/docs/SERVER.md +1085 -0
  316. package/docs/SKILL.md +6 -1
  317. package/docs/TERMINAL.md +75 -0
  318. package/docs/TUI.md +65 -14
  319. package/package.json +58 -26
  320. package/scripts/eval.ts +4 -0
  321. package/dist/acp-D-tVyUjw.js.map +0 -1
  322. package/dist/agent-D0zZnGbt.js.map +0 -1
  323. package/dist/anthropic-C2aole0G.js.map +0 -1
  324. package/dist/approvals-DKaJZYlm.js.map +0 -1
  325. package/dist/atomic-write-B7zr1ddF.js.map +0 -1
  326. package/dist/auth-BG2rVKSt.js.map +0 -1
  327. package/dist/completion-core-BRo86R_L.js.map +0 -1
  328. package/dist/contexts/daytona.js.map +0 -1
  329. package/dist/contexts/docker.js.map +0 -1
  330. package/dist/contexts/e2b.js.map +0 -1
  331. package/dist/contexts-CRE9pw07.js.map +0 -1
  332. package/dist/eval.js.map +0 -1
  333. package/dist/format-BF745pLL.js.map +0 -1
  334. package/dist/glob-DluQFSw1.js +0 -270
  335. package/dist/glob-DluQFSw1.js.map +0 -1
  336. package/dist/headless.js.map +0 -1
  337. package/dist/history-search-C4bk1Wz_.js.map +0 -1
  338. package/dist/index-BgN1T3lk.d.ts.map +0 -1
  339. package/dist/index-C37_ppWv.d.ts.map +0 -1
  340. package/dist/index-C6NEd5y_.d.ts.map +0 -1
  341. package/dist/ipc-zVSyo6K1.js +0 -331
  342. package/dist/ipc-zVSyo6K1.js.map +0 -1
  343. package/dist/ipc.d.ts.map +0 -1
  344. package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
  345. package/dist/json-rpc-rSWMUh9b.js +0 -192
  346. package/dist/json-rpc-rSWMUh9b.js.map +0 -1
  347. package/dist/logger--C1VQjwJ.js.map +0 -1
  348. package/dist/mcp-CgNd-f3F.js.map +0 -1
  349. package/dist/messages-2jcOA_5R.js.map +0 -1
  350. package/dist/openai-compat-Dy56weK8.js.map +0 -1
  351. package/dist/output-cap-C9zukcCj.js +0 -100
  352. package/dist/output-cap-C9zukcCj.js.map +0 -1
  353. package/dist/panels-B6gRk4bf.js +0 -789
  354. package/dist/panels-B6gRk4bf.js.map +0 -1
  355. package/dist/plugins-gbI7SBA6.js.map +0 -1
  356. package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
  357. package/dist/run-summary-BU_NgwUT.js +0 -269
  358. package/dist/run-summary-BU_NgwUT.js.map +0 -1
  359. package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
  360. package/dist/session/sqlite.js.map +0 -1
  361. package/dist/session-BqRP1hS7.js.map +0 -1
  362. package/dist/shell-path-h5czzllR.js +0 -1549
  363. package/dist/shell-path-h5czzllR.js.map +0 -1
  364. package/dist/skills-BC3dodWp.js.map +0 -1
  365. package/dist/src-CkNQKFKr.js.map +0 -1
  366. package/dist/todo-edits-UOwgxGKu.js +0 -528
  367. package/dist/todo-edits-UOwgxGKu.js.map +0 -1
  368. package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
  369. package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
  370. package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
  371. package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
  372. package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
  373. package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
  374. package/dist/turn-operations-DrbJlpq9.js.map +0 -1
  375. package/dist/types-CWgENVLY.js.map +0 -1
  376. package/dist/xai-CazjStOK.js.map +0 -1
@@ -1,10 +1,19 @@
1
+ import { a as loginShell, c as DEFAULT_MAX_OUTPUT_BUFFER, f as outputExceededMessage, i as localTerminalProcess, l as appendCapped, o as ptyChildProcess, r as hasLocalPty, s as spawnLocalPty, t as TERMINAL_ENV } from "./local-terminal-D8QThwo_.js";
1
2
  import { t as lazyAsync } from "./lazy-DLOurOC_.js";
2
- import { n as globLiteralPrefix, t as globFilesLocal } from "./glob-DluQFSw1.js";
3
- import { a as outputExceededMessage, n as appendCapped, t as DEFAULT_MAX_OUTPUT_BUFFER } from "./output-cap-C9zukcCj.js";
4
- import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
5
- import { accessSync, constants, createWriteStream, realpathSync, statSync } from "node:fs";
3
+ import { accessSync, constants, createWriteStream, openSync, realpathSync, statSync } from "node:fs";
4
+ import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
5
  import { homedir } from "node:os";
7
6
  import { mkdir, readFile, readdir, realpath, stat, writeFile } from "node:fs/promises";
7
+ import { spawn } from "node:child_process";
8
+ //#region src/contexts/types.ts
9
+ /**
10
+ * Effective {@link DetachedTasksCapability} of a context, with the
11
+ * backward-compatible inference for contexts that predate the field.
12
+ */
13
+ function resolveDetachedTasksCapability(context) {
14
+ return context.capabilities.detachedTasks ?? (context.execBackground ? "process-lifetime" : "none");
15
+ }
16
+ //#endregion
8
17
  //#region src/contexts/isolation.ts
9
18
  /**
10
19
  * Only `/usr/bin/sandbox-exec` is trusted — never a `PATH` lookup — so a
@@ -78,7 +87,7 @@ function isolationAvailability(platform = process.platform, env = process.env) {
78
87
  executable: bwrap
79
88
  } : {
80
89
  ok: false,
81
- reason: "bwrap (bubblewrap) is not installed — install it with your package manager (e.g. `apt install bubblewrap`)"
90
+ reason: "bwrap (bubblewrap) is not installed. Install it with your package manager (e.g. `apt install bubblewrap`)"
82
91
  };
83
92
  }
84
93
  /** Expand a leading `~` / `~/` to `home`. Other paths returned unchanged. */
@@ -488,7 +497,7 @@ function unavailableResult(reason) {
488
497
  };
489
498
  }
490
499
  //#endregion
491
- //#region src/contexts/process.ts
500
+ //#region src/contexts/process-state.ts
492
501
  /**
493
502
  * Lazily load `node:child_process`'s `spawn` (memoized after first use).
494
503
  *
@@ -512,18 +521,15 @@ const loadSpawn = lazyAsync(async () => (await import("node:child_process")).spa
512
521
  */
513
522
  const SUPPORTS_PROCESS_GROUPS = process.platform !== "win32";
514
523
  /**
515
- * Default cap on captured stdout / stderr per child. Matches the
516
- * pre-fix `child_process.exec` setting so existing callers see the
517
- * same buffer envelope. Output beyond this is truncated and a
518
- * marker is appended to stderr.
519
- */
520
- /**
521
- * How long `destroy()` waits for a SIGTERM'd background task to settle
522
- * before escalating to SIGKILL and abandoning the wait. Matches the MCP
523
- * connection's default `closeTimeout` so `agent.destroy()` stays bounded
524
- * by a single grace period regardless of which leg is slowest.
524
+ * Background tasks on a PTY see a terminal where they used to see pipes:
525
+ * keep pagers and git's credential prompt from waiting on input nobody
526
+ * types. The command's own env still wins.
525
527
  */
526
- const DESTROY_SIGTERM_GRACE_MS = 5e3;
528
+ const TERMINAL_TASK_ENV = {
529
+ PAGER: "cat",
530
+ GIT_PAGER: "cat",
531
+ GIT_TERMINAL_PROMPT: "0"
532
+ };
527
533
  /**
528
534
  * How long an `exec` kill (timeout / abort / output-overflow) waits after
529
535
  * SIGTERM before escalating the process group to SIGKILL. A child that
@@ -559,7 +565,7 @@ const EXIT_PIPE_DRAIN_GRACE_MS = 1500;
559
565
  */
560
566
  const TASK_ID_RE = /^[a-z][\w-]*$/i;
561
567
  function assertSafeTaskId(taskId) {
562
- if (!TASK_ID_RE.test(taskId)) throw new Error(`Invalid task id "${taskId}" — must match ${TASK_ID_RE}.`);
568
+ if (!TASK_ID_RE.test(taskId)) throw new Error(`Invalid task id "${taskId}": it must match ${TASK_ID_RE}.`);
563
569
  }
564
570
  /**
565
571
  * Format `date` as `YYYYMMDD-HHMMSS-mmm` in UTC.
@@ -580,12 +586,11 @@ function pickEnv(env, keys) {
580
586
  for (const key of keys) if (env[key] !== void 0) out[key] = env[key];
581
587
  return out;
582
588
  }
583
- function createProcessContext(config) {
584
- let counter = 0;
589
+ function createProcessState(config) {
585
590
  const handles = /* @__PURE__ */ new Map();
586
591
  const defaultCwd = config?.cwd ?? process.cwd();
587
592
  const defaultEnv = config?.env;
588
- const destroyGraceMs = config?.destroyGraceMs ?? DESTROY_SIGTERM_GRACE_MS;
593
+ const destroyGraceMs = config?.destroyGraceMs ?? 5e3;
589
594
  const baseEnv = config?.inheritEnv === false ? pickEnv(process.env, [
590
595
  "PATH",
591
596
  "HOME",
@@ -600,68 +605,6 @@ function createProcessContext(config) {
600
605
  const isolation = config?.isolation;
601
606
  const isolationState = isolation ? isolationAvailability() : void 0;
602
607
  /**
603
- * `{ file, args }` for `/bin/sh -c command`, wrapped in the sandbox when
604
- * isolation is on. Callers check `isolationState.ok` first (the three
605
- * shell entry points return `unavailableResult` / throw before spawning).
606
- */
607
- function shellCommand(command) {
608
- if (isolation && isolationState?.ok) return wrapCommand(isolationState.backend, isolationState.executable, isolation, command);
609
- return {
610
- file: "/bin/sh",
611
- args: ["-c", command]
612
- };
613
- }
614
- /** Failure result when isolation was requested but cannot be enforced; `undefined` = proceed. */
615
- function isolationBlocker() {
616
- return isolationState && !isolationState.ok ? unavailableResult(isolationState.reason) : void 0;
617
- }
618
- /**
619
- * Resolve `path` against the handle cwd and, when a `workspaceRoot` is
620
- * configured, reject anything that escapes it. Uses `realpath` on the
621
- * nearest existing ancestor so symlink escapes (and `..`) are caught for
622
- * both existing and not-yet-created targets.
623
- */
624
- async function resolveContained(handle, path) {
625
- const full = resolve(handle.cwd, path);
626
- if (workspaceRoot === void 0) return full;
627
- const canonical = await canonicalizeForCheck(full);
628
- const canonicalRoot = await realpath(workspaceRoot).catch(() => workspaceRoot);
629
- if (!isWithinRoot(canonicalRoot, canonical)) throw new Error(`Path escapes the workspace root: ${path} (resolved outside ${canonicalRoot})`);
630
- return full;
631
- }
632
- /**
633
- * Canonicalize the deepest existing ancestor of `full` to defeat symlink
634
- * escapes; the non-existent tail (for writes) is appended back verbatim.
635
- */
636
- async function canonicalizeForCheck(full) {
637
- let existing = full;
638
- const tail = [];
639
- while (true) try {
640
- existing = await realpath(existing);
641
- break;
642
- } catch {
643
- const parent = dirname(existing);
644
- if (parent === existing) {
645
- existing = parent;
646
- break;
647
- }
648
- tail.unshift(relative(parent, existing));
649
- existing = parent;
650
- }
651
- return tail.length ? resolve(existing, ...tail) : existing;
652
- }
653
- /**
654
- * In-process counterpart of the sandbox's write policy: `writeFile` (and
655
- * so the `write_file` / `edit` tools) may only target `isolation.writableRoots`.
656
- * Reads are deliberately unrestricted — same boundary the kernel enforces
657
- * for shell commands, so the two never disagree about where writes land.
658
- */
659
- async function assertWritable(full, path) {
660
- if (!isolation) return;
661
- const canonical = await canonicalizeForCheck(full);
662
- if (!isolation.writableRoots.some((root) => isWithinRoot(root, canonical))) throw new Error(`[sandbox] write denied: ${path} is outside the writable roots (${isolation.writableRoots.join(", ")}). Ask the user to add it under Settings → "Sandbox writable paths" or turn off "Sandbox shell commands".`);
663
- }
664
- /**
665
608
  * Per-context background-task registry. Entries live for the context's
666
609
  * lifetime — even after the child exits — so the model can read output
667
610
  * of completed tasks until `destroy()` tears everything down. Kept as
@@ -669,7 +612,6 @@ function createProcessContext(config) {
669
612
  * abstraction. The state and the operations on it live inline.
670
613
  */
671
614
  const tasks = /* @__PURE__ */ new Map();
672
- let taskCounter = 0;
673
615
  /**
674
616
  * Per-context UTC timestamp segment baked into every background task's
675
617
  * log filename. Same `taskCounter` value across two contexts (e.g. a
@@ -711,7 +653,25 @@ function createProcessContext(config) {
711
653
  */
712
654
  /** Live `execStream` children → owning handle id. See `exitHandler`. */
713
655
  const streamChildren = /* @__PURE__ */ new Map();
714
- let exitHandlerRegistered = false;
656
+ const s = {
657
+ config,
658
+ counter: 0,
659
+ handles,
660
+ defaultCwd,
661
+ defaultEnv,
662
+ destroyGraceMs,
663
+ baseEnv,
664
+ workspaceRoot,
665
+ isolation,
666
+ isolationState,
667
+ tasks,
668
+ taskCounter: 0,
669
+ contextTimestamp,
670
+ streamChildren,
671
+ exitHandlerRegistered: false,
672
+ exitHandler: () => {},
673
+ registerExitHandler: () => {}
674
+ };
715
675
  const exitHandler = () => {
716
676
  for (const task of tasks.values()) {
717
677
  if (task.status !== "running") continue;
@@ -725,156 +685,679 @@ function createProcessContext(config) {
725
685
  for (const child of streamChildren.keys()) killProcessGroup(child, "SIGTERM");
726
686
  };
727
687
  const registerExitHandler = () => {
728
- if (!exitHandlerRegistered) {
688
+ if (!s.exitHandlerRegistered) {
729
689
  process.on("exit", exitHandler);
730
- exitHandlerRegistered = true;
690
+ s.exitHandlerRegistered = true;
731
691
  }
732
692
  };
693
+ s.exitHandler = exitHandler;
694
+ s.registerExitHandler = registerExitHandler;
695
+ return s;
696
+ }
697
+ /**
698
+ * Send `signal` to the child's whole process group. Falls back to a
699
+ * single-process kill on Windows (no POSIX process groups). Shared
700
+ * across the foreground `exec` path, the background spawn / kill
701
+ * paths, and the shutdown-time orphan reaper — keeping one definition
702
+ * so the kill semantics can't drift between them.
703
+ */
704
+ function killProcessGroup(child, signal) {
705
+ const pid = child?.pid;
706
+ if (pid === void 0) return;
707
+ try {
708
+ if (SUPPORTS_PROCESS_GROUPS) process.kill(-pid, signal);
709
+ else process.kill(pid, signal);
710
+ } catch {}
711
+ }
712
+ /**
713
+ * Project a `TaskState` to the `TaskEntry` shape `listBackground` and
714
+ * `reassignBackgroundTasks` return. Single helper keeps the shape
715
+ * consistent across both call sites (and a future addition of fields
716
+ * to `TaskEntry` only needs to land here).
717
+ */
718
+ function stateToTaskEntry(state) {
733
719
  return {
734
- type: "process",
735
- capabilities: {
736
- shell: true,
737
- filesystem: true,
738
- glob: true,
739
- network: true,
740
- gpu: false,
741
- stream: true,
742
- isolated: isolation !== void 0 && isolationState?.ok === true,
743
- detachedTasks: "process-lifetime"
744
- },
745
- async spawn(overrides) {
746
- const id = `process-${++counter}`;
747
- const cwd = overrides?.cwd ?? defaultCwd;
748
- let needsCreate = false;
749
- try {
750
- await stat(cwd);
751
- } catch (err) {
752
- needsCreate = err?.code === "ENOENT";
720
+ taskId: state.taskId,
721
+ pid: state.pid,
722
+ command: state.command,
723
+ cwd: state.cwd,
724
+ startedAt: state.startedAt,
725
+ ...state.endedAt !== void 0 ? { endedAt: state.endedAt } : {},
726
+ outputPath: state.outputPath,
727
+ status: state.status,
728
+ ...state.exitCode !== void 0 ? { exitCode: state.exitCode } : {},
729
+ ...state.signal ? { signal: state.signal } : {},
730
+ bytesWritten: state.bytesWritten
731
+ };
732
+ }
733
+ /**
734
+ * Project a settled `TaskState` to the `TaskExitInfo` shape `settle()`
735
+ * fires from `onExit` and `killBackground` returns on the
736
+ * cached-exit path. Pre-condition: `state.status !== 'running'`
737
+ * (callers gate on this).
738
+ */
739
+ function stateToTaskExitInfo(state) {
740
+ return {
741
+ taskId: state.taskId,
742
+ status: state.status,
743
+ exitCode: state.exitCode ?? 0,
744
+ ...state.signal ? { signal: state.signal } : {},
745
+ outputPath: state.outputPath,
746
+ durationMs: (state.endedAt ?? Date.now()) - state.startedAt,
747
+ command: state.command
748
+ };
749
+ }
750
+ //#endregion
751
+ //#region src/contexts/process-background.ts
752
+ const NO_FOLLOW = constants.O_NOFOLLOW ?? 0;
753
+ /**
754
+ * The log's fd: a new file (exclusive), never through a symlink planted at
755
+ * its path. A name collision keeps the old append behavior on the existing
756
+ * regular file (still not following a link), as the comment at the call says.
757
+ */
758
+ function openTaskLog(path) {
759
+ const write = constants.O_WRONLY | constants.O_APPEND | NO_FOLLOW;
760
+ try {
761
+ return openSync(path, write | constants.O_CREAT | constants.O_EXCL, 384);
762
+ } catch (err) {
763
+ if (err.code !== "EEXIST") throw err;
764
+ return openSync(path, write);
765
+ }
766
+ }
767
+ function processBackgroundMethods(s, sh) {
768
+ const { baseEnv, defaultEnv, tasks, contextTimestamp, registerExitHandler } = s;
769
+ const { shellCommand, isolationBlocker, spawnTerminalTask } = sh;
770
+ return { async execBackground(handle, command, options) {
771
+ const cwd = options.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd;
772
+ const spawnChild = await loadSpawn();
773
+ const blocked = isolationBlocker();
774
+ if (blocked) throw new Error(blocked.stderr);
775
+ await mkdir(options.outputDir, { recursive: true });
776
+ const taskId = `bash_${++s.taskCounter}`;
777
+ assertSafeTaskId(taskId);
778
+ const outputPath = resolve(options.outputDir, `${taskId}.${contextTimestamp}.log`);
779
+ registerExitHandler();
780
+ const outputStream = createWriteStream(outputPath, { fd: openTaskLog(outputPath) });
781
+ outputStream.on("error", (err) => {
782
+ if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/contexts] task ${taskId} log stream error: ${err.message}\n`);
783
+ });
784
+ const shell = shellCommand(command);
785
+ const tty = options.terminal && hasLocalPty() ? options.terminal : void 0;
786
+ const child = tty ? spawnTerminalTask([shell.file, ...shell.args], cwd, {
787
+ ...TERMINAL_TASK_ENV,
788
+ ...options.env
789
+ }, tty) : spawnChild(shell.file, shell.args, {
790
+ cwd,
791
+ env: {
792
+ ...baseEnv,
793
+ ...defaultEnv,
794
+ ...options.env
795
+ },
796
+ stdio: [
797
+ "ignore",
798
+ "pipe",
799
+ "pipe"
800
+ ],
801
+ detached: SUPPORTS_PROCESS_GROUPS
802
+ });
803
+ const state = {
804
+ taskId,
805
+ handleId: handle.id,
806
+ pid: child.pid ?? -1,
807
+ command,
808
+ cwd,
809
+ startedAt: Date.now(),
810
+ outputPath,
811
+ outputStream,
812
+ child,
813
+ status: "running",
814
+ bytesWritten: 0,
815
+ settled: false,
816
+ onExit: options.onExit,
817
+ onStall: options.onStall,
818
+ exitWatchers: []
819
+ };
820
+ tasks.set(taskId, state);
821
+ const maxOutputBytes = typeof options.maxOutputBytes === "number" && options.maxOutputBytes > 0 ? options.maxOutputBytes : void 0;
822
+ let droppedBytes = 0;
823
+ let capExhausted = false;
824
+ const stallTimeoutMs = typeof options.stallTimeoutMs === "number" && options.stallTimeoutMs > 0 ? options.stallTimeoutMs : void 0;
825
+ let stallTimer;
826
+ let lastOutputAt = Date.now();
827
+ const armStallTimer = () => {
828
+ if (!stallTimeoutMs || !state.onStall) return;
829
+ if (stallTimer) clearTimeout(stallTimer);
830
+ stallTimer = setTimeout(() => {
831
+ stallTimer = void 0;
832
+ if (state.settled) return;
833
+ try {
834
+ state.onStall?.({
835
+ taskId,
836
+ command,
837
+ outputPath,
838
+ stalledForMs: Date.now() - lastOutputAt,
839
+ bytesWritten: state.bytesWritten
840
+ });
841
+ } catch (err) {
842
+ if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/contexts] task ${taskId} onStall threw: ${err instanceof Error ? err.message : String(err)}\n`);
843
+ }
844
+ }, stallTimeoutMs);
845
+ stallTimer.unref?.();
846
+ };
847
+ const clearStallTimer = () => {
848
+ if (stallTimer) {
849
+ clearTimeout(stallTimer);
850
+ stallTimer = void 0;
753
851
  }
754
- if (needsCreate) await mkdir(cwd, { recursive: true });
755
- const handle = {
756
- id,
757
- type: "process",
758
- cwd
852
+ };
853
+ armStallTimer();
854
+ const appendChunk = (chunk) => {
855
+ state.bytesWritten += chunk.length;
856
+ lastOutputAt = Date.now();
857
+ armStallTimer();
858
+ if (maxOutputBytes !== void 0) {
859
+ const onDisk = state.bytesWritten - droppedBytes - chunk.length;
860
+ if (capExhausted || onDisk >= maxOutputBytes) {
861
+ capExhausted = true;
862
+ droppedBytes += chunk.length;
863
+ return;
864
+ }
865
+ const room = maxOutputBytes - onDisk;
866
+ if (chunk.length > room) {
867
+ capExhausted = true;
868
+ let end = room;
869
+ while (end > 0 && (chunk[end] & 192) === 128) end--;
870
+ droppedBytes += chunk.length - end;
871
+ if (end > 0) outputStream.write(chunk.subarray(0, end));
872
+ return;
873
+ }
874
+ }
875
+ outputStream.write(chunk);
876
+ };
877
+ child.stdout?.on("data", (chunk) => appendChunk(chunk));
878
+ child.stderr?.on("data", (chunk) => appendChunk(chunk));
879
+ const settle = (cause, code, signal, errMessage) => {
880
+ if (state.settled) return;
881
+ state.settled = true;
882
+ clearStallTimer();
883
+ state.endedAt = Date.now();
884
+ const status = signal === "SIGTERM" || state.killRequested ? "killed" : "exited";
885
+ const exitCode = code !== null ? code : signal === "SIGTERM" ? 143 : signal ? 128 : 1;
886
+ state.status = status;
887
+ state.exitCode = exitCode;
888
+ if (signal) state.signal = signal;
889
+ if (errMessage) try {
890
+ outputStream.write(`\n${errMessage}\n`);
891
+ } catch {}
892
+ if (droppedBytes > 0) try {
893
+ outputStream.write(`\n<output-truncated bytes-dropped="${droppedBytes}"/>\n`);
894
+ } catch {}
895
+ outputStream.end(() => {
896
+ const info = stateToTaskExitInfo(state);
897
+ for (const callback of [state.onExit, ...state.exitWatchers.splice(0)]) try {
898
+ callback?.(info);
899
+ } catch (err) {
900
+ if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/contexts] task ${taskId} onExit threw: ${err instanceof Error ? err.message : String(err)}\n`);
901
+ }
902
+ });
903
+ state.child = void 0;
904
+ state.outputStream = void 0;
905
+ };
906
+ child.on("close", (code, signal) => settle("close", code, signal));
907
+ child.on("error", (err) => settle("error", null, null, `[spawn error] ${err.message}`));
908
+ child.on("exit", (code, signal) => {
909
+ setTimeout(() => {
910
+ if (state.settled) return;
911
+ child.stdout?.destroy();
912
+ child.stderr?.destroy();
913
+ settle("close", code, signal);
914
+ }, EXIT_PIPE_DRAIN_GRACE_MS).unref?.();
915
+ });
916
+ return {
917
+ taskId,
918
+ pid: state.pid,
919
+ outputPath,
920
+ ...tty ? { terminal: true } : {}
921
+ };
922
+ } };
923
+ }
924
+ //#endregion
925
+ //#region src/contexts/process-exec.ts
926
+ function processExecMethods(s, sh) {
927
+ const { config, baseEnv, defaultEnv, isolation } = s;
928
+ const { shellCommand, isolationBlocker } = sh;
929
+ return { async exec(handle, command, options) {
930
+ const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd;
931
+ if (options?.signal?.aborted) return {
932
+ stdout: "",
933
+ stderr: "aborted by signal before spawn",
934
+ exitCode: 143
935
+ };
936
+ const blocked = isolationBlocker();
937
+ if (blocked) return blocked;
938
+ const timeoutMs = (options?.timeout ?? config?.limits?.timeout ?? 30) * 1e3;
939
+ const maxBuffer = DEFAULT_MAX_OUTPUT_BUFFER;
940
+ const spawnChild = await loadSpawn();
941
+ return new Promise((resolveP) => {
942
+ const shell = shellCommand(command);
943
+ const child = spawnChild(shell.file, shell.args, {
944
+ cwd,
945
+ env: {
946
+ ...baseEnv,
947
+ ...defaultEnv,
948
+ ...options?.env
949
+ },
950
+ stdio: [
951
+ "ignore",
952
+ "pipe",
953
+ "pipe"
954
+ ],
955
+ detached: SUPPORTS_PROCESS_GROUPS
956
+ });
957
+ const stdoutSlot = {
958
+ text: "",
959
+ bytes: 0
759
960
  };
760
- handles.set(id, handle);
761
- return handle;
762
- },
763
- async exec(handle, command, options) {
764
- const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd;
765
- if (options?.signal?.aborted) return {
766
- stdout: "",
767
- stderr: "aborted by signal before spawn",
768
- exitCode: 143
961
+ const stderrSlot = {
962
+ text: "",
963
+ bytes: 0
769
964
  };
770
- const blocked = isolationBlocker();
771
- if (blocked) return blocked;
772
- const timeoutMs = (options?.timeout ?? config?.limits?.timeout ?? 30) * 1e3;
773
- const maxBuffer = DEFAULT_MAX_OUTPUT_BUFFER;
774
- const spawnChild = await loadSpawn();
775
- return new Promise((resolveP) => {
776
- const shell = shellCommand(command);
777
- const child = spawnChild(shell.file, shell.args, {
778
- cwd,
779
- env: {
780
- ...baseEnv,
781
- ...defaultEnv,
782
- ...options?.env
783
- },
784
- stdio: [
785
- "ignore",
786
- "pipe",
787
- "pipe"
788
- ],
789
- detached: SUPPORTS_PROCESS_GROUPS
790
- });
791
- const stdoutSlot = {
792
- text: "",
793
- bytes: 0
794
- };
795
- const stderrSlot = {
796
- text: "",
797
- bytes: 0
798
- };
799
- let bufferTruncated = false;
800
- let timedOut = false;
801
- let killedByAbort = false;
802
- let settled = false;
803
- const killWithEscalation = () => {
804
- killProcessGroup(child, "SIGTERM");
805
- setTimeout(() => {
806
- if (!settled) killProcessGroup(child, "SIGKILL");
807
- }, EXEC_KILL_ESCALATION_GRACE_MS).unref?.();
808
- };
809
- const onChunk = (slot, chunk) => {
810
- if (appendCapped(slot, chunk, maxBuffer) !== "ok" && !bufferTruncated) {
811
- bufferTruncated = true;
812
- killWithEscalation();
813
- }
814
- };
815
- child.stdout?.on("data", (chunk) => onChunk(stdoutSlot, chunk));
816
- child.stderr?.on("data", (chunk) => onChunk(stderrSlot, chunk));
817
- const timeoutTimer = timeoutMs > 0 ? setTimeout(() => {
818
- timedOut = true;
819
- killWithEscalation();
820
- }, timeoutMs) : void 0;
821
- const onAbort = () => {
822
- killedByAbort = true;
965
+ let bufferTruncated = false;
966
+ let timedOut = false;
967
+ let killedByAbort = false;
968
+ let settled = false;
969
+ const killWithEscalation = () => {
970
+ killProcessGroup(child, "SIGTERM");
971
+ setTimeout(() => {
972
+ if (!settled) killProcessGroup(child, "SIGKILL");
973
+ }, EXEC_KILL_ESCALATION_GRACE_MS).unref?.();
974
+ };
975
+ const onChunk = (slot, chunk) => {
976
+ if (appendCapped(slot, chunk, maxBuffer) !== "ok" && !bufferTruncated) {
977
+ bufferTruncated = true;
823
978
  killWithEscalation();
979
+ }
980
+ };
981
+ child.stdout?.on("data", (chunk) => onChunk(stdoutSlot, chunk));
982
+ child.stderr?.on("data", (chunk) => onChunk(stderrSlot, chunk));
983
+ const timeoutTimer = timeoutMs > 0 ? setTimeout(() => {
984
+ timedOut = true;
985
+ killWithEscalation();
986
+ }, timeoutMs) : void 0;
987
+ const onAbort = () => {
988
+ killedByAbort = true;
989
+ killWithEscalation();
990
+ };
991
+ const userSignal = options?.signal;
992
+ if (userSignal) userSignal.addEventListener("abort", onAbort, { once: true });
993
+ let exitGraceTimer;
994
+ const settle = (exitCode, extraStderr) => {
995
+ if (settled) return;
996
+ settled = true;
997
+ if (timeoutTimer) clearTimeout(timeoutTimer);
998
+ if (exitGraceTimer) clearTimeout(exitGraceTimer);
999
+ if (userSignal) userSignal.removeEventListener("abort", onAbort);
1000
+ const finalStderr = extraStderr ? stderrSlot.text ? `${stderrSlot.text}\n${extraStderr}` : extraStderr : stderrSlot.text;
1001
+ const result = {
1002
+ stdout: stdoutSlot.text,
1003
+ stderr: finalStderr,
1004
+ exitCode
824
1005
  };
825
- const userSignal = options?.signal;
826
- if (userSignal) userSignal.addEventListener("abort", onAbort, { once: true });
827
- let exitGraceTimer;
828
- const settle = (exitCode, extraStderr) => {
1006
+ resolveP(isolation ? annotateDenial(result, isolation) : result);
1007
+ };
1008
+ const settleFromStatus = (code, signal) => {
1009
+ if (killedByAbort) {
1010
+ settle(143, "aborted by signal");
1011
+ return;
1012
+ }
1013
+ if (timedOut) {
1014
+ settle(124, `command timed out after ${timeoutMs}ms`);
1015
+ return;
1016
+ }
1017
+ if (bufferTruncated) {
1018
+ settle(143, outputExceededMessage(maxBuffer, "process killed"));
1019
+ return;
1020
+ }
1021
+ if (signal) {
1022
+ settle(143, `terminated by signal ${signal}`);
1023
+ return;
1024
+ }
1025
+ settle(typeof code === "number" ? code : 1);
1026
+ };
1027
+ child.on("error", (err) => {
1028
+ settle(1, err.message);
1029
+ });
1030
+ child.on("close", (code, signal) => settleFromStatus(code, signal));
1031
+ child.on("exit", (code, signal) => {
1032
+ exitGraceTimer = setTimeout(() => {
829
1033
  if (settled) return;
830
- settled = true;
831
- if (timeoutTimer) clearTimeout(timeoutTimer);
832
- if (exitGraceTimer) clearTimeout(exitGraceTimer);
833
- if (userSignal) userSignal.removeEventListener("abort", onAbort);
834
- const finalStderr = extraStderr ? stderrSlot.text ? `${stderrSlot.text}\n${extraStderr}` : extraStderr : stderrSlot.text;
835
- const result = {
836
- stdout: stdoutSlot.text,
837
- stderr: finalStderr,
838
- exitCode
839
- };
840
- resolveP(isolation ? annotateDenial(result, isolation) : result);
841
- };
842
- const settleFromStatus = (code, signal) => {
843
- if (killedByAbort) {
844
- settle(143, "aborted by signal");
845
- return;
846
- }
847
- if (timedOut) {
848
- settle(124, `command timed out after ${timeoutMs}ms`);
849
- return;
850
- }
851
- if (bufferTruncated) {
852
- settle(143, outputExceededMessage(maxBuffer, "process killed"));
853
- return;
854
- }
855
- if (signal) {
856
- settle(143, `terminated by signal ${signal}`);
857
- return;
858
- }
859
- settle(typeof code === "number" ? code : 1);
860
- };
861
- child.on("error", (err) => {
862
- settle(1, err.message);
863
- });
864
- child.on("close", (code, signal) => settleFromStatus(code, signal));
865
- child.on("exit", (code, signal) => {
866
- exitGraceTimer = setTimeout(() => {
867
- if (settled) return;
868
- child.stdout?.destroy();
869
- child.stderr?.destroy();
870
- settleFromStatus(code, signal);
871
- }, EXIT_PIPE_DRAIN_GRACE_MS);
872
- exitGraceTimer.unref?.();
873
- });
1034
+ child.stdout?.destroy();
1035
+ child.stderr?.destroy();
1036
+ settleFromStatus(code, signal);
1037
+ }, EXIT_PIPE_DRAIN_GRACE_MS);
1038
+ exitGraceTimer.unref?.();
874
1039
  });
875
- },
876
- async readFile(handle, path) {
877
- return readFile(await resolveContained(handle, path), "utf-8");
1040
+ });
1041
+ } };
1042
+ }
1043
+ //#endregion
1044
+ //#region src/contexts/git-ls-files.ts
1045
+ /**
1046
+ * Pure (node-free) `git ls-files` helpers shared by the gitignore-aware paths
1047
+ * of the `glob` and `grep` tools.
1048
+ *
1049
+ * `git ls-files --cached --others --exclude-standard` lists tracked files plus
1050
+ * untracked-but-not-ignored files, applying `.gitignore`, `.git/info/exclude`,
1051
+ * and the user's global excludes. Git never descends ignored directories
1052
+ * (`node_modules`, build output, …), so this is both correct and sub-second on
1053
+ * large repos — unlike a raw filesystem walk.
1054
+ *
1055
+ * This module has ZERO node imports so it can be pulled into edge/workerd
1056
+ * bundles (e.g. via the `grep` tool, which drives git through the execution
1057
+ * context's `exec` seam). The node-spawn variant lives in
1058
+ * `./git-ls-files-local`.
1059
+ */
1060
+ /** Argv for `git ls-files`, NUL-delimited so paths with newlines stay intact. */
1061
+ const GIT_LS_FILES_ARGS = [
1062
+ "ls-files",
1063
+ "--cached",
1064
+ "--others",
1065
+ "--exclude-standard",
1066
+ "-z"
1067
+ ];
1068
+ /** The same invocation as a shell command string for the `exec` seam. */
1069
+ const GIT_LS_FILES_COMMAND = `git ${GIT_LS_FILES_ARGS.join(" ")}`;
1070
+ /** Split NUL-delimited `git ls-files -z` output into paths. */
1071
+ function parseGitLsFiles(stdout) {
1072
+ return stdout.split("\0").filter((p) => p.length > 0);
1073
+ }
1074
+ //#endregion
1075
+ //#region src/contexts/git-ls-files-local.ts
1076
+ /**
1077
+ * Node-spawn `git ls-files` runner used by the local process context's glob.
1078
+ *
1079
+ * Kept separate from `./git-ls-files` (which stays node-free for edge bundles)
1080
+ * because this leg imports `node:child_process`.
1081
+ */
1082
+ /**
1083
+ * Run `git ls-files` in `cwd` and return its non-ignored paths (relative to
1084
+ * `cwd`). Returns `null` when the directory is not a git repo, git is missing,
1085
+ * or the call aborts — callers treat `null` as "fall back to a filesystem
1086
+ * walk". Never throws.
1087
+ */
1088
+ async function gitListFilesLocal(cwd, signal) {
1089
+ return new Promise((resolveP) => {
1090
+ let child;
1091
+ try {
1092
+ child = spawn("git", [...GIT_LS_FILES_ARGS], {
1093
+ cwd,
1094
+ stdio: [
1095
+ "ignore",
1096
+ "pipe",
1097
+ "pipe"
1098
+ ]
1099
+ });
1100
+ } catch {
1101
+ resolveP(null);
1102
+ return;
1103
+ }
1104
+ if (signal?.aborted) {
1105
+ child.kill("SIGTERM");
1106
+ resolveP(null);
1107
+ return;
1108
+ }
1109
+ const stdoutChunks = [];
1110
+ const onAbort = () => child.kill("SIGTERM");
1111
+ signal?.addEventListener("abort", onAbort, { once: true });
1112
+ child.stdout?.setEncoding("utf8");
1113
+ child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
1114
+ child.on("error", () => {
1115
+ signal?.removeEventListener("abort", onAbort);
1116
+ resolveP(null);
1117
+ });
1118
+ child.on("close", (code) => {
1119
+ signal?.removeEventListener("abort", onAbort);
1120
+ if (code !== 0 || signal?.aborted) {
1121
+ resolveP(null);
1122
+ return;
1123
+ }
1124
+ resolveP(parseGitLsFiles(stdoutChunks.join("")));
1125
+ });
1126
+ });
1127
+ }
1128
+ //#endregion
1129
+ //#region src/contexts/glob.ts
1130
+ const loadNativeNodeGlob = lazyAsync(async () => {
1131
+ const candidate = (await import("node:fs/promises")).glob;
1132
+ return typeof candidate === "function" ? candidate : void 0;
1133
+ });
1134
+ function forcePortable() {
1135
+ return globalThis.__zidaneForcePortableGlobForTests === true;
1136
+ }
1137
+ async function loadNodeGlob() {
1138
+ if (forcePortable()) return void 0;
1139
+ return loadNativeNodeGlob();
1140
+ }
1141
+ /**
1142
+ * Bun's `node:fs/promises.glob` buffers the entire directory walk before
1143
+ * yielding (so an early `limit` break never fires — a broad `**` pattern over
1144
+ * a large `node_modules` hangs). `Bun.Glob().scan()` streams incrementally and
1145
+ * is the fast path under Bun. Returns undefined off Bun or when tests force the
1146
+ * portable walker.
1147
+ */
1148
+ function loadBunGlob() {
1149
+ if (forcePortable()) return void 0;
1150
+ const bun = globalThis.Bun;
1151
+ return bun && typeof bun.Glob === "function" ? bun.Glob : void 0;
1152
+ }
1153
+ function escapeRegexChar(char) {
1154
+ return /[\\^$.*+?()[\]{}|]/.test(char) ? `\\${char}` : char;
1155
+ }
1156
+ function expandBraces(pattern) {
1157
+ const start = pattern.indexOf("{");
1158
+ if (start === -1) return [pattern];
1159
+ const end = pattern.indexOf("}", start + 1);
1160
+ if (end === -1) return [pattern];
1161
+ const body = pattern.slice(start + 1, end);
1162
+ if (!body || !body.includes(",")) return [pattern];
1163
+ return body.split(",").flatMap((part) => expandBraces(`${pattern.slice(0, start)}${part}${pattern.slice(end + 1)}`));
1164
+ }
1165
+ function globPatternToRegex(pattern) {
1166
+ let out = "^";
1167
+ for (let i = 0; i < pattern.length; i++) {
1168
+ const char = pattern[i];
1169
+ const next = pattern[i + 1];
1170
+ if (char === "*") {
1171
+ if (next === "*") {
1172
+ if (pattern[i + 2] === "/") {
1173
+ out += "(?:[^/]+/)*";
1174
+ i += 2;
1175
+ } else {
1176
+ out += ".*";
1177
+ i += 1;
1178
+ }
1179
+ } else out += "[^/]*";
1180
+ } else if (char === "?") out += "[^/]";
1181
+ else if (char === "[") {
1182
+ const end = pattern.indexOf("]", i + 1);
1183
+ if (end === -1) out += "\\[";
1184
+ else {
1185
+ let cls = pattern.slice(i + 1, end);
1186
+ if (cls.startsWith("!")) cls = `^${cls.slice(1)}`;
1187
+ out += `[${cls}]`;
1188
+ i = end;
1189
+ }
1190
+ } else out += escapeRegexChar(char);
1191
+ }
1192
+ return new RegExp(`${out}$`);
1193
+ }
1194
+ function globLiteralPrefix(pattern) {
1195
+ const firstMeta = pattern.search(/[*?[\]{}]/);
1196
+ const prefix = firstMeta === -1 ? dirname(pattern) : pattern.slice(0, firstMeta);
1197
+ const slash = prefix.lastIndexOf("/");
1198
+ if (slash === -1) return "";
1199
+ return prefix.slice(0, slash);
1200
+ }
1201
+ /**
1202
+ * True when a pattern can match files outside `cwd` — an absolute path, a
1203
+ * Windows drive, or any `..` segment. Such patterns can't use the `git
1204
+ * ls-files` path (git only knows files under the repo) and fall back to the
1205
+ * filesystem walk.
1206
+ */
1207
+ function patternEscapesCwd(pattern) {
1208
+ const norm = pattern.split(sep).join("/");
1209
+ if (norm.startsWith("/") || /^[a-z]:/i.test(norm)) return true;
1210
+ return norm.split("/").includes("..");
1211
+ }
1212
+ async function addMetadata(cwd, rel, includeMetadata) {
1213
+ try {
1214
+ const s = await stat(resolve(cwd, rel));
1215
+ if (!s.isFile()) return null;
1216
+ return includeMetadata ? {
1217
+ path: rel,
1218
+ size: s.size,
1219
+ mtimeMs: s.mtimeMs
1220
+ } : { path: rel };
1221
+ } catch {
1222
+ return null;
1223
+ }
1224
+ }
1225
+ function compilePattern(pattern) {
1226
+ return expandBraces(pattern.split(sep).join("/")).map(globPatternToRegex);
1227
+ }
1228
+ /** Match the (already non-ignored) `git ls-files` set against the glob pattern. */
1229
+ async function matchGitFiles(gitFiles, pattern, cwd, limit, includeMetadata, signal) {
1230
+ const regexes = compilePattern(pattern);
1231
+ const out = [];
1232
+ for (const rel of gitFiles) {
1233
+ if (signal?.aborted || out.length >= limit) break;
1234
+ if (!regexes.some((regex) => regex.test(rel))) continue;
1235
+ out.push(includeMetadata ? await addMetadata(cwd, rel, true) ?? { path: rel } : { path: rel });
1236
+ }
1237
+ return out.sort((a, b) => a.path.localeCompare(b.path));
1238
+ }
1239
+ /** Depth-first walk that matches against `regexes` and stops at `limit`. */
1240
+ async function walkMatch(root, prefix, regexes, cwd, limit, includeMetadata, signal, out) {
1241
+ if (out.length >= limit || signal?.aborted) return;
1242
+ let entries;
1243
+ try {
1244
+ entries = await readdir(resolve(root, prefix), {
1245
+ encoding: "utf8",
1246
+ withFileTypes: true
1247
+ });
1248
+ } catch {
1249
+ return;
1250
+ }
1251
+ for (const entry of entries) {
1252
+ if (out.length >= limit || signal?.aborted) return;
1253
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
1254
+ if (entry.isDirectory()) await walkMatch(root, rel, regexes, cwd, limit, includeMetadata, signal, out);
1255
+ else if (entry.isFile() && regexes.some((regex) => regex.test(rel))) out.push(includeMetadata ? await addMetadata(cwd, rel, true) ?? { path: rel } : { path: rel });
1256
+ }
1257
+ }
1258
+ async function globPortable(pattern, cwd, limit, includeMetadata, signal) {
1259
+ const normalized = pattern.split(sep).join("/");
1260
+ const startPrefix = globLiteralPrefix(normalized);
1261
+ const regexes = expandBraces(normalized).map(globPatternToRegex);
1262
+ const out = [];
1263
+ await walkMatch(cwd, startPrefix, regexes, cwd, limit, includeMetadata, signal, out);
1264
+ return out.sort((a, b) => a.path.localeCompare(b.path));
1265
+ }
1266
+ async function collectFromIterable(iter, cwd, limit, includeMetadata, signal) {
1267
+ const results = [];
1268
+ try {
1269
+ for await (const rel of iter) {
1270
+ if (signal?.aborted) break;
1271
+ const entry = await addMetadata(cwd, rel, includeMetadata);
1272
+ if (!entry) continue;
1273
+ results.push(entry);
1274
+ if (results.length >= limit) break;
1275
+ }
1276
+ } catch {
1277
+ return null;
1278
+ }
1279
+ return results.sort((a, b) => a.path.localeCompare(b.path));
1280
+ }
1281
+ function globViaBun(BunGlob, pattern, cwd, limit, includeMetadata, signal) {
1282
+ return collectFromIterable(new BunGlob(pattern).scan({
1283
+ cwd,
1284
+ onlyFiles: true,
1285
+ dot: true
1286
+ }), cwd, limit, includeMetadata, signal);
1287
+ }
1288
+ async function globFilesLocal(pattern, cwd, options) {
1289
+ const includeMetadata = options.metadata === true;
1290
+ const { limit, signal } = options;
1291
+ if (options.gitignore !== false && !forcePortable() && !patternEscapesCwd(pattern)) {
1292
+ const gitFiles = await gitListFilesLocal(cwd, signal);
1293
+ if (gitFiles) return matchGitFiles(gitFiles, pattern, cwd, limit, includeMetadata, signal);
1294
+ }
1295
+ const bunGlob = loadBunGlob();
1296
+ if (bunGlob) {
1297
+ const viaBun = await globViaBun(bunGlob, pattern, cwd, limit, includeMetadata, signal);
1298
+ if (viaBun) return viaBun;
1299
+ } else {
1300
+ const nodeGlob = await loadNodeGlob();
1301
+ if (nodeGlob) {
1302
+ const viaNode = await collectFromIterable(nodeGlob(pattern, { cwd }), cwd, limit, includeMetadata, signal);
1303
+ if (viaNode) return viaNode;
1304
+ }
1305
+ }
1306
+ return globPortable(pattern, cwd, limit, includeMetadata, signal);
1307
+ }
1308
+ //#endregion
1309
+ //#region src/contexts/process-files.ts
1310
+ function processFileMethods(s) {
1311
+ const { workspaceRoot, isolation } = s;
1312
+ /**
1313
+ * Resolve `path` against the handle cwd and, when a `workspaceRoot` is
1314
+ * configured, reject anything that escapes it. Uses `realpath` on the
1315
+ * nearest existing ancestor so symlink escapes (and `..`) are caught for
1316
+ * both existing and not-yet-created targets.
1317
+ */
1318
+ async function resolveContained(handle, path) {
1319
+ const full = resolve(handle.cwd, path);
1320
+ if (workspaceRoot === void 0) return full;
1321
+ const canonical = await canonicalizeForCheck(full);
1322
+ const canonicalRoot = await realpath(workspaceRoot).catch(() => workspaceRoot);
1323
+ if (!isWithinRoot(canonicalRoot, canonical)) throw new Error(`Path escapes the workspace root: ${path} (resolved outside ${canonicalRoot})`);
1324
+ return full;
1325
+ }
1326
+ /**
1327
+ * Canonicalize the deepest existing ancestor of `full` to defeat symlink
1328
+ * escapes; the non-existent tail (for writes) is appended back verbatim.
1329
+ */
1330
+ async function canonicalizeForCheck(full) {
1331
+ let existing = full;
1332
+ const tail = [];
1333
+ while (true) try {
1334
+ existing = await realpath(existing);
1335
+ break;
1336
+ } catch {
1337
+ const parent = dirname(existing);
1338
+ if (parent === existing) {
1339
+ existing = parent;
1340
+ break;
1341
+ }
1342
+ tail.unshift(relative(parent, existing));
1343
+ existing = parent;
1344
+ }
1345
+ return tail.length ? resolve(existing, ...tail) : existing;
1346
+ }
1347
+ /**
1348
+ * In-process counterpart of the sandbox's write policy: `writeFile` (and
1349
+ * so the `write_file` / `edit` tools) may only target `isolation.writableRoots`.
1350
+ * Reads are deliberately unrestricted — same boundary the kernel enforces
1351
+ * for shell commands, so the two never disagree about where writes land.
1352
+ */
1353
+ async function assertWritable(full, path) {
1354
+ if (!isolation) return;
1355
+ const canonical = await canonicalizeForCheck(full);
1356
+ if (!isolation.writableRoots.some((root) => isWithinRoot(root, canonical))) throw new Error(`[sandbox] write denied: ${path} is outside the writable roots (${isolation.writableRoots.join(", ")}). Ask the user to add it under Settings → "Sandbox writable paths" or turn off "Sandbox shell commands".`);
1357
+ }
1358
+ return {
1359
+ async readFile(handle, path) {
1360
+ return readFile(await resolveContained(handle, path), "utf-8");
878
1361
  },
879
1362
  async fileSize(handle, path) {
880
1363
  try {
@@ -913,67 +1396,252 @@ function createProcessContext(config) {
913
1396
  return contained;
914
1397
  }
915
1398
  return (await readdir(await resolveContained(handle, path), { withFileTypes: true })).map((d) => d.isDirectory() ? `${d.name}/` : d.name);
1399
+ }
1400
+ };
1401
+ }
1402
+ //#endregion
1403
+ //#region src/contexts/ansi-strip.ts
1404
+ /** Parser states: plain text, after ESC, in a CSI, in an OSC/DCS string, after ESC inside one. */
1405
+ const State = {
1406
+ Text: 0,
1407
+ Esc: 1,
1408
+ Csi: 2,
1409
+ Str: 3,
1410
+ StrEsc: 4
1411
+ };
1412
+ function createAnsiStripper(options = {}) {
1413
+ let state = State.Text;
1414
+ /** Body of the OSC / DCS / APC string being read; OSC only is kept. */
1415
+ let str = "";
1416
+ let strIsOsc = false;
1417
+ let line = "";
1418
+ /** A `\r` arrived; the next printable rewinds the line unless `\n` follows. */
1419
+ let pendingCr = false;
1420
+ /** Part of the current line was handed out by `takePartial`: a rewind must start a new line. */
1421
+ let taken = false;
1422
+ const endString = () => {
1423
+ if (strIsOsc && options.onTitle) {
1424
+ const semi = str.indexOf(";");
1425
+ const code = semi === -1 ? str : str.slice(0, semi);
1426
+ if ((code === "0" || code === "2") && semi !== -1) options.onTitle(str.slice(semi + 1));
1427
+ }
1428
+ str = "";
1429
+ state = State.Text;
1430
+ };
1431
+ return {
1432
+ push(chunk) {
1433
+ let out = "";
1434
+ for (let i = 0; i < chunk.length; i++) {
1435
+ const ch = chunk[i];
1436
+ const code = ch.charCodeAt(0);
1437
+ switch (state) {
1438
+ case State.Text:
1439
+ if (code === 27) state = State.Esc;
1440
+ else if (ch === "\n") {
1441
+ out += `${line}\n`;
1442
+ line = "";
1443
+ pendingCr = false;
1444
+ taken = false;
1445
+ } else if (ch === "\r") pendingCr = true;
1446
+ else if (code === 8) line = line.slice(0, -1);
1447
+ else if (code === 155) state = State.Csi;
1448
+ else if (code === 157) {
1449
+ state = State.Str;
1450
+ strIsOsc = true;
1451
+ } else if (ch === " " || code >= 32) {
1452
+ if (code >= 127 && code < 160) break;
1453
+ if (pendingCr) {
1454
+ if (taken) out += "\n";
1455
+ taken = false;
1456
+ line = "";
1457
+ pendingCr = false;
1458
+ }
1459
+ line += ch;
1460
+ }
1461
+ break;
1462
+ case State.Esc:
1463
+ if (ch === "[") state = State.Csi;
1464
+ else if (ch === "]" || ch === "P" || ch === "X" || ch === "^" || ch === "_") {
1465
+ state = State.Str;
1466
+ strIsOsc = ch === "]";
1467
+ str = "";
1468
+ } else if (code >= 32 && code <= 47) {} else state = State.Text;
1469
+ break;
1470
+ case State.Csi:
1471
+ if (code >= 64 && code <= 126) state = State.Text;
1472
+ break;
1473
+ case State.Str:
1474
+ if (code === 7) endString();
1475
+ else if (code === 27) state = State.StrEsc;
1476
+ else if (code === 156) endString();
1477
+ else if (str.length < 4096) str += ch;
1478
+ break;
1479
+ case State.StrEsc: if (ch === "\\") endString();
1480
+ else {
1481
+ str += `\x1B${ch}`;
1482
+ state = State.Str;
1483
+ }
1484
+ }
1485
+ }
1486
+ return out;
916
1487
  },
917
- /**
918
- * Live bidirectional stream to a child process — see
919
- * `ExecutionContext.execStream` for the seam contract.
920
- *
921
- * The reference implementation: an ordinary piped `spawn`, so both
922
- * streams stay separate and no terminal line discipline sits between
923
- * the caller and the child. Sandbox-backed contexts have to work
924
- * harder to reach the same shape.
925
- *
926
- * Unlike `execBackground`, nothing here lands in the `tasks` registry:
927
- * this handle is owned by its caller (an MCP transport's `close()`),
928
- * not by the model-facing background-task list that `shell_kill` /
929
- * `wait_task` walk. It IS tracked in `streamChildren` so the shared
930
- * orphan reaper and `destroy()` can sweep it — detached process-group
931
- * semantics (needed so `kill()` takes the child's own children with
932
- * it) are exactly what would otherwise leak a live server per crash.
933
- */
934
- async execStream(handle, command, options) {
935
- const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd;
936
- const spawnChild = await loadSpawn();
937
- options?.signal?.throwIfAborted();
938
- const blocked = isolationBlocker();
939
- if (blocked) throw new Error(blocked.stderr);
940
- const shell = shellCommand(command);
941
- const child = spawnChild(shell.file, shell.args, {
942
- cwd,
943
- env: {
944
- ...baseEnv,
945
- ...defaultEnv,
946
- ...options?.env
947
- },
948
- stdio: [
949
- "pipe",
950
- "pipe",
951
- "pipe"
952
- ],
953
- detached: SUPPORTS_PROCESS_GROUPS
954
- });
955
- registerExitHandler();
956
- streamChildren.set(child, handle.id);
957
- let spawnError = null;
958
- child.on("error", (err) => {
959
- spawnError = err;
960
- });
961
- const exited = new Promise((resolveExit) => {
962
- child.once("close", (code, signalName) => {
963
- streamChildren.delete(child);
964
- resolveExit(code !== null ? code : signalName === "SIGTERM" ? 143 : signalName ? 128 : 1);
965
- });
1488
+ partial: () => line,
1489
+ takePartial() {
1490
+ const text = line;
1491
+ if (text) taken = true;
1492
+ line = "";
1493
+ return text;
1494
+ }
1495
+ };
1496
+ }
1497
+ //#endregion
1498
+ //#region src/contexts/process-shell.ts
1499
+ function createProcessShell(s) {
1500
+ const { baseEnv, defaultEnv, isolation, isolationState } = s;
1501
+ /**
1502
+ * `{ file, args }` for `/bin/sh -c command`, wrapped in the sandbox when
1503
+ * isolation is on. Callers check `isolationState.ok` first (the three
1504
+ * shell entry points return `unavailableResult` / throw before spawning).
1505
+ */
1506
+ function shellCommand(command) {
1507
+ if (isolation && isolationState?.ok) return wrapCommand(isolationState.backend, isolationState.executable, isolation, command);
1508
+ return {
1509
+ file: "/bin/sh",
1510
+ args: ["-c", command]
1511
+ };
1512
+ }
1513
+ /** Failure result when isolation was requested but cannot be enforced; `undefined` = proceed. */
1514
+ function isolationBlocker() {
1515
+ return isolationState && !isolationState.ok ? unavailableResult(isolationState.reason) : void 0;
1516
+ }
1517
+ /**
1518
+ * A background task in a PTY, as a `ChildProcess` look-alike whose stdout
1519
+ * carries the log text. A line still unfinished after a quiet moment (a
1520
+ * prompt waiting for input) is written out too, so the log shows it.
1521
+ */
1522
+ function spawnTerminalTask(argv, cwd, extraEnv, tty) {
1523
+ const stripper = createAnsiStripper();
1524
+ const decoder = new TextDecoder();
1525
+ let quiet;
1526
+ let emitLog = () => {};
1527
+ const flushPartial = () => {
1528
+ const rest = stripper.takePartial();
1529
+ if (rest) emitLog(rest);
1530
+ };
1531
+ const pty = spawnLocalPty(argv, {
1532
+ cwd,
1533
+ env: terminalEnv(extraEnv),
1534
+ cols: tty.cols,
1535
+ rows: tty.rows,
1536
+ onData: (data) => {
1537
+ tty.onData(data);
1538
+ const text = stripper.push(decoder.decode(data, { stream: true }));
1539
+ if (text) emitLog(text);
1540
+ if (quiet) clearTimeout(quiet);
1541
+ quiet = setTimeout(flushPartial, 300);
1542
+ quiet.unref?.();
1543
+ }
1544
+ });
1545
+ const { child, stdout } = ptyChildProcess(pty);
1546
+ const encoder = new TextEncoder();
1547
+ emitLog = (text) => stdout.emit("data", encoder.encode(text));
1548
+ pty.exited.then(() => {
1549
+ if (quiet) clearTimeout(quiet);
1550
+ flushPartial();
1551
+ });
1552
+ tty.onReady?.({
1553
+ write: pty.write,
1554
+ resize: pty.resize
1555
+ });
1556
+ return child;
1557
+ }
1558
+ /** The environment of a terminal: what commands get, plus terminal identity, minus `unset`. */
1559
+ function terminalEnv(extra, unset = []) {
1560
+ const env = {};
1561
+ for (const [key, value] of Object.entries({
1562
+ ...baseEnv,
1563
+ ...defaultEnv,
1564
+ ...extra,
1565
+ ...TERMINAL_ENV
1566
+ })) if (typeof value === "string" && !unset.includes(key)) env[key] = value;
1567
+ return env;
1568
+ }
1569
+ return {
1570
+ shellCommand,
1571
+ isolationBlocker,
1572
+ spawnTerminalTask,
1573
+ terminalEnv
1574
+ };
1575
+ }
1576
+ //#endregion
1577
+ //#region src/contexts/process-stream.ts
1578
+ function processStreamMethods(s, sh) {
1579
+ const { baseEnv, defaultEnv, registerExitHandler, streamChildren } = s;
1580
+ const { shellCommand, isolationBlocker } = sh;
1581
+ return {
1582
+ /**
1583
+ * Live bidirectional stream to a child process — see
1584
+ * `ExecutionContext.execStream` for the seam contract.
1585
+ *
1586
+ * The reference implementation: an ordinary piped `spawn`, so both
1587
+ * streams stay separate and no terminal line discipline sits between
1588
+ * the caller and the child. Sandbox-backed contexts have to work
1589
+ * harder to reach the same shape.
1590
+ *
1591
+ * Unlike `execBackground`, nothing here lands in the `tasks` registry:
1592
+ * this handle is owned by its caller (an MCP transport's `close()`),
1593
+ * not by the model-facing background-task list that `shell_kill` /
1594
+ * `wait_task` walk. It IS tracked in `streamChildren` so the shared
1595
+ * orphan reaper and `destroy()` can sweep it — detached process-group
1596
+ * semantics (needed so `kill()` takes the child's own children with
1597
+ * it) are exactly what would otherwise leak a live server per crash.
1598
+ */
1599
+ async execStream(handle, command, options) {
1600
+ const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd;
1601
+ const spawnChild = await loadSpawn();
1602
+ options?.signal?.throwIfAborted();
1603
+ const blocked = isolationBlocker();
1604
+ if (blocked) throw new Error(blocked.stderr);
1605
+ const shell = shellCommand(command);
1606
+ const child = spawnChild(shell.file, shell.args, {
1607
+ cwd,
1608
+ env: {
1609
+ ...baseEnv,
1610
+ ...defaultEnv,
1611
+ ...options?.env
1612
+ },
1613
+ stdio: [
1614
+ "pipe",
1615
+ "pipe",
1616
+ "pipe"
1617
+ ],
1618
+ detached: SUPPORTS_PROCESS_GROUPS
1619
+ });
1620
+ registerExitHandler();
1621
+ streamChildren.set(child, handle.id);
1622
+ let spawnError = null;
1623
+ child.on("error", (err) => {
1624
+ spawnError = err;
1625
+ });
1626
+ let closed = false;
1627
+ let escalation;
1628
+ const exited = new Promise((resolveExit) => {
1629
+ child.once("close", (code, signalName) => {
1630
+ closed = true;
1631
+ clearTimeout(escalation);
1632
+ streamChildren.delete(child);
1633
+ resolveExit(code !== null ? code : signalName === "SIGTERM" ? 143 : signalName ? 128 : 1);
966
1634
  });
967
- const kill = async () => {
968
- if (child.exitCode !== null || child.signalCode !== null) return;
969
- const pid = child.pid;
970
- if (pid === void 0) return;
971
- try {
972
- if (SUPPORTS_PROCESS_GROUPS) process.kill(-pid, "SIGTERM");
973
- else process.kill(pid, "SIGTERM");
974
- } catch {}
975
- };
976
- if (options?.signal) if (options.signal.aborted) kill();
1635
+ });
1636
+ const kill = async () => {
1637
+ if (closed || child.pid === void 0) return;
1638
+ killProcessGroup(child, "SIGTERM");
1639
+ if (escalation) return;
1640
+ escalation = setTimeout(killProcessGroup, EXEC_KILL_ESCALATION_GRACE_MS, child, "SIGKILL");
1641
+ escalation.unref?.();
1642
+ };
1643
+ if (options?.signal) {
1644
+ if (options.signal.aborted) kill();
977
1645
  else {
978
1646
  const onAbort = () => {
979
1647
  kill();
@@ -982,168 +1650,29 @@ function createProcessContext(config) {
982
1650
  options.signal.addEventListener("abort", onAbort, { once: true });
983
1651
  exited.then(detach, detach);
984
1652
  }
985
- return {
986
- pid: child.pid ?? -1,
987
- stdout: child.stdout,
988
- stderr: child.stderr,
989
- write: async (data) => {
990
- if (spawnError) throw spawnError;
991
- const stdin = child.stdin;
992
- if (!stdin || stdin.destroyed) throw new Error(`execStream: stdin is closed (pid ${child.pid ?? -1})`);
993
- await new Promise((resolveWrite, rejectWrite) => {
994
- stdin.write(data, (err) => err ? rejectWrite(err) : resolveWrite());
995
- });
996
- },
997
- kill,
998
- exited
999
- };
1000
- },
1001
- async execBackground(handle, command, options) {
1002
- const cwd = options.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd;
1003
- const spawnChild = await loadSpawn();
1004
- const blocked = isolationBlocker();
1005
- if (blocked) throw new Error(blocked.stderr);
1006
- await mkdir(options.outputDir, { recursive: true });
1007
- const taskId = `bash_${++taskCounter}`;
1008
- assertSafeTaskId(taskId);
1009
- const outputPath = resolve(options.outputDir, `${taskId}.${contextTimestamp}.log`);
1010
- registerExitHandler();
1011
- const outputStream = createWriteStream(outputPath, { flags: "a" });
1012
- outputStream.on("error", (err) => {
1013
- if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/contexts] task ${taskId} log stream error: ${err.message}\n`);
1014
- });
1015
- const shell = shellCommand(command);
1016
- const child = spawnChild(shell.file, shell.args, {
1017
- cwd,
1018
- env: {
1019
- ...baseEnv,
1020
- ...defaultEnv,
1021
- ...options.env
1022
- },
1023
- stdio: [
1024
- "ignore",
1025
- "pipe",
1026
- "pipe"
1027
- ],
1028
- detached: SUPPORTS_PROCESS_GROUPS
1029
- });
1030
- const state = {
1031
- taskId,
1032
- handleId: handle.id,
1033
- pid: child.pid ?? -1,
1034
- command,
1035
- cwd,
1036
- startedAt: Date.now(),
1037
- outputPath,
1038
- outputStream,
1039
- child,
1040
- status: "running",
1041
- bytesWritten: 0,
1042
- settled: false,
1043
- onExit: options.onExit
1044
- };
1045
- tasks.set(taskId, state);
1046
- const maxOutputBytes = typeof options.maxOutputBytes === "number" && options.maxOutputBytes > 0 ? options.maxOutputBytes : void 0;
1047
- let droppedBytes = 0;
1048
- let capExhausted = false;
1049
- const stallTimeoutMs = typeof options.stallTimeoutMs === "number" && options.stallTimeoutMs > 0 ? options.stallTimeoutMs : void 0;
1050
- const onStall = options.onStall;
1051
- let stallTimer;
1052
- let lastOutputAt = Date.now();
1053
- const armStallTimer = () => {
1054
- if (!stallTimeoutMs || !onStall) return;
1055
- if (stallTimer) clearTimeout(stallTimer);
1056
- stallTimer = setTimeout(() => {
1057
- stallTimer = void 0;
1058
- if (state.settled) return;
1059
- try {
1060
- onStall({
1061
- taskId,
1062
- command,
1063
- outputPath,
1064
- stalledForMs: Date.now() - lastOutputAt,
1065
- bytesWritten: state.bytesWritten
1066
- });
1067
- } catch (err) {
1068
- if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/contexts] task ${taskId} onStall threw: ${err instanceof Error ? err.message : String(err)}\n`);
1069
- }
1070
- }, stallTimeoutMs);
1071
- stallTimer.unref?.();
1072
- };
1073
- const clearStallTimer = () => {
1074
- if (stallTimer) {
1075
- clearTimeout(stallTimer);
1076
- stallTimer = void 0;
1077
- }
1078
- };
1079
- armStallTimer();
1080
- const appendChunk = (chunk) => {
1081
- state.bytesWritten += chunk.length;
1082
- lastOutputAt = Date.now();
1083
- armStallTimer();
1084
- if (maxOutputBytes !== void 0) {
1085
- const onDisk = state.bytesWritten - droppedBytes - chunk.length;
1086
- if (capExhausted || onDisk >= maxOutputBytes) {
1087
- capExhausted = true;
1088
- droppedBytes += chunk.length;
1089
- return;
1090
- }
1091
- const room = maxOutputBytes - onDisk;
1092
- if (chunk.length > room) {
1093
- capExhausted = true;
1094
- let end = room;
1095
- while (end > 0 && (chunk[end] & 192) === 128) end--;
1096
- droppedBytes += chunk.length - end;
1097
- if (end > 0) outputStream.write(chunk.subarray(0, end));
1098
- return;
1099
- }
1100
- }
1101
- outputStream.write(chunk);
1102
- };
1103
- child.stdout?.on("data", (chunk) => appendChunk(chunk));
1104
- child.stderr?.on("data", (chunk) => appendChunk(chunk));
1105
- const settle = (cause, code, signal, errMessage) => {
1106
- if (state.settled) return;
1107
- state.settled = true;
1108
- clearStallTimer();
1109
- state.endedAt = Date.now();
1110
- const status = signal === "SIGTERM" || state.killRequested ? "killed" : "exited";
1111
- const exitCode = code !== null ? code : signal === "SIGTERM" ? 143 : signal ? 128 : 1;
1112
- state.status = status;
1113
- state.exitCode = exitCode;
1114
- if (signal) state.signal = signal;
1115
- if (errMessage) try {
1116
- outputStream.write(`\n${errMessage}\n`);
1117
- } catch {}
1118
- if (droppedBytes > 0) try {
1119
- outputStream.write(`\n<output-truncated bytes-dropped="${droppedBytes}"/>\n`);
1120
- } catch {}
1121
- outputStream.end(() => {
1122
- try {
1123
- state.onExit?.(stateToTaskExitInfo(state));
1124
- } catch (err) {
1125
- if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/contexts] task ${taskId} onExit threw: ${err instanceof Error ? err.message : String(err)}\n`);
1126
- }
1653
+ }
1654
+ return {
1655
+ pid: child.pid ?? -1,
1656
+ stdout: child.stdout,
1657
+ stderr: child.stderr,
1658
+ write: async (data) => {
1659
+ if (spawnError) throw spawnError;
1660
+ const stdin = child.stdin;
1661
+ if (!stdin || stdin.destroyed) throw new Error(`execStream: stdin is closed (pid ${child.pid ?? -1})`);
1662
+ await new Promise((resolveWrite, rejectWrite) => {
1663
+ stdin.write(data, (err) => err ? rejectWrite(err) : resolveWrite());
1127
1664
  });
1128
- state.child = void 0;
1129
- state.outputStream = void 0;
1130
- };
1131
- child.on("close", (code, signal) => settle("close", code, signal));
1132
- child.on("error", (err) => settle("error", null, null, `[spawn error] ${err.message}`));
1133
- child.on("exit", (code, signal) => {
1134
- setTimeout(() => {
1135
- if (state.settled) return;
1136
- child.stdout?.destroy();
1137
- child.stderr?.destroy();
1138
- settle("close", code, signal);
1139
- }, EXIT_PIPE_DRAIN_GRACE_MS).unref?.();
1140
- });
1141
- return {
1142
- taskId,
1143
- pid: state.pid,
1144
- outputPath
1145
- };
1146
- },
1665
+ },
1666
+ kill,
1667
+ exited
1668
+ };
1669
+ } };
1670
+ }
1671
+ //#endregion
1672
+ //#region src/contexts/process-tasks.ts
1673
+ function processTaskMethods(s) {
1674
+ const { tasks, destroyGraceMs, streamChildren, handles } = s;
1675
+ return {
1147
1676
  async killBackground(handle, taskId) {
1148
1677
  const state = tasks.get(taskId);
1149
1678
  if (!state || state.handleId !== handle.id) return null;
@@ -1154,11 +1683,7 @@ function createProcessContext(config) {
1154
1683
  resolveP();
1155
1684
  return;
1156
1685
  }
1157
- const originalOnExit = state.onExit;
1158
- state.onExit = (info) => {
1159
- originalOnExit?.(info);
1160
- resolveP();
1161
- };
1686
+ state.exitWatchers.push(() => resolveP());
1162
1687
  });
1163
1688
  killProcessGroup(state.child, "SIGTERM");
1164
1689
  await closed;
@@ -1177,16 +1702,18 @@ function createProcessContext(config) {
1177
1702
  done = true;
1178
1703
  if (timer) clearTimeout(timer);
1179
1704
  signal?.removeEventListener("abort", onAbort);
1705
+ const index = state.exitWatchers.indexOf(watcher);
1706
+ if (index >= 0) state.exitWatchers.splice(index, 1);
1180
1707
  resolveP(value);
1181
1708
  };
1182
1709
  function onAbort() {
1183
1710
  finish(null);
1184
1711
  }
1185
- const originalOnExit = state.onExit;
1186
- state.onExit = (info) => {
1187
- originalOnExit?.(info);
1188
- finish(stateToTaskExitInfo(state));
1189
- };
1712
+ const task = state;
1713
+ function watcher() {
1714
+ finish(stateToTaskExitInfo(task));
1715
+ }
1716
+ state.exitWatchers.push(watcher);
1190
1717
  if (signal) {
1191
1718
  if (signal.aborted) {
1192
1719
  finish(null);
@@ -1201,13 +1728,13 @@ function createProcessContext(config) {
1201
1728
  }
1202
1729
  });
1203
1730
  },
1204
- async reassignBackgroundTasks(fromHandle, toHandle, newOnExit) {
1205
- if (fromHandle.id === toHandle.id) return [];
1731
+ async reassignBackgroundTasks(fromHandle, toHandle, newOnExit, newOnStall) {
1206
1732
  const promoted = [];
1207
1733
  for (const state of tasks.values()) {
1208
1734
  if (state.handleId !== fromHandle.id || state.status !== "running") continue;
1209
1735
  state.handleId = toHandle.id;
1210
1736
  if (newOnExit) state.onExit = newOnExit;
1737
+ if (newOnStall) state.onStall = newOnStall;
1211
1738
  promoted.push(stateToTaskEntry(state));
1212
1739
  }
1213
1740
  return promoted;
@@ -1221,12 +1748,10 @@ function createProcessContext(config) {
1221
1748
  state.killRequested = true;
1222
1749
  await new Promise((resolveP) => {
1223
1750
  let graceTimer;
1224
- const originalOnExit = state.onExit;
1225
- state.onExit = (info) => {
1226
- originalOnExit?.(info);
1751
+ state.exitWatchers.push(() => {
1227
1752
  if (graceTimer) clearTimeout(graceTimer);
1228
1753
  resolveP();
1229
- };
1754
+ });
1230
1755
  killProcessGroup(state.child, "SIGTERM");
1231
1756
  graceTimer = setTimeout(() => {
1232
1757
  killProcessGroup(state.child, "SIGKILL");
@@ -1242,76 +1767,86 @@ function createProcessContext(config) {
1242
1767
  streamChildren.delete(child);
1243
1768
  }
1244
1769
  handles.delete(handle.id);
1245
- if (exitHandlerRegistered && handles.size === 0) {
1246
- process.off("exit", exitHandler);
1247
- exitHandlerRegistered = false;
1770
+ if (s.exitHandlerRegistered && handles.size === 0) {
1771
+ process.off("exit", s.exitHandler);
1772
+ s.exitHandlerRegistered = false;
1248
1773
  }
1249
1774
  }
1250
1775
  };
1251
1776
  }
1252
- /**
1253
- * Send `signal` to the child's whole process group. Falls back to a
1254
- * single-process kill on Windows (no POSIX process groups). Shared
1255
- * across the foreground `exec` path, the background spawn / kill
1256
- * paths, and the shutdown-time orphan reaper — keeping one definition
1257
- * so the kill semantics can't drift between them.
1258
- */
1259
- function killProcessGroup(child, signal) {
1260
- const pid = child?.pid;
1261
- if (pid === void 0) return;
1262
- try {
1263
- if (SUPPORTS_PROCESS_GROUPS) process.kill(-pid, signal);
1264
- else process.kill(pid, signal);
1265
- } catch {}
1266
- }
1267
- /**
1268
- * Project a `TaskState` to the `TaskEntry` shape `listBackground` and
1269
- * `reassignBackgroundTasks` return. Single helper keeps the shape
1270
- * consistent across both call sites (and a future addition of fields
1271
- * to `TaskEntry` only needs to land here).
1272
- */
1273
- function stateToTaskEntry(state) {
1274
- return {
1275
- taskId: state.taskId,
1276
- pid: state.pid,
1277
- command: state.command,
1278
- cwd: state.cwd,
1279
- startedAt: state.startedAt,
1280
- ...state.endedAt !== void 0 ? { endedAt: state.endedAt } : {},
1281
- outputPath: state.outputPath,
1282
- status: state.status,
1283
- ...state.exitCode !== void 0 ? { exitCode: state.exitCode } : {},
1284
- ...state.signal ? { signal: state.signal } : {},
1285
- bytesWritten: state.bytesWritten
1286
- };
1287
- }
1288
- /**
1289
- * Project a settled `TaskState` to the `TaskExitInfo` shape `settle()`
1290
- * fires from `onExit` and `killBackground` returns on the
1291
- * cached-exit path. Pre-condition: `state.status !== 'running'`
1292
- * (callers gate on this).
1293
- */
1294
- function stateToTaskExitInfo(state) {
1777
+ //#endregion
1778
+ //#region src/contexts/process.ts
1779
+ function createProcessContext(config) {
1780
+ const s = createProcessState(config);
1781
+ const { handles, defaultCwd, isolation, isolationState } = s;
1782
+ const sh = createProcessShell(s);
1783
+ const { shellCommand, isolationBlocker, terminalEnv } = sh;
1784
+ const files = processFileMethods(s);
1785
+ const execM = processExecMethods(s, sh);
1786
+ const streamM = processStreamMethods(s, sh);
1787
+ const bg = processBackgroundMethods(s, sh);
1788
+ const taskM = processTaskMethods(s);
1295
1789
  return {
1296
- taskId: state.taskId,
1297
- status: state.status,
1298
- exitCode: state.exitCode ?? 0,
1299
- ...state.signal ? { signal: state.signal } : {},
1300
- outputPath: state.outputPath,
1301
- durationMs: (state.endedAt ?? Date.now()) - state.startedAt,
1302
- command: state.command
1790
+ type: "process",
1791
+ capabilities: {
1792
+ shell: true,
1793
+ filesystem: true,
1794
+ glob: true,
1795
+ network: true,
1796
+ gpu: false,
1797
+ stream: true,
1798
+ isolated: isolation !== void 0 && isolationState?.ok === true,
1799
+ detachedTasks: "process-lifetime"
1800
+ },
1801
+ async spawn(overrides) {
1802
+ const id = `process-${++s.counter}`;
1803
+ const cwd = overrides?.cwd ?? defaultCwd;
1804
+ let needsCreate = false;
1805
+ try {
1806
+ await stat(cwd);
1807
+ } catch (err) {
1808
+ needsCreate = err?.code === "ENOENT";
1809
+ }
1810
+ if (needsCreate) await mkdir(cwd, { recursive: true });
1811
+ const handle = {
1812
+ id,
1813
+ type: "process",
1814
+ cwd
1815
+ };
1816
+ handles.set(id, handle);
1817
+ return handle;
1818
+ },
1819
+ exec: execM.exec,
1820
+ readFile: files.readFile,
1821
+ fileSize: files.fileSize,
1822
+ readFileBinary: files.readFileBinary,
1823
+ writeFile: files.writeFile,
1824
+ listFiles: files.listFiles,
1825
+ execStream: streamM.execStream,
1826
+ ...hasLocalPty() ? { async openTerminal(handle, options) {
1827
+ const blocked = isolationBlocker();
1828
+ if (blocked) throw new Error(blocked.stderr);
1829
+ const env = terminalEnv(options.env, options.unsetEnv);
1830
+ const shell = loginShell(env);
1831
+ const argv = isolation && isolationState?.ok ? (({ file, args }) => [file, ...args])(shellCommand(`exec ${JSON.stringify(shell)} -l`)) : [shell, "-l"];
1832
+ const pty = spawnLocalPty(argv, {
1833
+ cwd: options.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd,
1834
+ env,
1835
+ cols: options.cols,
1836
+ rows: options.rows,
1837
+ onData: options.onData
1838
+ });
1839
+ return localTerminalProcess(pty, { id: `pty-${pty.pid}` });
1840
+ } } : {},
1841
+ execBackground: bg.execBackground,
1842
+ killBackground: taskM.killBackground,
1843
+ waitBackground: taskM.waitBackground,
1844
+ reassignBackgroundTasks: taskM.reassignBackgroundTasks,
1845
+ listBackground: taskM.listBackground,
1846
+ destroy: taskM.destroy
1303
1847
  };
1304
1848
  }
1305
1849
  //#endregion
1306
- //#region src/contexts/types.ts
1307
- /**
1308
- * Effective {@link DetachedTasksCapability} of a context, with the
1309
- * backward-compatible inference for contexts that predate the field.
1310
- */
1311
- function resolveDetachedTasksCapability(context) {
1312
- return context.capabilities.detachedTasks ?? (context.execBackground ? "process-lifetime" : "none");
1313
- }
1314
- //#endregion
1315
- export { DEFAULT_UNREADABLE_DIRS as a, resolveUnreadableDirs as c, killProcessGroup as i, resolveWritableRoots as l, SUPPORTS_PROCESS_GROUPS as n, isolationAvailability as o, createProcessContext as r, parseAllowWriteSetting as s, resolveDetachedTasksCapability as t, resolveWritableRootsAsync as u };
1850
+ export { SUPPORTS_PROCESS_GROUPS as a, isolationAvailability as c, resolveWritableRoots as d, resolveWritableRootsAsync as f, parseGitLsFiles as i, parseAllowWriteSetting as l, createAnsiStripper as n, killProcessGroup as o, resolveDetachedTasksCapability as p, GIT_LS_FILES_COMMAND as r, DEFAULT_UNREADABLE_DIRS as s, createProcessContext as t, resolveUnreadableDirs as u };
1316
1851
 
1317
- //# sourceMappingURL=contexts-CRE9pw07.js.map
1852
+ //# sourceMappingURL=contexts-d7UolYot.js.map