@earendil-works/pi-agent-core 0.84.3 → 0.85.0

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 (343) hide show
  1. package/README.md +4 -0
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +32 -25
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/harness/agent-harness.d.ts +639 -384
  6. package/dist/harness/agent-harness.d.ts.map +1 -1
  7. package/dist/harness/agent-harness.js +5 -251
  8. package/dist/harness/agent-harness.js.map +1 -1
  9. package/dist/harness/compaction/branch-summarization.d.ts +12 -6
  10. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  11. package/dist/harness/compaction/branch-summarization.js +33 -26
  12. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  13. package/dist/harness/compaction/compaction.d.ts +30 -8
  14. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  15. package/dist/harness/compaction/compaction.js +53 -63
  16. package/dist/harness/compaction/compaction.js.map +1 -1
  17. package/dist/harness/config.d.ts +9 -0
  18. package/dist/harness/config.d.ts.map +1 -0
  19. package/dist/harness/config.js +27 -0
  20. package/dist/harness/config.js.map +1 -0
  21. package/dist/harness/context.d.ts +9 -0
  22. package/dist/harness/context.d.ts.map +1 -0
  23. package/dist/harness/context.js +13 -0
  24. package/dist/harness/context.js.map +1 -0
  25. package/dist/harness/env/nodejs.d.ts +24 -28
  26. package/dist/harness/env/nodejs.d.ts.map +1 -1
  27. package/dist/harness/env/nodejs.js +242 -87
  28. package/dist/harness/env/nodejs.js.map +1 -1
  29. package/dist/harness/events.d.ts +20 -38
  30. package/dist/harness/events.d.ts.map +1 -1
  31. package/dist/harness/events.js +240 -55
  32. package/dist/harness/events.js.map +1 -1
  33. package/dist/harness/execution/assistant.d.ts +42 -0
  34. package/dist/harness/execution/assistant.d.ts.map +1 -0
  35. package/dist/harness/execution/assistant.js +82 -0
  36. package/dist/harness/execution/assistant.js.map +1 -0
  37. package/dist/harness/execution/effect-gate.d.ts +22 -0
  38. package/dist/harness/execution/effect-gate.d.ts.map +1 -0
  39. package/dist/harness/execution/effect-gate.js +49 -0
  40. package/dist/harness/execution/effect-gate.js.map +1 -0
  41. package/dist/harness/execution/tools.d.ts +67 -0
  42. package/dist/harness/execution/tools.d.ts.map +1 -0
  43. package/dist/harness/execution/tools.js +121 -0
  44. package/dist/harness/execution/tools.js.map +1 -0
  45. package/dist/harness/hooks.d.ts +38 -0
  46. package/dist/harness/hooks.d.ts.map +1 -0
  47. package/dist/harness/hooks.js +406 -0
  48. package/dist/harness/hooks.js.map +1 -0
  49. package/dist/harness/messages.d.ts +2 -2
  50. package/dist/harness/messages.d.ts.map +1 -1
  51. package/dist/harness/messages.js.map +1 -1
  52. package/dist/harness/prompt-templates.d.ts +3 -2
  53. package/dist/harness/prompt-templates.d.ts.map +1 -1
  54. package/dist/harness/prompt-templates.js +17 -17
  55. package/dist/harness/prompt-templates.js.map +1 -1
  56. package/dist/harness/result.d.ts +93 -0
  57. package/dist/harness/result.d.ts.map +1 -1
  58. package/dist/harness/result.js +40 -0
  59. package/dist/harness/result.js.map +1 -1
  60. package/dist/harness/runtime/drive/boundary.d.ts +28 -0
  61. package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
  62. package/dist/harness/runtime/drive/boundary.js +170 -0
  63. package/dist/harness/runtime/drive/boundary.js.map +1 -0
  64. package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
  65. package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
  66. package/dist/harness/runtime/drive/checkpoint.js +133 -0
  67. package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
  68. package/dist/harness/runtime/drive/deferred.d.ts +15 -0
  69. package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
  70. package/dist/harness/runtime/drive/deferred.js +179 -0
  71. package/dist/harness/runtime/drive/deferred.js.map +1 -0
  72. package/dist/harness/runtime/drive/generation.d.ts +8 -0
  73. package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
  74. package/dist/harness/runtime/drive/generation.js +205 -0
  75. package/dist/harness/runtime/drive/generation.js.map +1 -0
  76. package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
  77. package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
  78. package/dist/harness/runtime/drive/reconcile.js +140 -0
  79. package/dist/harness/runtime/drive/reconcile.js.map +1 -0
  80. package/dist/harness/runtime/drive/recovery.d.ts +8 -0
  81. package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
  82. package/dist/harness/runtime/drive/recovery.js +85 -0
  83. package/dist/harness/runtime/drive/recovery.js.map +1 -0
  84. package/dist/harness/runtime/drive/response.d.ts +23 -0
  85. package/dist/harness/runtime/drive/response.d.ts.map +1 -0
  86. package/dist/harness/runtime/drive/response.js +394 -0
  87. package/dist/harness/runtime/drive/response.js.map +1 -0
  88. package/dist/harness/runtime/drive/retry.d.ts +4 -0
  89. package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
  90. package/dist/harness/runtime/drive/retry.js +37 -0
  91. package/dist/harness/runtime/drive/retry.js.map +1 -0
  92. package/dist/harness/runtime/drive/structural.d.ts +32 -0
  93. package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
  94. package/dist/harness/runtime/drive/structural.js +929 -0
  95. package/dist/harness/runtime/drive/structural.js.map +1 -0
  96. package/dist/harness/runtime/drive/terminal.d.ts +7 -0
  97. package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
  98. package/dist/harness/runtime/drive/terminal.js +49 -0
  99. package/dist/harness/runtime/drive/terminal.js.map +1 -0
  100. package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
  101. package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
  102. package/dist/harness/runtime/drive/tool-placement.js +244 -0
  103. package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
  104. package/dist/harness/runtime/drive/tools.d.ts +6 -0
  105. package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
  106. package/dist/harness/runtime/drive/tools.js +449 -0
  107. package/dist/harness/runtime/drive/tools.js.map +1 -0
  108. package/dist/harness/runtime/drive.d.ts +6 -0
  109. package/dist/harness/runtime/drive.d.ts.map +1 -0
  110. package/dist/harness/runtime/drive.js +93 -0
  111. package/dist/harness/runtime/drive.js.map +1 -0
  112. package/dist/harness/runtime/harness.d.ts +59 -0
  113. package/dist/harness/runtime/harness.d.ts.map +1 -0
  114. package/dist/harness/runtime/harness.js +311 -0
  115. package/dist/harness/runtime/harness.js.map +1 -0
  116. package/dist/harness/runtime/index.d.ts +2 -0
  117. package/dist/harness/runtime/index.d.ts.map +1 -0
  118. package/dist/harness/runtime/index.js +2 -0
  119. package/dist/harness/runtime/index.js.map +1 -0
  120. package/dist/harness/runtime/lane.d.ts +101 -0
  121. package/dist/harness/runtime/lane.d.ts.map +1 -0
  122. package/dist/harness/runtime/lane.js +1575 -0
  123. package/dist/harness/runtime/lane.js.map +1 -0
  124. package/dist/harness/runtime/progress.d.ts +15 -0
  125. package/dist/harness/runtime/progress.d.ts.map +1 -0
  126. package/dist/harness/runtime/progress.js +66 -0
  127. package/dist/harness/runtime/progress.js.map +1 -0
  128. package/dist/harness/runtime/reducer.d.ts +5 -0
  129. package/dist/harness/runtime/reducer.d.ts.map +1 -0
  130. package/dist/harness/runtime/reducer.js +240 -0
  131. package/dist/harness/runtime/reducer.js.map +1 -0
  132. package/dist/harness/runtime/restore.d.ts +24 -0
  133. package/dist/harness/runtime/restore.d.ts.map +1 -0
  134. package/dist/harness/runtime/restore.js +117 -0
  135. package/dist/harness/runtime/restore.js.map +1 -0
  136. package/dist/harness/runtime/transcript.d.ts +21 -0
  137. package/dist/harness/runtime/transcript.d.ts.map +1 -0
  138. package/dist/harness/runtime/transcript.js +72 -0
  139. package/dist/harness/runtime/transcript.js.map +1 -0
  140. package/dist/harness/runtime/types.d.ts +105 -0
  141. package/dist/harness/runtime/types.d.ts.map +1 -0
  142. package/dist/harness/runtime/types.js +61 -0
  143. package/dist/harness/runtime/types.js.map +1 -0
  144. package/dist/harness/session/commit.d.ts +53 -0
  145. package/dist/harness/session/commit.d.ts.map +1 -0
  146. package/dist/harness/session/commit.js +57 -0
  147. package/dist/harness/session/commit.js.map +1 -0
  148. package/dist/harness/session/context.d.ts +6 -18
  149. package/dist/harness/session/context.d.ts.map +1 -1
  150. package/dist/harness/session/context.js +31 -49
  151. package/dist/harness/session/context.js.map +1 -1
  152. package/dist/harness/session/fork-policy.d.ts +13 -0
  153. package/dist/harness/session/fork-policy.d.ts.map +1 -0
  154. package/dist/harness/session/fork-policy.js +22 -0
  155. package/dist/harness/session/fork-policy.js.map +1 -0
  156. package/dist/harness/session/fork.d.ts +18 -0
  157. package/dist/harness/session/fork.d.ts.map +1 -0
  158. package/dist/harness/session/fork.js +127 -0
  159. package/dist/harness/session/fork.js.map +1 -0
  160. package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
  161. package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
  162. package/dist/harness/session/in-memory-storage-state.js +216 -0
  163. package/dist/harness/session/in-memory-storage-state.js.map +1 -0
  164. package/dist/harness/session/index.d.ts +9 -5
  165. package/dist/harness/session/index.d.ts.map +1 -1
  166. package/dist/harness/session/index.js +7 -4
  167. package/dist/harness/session/index.js.map +1 -1
  168. package/dist/harness/session/jsonl/codec.d.ts +19 -8
  169. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  170. package/dist/harness/session/jsonl/codec.js +34 -218
  171. package/dist/harness/session/jsonl/codec.js.map +1 -1
  172. package/dist/harness/session/jsonl/index.d.ts +4 -0
  173. package/dist/harness/session/jsonl/index.d.ts.map +1 -0
  174. package/dist/harness/session/jsonl/index.js +4 -0
  175. package/dist/harness/session/jsonl/index.js.map +1 -0
  176. package/dist/harness/session/jsonl/legacy-v3.d.ts +18 -0
  177. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
  178. package/dist/harness/session/jsonl/legacy-v3.js +383 -0
  179. package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
  180. package/dist/harness/session/jsonl/repo.d.ts +26 -19
  181. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  182. package/dist/harness/session/jsonl/repo.js +266 -156
  183. package/dist/harness/session/jsonl/repo.js.map +1 -1
  184. package/dist/harness/session/jsonl/storage.d.ts +39 -41
  185. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  186. package/dist/harness/session/jsonl/storage.js +238 -181
  187. package/dist/harness/session/jsonl/storage.js.map +1 -1
  188. package/dist/harness/session/jsonl/types.d.ts +23 -22
  189. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  190. package/dist/harness/session/jsonl/types.js +2 -1
  191. package/dist/harness/session/jsonl/types.js.map +1 -1
  192. package/dist/harness/session/memory.d.ts +46 -40
  193. package/dist/harness/session/memory.d.ts.map +1 -1
  194. package/dist/harness/session/memory.js +346 -113
  195. package/dist/harness/session/memory.js.map +1 -1
  196. package/dist/harness/session/mutation-line.d.ts +8 -0
  197. package/dist/harness/session/mutation-line.d.ts.map +1 -0
  198. package/dist/harness/session/mutation-line.js +21 -0
  199. package/dist/harness/session/mutation-line.js.map +1 -0
  200. package/dist/harness/session/session.d.ts +78 -46
  201. package/dist/harness/session/session.d.ts.map +1 -1
  202. package/dist/harness/session/session.js +343 -209
  203. package/dist/harness/session/session.js.map +1 -1
  204. package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
  205. package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
  206. package/dist/harness/session/testing/benchmark/datasets.js +24 -0
  207. package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
  208. package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
  209. package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
  210. package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
  211. package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
  212. package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
  213. package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
  214. package/dist/harness/session/testing/benchmark/storage.js +114 -0
  215. package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
  216. package/dist/harness/session/testing/conformance/session-repo.d.ts +21 -0
  217. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
  218. package/dist/harness/session/testing/conformance/session-repo.js +590 -0
  219. package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
  220. package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
  221. package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
  222. package/dist/harness/session/testing/conformance/storage.js +600 -0
  223. package/dist/harness/session/testing/conformance/storage.js.map +1 -0
  224. package/dist/harness/session/testing/gating-storage.d.ts +26 -0
  225. package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
  226. package/dist/harness/session/testing/gating-storage.js +100 -0
  227. package/dist/harness/session/testing/gating-storage.js.map +1 -0
  228. package/dist/harness/session/testing/index.d.ts +9 -2
  229. package/dist/harness/session/testing/index.d.ts.map +1 -1
  230. package/dist/harness/session/testing/index.js +8 -1
  231. package/dist/harness/session/testing/index.js.map +1 -1
  232. package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
  233. package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
  234. package/dist/harness/session/testing/instrumented-storage.js +16 -0
  235. package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
  236. package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
  237. package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
  238. package/dist/harness/session/testing/storage-decorator.js +41 -0
  239. package/dist/harness/session/testing/storage-decorator.js.map +1 -0
  240. package/dist/harness/session/testing/types.d.ts +5 -7
  241. package/dist/harness/session/testing/types.d.ts.map +1 -1
  242. package/dist/harness/session/testing/types.js.map +1 -1
  243. package/dist/harness/session/types.d.ts +424 -259
  244. package/dist/harness/session/types.d.ts.map +1 -1
  245. package/dist/harness/session/types.js +7 -8
  246. package/dist/harness/session/types.js.map +1 -1
  247. package/dist/harness/session/values.d.ts +95 -0
  248. package/dist/harness/session/values.d.ts.map +1 -0
  249. package/dist/harness/session/values.js +81 -0
  250. package/dist/harness/session/values.js.map +1 -0
  251. package/dist/harness/skills.d.ts +3 -2
  252. package/dist/harness/skills.d.ts.map +1 -1
  253. package/dist/harness/skills.js +29 -26
  254. package/dist/harness/skills.js.map +1 -1
  255. package/dist/harness/telemetry.d.ts +64 -43
  256. package/dist/harness/telemetry.d.ts.map +1 -1
  257. package/dist/harness/telemetry.js +45 -26
  258. package/dist/harness/telemetry.js.map +1 -1
  259. package/dist/harness/tools/bash.d.ts +4 -4
  260. package/dist/harness/tools/bash.d.ts.map +1 -1
  261. package/dist/harness/tools/bash.js +66 -93
  262. package/dist/harness/tools/bash.js.map +1 -1
  263. package/dist/harness/tools/edit.d.ts.map +1 -1
  264. package/dist/harness/tools/edit.js +10 -10
  265. package/dist/harness/tools/edit.js.map +1 -1
  266. package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
  267. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
  268. package/dist/harness/tools/file-mutation-queue.js +5 -5
  269. package/dist/harness/tools/file-mutation-queue.js.map +1 -1
  270. package/dist/harness/tools/path-utils.d.ts +3 -2
  271. package/dist/harness/tools/path-utils.d.ts.map +1 -1
  272. package/dist/harness/tools/path-utils.js +5 -5
  273. package/dist/harness/tools/path-utils.js.map +1 -1
  274. package/dist/harness/tools/read.d.ts +2 -1
  275. package/dist/harness/tools/read.d.ts.map +1 -1
  276. package/dist/harness/tools/read.js +4 -6
  277. package/dist/harness/tools/read.js.map +1 -1
  278. package/dist/harness/tools/write.d.ts.map +1 -1
  279. package/dist/harness/tools/write.js +7 -7
  280. package/dist/harness/tools/write.js.map +1 -1
  281. package/dist/harness/types.d.ts +108 -45
  282. package/dist/harness/types.d.ts.map +1 -1
  283. package/dist/harness/types.js.map +1 -1
  284. package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
  285. package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
  286. package/dist/harness/utils/adaptive-publisher.js +79 -0
  287. package/dist/harness/utils/adaptive-publisher.js.map +1 -0
  288. package/dist/harness/utils/output-capture.d.ts +31 -0
  289. package/dist/harness/utils/output-capture.d.ts.map +1 -0
  290. package/dist/harness/utils/output-capture.js +217 -0
  291. package/dist/harness/utils/output-capture.js.map +1 -0
  292. package/dist/harness/utils/shell-output.d.ts +12 -5
  293. package/dist/harness/utils/shell-output.d.ts.map +1 -1
  294. package/dist/harness/utils/shell-output.js +64 -174
  295. package/dist/harness/utils/shell-output.js.map +1 -1
  296. package/dist/harness/utils/truncate.d.ts +1 -0
  297. package/dist/harness/utils/truncate.d.ts.map +1 -1
  298. package/dist/harness/utils/truncate.js +1 -1
  299. package/dist/harness/utils/truncate.js.map +1 -1
  300. package/dist/harness/utils/usage.d.ts +4 -0
  301. package/dist/harness/utils/usage.d.ts.map +1 -0
  302. package/dist/harness/utils/usage.js +33 -0
  303. package/dist/harness/utils/usage.js.map +1 -0
  304. package/dist/index.d.ts +4 -1
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +3 -6
  307. package/dist/index.js.map +1 -1
  308. package/dist/proxy.d.ts +2 -0
  309. package/dist/proxy.d.ts.map +1 -1
  310. package/dist/proxy.js +40 -10
  311. package/dist/proxy.js.map +1 -1
  312. package/dist/search/index.d.ts +24 -16
  313. package/dist/search/index.d.ts.map +1 -1
  314. package/dist/search/index.js +1 -1
  315. package/dist/search/index.js.map +1 -1
  316. package/dist/types.d.ts +5 -2
  317. package/dist/types.d.ts.map +1 -1
  318. package/dist/types.js.map +1 -1
  319. package/package.json +25 -4
  320. package/dist/harness/reducer.d.ts +0 -100
  321. package/dist/harness/reducer.d.ts.map +0 -1
  322. package/dist/harness/reducer.js +0 -415
  323. package/dist/harness/reducer.js.map +0 -1
  324. package/dist/harness/session/jsonl/errors.d.ts +0 -9
  325. package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
  326. package/dist/harness/session/jsonl/errors.js +0 -19
  327. package/dist/harness/session/jsonl/errors.js.map +0 -1
  328. package/dist/harness/session/jsonl.d.ts +0 -3
  329. package/dist/harness/session/jsonl.d.ts.map +0 -1
  330. package/dist/harness/session/jsonl.js +0 -2
  331. package/dist/harness/session/jsonl.js.map +0 -1
  332. package/dist/harness/session/state.d.ts +0 -65
  333. package/dist/harness/session/state.d.ts.map +0 -1
  334. package/dist/harness/session/state.js +0 -319
  335. package/dist/harness/session/state.js.map +0 -1
  336. package/dist/harness/session/testing/conformance.d.ts +0 -4
  337. package/dist/harness/session/testing/conformance.d.ts.map +0 -1
  338. package/dist/harness/session/testing/conformance.js +0 -762
  339. package/dist/harness/session/testing/conformance.js.map +0 -1
  340. package/dist/search/scanning.d.ts +0 -29
  341. package/dist/search/scanning.d.ts.map +0 -1
  342. package/dist/search/scanning.js +0 -103
  343. package/dist/search/scanning.js.map +0 -1
@@ -1,15 +1,17 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
- import { constants, createReadStream } from "node:fs";
3
+ import { constants, createReadStream, createWriteStream } from "node:fs";
4
4
  import { access, appendFile, lstat, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, writeFile, } from "node:fs/promises";
5
- import { homedir, tmpdir } from "node:os";
5
+ import { homedir, constants as osConstants, tmpdir } from "node:os";
6
6
  import { basename, isAbsolute, join, resolve } from "node:path";
7
7
  import { createInterface } from "node:readline";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { ExecutionError, err, FileError, ok, toError, } from "../types.js";
10
+ import { OutputCapture } from "../utils/output-capture.js";
10
11
  const MAX_TIMEOUT_MS = 2_147_483_647;
11
12
  const MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;
12
13
  const EXIT_STDIO_GRACE_MS = 100;
14
+ const SPILL_HIGH_WATER_MARK = 8 * 1024 * 1024;
13
15
  function resolveTimeoutMs(timeout) {
14
16
  if (timeout === undefined)
15
17
  return ok(undefined);
@@ -201,11 +203,13 @@ function getShellEnv(baseEnv, extraEnv, inheritEnv = true) {
201
203
  function killProcessTree(pid) {
202
204
  if (process.platform === "win32") {
203
205
  try {
204
- spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
206
+ const child = spawn(join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], {
205
207
  stdio: "ignore",
206
208
  detached: true,
207
209
  windowsHide: true,
208
210
  });
211
+ // A failed spawn emits "error" asynchronously; consume it to avoid crashing Node.
212
+ child.once("error", () => { });
209
213
  }
210
214
  catch {
211
215
  // Ignore errors.
@@ -224,11 +228,12 @@ function killProcessTree(pid) {
224
228
  }
225
229
  }
226
230
  }
227
- function waitForChildProcess(child) {
231
+ function waitForChildProcess(child, spillIsDraining) {
228
232
  return new Promise((resolvePromise, reject) => {
229
233
  let settled = false;
230
234
  let exited = false;
231
235
  let exitCode = null;
236
+ let exitSignal = null;
232
237
  let postExitTimer;
233
238
  let stdoutEnded = child.stdout === null;
234
239
  let stderrEnded = child.stderr === null;
@@ -243,23 +248,28 @@ function waitForChildProcess(child) {
243
248
  child.stdout?.removeListener("data", onData);
244
249
  child.stderr?.removeListener("data", onData);
245
250
  };
246
- const finalize = (code) => {
251
+ const finalize = () => {
247
252
  if (settled)
248
253
  return;
249
254
  settled = true;
250
255
  cleanup();
251
256
  child.stdout?.destroy();
252
257
  child.stderr?.destroy();
253
- resolvePromise(code);
258
+ resolvePromise({ code: exitCode, signal: exitSignal });
254
259
  };
255
260
  const maybeFinalizeAfterExit = () => {
256
261
  if (exited && stdoutEnded && stderrEnded)
257
- finalize(exitCode);
262
+ finalize();
258
263
  };
259
264
  const armIdleTimer = () => {
260
265
  if (postExitTimer)
261
266
  clearTimeout(postExitTimer);
262
- postExitTimer = setTimeout(() => finalize(exitCode), EXIT_STDIO_GRACE_MS);
267
+ postExitTimer = setTimeout(() => {
268
+ if (spillIsDraining())
269
+ armIdleTimer();
270
+ else
271
+ finalize();
272
+ }, EXIT_STDIO_GRACE_MS);
263
273
  };
264
274
  const onData = () => {
265
275
  if (exited && !settled)
@@ -280,14 +290,19 @@ function waitForChildProcess(child) {
280
290
  cleanup();
281
291
  reject(error);
282
292
  };
283
- const onExit = (code) => {
293
+ const onExit = (code, signal) => {
284
294
  exited = true;
285
295
  exitCode = code;
296
+ exitSignal = signal;
286
297
  maybeFinalizeAfterExit();
287
298
  if (!settled)
288
299
  armIdleTimer();
289
300
  };
290
- const onClose = (code) => finalize(code);
301
+ const onClose = (code, signal) => {
302
+ exitCode = code;
303
+ exitSignal = signal;
304
+ finalize();
305
+ };
291
306
  child.stdout?.once("end", onStdoutEnd);
292
307
  child.stderr?.once("end", onStderrEnd);
293
308
  child.stdout?.on("data", onData);
@@ -307,14 +322,15 @@ export class NodeExecutionEnv {
307
322
  this.shellPath = options.shellPath;
308
323
  this.shellEnv = options.shellEnv;
309
324
  }
310
- async absolutePath(path) {
325
+ async absolutePath(path, _context) {
311
326
  return ok(resolvePath(this.cwd, path));
312
327
  }
313
- async joinPath(parts) {
328
+ async joinPath(parts, _context) {
314
329
  return ok(join(...parts));
315
330
  }
316
- async exec(command, options) {
317
- if (options?.abortSignal?.aborted)
331
+ async exec(command, options, context) {
332
+ const signal = context.abortSignal;
333
+ if (signal?.aborted)
318
334
  return err(new ExecutionError("aborted", "aborted"));
319
335
  const timeoutMsResult = resolveTimeoutMs(options?.timeout);
320
336
  if (!timeoutMsResult.ok)
@@ -332,30 +348,119 @@ export class NodeExecutionEnv {
332
348
  return err(new ExecutionError("spawn_error", `Working directory does not exist: ${cwd}\nCannot execute bash commands.`, cause));
333
349
  }
334
350
  return await new Promise((resolvePromise) => {
335
- let stdout = "";
336
- let stderr = "";
337
351
  let settled = false;
338
352
  let timedOut = false;
339
353
  let callbackError;
354
+ let spillError;
340
355
  let child;
341
356
  let timeoutId;
357
+ const spillPrefix = [];
358
+ let spillPath;
359
+ const spillQueue = [];
360
+ let spillStart;
361
+ let spillStream;
362
+ let spillBackpressured = false;
342
363
  const onAbort = () => {
343
- if (child?.pid) {
364
+ if (child?.pid)
344
365
  killProcessTree(child.pid);
345
- }
346
366
  };
367
+ const failCallback = (error) => {
368
+ if (callbackError !== undefined)
369
+ return;
370
+ const cause = toError(error);
371
+ callbackError = new ExecutionError("callback_error", cause.message, cause);
372
+ onAbort();
373
+ };
374
+ let capture;
375
+ try {
376
+ capture = new OutputCapture(options?.capture, context, {
377
+ onUpdate: options?.onUpdate,
378
+ onError: failCallback,
379
+ });
380
+ }
381
+ catch (error) {
382
+ const cause = toError(error);
383
+ resolvePromise(err(new ExecutionError("unknown", cause.message, cause)));
384
+ return;
385
+ }
347
386
  const settle = (result) => {
387
+ if (settled)
388
+ return;
389
+ settled = true;
348
390
  if (timeoutId)
349
391
  clearTimeout(timeoutId);
350
- if (options?.abortSignal)
351
- options.abortSignal.removeEventListener("abort", onAbort);
392
+ if (signal)
393
+ signal.removeEventListener("abort", onAbort);
352
394
  if (child?.pid)
353
395
  this.activeChildPids.delete(child.pid);
354
- if (settled)
355
- return;
356
- settled = true;
396
+ capture.dispose();
357
397
  resolvePromise(result);
358
398
  };
399
+ const pauseOutput = () => {
400
+ child?.stdout?.pause();
401
+ child?.stderr?.pause();
402
+ };
403
+ const resumeOutput = () => {
404
+ if (callbackError || spillError || timedOut || signal?.aborted || spillBackpressured)
405
+ return;
406
+ child?.stdout?.resume();
407
+ child?.stderr?.resume();
408
+ };
409
+ const failSpill = (error) => {
410
+ if (spillError !== undefined)
411
+ return;
412
+ const cause = toError(error);
413
+ spillError = new ExecutionError("unknown", `Failed to preserve complete shell output: ${cause.message}`, cause);
414
+ spillBackpressured = false;
415
+ onAbort();
416
+ };
417
+ const writeSpill = (chunk) => {
418
+ if (spillStream === undefined || chunk.length === 0)
419
+ return;
420
+ if (spillStream.write(chunk) || spillBackpressured)
421
+ return;
422
+ spillBackpressured = true;
423
+ pauseOutput();
424
+ spillStream.once("drain", () => {
425
+ spillBackpressured = false;
426
+ resumeOutput();
427
+ });
428
+ };
429
+ const startSpill = (chunk) => {
430
+ if (spillStream !== undefined) {
431
+ writeSpill(chunk);
432
+ return;
433
+ }
434
+ spillQueue.push(chunk);
435
+ if (spillStart !== undefined)
436
+ return;
437
+ pauseOutput();
438
+ spillStart = (async () => {
439
+ const created = await this.createTempFile({ prefix: "pi-output-", suffix: ".log" }, context);
440
+ if (!created.ok)
441
+ throw created.error;
442
+ spillPath = created.value;
443
+ capture.setSpillPath(spillPath);
444
+ spillStream = createWriteStream(spillPath, { flags: "a", highWaterMark: SPILL_HIGH_WATER_MARK });
445
+ spillStream.on("error", failSpill);
446
+ for (const queued of spillQueue)
447
+ writeSpill(queued);
448
+ spillQueue.length = 0;
449
+ })()
450
+ .catch(failSpill)
451
+ .finally(resumeOutput);
452
+ };
453
+ const finishSpill = async () => {
454
+ await spillStart;
455
+ const stream = spillStream;
456
+ if (stream === undefined || spillError !== undefined || stream.destroyed)
457
+ return;
458
+ await new Promise((resolveFinish) => {
459
+ stream.once("error", () => resolveFinish());
460
+ stream.once("finish", resolveFinish);
461
+ stream.end();
462
+ });
463
+ };
359
464
  try {
360
465
  const commandFromStdin = shellConfig.value.commandTransport === "stdin";
361
466
  child = spawn(shellConfig.value.shell, commandFromStdin ? shellConfig.value.args : [...shellConfig.value.args, command], {
@@ -378,47 +483,54 @@ export class NodeExecutionEnv {
378
483
  return;
379
484
  }
380
485
  timeoutId =
381
- timeoutMs !== undefined
382
- ? setTimeout(() => {
486
+ timeoutMs === undefined
487
+ ? undefined
488
+ : setTimeout(() => {
383
489
  timedOut = true;
384
- if (child?.pid) {
385
- killProcessTree(child.pid);
386
- }
387
- }, timeoutMs)
388
- : undefined;
389
- if (options?.abortSignal) {
390
- if (options.abortSignal.aborted) {
490
+ onAbort();
491
+ }, timeoutMs);
492
+ if (signal) {
493
+ if (signal.aborted)
391
494
  onAbort();
392
- }
393
- else {
394
- options.abortSignal.addEventListener("abort", onAbort, { once: true });
395
- }
495
+ else
496
+ signal.addEventListener("abort", onAbort, { once: true });
396
497
  }
397
- child.stdout?.setEncoding("utf8");
398
- child.stderr?.setEncoding("utf8");
399
- child.stdout?.on("data", (chunk) => {
400
- stdout += chunk;
498
+ const feed = (chunk) => {
401
499
  try {
402
- options?.onStdout?.(chunk);
500
+ const wasTruncated = capture.truncated;
501
+ capture.push(chunk);
502
+ if (!options?.capture?.spill || chunk.length === 0)
503
+ return;
504
+ if (spillPath !== undefined || wasTruncated) {
505
+ startSpill(chunk);
506
+ }
507
+ else if (capture.truncated) {
508
+ for (const prefix of spillPrefix)
509
+ startSpill(prefix);
510
+ spillPrefix.length = 0;
511
+ startSpill(chunk);
512
+ }
513
+ else {
514
+ spillPrefix.push(chunk);
515
+ }
403
516
  }
404
517
  catch (error) {
405
- const cause = toError(error);
406
- callbackError = new ExecutionError("callback_error", cause.message, cause);
407
- onAbort();
518
+ failCallback(error);
408
519
  }
409
- });
410
- child.stderr?.on("data", (chunk) => {
411
- stderr += chunk;
520
+ };
521
+ child.stdout?.on("data", feed);
522
+ child.stderr?.on("data", feed);
523
+ void waitForChildProcess(child, () => spillError === undefined &&
524
+ spillStart !== undefined &&
525
+ (spillStream === undefined || spillBackpressured)).then(async ({ code, signal: exitSignal }) => {
526
+ await finishSpill();
412
527
  try {
413
- options?.onStderr?.(chunk);
528
+ capture.finish();
529
+ capture.flush();
414
530
  }
415
531
  catch (error) {
416
- const cause = toError(error);
417
- callbackError = new ExecutionError("callback_error", cause.message, cause);
418
- onAbort();
532
+ failCallback(error);
419
533
  }
420
- });
421
- void waitForChildProcess(child).then((code) => {
422
534
  if (callbackError) {
423
535
  settle(err(callbackError));
424
536
  return;
@@ -427,29 +539,45 @@ export class NodeExecutionEnv {
427
539
  settle(err(new ExecutionError("timeout", `timeout:${options?.timeout}`)));
428
540
  return;
429
541
  }
430
- if (options?.abortSignal?.aborted) {
542
+ if (signal?.aborted) {
431
543
  settle(err(new ExecutionError("aborted", "aborted")));
432
544
  return;
433
545
  }
434
- settle(ok({ stdout, stderr, exitCode: code ?? 0 }));
546
+ if (spillError) {
547
+ settle(err(spillError));
548
+ return;
549
+ }
550
+ const output = capture.snapshot();
551
+ // A process killed by a signal (e.g. OOM killer) has no exit code; map it
552
+ // to the conventional 128 + signal number so callers do not mistake it
553
+ // for a successful exit.
554
+ const exitCode = code ?? (exitSignal ? 128 + (osConstants.signals[exitSignal] ?? 0) : 1);
555
+ settle(ok({
556
+ exitCode,
557
+ truncation: output.truncation,
558
+ ...(output.spillPath === undefined ? {} : { spillPath: output.spillPath }),
559
+ ...(output.lastLineBytes === undefined ? {} : { lastLineBytes: output.lastLineBytes }),
560
+ }));
435
561
  }, (error) => settle(err(new ExecutionError("spawn_error", error.message, error))));
436
562
  });
437
563
  }
438
- async readTextFile(path, abortSignal) {
564
+ async readTextFile(path, context) {
439
565
  const resolved = resolvePath(this.cwd, path);
440
- const aborted = abortResult(abortSignal, resolved);
566
+ const signal = context.abortSignal;
567
+ const aborted = abortResult(signal, resolved);
441
568
  if (aborted)
442
569
  return aborted;
443
570
  try {
444
- return ok(await readFile(resolved, { encoding: "utf8", signal: abortSignal }));
571
+ return ok(await readFile(resolved, { encoding: "utf8", signal }));
445
572
  }
446
573
  catch (error) {
447
574
  return err(toFileError(error, resolved));
448
575
  }
449
576
  }
450
- async readTextLines(path, options) {
577
+ async readTextLines(path, options, context) {
451
578
  const resolved = resolvePath(this.cwd, path);
452
- const aborted = abortResult(options?.abortSignal, resolved);
579
+ const signal = context.abortSignal;
580
+ const aborted = abortResult(signal, resolved);
453
581
  if (aborted)
454
582
  return aborted;
455
583
  if (options?.maxLines !== undefined && options.maxLines <= 0)
@@ -457,18 +585,18 @@ export class NodeExecutionEnv {
457
585
  let stream;
458
586
  let lineReader;
459
587
  try {
460
- stream = createReadStream(resolved, { encoding: "utf8", signal: options?.abortSignal });
588
+ stream = createReadStream(resolved, { encoding: "utf8", signal });
461
589
  lineReader = createInterface({ input: stream, crlfDelay: Infinity });
462
590
  const lines = [];
463
591
  for await (const line of lineReader) {
464
- const loopAbort = abortResult(options?.abortSignal, resolved);
592
+ const loopAbort = abortResult(signal, resolved);
465
593
  if (loopAbort)
466
594
  return loopAbort;
467
595
  lines.push(line);
468
596
  if (options?.maxLines !== undefined && lines.length >= options.maxLines)
469
597
  break;
470
598
  }
471
- const afterReadAbort = abortResult(options?.abortSignal, resolved);
599
+ const afterReadAbort = abortResult(signal, resolved);
472
600
  if (afterReadAbort)
473
601
  return afterReadAbort;
474
602
  return ok(lines);
@@ -481,50 +609,60 @@ export class NodeExecutionEnv {
481
609
  stream?.destroy();
482
610
  }
483
611
  }
484
- async readBinaryFile(path, abortSignal) {
612
+ async readBinaryFile(path, context) {
485
613
  const resolved = resolvePath(this.cwd, path);
486
- const aborted = abortResult(abortSignal, resolved);
614
+ const signal = context.abortSignal;
615
+ const aborted = abortResult(signal, resolved);
487
616
  if (aborted)
488
617
  return aborted;
489
618
  try {
490
- return ok(await readFile(resolved, { signal: abortSignal }));
619
+ return ok(await readFile(resolved, { signal }));
491
620
  }
492
621
  catch (error) {
493
622
  return err(toFileError(error, resolved));
494
623
  }
495
624
  }
496
- async writeFile(path, content, abortSignal) {
625
+ async writeFile(path, content, context) {
497
626
  const resolved = resolvePath(this.cwd, path);
498
- const aborted = abortResult(abortSignal, resolved);
627
+ const signal = context.abortSignal;
628
+ const aborted = abortResult(signal, resolved);
499
629
  if (aborted)
500
630
  return aborted;
501
631
  try {
502
632
  await mkdir(resolve(resolved, ".."), { recursive: true });
503
- const afterMkdirAbort = abortResult(abortSignal, resolved);
633
+ const afterMkdirAbort = abortResult(signal, resolved);
504
634
  if (afterMkdirAbort)
505
635
  return afterMkdirAbort;
506
- await writeFile(resolved, content, { signal: abortSignal });
636
+ await writeFile(resolved, content, { signal });
507
637
  return ok(undefined);
508
638
  }
509
639
  catch (error) {
510
640
  return err(toFileError(error, resolved));
511
641
  }
512
642
  }
513
- async appendFile(path, content) {
643
+ async appendFile(path, content, context) {
514
644
  const resolved = resolvePath(this.cwd, path);
645
+ const signal = context.abortSignal;
646
+ const aborted = abortResult(signal, resolved);
647
+ if (aborted)
648
+ return aborted;
515
649
  try {
516
650
  await mkdir(resolve(resolved, ".."), { recursive: true });
651
+ const afterMkdirAbort = abortResult(signal, resolved);
652
+ if (afterMkdirAbort)
653
+ return afterMkdirAbort;
517
654
  await appendFile(resolved, content);
518
- return ok(undefined);
655
+ const afterAppendAbort = abortResult(signal, resolved);
656
+ return afterAppendAbort ?? ok(undefined);
519
657
  }
520
658
  catch (error) {
521
659
  return err(toFileError(error, resolved));
522
660
  }
523
661
  }
524
- async renameFile(sourcePath, destinationPath, abortSignal) {
662
+ async renameFile(sourcePath, destinationPath, context) {
525
663
  const source = resolvePath(this.cwd, sourcePath);
526
664
  const destination = resolvePath(this.cwd, destinationPath);
527
- const aborted = abortResult(abortSignal, destination);
665
+ const aborted = abortResult(context.abortSignal, destination);
528
666
  if (aborted)
529
667
  return aborted;
530
668
  try {
@@ -535,8 +673,11 @@ export class NodeExecutionEnv {
535
673
  return err(toFileError(error, source));
536
674
  }
537
675
  }
538
- async fileInfo(path) {
676
+ async fileInfo(path, context) {
539
677
  const resolved = resolvePath(this.cwd, path);
678
+ const aborted = abortResult(context.abortSignal, resolved);
679
+ if (aborted)
680
+ return aborted;
540
681
  try {
541
682
  return fileInfoFromStats(resolved, await lstat(resolved));
542
683
  }
@@ -544,16 +685,17 @@ export class NodeExecutionEnv {
544
685
  return err(toFileError(error, resolved));
545
686
  }
546
687
  }
547
- async listDir(path, abortSignal) {
688
+ async listDir(path, context) {
548
689
  const resolved = resolvePath(this.cwd, path);
549
- const aborted = abortResult(abortSignal, resolved);
690
+ const signal = context.abortSignal;
691
+ const aborted = abortResult(signal, resolved);
550
692
  if (aborted)
551
693
  return aborted;
552
694
  try {
553
695
  const entries = await readdir(resolved, { withFileTypes: true });
554
696
  const infos = [];
555
697
  for (const entry of entries) {
556
- const loopAbort = abortResult(abortSignal, resolved);
698
+ const loopAbort = abortResult(signal, resolved);
557
699
  if (loopAbort)
558
700
  return loopAbort;
559
701
  const entryPath = resolve(resolved, entry.name);
@@ -572,8 +714,11 @@ export class NodeExecutionEnv {
572
714
  return err(toFileError(error, resolved));
573
715
  }
574
716
  }
575
- async canonicalPath(path) {
717
+ async canonicalPath(path, context) {
576
718
  const resolved = resolvePath(this.cwd, path);
719
+ const aborted = abortResult(context.abortSignal, resolved);
720
+ if (aborted)
721
+ return aborted;
577
722
  try {
578
723
  return ok(await realpath(resolved));
579
724
  }
@@ -581,16 +726,19 @@ export class NodeExecutionEnv {
581
726
  return err(toFileError(error, resolved));
582
727
  }
583
728
  }
584
- async exists(path) {
585
- const result = await this.fileInfo(path);
729
+ async exists(path, context) {
730
+ const result = await this.fileInfo(path, context);
586
731
  if (result.ok)
587
732
  return ok(true);
588
733
  if (result.error.code === "not_found")
589
734
  return ok(false);
590
735
  return err(result.error);
591
736
  }
592
- async createDir(path, options) {
737
+ async createDir(path, options, context) {
593
738
  const resolved = resolvePath(this.cwd, path);
739
+ const aborted = abortResult(context.abortSignal, resolved);
740
+ if (aborted)
741
+ return aborted;
594
742
  try {
595
743
  await mkdir(resolved, { recursive: options?.recursive ?? true });
596
744
  return ok(undefined);
@@ -599,8 +747,11 @@ export class NodeExecutionEnv {
599
747
  return err(toFileError(error, resolved));
600
748
  }
601
749
  }
602
- async remove(path, options) {
750
+ async remove(path, options, context) {
603
751
  const resolved = resolvePath(this.cwd, path);
752
+ const aborted = abortResult(context.abortSignal, resolved);
753
+ if (aborted)
754
+ return aborted;
604
755
  try {
605
756
  await rm(resolved, { recursive: options?.recursive ?? false, force: options?.force ?? false });
606
757
  return ok(undefined);
@@ -609,16 +760,20 @@ export class NodeExecutionEnv {
609
760
  return err(toFileError(error, resolved));
610
761
  }
611
762
  }
612
- async createTempDir(prefix = "tmp-") {
763
+ async createTempDir(prefix, context) {
764
+ const aborted = abortResult(context.abortSignal);
765
+ if (aborted)
766
+ return aborted;
613
767
  try {
768
+ prefix ??= "tmp-";
614
769
  return ok(await mkdtemp(join(tmpdir(), prefix)));
615
770
  }
616
771
  catch (error) {
617
772
  return err(toFileError(error));
618
773
  }
619
774
  }
620
- async createTempFile(options) {
621
- const dir = await this.createTempDir("tmp-");
775
+ async createTempFile(options, context) {
776
+ const dir = await this.createTempDir("tmp-", context);
622
777
  if (!dir.ok)
623
778
  return dir;
624
779
  const filePath = join(dir.value, `${options?.prefix ?? ""}${randomUUID()}${options?.suffix ?? ""}`);
@@ -630,7 +785,7 @@ export class NodeExecutionEnv {
630
785
  return err(toFileError(error, filePath));
631
786
  }
632
787
  }
633
- async cleanup() {
788
+ async cleanup(_context) {
634
789
  for (const pid of this.activeChildPids)
635
790
  killProcessTree(pid);
636
791
  this.activeChildPids.clear();