@earendil-works/pi-agent-core 0.84.4 → 0.85.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +9 -0
  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 +239 -86
  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 +2 -0
  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);
@@ -226,11 +228,12 @@ function killProcessTree(pid) {
226
228
  }
227
229
  }
228
230
  }
229
- function waitForChildProcess(child) {
231
+ function waitForChildProcess(child, spillIsDraining) {
230
232
  return new Promise((resolvePromise, reject) => {
231
233
  let settled = false;
232
234
  let exited = false;
233
235
  let exitCode = null;
236
+ let exitSignal = null;
234
237
  let postExitTimer;
235
238
  let stdoutEnded = child.stdout === null;
236
239
  let stderrEnded = child.stderr === null;
@@ -245,23 +248,28 @@ function waitForChildProcess(child) {
245
248
  child.stdout?.removeListener("data", onData);
246
249
  child.stderr?.removeListener("data", onData);
247
250
  };
248
- const finalize = (code) => {
251
+ const finalize = () => {
249
252
  if (settled)
250
253
  return;
251
254
  settled = true;
252
255
  cleanup();
253
256
  child.stdout?.destroy();
254
257
  child.stderr?.destroy();
255
- resolvePromise(code);
258
+ resolvePromise({ code: exitCode, signal: exitSignal });
256
259
  };
257
260
  const maybeFinalizeAfterExit = () => {
258
261
  if (exited && stdoutEnded && stderrEnded)
259
- finalize(exitCode);
262
+ finalize();
260
263
  };
261
264
  const armIdleTimer = () => {
262
265
  if (postExitTimer)
263
266
  clearTimeout(postExitTimer);
264
- 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);
265
273
  };
266
274
  const onData = () => {
267
275
  if (exited && !settled)
@@ -282,14 +290,19 @@ function waitForChildProcess(child) {
282
290
  cleanup();
283
291
  reject(error);
284
292
  };
285
- const onExit = (code) => {
293
+ const onExit = (code, signal) => {
286
294
  exited = true;
287
295
  exitCode = code;
296
+ exitSignal = signal;
288
297
  maybeFinalizeAfterExit();
289
298
  if (!settled)
290
299
  armIdleTimer();
291
300
  };
292
- const onClose = (code) => finalize(code);
301
+ const onClose = (code, signal) => {
302
+ exitCode = code;
303
+ exitSignal = signal;
304
+ finalize();
305
+ };
293
306
  child.stdout?.once("end", onStdoutEnd);
294
307
  child.stderr?.once("end", onStderrEnd);
295
308
  child.stdout?.on("data", onData);
@@ -309,14 +322,15 @@ export class NodeExecutionEnv {
309
322
  this.shellPath = options.shellPath;
310
323
  this.shellEnv = options.shellEnv;
311
324
  }
312
- async absolutePath(path) {
325
+ async absolutePath(path, _context) {
313
326
  return ok(resolvePath(this.cwd, path));
314
327
  }
315
- async joinPath(parts) {
328
+ async joinPath(parts, _context) {
316
329
  return ok(join(...parts));
317
330
  }
318
- async exec(command, options) {
319
- if (options?.abortSignal?.aborted)
331
+ async exec(command, options, context) {
332
+ const signal = context.abortSignal;
333
+ if (signal?.aborted)
320
334
  return err(new ExecutionError("aborted", "aborted"));
321
335
  const timeoutMsResult = resolveTimeoutMs(options?.timeout);
322
336
  if (!timeoutMsResult.ok)
@@ -334,30 +348,119 @@ export class NodeExecutionEnv {
334
348
  return err(new ExecutionError("spawn_error", `Working directory does not exist: ${cwd}\nCannot execute bash commands.`, cause));
335
349
  }
336
350
  return await new Promise((resolvePromise) => {
337
- let stdout = "";
338
- let stderr = "";
339
351
  let settled = false;
340
352
  let timedOut = false;
341
353
  let callbackError;
354
+ let spillError;
342
355
  let child;
343
356
  let timeoutId;
357
+ const spillPrefix = [];
358
+ let spillPath;
359
+ const spillQueue = [];
360
+ let spillStart;
361
+ let spillStream;
362
+ let spillBackpressured = false;
344
363
  const onAbort = () => {
345
- if (child?.pid) {
364
+ if (child?.pid)
346
365
  killProcessTree(child.pid);
347
- }
348
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
+ }
349
386
  const settle = (result) => {
387
+ if (settled)
388
+ return;
389
+ settled = true;
350
390
  if (timeoutId)
351
391
  clearTimeout(timeoutId);
352
- if (options?.abortSignal)
353
- options.abortSignal.removeEventListener("abort", onAbort);
392
+ if (signal)
393
+ signal.removeEventListener("abort", onAbort);
354
394
  if (child?.pid)
355
395
  this.activeChildPids.delete(child.pid);
356
- if (settled)
357
- return;
358
- settled = true;
396
+ capture.dispose();
359
397
  resolvePromise(result);
360
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
+ };
361
464
  try {
362
465
  const commandFromStdin = shellConfig.value.commandTransport === "stdin";
363
466
  child = spawn(shellConfig.value.shell, commandFromStdin ? shellConfig.value.args : [...shellConfig.value.args, command], {
@@ -380,47 +483,54 @@ export class NodeExecutionEnv {
380
483
  return;
381
484
  }
382
485
  timeoutId =
383
- timeoutMs !== undefined
384
- ? setTimeout(() => {
486
+ timeoutMs === undefined
487
+ ? undefined
488
+ : setTimeout(() => {
385
489
  timedOut = true;
386
- if (child?.pid) {
387
- killProcessTree(child.pid);
388
- }
389
- }, timeoutMs)
390
- : undefined;
391
- if (options?.abortSignal) {
392
- if (options.abortSignal.aborted) {
490
+ onAbort();
491
+ }, timeoutMs);
492
+ if (signal) {
493
+ if (signal.aborted)
393
494
  onAbort();
394
- }
395
- else {
396
- options.abortSignal.addEventListener("abort", onAbort, { once: true });
397
- }
495
+ else
496
+ signal.addEventListener("abort", onAbort, { once: true });
398
497
  }
399
- child.stdout?.setEncoding("utf8");
400
- child.stderr?.setEncoding("utf8");
401
- child.stdout?.on("data", (chunk) => {
402
- stdout += chunk;
498
+ const feed = (chunk) => {
403
499
  try {
404
- 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
+ }
405
516
  }
406
517
  catch (error) {
407
- const cause = toError(error);
408
- callbackError = new ExecutionError("callback_error", cause.message, cause);
409
- onAbort();
518
+ failCallback(error);
410
519
  }
411
- });
412
- child.stderr?.on("data", (chunk) => {
413
- 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();
414
527
  try {
415
- options?.onStderr?.(chunk);
528
+ capture.finish();
529
+ capture.flush();
416
530
  }
417
531
  catch (error) {
418
- const cause = toError(error);
419
- callbackError = new ExecutionError("callback_error", cause.message, cause);
420
- onAbort();
532
+ failCallback(error);
421
533
  }
422
- });
423
- void waitForChildProcess(child).then((code) => {
424
534
  if (callbackError) {
425
535
  settle(err(callbackError));
426
536
  return;
@@ -429,29 +539,45 @@ export class NodeExecutionEnv {
429
539
  settle(err(new ExecutionError("timeout", `timeout:${options?.timeout}`)));
430
540
  return;
431
541
  }
432
- if (options?.abortSignal?.aborted) {
542
+ if (signal?.aborted) {
433
543
  settle(err(new ExecutionError("aborted", "aborted")));
434
544
  return;
435
545
  }
436
- 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
+ }));
437
561
  }, (error) => settle(err(new ExecutionError("spawn_error", error.message, error))));
438
562
  });
439
563
  }
440
- async readTextFile(path, abortSignal) {
564
+ async readTextFile(path, context) {
441
565
  const resolved = resolvePath(this.cwd, path);
442
- const aborted = abortResult(abortSignal, resolved);
566
+ const signal = context.abortSignal;
567
+ const aborted = abortResult(signal, resolved);
443
568
  if (aborted)
444
569
  return aborted;
445
570
  try {
446
- return ok(await readFile(resolved, { encoding: "utf8", signal: abortSignal }));
571
+ return ok(await readFile(resolved, { encoding: "utf8", signal }));
447
572
  }
448
573
  catch (error) {
449
574
  return err(toFileError(error, resolved));
450
575
  }
451
576
  }
452
- async readTextLines(path, options) {
577
+ async readTextLines(path, options, context) {
453
578
  const resolved = resolvePath(this.cwd, path);
454
- const aborted = abortResult(options?.abortSignal, resolved);
579
+ const signal = context.abortSignal;
580
+ const aborted = abortResult(signal, resolved);
455
581
  if (aborted)
456
582
  return aborted;
457
583
  if (options?.maxLines !== undefined && options.maxLines <= 0)
@@ -459,18 +585,18 @@ export class NodeExecutionEnv {
459
585
  let stream;
460
586
  let lineReader;
461
587
  try {
462
- stream = createReadStream(resolved, { encoding: "utf8", signal: options?.abortSignal });
588
+ stream = createReadStream(resolved, { encoding: "utf8", signal });
463
589
  lineReader = createInterface({ input: stream, crlfDelay: Infinity });
464
590
  const lines = [];
465
591
  for await (const line of lineReader) {
466
- const loopAbort = abortResult(options?.abortSignal, resolved);
592
+ const loopAbort = abortResult(signal, resolved);
467
593
  if (loopAbort)
468
594
  return loopAbort;
469
595
  lines.push(line);
470
596
  if (options?.maxLines !== undefined && lines.length >= options.maxLines)
471
597
  break;
472
598
  }
473
- const afterReadAbort = abortResult(options?.abortSignal, resolved);
599
+ const afterReadAbort = abortResult(signal, resolved);
474
600
  if (afterReadAbort)
475
601
  return afterReadAbort;
476
602
  return ok(lines);
@@ -483,50 +609,60 @@ export class NodeExecutionEnv {
483
609
  stream?.destroy();
484
610
  }
485
611
  }
486
- async readBinaryFile(path, abortSignal) {
612
+ async readBinaryFile(path, context) {
487
613
  const resolved = resolvePath(this.cwd, path);
488
- const aborted = abortResult(abortSignal, resolved);
614
+ const signal = context.abortSignal;
615
+ const aborted = abortResult(signal, resolved);
489
616
  if (aborted)
490
617
  return aborted;
491
618
  try {
492
- return ok(await readFile(resolved, { signal: abortSignal }));
619
+ return ok(await readFile(resolved, { signal }));
493
620
  }
494
621
  catch (error) {
495
622
  return err(toFileError(error, resolved));
496
623
  }
497
624
  }
498
- async writeFile(path, content, abortSignal) {
625
+ async writeFile(path, content, context) {
499
626
  const resolved = resolvePath(this.cwd, path);
500
- const aborted = abortResult(abortSignal, resolved);
627
+ const signal = context.abortSignal;
628
+ const aborted = abortResult(signal, resolved);
501
629
  if (aborted)
502
630
  return aborted;
503
631
  try {
504
632
  await mkdir(resolve(resolved, ".."), { recursive: true });
505
- const afterMkdirAbort = abortResult(abortSignal, resolved);
633
+ const afterMkdirAbort = abortResult(signal, resolved);
506
634
  if (afterMkdirAbort)
507
635
  return afterMkdirAbort;
508
- await writeFile(resolved, content, { signal: abortSignal });
636
+ await writeFile(resolved, content, { signal });
509
637
  return ok(undefined);
510
638
  }
511
639
  catch (error) {
512
640
  return err(toFileError(error, resolved));
513
641
  }
514
642
  }
515
- async appendFile(path, content) {
643
+ async appendFile(path, content, context) {
516
644
  const resolved = resolvePath(this.cwd, path);
645
+ const signal = context.abortSignal;
646
+ const aborted = abortResult(signal, resolved);
647
+ if (aborted)
648
+ return aborted;
517
649
  try {
518
650
  await mkdir(resolve(resolved, ".."), { recursive: true });
651
+ const afterMkdirAbort = abortResult(signal, resolved);
652
+ if (afterMkdirAbort)
653
+ return afterMkdirAbort;
519
654
  await appendFile(resolved, content);
520
- return ok(undefined);
655
+ const afterAppendAbort = abortResult(signal, resolved);
656
+ return afterAppendAbort ?? ok(undefined);
521
657
  }
522
658
  catch (error) {
523
659
  return err(toFileError(error, resolved));
524
660
  }
525
661
  }
526
- async renameFile(sourcePath, destinationPath, abortSignal) {
662
+ async renameFile(sourcePath, destinationPath, context) {
527
663
  const source = resolvePath(this.cwd, sourcePath);
528
664
  const destination = resolvePath(this.cwd, destinationPath);
529
- const aborted = abortResult(abortSignal, destination);
665
+ const aborted = abortResult(context.abortSignal, destination);
530
666
  if (aborted)
531
667
  return aborted;
532
668
  try {
@@ -537,8 +673,11 @@ export class NodeExecutionEnv {
537
673
  return err(toFileError(error, source));
538
674
  }
539
675
  }
540
- async fileInfo(path) {
676
+ async fileInfo(path, context) {
541
677
  const resolved = resolvePath(this.cwd, path);
678
+ const aborted = abortResult(context.abortSignal, resolved);
679
+ if (aborted)
680
+ return aborted;
542
681
  try {
543
682
  return fileInfoFromStats(resolved, await lstat(resolved));
544
683
  }
@@ -546,16 +685,17 @@ export class NodeExecutionEnv {
546
685
  return err(toFileError(error, resolved));
547
686
  }
548
687
  }
549
- async listDir(path, abortSignal) {
688
+ async listDir(path, context) {
550
689
  const resolved = resolvePath(this.cwd, path);
551
- const aborted = abortResult(abortSignal, resolved);
690
+ const signal = context.abortSignal;
691
+ const aborted = abortResult(signal, resolved);
552
692
  if (aborted)
553
693
  return aborted;
554
694
  try {
555
695
  const entries = await readdir(resolved, { withFileTypes: true });
556
696
  const infos = [];
557
697
  for (const entry of entries) {
558
- const loopAbort = abortResult(abortSignal, resolved);
698
+ const loopAbort = abortResult(signal, resolved);
559
699
  if (loopAbort)
560
700
  return loopAbort;
561
701
  const entryPath = resolve(resolved, entry.name);
@@ -574,8 +714,11 @@ export class NodeExecutionEnv {
574
714
  return err(toFileError(error, resolved));
575
715
  }
576
716
  }
577
- async canonicalPath(path) {
717
+ async canonicalPath(path, context) {
578
718
  const resolved = resolvePath(this.cwd, path);
719
+ const aborted = abortResult(context.abortSignal, resolved);
720
+ if (aborted)
721
+ return aborted;
579
722
  try {
580
723
  return ok(await realpath(resolved));
581
724
  }
@@ -583,16 +726,19 @@ export class NodeExecutionEnv {
583
726
  return err(toFileError(error, resolved));
584
727
  }
585
728
  }
586
- async exists(path) {
587
- const result = await this.fileInfo(path);
729
+ async exists(path, context) {
730
+ const result = await this.fileInfo(path, context);
588
731
  if (result.ok)
589
732
  return ok(true);
590
733
  if (result.error.code === "not_found")
591
734
  return ok(false);
592
735
  return err(result.error);
593
736
  }
594
- async createDir(path, options) {
737
+ async createDir(path, options, context) {
595
738
  const resolved = resolvePath(this.cwd, path);
739
+ const aborted = abortResult(context.abortSignal, resolved);
740
+ if (aborted)
741
+ return aborted;
596
742
  try {
597
743
  await mkdir(resolved, { recursive: options?.recursive ?? true });
598
744
  return ok(undefined);
@@ -601,8 +747,11 @@ export class NodeExecutionEnv {
601
747
  return err(toFileError(error, resolved));
602
748
  }
603
749
  }
604
- async remove(path, options) {
750
+ async remove(path, options, context) {
605
751
  const resolved = resolvePath(this.cwd, path);
752
+ const aborted = abortResult(context.abortSignal, resolved);
753
+ if (aborted)
754
+ return aborted;
606
755
  try {
607
756
  await rm(resolved, { recursive: options?.recursive ?? false, force: options?.force ?? false });
608
757
  return ok(undefined);
@@ -611,16 +760,20 @@ export class NodeExecutionEnv {
611
760
  return err(toFileError(error, resolved));
612
761
  }
613
762
  }
614
- async createTempDir(prefix = "tmp-") {
763
+ async createTempDir(prefix, context) {
764
+ const aborted = abortResult(context.abortSignal);
765
+ if (aborted)
766
+ return aborted;
615
767
  try {
768
+ prefix ??= "tmp-";
616
769
  return ok(await mkdtemp(join(tmpdir(), prefix)));
617
770
  }
618
771
  catch (error) {
619
772
  return err(toFileError(error));
620
773
  }
621
774
  }
622
- async createTempFile(options) {
623
- const dir = await this.createTempDir("tmp-");
775
+ async createTempFile(options, context) {
776
+ const dir = await this.createTempDir("tmp-", context);
624
777
  if (!dir.ok)
625
778
  return dir;
626
779
  const filePath = join(dir.value, `${options?.prefix ?? ""}${randomUUID()}${options?.suffix ?? ""}`);
@@ -632,7 +785,7 @@ export class NodeExecutionEnv {
632
785
  return err(toFileError(error, filePath));
633
786
  }
634
787
  }
635
- async cleanup() {
788
+ async cleanup(_context) {
636
789
  for (const pid of this.activeChildPids)
637
790
  killProcessTree(pid);
638
791
  this.activeChildPids.clear();