@code-yeongyu/senpi-agent-core 2026.9.12 → 2026.9.13

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 (344) hide show
  1. package/README.md +4 -0
  2. package/dist/harness/agent-harness.d.ts +639 -384
  3. package/dist/harness/agent-harness.d.ts.map +1 -1
  4. package/dist/harness/agent-harness.js +5 -250
  5. package/dist/harness/agent-harness.js.map +1 -1
  6. package/dist/harness/compaction/branch-summarization.d.ts +12 -6
  7. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  8. package/dist/harness/compaction/branch-summarization.js +33 -27
  9. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  10. package/dist/harness/compaction/compaction.d.ts +30 -8
  11. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  12. package/dist/harness/compaction/compaction.js +53 -64
  13. package/dist/harness/compaction/compaction.js.map +1 -1
  14. package/dist/harness/config.d.ts +9 -0
  15. package/dist/harness/config.d.ts.map +1 -0
  16. package/dist/harness/config.js +35 -0
  17. package/dist/harness/config.js.map +1 -0
  18. package/dist/harness/context.d.ts +9 -0
  19. package/dist/harness/context.d.ts.map +1 -0
  20. package/dist/harness/context.js +13 -0
  21. package/dist/harness/context.js.map +1 -0
  22. package/dist/harness/env/nodejs.d.ts +25 -28
  23. package/dist/harness/env/nodejs.d.ts.map +1 -1
  24. package/dist/harness/env/nodejs.js +368 -125
  25. package/dist/harness/env/nodejs.js.map +1 -1
  26. package/dist/harness/events.d.ts +20 -38
  27. package/dist/harness/events.d.ts.map +1 -1
  28. package/dist/harness/events.js +233 -55
  29. package/dist/harness/events.js.map +1 -1
  30. package/dist/harness/execution/assistant.d.ts +42 -0
  31. package/dist/harness/execution/assistant.d.ts.map +1 -0
  32. package/dist/harness/execution/assistant.js +82 -0
  33. package/dist/harness/execution/assistant.js.map +1 -0
  34. package/dist/harness/execution/effect-gate.d.ts +22 -0
  35. package/dist/harness/execution/effect-gate.d.ts.map +1 -0
  36. package/dist/harness/execution/effect-gate.js +48 -0
  37. package/dist/harness/execution/effect-gate.js.map +1 -0
  38. package/dist/harness/execution/tools.d.ts +67 -0
  39. package/dist/harness/execution/tools.d.ts.map +1 -0
  40. package/dist/harness/execution/tools.js +121 -0
  41. package/dist/harness/execution/tools.js.map +1 -0
  42. package/dist/harness/hooks.d.ts +38 -0
  43. package/dist/harness/hooks.d.ts.map +1 -0
  44. package/dist/harness/hooks.js +404 -0
  45. package/dist/harness/hooks.js.map +1 -0
  46. package/dist/harness/messages.d.ts +2 -2
  47. package/dist/harness/messages.d.ts.map +1 -1
  48. package/dist/harness/messages.js.map +1 -1
  49. package/dist/harness/prompt-templates.d.ts +3 -2
  50. package/dist/harness/prompt-templates.d.ts.map +1 -1
  51. package/dist/harness/prompt-templates.js +17 -17
  52. package/dist/harness/prompt-templates.js.map +1 -1
  53. package/dist/harness/result.d.ts +93 -0
  54. package/dist/harness/result.d.ts.map +1 -1
  55. package/dist/harness/result.js +39 -0
  56. package/dist/harness/result.js.map +1 -1
  57. package/dist/harness/runtime/drive/boundary.d.ts +28 -0
  58. package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
  59. package/dist/harness/runtime/drive/boundary.js +173 -0
  60. package/dist/harness/runtime/drive/boundary.js.map +1 -0
  61. package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
  62. package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
  63. package/dist/harness/runtime/drive/checkpoint.js +133 -0
  64. package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
  65. package/dist/harness/runtime/drive/deferred.d.ts +15 -0
  66. package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
  67. package/dist/harness/runtime/drive/deferred.js +179 -0
  68. package/dist/harness/runtime/drive/deferred.js.map +1 -0
  69. package/dist/harness/runtime/drive/generation.d.ts +8 -0
  70. package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
  71. package/dist/harness/runtime/drive/generation.js +205 -0
  72. package/dist/harness/runtime/drive/generation.js.map +1 -0
  73. package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
  74. package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
  75. package/dist/harness/runtime/drive/reconcile.js +140 -0
  76. package/dist/harness/runtime/drive/reconcile.js.map +1 -0
  77. package/dist/harness/runtime/drive/recovery.d.ts +8 -0
  78. package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
  79. package/dist/harness/runtime/drive/recovery.js +85 -0
  80. package/dist/harness/runtime/drive/recovery.js.map +1 -0
  81. package/dist/harness/runtime/drive/response.d.ts +23 -0
  82. package/dist/harness/runtime/drive/response.d.ts.map +1 -0
  83. package/dist/harness/runtime/drive/response.js +394 -0
  84. package/dist/harness/runtime/drive/response.js.map +1 -0
  85. package/dist/harness/runtime/drive/retry.d.ts +4 -0
  86. package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
  87. package/dist/harness/runtime/drive/retry.js +34 -0
  88. package/dist/harness/runtime/drive/retry.js.map +1 -0
  89. package/dist/harness/runtime/drive/structural.d.ts +32 -0
  90. package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
  91. package/dist/harness/runtime/drive/structural.js +929 -0
  92. package/dist/harness/runtime/drive/structural.js.map +1 -0
  93. package/dist/harness/runtime/drive/terminal.d.ts +7 -0
  94. package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
  95. package/dist/harness/runtime/drive/terminal.js +49 -0
  96. package/dist/harness/runtime/drive/terminal.js.map +1 -0
  97. package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
  98. package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
  99. package/dist/harness/runtime/drive/tool-placement.js +244 -0
  100. package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
  101. package/dist/harness/runtime/drive/tools.d.ts +6 -0
  102. package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
  103. package/dist/harness/runtime/drive/tools.js +449 -0
  104. package/dist/harness/runtime/drive/tools.js.map +1 -0
  105. package/dist/harness/runtime/drive.d.ts +6 -0
  106. package/dist/harness/runtime/drive.d.ts.map +1 -0
  107. package/dist/harness/runtime/drive.js +93 -0
  108. package/dist/harness/runtime/drive.js.map +1 -0
  109. package/dist/harness/runtime/harness.d.ts +59 -0
  110. package/dist/harness/runtime/harness.d.ts.map +1 -0
  111. package/dist/harness/runtime/harness.js +302 -0
  112. package/dist/harness/runtime/harness.js.map +1 -0
  113. package/dist/harness/runtime/index.d.ts +2 -0
  114. package/dist/harness/runtime/index.d.ts.map +1 -0
  115. package/dist/harness/runtime/index.js +2 -0
  116. package/dist/harness/runtime/index.js.map +1 -0
  117. package/dist/harness/runtime/lane.d.ts +118 -0
  118. package/dist/harness/runtime/lane.d.ts.map +1 -0
  119. package/dist/harness/runtime/lane.js +1559 -0
  120. package/dist/harness/runtime/lane.js.map +1 -0
  121. package/dist/harness/runtime/progress.d.ts +15 -0
  122. package/dist/harness/runtime/progress.d.ts.map +1 -0
  123. package/dist/harness/runtime/progress.js +66 -0
  124. package/dist/harness/runtime/progress.js.map +1 -0
  125. package/dist/harness/runtime/reducer.d.ts +5 -0
  126. package/dist/harness/runtime/reducer.d.ts.map +1 -0
  127. package/dist/harness/runtime/reducer.js +240 -0
  128. package/dist/harness/runtime/reducer.js.map +1 -0
  129. package/dist/harness/runtime/restore.d.ts +24 -0
  130. package/dist/harness/runtime/restore.d.ts.map +1 -0
  131. package/dist/harness/runtime/restore.js +117 -0
  132. package/dist/harness/runtime/restore.js.map +1 -0
  133. package/dist/harness/runtime/transcript.d.ts +21 -0
  134. package/dist/harness/runtime/transcript.d.ts.map +1 -0
  135. package/dist/harness/runtime/transcript.js +72 -0
  136. package/dist/harness/runtime/transcript.js.map +1 -0
  137. package/dist/harness/runtime/types.d.ts +105 -0
  138. package/dist/harness/runtime/types.d.ts.map +1 -0
  139. package/dist/harness/runtime/types.js +50 -0
  140. package/dist/harness/runtime/types.js.map +1 -0
  141. package/dist/harness/session/commit.d.ts +53 -0
  142. package/dist/harness/session/commit.d.ts.map +1 -0
  143. package/dist/harness/session/commit.js +57 -0
  144. package/dist/harness/session/commit.js.map +1 -0
  145. package/dist/harness/session/context.d.ts +6 -19
  146. package/dist/harness/session/context.d.ts.map +1 -1
  147. package/dist/harness/session/context.js +31 -53
  148. package/dist/harness/session/context.js.map +1 -1
  149. package/dist/harness/session/fork-policy.d.ts +21 -0
  150. package/dist/harness/session/fork-policy.d.ts.map +1 -0
  151. package/dist/harness/session/fork-policy.js +55 -0
  152. package/dist/harness/session/fork-policy.js.map +1 -0
  153. package/dist/harness/session/fork.d.ts +16 -0
  154. package/dist/harness/session/fork.d.ts.map +1 -0
  155. package/dist/harness/session/fork.js +79 -0
  156. package/dist/harness/session/fork.js.map +1 -0
  157. package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
  158. package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
  159. package/dist/harness/session/in-memory-storage-state.js +269 -0
  160. package/dist/harness/session/in-memory-storage-state.js.map +1 -0
  161. package/dist/harness/session/index.d.ts +9 -5
  162. package/dist/harness/session/index.d.ts.map +1 -1
  163. package/dist/harness/session/index.js +7 -4
  164. package/dist/harness/session/index.js.map +1 -1
  165. package/dist/harness/session/jsonl/codec.d.ts +19 -8
  166. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  167. package/dist/harness/session/jsonl/codec.js +34 -219
  168. package/dist/harness/session/jsonl/codec.js.map +1 -1
  169. package/dist/harness/session/jsonl/fork.d.ts +38 -0
  170. package/dist/harness/session/jsonl/fork.d.ts.map +1 -0
  171. package/dist/harness/session/jsonl/fork.js +266 -0
  172. package/dist/harness/session/jsonl/fork.js.map +1 -0
  173. package/dist/harness/session/jsonl/index.d.ts +4 -0
  174. package/dist/harness/session/jsonl/index.d.ts.map +1 -0
  175. package/dist/harness/session/jsonl/index.js +4 -0
  176. package/dist/harness/session/jsonl/index.js.map +1 -0
  177. package/dist/harness/session/jsonl/io.d.ts +14 -0
  178. package/dist/harness/session/jsonl/io.d.ts.map +1 -0
  179. package/dist/harness/session/jsonl/io.js +86 -0
  180. package/dist/harness/session/jsonl/io.js.map +1 -0
  181. package/dist/harness/session/jsonl/legacy-v3.d.ts +44 -0
  182. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
  183. package/dist/harness/session/jsonl/legacy-v3.js +454 -0
  184. package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
  185. package/dist/harness/session/jsonl/repo.d.ts +27 -24
  186. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  187. package/dist/harness/session/jsonl/repo.js +276 -151
  188. package/dist/harness/session/jsonl/repo.js.map +1 -1
  189. package/dist/harness/session/jsonl/storage.d.ts +39 -41
  190. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  191. package/dist/harness/session/jsonl/storage.js +175 -196
  192. package/dist/harness/session/jsonl/storage.js.map +1 -1
  193. package/dist/harness/session/jsonl/types.d.ts +23 -22
  194. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  195. package/dist/harness/session/jsonl/types.js +2 -1
  196. package/dist/harness/session/jsonl/types.js.map +1 -1
  197. package/dist/harness/session/memory.d.ts +44 -40
  198. package/dist/harness/session/memory.d.ts.map +1 -1
  199. package/dist/harness/session/memory.js +333 -115
  200. package/dist/harness/session/memory.js.map +1 -1
  201. package/dist/harness/session/mutation-line.d.ts +8 -0
  202. package/dist/harness/session/mutation-line.d.ts.map +1 -0
  203. package/dist/harness/session/mutation-line.js +22 -0
  204. package/dist/harness/session/mutation-line.js.map +1 -0
  205. package/dist/harness/session/session.d.ts +78 -51
  206. package/dist/harness/session/session.d.ts.map +1 -1
  207. package/dist/harness/session/session.js +327 -207
  208. package/dist/harness/session/session.js.map +1 -1
  209. package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
  210. package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
  211. package/dist/harness/session/testing/benchmark/datasets.js +24 -0
  212. package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
  213. package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
  214. package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
  215. package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
  216. package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
  217. package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
  218. package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
  219. package/dist/harness/session/testing/benchmark/storage.js +114 -0
  220. package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
  221. package/dist/harness/session/testing/conformance/session-repo.d.ts +23 -0
  222. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
  223. package/dist/harness/session/testing/conformance/session-repo.js +809 -0
  224. package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
  225. package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
  226. package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
  227. package/dist/harness/session/testing/conformance/storage.js +600 -0
  228. package/dist/harness/session/testing/conformance/storage.js.map +1 -0
  229. package/dist/harness/session/testing/gating-storage.d.ts +26 -0
  230. package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
  231. package/dist/harness/session/testing/gating-storage.js +103 -0
  232. package/dist/harness/session/testing/gating-storage.js.map +1 -0
  233. package/dist/harness/session/testing/index.d.ts +9 -2
  234. package/dist/harness/session/testing/index.d.ts.map +1 -1
  235. package/dist/harness/session/testing/index.js +8 -1
  236. package/dist/harness/session/testing/index.js.map +1 -1
  237. package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
  238. package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
  239. package/dist/harness/session/testing/instrumented-storage.js +19 -0
  240. package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
  241. package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
  242. package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
  243. package/dist/harness/session/testing/storage-decorator.js +40 -0
  244. package/dist/harness/session/testing/storage-decorator.js.map +1 -0
  245. package/dist/harness/session/testing/types.d.ts +5 -7
  246. package/dist/harness/session/testing/types.d.ts.map +1 -1
  247. package/dist/harness/session/testing/types.js.map +1 -1
  248. package/dist/harness/session/types.d.ts +425 -265
  249. package/dist/harness/session/types.d.ts.map +1 -1
  250. package/dist/harness/session/types.js +7 -7
  251. package/dist/harness/session/types.js.map +1 -1
  252. package/dist/harness/session/values.d.ts +95 -0
  253. package/dist/harness/session/values.d.ts.map +1 -0
  254. package/dist/harness/session/values.js +81 -0
  255. package/dist/harness/session/values.js.map +1 -0
  256. package/dist/harness/skills.d.ts +3 -2
  257. package/dist/harness/skills.d.ts.map +1 -1
  258. package/dist/harness/skills.js +29 -26
  259. package/dist/harness/skills.js.map +1 -1
  260. package/dist/harness/telemetry.d.ts +64 -43
  261. package/dist/harness/telemetry.d.ts.map +1 -1
  262. package/dist/harness/telemetry.js +45 -26
  263. package/dist/harness/telemetry.js.map +1 -1
  264. package/dist/harness/tools/bash.d.ts +4 -4
  265. package/dist/harness/tools/bash.d.ts.map +1 -1
  266. package/dist/harness/tools/bash.js +66 -93
  267. package/dist/harness/tools/bash.js.map +1 -1
  268. package/dist/harness/tools/edit.d.ts.map +1 -1
  269. package/dist/harness/tools/edit.js +17 -13
  270. package/dist/harness/tools/edit.js.map +1 -1
  271. package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
  272. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
  273. package/dist/harness/tools/file-mutation-queue.js +5 -5
  274. package/dist/harness/tools/file-mutation-queue.js.map +1 -1
  275. package/dist/harness/tools/path-utils.d.ts +3 -2
  276. package/dist/harness/tools/path-utils.d.ts.map +1 -1
  277. package/dist/harness/tools/path-utils.js +5 -5
  278. package/dist/harness/tools/path-utils.js.map +1 -1
  279. package/dist/harness/tools/read.d.ts +2 -1
  280. package/dist/harness/tools/read.d.ts.map +1 -1
  281. package/dist/harness/tools/read.js +4 -6
  282. package/dist/harness/tools/read.js.map +1 -1
  283. package/dist/harness/tools/write.d.ts.map +1 -1
  284. package/dist/harness/tools/write.js +12 -8
  285. package/dist/harness/tools/write.js.map +1 -1
  286. package/dist/harness/types.d.ts +128 -45
  287. package/dist/harness/types.d.ts.map +1 -1
  288. package/dist/harness/types.js.map +1 -1
  289. package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
  290. package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
  291. package/dist/harness/utils/adaptive-publisher.js +79 -0
  292. package/dist/harness/utils/adaptive-publisher.js.map +1 -0
  293. package/dist/harness/utils/output-capture.d.ts +31 -0
  294. package/dist/harness/utils/output-capture.d.ts.map +1 -0
  295. package/dist/harness/utils/output-capture.js +217 -0
  296. package/dist/harness/utils/output-capture.js.map +1 -0
  297. package/dist/harness/utils/shell-output.d.ts +19 -5
  298. package/dist/harness/utils/shell-output.d.ts.map +1 -1
  299. package/dist/harness/utils/shell-output.js +65 -192
  300. package/dist/harness/utils/shell-output.js.map +1 -1
  301. package/dist/harness/utils/truncate.d.ts +1 -0
  302. package/dist/harness/utils/truncate.d.ts.map +1 -1
  303. package/dist/harness/utils/truncate.js +1 -1
  304. package/dist/harness/utils/truncate.js.map +1 -1
  305. package/dist/harness/utils/usage.d.ts +4 -0
  306. package/dist/harness/utils/usage.d.ts.map +1 -0
  307. package/dist/harness/utils/usage.js +33 -0
  308. package/dist/harness/utils/usage.js.map +1 -0
  309. package/dist/index.d.ts +4 -1
  310. package/dist/index.d.ts.map +1 -1
  311. package/dist/index.js +3 -6
  312. package/dist/index.js.map +1 -1
  313. package/dist/search/index.d.ts +24 -16
  314. package/dist/search/index.d.ts.map +1 -1
  315. package/dist/search/index.js +1 -1
  316. package/dist/search/index.js.map +1 -1
  317. package/dist/types.d.ts +2 -0
  318. package/dist/types.d.ts.map +1 -1
  319. package/dist/types.js.map +1 -1
  320. package/package.json +27 -6
  321. package/dist/harness/reducer.d.ts +0 -100
  322. package/dist/harness/reducer.d.ts.map +0 -1
  323. package/dist/harness/reducer.js +0 -420
  324. package/dist/harness/reducer.js.map +0 -1
  325. package/dist/harness/session/jsonl/errors.d.ts +0 -9
  326. package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
  327. package/dist/harness/session/jsonl/errors.js +0 -18
  328. package/dist/harness/session/jsonl/errors.js.map +0 -1
  329. package/dist/harness/session/jsonl.d.ts +0 -3
  330. package/dist/harness/session/jsonl.d.ts.map +0 -1
  331. package/dist/harness/session/jsonl.js +0 -2
  332. package/dist/harness/session/jsonl.js.map +0 -1
  333. package/dist/harness/session/state.d.ts +0 -65
  334. package/dist/harness/session/state.d.ts.map +0 -1
  335. package/dist/harness/session/state.js +0 -320
  336. package/dist/harness/session/state.js.map +0 -1
  337. package/dist/harness/session/testing/conformance.d.ts +0 -4
  338. package/dist/harness/session/testing/conformance.d.ts.map +0 -1
  339. package/dist/harness/session/testing/conformance.js +0 -762
  340. package/dist/harness/session/testing/conformance.js.map +0 -1
  341. package/dist/search/scanning.d.ts +0 -29
  342. package/dist/search/scanning.d.ts.map +0 -1
  343. package/dist/search/scanning.js +0 -103
  344. package/dist/search/scanning.js.map +0 -1
@@ -1,15 +1,16 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
- import { constants, createReadStream, existsSync } from "node:fs";
4
- import { access, appendFile, lstat, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, writeFile, } from "node:fs/promises";
5
- import { homedir, tmpdir } from "node:os";
3
+ import { constants, createWriteStream, existsSync } from "node:fs";
4
+ import { access, appendFile, lstat, mkdir, mkdtemp, open as openFile, readdir, readFile, realpath, rename, rm, writeFile, } from "node:fs/promises";
5
+ import { homedir, constants as osConstants, tmpdir } from "node:os";
6
6
  import { basename, isAbsolute, join, resolve } from "node:path";
7
- import { createInterface } from "node:readline";
8
7
  import { fileURLToPath } from "node:url";
9
8
  import { ExecutionError, err, FileError, ok, toError, } from "../types.js";
9
+ import { OutputCapture } from "../utils/output-capture.js";
10
10
  const MAX_TIMEOUT_MS = 2_147_483_647;
11
11
  const MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;
12
12
  const EXIT_STDIO_GRACE_MS = 100;
13
+ const SPILL_HIGH_WATER_MARK = 8 * 1024 * 1024;
13
14
  function resolveTimeoutMs(timeout) {
14
15
  if (timeout === undefined)
15
16
  return ok(undefined);
@@ -289,11 +290,12 @@ function killProcessTree(pid) {
289
290
  }
290
291
  }
291
292
  }
292
- function waitForChildProcess(child) {
293
+ function waitForChildProcess(child, spillIsDraining) {
293
294
  return new Promise((resolvePromise, reject) => {
294
295
  let settled = false;
295
296
  let exited = false;
296
297
  let exitCode = null;
298
+ let exitSignal = null;
297
299
  let postExitTimer;
298
300
  let stdoutEnded = child.stdout === null;
299
301
  let stderrEnded = child.stderr === null;
@@ -308,23 +310,28 @@ function waitForChildProcess(child) {
308
310
  child.stdout?.removeListener("data", onData);
309
311
  child.stderr?.removeListener("data", onData);
310
312
  };
311
- const finalize = (code) => {
313
+ const finalize = () => {
312
314
  if (settled)
313
315
  return;
314
316
  settled = true;
315
317
  cleanup();
316
318
  child.stdout?.destroy();
317
319
  child.stderr?.destroy();
318
- resolvePromise(code);
320
+ resolvePromise({ code: exitCode, signal: exitSignal });
319
321
  };
320
322
  const maybeFinalizeAfterExit = () => {
321
323
  if (exited && stdoutEnded && stderrEnded)
322
- finalize(exitCode);
324
+ finalize();
323
325
  };
324
326
  const armIdleTimer = () => {
325
327
  if (postExitTimer)
326
328
  clearTimeout(postExitTimer);
327
- postExitTimer = setTimeout(() => finalize(exitCode), EXIT_STDIO_GRACE_MS);
329
+ postExitTimer = setTimeout(() => {
330
+ if (spillIsDraining())
331
+ armIdleTimer();
332
+ else
333
+ finalize();
334
+ }, EXIT_STDIO_GRACE_MS);
328
335
  };
329
336
  const onData = () => {
330
337
  if (exited && !settled)
@@ -345,14 +352,19 @@ function waitForChildProcess(child) {
345
352
  cleanup();
346
353
  reject(error);
347
354
  };
348
- const onExit = (code) => {
355
+ const onExit = (code, signal) => {
349
356
  exited = true;
350
357
  exitCode = code;
358
+ exitSignal = signal;
351
359
  maybeFinalizeAfterExit();
352
360
  if (!settled)
353
361
  armIdleTimer();
354
362
  };
355
- const onClose = (code) => finalize(code);
363
+ const onClose = (code, signal) => {
364
+ exitCode = code;
365
+ exitSignal = signal;
366
+ finalize();
367
+ };
356
368
  child.stdout?.once("end", onStdoutEnd);
357
369
  child.stderr?.once("end", onStderrEnd);
358
370
  child.stdout?.on("data", onData);
@@ -363,6 +375,71 @@ function waitForChildProcess(child) {
363
375
  });
364
376
  }
365
377
  const NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS = 5_000;
378
+ /** Strict LF reader; Node readline does not report whether its final line was newline-terminated. */
379
+ class NodeTextLineReader {
380
+ constructor(file, path) {
381
+ this.decoder = new TextDecoder();
382
+ this.chunk = new Uint8Array(64 * 1024);
383
+ this.byteOffset = 0;
384
+ this.buffered = "";
385
+ this.ended = false;
386
+ this.closed = false;
387
+ this.file = file;
388
+ this.path = path;
389
+ }
390
+ async readLine(context) {
391
+ const aborted = abortResult(context.abortSignal, this.path);
392
+ if (aborted)
393
+ return aborted;
394
+ if (this.closed)
395
+ return err(new FileError("invalid", "Text line reader is closed", this.path));
396
+ try {
397
+ while (true) {
398
+ const newline = this.buffered.indexOf("\n");
399
+ if (newline !== -1) {
400
+ const text = this.buffered.slice(0, newline);
401
+ this.buffered = this.buffered.slice(newline + 1);
402
+ return ok({ text, terminated: true });
403
+ }
404
+ if (this.ended) {
405
+ if (this.buffered.length === 0)
406
+ return ok(undefined);
407
+ const text = this.buffered;
408
+ this.buffered = "";
409
+ return ok({ text, terminated: false });
410
+ }
411
+ // Explicit positions allow an aborted read to be retried without skipping bytes.
412
+ const { bytesRead } = await this.file.read(this.chunk, 0, this.chunk.length, this.byteOffset);
413
+ const afterReadAbort = abortResult(context.abortSignal, this.path);
414
+ if (afterReadAbort)
415
+ return afterReadAbort;
416
+ this.byteOffset += bytesRead;
417
+ if (bytesRead === 0) {
418
+ this.buffered += this.decoder.decode();
419
+ this.ended = true;
420
+ }
421
+ else {
422
+ this.buffered += this.decoder.decode(this.chunk.subarray(0, bytesRead), { stream: true });
423
+ }
424
+ }
425
+ }
426
+ catch (error) {
427
+ return err(toFileError(error, this.path));
428
+ }
429
+ }
430
+ async close(_context) {
431
+ if (this.closed)
432
+ return;
433
+ this.closed = true;
434
+ this.buffered = "";
435
+ try {
436
+ await this.file.close();
437
+ }
438
+ catch {
439
+ // Closing is best-effort, including after cancellation or an earlier I/O failure.
440
+ }
441
+ }
442
+ }
366
443
  export class NodeExecutionEnv {
367
444
  constructor(options) {
368
445
  this.activeChildPids = new Set();
@@ -370,14 +447,15 @@ export class NodeExecutionEnv {
370
447
  this.shellPath = options.shellPath;
371
448
  this.shellEnv = options.shellEnv;
372
449
  }
373
- async absolutePath(path) {
450
+ async absolutePath(path, _context) {
374
451
  return ok(resolvePath(this.cwd, path));
375
452
  }
376
- async joinPath(parts) {
453
+ async joinPath(parts, _context) {
377
454
  return ok(join(...parts));
378
455
  }
379
- async exec(command, options) {
380
- if (options?.abortSignal?.aborted)
456
+ async exec(command, options, context) {
457
+ const signal = context.abortSignal;
458
+ if (signal?.aborted)
381
459
  return err(new ExecutionError("aborted", "aborted"));
382
460
  const timeoutMsResult = resolveTimeoutMs(options?.timeout);
383
461
  if (!timeoutMsResult.ok)
@@ -395,31 +473,137 @@ export class NodeExecutionEnv {
395
473
  return err(new ExecutionError("spawn_error", `Working directory does not exist: ${cwd}\nCannot execute bash commands.`, cause));
396
474
  }
397
475
  return await new Promise((resolvePromise) => {
398
- let stdout = "";
399
- let stderr = "";
400
476
  let settled = false;
401
477
  let timedOut = false;
402
478
  let callbackError;
479
+ let spillError;
403
480
  const callbackPromises = new Set();
404
481
  let child;
405
482
  let timeoutId;
483
+ const spillPrefix = [];
484
+ let spillPath;
485
+ const spillQueue = [];
486
+ let spillStart;
487
+ let spillStream;
488
+ let spillBackpressured = false;
406
489
  const onAbort = () => {
407
- if (child?.pid) {
490
+ if (child?.pid)
408
491
  killProcessTree(child.pid);
409
- }
410
492
  };
493
+ const failCallback = (error) => {
494
+ if (callbackError !== undefined)
495
+ return;
496
+ const cause = toError(error);
497
+ // The raw rejection value is the cause so observers that reject with a non-Error
498
+ // payload can still be identified by callers.
499
+ callbackError = new ExecutionError("callback_error", cause.message, error);
500
+ onAbort();
501
+ };
502
+ // An observer may return a promise. Track it so a rejection fails the execution with a
503
+ // callback error instead of escaping as an unhandled rejection.
504
+ const observeUpdate = options?.onUpdate;
505
+ const trackedOnUpdate = observeUpdate === undefined
506
+ ? undefined
507
+ : (update, updateContext) => {
508
+ const settlement = observeUpdate(update, updateContext);
509
+ if (settlement === undefined || settlement === null)
510
+ return;
511
+ const tracked = Promise.resolve(settlement).then(() => undefined, (error) => {
512
+ failCallback(error);
513
+ });
514
+ callbackPromises.add(tracked);
515
+ void tracked.finally(() => callbackPromises.delete(tracked));
516
+ };
517
+ let capture;
518
+ try {
519
+ capture = new OutputCapture(options?.capture, context, {
520
+ onUpdate: trackedOnUpdate,
521
+ onError: failCallback,
522
+ });
523
+ }
524
+ catch (error) {
525
+ const cause = toError(error);
526
+ resolvePromise(err(new ExecutionError("unknown", cause.message, cause)));
527
+ return;
528
+ }
411
529
  const settle = (result) => {
530
+ if (settled)
531
+ return;
532
+ settled = true;
412
533
  if (timeoutId)
413
534
  clearTimeout(timeoutId);
414
- if (options?.abortSignal)
415
- options.abortSignal.removeEventListener("abort", onAbort);
535
+ if (signal)
536
+ signal.removeEventListener("abort", onAbort);
416
537
  if (child?.pid)
417
538
  this.activeChildPids.delete(child.pid);
418
- if (settled)
419
- return;
420
- settled = true;
539
+ capture.dispose();
421
540
  resolvePromise(result);
422
541
  };
542
+ const pauseOutput = () => {
543
+ child?.stdout?.pause();
544
+ child?.stderr?.pause();
545
+ };
546
+ const resumeOutput = () => {
547
+ if (callbackError || spillError || timedOut || signal?.aborted || spillBackpressured)
548
+ return;
549
+ child?.stdout?.resume();
550
+ child?.stderr?.resume();
551
+ };
552
+ const failSpill = (error) => {
553
+ if (spillError !== undefined)
554
+ return;
555
+ const cause = toError(error);
556
+ spillError = new ExecutionError("unknown", `Failed to preserve complete shell output: ${cause.message}`, cause);
557
+ spillBackpressured = false;
558
+ onAbort();
559
+ };
560
+ const writeSpill = (chunk) => {
561
+ if (spillStream === undefined || chunk.length === 0)
562
+ return;
563
+ if (spillStream.write(chunk) || spillBackpressured)
564
+ return;
565
+ spillBackpressured = true;
566
+ pauseOutput();
567
+ spillStream.once("drain", () => {
568
+ spillBackpressured = false;
569
+ resumeOutput();
570
+ });
571
+ };
572
+ const startSpill = (chunk) => {
573
+ if (spillStream !== undefined) {
574
+ writeSpill(chunk);
575
+ return;
576
+ }
577
+ spillQueue.push(chunk);
578
+ if (spillStart !== undefined)
579
+ return;
580
+ pauseOutput();
581
+ spillStart = (async () => {
582
+ const created = await this.createTempFile({ prefix: "pi-output-", suffix: ".log" }, context);
583
+ if (!created.ok)
584
+ throw created.error;
585
+ spillPath = created.value;
586
+ capture.setSpillPath(spillPath);
587
+ spillStream = createWriteStream(spillPath, { flags: "a", highWaterMark: SPILL_HIGH_WATER_MARK });
588
+ spillStream.on("error", failSpill);
589
+ for (const queued of spillQueue)
590
+ writeSpill(queued);
591
+ spillQueue.length = 0;
592
+ })()
593
+ .catch(failSpill)
594
+ .finally(resumeOutput);
595
+ };
596
+ const finishSpill = async () => {
597
+ await spillStart;
598
+ const stream = spillStream;
599
+ if (stream === undefined || spillError !== undefined || stream.destroyed)
600
+ return;
601
+ await new Promise((resolveFinish) => {
602
+ stream.once("error", () => resolveFinish());
603
+ stream.once("finish", resolveFinish);
604
+ stream.end();
605
+ });
606
+ };
423
607
  try {
424
608
  const commandFromStdin = shellConfig.value.commandTransport === "stdin";
425
609
  child = spawn(shellConfig.value.shell, commandFromStdin ? shellConfig.value.args : [...shellConfig.value.args, command], {
@@ -442,64 +626,75 @@ export class NodeExecutionEnv {
442
626
  return;
443
627
  }
444
628
  timeoutId =
445
- timeoutMs !== undefined
446
- ? setTimeout(() => {
629
+ timeoutMs === undefined
630
+ ? undefined
631
+ : setTimeout(() => {
447
632
  timedOut = true;
448
- if (child?.pid) {
449
- killProcessTree(child.pid);
450
- }
451
- }, timeoutMs)
452
- : undefined;
453
- if (options?.abortSignal) {
454
- if (options.abortSignal.aborted) {
633
+ onAbort();
634
+ }, timeoutMs);
635
+ if (signal) {
636
+ if (signal.aborted)
455
637
  onAbort();
456
- }
457
- else {
458
- options.abortSignal.addEventListener("abort", onAbort, { once: true });
459
- }
638
+ else
639
+ signal.addEventListener("abort", onAbort, { once: true });
460
640
  }
461
- child.stdout?.setEncoding("utf8");
462
- child.stderr?.setEncoding("utf8");
463
- const handleChunk = (chunk, callback, append) => {
464
- append(chunk);
465
- if (callback === undefined)
466
- return;
467
- const callbackPromise = Promise.resolve()
468
- .then(() => callback(chunk))
469
- .catch((error) => {
470
- if (!callbackError) {
471
- const cause = toError(error);
472
- callbackError = new ExecutionError("callback_error", cause.message, error);
473
- onAbort();
474
- }
475
- throw error;
476
- });
477
- callbackPromises.add(callbackPromise);
478
- void callbackPromise.then(() => callbackPromises.delete(callbackPromise), () => callbackPromises.delete(callbackPromise));
479
- };
480
- child.stdout?.on("data", (chunk) => handleChunk(chunk, options?.onStdout, (value) => (stdout += value)));
481
- child.stderr?.on("data", (chunk) => handleChunk(chunk, options?.onStderr, (value) => (stderr += value)));
482
- void waitForChildProcess(child).then(async (code) => {
483
- // Normal command completion must not be held hostage by an observer that
484
- // never settles. Abort completion uses the shorter cancellation path above;
485
- // this generous bound preserves slow, legitimate callbacks without hanging
486
- // the execution forever.
487
- const callbacksSettled = Promise.allSettled([...callbackPromises]);
488
- let callbackTimeout;
489
- const callbackBound = new Promise((resolve) => {
490
- callbackTimeout = setTimeout(() => resolve(false), NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS);
491
- callbackTimeout.unref?.();
492
- });
641
+ const feed = (chunk) => {
493
642
  try {
494
- const settled = await Promise.race([callbacksSettled.then(() => true), callbackBound]);
495
- if (!settled && !callbackError) {
496
- settle(err(new ExecutionError("callback_error", "Output callback did not settle within 5000ms")));
643
+ const wasTruncated = capture.truncated;
644
+ capture.push(chunk);
645
+ if (!options?.capture?.spill || chunk.length === 0)
497
646
  return;
647
+ if (spillPath !== undefined || wasTruncated) {
648
+ startSpill(chunk);
498
649
  }
650
+ else if (capture.truncated) {
651
+ for (const prefix of spillPrefix)
652
+ startSpill(prefix);
653
+ spillPrefix.length = 0;
654
+ startSpill(chunk);
655
+ }
656
+ else {
657
+ spillPrefix.push(chunk);
658
+ }
659
+ }
660
+ catch (error) {
661
+ failCallback(error);
662
+ }
663
+ };
664
+ child.stdout?.on("data", feed);
665
+ child.stderr?.on("data", feed);
666
+ void waitForChildProcess(child, () => spillError === undefined &&
667
+ spillStart !== undefined &&
668
+ (spillStream === undefined || spillBackpressured)).then(async ({ code, signal: exitSignal }) => {
669
+ await finishSpill();
670
+ try {
671
+ capture.finish();
672
+ capture.flush();
499
673
  }
500
- finally {
501
- if (callbackTimeout)
502
- clearTimeout(callbackTimeout);
674
+ catch (error) {
675
+ failCallback(error);
676
+ }
677
+ // Normal command completion must not be held hostage by an observer that never
678
+ // settles. Abort completion uses the shorter cancellation path above; this generous
679
+ // bound preserves slow, legitimate observers without hanging the execution forever.
680
+ if (callbackPromises.size > 0) {
681
+ const callbacksSettled = Promise.allSettled([...callbackPromises]);
682
+ let callbackTimeout;
683
+ const callbackBound = new Promise((resolve) => {
684
+ callbackTimeout = setTimeout(() => resolve(false), NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS);
685
+ callbackTimeout.unref?.();
686
+ });
687
+ try {
688
+ const observersSettled = await Promise.race([callbacksSettled.then(() => true), callbackBound]);
689
+ if (!observersSettled && !callbackError) {
690
+ settle(err(new ExecutionError("callback_error", `Output callback did not settle within ${NORMAL_CALLBACK_SETTLEMENT_TIMEOUT_MS}ms`)));
691
+ return;
692
+ }
693
+ }
694
+ finally {
695
+ if (callbackTimeout)
696
+ clearTimeout(callbackTimeout);
697
+ }
503
698
  }
504
699
  if (callbackError) {
505
700
  settle(err(callbackError));
@@ -509,104 +704,135 @@ export class NodeExecutionEnv {
509
704
  settle(err(new ExecutionError("timeout", `timeout:${options?.timeout}`)));
510
705
  return;
511
706
  }
512
- if (options?.abortSignal?.aborted) {
707
+ if (signal?.aborted) {
513
708
  settle(err(new ExecutionError("aborted", "aborted")));
514
709
  return;
515
710
  }
516
- settle(ok({ stdout, stderr, exitCode: code ?? 0 }));
711
+ if (spillError) {
712
+ settle(err(spillError));
713
+ return;
714
+ }
715
+ const output = capture.snapshot();
716
+ // A process killed by a signal (e.g. OOM killer) has no exit code; map it
717
+ // to the conventional 128 + signal number so callers do not mistake it
718
+ // for a successful exit.
719
+ const exitCode = code ?? (exitSignal ? 128 + (osConstants.signals[exitSignal] ?? 0) : 1);
720
+ settle(ok({
721
+ exitCode,
722
+ truncation: output.truncation,
723
+ ...(output.spillPath === undefined ? {} : { spillPath: output.spillPath }),
724
+ ...(output.lastLineBytes === undefined ? {} : { lastLineBytes: output.lastLineBytes }),
725
+ }));
517
726
  }, (error) => settle(err(new ExecutionError("spawn_error", error.message, error))));
518
727
  });
519
728
  }
520
- async readTextFile(path, abortSignal) {
729
+ async openTextLineReader(path, context) {
521
730
  const resolved = resolvePath(this.cwd, path);
522
- const aborted = abortResult(abortSignal, resolved);
731
+ const aborted = abortResult(context.abortSignal, resolved);
523
732
  if (aborted)
524
733
  return aborted;
525
734
  try {
526
- return ok(await readFile(resolved, { encoding: "utf8", signal: abortSignal }));
735
+ const file = await openFile(resolved, "r");
736
+ const afterOpenAbort = abortResult(context.abortSignal, resolved);
737
+ if (afterOpenAbort) {
738
+ await file.close().catch(() => undefined);
739
+ return afterOpenAbort;
740
+ }
741
+ return ok(new NodeTextLineReader(file, resolved));
527
742
  }
528
743
  catch (error) {
529
744
  return err(toFileError(error, resolved));
530
745
  }
531
746
  }
532
- async readTextLines(path, options) {
747
+ async readTextFile(path, context) {
533
748
  const resolved = resolvePath(this.cwd, path);
534
- const aborted = abortResult(options?.abortSignal, resolved);
749
+ const signal = context.abortSignal;
750
+ const aborted = abortResult(signal, resolved);
535
751
  if (aborted)
536
752
  return aborted;
753
+ try {
754
+ return ok(await readFile(resolved, { encoding: "utf8", signal }));
755
+ }
756
+ catch (error) {
757
+ return err(toFileError(error, resolved));
758
+ }
759
+ }
760
+ async readTextLines(path, options, context) {
537
761
  if (options?.maxLines !== undefined && options.maxLines <= 0)
538
762
  return ok([]);
539
- let stream;
540
- let lineReader;
763
+ const opened = await this.openTextLineReader(path, context);
764
+ if (!opened.ok)
765
+ return opened;
766
+ const lines = [];
541
767
  try {
542
- stream = createReadStream(resolved, { encoding: "utf8", signal: options?.abortSignal });
543
- lineReader = createInterface({ input: stream, crlfDelay: Infinity });
544
- const lines = [];
545
- for await (const line of lineReader) {
546
- const loopAbort = abortResult(options?.abortSignal, resolved);
547
- if (loopAbort)
548
- return loopAbort;
549
- lines.push(line);
550
- if (options?.maxLines !== undefined && lines.length >= options.maxLines)
768
+ while (options?.maxLines === undefined || lines.length < options.maxLines) {
769
+ const line = await opened.value.readLine(context);
770
+ if (!line.ok)
771
+ return line;
772
+ if (line.value === undefined)
551
773
  break;
774
+ lines.push(line.value.text);
552
775
  }
553
- const afterReadAbort = abortResult(options?.abortSignal, resolved);
554
- if (afterReadAbort)
555
- return afterReadAbort;
556
776
  return ok(lines);
557
777
  }
558
- catch (error) {
559
- return err(toFileError(error, resolved));
560
- }
561
778
  finally {
562
- lineReader?.close();
563
- stream?.destroy();
779
+ await opened.value.close(context);
564
780
  }
565
781
  }
566
- async readBinaryFile(path, abortSignal) {
782
+ async readBinaryFile(path, context) {
567
783
  const resolved = resolvePath(this.cwd, path);
568
- const aborted = abortResult(abortSignal, resolved);
784
+ const signal = context.abortSignal;
785
+ const aborted = abortResult(signal, resolved);
569
786
  if (aborted)
570
787
  return aborted;
571
788
  try {
572
- return ok(await readFile(resolved, { signal: abortSignal }));
789
+ return ok(await readFile(resolved, { signal }));
573
790
  }
574
791
  catch (error) {
575
792
  return err(toFileError(error, resolved));
576
793
  }
577
794
  }
578
- async writeFile(path, content, abortSignal) {
795
+ async writeFile(path, content, context) {
579
796
  const resolved = resolvePath(this.cwd, path);
580
- const aborted = abortResult(abortSignal, resolved);
797
+ const signal = context.abortSignal;
798
+ const aborted = abortResult(signal, resolved);
581
799
  if (aborted)
582
800
  return aborted;
583
801
  try {
584
802
  await mkdir(resolve(resolved, ".."), { recursive: true });
585
- const afterMkdirAbort = abortResult(abortSignal, resolved);
803
+ const afterMkdirAbort = abortResult(signal, resolved);
586
804
  if (afterMkdirAbort)
587
805
  return afterMkdirAbort;
588
- await writeFile(resolved, content, { signal: abortSignal });
806
+ await writeFile(resolved, content, { signal });
589
807
  return ok(undefined);
590
808
  }
591
809
  catch (error) {
592
810
  return err(toFileError(error, resolved));
593
811
  }
594
812
  }
595
- async appendFile(path, content) {
813
+ async appendFile(path, content, context) {
596
814
  const resolved = resolvePath(this.cwd, path);
815
+ const signal = context.abortSignal;
816
+ const aborted = abortResult(signal, resolved);
817
+ if (aborted)
818
+ return aborted;
597
819
  try {
598
820
  await mkdir(resolve(resolved, ".."), { recursive: true });
821
+ const afterMkdirAbort = abortResult(signal, resolved);
822
+ if (afterMkdirAbort)
823
+ return afterMkdirAbort;
599
824
  await appendFile(resolved, content);
600
- return ok(undefined);
825
+ const afterAppendAbort = abortResult(signal, resolved);
826
+ return afterAppendAbort ?? ok(undefined);
601
827
  }
602
828
  catch (error) {
603
829
  return err(toFileError(error, resolved));
604
830
  }
605
831
  }
606
- async renameFile(sourcePath, destinationPath, abortSignal) {
832
+ async renameFile(sourcePath, destinationPath, context) {
607
833
  const source = resolvePath(this.cwd, sourcePath);
608
834
  const destination = resolvePath(this.cwd, destinationPath);
609
- const aborted = abortResult(abortSignal, destination);
835
+ const aborted = abortResult(context.abortSignal, destination);
610
836
  if (aborted)
611
837
  return aborted;
612
838
  try {
@@ -617,8 +843,11 @@ export class NodeExecutionEnv {
617
843
  return err(toFileError(error, source));
618
844
  }
619
845
  }
620
- async fileInfo(path) {
846
+ async fileInfo(path, context) {
621
847
  const resolved = resolvePath(this.cwd, path);
848
+ const aborted = abortResult(context.abortSignal, resolved);
849
+ if (aborted)
850
+ return aborted;
622
851
  try {
623
852
  return fileInfoFromStats(resolved, await lstat(resolved));
624
853
  }
@@ -626,16 +855,17 @@ export class NodeExecutionEnv {
626
855
  return err(toFileError(error, resolved));
627
856
  }
628
857
  }
629
- async listDir(path, abortSignal) {
858
+ async listDir(path, context) {
630
859
  const resolved = resolvePath(this.cwd, path);
631
- const aborted = abortResult(abortSignal, resolved);
860
+ const signal = context.abortSignal;
861
+ const aborted = abortResult(signal, resolved);
632
862
  if (aborted)
633
863
  return aborted;
634
864
  try {
635
865
  const entries = await readdir(resolved, { withFileTypes: true });
636
866
  const infos = [];
637
867
  for (const entry of entries) {
638
- const loopAbort = abortResult(abortSignal, resolved);
868
+ const loopAbort = abortResult(signal, resolved);
639
869
  if (loopAbort)
640
870
  return loopAbort;
641
871
  const entryPath = resolve(resolved, entry.name);
@@ -654,8 +884,11 @@ export class NodeExecutionEnv {
654
884
  return err(toFileError(error, resolved));
655
885
  }
656
886
  }
657
- async canonicalPath(path) {
887
+ async canonicalPath(path, context) {
658
888
  const resolved = resolvePath(this.cwd, path);
889
+ const aborted = abortResult(context.abortSignal, resolved);
890
+ if (aborted)
891
+ return aborted;
659
892
  try {
660
893
  return ok(await realpath(resolved));
661
894
  }
@@ -663,16 +896,19 @@ export class NodeExecutionEnv {
663
896
  return err(toFileError(error, resolved));
664
897
  }
665
898
  }
666
- async exists(path) {
667
- const result = await this.fileInfo(path);
899
+ async exists(path, context) {
900
+ const result = await this.fileInfo(path, context);
668
901
  if (result.ok)
669
902
  return ok(true);
670
903
  if (result.error.code === "not_found")
671
904
  return ok(false);
672
905
  return err(result.error);
673
906
  }
674
- async createDir(path, options) {
907
+ async createDir(path, options, context) {
675
908
  const resolved = resolvePath(this.cwd, path);
909
+ const aborted = abortResult(context.abortSignal, resolved);
910
+ if (aborted)
911
+ return aborted;
676
912
  try {
677
913
  await mkdir(resolved, { recursive: options?.recursive ?? true });
678
914
  return ok(undefined);
@@ -681,8 +917,11 @@ export class NodeExecutionEnv {
681
917
  return err(toFileError(error, resolved));
682
918
  }
683
919
  }
684
- async remove(path, options) {
920
+ async remove(path, options, context) {
685
921
  const resolved = resolvePath(this.cwd, path);
922
+ const aborted = abortResult(context.abortSignal, resolved);
923
+ if (aborted)
924
+ return aborted;
686
925
  try {
687
926
  await rm(resolved, { recursive: options?.recursive ?? false, force: options?.force ?? false });
688
927
  return ok(undefined);
@@ -691,16 +930,20 @@ export class NodeExecutionEnv {
691
930
  return err(toFileError(error, resolved));
692
931
  }
693
932
  }
694
- async createTempDir(prefix = "tmp-") {
933
+ async createTempDir(prefix, context) {
934
+ const aborted = abortResult(context.abortSignal);
935
+ if (aborted)
936
+ return aborted;
695
937
  try {
938
+ prefix ??= "tmp-";
696
939
  return ok(await mkdtemp(join(tmpdir(), prefix)));
697
940
  }
698
941
  catch (error) {
699
942
  return err(toFileError(error));
700
943
  }
701
944
  }
702
- async createTempFile(options) {
703
- const dir = await this.createTempDir("tmp-");
945
+ async createTempFile(options, context) {
946
+ const dir = await this.createTempDir("tmp-", context);
704
947
  if (!dir.ok)
705
948
  return dir;
706
949
  const filePath = join(dir.value, `${options?.prefix ?? ""}${randomUUID()}${options?.suffix ?? ""}`);
@@ -712,7 +955,7 @@ export class NodeExecutionEnv {
712
955
  return err(toFileError(error, filePath));
713
956
  }
714
957
  }
715
- async cleanup() {
958
+ async cleanup(_context) {
716
959
  for (const pid of this.activeChildPids)
717
960
  killProcessTree(pid);
718
961
  this.activeChildPids.clear();