@tickernelz/paperclip-pro-adapter-utils 2026.925.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 (1826) hide show
  1. package/README.md +37 -0
  2. package/dist/acpx-engine/cli.d.ts +2 -0
  3. package/dist/acpx-engine/cli.d.ts.map +1 -0
  4. package/dist/acpx-engine/cli.js +128 -0
  5. package/dist/acpx-engine/cli.js.map +1 -0
  6. package/dist/acpx-engine/composed-run-characterization.test.d.ts +2 -0
  7. package/dist/acpx-engine/composed-run-characterization.test.d.ts.map +1 -0
  8. package/dist/acpx-engine/composed-run-characterization.test.js +739 -0
  9. package/dist/acpx-engine/composed-run-characterization.test.js.map +1 -0
  10. package/dist/acpx-engine/constants.d.ts +20 -0
  11. package/dist/acpx-engine/constants.d.ts.map +1 -0
  12. package/dist/acpx-engine/constants.js +37 -0
  13. package/dist/acpx-engine/constants.js.map +1 -0
  14. package/dist/acpx-engine/execute-identity.test.d.ts +2 -0
  15. package/dist/acpx-engine/execute-identity.test.d.ts.map +1 -0
  16. package/dist/acpx-engine/execute-identity.test.js +218 -0
  17. package/dist/acpx-engine/execute-identity.test.js.map +1 -0
  18. package/dist/acpx-engine/execute.d.ts +325 -0
  19. package/dist/acpx-engine/execute.d.ts.map +1 -0
  20. package/dist/acpx-engine/execute.js +4448 -0
  21. package/dist/acpx-engine/execute.js.map +1 -0
  22. package/dist/acpx-engine/execute.test.d.ts +2 -0
  23. package/dist/acpx-engine/execute.test.d.ts.map +1 -0
  24. package/dist/acpx-engine/execute.test.js +6694 -0
  25. package/dist/acpx-engine/execute.test.js.map +1 -0
  26. package/dist/acpx-engine/index.d.ts +6 -0
  27. package/dist/acpx-engine/index.d.ts.map +1 -0
  28. package/dist/acpx-engine/index.js +6 -0
  29. package/dist/acpx-engine/index.js.map +1 -0
  30. package/dist/acpx-engine/local-process-control.d.ts +5 -0
  31. package/dist/acpx-engine/local-process-control.d.ts.map +1 -0
  32. package/dist/acpx-engine/local-process-control.js +39 -0
  33. package/dist/acpx-engine/local-process-control.js.map +1 -0
  34. package/dist/acpx-engine/local-process-control.test.d.ts +2 -0
  35. package/dist/acpx-engine/local-process-control.test.d.ts.map +1 -0
  36. package/dist/acpx-engine/local-process-control.test.js +38 -0
  37. package/dist/acpx-engine/local-process-control.test.js.map +1 -0
  38. package/dist/acpx-engine/operator-stop.test.d.ts +2 -0
  39. package/dist/acpx-engine/operator-stop.test.d.ts.map +1 -0
  40. package/dist/acpx-engine/operator-stop.test.js +107 -0
  41. package/dist/acpx-engine/operator-stop.test.js.map +1 -0
  42. package/dist/acpx-engine/remote-spawn-smoke.test.d.ts +2 -0
  43. package/dist/acpx-engine/remote-spawn-smoke.test.d.ts.map +1 -0
  44. package/dist/acpx-engine/remote-spawn-smoke.test.js +151 -0
  45. package/dist/acpx-engine/remote-spawn-smoke.test.js.map +1 -0
  46. package/dist/acpx-engine/run-contracts.d.ts +413 -0
  47. package/dist/acpx-engine/run-contracts.d.ts.map +1 -0
  48. package/dist/acpx-engine/run-contracts.js +20 -0
  49. package/dist/acpx-engine/run-contracts.js.map +1 -0
  50. package/dist/acpx-engine/run-contracts.test.d.ts +2 -0
  51. package/dist/acpx-engine/run-contracts.test.d.ts.map +1 -0
  52. package/dist/acpx-engine/run-contracts.test.js +57 -0
  53. package/dist/acpx-engine/run-contracts.test.js.map +1 -0
  54. package/dist/acpx-engine/run-coordinator.d.ts +57 -0
  55. package/dist/acpx-engine/run-coordinator.d.ts.map +1 -0
  56. package/dist/acpx-engine/run-coordinator.js +63 -0
  57. package/dist/acpx-engine/run-coordinator.js.map +1 -0
  58. package/dist/acpx-engine/run-coordinator.test.d.ts +2 -0
  59. package/dist/acpx-engine/run-coordinator.test.d.ts.map +1 -0
  60. package/dist/acpx-engine/run-coordinator.test.js +160 -0
  61. package/dist/acpx-engine/run-coordinator.test.js.map +1 -0
  62. package/dist/acpx-engine/run-fault-matrix.test.d.ts +2 -0
  63. package/dist/acpx-engine/run-fault-matrix.test.d.ts.map +1 -0
  64. package/dist/acpx-engine/run-fault-matrix.test.js +829 -0
  65. package/dist/acpx-engine/run-fault-matrix.test.js.map +1 -0
  66. package/dist/acpx-engine/run-resource-ledger.d.ts +30 -0
  67. package/dist/acpx-engine/run-resource-ledger.d.ts.map +1 -0
  68. package/dist/acpx-engine/run-resource-ledger.js +148 -0
  69. package/dist/acpx-engine/run-resource-ledger.js.map +1 -0
  70. package/dist/acpx-engine/run-resource-ledger.test.d.ts +2 -0
  71. package/dist/acpx-engine/run-resource-ledger.test.d.ts.map +1 -0
  72. package/dist/acpx-engine/run-resource-ledger.test.js +154 -0
  73. package/dist/acpx-engine/run-resource-ledger.test.js.map +1 -0
  74. package/dist/acpx-engine/run-site-host.d.ts +73 -0
  75. package/dist/acpx-engine/run-site-host.d.ts.map +1 -0
  76. package/dist/acpx-engine/run-site-host.js +65 -0
  77. package/dist/acpx-engine/run-site-host.js.map +1 -0
  78. package/dist/acpx-engine/run-site-host.test.d.ts +2 -0
  79. package/dist/acpx-engine/run-site-host.test.d.ts.map +1 -0
  80. package/dist/acpx-engine/run-site-host.test.js +98 -0
  81. package/dist/acpx-engine/run-site-host.test.js.map +1 -0
  82. package/dist/acpx-engine/run-site-sandbox.d.ts +142 -0
  83. package/dist/acpx-engine/run-site-sandbox.d.ts.map +1 -0
  84. package/dist/acpx-engine/run-site-sandbox.js +278 -0
  85. package/dist/acpx-engine/run-site-sandbox.js.map +1 -0
  86. package/dist/acpx-engine/run-site-sandbox.test.d.ts +2 -0
  87. package/dist/acpx-engine/run-site-sandbox.test.d.ts.map +1 -0
  88. package/dist/acpx-engine/run-site-sandbox.test.js +275 -0
  89. package/dist/acpx-engine/run-site-sandbox.test.js.map +1 -0
  90. package/dist/acpx-engine/session-codec.d.ts +3 -0
  91. package/dist/acpx-engine/session-codec.d.ts.map +1 -0
  92. package/dist/acpx-engine/session-codec.js +49 -0
  93. package/dist/acpx-engine/session-codec.js.map +1 -0
  94. package/dist/acpx-engine/session-reuse-store.d.ts +62 -0
  95. package/dist/acpx-engine/session-reuse-store.d.ts.map +1 -0
  96. package/dist/acpx-engine/session-reuse-store.js +134 -0
  97. package/dist/acpx-engine/session-reuse-store.js.map +1 -0
  98. package/dist/acpx-engine/session-reuse-store.test.d.ts +2 -0
  99. package/dist/acpx-engine/session-reuse-store.test.d.ts.map +1 -0
  100. package/dist/acpx-engine/session-reuse-store.test.js +134 -0
  101. package/dist/acpx-engine/session-reuse-store.test.js.map +1 -0
  102. package/dist/acpx-engine/settlement-characterization.test.d.ts +2 -0
  103. package/dist/acpx-engine/settlement-characterization.test.d.ts.map +1 -0
  104. package/dist/acpx-engine/settlement-characterization.test.js +880 -0
  105. package/dist/acpx-engine/settlement-characterization.test.js.map +1 -0
  106. package/dist/acpx-engine/settlement-sequence.d.ts +77 -0
  107. package/dist/acpx-engine/settlement-sequence.d.ts.map +1 -0
  108. package/dist/acpx-engine/settlement-sequence.js +119 -0
  109. package/dist/acpx-engine/settlement-sequence.js.map +1 -0
  110. package/dist/acpx-engine/settlement-sequence.test.d.ts +2 -0
  111. package/dist/acpx-engine/settlement-sequence.test.d.ts.map +1 -0
  112. package/dist/acpx-engine/settlement-sequence.test.js +272 -0
  113. package/dist/acpx-engine/settlement-sequence.test.js.map +1 -0
  114. package/dist/acpx-engine/spawn-smoke.test.d.ts +2 -0
  115. package/dist/acpx-engine/spawn-smoke.test.d.ts.map +1 -0
  116. package/dist/acpx-engine/spawn-smoke.test.js +184 -0
  117. package/dist/acpx-engine/spawn-smoke.test.js.map +1 -0
  118. package/dist/acpx-engine/startup-cancellation.d.ts +8 -0
  119. package/dist/acpx-engine/startup-cancellation.d.ts.map +1 -0
  120. package/dist/acpx-engine/startup-cancellation.js +92 -0
  121. package/dist/acpx-engine/startup-cancellation.js.map +1 -0
  122. package/dist/acpx-engine/startup-cancellation.test.d.ts +2 -0
  123. package/dist/acpx-engine/startup-cancellation.test.d.ts.map +1 -0
  124. package/dist/acpx-engine/startup-cancellation.test.js +115 -0
  125. package/dist/acpx-engine/startup-cancellation.test.js.map +1 -0
  126. package/dist/acpx-engine/startup-characterization.test.d.ts +2 -0
  127. package/dist/acpx-engine/startup-characterization.test.d.ts.map +1 -0
  128. package/dist/acpx-engine/startup-characterization.test.js +742 -0
  129. package/dist/acpx-engine/startup-characterization.test.js.map +1 -0
  130. package/dist/acpx-engine/startup-timing.d.ts +419 -0
  131. package/dist/acpx-engine/startup-timing.d.ts.map +1 -0
  132. package/dist/acpx-engine/startup-timing.js +616 -0
  133. package/dist/acpx-engine/startup-timing.js.map +1 -0
  134. package/dist/acpx-engine/startup-timing.test.d.ts +2 -0
  135. package/dist/acpx-engine/startup-timing.test.d.ts.map +1 -0
  136. package/dist/acpx-engine/startup-timing.test.js +619 -0
  137. package/dist/acpx-engine/startup-timing.test.js.map +1 -0
  138. package/dist/acpx-engine/turn-characterization.test.d.ts +2 -0
  139. package/dist/acpx-engine/turn-characterization.test.d.ts.map +1 -0
  140. package/dist/acpx-engine/turn-characterization.test.js +566 -0
  141. package/dist/acpx-engine/turn-characterization.test.js.map +1 -0
  142. package/dist/acpx-engine/turn-sequence.d.ts +54 -0
  143. package/dist/acpx-engine/turn-sequence.d.ts.map +1 -0
  144. package/dist/acpx-engine/turn-sequence.js +75 -0
  145. package/dist/acpx-engine/turn-sequence.js.map +1 -0
  146. package/dist/acpx-engine/turn-sequence.test.d.ts +2 -0
  147. package/dist/acpx-engine/turn-sequence.test.d.ts.map +1 -0
  148. package/dist/acpx-engine/turn-sequence.test.js +177 -0
  149. package/dist/acpx-engine/turn-sequence.test.js.map +1 -0
  150. package/dist/acpx-engine/ui.d.ts +3 -0
  151. package/dist/acpx-engine/ui.d.ts.map +1 -0
  152. package/dist/acpx-engine/ui.js +162 -0
  153. package/dist/acpx-engine/ui.js.map +1 -0
  154. package/dist/billing.d.ts +2 -0
  155. package/dist/billing.d.ts.map +1 -0
  156. package/dist/billing.js +16 -0
  157. package/dist/billing.js.map +1 -0
  158. package/dist/billing.test.d.ts +2 -0
  159. package/dist/billing.test.d.ts.map +1 -0
  160. package/dist/billing.test.js +14 -0
  161. package/dist/billing.test.js.map +1 -0
  162. package/dist/bridge-transport-contract.d.ts +43 -0
  163. package/dist/bridge-transport-contract.d.ts.map +1 -0
  164. package/dist/bridge-transport-contract.js +33 -0
  165. package/dist/bridge-transport-contract.js.map +1 -0
  166. package/dist/chat-file-delivery.d.ts +10 -0
  167. package/dist/chat-file-delivery.d.ts.map +1 -0
  168. package/dist/chat-file-delivery.js +38 -0
  169. package/dist/chat-file-delivery.js.map +1 -0
  170. package/dist/chat-file-delivery.test.d.ts +2 -0
  171. package/dist/chat-file-delivery.test.d.ts.map +1 -0
  172. package/dist/chat-file-delivery.test.js +92 -0
  173. package/dist/chat-file-delivery.test.js.map +1 -0
  174. package/dist/command-managed-runtime.d.ts +162 -0
  175. package/dist/command-managed-runtime.d.ts.map +1 -0
  176. package/dist/command-managed-runtime.js +456 -0
  177. package/dist/command-managed-runtime.js.map +1 -0
  178. package/dist/command-managed-runtime.test.d.ts +2 -0
  179. package/dist/command-managed-runtime.test.d.ts.map +1 -0
  180. package/dist/command-managed-runtime.test.js +908 -0
  181. package/dist/command-managed-runtime.test.js.map +1 -0
  182. package/dist/command-redaction.d.ts +15 -0
  183. package/dist/command-redaction.d.ts.map +1 -0
  184. package/dist/command-redaction.js +97 -0
  185. package/dist/command-redaction.js.map +1 -0
  186. package/dist/command-redaction.test.d.ts +2 -0
  187. package/dist/command-redaction.test.d.ts.map +1 -0
  188. package/dist/command-redaction.test.js +80 -0
  189. package/dist/command-redaction.test.js.map +1 -0
  190. package/dist/duplex-frame-codec.d.ts +124 -0
  191. package/dist/duplex-frame-codec.d.ts.map +1 -0
  192. package/dist/duplex-frame-codec.js +134 -0
  193. package/dist/duplex-frame-codec.js.map +1 -0
  194. package/dist/duplex-frame-codec.test.d.ts +2 -0
  195. package/dist/duplex-frame-codec.test.d.ts.map +1 -0
  196. package/dist/duplex-frame-codec.test.js +172 -0
  197. package/dist/duplex-frame-codec.test.js.map +1 -0
  198. package/dist/duplex-observability.d.ts +220 -0
  199. package/dist/duplex-observability.d.ts.map +1 -0
  200. package/dist/duplex-observability.js +248 -0
  201. package/dist/duplex-observability.js.map +1 -0
  202. package/dist/duplex-observability.test.d.ts +2 -0
  203. package/dist/duplex-observability.test.d.ts.map +1 -0
  204. package/dist/duplex-observability.test.js +55 -0
  205. package/dist/duplex-observability.test.js.map +1 -0
  206. package/dist/env-bindings.d.ts +17 -0
  207. package/dist/env-bindings.d.ts.map +1 -0
  208. package/dist/env-bindings.js +98 -0
  209. package/dist/env-bindings.js.map +1 -0
  210. package/dist/env-bindings.test.d.ts +2 -0
  211. package/dist/env-bindings.test.d.ts.map +1 -0
  212. package/dist/env-bindings.test.js +66 -0
  213. package/dist/env-bindings.test.js.map +1 -0
  214. package/dist/exclude-patterns.d.ts +4 -0
  215. package/dist/exclude-patterns.d.ts.map +1 -0
  216. package/dist/exclude-patterns.js +26 -0
  217. package/dist/exclude-patterns.js.map +1 -0
  218. package/dist/execution-target-sandbox.test.d.ts +2 -0
  219. package/dist/execution-target-sandbox.test.d.ts.map +1 -0
  220. package/dist/execution-target-sandbox.test.js +6522 -0
  221. package/dist/execution-target-sandbox.test.js.map +1 -0
  222. package/dist/execution-target-stdin-race.test.d.ts +2 -0
  223. package/dist/execution-target-stdin-race.test.d.ts.map +1 -0
  224. package/dist/execution-target-stdin-race.test.js +1842 -0
  225. package/dist/execution-target-stdin-race.test.js.map +1 -0
  226. package/dist/execution-target.d.ts +537 -0
  227. package/dist/execution-target.d.ts.map +1 -0
  228. package/dist/execution-target.js +3877 -0
  229. package/dist/execution-target.js.map +1 -0
  230. package/dist/execution-target.test.d.ts +2 -0
  231. package/dist/execution-target.test.d.ts.map +1 -0
  232. package/dist/execution-target.test.js +346 -0
  233. package/dist/execution-target.test.js.map +1 -0
  234. package/dist/git-workspace-sync.d.ts +193 -0
  235. package/dist/git-workspace-sync.d.ts.map +1 -0
  236. package/dist/git-workspace-sync.js +786 -0
  237. package/dist/git-workspace-sync.js.map +1 -0
  238. package/dist/git-workspace-sync.test.d.ts +2 -0
  239. package/dist/git-workspace-sync.test.d.ts.map +1 -0
  240. package/dist/git-workspace-sync.test.js +830 -0
  241. package/dist/git-workspace-sync.test.js.map +1 -0
  242. package/dist/github-launcher-environment.test.d.ts +2 -0
  243. package/dist/github-launcher-environment.test.d.ts.map +1 -0
  244. package/dist/github-launcher-environment.test.js +285 -0
  245. package/dist/github-launcher-environment.test.js.map +1 -0
  246. package/dist/github-launcher.d.ts +8 -0
  247. package/dist/github-launcher.d.ts.map +1 -0
  248. package/dist/github-launcher.js +127 -0
  249. package/dist/github-launcher.js.map +1 -0
  250. package/dist/github-launcher.test.d.ts +2 -0
  251. package/dist/github-launcher.test.d.ts.map +1 -0
  252. package/dist/github-launcher.test.js +166 -0
  253. package/dist/github-launcher.test.js.map +1 -0
  254. package/dist/http2-bridge-server.d.ts +465 -0
  255. package/dist/http2-bridge-server.d.ts.map +1 -0
  256. package/dist/http2-bridge-server.js +1087 -0
  257. package/dist/http2-bridge-server.js.map +1 -0
  258. package/dist/http2-bridge-server.test.d.ts +2 -0
  259. package/dist/http2-bridge-server.test.d.ts.map +1 -0
  260. package/dist/http2-bridge-server.test.js +2129 -0
  261. package/dist/http2-bridge-server.test.js.map +1 -0
  262. package/dist/index.d.ts +20 -0
  263. package/dist/index.d.ts.map +1 -0
  264. package/dist/index.js +12 -0
  265. package/dist/index.js.map +1 -0
  266. package/dist/local-process-sandbox.d.ts +41 -0
  267. package/dist/local-process-sandbox.d.ts.map +1 -0
  268. package/dist/local-process-sandbox.js +432 -0
  269. package/dist/local-process-sandbox.js.map +1 -0
  270. package/dist/local-process-sandbox.test.d.ts +2 -0
  271. package/dist/local-process-sandbox.test.d.ts.map +1 -0
  272. package/dist/local-process-sandbox.test.js +399 -0
  273. package/dist/local-process-sandbox.test.js.map +1 -0
  274. package/dist/log-redaction.d.ts +9 -0
  275. package/dist/log-redaction.d.ts.map +1 -0
  276. package/dist/log-redaction.js +88 -0
  277. package/dist/log-redaction.js.map +1 -0
  278. package/dist/login-capability.d.ts +88 -0
  279. package/dist/login-capability.d.ts.map +1 -0
  280. package/dist/login-capability.js +78 -0
  281. package/dist/login-capability.js.map +1 -0
  282. package/dist/login-capability.test.d.ts +2 -0
  283. package/dist/login-capability.test.d.ts.map +1 -0
  284. package/dist/login-capability.test.js +78 -0
  285. package/dist/login-capability.test.js.map +1 -0
  286. package/dist/login-pty-transport.d.ts +80 -0
  287. package/dist/login-pty-transport.d.ts.map +1 -0
  288. package/dist/login-pty-transport.js +75 -0
  289. package/dist/login-pty-transport.js.map +1 -0
  290. package/dist/login-pty-transport.test.d.ts +2 -0
  291. package/dist/login-pty-transport.test.d.ts.map +1 -0
  292. package/dist/login-pty-transport.test.js +185 -0
  293. package/dist/login-pty-transport.test.js.map +1 -0
  294. package/dist/login-runner-lifecycle.d.ts +70 -0
  295. package/dist/login-runner-lifecycle.d.ts.map +1 -0
  296. package/dist/login-runner-lifecycle.js +52 -0
  297. package/dist/login-runner-lifecycle.js.map +1 -0
  298. package/dist/login-runner-lifecycle.test.d.ts +2 -0
  299. package/dist/login-runner-lifecycle.test.d.ts.map +1 -0
  300. package/dist/login-runner-lifecycle.test.js +122 -0
  301. package/dist/login-runner-lifecycle.test.js.map +1 -0
  302. package/dist/mcp-isolation.integration.test.d.ts +2 -0
  303. package/dist/mcp-isolation.integration.test.d.ts.map +1 -0
  304. package/dist/mcp-isolation.integration.test.js +202 -0
  305. package/dist/mcp-isolation.integration.test.js.map +1 -0
  306. package/dist/paperclip-runner-permissions.d.ts +110 -0
  307. package/dist/paperclip-runner-permissions.d.ts.map +1 -0
  308. package/dist/paperclip-runner-permissions.js +150 -0
  309. package/dist/paperclip-runner-permissions.js.map +1 -0
  310. package/dist/paperclip-runner-permissions.test.d.ts +2 -0
  311. package/dist/paperclip-runner-permissions.test.d.ts.map +1 -0
  312. package/dist/paperclip-runner-permissions.test.js +47 -0
  313. package/dist/paperclip-runner-permissions.test.js.map +1 -0
  314. package/dist/published-exports.test.d.ts +2 -0
  315. package/dist/published-exports.test.d.ts.map +1 -0
  316. package/dist/published-exports.test.js +17 -0
  317. package/dist/published-exports.test.js.map +1 -0
  318. package/dist/remote-execution-env.d.ts +2 -0
  319. package/dist/remote-execution-env.d.ts.map +1 -0
  320. package/dist/remote-execution-env.js +46 -0
  321. package/dist/remote-execution-env.js.map +1 -0
  322. package/dist/remote-managed-runtime.d.ts +45 -0
  323. package/dist/remote-managed-runtime.d.ts.map +1 -0
  324. package/dist/remote-managed-runtime.js +183 -0
  325. package/dist/remote-managed-runtime.js.map +1 -0
  326. package/dist/remote-managed-runtime.test.d.ts +2 -0
  327. package/dist/remote-managed-runtime.test.d.ts.map +1 -0
  328. package/dist/remote-managed-runtime.test.js +294 -0
  329. package/dist/remote-managed-runtime.test.js.map +1 -0
  330. package/dist/runner-connectivity.d.ts +61 -0
  331. package/dist/runner-connectivity.d.ts.map +1 -0
  332. package/dist/runner-connectivity.js +81 -0
  333. package/dist/runner-connectivity.js.map +1 -0
  334. package/dist/runner-connectivity.test.d.ts +2 -0
  335. package/dist/runner-connectivity.test.d.ts.map +1 -0
  336. package/dist/runner-connectivity.test.js +183 -0
  337. package/dist/runner-connectivity.test.js.map +1 -0
  338. package/dist/runtime-progress.d.ts +48 -0
  339. package/dist/runtime-progress.d.ts.map +1 -0
  340. package/dist/runtime-progress.js +99 -0
  341. package/dist/runtime-progress.js.map +1 -0
  342. package/dist/runtime-progress.test.d.ts +2 -0
  343. package/dist/runtime-progress.test.d.ts.map +1 -0
  344. package/dist/runtime-progress.test.js +240 -0
  345. package/dist/runtime-progress.test.js.map +1 -0
  346. package/dist/sandbox-callback-bridge-body.d.ts +12 -0
  347. package/dist/sandbox-callback-bridge-body.d.ts.map +1 -0
  348. package/dist/sandbox-callback-bridge-body.js +39 -0
  349. package/dist/sandbox-callback-bridge-body.js.map +1 -0
  350. package/dist/sandbox-callback-bridge-body.test.d.ts +2 -0
  351. package/dist/sandbox-callback-bridge-body.test.d.ts.map +1 -0
  352. package/dist/sandbox-callback-bridge-body.test.js +31 -0
  353. package/dist/sandbox-callback-bridge-body.test.js.map +1 -0
  354. package/dist/sandbox-callback-bridge.d.ts +268 -0
  355. package/dist/sandbox-callback-bridge.d.ts.map +1 -0
  356. package/dist/sandbox-callback-bridge.js +2357 -0
  357. package/dist/sandbox-callback-bridge.js.map +1 -0
  358. package/dist/sandbox-callback-bridge.test.d.ts +2 -0
  359. package/dist/sandbox-callback-bridge.test.d.ts.map +1 -0
  360. package/dist/sandbox-callback-bridge.test.js +3253 -0
  361. package/dist/sandbox-callback-bridge.test.js.map +1 -0
  362. package/dist/sandbox-file-sync.test.d.ts +2 -0
  363. package/dist/sandbox-file-sync.test.d.ts.map +1 -0
  364. package/dist/sandbox-file-sync.test.js +419 -0
  365. package/dist/sandbox-file-sync.test.js.map +1 -0
  366. package/dist/sandbox-install-command.d.ts +2 -0
  367. package/dist/sandbox-install-command.d.ts.map +1 -0
  368. package/dist/sandbox-install-command.js +47 -0
  369. package/dist/sandbox-install-command.js.map +1 -0
  370. package/dist/sandbox-install-command.test.d.ts +2 -0
  371. package/dist/sandbox-install-command.test.d.ts.map +1 -0
  372. package/dist/sandbox-install-command.test.js +22 -0
  373. package/dist/sandbox-install-command.test.js.map +1 -0
  374. package/dist/sandbox-managed-runtime.d.ts +417 -0
  375. package/dist/sandbox-managed-runtime.d.ts.map +1 -0
  376. package/dist/sandbox-managed-runtime.js +1337 -0
  377. package/dist/sandbox-managed-runtime.js.map +1 -0
  378. package/dist/sandbox-managed-runtime.test.d.ts +2 -0
  379. package/dist/sandbox-managed-runtime.test.d.ts.map +1 -0
  380. package/dist/sandbox-managed-runtime.test.js +3746 -0
  381. package/dist/sandbox-managed-runtime.test.js.map +1 -0
  382. package/dist/sandbox-run-log-stream.d.ts +44 -0
  383. package/dist/sandbox-run-log-stream.d.ts.map +1 -0
  384. package/dist/sandbox-run-log-stream.js +203 -0
  385. package/dist/sandbox-run-log-stream.js.map +1 -0
  386. package/dist/sandbox-shell.d.ts +3 -0
  387. package/dist/sandbox-shell.d.ts.map +1 -0
  388. package/dist/sandbox-shell.js +7 -0
  389. package/dist/sandbox-shell.js.map +1 -0
  390. package/dist/server-utils-env.test.d.ts +2 -0
  391. package/dist/server-utils-env.test.d.ts.map +1 -0
  392. package/dist/server-utils-env.test.js +15 -0
  393. package/dist/server-utils-env.test.js.map +1 -0
  394. package/dist/server-utils.d.ts +617 -0
  395. package/dist/server-utils.d.ts.map +1 -0
  396. package/dist/server-utils.js +3258 -0
  397. package/dist/server-utils.js.map +1 -0
  398. package/dist/server-utils.test.d.ts +2 -0
  399. package/dist/server-utils.test.d.ts.map +1 -0
  400. package/dist/server-utils.test.js +3047 -0
  401. package/dist/server-utils.test.js.map +1 -0
  402. package/dist/session-compaction.d.ts +25 -0
  403. package/dist/session-compaction.d.ts.map +1 -0
  404. package/dist/session-compaction.js +165 -0
  405. package/dist/session-compaction.js.map +1 -0
  406. package/dist/skill-library-manifest.d.ts +6 -0
  407. package/dist/skill-library-manifest.d.ts.map +1 -0
  408. package/dist/skill-library-manifest.js +53 -0
  409. package/dist/skill-library-manifest.js.map +1 -0
  410. package/dist/skill-library-manifest.test.d.ts +2 -0
  411. package/dist/skill-library-manifest.test.d.ts.map +1 -0
  412. package/dist/skill-library-manifest.test.js +84 -0
  413. package/dist/skill-library-manifest.test.js.map +1 -0
  414. package/dist/skills-staging-follow-symlinks.test.d.ts +2 -0
  415. package/dist/skills-staging-follow-symlinks.test.d.ts.map +1 -0
  416. package/dist/skills-staging-follow-symlinks.test.js +126 -0
  417. package/dist/skills-staging-follow-symlinks.test.js.map +1 -0
  418. package/dist/ssh-fixture.test.d.ts +2 -0
  419. package/dist/ssh-fixture.test.d.ts.map +1 -0
  420. package/dist/ssh-fixture.test.js +832 -0
  421. package/dist/ssh-fixture.test.js.map +1 -0
  422. package/dist/ssh.d.ts +124 -0
  423. package/dist/ssh.d.ts.map +1 -0
  424. package/dist/ssh.js +1667 -0
  425. package/dist/ssh.js.map +1 -0
  426. package/dist/sync-operation-schedule.d.ts +17 -0
  427. package/dist/sync-operation-schedule.d.ts.map +1 -0
  428. package/dist/sync-operation-schedule.js +66 -0
  429. package/dist/sync-operation-schedule.js.map +1 -0
  430. package/dist/sync-operation-schedule.test.d.ts +2 -0
  431. package/dist/sync-operation-schedule.test.d.ts.map +1 -0
  432. package/dist/sync-operation-schedule.test.js +130 -0
  433. package/dist/sync-operation-schedule.test.js.map +1 -0
  434. package/dist/test-support/mcp-isolation-harness.d.ts +23 -0
  435. package/dist/test-support/mcp-isolation-harness.d.ts.map +1 -0
  436. package/dist/test-support/mcp-isolation-harness.js +67 -0
  437. package/dist/test-support/mcp-isolation-harness.js.map +1 -0
  438. package/dist/types.d.ts +780 -0
  439. package/dist/types.d.ts.map +1 -0
  440. package/dist/types.js +5 -0
  441. package/dist/types.js.map +1 -0
  442. package/dist/workspace-restore-merge.d.ts +77 -0
  443. package/dist/workspace-restore-merge.d.ts.map +1 -0
  444. package/dist/workspace-restore-merge.js +396 -0
  445. package/dist/workspace-restore-merge.js.map +1 -0
  446. package/dist/workspace-restore-merge.test.d.ts +2 -0
  447. package/dist/workspace-restore-merge.test.d.ts.map +1 -0
  448. package/dist/workspace-restore-merge.test.js +438 -0
  449. package/dist/workspace-restore-merge.test.js.map +1 -0
  450. package/dist/workspace-restore-teardown.d.ts +29 -0
  451. package/dist/workspace-restore-teardown.d.ts.map +1 -0
  452. package/dist/workspace-restore-teardown.js +34 -0
  453. package/dist/workspace-restore-teardown.js.map +1 -0
  454. package/dist/workspace-restore-teardown.test.d.ts +2 -0
  455. package/dist/workspace-restore-teardown.test.d.ts.map +1 -0
  456. package/dist/workspace-restore-teardown.test.js +71 -0
  457. package/dist/workspace-restore-teardown.test.js.map +1 -0
  458. package/node_modules/@agentclientprotocol/sdk/LICENSE +191 -0
  459. package/node_modules/@agentclientprotocol/sdk/README.md +70 -0
  460. package/node_modules/@agentclientprotocol/sdk/dist/acp.d.ts +1803 -0
  461. package/node_modules/@agentclientprotocol/sdk/dist/acp.js +1869 -0
  462. package/node_modules/@agentclientprotocol/sdk/dist/acp.js.map +1 -0
  463. package/node_modules/@agentclientprotocol/sdk/dist/acp.test.d.ts +1 -0
  464. package/node_modules/@agentclientprotocol/sdk/dist/acp.test.js +5036 -0
  465. package/node_modules/@agentclientprotocol/sdk/dist/acp.test.js.map +1 -0
  466. package/node_modules/@agentclientprotocol/sdk/dist/connection.d.ts +89 -0
  467. package/node_modules/@agentclientprotocol/sdk/dist/connection.js +490 -0
  468. package/node_modules/@agentclientprotocol/sdk/dist/connection.js.map +1 -0
  469. package/node_modules/@agentclientprotocol/sdk/dist/connection.test.d.ts +1 -0
  470. package/node_modules/@agentclientprotocol/sdk/dist/connection.test.js +507 -0
  471. package/node_modules/@agentclientprotocol/sdk/dist/connection.test.js.map +1 -0
  472. package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.d.ts +26 -0
  473. package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.js +96 -0
  474. package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.js.map +1 -0
  475. package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.d.ts +1 -0
  476. package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.js +77 -0
  477. package/node_modules/@agentclientprotocol/sdk/dist/cookie-store.test.js.map +1 -0
  478. package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.d.ts +2 -0
  479. package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js +232 -0
  480. package/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js.map +1 -0
  481. package/node_modules/@agentclientprotocol/sdk/dist/examples/client.d.ts +2 -0
  482. package/node_modules/@agentclientprotocol/sdk/dist/examples/client.js +132 -0
  483. package/node_modules/@agentclientprotocol/sdk/dist/examples/client.js.map +1 -0
  484. package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.d.ts +9 -0
  485. package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.js +221 -0
  486. package/node_modules/@agentclientprotocol/sdk/dist/examples/dual-version-agent.js.map +1 -0
  487. package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.d.ts +2 -0
  488. package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.js +86 -0
  489. package/node_modules/@agentclientprotocol/sdk/dist/examples/http-client.js.map +1 -0
  490. package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.d.ts +2 -0
  491. package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.js +112 -0
  492. package/node_modules/@agentclientprotocol/sdk/dist/examples/http-server.js.map +1 -0
  493. package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.d.ts +2 -0
  494. package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.js +88 -0
  495. package/node_modules/@agentclientprotocol/sdk/dist/examples/ws-client.js.map +1 -0
  496. package/node_modules/@agentclientprotocol/sdk/dist/http-stream.d.ts +33 -0
  497. package/node_modules/@agentclientprotocol/sdk/dist/http-stream.js +435 -0
  498. package/node_modules/@agentclientprotocol/sdk/dist/http-stream.js.map +1 -0
  499. package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.d.ts +1 -0
  500. package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.js +1380 -0
  501. package/node_modules/@agentclientprotocol/sdk/dist/http-stream.test.js.map +1 -0
  502. package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.d.ts +603 -0
  503. package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js +1076 -0
  504. package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js.map +1 -0
  505. package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.d.ts +1 -0
  506. package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.js +917 -0
  507. package/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.test.js.map +1 -0
  508. package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.d.ts +22 -0
  509. package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.js +64 -0
  510. package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.js.map +1 -0
  511. package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.d.ts +1 -0
  512. package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.js +45 -0
  513. package/node_modules/@agentclientprotocol/sdk/dist/line-buffer.test.js.map +1 -0
  514. package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.d.ts +17 -0
  515. package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.js +426 -0
  516. package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.js.map +1 -0
  517. package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.d.ts +1 -0
  518. package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.js +499 -0
  519. package/node_modules/@agentclientprotocol/sdk/dist/node-adapter.test.js.map +1 -0
  520. package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.d.ts +37 -0
  521. package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.js +474 -0
  522. package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.js.map +1 -0
  523. package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.d.ts +1 -0
  524. package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.js +535 -0
  525. package/node_modules/@agentclientprotocol/sdk/dist/protocol-router.test.js.map +1 -0
  526. package/node_modules/@agentclientprotocol/sdk/dist/protocol.d.ts +11 -0
  527. package/node_modules/@agentclientprotocol/sdk/dist/protocol.js +69 -0
  528. package/node_modules/@agentclientprotocol/sdk/dist/protocol.js.map +1 -0
  529. package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.d.ts +1 -0
  530. package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.js +82 -0
  531. package/node_modules/@agentclientprotocol/sdk/dist/protocol.test.js.map +1 -0
  532. package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.d.ts +183 -0
  533. package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js +207 -0
  534. package/node_modules/@agentclientprotocol/sdk/dist/schema/guards.gen.js.map +1 -0
  535. package/node_modules/@agentclientprotocol/sdk/dist/schema/index.d.ts +51 -0
  536. package/node_modules/@agentclientprotocol/sdk/dist/schema/index.js +52 -0
  537. package/node_modules/@agentclientprotocol/sdk/dist/schema/index.js.map +1 -0
  538. package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.d.ts +6219 -0
  539. package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js +3 -0
  540. package/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js.map +1 -0
  541. package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.d.ts +15215 -0
  542. package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js +3084 -0
  543. package/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js.map +1 -0
  544. package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.d.ts +30 -0
  545. package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.js +98 -0
  546. package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.js.map +1 -0
  547. package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.d.ts +1 -0
  548. package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.js +93 -0
  549. package/node_modules/@agentclientprotocol/sdk/dist/schema-deserialize.test.js.map +1 -0
  550. package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.d.ts +1 -0
  551. package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.js +261 -0
  552. package/node_modules/@agentclientprotocol/sdk/dist/server-permission.test.js.map +1 -0
  553. package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.d.ts +1 -0
  554. package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.js +536 -0
  555. package/node_modules/@agentclientprotocol/sdk/dist/server-session-sse.test.js.map +1 -0
  556. package/node_modules/@agentclientprotocol/sdk/dist/server-sse.d.ts +2 -0
  557. package/node_modules/@agentclientprotocol/sdk/dist/server-sse.js +88 -0
  558. package/node_modules/@agentclientprotocol/sdk/dist/server-sse.js.map +1 -0
  559. package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.d.ts +1 -0
  560. package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.js +53 -0
  561. package/node_modules/@agentclientprotocol/sdk/dist/server-sse.test.js.map +1 -0
  562. package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.d.ts +1 -0
  563. package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.js +992 -0
  564. package/node_modules/@agentclientprotocol/sdk/dist/server-websocket-upgrade.test.js.map +1 -0
  565. package/node_modules/@agentclientprotocol/sdk/dist/server.d.ts +81 -0
  566. package/node_modules/@agentclientprotocol/sdk/dist/server.js +386 -0
  567. package/node_modules/@agentclientprotocol/sdk/dist/server.js.map +1 -0
  568. package/node_modules/@agentclientprotocol/sdk/dist/server.test.d.ts +1 -0
  569. package/node_modules/@agentclientprotocol/sdk/dist/server.test.js +1116 -0
  570. package/node_modules/@agentclientprotocol/sdk/dist/server.test.js.map +1 -0
  571. package/node_modules/@agentclientprotocol/sdk/dist/sse.d.ts +4 -0
  572. package/node_modules/@agentclientprotocol/sdk/dist/sse.js +91 -0
  573. package/node_modules/@agentclientprotocol/sdk/dist/sse.js.map +1 -0
  574. package/node_modules/@agentclientprotocol/sdk/dist/sse.test.d.ts +1 -0
  575. package/node_modules/@agentclientprotocol/sdk/dist/sse.test.js +126 -0
  576. package/node_modules/@agentclientprotocol/sdk/dist/sse.test.js.map +1 -0
  577. package/node_modules/@agentclientprotocol/sdk/dist/stream.d.ts +35 -0
  578. package/node_modules/@agentclientprotocol/sdk/dist/stream.js +115 -0
  579. package/node_modules/@agentclientprotocol/sdk/dist/stream.js.map +1 -0
  580. package/node_modules/@agentclientprotocol/sdk/dist/stream.test.d.ts +1 -0
  581. package/node_modules/@agentclientprotocol/sdk/dist/stream.test.js +195 -0
  582. package/node_modules/@agentclientprotocol/sdk/dist/stream.test.js.map +1 -0
  583. package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.d.ts +8 -0
  584. package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.js +51 -0
  585. package/node_modules/@agentclientprotocol/sdk/dist/test-support/streams.js.map +1 -0
  586. package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.d.ts +24 -0
  587. package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.js +163 -0
  588. package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-agent.js.map +1 -0
  589. package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.d.ts +9 -0
  590. package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.js +74 -0
  591. package/node_modules/@agentclientprotocol/sdk/dist/test-support/test-http-server.js.map +1 -0
  592. package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.d.ts +957 -0
  593. package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.js +1484 -0
  594. package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.js.map +1 -0
  595. package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.d.ts +1 -0
  596. package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.js +1287 -0
  597. package/node_modules/@agentclientprotocol/sdk/dist/v2/acp.test.js.map +1 -0
  598. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.d.ts +946 -0
  599. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.js +1046 -0
  600. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/guards.gen.js.map +1 -0
  601. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.d.ts +42 -0
  602. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.js +43 -0
  603. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/index.js.map +1 -0
  604. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.d.ts +6208 -0
  605. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.js +3 -0
  606. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/types.gen.js.map +1 -0
  607. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.d.ts +33267 -0
  608. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.js +3199 -0
  609. package/node_modules/@agentclientprotocol/sdk/dist/v2/schema/zod.gen.js.map +1 -0
  610. package/node_modules/@agentclientprotocol/sdk/dist/ws-server.d.ts +14 -0
  611. package/node_modules/@agentclientprotocol/sdk/dist/ws-server.js +357 -0
  612. package/node_modules/@agentclientprotocol/sdk/dist/ws-server.js.map +1 -0
  613. package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.d.ts +54 -0
  614. package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.js +253 -0
  615. package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.js.map +1 -0
  616. package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.d.ts +1 -0
  617. package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.js +836 -0
  618. package/node_modules/@agentclientprotocol/sdk/dist/ws-stream.test.js.map +1 -0
  619. package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.d.ts +13 -0
  620. package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.js +78 -0
  621. package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.js.map +1 -0
  622. package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.d.ts +1 -0
  623. package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.js +42 -0
  624. package/node_modules/@agentclientprotocol/sdk/dist/ws-utils.test.js.map +1 -0
  625. package/node_modules/@agentclientprotocol/sdk/package.json +104 -0
  626. package/node_modules/@agentclientprotocol/sdk/schema/schema.json +8967 -0
  627. package/node_modules/@agentclientprotocol/sdk/schema/v2/schema.unstable.json +10242 -0
  628. package/node_modules/@clack/core/LICENSE +9 -0
  629. package/node_modules/@clack/core/README.md +22 -0
  630. package/node_modules/@clack/core/dist/index.d.mts +503 -0
  631. package/node_modules/@clack/core/dist/index.mjs +1003 -0
  632. package/node_modules/@clack/core/package.json +63 -0
  633. package/node_modules/@clack/prompts/LICENSE +9 -0
  634. package/node_modules/@clack/prompts/README.md +396 -0
  635. package/node_modules/@clack/prompts/dist/index.d.mts +949 -0
  636. package/node_modules/@clack/prompts/dist/index.mjs +1423 -0
  637. package/node_modules/@clack/prompts/package.json +67 -0
  638. package/node_modules/@esbuild/linux-x64/README.md +3 -0
  639. package/node_modules/@esbuild/linux-x64/bin/esbuild +0 -0
  640. package/node_modules/@esbuild/linux-x64/package.json +20 -0
  641. package/node_modules/acpx/LICENSE +21 -0
  642. package/node_modules/acpx/README.md +424 -0
  643. package/node_modules/acpx/dist/cli-CxsS1XQp.js +197 -0
  644. package/node_modules/acpx/dist/cli-CxsS1XQp.js.map +1 -0
  645. package/node_modules/acpx/dist/cli.d.ts +14 -0
  646. package/node_modules/acpx/dist/cli.d.ts.map +1 -0
  647. package/node_modules/acpx/dist/cli.js +2683 -0
  648. package/node_modules/acpx/dist/cli.js.map +1 -0
  649. package/node_modules/acpx/dist/client-DwNyFauq.d.ts +192 -0
  650. package/node_modules/acpx/dist/client-DwNyFauq.d.ts.map +1 -0
  651. package/node_modules/acpx/dist/flags-Bjq_XQ4-.js +260 -0
  652. package/node_modules/acpx/dist/flags-Bjq_XQ4-.js.map +1 -0
  653. package/node_modules/acpx/dist/flows-Db_LZKu9.js +1920 -0
  654. package/node_modules/acpx/dist/flows-Db_LZKu9.js.map +1 -0
  655. package/node_modules/acpx/dist/flows.d.ts +318 -0
  656. package/node_modules/acpx/dist/flows.d.ts.map +1 -0
  657. package/node_modules/acpx/dist/flows.js +2 -0
  658. package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js +6210 -0
  659. package/node_modules/acpx/dist/live-checkpoint-ClPCSdrW.js.map +1 -0
  660. package/node_modules/acpx/dist/output-CjdF5rHk.js +4593 -0
  661. package/node_modules/acpx/dist/output-CjdF5rHk.js.map +1 -0
  662. package/node_modules/acpx/dist/runtime.d.ts +472 -0
  663. package/node_modules/acpx/dist/runtime.d.ts.map +1 -0
  664. package/node_modules/acpx/dist/runtime.js +1711 -0
  665. package/node_modules/acpx/dist/runtime.js.map +1 -0
  666. package/node_modules/acpx/dist/session-options-jkYbBxGE.d.ts +278 -0
  667. package/node_modules/acpx/dist/session-options-jkYbBxGE.d.ts.map +1 -0
  668. package/node_modules/acpx/package.json +129 -0
  669. package/node_modules/acpx/skills/acpx/SKILL.md +617 -0
  670. package/node_modules/b4a/LICENSE +201 -0
  671. package/node_modules/b4a/README.md +153 -0
  672. package/node_modules/b4a/browser.js +546 -0
  673. package/node_modules/b4a/index.d.ts +144 -0
  674. package/node_modules/b4a/index.js +193 -0
  675. package/node_modules/b4a/lib/ascii.js +31 -0
  676. package/node_modules/b4a/lib/base64.js +64 -0
  677. package/node_modules/b4a/lib/hex.js +51 -0
  678. package/node_modules/b4a/lib/latin1.js +31 -0
  679. package/node_modules/b4a/lib/utf16le.js +40 -0
  680. package/node_modules/b4a/lib/utf8.js +144 -0
  681. package/node_modules/b4a/package.json +54 -0
  682. package/node_modules/b4a/react-native.js +5 -0
  683. package/node_modules/b4a/runtime.d.ts +14 -0
  684. package/node_modules/bare-events/LICENSE +201 -0
  685. package/node_modules/bare-events/README.md +25 -0
  686. package/node_modules/bare-events/global.d.ts +15 -0
  687. package/node_modules/bare-events/global.js +5 -0
  688. package/node_modules/bare-events/index.d.ts +89 -0
  689. package/node_modules/bare-events/index.js +397 -0
  690. package/node_modules/bare-events/lib/errors.js +23 -0
  691. package/node_modules/bare-events/package.json +63 -0
  692. package/node_modules/bare-events/web.d.ts +79 -0
  693. package/node_modules/bare-events/web.js +417 -0
  694. package/node_modules/bare-fs/CMakeLists.txt +13 -0
  695. package/node_modules/bare-fs/LICENSE +201 -0
  696. package/node_modules/bare-fs/README.md +33 -0
  697. package/node_modules/bare-fs/binding.c +2913 -0
  698. package/node_modules/bare-fs/binding.js +1 -0
  699. package/node_modules/bare-fs/index.d.ts +1502 -0
  700. package/node_modules/bare-fs/index.js +2855 -0
  701. package/node_modules/bare-fs/lib/constants.d.ts +50 -0
  702. package/node_modules/bare-fs/lib/constants.js +49 -0
  703. package/node_modules/bare-fs/lib/errors.d.ts +13 -0
  704. package/node_modules/bare-fs/lib/errors.js +55 -0
  705. package/node_modules/bare-fs/package.json +74 -0
  706. package/node_modules/bare-fs/prebuilds/android-arm/bare-fs.bare +0 -0
  707. package/node_modules/bare-fs/prebuilds/android-arm64/bare-fs.bare +0 -0
  708. package/node_modules/bare-fs/prebuilds/android-ia32/bare-fs.bare +0 -0
  709. package/node_modules/bare-fs/prebuilds/android-x64/bare-fs.bare +0 -0
  710. package/node_modules/bare-fs/prebuilds/darwin-arm64/bare-fs.bare +0 -0
  711. package/node_modules/bare-fs/prebuilds/darwin-x64/bare-fs.bare +0 -0
  712. package/node_modules/bare-fs/prebuilds/ios-arm64/bare-fs.bare +0 -0
  713. package/node_modules/bare-fs/prebuilds/ios-arm64-simulator/bare-fs.bare +0 -0
  714. package/node_modules/bare-fs/prebuilds/ios-x64-simulator/bare-fs.bare +0 -0
  715. package/node_modules/bare-fs/prebuilds/linux-arm64/bare-fs.bare +0 -0
  716. package/node_modules/bare-fs/prebuilds/linux-x64/bare-fs.bare +0 -0
  717. package/node_modules/bare-fs/prebuilds/win32-arm64/bare-fs.bare +0 -0
  718. package/node_modules/bare-fs/prebuilds/win32-x64/bare-fs.bare +0 -0
  719. package/node_modules/bare-fs/promises.d.ts +480 -0
  720. package/node_modules/bare-fs/promises.js +119 -0
  721. package/node_modules/bare-path/CMakeLists.txt +13 -0
  722. package/node_modules/bare-path/LICENSE +201 -0
  723. package/node_modules/bare-path/NOTICE +28 -0
  724. package/node_modules/bare-path/README.md +19 -0
  725. package/node_modules/bare-path/binding.c +106 -0
  726. package/node_modules/bare-path/binding.js +1 -0
  727. package/node_modules/bare-path/index.d.ts +27 -0
  728. package/node_modules/bare-path/index.js +9 -0
  729. package/node_modules/bare-path/lib/constants.js +11 -0
  730. package/node_modules/bare-path/lib/posix.js +263 -0
  731. package/node_modules/bare-path/lib/shared.js +81 -0
  732. package/node_modules/bare-path/lib/win32.js +564 -0
  733. package/node_modules/bare-path/package.json +47 -0
  734. package/node_modules/bare-path/prebuilds/android-arm/bare-path.bare +0 -0
  735. package/node_modules/bare-path/prebuilds/android-arm64/bare-path.bare +0 -0
  736. package/node_modules/bare-path/prebuilds/android-ia32/bare-path.bare +0 -0
  737. package/node_modules/bare-path/prebuilds/android-x64/bare-path.bare +0 -0
  738. package/node_modules/bare-path/prebuilds/darwin-arm64/bare-path.bare +0 -0
  739. package/node_modules/bare-path/prebuilds/darwin-x64/bare-path.bare +0 -0
  740. package/node_modules/bare-path/prebuilds/ios-arm64/bare-path.bare +0 -0
  741. package/node_modules/bare-path/prebuilds/ios-arm64-simulator/bare-path.bare +0 -0
  742. package/node_modules/bare-path/prebuilds/ios-x64-simulator/bare-path.bare +0 -0
  743. package/node_modules/bare-path/prebuilds/linux-arm64/bare-path.bare +0 -0
  744. package/node_modules/bare-path/prebuilds/linux-x64/bare-path.bare +0 -0
  745. package/node_modules/bare-path/prebuilds/win32-arm64/bare-path.bare +0 -0
  746. package/node_modules/bare-path/prebuilds/win32-x64/bare-path.bare +0 -0
  747. package/node_modules/bare-stream/LICENSE +201 -0
  748. package/node_modules/bare-stream/README.md +354 -0
  749. package/node_modules/bare-stream/global.d.ts +31 -0
  750. package/node_modules/bare-stream/global.js +15 -0
  751. package/node_modules/bare-stream/index.d.ts +235 -0
  752. package/node_modules/bare-stream/index.js +493 -0
  753. package/node_modules/bare-stream/package.json +75 -0
  754. package/node_modules/bare-stream/promises.js +3 -0
  755. package/node_modules/bare-stream/web.d.ts +152 -0
  756. package/node_modules/bare-stream/web.js +607 -0
  757. package/node_modules/bare-url/CMakeLists.txt +36 -0
  758. package/node_modules/bare-url/LICENSE +201 -0
  759. package/node_modules/bare-url/README.md +33 -0
  760. package/node_modules/bare-url/binding.c +422 -0
  761. package/node_modules/bare-url/binding.js +1 -0
  762. package/node_modules/bare-url/global.d.ts +24 -0
  763. package/node_modules/bare-url/global.js +2 -0
  764. package/node_modules/bare-url/index.d.ts +96 -0
  765. package/node_modules/bare-url/index.js +676 -0
  766. package/node_modules/bare-url/lib/errors.d.ts +10 -0
  767. package/node_modules/bare-url/lib/errors.js +33 -0
  768. package/node_modules/bare-url/lib/url-search-params.d.ts +66 -0
  769. package/node_modules/bare-url/lib/url-search-params.js +276 -0
  770. package/node_modules/bare-url/package.json +54 -0
  771. package/node_modules/bare-url/prebuilds/android-arm/bare-url.bare +0 -0
  772. package/node_modules/bare-url/prebuilds/android-arm64/bare-url.bare +0 -0
  773. package/node_modules/bare-url/prebuilds/android-ia32/bare-url.bare +0 -0
  774. package/node_modules/bare-url/prebuilds/android-x64/bare-url.bare +0 -0
  775. package/node_modules/bare-url/prebuilds/darwin-arm64/bare-url.bare +0 -0
  776. package/node_modules/bare-url/prebuilds/darwin-x64/bare-url.bare +0 -0
  777. package/node_modules/bare-url/prebuilds/ios-arm64/bare-url.bare +0 -0
  778. package/node_modules/bare-url/prebuilds/ios-arm64-simulator/bare-url.bare +0 -0
  779. package/node_modules/bare-url/prebuilds/ios-x64-simulator/bare-url.bare +0 -0
  780. package/node_modules/bare-url/prebuilds/linux-arm64/bare-url.bare +0 -0
  781. package/node_modules/bare-url/prebuilds/linux-x64/bare-url.bare +0 -0
  782. package/node_modules/bare-url/prebuilds/win32-arm64/bare-url.bare +0 -0
  783. package/node_modules/bare-url/prebuilds/win32-x64/bare-url.bare +0 -0
  784. package/node_modules/commander/LICENSE +22 -0
  785. package/node_modules/commander/Readme.md +1172 -0
  786. package/node_modules/commander/index.js +21 -0
  787. package/node_modules/commander/lib/argument.js +147 -0
  788. package/node_modules/commander/lib/command.js +2790 -0
  789. package/node_modules/commander/lib/error.js +36 -0
  790. package/node_modules/commander/lib/help.js +731 -0
  791. package/node_modules/commander/lib/option.js +377 -0
  792. package/node_modules/commander/lib/suggestSimilar.js +99 -0
  793. package/node_modules/commander/package-support.json +19 -0
  794. package/node_modules/commander/package.json +64 -0
  795. package/node_modules/commander/typings/index.d.ts +1113 -0
  796. package/node_modules/esbuild/LICENSE.md +21 -0
  797. package/node_modules/esbuild/README.md +3 -0
  798. package/node_modules/esbuild/bin/esbuild +223 -0
  799. package/node_modules/esbuild/install.js +300 -0
  800. package/node_modules/esbuild/lib/main.d.ts +720 -0
  801. package/node_modules/esbuild/lib/main.js +2536 -0
  802. package/node_modules/esbuild/package.json +74 -0
  803. package/node_modules/events-universal/LICENSE +201 -0
  804. package/node_modules/events-universal/README.md +17 -0
  805. package/node_modules/events-universal/bare.js +1 -0
  806. package/node_modules/events-universal/default.js +1 -0
  807. package/node_modules/events-universal/index.js +1 -0
  808. package/node_modules/events-universal/package.json +39 -0
  809. package/node_modules/events-universal/react-native.js +1 -0
  810. package/node_modules/fast-fifo/LICENSE +21 -0
  811. package/node_modules/fast-fifo/README.md +78 -0
  812. package/node_modules/fast-fifo/fixed-size.js +39 -0
  813. package/node_modules/fast-fifo/index.js +48 -0
  814. package/node_modules/fast-fifo/package.json +28 -0
  815. package/node_modules/fast-string-truncated-width/dist/index.d.ts +4 -0
  816. package/node_modules/fast-string-truncated-width/dist/index.js +111 -0
  817. package/node_modules/fast-string-truncated-width/dist/types.d.ts +19 -0
  818. package/node_modules/fast-string-truncated-width/dist/types.js +2 -0
  819. package/node_modules/fast-string-truncated-width/dist/utils.d.ts +4 -0
  820. package/node_modules/fast-string-truncated-width/dist/utils.js +20 -0
  821. package/node_modules/fast-string-truncated-width/license +21 -0
  822. package/node_modules/fast-string-truncated-width/package.json +35 -0
  823. package/node_modules/fast-string-truncated-width/readme.md +59 -0
  824. package/node_modules/fast-string-width/dist/index.d.ts +4 -0
  825. package/node_modules/fast-string-width/dist/index.js +14 -0
  826. package/node_modules/fast-string-width/license +21 -0
  827. package/node_modules/fast-string-width/package.json +34 -0
  828. package/node_modules/fast-string-width/readme.md +42 -0
  829. package/node_modules/fast-wrap-ansi/LICENSE +23 -0
  830. package/node_modules/fast-wrap-ansi/README.md +26 -0
  831. package/node_modules/fast-wrap-ansi/lib/main.d.ts +6 -0
  832. package/node_modules/fast-wrap-ansi/lib/main.js +218 -0
  833. package/node_modules/fast-wrap-ansi/package.json +51 -0
  834. package/node_modules/sisteransi/license +21 -0
  835. package/node_modules/sisteransi/package.json +34 -0
  836. package/node_modules/sisteransi/readme.md +113 -0
  837. package/node_modules/sisteransi/src/index.js +58 -0
  838. package/node_modules/sisteransi/src/sisteransi.d.ts +35 -0
  839. package/node_modules/skillflag/LICENSE +21 -0
  840. package/node_modules/skillflag/README.md +44 -0
  841. package/node_modules/skillflag/dist/bin/skill-install.d.ts +3 -0
  842. package/node_modules/skillflag/dist/bin/skill-install.d.ts.map +1 -0
  843. package/node_modules/skillflag/dist/bin/skill-install.js +6 -0
  844. package/node_modules/skillflag/dist/bin/skill-install.js.map +1 -0
  845. package/node_modules/skillflag/dist/bin/skillflag.d.ts +3 -0
  846. package/node_modules/skillflag/dist/bin/skillflag.d.ts.map +1 -0
  847. package/node_modules/skillflag/dist/bin/skillflag.js +25 -0
  848. package/node_modules/skillflag/dist/bin/skillflag.js.map +1 -0
  849. package/node_modules/skillflag/dist/core/digest.d.ts +2 -0
  850. package/node_modules/skillflag/dist/core/digest.d.ts.map +1 -0
  851. package/node_modules/skillflag/dist/core/digest.js +15 -0
  852. package/node_modules/skillflag/dist/core/digest.js.map +1 -0
  853. package/node_modules/skillflag/dist/core/errors.d.ts +6 -0
  854. package/node_modules/skillflag/dist/core/errors.d.ts.map +1 -0
  855. package/node_modules/skillflag/dist/core/errors.js +13 -0
  856. package/node_modules/skillflag/dist/core/errors.js.map +1 -0
  857. package/node_modules/skillflag/dist/core/export.d.ts +2 -0
  858. package/node_modules/skillflag/dist/core/export.d.ts.map +1 -0
  859. package/node_modules/skillflag/dist/core/export.js +28 -0
  860. package/node_modules/skillflag/dist/core/export.js.map +1 -0
  861. package/node_modules/skillflag/dist/core/list.d.ts +21 -0
  862. package/node_modules/skillflag/dist/core/list.d.ts.map +1 -0
  863. package/node_modules/skillflag/dist/core/list.js +71 -0
  864. package/node_modules/skillflag/dist/core/list.js.map +1 -0
  865. package/node_modules/skillflag/dist/core/paths.d.ts +15 -0
  866. package/node_modules/skillflag/dist/core/paths.d.ts.map +1 -0
  867. package/node_modules/skillflag/dist/core/paths.js +149 -0
  868. package/node_modules/skillflag/dist/core/paths.js.map +1 -0
  869. package/node_modules/skillflag/dist/core/show.d.ts +2 -0
  870. package/node_modules/skillflag/dist/core/show.d.ts.map +1 -0
  871. package/node_modules/skillflag/dist/core/show.js +8 -0
  872. package/node_modules/skillflag/dist/core/show.js.map +1 -0
  873. package/node_modules/skillflag/dist/core/skillflag.d.ts +3 -0
  874. package/node_modules/skillflag/dist/core/skillflag.d.ts.map +1 -0
  875. package/node_modules/skillflag/dist/core/skillflag.js +2 -0
  876. package/node_modules/skillflag/dist/core/skillflag.js.map +1 -0
  877. package/node_modules/skillflag/dist/core/tar.d.ts +16 -0
  878. package/node_modules/skillflag/dist/core/tar.d.ts.map +1 -0
  879. package/node_modules/skillflag/dist/core/tar.js +140 -0
  880. package/node_modules/skillflag/dist/core/tar.js.map +1 -0
  881. package/node_modules/skillflag/dist/index.d.ts +5 -0
  882. package/node_modules/skillflag/dist/index.d.ts.map +1 -0
  883. package/node_modules/skillflag/dist/index.js +3 -0
  884. package/node_modules/skillflag/dist/index.js.map +1 -0
  885. package/node_modules/skillflag/dist/install/cli.d.ts +57 -0
  886. package/node_modules/skillflag/dist/install/cli.d.ts.map +1 -0
  887. package/node_modules/skillflag/dist/install/cli.js +653 -0
  888. package/node_modules/skillflag/dist/install/cli.js.map +1 -0
  889. package/node_modules/skillflag/dist/install/copy.d.ts +2 -0
  890. package/node_modules/skillflag/dist/install/copy.d.ts.map +1 -0
  891. package/node_modules/skillflag/dist/install/copy.js +19 -0
  892. package/node_modules/skillflag/dist/install/copy.js.map +1 -0
  893. package/node_modules/skillflag/dist/install/errors.d.ts +6 -0
  894. package/node_modules/skillflag/dist/install/errors.d.ts.map +1 -0
  895. package/node_modules/skillflag/dist/install/errors.js +13 -0
  896. package/node_modules/skillflag/dist/install/errors.js.map +1 -0
  897. package/node_modules/skillflag/dist/install/extract.d.ts +3 -0
  898. package/node_modules/skillflag/dist/install/extract.d.ts.map +1 -0
  899. package/node_modules/skillflag/dist/install/extract.js +76 -0
  900. package/node_modules/skillflag/dist/install/extract.js.map +1 -0
  901. package/node_modules/skillflag/dist/install/install.d.ts +21 -0
  902. package/node_modules/skillflag/dist/install/install.d.ts.map +1 -0
  903. package/node_modules/skillflag/dist/install/install.js +35 -0
  904. package/node_modules/skillflag/dist/install/install.js.map +1 -0
  905. package/node_modules/skillflag/dist/install/resolve.d.ts +12 -0
  906. package/node_modules/skillflag/dist/install/resolve.d.ts.map +1 -0
  907. package/node_modules/skillflag/dist/install/resolve.js +119 -0
  908. package/node_modules/skillflag/dist/install/resolve.js.map +1 -0
  909. package/node_modules/skillflag/dist/install/validate.d.ts +7 -0
  910. package/node_modules/skillflag/dist/install/validate.d.ts.map +1 -0
  911. package/node_modules/skillflag/dist/install/validate.js +28 -0
  912. package/node_modules/skillflag/dist/install/validate.js.map +1 -0
  913. package/node_modules/skillflag/dist/shared/frontmatter.d.ts +2 -0
  914. package/node_modules/skillflag/dist/shared/frontmatter.d.ts.map +1 -0
  915. package/node_modules/skillflag/dist/shared/frontmatter.js +28 -0
  916. package/node_modules/skillflag/dist/shared/frontmatter.js.map +1 -0
  917. package/node_modules/skillflag/dist/skillflag.d.ts +29 -0
  918. package/node_modules/skillflag/dist/skillflag.d.ts.map +1 -0
  919. package/node_modules/skillflag/dist/skillflag.js +230 -0
  920. package/node_modules/skillflag/dist/skillflag.js.map +1 -0
  921. package/node_modules/skillflag/dist/utils/collections.d.ts +3 -0
  922. package/node_modules/skillflag/dist/utils/collections.d.ts.map +1 -0
  923. package/node_modules/skillflag/dist/utils/collections.js +18 -0
  924. package/node_modules/skillflag/dist/utils/collections.js.map +1 -0
  925. package/node_modules/skillflag/package.json +78 -0
  926. package/node_modules/skillflag/skills/skillflag/SKILL.md +144 -0
  927. package/node_modules/streamx/LICENSE +21 -0
  928. package/node_modules/streamx/README.md +550 -0
  929. package/node_modules/streamx/index.d.ts +198 -0
  930. package/node_modules/streamx/index.js +1314 -0
  931. package/node_modules/streamx/lib/errors.d.ts +13 -0
  932. package/node_modules/streamx/lib/errors.js +47 -0
  933. package/node_modules/streamx/package.json +51 -0
  934. package/node_modules/streamx/runtime.d.ts +55 -0
  935. package/node_modules/tar-stream/LICENSE +21 -0
  936. package/node_modules/tar-stream/README.md +183 -0
  937. package/node_modules/tar-stream/constants.js +14 -0
  938. package/node_modules/tar-stream/extract.js +418 -0
  939. package/node_modules/tar-stream/headers.js +322 -0
  940. package/node_modules/tar-stream/index.d.ts +60 -0
  941. package/node_modules/tar-stream/index.js +2 -0
  942. package/node_modules/tar-stream/pack.js +287 -0
  943. package/node_modules/tar-stream/package.json +50 -0
  944. package/node_modules/teex/LICENSE +21 -0
  945. package/node_modules/teex/README.md +31 -0
  946. package/node_modules/teex/example.js +16 -0
  947. package/node_modules/teex/index.js +52 -0
  948. package/node_modules/teex/package.json +26 -0
  949. package/node_modules/teex/test.js +68 -0
  950. package/node_modules/text-decoder/LICENSE +201 -0
  951. package/node_modules/text-decoder/README.md +36 -0
  952. package/node_modules/text-decoder/index.js +64 -0
  953. package/node_modules/text-decoder/lib/pass-through-decoder.js +19 -0
  954. package/node_modules/text-decoder/lib/utf8-decoder.js +185 -0
  955. package/node_modules/text-decoder/package.json +39 -0
  956. package/node_modules/tsx/LICENSE +21 -0
  957. package/node_modules/tsx/README.md +32 -0
  958. package/node_modules/tsx/dist/cjs/api/index.cjs +2 -0
  959. package/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
  960. package/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
  961. package/node_modules/tsx/dist/cjs/api/index.mjs +2 -0
  962. package/node_modules/tsx/dist/cjs/index.cjs +1 -0
  963. package/node_modules/tsx/dist/cli.mjs +55 -0
  964. package/node_modules/tsx/dist/client-XItNFmsq.cjs +1 -0
  965. package/node_modules/tsx/dist/esm/api/index.cjs +2 -0
  966. package/node_modules/tsx/dist/esm/api/index.d.cts +37 -0
  967. package/node_modules/tsx/dist/esm/api/index.d.mts +37 -0
  968. package/node_modules/tsx/dist/esm/api/index.mjs +2 -0
  969. package/node_modules/tsx/dist/esm/index.mjs +2 -0
  970. package/node_modules/tsx/dist/index-Bqjv9TxC.mjs +1 -0
  971. package/node_modules/tsx/dist/index-DE3OBZuV.mjs +14 -0
  972. package/node_modules/tsx/dist/lexer-CGfpMDSb.cjs +3 -0
  973. package/node_modules/tsx/dist/lexer-CanA6ArK.mjs +3 -0
  974. package/node_modules/tsx/dist/loader.mjs +2 -0
  975. package/node_modules/tsx/dist/package-Dj0mHsMt.mjs +1 -0
  976. package/node_modules/tsx/dist/patch-repl.cjs +1 -0
  977. package/node_modules/tsx/dist/preflight.cjs +1 -0
  978. package/node_modules/tsx/dist/register-B1c7OH6V.cjs +23 -0
  979. package/node_modules/tsx/dist/register-nyXW-TH3.mjs +2 -0
  980. package/node_modules/tsx/dist/repl.mjs +4 -0
  981. package/node_modules/tsx/dist/require-CBjy4Foe.mjs +10 -0
  982. package/node_modules/tsx/dist/require-CVaYn9z3.cjs +1 -0
  983. package/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
  984. package/node_modules/tsx/dist/temporary-directory-Du7LpLp9.mjs +1 -0
  985. package/node_modules/tsx/package.json +70 -0
  986. package/node_modules/zod/LICENSE +21 -0
  987. package/node_modules/zod/README.md +218 -0
  988. package/node_modules/zod/compile.cjs +96 -0
  989. package/node_modules/zod/compile.d.cts +1 -0
  990. package/node_modules/zod/compile.d.ts +1 -0
  991. package/node_modules/zod/compile.js +52 -0
  992. package/node_modules/zod/index.cjs +52 -0
  993. package/node_modules/zod/index.d.cts +3 -0
  994. package/node_modules/zod/index.d.ts +3 -0
  995. package/node_modules/zod/index.js +7 -0
  996. package/node_modules/zod/locales/index.cjs +36 -0
  997. package/node_modules/zod/locales/index.d.cts +1 -0
  998. package/node_modules/zod/locales/index.d.ts +1 -0
  999. package/node_modules/zod/locales/index.js +1 -0
  1000. package/node_modules/zod/locales/package.json +7 -0
  1001. package/node_modules/zod/mini/index.cjs +51 -0
  1002. package/node_modules/zod/mini/index.d.cts +3 -0
  1003. package/node_modules/zod/mini/index.d.ts +3 -0
  1004. package/node_modules/zod/mini/index.js +3 -0
  1005. package/node_modules/zod/mini/package.json +7 -0
  1006. package/node_modules/zod/package.json +147 -0
  1007. package/node_modules/zod/src/compile.ts +56 -0
  1008. package/node_modules/zod/src/index.ts +7 -0
  1009. package/node_modules/zod/src/locales/index.ts +1 -0
  1010. package/node_modules/zod/src/mini/index.ts +3 -0
  1011. package/node_modules/zod/src/v3/ZodError.ts +340 -0
  1012. package/node_modules/zod/src/v3/benchmarks/datetime.ts +58 -0
  1013. package/node_modules/zod/src/v3/benchmarks/discriminatedUnion.ts +80 -0
  1014. package/node_modules/zod/src/v3/benchmarks/index.ts +59 -0
  1015. package/node_modules/zod/src/v3/benchmarks/ipv4.ts +57 -0
  1016. package/node_modules/zod/src/v3/benchmarks/object.ts +69 -0
  1017. package/node_modules/zod/src/v3/benchmarks/primitives.ts +162 -0
  1018. package/node_modules/zod/src/v3/benchmarks/realworld.ts +63 -0
  1019. package/node_modules/zod/src/v3/benchmarks/string.ts +55 -0
  1020. package/node_modules/zod/src/v3/benchmarks/union.ts +80 -0
  1021. package/node_modules/zod/src/v3/errors.ts +13 -0
  1022. package/node_modules/zod/src/v3/external.ts +6 -0
  1023. package/node_modules/zod/src/v3/helpers/enumUtil.ts +17 -0
  1024. package/node_modules/zod/src/v3/helpers/errorUtil.ts +8 -0
  1025. package/node_modules/zod/src/v3/helpers/parseUtil.ts +176 -0
  1026. package/node_modules/zod/src/v3/helpers/partialUtil.ts +34 -0
  1027. package/node_modules/zod/src/v3/helpers/typeAliases.ts +2 -0
  1028. package/node_modules/zod/src/v3/helpers/util.ts +224 -0
  1029. package/node_modules/zod/src/v3/index.ts +4 -0
  1030. package/node_modules/zod/src/v3/locales/en.ts +124 -0
  1031. package/node_modules/zod/src/v3/standard-schema.ts +113 -0
  1032. package/node_modules/zod/src/v3/tests/Mocker.ts +54 -0
  1033. package/node_modules/zod/src/v3/tests/all-errors.test.ts +157 -0
  1034. package/node_modules/zod/src/v3/tests/anyunknown.test.ts +28 -0
  1035. package/node_modules/zod/src/v3/tests/array.test.ts +71 -0
  1036. package/node_modules/zod/src/v3/tests/async-parsing.test.ts +388 -0
  1037. package/node_modules/zod/src/v3/tests/async-refinements.test.ts +46 -0
  1038. package/node_modules/zod/src/v3/tests/base.test.ts +29 -0
  1039. package/node_modules/zod/src/v3/tests/bigint.test.ts +55 -0
  1040. package/node_modules/zod/src/v3/tests/branded.test.ts +53 -0
  1041. package/node_modules/zod/src/v3/tests/catch.test.ts +220 -0
  1042. package/node_modules/zod/src/v3/tests/coerce.test.ts +133 -0
  1043. package/node_modules/zod/src/v3/tests/complex.test.ts +70 -0
  1044. package/node_modules/zod/src/v3/tests/custom.test.ts +31 -0
  1045. package/node_modules/zod/src/v3/tests/date.test.ts +32 -0
  1046. package/node_modules/zod/src/v3/tests/deepmasking.test.ts +186 -0
  1047. package/node_modules/zod/src/v3/tests/default.test.ts +112 -0
  1048. package/node_modules/zod/src/v3/tests/description.test.ts +33 -0
  1049. package/node_modules/zod/src/v3/tests/discriminated-unions.test.ts +315 -0
  1050. package/node_modules/zod/src/v3/tests/enum.test.ts +80 -0
  1051. package/node_modules/zod/src/v3/tests/error.test.ts +586 -0
  1052. package/node_modules/zod/src/v3/tests/firstparty.test.ts +87 -0
  1053. package/node_modules/zod/src/v3/tests/firstpartyschematypes.test.ts +21 -0
  1054. package/node_modules/zod/src/v3/tests/function.test.ts +261 -0
  1055. package/node_modules/zod/src/v3/tests/generics.test.ts +48 -0
  1056. package/node_modules/zod/src/v3/tests/instanceof.test.ts +37 -0
  1057. package/node_modules/zod/src/v3/tests/intersection.test.ts +128 -0
  1058. package/node_modules/zod/src/v3/tests/language-server.source.ts +76 -0
  1059. package/node_modules/zod/src/v3/tests/language-server.test.ts +206 -0
  1060. package/node_modules/zod/src/v3/tests/literal.test.ts +36 -0
  1061. package/node_modules/zod/src/v3/tests/map.test.ts +110 -0
  1062. package/node_modules/zod/src/v3/tests/masking.test.ts +4 -0
  1063. package/node_modules/zod/src/v3/tests/mocker.test.ts +19 -0
  1064. package/node_modules/zod/src/v3/tests/nan.test.ts +24 -0
  1065. package/node_modules/zod/src/v3/tests/nativeEnum.test.ts +87 -0
  1066. package/node_modules/zod/src/v3/tests/nullable.test.ts +42 -0
  1067. package/node_modules/zod/src/v3/tests/number.test.ts +176 -0
  1068. package/node_modules/zod/src/v3/tests/object-augmentation.test.ts +29 -0
  1069. package/node_modules/zod/src/v3/tests/object-in-es5-env.test.ts +29 -0
  1070. package/node_modules/zod/src/v3/tests/object.test.ts +431 -0
  1071. package/node_modules/zod/src/v3/tests/optional.test.ts +42 -0
  1072. package/node_modules/zod/src/v3/tests/parseUtil.test.ts +23 -0
  1073. package/node_modules/zod/src/v3/tests/parser.test.ts +41 -0
  1074. package/node_modules/zod/src/v3/tests/partials.test.ts +243 -0
  1075. package/node_modules/zod/src/v3/tests/pickomit.test.ts +111 -0
  1076. package/node_modules/zod/src/v3/tests/pipeline.test.ts +29 -0
  1077. package/node_modules/zod/src/v3/tests/preprocess.test.ts +186 -0
  1078. package/node_modules/zod/src/v3/tests/primitive.test.ts +440 -0
  1079. package/node_modules/zod/src/v3/tests/promise.test.ts +90 -0
  1080. package/node_modules/zod/src/v3/tests/readonly.test.ts +114 -0
  1081. package/node_modules/zod/src/v3/tests/record.test.ts +171 -0
  1082. package/node_modules/zod/src/v3/tests/recursive.test.ts +197 -0
  1083. package/node_modules/zod/src/v3/tests/refine.test.ts +313 -0
  1084. package/node_modules/zod/src/v3/tests/safeparse.test.ts +27 -0
  1085. package/node_modules/zod/src/v3/tests/set.test.ts +142 -0
  1086. package/node_modules/zod/src/v3/tests/standard-schema.test.ts +83 -0
  1087. package/node_modules/zod/src/v3/tests/string.test.ts +922 -0
  1088. package/node_modules/zod/src/v3/tests/transformer.test.ts +233 -0
  1089. package/node_modules/zod/src/v3/tests/tuple.test.ts +90 -0
  1090. package/node_modules/zod/src/v3/tests/unions.test.ts +57 -0
  1091. package/node_modules/zod/src/v3/tests/validations.test.ts +133 -0
  1092. package/node_modules/zod/src/v3/tests/void.test.ts +15 -0
  1093. package/node_modules/zod/src/v3/types.ts +5138 -0
  1094. package/node_modules/zod/src/v4/classic/checks.ts +33 -0
  1095. package/node_modules/zod/src/v4/classic/coerce.ts +27 -0
  1096. package/node_modules/zod/src/v4/classic/compat.ts +78 -0
  1097. package/node_modules/zod/src/v4/classic/deep-partial.ts +76 -0
  1098. package/node_modules/zod/src/v4/classic/errors.ts +97 -0
  1099. package/node_modules/zod/src/v4/classic/external.ts +57 -0
  1100. package/node_modules/zod/src/v4/classic/from-json-schema.ts +944 -0
  1101. package/node_modules/zod/src/v4/classic/in-out.ts +45 -0
  1102. package/node_modules/zod/src/v4/classic/index.ts +5 -0
  1103. package/node_modules/zod/src/v4/classic/iso.ts +20 -0
  1104. package/node_modules/zod/src/v4/classic/parse.ts +88 -0
  1105. package/node_modules/zod/src/v4/classic/schemas.ts +2937 -0
  1106. package/node_modules/zod/src/v4/classic/tests/anyunknown.test.ts +26 -0
  1107. package/node_modules/zod/src/v4/classic/tests/apply.test.ts +86 -0
  1108. package/node_modules/zod/src/v4/classic/tests/array.test.ts +264 -0
  1109. package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +402 -0
  1110. package/node_modules/zod/src/v4/classic/tests/async-parsing.test.ts +381 -0
  1111. package/node_modules/zod/src/v4/classic/tests/async-refinements.test.ts +68 -0
  1112. package/node_modules/zod/src/v4/classic/tests/base.test.ts +7 -0
  1113. package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +89 -0
  1114. package/node_modules/zod/src/v4/classic/tests/brand.test.ts +106 -0
  1115. package/node_modules/zod/src/v4/classic/tests/catch.test.ts +604 -0
  1116. package/node_modules/zod/src/v4/classic/tests/coalesce.test.ts +20 -0
  1117. package/node_modules/zod/src/v4/classic/tests/codec-examples.test.ts +572 -0
  1118. package/node_modules/zod/src/v4/classic/tests/codec.test.ts +704 -0
  1119. package/node_modules/zod/src/v4/classic/tests/coerce.test.ts +160 -0
  1120. package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +374 -0
  1121. package/node_modules/zod/src/v4/classic/tests/credit-card.test.ts +76 -0
  1122. package/node_modules/zod/src/v4/classic/tests/custom.test.ts +40 -0
  1123. package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +985 -0
  1124. package/node_modules/zod/src/v4/classic/tests/date.test.ts +92 -0
  1125. package/node_modules/zod/src/v4/classic/tests/datetime.test.ts +316 -0
  1126. package/node_modules/zod/src/v4/classic/tests/deep-partial.test.ts +380 -0
  1127. package/node_modules/zod/src/v4/classic/tests/default-locale.test.ts +32 -0
  1128. package/node_modules/zod/src/v4/classic/tests/default.test.ts +455 -0
  1129. package/node_modules/zod/src/v4/classic/tests/describe-meta-checks.test.ts +27 -0
  1130. package/node_modules/zod/src/v4/classic/tests/description.test.ts +32 -0
  1131. package/node_modules/zod/src/v4/classic/tests/detached-methods.test.ts +221 -0
  1132. package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +942 -0
  1133. package/node_modules/zod/src/v4/classic/tests/enum.test.ts +305 -0
  1134. package/node_modules/zod/src/v4/classic/tests/error-utils.test.ts +917 -0
  1135. package/node_modules/zod/src/v4/classic/tests/error.test.ts +1108 -0
  1136. package/node_modules/zod/src/v4/classic/tests/file.test.ts +96 -0
  1137. package/node_modules/zod/src/v4/classic/tests/firstparty.test.ts +179 -0
  1138. package/node_modules/zod/src/v4/classic/tests/fix-json-issue.test.ts +26 -0
  1139. package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +1467 -0
  1140. package/node_modules/zod/src/v4/classic/tests/function.test.ts +391 -0
  1141. package/node_modules/zod/src/v4/classic/tests/generics.test.ts +72 -0
  1142. package/node_modules/zod/src/v4/classic/tests/global-config.test.ts +32 -0
  1143. package/node_modules/zod/src/v4/classic/tests/hash.test.ts +67 -0
  1144. package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
  1145. package/node_modules/zod/src/v4/classic/tests/in-out.test.ts +159 -0
  1146. package/node_modules/zod/src/v4/classic/tests/index.test.ts +958 -0
  1147. package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +319 -0
  1148. package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +127 -0
  1149. package/node_modules/zod/src/v4/classic/tests/intersection.test.ts +334 -0
  1150. package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +215 -0
  1151. package/node_modules/zod/src/v4/classic/tests/jitless-allows-eval.test.ts +39 -0
  1152. package/node_modules/zod/src/v4/classic/tests/json.test.ts +109 -0
  1153. package/node_modules/zod/src/v4/classic/tests/lazy.test.ts +262 -0
  1154. package/node_modules/zod/src/v4/classic/tests/literal.test.ts +128 -0
  1155. package/node_modules/zod/src/v4/classic/tests/locales_ka.test.ts +29 -0
  1156. package/node_modules/zod/src/v4/classic/tests/locales_ro.test.ts +24 -0
  1157. package/node_modules/zod/src/v4/classic/tests/map.test.ts +342 -0
  1158. package/node_modules/zod/src/v4/classic/tests/nan.test.ts +21 -0
  1159. package/node_modules/zod/src/v4/classic/tests/nested-refine.test.ts +168 -0
  1160. package/node_modules/zod/src/v4/classic/tests/nonoptional.test.ts +101 -0
  1161. package/node_modules/zod/src/v4/classic/tests/nullable.test.ts +22 -0
  1162. package/node_modules/zod/src/v4/classic/tests/number.test.ts +401 -0
  1163. package/node_modules/zod/src/v4/classic/tests/object.test.ts +1006 -0
  1164. package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +235 -0
  1165. package/node_modules/zod/src/v4/classic/tests/optional.test.ts +366 -0
  1166. package/node_modules/zod/src/v4/classic/tests/partial.test.ts +497 -0
  1167. package/node_modules/zod/src/v4/classic/tests/pickomit.test.ts +211 -0
  1168. package/node_modules/zod/src/v4/classic/tests/pipe.test.ts +101 -0
  1169. package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +144 -0
  1170. package/node_modules/zod/src/v4/classic/tests/preprocess-types.test.ts +44 -0
  1171. package/node_modules/zod/src/v4/classic/tests/preprocess.test.ts +351 -0
  1172. package/node_modules/zod/src/v4/classic/tests/primitive.test.ts +175 -0
  1173. package/node_modules/zod/src/v4/classic/tests/promise.test.ts +81 -0
  1174. package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
  1175. package/node_modules/zod/src/v4/classic/tests/prototypes.test.ts +23 -0
  1176. package/node_modules/zod/src/v4/classic/tests/readonly.test.ts +184 -0
  1177. package/node_modules/zod/src/v4/classic/tests/record.test.ts +885 -0
  1178. package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +707 -0
  1179. package/node_modules/zod/src/v4/classic/tests/redos.test.ts +68 -0
  1180. package/node_modules/zod/src/v4/classic/tests/refine.test.ts +693 -0
  1181. package/node_modules/zod/src/v4/classic/tests/registries.test.ts +243 -0
  1182. package/node_modules/zod/src/v4/classic/tests/set.test.ts +190 -0
  1183. package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +153 -0
  1184. package/node_modules/zod/src/v4/classic/tests/string-formats.test.ts +125 -0
  1185. package/node_modules/zod/src/v4/classic/tests/string.test.ts +1440 -0
  1186. package/node_modules/zod/src/v4/classic/tests/stringbool.test.ts +124 -0
  1187. package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +812 -0
  1188. package/node_modules/zod/src/v4/classic/tests/to-json-schema-methods.test.ts +438 -0
  1189. package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +4733 -0
  1190. package/node_modules/zod/src/v4/classic/tests/transform.test.ts +378 -0
  1191. package/node_modules/zod/src/v4/classic/tests/tuple.test.ts +511 -0
  1192. package/node_modules/zod/src/v4/classic/tests/union.test.ts +310 -0
  1193. package/node_modules/zod/src/v4/classic/tests/url.test.ts +13 -0
  1194. package/node_modules/zod/src/v4/classic/tests/validate.test.ts +645 -0
  1195. package/node_modules/zod/src/v4/classic/tests/validations.test.ts +283 -0
  1196. package/node_modules/zod/src/v4/classic/tests/void.test.ts +12 -0
  1197. package/node_modules/zod/src/v4/core/api.ts +1875 -0
  1198. package/node_modules/zod/src/v4/core/checks.ts +1266 -0
  1199. package/node_modules/zod/src/v4/core/compile.ts +2313 -0
  1200. package/node_modules/zod/src/v4/core/core.ts +230 -0
  1201. package/node_modules/zod/src/v4/core/doc.ts +52 -0
  1202. package/node_modules/zod/src/v4/core/errors.ts +543 -0
  1203. package/node_modules/zod/src/v4/core/index.ts +19 -0
  1204. package/node_modules/zod/src/v4/core/json-schema-generator.ts +131 -0
  1205. package/node_modules/zod/src/v4/core/json-schema-processors.ts +934 -0
  1206. package/node_modules/zod/src/v4/core/json-schema.ts +148 -0
  1207. package/node_modules/zod/src/v4/core/memoizer.ts +331 -0
  1208. package/node_modules/zod/src/v4/core/parse.ts +309 -0
  1209. package/node_modules/zod/src/v4/core/regexes.ts +217 -0
  1210. package/node_modules/zod/src/v4/core/registries.ts +105 -0
  1211. package/node_modules/zod/src/v4/core/schemas.ts +5192 -0
  1212. package/node_modules/zod/src/v4/core/standard-schema.ts +159 -0
  1213. package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
  1214. package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +1004 -0
  1215. package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
  1216. package/node_modules/zod/src/v4/core/tests/compile-global.test.ts +89 -0
  1217. package/node_modules/zod/src/v4/core/tests/compile-off-by-default.test.ts +26 -0
  1218. package/node_modules/zod/src/v4/core/tests/compile-stats.test.ts +34 -0
  1219. package/node_modules/zod/src/v4/core/tests/compile.test.ts +2036 -0
  1220. package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
  1221. package/node_modules/zod/src/v4/core/tests/extend.test.ts +59 -0
  1222. package/node_modules/zod/src/v4/core/tests/index.test.ts +46 -0
  1223. package/node_modules/zod/src/v4/core/tests/locales/be.test.ts +124 -0
  1224. package/node_modules/zod/src/v4/core/tests/locales/el.test.ts +214 -0
  1225. package/node_modules/zod/src/v4/core/tests/locales/en.test.ts +22 -0
  1226. package/node_modules/zod/src/v4/core/tests/locales/es.test.ts +180 -0
  1227. package/node_modules/zod/src/v4/core/tests/locales/fr.test.ts +72 -0
  1228. package/node_modules/zod/src/v4/core/tests/locales/he.test.ts +379 -0
  1229. package/node_modules/zod/src/v4/core/tests/locales/hr.test.ts +163 -0
  1230. package/node_modules/zod/src/v4/core/tests/locales/nl.test.ts +46 -0
  1231. package/node_modules/zod/src/v4/core/tests/locales/parity.test.ts +42 -0
  1232. package/node_modules/zod/src/v4/core/tests/locales/ru.test.ts +128 -0
  1233. package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
  1234. package/node_modules/zod/src/v4/core/tests/locales/tk.test.ts +69 -0
  1235. package/node_modules/zod/src/v4/core/tests/locales/tr.test.ts +69 -0
  1236. package/node_modules/zod/src/v4/core/tests/locales/uz.test.ts +105 -0
  1237. package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
  1238. package/node_modules/zod/src/v4/core/tests/record-constructor.test.ts +125 -0
  1239. package/node_modules/zod/src/v4/core/tests/recursive-tuples.test.ts +45 -0
  1240. package/node_modules/zod/src/v4/core/tests/treeshake.test.ts +51 -0
  1241. package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +133 -0
  1242. package/node_modules/zod/src/v4/core/to-json-schema.ts +857 -0
  1243. package/node_modules/zod/src/v4/core/util.ts +1280 -0
  1244. package/node_modules/zod/src/v4/core/versions.ts +5 -0
  1245. package/node_modules/zod/src/v4/core/visit.ts +193 -0
  1246. package/node_modules/zod/src/v4/core/zsf.ts +323 -0
  1247. package/node_modules/zod/src/v4/index.ts +4 -0
  1248. package/node_modules/zod/src/v4/locales/ar.ts +120 -0
  1249. package/node_modules/zod/src/v4/locales/az.ts +116 -0
  1250. package/node_modules/zod/src/v4/locales/be.ts +188 -0
  1251. package/node_modules/zod/src/v4/locales/bg.ts +133 -0
  1252. package/node_modules/zod/src/v4/locales/bn.ts +123 -0
  1253. package/node_modules/zod/src/v4/locales/ca.ts +121 -0
  1254. package/node_modules/zod/src/v4/locales/ckb.ts +140 -0
  1255. package/node_modules/zod/src/v4/locales/cs.ts +123 -0
  1256. package/node_modules/zod/src/v4/locales/da.ts +128 -0
  1257. package/node_modules/zod/src/v4/locales/de.ts +121 -0
  1258. package/node_modules/zod/src/v4/locales/el.ts +124 -0
  1259. package/node_modules/zod/src/v4/locales/en.ts +136 -0
  1260. package/node_modules/zod/src/v4/locales/eo.ts +123 -0
  1261. package/node_modules/zod/src/v4/locales/es.ts +145 -0
  1262. package/node_modules/zod/src/v4/locales/fa.ts +131 -0
  1263. package/node_modules/zod/src/v4/locales/fi.ts +126 -0
  1264. package/node_modules/zod/src/v4/locales/fr-CA.ts +121 -0
  1265. package/node_modules/zod/src/v4/locales/fr.ts +139 -0
  1266. package/node_modules/zod/src/v4/locales/gu.ts +123 -0
  1267. package/node_modules/zod/src/v4/locales/he.ts +253 -0
  1268. package/node_modules/zod/src/v4/locales/hi.ts +126 -0
  1269. package/node_modules/zod/src/v4/locales/hr.ts +135 -0
  1270. package/node_modules/zod/src/v4/locales/hu.ts +122 -0
  1271. package/node_modules/zod/src/v4/locales/hy.ts +175 -0
  1272. package/node_modules/zod/src/v4/locales/id.ts +120 -0
  1273. package/node_modules/zod/src/v4/locales/index.ts +63 -0
  1274. package/node_modules/zod/src/v4/locales/is.ts +124 -0
  1275. package/node_modules/zod/src/v4/locales/it.ts +121 -0
  1276. package/node_modules/zod/src/v4/locales/ja.ts +119 -0
  1277. package/node_modules/zod/src/v4/locales/ka.ts +128 -0
  1278. package/node_modules/zod/src/v4/locales/kh.ts +7 -0
  1279. package/node_modules/zod/src/v4/locales/km.ts +124 -0
  1280. package/node_modules/zod/src/v4/locales/kn.ts +126 -0
  1281. package/node_modules/zod/src/v4/locales/ko.ts +126 -0
  1282. package/node_modules/zod/src/v4/locales/lt.ts +243 -0
  1283. package/node_modules/zod/src/v4/locales/mk.ts +123 -0
  1284. package/node_modules/zod/src/v4/locales/ms.ts +120 -0
  1285. package/node_modules/zod/src/v4/locales/ne.ts +128 -0
  1286. package/node_modules/zod/src/v4/locales/nl.ts +126 -0
  1287. package/node_modules/zod/src/v4/locales/nn.ts +121 -0
  1288. package/node_modules/zod/src/v4/locales/no.ts +121 -0
  1289. package/node_modules/zod/src/v4/locales/ota.ts +122 -0
  1290. package/node_modules/zod/src/v4/locales/pl.ts +123 -0
  1291. package/node_modules/zod/src/v4/locales/ps.ts +131 -0
  1292. package/node_modules/zod/src/v4/locales/pt-BR.ts +161 -0
  1293. package/node_modules/zod/src/v4/locales/pt.ts +159 -0
  1294. package/node_modules/zod/src/v4/locales/ro.ts +132 -0
  1295. package/node_modules/zod/src/v4/locales/ru.ts +188 -0
  1296. package/node_modules/zod/src/v4/locales/sk.ts +123 -0
  1297. package/node_modules/zod/src/v4/locales/sl.ts +123 -0
  1298. package/node_modules/zod/src/v4/locales/sv.ts +124 -0
  1299. package/node_modules/zod/src/v4/locales/ta.ts +123 -0
  1300. package/node_modules/zod/src/v4/locales/tg.ts +135 -0
  1301. package/node_modules/zod/src/v4/locales/th.ts +124 -0
  1302. package/node_modules/zod/src/v4/locales/tk.ts +113 -0
  1303. package/node_modules/zod/src/v4/locales/tr.ts +116 -0
  1304. package/node_modules/zod/src/v4/locales/ua.ts +7 -0
  1305. package/node_modules/zod/src/v4/locales/uk.ts +122 -0
  1306. package/node_modules/zod/src/v4/locales/ur.ts +124 -0
  1307. package/node_modules/zod/src/v4/locales/uz.ts +120 -0
  1308. package/node_modules/zod/src/v4/locales/vi.ts +122 -0
  1309. package/node_modules/zod/src/v4/locales/yo.ts +129 -0
  1310. package/node_modules/zod/src/v4/locales/zh-CN.ts +121 -0
  1311. package/node_modules/zod/src/v4/locales/zh-TW.ts +120 -0
  1312. package/node_modules/zod/src/v4/mini/checks.ts +33 -0
  1313. package/node_modules/zod/src/v4/mini/coerce.ts +27 -0
  1314. package/node_modules/zod/src/v4/mini/deep-partial.ts +73 -0
  1315. package/node_modules/zod/src/v4/mini/external.ts +52 -0
  1316. package/node_modules/zod/src/v4/mini/in-out.ts +45 -0
  1317. package/node_modules/zod/src/v4/mini/index.ts +3 -0
  1318. package/node_modules/zod/src/v4/mini/iso.ts +66 -0
  1319. package/node_modules/zod/src/v4/mini/parse.ts +16 -0
  1320. package/node_modules/zod/src/v4/mini/schemas.ts +2051 -0
  1321. package/node_modules/zod/src/v4/mini/tests/apply.test.ts +57 -0
  1322. package/node_modules/zod/src/v4/mini/tests/assignability.test.ts +190 -0
  1323. package/node_modules/zod/src/v4/mini/tests/brand.test.ts +94 -0
  1324. package/node_modules/zod/src/v4/mini/tests/checks.test.ts +158 -0
  1325. package/node_modules/zod/src/v4/mini/tests/codec.test.ts +582 -0
  1326. package/node_modules/zod/src/v4/mini/tests/computed.test.ts +30 -0
  1327. package/node_modules/zod/src/v4/mini/tests/cyclic-data.test.ts +84 -0
  1328. package/node_modules/zod/src/v4/mini/tests/deep-partial.test.ts +54 -0
  1329. package/node_modules/zod/src/v4/mini/tests/error.test.ts +41 -0
  1330. package/node_modules/zod/src/v4/mini/tests/functions.test.ts +14 -0
  1331. package/node_modules/zod/src/v4/mini/tests/index.test.ts +1146 -0
  1332. package/node_modules/zod/src/v4/mini/tests/number.test.ts +109 -0
  1333. package/node_modules/zod/src/v4/mini/tests/object.test.ts +292 -0
  1334. package/node_modules/zod/src/v4/mini/tests/prototypes.test.ts +43 -0
  1335. package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +341 -0
  1336. package/node_modules/zod/src/v4/mini/tests/standard-schema.test.ts +50 -0
  1337. package/node_modules/zod/src/v4/mini/tests/string.test.ts +396 -0
  1338. package/node_modules/zod/src/v4-mini/index.ts +3 -0
  1339. package/node_modules/zod/v3/ZodError.cjs +167 -0
  1340. package/node_modules/zod/v3/ZodError.d.cts +164 -0
  1341. package/node_modules/zod/v3/ZodError.d.ts +164 -0
  1342. package/node_modules/zod/v3/ZodError.js +143 -0
  1343. package/node_modules/zod/v3/errors.cjs +36 -0
  1344. package/node_modules/zod/v3/errors.d.cts +5 -0
  1345. package/node_modules/zod/v3/errors.d.ts +5 -0
  1346. package/node_modules/zod/v3/errors.js +9 -0
  1347. package/node_modules/zod/v3/external.cjs +41 -0
  1348. package/node_modules/zod/v3/external.d.cts +6 -0
  1349. package/node_modules/zod/v3/external.d.ts +6 -0
  1350. package/node_modules/zod/v3/external.js +6 -0
  1351. package/node_modules/zod/v3/helpers/enumUtil.cjs +21 -0
  1352. package/node_modules/zod/v3/helpers/enumUtil.d.cts +8 -0
  1353. package/node_modules/zod/v3/helpers/enumUtil.d.ts +8 -0
  1354. package/node_modules/zod/v3/helpers/enumUtil.js +1 -0
  1355. package/node_modules/zod/v3/helpers/errorUtil.cjs +28 -0
  1356. package/node_modules/zod/v3/helpers/errorUtil.d.cts +9 -0
  1357. package/node_modules/zod/v3/helpers/errorUtil.d.ts +9 -0
  1358. package/node_modules/zod/v3/helpers/errorUtil.js +6 -0
  1359. package/node_modules/zod/v3/helpers/parseUtil.cjs +143 -0
  1360. package/node_modules/zod/v3/helpers/parseUtil.d.cts +78 -0
  1361. package/node_modules/zod/v3/helpers/parseUtil.d.ts +78 -0
  1362. package/node_modules/zod/v3/helpers/parseUtil.js +109 -0
  1363. package/node_modules/zod/v3/helpers/partialUtil.cjs +21 -0
  1364. package/node_modules/zod/v3/helpers/partialUtil.d.cts +8 -0
  1365. package/node_modules/zod/v3/helpers/partialUtil.d.ts +8 -0
  1366. package/node_modules/zod/v3/helpers/partialUtil.js +1 -0
  1367. package/node_modules/zod/v3/helpers/typeAliases.cjs +21 -0
  1368. package/node_modules/zod/v3/helpers/typeAliases.d.cts +2 -0
  1369. package/node_modules/zod/v3/helpers/typeAliases.d.ts +2 -0
  1370. package/node_modules/zod/v3/helpers/typeAliases.js +1 -0
  1371. package/node_modules/zod/v3/helpers/util.cjs +156 -0
  1372. package/node_modules/zod/v3/helpers/util.d.cts +85 -0
  1373. package/node_modules/zod/v3/helpers/util.d.ts +85 -0
  1374. package/node_modules/zod/v3/helpers/util.js +133 -0
  1375. package/node_modules/zod/v3/index.cjs +52 -0
  1376. package/node_modules/zod/v3/index.d.cts +4 -0
  1377. package/node_modules/zod/v3/index.d.ts +4 -0
  1378. package/node_modules/zod/v3/index.js +4 -0
  1379. package/node_modules/zod/v3/locales/en.cjs +112 -0
  1380. package/node_modules/zod/v3/locales/en.d.cts +3 -0
  1381. package/node_modules/zod/v3/locales/en.d.ts +3 -0
  1382. package/node_modules/zod/v3/locales/en.js +109 -0
  1383. package/node_modules/zod/v3/package.json +7 -0
  1384. package/node_modules/zod/v3/standard-schema.cjs +21 -0
  1385. package/node_modules/zod/v3/standard-schema.d.cts +102 -0
  1386. package/node_modules/zod/v3/standard-schema.d.ts +102 -0
  1387. package/node_modules/zod/v3/standard-schema.js +1 -0
  1388. package/node_modules/zod/v3/types.cjs +3798 -0
  1389. package/node_modules/zod/v3/types.d.cts +1034 -0
  1390. package/node_modules/zod/v3/types.d.ts +1034 -0
  1391. package/node_modules/zod/v3/types.js +3697 -0
  1392. package/node_modules/zod/v4/classic/checks.cjs +53 -0
  1393. package/node_modules/zod/v4/classic/checks.d.cts +1 -0
  1394. package/node_modules/zod/v4/classic/checks.d.ts +1 -0
  1395. package/node_modules/zod/v4/classic/checks.js +1 -0
  1396. package/node_modules/zod/v4/classic/coerce.cjs +66 -0
  1397. package/node_modules/zod/v4/classic/coerce.d.cts +17 -0
  1398. package/node_modules/zod/v4/classic/coerce.d.ts +17 -0
  1399. package/node_modules/zod/v4/classic/coerce.js +17 -0
  1400. package/node_modules/zod/v4/classic/compat.cjs +80 -0
  1401. package/node_modules/zod/v4/classic/compat.d.cts +48 -0
  1402. package/node_modules/zod/v4/classic/compat.d.ts +48 -0
  1403. package/node_modules/zod/v4/classic/compat.js +31 -0
  1404. package/node_modules/zod/v4/classic/deep-partial.cjs +58 -0
  1405. package/node_modules/zod/v4/classic/deep-partial.d.cts +14 -0
  1406. package/node_modules/zod/v4/classic/deep-partial.d.ts +14 -0
  1407. package/node_modules/zod/v4/classic/deep-partial.js +13 -0
  1408. package/node_modules/zod/v4/classic/errors.cjs +102 -0
  1409. package/node_modules/zod/v4/classic/errors.d.cts +29 -0
  1410. package/node_modules/zod/v4/classic/errors.d.ts +29 -0
  1411. package/node_modules/zod/v4/classic/errors.js +57 -0
  1412. package/node_modules/zod/v4/classic/external.cjs +97 -0
  1413. package/node_modules/zod/v4/classic/external.d.cts +19 -0
  1414. package/node_modules/zod/v4/classic/external.d.ts +19 -0
  1415. package/node_modules/zod/v4/classic/external.js +17 -0
  1416. package/node_modules/zod/v4/classic/from-json-schema.cjs +926 -0
  1417. package/node_modules/zod/v4/classic/from-json-schema.d.cts +12 -0
  1418. package/node_modules/zod/v4/classic/from-json-schema.d.ts +12 -0
  1419. package/node_modules/zod/v4/classic/from-json-schema.js +881 -0
  1420. package/node_modules/zod/v4/classic/in-out.cjs +82 -0
  1421. package/node_modules/zod/v4/classic/in-out.d.cts +8 -0
  1422. package/node_modules/zod/v4/classic/in-out.d.ts +8 -0
  1423. package/node_modules/zod/v4/classic/in-out.js +36 -0
  1424. package/node_modules/zod/v4/classic/index.cjs +52 -0
  1425. package/node_modules/zod/v4/classic/index.d.cts +3 -0
  1426. package/node_modules/zod/v4/classic/index.d.ts +3 -0
  1427. package/node_modules/zod/v4/classic/index.js +4 -0
  1428. package/node_modules/zod/v4/classic/iso.cjs +68 -0
  1429. package/node_modules/zod/v4/classic/iso.d.cts +7 -0
  1430. package/node_modules/zod/v4/classic/iso.d.ts +7 -0
  1431. package/node_modules/zod/v4/classic/iso.js +15 -0
  1432. package/node_modules/zod/v4/classic/package.json +7 -0
  1433. package/node_modules/zod/v4/classic/parse.cjs +63 -0
  1434. package/node_modules/zod/v4/classic/parse.d.cts +44 -0
  1435. package/node_modules/zod/v4/classic/parse.d.ts +44 -0
  1436. package/node_modules/zod/v4/classic/parse.js +16 -0
  1437. package/node_modules/zod/v4/classic/schemas.cjs +1676 -0
  1438. package/node_modules/zod/v4/classic/schemas.d.cts +816 -0
  1439. package/node_modules/zod/v4/classic/schemas.d.ts +816 -0
  1440. package/node_modules/zod/v4/classic/schemas.js +1535 -0
  1441. package/node_modules/zod/v4/core/api.cjs +1269 -0
  1442. package/node_modules/zod/v4/core/api.d.cts +335 -0
  1443. package/node_modules/zod/v4/core/api.d.ts +335 -0
  1444. package/node_modules/zod/v4/core/api.js +1107 -0
  1445. package/node_modules/zod/v4/core/checks.cjs +561 -0
  1446. package/node_modules/zod/v4/core/checks.d.cts +294 -0
  1447. package/node_modules/zod/v4/core/checks.d.ts +294 -0
  1448. package/node_modules/zod/v4/core/checks.js +516 -0
  1449. package/node_modules/zod/v4/core/compile.cjs +1908 -0
  1450. package/node_modules/zod/v4/core/compile.d.cts +65 -0
  1451. package/node_modules/zod/v4/core/compile.d.ts +65 -0
  1452. package/node_modules/zod/v4/core/compile.js +1858 -0
  1453. package/node_modules/zod/v4/core/core.cjs +167 -0
  1454. package/node_modules/zod/v4/core/core.d.cts +82 -0
  1455. package/node_modules/zod/v4/core/core.d.ts +82 -0
  1456. package/node_modules/zod/v4/core/core.js +141 -0
  1457. package/node_modules/zod/v4/core/doc.cjs +61 -0
  1458. package/node_modules/zod/v4/core/doc.d.cts +16 -0
  1459. package/node_modules/zod/v4/core/doc.d.ts +16 -0
  1460. package/node_modules/zod/v4/core/doc.js +38 -0
  1461. package/node_modules/zod/v4/core/errors.cjs +317 -0
  1462. package/node_modules/zod/v4/core/errors.d.cts +225 -0
  1463. package/node_modules/zod/v4/core/errors.d.ts +225 -0
  1464. package/node_modules/zod/v4/core/errors.js +267 -0
  1465. package/node_modules/zod/v4/core/index.cjs +70 -0
  1466. package/node_modules/zod/v4/core/index.d.cts +19 -0
  1467. package/node_modules/zod/v4/core/index.d.ts +19 -0
  1468. package/node_modules/zod/v4/core/index.js +19 -0
  1469. package/node_modules/zod/v4/core/json-schema-generator.cjs +122 -0
  1470. package/node_modules/zod/v4/core/json-schema-generator.d.cts +65 -0
  1471. package/node_modules/zod/v4/core/json-schema-generator.d.ts +65 -0
  1472. package/node_modules/zod/v4/core/json-schema-generator.js +99 -0
  1473. package/node_modules/zod/v4/core/json-schema-processors.cjs +929 -0
  1474. package/node_modules/zod/v4/core/json-schema-processors.d.cts +65 -0
  1475. package/node_modules/zod/v4/core/json-schema-processors.d.ts +65 -0
  1476. package/node_modules/zod/v4/core/json-schema-processors.js +843 -0
  1477. package/node_modules/zod/v4/core/json-schema.cjs +21 -0
  1478. package/node_modules/zod/v4/core/json-schema.d.cts +89 -0
  1479. package/node_modules/zod/v4/core/json-schema.d.ts +89 -0
  1480. package/node_modules/zod/v4/core/json-schema.js +1 -0
  1481. package/node_modules/zod/v4/core/memoizer.cjs +360 -0
  1482. package/node_modules/zod/v4/core/memoizer.d.cts +15 -0
  1483. package/node_modules/zod/v4/core/memoizer.d.ts +15 -0
  1484. package/node_modules/zod/v4/core/memoizer.js +311 -0
  1485. package/node_modules/zod/v4/core/package.json +7 -0
  1486. package/node_modules/zod/v4/core/parse.cjs +233 -0
  1487. package/node_modules/zod/v4/core/parse.d.cts +52 -0
  1488. package/node_modules/zod/v4/core/parse.d.ts +52 -0
  1489. package/node_modules/zod/v4/core/parse.js +175 -0
  1490. package/node_modules/zod/v4/core/regexes.cjs +209 -0
  1491. package/node_modules/zod/v4/core/regexes.d.cts +90 -0
  1492. package/node_modules/zod/v4/core/regexes.d.ts +90 -0
  1493. package/node_modules/zod/v4/core/regexes.js +156 -0
  1494. package/node_modules/zod/v4/core/registries.cjs +75 -0
  1495. package/node_modules/zod/v4/core/registries.d.cts +35 -0
  1496. package/node_modules/zod/v4/core/registries.d.ts +35 -0
  1497. package/node_modules/zod/v4/core/registries.js +51 -0
  1498. package/node_modules/zod/v4/core/schemas.cjs +2669 -0
  1499. package/node_modules/zod/v4/core/schemas.d.cts +1273 -0
  1500. package/node_modules/zod/v4/core/schemas.d.ts +1273 -0
  1501. package/node_modules/zod/v4/core/schemas.js +2606 -0
  1502. package/node_modules/zod/v4/core/standard-schema.cjs +21 -0
  1503. package/node_modules/zod/v4/core/standard-schema.d.cts +126 -0
  1504. package/node_modules/zod/v4/core/standard-schema.d.ts +126 -0
  1505. package/node_modules/zod/v4/core/standard-schema.js +1 -0
  1506. package/node_modules/zod/v4/core/to-json-schema.cjs +681 -0
  1507. package/node_modules/zod/v4/core/to-json-schema.d.cts +154 -0
  1508. package/node_modules/zod/v4/core/to-json-schema.d.ts +154 -0
  1509. package/node_modules/zod/v4/core/to-json-schema.js +651 -0
  1510. package/node_modules/zod/v4/core/util.cjs +949 -0
  1511. package/node_modules/zod/v4/core/util.d.cts +278 -0
  1512. package/node_modules/zod/v4/core/util.d.ts +278 -0
  1513. package/node_modules/zod/v4/core/util.js +859 -0
  1514. package/node_modules/zod/v4/core/versions.cjs +27 -0
  1515. package/node_modules/zod/v4/core/versions.d.cts +5 -0
  1516. package/node_modules/zod/v4/core/versions.d.ts +5 -0
  1517. package/node_modules/zod/v4/core/versions.js +5 -0
  1518. package/node_modules/zod/v4/core/visit.cjs +214 -0
  1519. package/node_modules/zod/v4/core/visit.d.cts +30 -0
  1520. package/node_modules/zod/v4/core/visit.d.ts +30 -0
  1521. package/node_modules/zod/v4/core/visit.js +169 -0
  1522. package/node_modules/zod/v4/index.cjs +51 -0
  1523. package/node_modules/zod/v4/index.d.cts +3 -0
  1524. package/node_modules/zod/v4/index.d.ts +3 -0
  1525. package/node_modules/zod/v4/index.js +4 -0
  1526. package/node_modules/zod/v4/locales/ar.cjs +138 -0
  1527. package/node_modules/zod/v4/locales/ar.d.cts +5 -0
  1528. package/node_modules/zod/v4/locales/ar.d.ts +4 -0
  1529. package/node_modules/zod/v4/locales/ar.js +111 -0
  1530. package/node_modules/zod/v4/locales/az.cjs +137 -0
  1531. package/node_modules/zod/v4/locales/az.d.cts +5 -0
  1532. package/node_modules/zod/v4/locales/az.d.ts +4 -0
  1533. package/node_modules/zod/v4/locales/az.js +110 -0
  1534. package/node_modules/zod/v4/locales/be.cjs +195 -0
  1535. package/node_modules/zod/v4/locales/be.d.cts +5 -0
  1536. package/node_modules/zod/v4/locales/be.d.ts +4 -0
  1537. package/node_modules/zod/v4/locales/be.js +168 -0
  1538. package/node_modules/zod/v4/locales/bg.cjs +152 -0
  1539. package/node_modules/zod/v4/locales/bg.d.cts +5 -0
  1540. package/node_modules/zod/v4/locales/bg.d.ts +4 -0
  1541. package/node_modules/zod/v4/locales/bg.js +125 -0
  1542. package/node_modules/zod/v4/locales/bn.cjs +140 -0
  1543. package/node_modules/zod/v4/locales/bn.d.cts +5 -0
  1544. package/node_modules/zod/v4/locales/bn.d.ts +4 -0
  1545. package/node_modules/zod/v4/locales/bn.js +113 -0
  1546. package/node_modules/zod/v4/locales/ca.cjs +139 -0
  1547. package/node_modules/zod/v4/locales/ca.d.cts +5 -0
  1548. package/node_modules/zod/v4/locales/ca.d.ts +4 -0
  1549. package/node_modules/zod/v4/locales/ca.js +112 -0
  1550. package/node_modules/zod/v4/locales/ckb.cjs +160 -0
  1551. package/node_modules/zod/v4/locales/ckb.d.cts +5 -0
  1552. package/node_modules/zod/v4/locales/ckb.d.ts +4 -0
  1553. package/node_modules/zod/v4/locales/ckb.js +133 -0
  1554. package/node_modules/zod/v4/locales/cs.cjs +143 -0
  1555. package/node_modules/zod/v4/locales/cs.d.cts +5 -0
  1556. package/node_modules/zod/v4/locales/cs.d.ts +4 -0
  1557. package/node_modules/zod/v4/locales/cs.js +116 -0
  1558. package/node_modules/zod/v4/locales/da.cjs +147 -0
  1559. package/node_modules/zod/v4/locales/da.d.cts +5 -0
  1560. package/node_modules/zod/v4/locales/da.d.ts +4 -0
  1561. package/node_modules/zod/v4/locales/da.js +120 -0
  1562. package/node_modules/zod/v4/locales/de.cjs +140 -0
  1563. package/node_modules/zod/v4/locales/de.d.cts +5 -0
  1564. package/node_modules/zod/v4/locales/de.d.ts +4 -0
  1565. package/node_modules/zod/v4/locales/de.js +113 -0
  1566. package/node_modules/zod/v4/locales/el.cjs +139 -0
  1567. package/node_modules/zod/v4/locales/el.d.cts +5 -0
  1568. package/node_modules/zod/v4/locales/el.d.ts +4 -0
  1569. package/node_modules/zod/v4/locales/el.js +112 -0
  1570. package/node_modules/zod/v4/locales/en.cjs +152 -0
  1571. package/node_modules/zod/v4/locales/en.d.cts +5 -0
  1572. package/node_modules/zod/v4/locales/en.d.ts +4 -0
  1573. package/node_modules/zod/v4/locales/en.js +125 -0
  1574. package/node_modules/zod/v4/locales/eo.cjs +141 -0
  1575. package/node_modules/zod/v4/locales/eo.d.cts +5 -0
  1576. package/node_modules/zod/v4/locales/eo.d.ts +4 -0
  1577. package/node_modules/zod/v4/locales/eo.js +114 -0
  1578. package/node_modules/zod/v4/locales/es.cjs +163 -0
  1579. package/node_modules/zod/v4/locales/es.d.cts +5 -0
  1580. package/node_modules/zod/v4/locales/es.d.ts +4 -0
  1581. package/node_modules/zod/v4/locales/es.js +136 -0
  1582. package/node_modules/zod/v4/locales/fa.cjs +146 -0
  1583. package/node_modules/zod/v4/locales/fa.d.cts +5 -0
  1584. package/node_modules/zod/v4/locales/fa.d.ts +4 -0
  1585. package/node_modules/zod/v4/locales/fa.js +119 -0
  1586. package/node_modules/zod/v4/locales/fi.cjs +144 -0
  1587. package/node_modules/zod/v4/locales/fi.d.cts +5 -0
  1588. package/node_modules/zod/v4/locales/fi.d.ts +4 -0
  1589. package/node_modules/zod/v4/locales/fi.js +117 -0
  1590. package/node_modules/zod/v4/locales/fr-CA.cjs +139 -0
  1591. package/node_modules/zod/v4/locales/fr-CA.d.cts +5 -0
  1592. package/node_modules/zod/v4/locales/fr-CA.d.ts +4 -0
  1593. package/node_modules/zod/v4/locales/fr-CA.js +112 -0
  1594. package/node_modules/zod/v4/locales/fr.cjs +156 -0
  1595. package/node_modules/zod/v4/locales/fr.d.cts +5 -0
  1596. package/node_modules/zod/v4/locales/fr.d.ts +4 -0
  1597. package/node_modules/zod/v4/locales/fr.js +129 -0
  1598. package/node_modules/zod/v4/locales/gu.cjs +140 -0
  1599. package/node_modules/zod/v4/locales/gu.d.cts +5 -0
  1600. package/node_modules/zod/v4/locales/gu.d.ts +4 -0
  1601. package/node_modules/zod/v4/locales/gu.js +113 -0
  1602. package/node_modules/zod/v4/locales/he.cjs +248 -0
  1603. package/node_modules/zod/v4/locales/he.d.cts +5 -0
  1604. package/node_modules/zod/v4/locales/he.d.ts +4 -0
  1605. package/node_modules/zod/v4/locales/he.js +221 -0
  1606. package/node_modules/zod/v4/locales/hi.cjs +138 -0
  1607. package/node_modules/zod/v4/locales/hi.d.cts +5 -0
  1608. package/node_modules/zod/v4/locales/hi.d.ts +4 -0
  1609. package/node_modules/zod/v4/locales/hi.js +111 -0
  1610. package/node_modules/zod/v4/locales/hr.cjs +153 -0
  1611. package/node_modules/zod/v4/locales/hr.d.cts +5 -0
  1612. package/node_modules/zod/v4/locales/hr.d.ts +4 -0
  1613. package/node_modules/zod/v4/locales/hr.js +126 -0
  1614. package/node_modules/zod/v4/locales/hu.cjs +140 -0
  1615. package/node_modules/zod/v4/locales/hu.d.cts +5 -0
  1616. package/node_modules/zod/v4/locales/hu.d.ts +4 -0
  1617. package/node_modules/zod/v4/locales/hu.js +113 -0
  1618. package/node_modules/zod/v4/locales/hy.cjs +185 -0
  1619. package/node_modules/zod/v4/locales/hy.d.cts +5 -0
  1620. package/node_modules/zod/v4/locales/hy.d.ts +4 -0
  1621. package/node_modules/zod/v4/locales/hy.js +158 -0
  1622. package/node_modules/zod/v4/locales/id.cjs +138 -0
  1623. package/node_modules/zod/v4/locales/id.d.cts +5 -0
  1624. package/node_modules/zod/v4/locales/id.d.ts +4 -0
  1625. package/node_modules/zod/v4/locales/id.js +111 -0
  1626. package/node_modules/zod/v4/locales/index.cjs +152 -0
  1627. package/node_modules/zod/v4/locales/index.d.cts +63 -0
  1628. package/node_modules/zod/v4/locales/index.d.ts +63 -0
  1629. package/node_modules/zod/v4/locales/index.js +63 -0
  1630. package/node_modules/zod/v4/locales/is.cjs +141 -0
  1631. package/node_modules/zod/v4/locales/is.d.cts +5 -0
  1632. package/node_modules/zod/v4/locales/is.d.ts +4 -0
  1633. package/node_modules/zod/v4/locales/is.js +114 -0
  1634. package/node_modules/zod/v4/locales/it.cjs +140 -0
  1635. package/node_modules/zod/v4/locales/it.d.cts +5 -0
  1636. package/node_modules/zod/v4/locales/it.d.ts +4 -0
  1637. package/node_modules/zod/v4/locales/it.js +113 -0
  1638. package/node_modules/zod/v4/locales/ja.cjs +139 -0
  1639. package/node_modules/zod/v4/locales/ja.d.cts +5 -0
  1640. package/node_modules/zod/v4/locales/ja.d.ts +4 -0
  1641. package/node_modules/zod/v4/locales/ja.js +112 -0
  1642. package/node_modules/zod/v4/locales/ka.cjs +144 -0
  1643. package/node_modules/zod/v4/locales/ka.d.cts +5 -0
  1644. package/node_modules/zod/v4/locales/ka.d.ts +4 -0
  1645. package/node_modules/zod/v4/locales/ka.js +117 -0
  1646. package/node_modules/zod/v4/locales/kh.cjs +12 -0
  1647. package/node_modules/zod/v4/locales/kh.d.cts +5 -0
  1648. package/node_modules/zod/v4/locales/kh.d.ts +5 -0
  1649. package/node_modules/zod/v4/locales/kh.js +5 -0
  1650. package/node_modules/zod/v4/locales/km.cjs +142 -0
  1651. package/node_modules/zod/v4/locales/km.d.cts +5 -0
  1652. package/node_modules/zod/v4/locales/km.d.ts +4 -0
  1653. package/node_modules/zod/v4/locales/km.js +115 -0
  1654. package/node_modules/zod/v4/locales/kn.cjs +143 -0
  1655. package/node_modules/zod/v4/locales/kn.d.cts +5 -0
  1656. package/node_modules/zod/v4/locales/kn.d.ts +4 -0
  1657. package/node_modules/zod/v4/locales/kn.js +116 -0
  1658. package/node_modules/zod/v4/locales/ko.cjs +143 -0
  1659. package/node_modules/zod/v4/locales/ko.d.cts +5 -0
  1660. package/node_modules/zod/v4/locales/ko.d.ts +4 -0
  1661. package/node_modules/zod/v4/locales/ko.js +116 -0
  1662. package/node_modules/zod/v4/locales/lt.cjs +234 -0
  1663. package/node_modules/zod/v4/locales/lt.d.cts +5 -0
  1664. package/node_modules/zod/v4/locales/lt.d.ts +4 -0
  1665. package/node_modules/zod/v4/locales/lt.js +207 -0
  1666. package/node_modules/zod/v4/locales/mk.cjs +141 -0
  1667. package/node_modules/zod/v4/locales/mk.d.cts +5 -0
  1668. package/node_modules/zod/v4/locales/mk.d.ts +4 -0
  1669. package/node_modules/zod/v4/locales/mk.js +114 -0
  1670. package/node_modules/zod/v4/locales/ms.cjs +139 -0
  1671. package/node_modules/zod/v4/locales/ms.d.cts +5 -0
  1672. package/node_modules/zod/v4/locales/ms.d.ts +4 -0
  1673. package/node_modules/zod/v4/locales/ms.js +112 -0
  1674. package/node_modules/zod/v4/locales/ne.cjs +138 -0
  1675. package/node_modules/zod/v4/locales/ne.d.cts +5 -0
  1676. package/node_modules/zod/v4/locales/ne.d.ts +4 -0
  1677. package/node_modules/zod/v4/locales/ne.js +111 -0
  1678. package/node_modules/zod/v4/locales/nl.cjs +142 -0
  1679. package/node_modules/zod/v4/locales/nl.d.cts +5 -0
  1680. package/node_modules/zod/v4/locales/nl.d.ts +4 -0
  1681. package/node_modules/zod/v4/locales/nl.js +115 -0
  1682. package/node_modules/zod/v4/locales/nn.cjs +140 -0
  1683. package/node_modules/zod/v4/locales/nn.d.cts +5 -0
  1684. package/node_modules/zod/v4/locales/nn.d.ts +4 -0
  1685. package/node_modules/zod/v4/locales/nn.js +113 -0
  1686. package/node_modules/zod/v4/locales/no.cjs +140 -0
  1687. package/node_modules/zod/v4/locales/no.d.cts +5 -0
  1688. package/node_modules/zod/v4/locales/no.d.ts +4 -0
  1689. package/node_modules/zod/v4/locales/no.js +113 -0
  1690. package/node_modules/zod/v4/locales/ota.cjs +141 -0
  1691. package/node_modules/zod/v4/locales/ota.d.cts +5 -0
  1692. package/node_modules/zod/v4/locales/ota.d.ts +4 -0
  1693. package/node_modules/zod/v4/locales/ota.js +114 -0
  1694. package/node_modules/zod/v4/locales/package.json +7 -0
  1695. package/node_modules/zod/v4/locales/pl.cjs +141 -0
  1696. package/node_modules/zod/v4/locales/pl.d.cts +5 -0
  1697. package/node_modules/zod/v4/locales/pl.d.ts +4 -0
  1698. package/node_modules/zod/v4/locales/pl.js +114 -0
  1699. package/node_modules/zod/v4/locales/ps.cjs +146 -0
  1700. package/node_modules/zod/v4/locales/ps.d.cts +5 -0
  1701. package/node_modules/zod/v4/locales/ps.d.ts +4 -0
  1702. package/node_modules/zod/v4/locales/ps.js +119 -0
  1703. package/node_modules/zod/v4/locales/pt-BR.cjs +170 -0
  1704. package/node_modules/zod/v4/locales/pt-BR.d.cts +5 -0
  1705. package/node_modules/zod/v4/locales/pt-BR.d.ts +4 -0
  1706. package/node_modules/zod/v4/locales/pt-BR.js +143 -0
  1707. package/node_modules/zod/v4/locales/pt.cjs +169 -0
  1708. package/node_modules/zod/v4/locales/pt.d.cts +5 -0
  1709. package/node_modules/zod/v4/locales/pt.d.ts +4 -0
  1710. package/node_modules/zod/v4/locales/pt.js +142 -0
  1711. package/node_modules/zod/v4/locales/ro.cjs +149 -0
  1712. package/node_modules/zod/v4/locales/ro.d.cts +5 -0
  1713. package/node_modules/zod/v4/locales/ro.d.ts +4 -0
  1714. package/node_modules/zod/v4/locales/ro.js +122 -0
  1715. package/node_modules/zod/v4/locales/ru.cjs +195 -0
  1716. package/node_modules/zod/v4/locales/ru.d.cts +5 -0
  1717. package/node_modules/zod/v4/locales/ru.d.ts +4 -0
  1718. package/node_modules/zod/v4/locales/ru.js +168 -0
  1719. package/node_modules/zod/v4/locales/sk.cjs +143 -0
  1720. package/node_modules/zod/v4/locales/sk.d.cts +5 -0
  1721. package/node_modules/zod/v4/locales/sk.d.ts +4 -0
  1722. package/node_modules/zod/v4/locales/sk.js +116 -0
  1723. package/node_modules/zod/v4/locales/sl.cjs +141 -0
  1724. package/node_modules/zod/v4/locales/sl.d.cts +5 -0
  1725. package/node_modules/zod/v4/locales/sl.d.ts +4 -0
  1726. package/node_modules/zod/v4/locales/sl.js +114 -0
  1727. package/node_modules/zod/v4/locales/sv.cjs +142 -0
  1728. package/node_modules/zod/v4/locales/sv.d.cts +5 -0
  1729. package/node_modules/zod/v4/locales/sv.d.ts +4 -0
  1730. package/node_modules/zod/v4/locales/sv.js +115 -0
  1731. package/node_modules/zod/v4/locales/ta.cjs +142 -0
  1732. package/node_modules/zod/v4/locales/ta.d.cts +5 -0
  1733. package/node_modules/zod/v4/locales/ta.d.ts +4 -0
  1734. package/node_modules/zod/v4/locales/ta.js +115 -0
  1735. package/node_modules/zod/v4/locales/tg.cjs +144 -0
  1736. package/node_modules/zod/v4/locales/tg.d.cts +5 -0
  1737. package/node_modules/zod/v4/locales/tg.d.ts +4 -0
  1738. package/node_modules/zod/v4/locales/tg.js +117 -0
  1739. package/node_modules/zod/v4/locales/th.cjs +142 -0
  1740. package/node_modules/zod/v4/locales/th.d.cts +5 -0
  1741. package/node_modules/zod/v4/locales/th.d.ts +4 -0
  1742. package/node_modules/zod/v4/locales/th.js +115 -0
  1743. package/node_modules/zod/v4/locales/tk.cjs +134 -0
  1744. package/node_modules/zod/v4/locales/tk.d.cts +5 -0
  1745. package/node_modules/zod/v4/locales/tk.d.ts +4 -0
  1746. package/node_modules/zod/v4/locales/tk.js +107 -0
  1747. package/node_modules/zod/v4/locales/tr.cjs +137 -0
  1748. package/node_modules/zod/v4/locales/tr.d.cts +5 -0
  1749. package/node_modules/zod/v4/locales/tr.d.ts +4 -0
  1750. package/node_modules/zod/v4/locales/tr.js +110 -0
  1751. package/node_modules/zod/v4/locales/ua.cjs +12 -0
  1752. package/node_modules/zod/v4/locales/ua.d.cts +5 -0
  1753. package/node_modules/zod/v4/locales/ua.d.ts +5 -0
  1754. package/node_modules/zod/v4/locales/ua.js +5 -0
  1755. package/node_modules/zod/v4/locales/uk.cjs +140 -0
  1756. package/node_modules/zod/v4/locales/uk.d.cts +5 -0
  1757. package/node_modules/zod/v4/locales/uk.d.ts +4 -0
  1758. package/node_modules/zod/v4/locales/uk.js +113 -0
  1759. package/node_modules/zod/v4/locales/ur.cjs +142 -0
  1760. package/node_modules/zod/v4/locales/ur.d.cts +5 -0
  1761. package/node_modules/zod/v4/locales/ur.d.ts +4 -0
  1762. package/node_modules/zod/v4/locales/ur.js +115 -0
  1763. package/node_modules/zod/v4/locales/uz.cjs +140 -0
  1764. package/node_modules/zod/v4/locales/uz.d.cts +5 -0
  1765. package/node_modules/zod/v4/locales/uz.d.ts +4 -0
  1766. package/node_modules/zod/v4/locales/uz.js +113 -0
  1767. package/node_modules/zod/v4/locales/vi.cjs +140 -0
  1768. package/node_modules/zod/v4/locales/vi.d.cts +5 -0
  1769. package/node_modules/zod/v4/locales/vi.d.ts +4 -0
  1770. package/node_modules/zod/v4/locales/vi.js +113 -0
  1771. package/node_modules/zod/v4/locales/yo.cjs +139 -0
  1772. package/node_modules/zod/v4/locales/yo.d.cts +5 -0
  1773. package/node_modules/zod/v4/locales/yo.d.ts +4 -0
  1774. package/node_modules/zod/v4/locales/yo.js +112 -0
  1775. package/node_modules/zod/v4/locales/zh-CN.cjs +141 -0
  1776. package/node_modules/zod/v4/locales/zh-CN.d.cts +5 -0
  1777. package/node_modules/zod/v4/locales/zh-CN.d.ts +4 -0
  1778. package/node_modules/zod/v4/locales/zh-CN.js +114 -0
  1779. package/node_modules/zod/v4/locales/zh-TW.cjs +139 -0
  1780. package/node_modules/zod/v4/locales/zh-TW.d.cts +5 -0
  1781. package/node_modules/zod/v4/locales/zh-TW.d.ts +4 -0
  1782. package/node_modules/zod/v4/locales/zh-TW.js +112 -0
  1783. package/node_modules/zod/v4/mini/checks.cjs +54 -0
  1784. package/node_modules/zod/v4/mini/checks.d.cts +1 -0
  1785. package/node_modules/zod/v4/mini/checks.d.ts +1 -0
  1786. package/node_modules/zod/v4/mini/checks.js +1 -0
  1787. package/node_modules/zod/v4/mini/coerce.cjs +71 -0
  1788. package/node_modules/zod/v4/mini/coerce.d.cts +7 -0
  1789. package/node_modules/zod/v4/mini/coerce.d.ts +7 -0
  1790. package/node_modules/zod/v4/mini/coerce.js +22 -0
  1791. package/node_modules/zod/v4/mini/deep-partial.cjs +58 -0
  1792. package/node_modules/zod/v4/mini/deep-partial.d.cts +14 -0
  1793. package/node_modules/zod/v4/mini/deep-partial.d.ts +14 -0
  1794. package/node_modules/zod/v4/mini/deep-partial.js +13 -0
  1795. package/node_modules/zod/v4/mini/external.cjs +95 -0
  1796. package/node_modules/zod/v4/mini/external.d.cts +16 -0
  1797. package/node_modules/zod/v4/mini/external.d.ts +16 -0
  1798. package/node_modules/zod/v4/mini/external.js +16 -0
  1799. package/node_modules/zod/v4/mini/in-out.cjs +82 -0
  1800. package/node_modules/zod/v4/mini/in-out.d.cts +8 -0
  1801. package/node_modules/zod/v4/mini/in-out.d.ts +8 -0
  1802. package/node_modules/zod/v4/mini/in-out.js +36 -0
  1803. package/node_modules/zod/v4/mini/index.cjs +51 -0
  1804. package/node_modules/zod/v4/mini/index.d.cts +3 -0
  1805. package/node_modules/zod/v4/mini/index.d.ts +3 -0
  1806. package/node_modules/zod/v4/mini/index.js +3 -0
  1807. package/node_modules/zod/v4/mini/iso.cjs +83 -0
  1808. package/node_modules/zod/v4/mini/iso.d.cts +22 -0
  1809. package/node_modules/zod/v4/mini/iso.d.ts +22 -0
  1810. package/node_modules/zod/v4/mini/iso.js +34 -0
  1811. package/node_modules/zod/v4/mini/package.json +7 -0
  1812. package/node_modules/zod/v4/mini/parse.cjs +37 -0
  1813. package/node_modules/zod/v4/mini/parse.d.cts +1 -0
  1814. package/node_modules/zod/v4/mini/parse.d.ts +1 -0
  1815. package/node_modules/zod/v4/mini/parse.js +1 -0
  1816. package/node_modules/zod/v4/mini/schemas.cjs +1147 -0
  1817. package/node_modules/zod/v4/mini/schemas.d.cts +480 -0
  1818. package/node_modules/zod/v4/mini/schemas.d.ts +480 -0
  1819. package/node_modules/zod/v4/mini/schemas.js +1002 -0
  1820. package/node_modules/zod/v4/package.json +7 -0
  1821. package/node_modules/zod/v4-mini/index.cjs +51 -0
  1822. package/node_modules/zod/v4-mini/index.d.cts +3 -0
  1823. package/node_modules/zod/v4-mini/index.d.ts +3 -0
  1824. package/node_modules/zod/v4-mini/index.js +3 -0
  1825. package/node_modules/zod/v4-mini/package.json +7 -0
  1826. package/package.json +50 -0
@@ -0,0 +1,4448 @@
1
+ import { cancellableSandboxStartup } from "./startup-cancellation.js";
2
+ import fs from "node:fs/promises";
3
+ import fsSync from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { execFile } from "node:child_process";
7
+ import { promisify } from "node:util";
8
+ import { createHash, randomUUID } from "node:crypto";
9
+ import { fileURLToPath } from "node:url";
10
+ import { adapterExecutionTargetSessionIdentity, describeAdapterExecutionTarget, adapterExecutionTargetDuplexObservabilityRecorder, adapterExecutionTargetEnablesSandboxDuplexBridge, formatAdapterExecutionTimeoutErrorMessage, formatAdapterExecutionTimeoutStartLogLine, prepareAdapterExecutionTargetRuntime, readAdapterExecutionTarget, resolveAdapterExecutionTargetTimeout, resolveReferencedSourceIgnore, runAdapterExecutionTargetShellCommand, startAdapterExecutionTargetPaperclipBridge, startAdapterExecutionTargetProcessSessionBridge, } from "@tickernelz/paperclip-pro-adapter-utils/execution-target";
11
+ import { captureLocalProcess, capturedProcessExited, killCapturedLocalProcess } from "./local-process-control.js";
12
+ import { DUPLEX_CHANNEL_LOST_ERROR_CODE } from "../bridge-transport-contract.js";
13
+ import { classifyWorkspaceRestoreFailure, describeWorkspaceRestoreFailure, } from "../workspace-restore-merge.js";
14
+ import { DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE, DEFAULT_PAPERCLIP_CONVERSATION_PROMPT_TEMPLATE, applyPaperclipWorkspaceEnv, asNumber, asString, buildInvocationEnvForLogs, buildPaperclipEnv, ensureAbsoluteDirectory, ensurePathInEnv, ensurePaperclipSkillSymlink, isForbiddenConfigEnvKey, isPaperclipExternalChatTurn, isPaperclipRuntimeEnvKey, joinPromptSections, materializePaperclipSkillCopy, parseObject, isPaperclipSkillSourceMissing, readPaperclipRuntimeSkillEntries, readPaperclipIssueWorkModeFromContext, renderPaperclipWakePrompt, renderTemplate, resolvePaperclipInstanceRootForAdapter, selectPaperclipTaskMarkdown, resolveLegacyPaperclipDesiredSkillNames, removeMaintainerOnlySkillSymlinks, rewriteWorkspaceCwdEnvVarsForExecution, shapePaperclipWorkspaceEnvForExecution, } from "@tickernelz/paperclip-pro-adapter-utils/server-utils";
15
+ import { shellQuote } from "@tickernelz/paperclip-pro-adapter-utils/ssh";
16
+ import { createAcpRuntime, createAgentRegistry, createRuntimeStore, isAcpRuntimeError, } from "acpx/runtime";
17
+ import { ACPX_DUPLEX_LOSS_CANCEL_DEADLINE_MS, ACPX_HANDSHAKE_TIMEOUT_MS, ACPX_HANDSHAKE_TRANSPORT_POLL_MS, DEFAULT_ACP_ENGINE_AGENT, DEFAULT_ACP_ENGINE_MODE, DEFAULT_ACP_ENGINE_NON_INTERACTIVE_PERMISSIONS, DEFAULT_ACP_ENGINE_PERMISSION_MODE, DEFAULT_ACP_ENGINE_TIMEOUT_SEC, DEFAULT_ACP_ENGINE_WARM_HANDLE_IDLE_MS, } from "./constants.js";
18
+ import { createRunResourceLedger } from "./run-resource-ledger.js";
19
+ import { settleAcpRun } from "./settlement-sequence.js";
20
+ import { runAttempt, } from "./run-coordinator.js";
21
+ import { runTurn as runTurnSequence, } from "./turn-sequence.js";
22
+ import { createHostRunSite, } from "./run-site-host.js";
23
+ import { createSandboxRunSite } from "./run-site-sandbox.js";
24
+ import { createRuntimeSpanRunner, emitRunPhaseTiming, emitSkippedStartupStep, getActiveStepContext, measureStartupStep, NOOP_STARTUP_SPAN, NOOP_STARTUP_TRACE_CONTEXT, runWithRuntimeParent, setSandboxRootSpanAttributes, } from "./startup-timing.js";
25
+ const defaultModuleDir = path.dirname(fileURLToPath(import.meta.url));
26
+ const PAPERCLIP_MANAGED_CODEX_SKILLS_MANIFEST = ".paperclip-managed-skills.json";
27
+ const BENIGN_NES_CLOSE_STDERR = /method: ['"]nes\/close['"].*-32601/;
28
+ function routeChildStderr(state, chunk) {
29
+ if (state.logPath) {
30
+ fsSync.mkdirSync(path.dirname(state.logPath), { recursive: true });
31
+ fsSync.appendFileSync(state.logPath, chunk);
32
+ }
33
+ const combined = state.pendingLiveLine + chunk;
34
+ const lastNewline = combined.lastIndexOf("\n");
35
+ if (lastNewline < 0) {
36
+ state.pendingLiveLine = combined;
37
+ return;
38
+ }
39
+ const complete = combined.slice(0, lastNewline + 1);
40
+ state.pendingLiveLine = combined.slice(lastNewline + 1);
41
+ const filtered = complete
42
+ .split(/(?<=\n)/)
43
+ .filter((line) => !BENIGN_NES_CLOSE_STDERR.test(line))
44
+ .join("");
45
+ if (filtered)
46
+ process.stderr.write(filtered);
47
+ }
48
+ function flushChildStderr(state) {
49
+ if (state.pendingLiveLine && !BENIGN_NES_CLOSE_STDERR.test(state.pendingLiveLine)) {
50
+ process.stderr.write(state.pendingLiveLine);
51
+ }
52
+ state.pendingLiveLine = "";
53
+ }
54
+ const defaultWarmHandles = new Map();
55
+ const defaultStagedRuntimes = new Map();
56
+ const defaultStagingLocks = new Map();
57
+ function resolveEngineSettings(options) {
58
+ const moduleDir = path.resolve(options.moduleDir ?? defaultModuleDir);
59
+ return {
60
+ adapterType: options.adapterType?.trim() || "acp_engine",
61
+ moduleDir,
62
+ packageRootDir: path.resolve(options.packageRootDir ?? path.resolve(moduleDir, "../..")),
63
+ };
64
+ }
65
+ function stableJson(value) {
66
+ if (Array.isArray(value))
67
+ return `[${value.map(stableJson).join(",")}]`;
68
+ if (value && typeof value === "object") {
69
+ return `{${Object.entries(value)
70
+ .sort(([a], [b]) => a.localeCompare(b))
71
+ .map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
72
+ .join(",")}}`;
73
+ }
74
+ return JSON.stringify(value);
75
+ }
76
+ function shortHash(value) {
77
+ return createHash("sha256").update(stableJson(value)).digest("hex").slice(0, 16);
78
+ }
79
+ /**
80
+ * Hash the session fingerprint. The builder accepts only a
81
+ * `SessionFingerprintIdentity`, so no company, agent, or task identifier can
82
+ * enter the hash. Those identifiers scope the outer session key only (see
83
+ * `SessionKeyIdentity`).
84
+ */
85
+ export function buildSessionFingerprint(identity) {
86
+ return shortHash(identity);
87
+ }
88
+ /**
89
+ * Build the session key from the fingerprint and the outer-key identity. The key
90
+ * form is `paperclip:companyId:agentId:taskKey:fingerprint`.
91
+ */
92
+ export function buildSessionKey(identity, fingerprint) {
93
+ return `paperclip:${identity.companyId}:${identity.agentId}:${identity.taskKey}:${fingerprint}`;
94
+ }
95
+ // ACPX runs inside the long-lived Paperclip server process. A local child needs
96
+ // a small amount of host context (PATH, locale, certificate/proxy settings, and
97
+ // provider authentication), but it must not inherit the server's complete
98
+ // environment. A runner-backed remote sandbox inherits no ambient host context
99
+ // at all. In particular, native-runner bootstrap and MCP credentials are host
100
+ // authority, not provider credentials.
101
+ const ACPX_INHERITED_HOST_ENV_KEYS = new Set([
102
+ "PATH",
103
+ "PATHEXT",
104
+ "SYSTEMROOT",
105
+ "WINDIR",
106
+ "COMSPEC",
107
+ "HOME",
108
+ "USERPROFILE",
109
+ "HOMEDRIVE",
110
+ "HOMEPATH",
111
+ "USER",
112
+ "USERNAME",
113
+ "LOGNAME",
114
+ "SHELL",
115
+ "LANG",
116
+ "LANGUAGE",
117
+ "TZ",
118
+ "TMPDIR",
119
+ "TEMP",
120
+ "TMP",
121
+ "XDG_CONFIG_HOME",
122
+ "XDG_CACHE_HOME",
123
+ "XDG_DATA_HOME",
124
+ "SSL_CERT_FILE",
125
+ "SSL_CERT_DIR",
126
+ "NODE_EXTRA_CA_CERTS",
127
+ "HTTP_PROXY",
128
+ "HTTPS_PROXY",
129
+ "NO_PROXY",
130
+ "ALL_PROXY",
131
+ ]);
132
+ const ACPX_INHERITED_PROVIDER_ENV_KEYS = {
133
+ codex: new Set([
134
+ "OPENAI_API_KEY",
135
+ "CODEX_API_KEY",
136
+ ]),
137
+ claude: new Set([
138
+ "ANTHROPIC_API_KEY",
139
+ "ANTHROPIC_AUTH_TOKEN",
140
+ "CLAUDE_CODE_OAUTH_TOKEN",
141
+ "ANTHROPIC_BASE_URL",
142
+ "ANTHROPIC_MODEL",
143
+ "ANTHROPIC_SMALL_FAST_MODEL",
144
+ "CLAUDE_CONFIG_DIR",
145
+ "CLAUDE_CODE_USE_BEDROCK",
146
+ "ANTHROPIC_BEDROCK_BASE_URL",
147
+ "AWS_BEARER_TOKEN_BEDROCK",
148
+ "AWS_ACCESS_KEY_ID",
149
+ "AWS_SECRET_ACCESS_KEY",
150
+ "AWS_SESSION_TOKEN",
151
+ "AWS_REGION",
152
+ "AWS_DEFAULT_REGION",
153
+ "AWS_PROFILE",
154
+ "AWS_CONFIG_FILE",
155
+ "AWS_SHARED_CREDENTIALS_FILE",
156
+ ]),
157
+ pi: new Set(["OPENROUTER_API_KEY"]),
158
+ gemini: new Set([
159
+ "GEMINI_API_KEY",
160
+ "GOOGLE_API_KEY",
161
+ "GOOGLE_APPLICATION_CREDENTIALS",
162
+ "GOOGLE_GENAI_USE_GCA",
163
+ ]),
164
+ kimi: new Set([
165
+ "KIMI_API_KEY",
166
+ "MOONSHOT_API_KEY",
167
+ "KIMI_MODEL_NAME",
168
+ "KIMI_MODEL_API_KEY",
169
+ "KIMI_MODEL_BASE_URL",
170
+ "KIMI_MODEL_PROVIDER_TYPE",
171
+ "KIMI_CODE_HOME",
172
+ ]),
173
+ grok: new Set(["XAI_API_KEY"]),
174
+ };
175
+ /**
176
+ * Project the server environment onto the closed set a host ACPX provider may
177
+ * inherit. A runner-backed remote sandbox passes `false` and projects nothing.
178
+ * Explicit adapter/runtime env is merged later and is intentionally not
179
+ * restricted by this host projection.
180
+ */
181
+ export function projectAcpxInheritedHostEnvironment(inheritedEnv, acpxAgent, inheritHostEnvironment) {
182
+ // A runner-backed remote sandbox crosses a serialization boundary. Ambient
183
+ // server state is never part of that contract: provider auth/config must be
184
+ // supplied through adapter config, resolved runtime env, or a contribution.
185
+ if (!inheritHostEnvironment)
186
+ return {};
187
+ const providerKeys = ACPX_INHERITED_PROVIDER_ENV_KEYS[acpxAgent];
188
+ const projected = {};
189
+ for (const [key, value] of Object.entries(inheritedEnv)) {
190
+ if (typeof value !== "string")
191
+ continue;
192
+ const normalizedKey = key.toUpperCase();
193
+ const allowed = ACPX_INHERITED_HOST_ENV_KEYS.has(normalizedKey) ||
194
+ /^LC_[A-Z0-9_]{1,32}$/.test(normalizedKey) ||
195
+ providerKeys?.has(normalizedKey) === true;
196
+ if (allowed)
197
+ projected[key] = value;
198
+ }
199
+ return projected;
200
+ }
201
+ /**
202
+ * Build the single branded launch environment for a run. This is the sole
203
+ * constructor of `LaunchEnvironment`. It applies each contribution into the base
204
+ * env in registration order, then resolves and freezes the launch env.
205
+ *
206
+ * A contribution carries its credential scope. A run-scoped contribution (the
207
+ * run API key, a bridge token) and a session-scoped contribution (the Codex auth
208
+ * copy-back material) both merge here. Neither scope can escape into a reuse
209
+ * payload, because the branded contribution type and the branded environment
210
+ * type forbid it.
211
+ */
212
+ export function finalizeLaunchEnvironment(baseEnv, contributions, options) {
213
+ for (const contribution of contributions) {
214
+ Object.assign(baseEnv, contribution.env);
215
+ }
216
+ const env = Object.freeze(resolveRuntimeEnv(baseEnv, options.acpxAgent, options));
217
+ return { env };
218
+ }
219
+ // Directory names the staging path never ships for a referenced project (heavy
220
+ // build/cache output and git history), applied regardless of the project's
221
+ // ignore resolution. The content signature skips them so it reflects only the
222
+ // staged tree and never reads their bytes. Keep this set equal to the fixed
223
+ // excludes the sandbox and SSH runtimes always apply. A project's OWN resolved
224
+ // Git-ignored paths (see `resolveReferencedSourceIgnore`) are matched
225
+ // separately, by relative path, inside `referencedSourceContentSignature` — that
226
+ // is the real invariant now: the signature and both staging lanes must consume
227
+ // the SAME one resolution per project, not just this fixed name list.
228
+ const REFERENCED_SOURCE_SIGNATURE_SKIP_DIRS = new Set([
229
+ "node_modules",
230
+ "vendor",
231
+ "dist",
232
+ "build",
233
+ "out",
234
+ "coverage",
235
+ ".next",
236
+ ".turbo",
237
+ ".cache",
238
+ ".git",
239
+ ]);
240
+ /**
241
+ * Content signature of a referenced-project host tree for the session fingerprint.
242
+ *
243
+ * The staged-runtime cache reuses an already-staged referenced-project tree on a
244
+ * compatible resume and does not re-sync it. Referenced-project metadata (id, host
245
+ * path, workspace id, repo url, pinned ref) can stay identical while the files at
246
+ * that host path change: a branch moved to a new commit, a re-checkout in place, or
247
+ * a dirty worktree. So the metadata identity alone lets a resume serve a stale tree.
248
+ * This signature folds the tree's own content state into the identity.
249
+ *
250
+ * The walk reads each file's relative path and bytes and folds them into the hash.
251
+ * It reads bytes, not only file stats. A stat-only signature (size and modification
252
+ * time) collides when an edit keeps the byte length and the modification time — a
253
+ * re-checkout that restores the same size and timestamp. The byte hash busts on any
254
+ * content change, so the fingerprint busts and the next launch stages the current
255
+ * tree. The walk skips the heavy build, cache, and git directories the staging path
256
+ * never ships, plus the project's own resolved Git-ignored paths, and records a
257
+ * symlink by its target text without following it. On a read error the function
258
+ * returns a stable marker, so the fingerprint does not churn while staging
259
+ * surfaces the real error. The walk runs only when the run carries referenced
260
+ * projects (the multi-project sync path).
261
+ *
262
+ * `ignoreResolution` is the ONE resolution `resolveReferencedSourceIgnore`
263
+ * computed for this project — the same one the sandbox lane and the SSH lane
264
+ * consume. A `failed` resolution skips the walk entirely and returns a stable
265
+ * marker instead, because a failed project is not staged and its bytes are not
266
+ * read anywhere.
267
+ */
268
+ export async function referencedSourceContentSignature(localPath, ignoreResolution) {
269
+ if (ignoreResolution.kind === "failed") {
270
+ return `unreadable:${ignoreResolution.reason}`;
271
+ }
272
+ const isIgnoredByGitResolution = (relativePath) => ignoreResolution.kind === "git" &&
273
+ ignoreResolution.ignoredPaths.some((entry) => relativePath === entry || relativePath.startsWith(`${entry}/`));
274
+ const hash = createHash("sha256");
275
+ const walk = async (relative) => {
276
+ const current = relative ? path.join(localPath, relative) : localPath;
277
+ const dirents = await fs.readdir(current, { withFileTypes: true });
278
+ dirents.sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
279
+ for (const dirent of dirents) {
280
+ const next = relative ? path.posix.join(relative, dirent.name) : dirent.name;
281
+ if (isIgnoredByGitResolution(next)) {
282
+ continue;
283
+ }
284
+ if (dirent.isDirectory()) {
285
+ if (REFERENCED_SOURCE_SIGNATURE_SKIP_DIRS.has(dirent.name)) {
286
+ continue;
287
+ }
288
+ await walk(next);
289
+ continue;
290
+ }
291
+ const absolute = path.join(localPath, next);
292
+ const stats = await fs.lstat(absolute);
293
+ if (stats.isSymbolicLink()) {
294
+ const target = await fs.readlink(absolute);
295
+ hash.update(`symlink:${next}:${target}\n`);
296
+ continue;
297
+ }
298
+ if (!stats.isFile()) {
299
+ hash.update(`other:${next}:${stats.mode}\n`);
300
+ continue;
301
+ }
302
+ hash.update(`file:${next}:${stats.size}\n`);
303
+ hash.update(await fs.readFile(absolute));
304
+ hash.update("\n");
305
+ }
306
+ };
307
+ try {
308
+ await walk("");
309
+ }
310
+ catch (error) {
311
+ return `unreadable:${String(error)}`;
312
+ }
313
+ return hash.digest("hex").slice(0, 16);
314
+ }
315
+ function defaultPaperclipInstanceDir() {
316
+ const home = process.env.PAPERCLIP_HOME?.trim() || path.join(os.homedir(), ".paperclip-pro");
317
+ const instanceId = process.env.PAPERCLIP_INSTANCE_ID?.trim() || "default";
318
+ return resolvePaperclipInstanceRootForAdapter({
319
+ homeDir: home,
320
+ instanceId,
321
+ });
322
+ }
323
+ function defaultStateDir(companyId, agentId) {
324
+ return path.join(defaultPaperclipInstanceDir(), "companies", companyId, "acp-engine", "agents", agentId);
325
+ }
326
+ function resolveManagedCodexHomeDir(companyId) {
327
+ return path.join(defaultPaperclipInstanceDir(), "companies", companyId, "codex-home");
328
+ }
329
+ // Mirrors `resolveManagedGrokHomeDir` in
330
+ // `packages/adapters/grok-local/src/server/grok-home.ts` — this package
331
+ // cannot import that adapter package (it would invert the dependency
332
+ // direction), so the path scheme is duplicated here, the same way
333
+ // `resolveManagedCodexHomeDir` above duplicates the Codex adapter's own
334
+ // helper.
335
+ function resolveManagedGrokHomeDir(companyId) {
336
+ return path.join(defaultPaperclipInstanceDir(), "companies", companyId, "grok-home");
337
+ }
338
+ // Walk up from startDir looking for `node_modules/.bin/<binName>`. This matches
339
+ // npm/pnpm binary hoisting in packaged installs while preserving monorepo dev.
340
+ export async function findAncestorBin(startDir, binName) {
341
+ let current = path.resolve(startDir);
342
+ while (true) {
343
+ const binDir = path.join(current, "node_modules", ".bin");
344
+ const candidates = process.platform === "win32"
345
+ ? [path.join(binDir, `${binName}.cmd`), path.join(binDir, binName)]
346
+ : [path.join(binDir, binName)];
347
+ for (const candidate of candidates) {
348
+ if (await pathExists(candidate))
349
+ return candidate;
350
+ }
351
+ const parent = path.dirname(current);
352
+ if (parent === current)
353
+ return null;
354
+ current = parent;
355
+ }
356
+ }
357
+ async function resolveBuiltInAgentCommand(input) {
358
+ const { agent, packageRootDir, executionTargetIsRemote } = input;
359
+ if (agent === "gemini") {
360
+ return { command: "gemini --acp", shellCommand: "gemini --acp" };
361
+ }
362
+ if (agent === "kimi") {
363
+ // Kimi Code exposes its ACP server via the `kimi acp` subcommand (stdio),
364
+ // rather than a flag (gemini) or a dedicated bin (claude/codex).
365
+ return { command: "kimi acp", shellCommand: "kimi acp" };
366
+ }
367
+ const binName = agent === "claude" ? "claude-agent-acp" : agent === "codex" ? "codex-acp" : null;
368
+ if (!binName)
369
+ return null;
370
+ if (executionTargetIsRemote) {
371
+ return { command: binName, shellCommand: binName };
372
+ }
373
+ const resolved = (await findAncestorBin(packageRootDir, binName)) ?? binName;
374
+ return { command: resolved, shellCommand: shellQuote(resolved) };
375
+ }
376
+ const execFileAsync = promisify(execFile);
377
+ // Gemini CLI renamed --experimental-acp to --acp in 0.33.0. acpx normally
378
+ // rewrites the flag itself, but the agent wrapper script hides the gemini
379
+ // command from acpx's detection, so the engine must downgrade it here.
380
+ const GEMINI_NATIVE_ACP_FLAG_MIN_VERSION = [0, 33, 0];
381
+ const GEMINI_VERSION_PROBE_TIMEOUT_MS = 2000;
382
+ export function parseGeminiVersionParts(output) {
383
+ const match = output?.match(/(\d+)\.(\d+)\.(\d+)/);
384
+ if (!match)
385
+ return null;
386
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
387
+ }
388
+ export function geminiVersionSupportsNativeAcpFlag(parts) {
389
+ if (!parts)
390
+ return true;
391
+ for (let index = 0; index < GEMINI_NATIVE_ACP_FLAG_MIN_VERSION.length; index += 1) {
392
+ const diff = (parts[index] ?? 0) - GEMINI_NATIVE_ACP_FLAG_MIN_VERSION[index];
393
+ if (diff !== 0)
394
+ return diff > 0;
395
+ }
396
+ return true;
397
+ }
398
+ export function rewriteGeminiAcpFlagForVersion(commandShell, versionParts) {
399
+ if (geminiVersionSupportsNativeAcpFlag(versionParts))
400
+ return commandShell;
401
+ return commandShell
402
+ .trim()
403
+ .split(/\s+/)
404
+ .map((token) => (token === "--acp" ? "--experimental-acp" : token))
405
+ .join(" ");
406
+ }
407
+ function geminiAcpCommandTokens(commandShell) {
408
+ const tokens = commandShell.trim().split(/\s+/);
409
+ const bin = tokens[0];
410
+ if (!bin || bin.startsWith("'") || bin.startsWith('"'))
411
+ return null;
412
+ if (path.basename(bin) !== "gemini")
413
+ return null;
414
+ if (!tokens.includes("--acp"))
415
+ return null;
416
+ return tokens;
417
+ }
418
+ async function normalizeGeminiAcpCommandShell(commandShell, env) {
419
+ const tokens = geminiAcpCommandTokens(commandShell);
420
+ if (!tokens)
421
+ return commandShell;
422
+ let versionParts = null;
423
+ try {
424
+ const { stdout } = await execFileAsync(tokens[0], ["--version"], {
425
+ timeout: GEMINI_VERSION_PROBE_TIMEOUT_MS,
426
+ encoding: "utf8",
427
+ env,
428
+ });
429
+ versionParts = parseGeminiVersionParts(stdout);
430
+ }
431
+ catch {
432
+ return commandShell;
433
+ }
434
+ return rewriteGeminiAcpFlagForVersion(commandShell, versionParts);
435
+ }
436
+ function normalizeAgent(config) {
437
+ const agent = asString(config.agent, DEFAULT_ACP_ENGINE_AGENT).trim();
438
+ return agent || DEFAULT_ACP_ENGINE_AGENT;
439
+ }
440
+ async function pathExists(candidate) {
441
+ return fs.access(candidate).then(() => true).catch(() => false);
442
+ }
443
+ async function ensureParentDir(target) {
444
+ await fs.mkdir(path.dirname(target), { recursive: true });
445
+ }
446
+ async function writeFileAtomically(input) {
447
+ await ensureParentDir(input.target);
448
+ const tempPath = `${input.target}.tmp-${process.pid}-${randomUUID()}`;
449
+ const handle = await fs.open(tempPath, "wx", input.mode);
450
+ try {
451
+ await handle.writeFile(input.contents, "utf8");
452
+ await handle.close();
453
+ await fs.rename(tempPath, input.target);
454
+ await fs.chmod(input.target, input.mode).catch(() => { });
455
+ }
456
+ catch (err) {
457
+ await handle.close().catch(() => { });
458
+ await fs.rm(tempPath, { force: true }).catch(() => { });
459
+ throw err;
460
+ }
461
+ }
462
+ async function ensureSymlink(target, source) {
463
+ const resolvedSource = path.resolve(source);
464
+ const existing = await fs.lstat(target).catch(() => null);
465
+ if (!existing) {
466
+ await ensureParentDir(target);
467
+ await symlinkOrCopyFile(resolvedSource, target);
468
+ return;
469
+ }
470
+ if (!existing.isSymbolicLink()) {
471
+ await fs.rm(target, { recursive: true, force: true });
472
+ await symlinkOrCopyFile(resolvedSource, target);
473
+ return;
474
+ }
475
+ const linkedPath = await fs.readlink(target).catch(() => null);
476
+ if (!linkedPath)
477
+ return;
478
+ const resolvedLinkedPath = path.resolve(path.dirname(target), linkedPath);
479
+ if (resolvedLinkedPath === resolvedSource)
480
+ return;
481
+ await fs.unlink(target);
482
+ await symlinkOrCopyFile(resolvedSource, target);
483
+ }
484
+ async function symlinkOrCopyFile(source, target) {
485
+ try {
486
+ await fs.symlink(source, target);
487
+ }
488
+ catch (err) {
489
+ if (!isErrnoException(err, "EPERM"))
490
+ throw err;
491
+ await fs.copyFile(source, target);
492
+ }
493
+ }
494
+ function isErrnoException(err, code) {
495
+ return err instanceof Error && "code" in err && err.code === code;
496
+ }
497
+ async function ensureCopiedFile(target, source) {
498
+ if (await pathExists(target))
499
+ return;
500
+ await ensureParentDir(target);
501
+ await fs.copyFile(source, target);
502
+ }
503
+ async function prepareManagedCodexHome(input) {
504
+ const { sourceHome, targetHome, onLog } = input;
505
+ if (path.resolve(sourceHome) === path.resolve(targetHome))
506
+ return targetHome;
507
+ await fs.mkdir(targetHome, { recursive: true });
508
+ const authJson = path.join(sourceHome, "auth.json");
509
+ if (await pathExists(authJson))
510
+ await ensureSymlink(path.join(targetHome, "auth.json"), authJson);
511
+ for (const name of ["config.json", "config.toml", "instructions.md"]) {
512
+ const source = path.join(sourceHome, name);
513
+ if (await pathExists(source))
514
+ await ensureCopiedFile(path.join(targetHome, name), source);
515
+ }
516
+ await onLog("stdout", `[paperclip] Using Paperclip-managed ACPX Codex home "${targetHome}" (seeded from "${sourceHome}").\n`);
517
+ return targetHome;
518
+ }
519
+ async function hashPathContents(candidate, hash, relativePath, seenDirectories) {
520
+ const stat = await fs.lstat(candidate);
521
+ if (stat.isSymbolicLink()) {
522
+ hash.update(`symlink-skipped:${relativePath}\n`);
523
+ return;
524
+ }
525
+ if (stat.isDirectory()) {
526
+ const realDir = await fs.realpath(candidate).catch(() => candidate);
527
+ hash.update(`dir:${relativePath}\n`);
528
+ if (seenDirectories.has(realDir)) {
529
+ hash.update("loop\n");
530
+ return;
531
+ }
532
+ seenDirectories.add(realDir);
533
+ const entries = await fs.readdir(candidate, { withFileTypes: true });
534
+ entries.sort((left, right) => left.name.localeCompare(right.name));
535
+ for (const entry of entries) {
536
+ const childRelativePath = relativePath.length > 0 ? `${relativePath}/${entry.name}` : entry.name;
537
+ await hashPathContents(path.join(candidate, entry.name), hash, childRelativePath, seenDirectories);
538
+ }
539
+ return;
540
+ }
541
+ if (stat.isFile()) {
542
+ hash.update(`file:${relativePath}\n`);
543
+ hash.update(await fs.readFile(candidate));
544
+ hash.update("\n");
545
+ return;
546
+ }
547
+ hash.update(`other:${relativePath}:${stat.mode}\n`);
548
+ }
549
+ async function buildSkillSetKey(input) {
550
+ const hash = createHash("sha256");
551
+ hash.update(`paperclip-acpx-${input.label}-skills:v1\n`);
552
+ const sorted = [...input.skills].sort((left, right) => left.runtimeName.localeCompare(right.runtimeName));
553
+ for (const entry of sorted) {
554
+ hash.update(`skill:${entry.key}:${entry.runtimeName}\n`);
555
+ await hashPathContents(entry.source, hash, entry.runtimeName, new Set());
556
+ }
557
+ return hash.digest("hex");
558
+ }
559
+ async function resolveSelectedRuntimeSkills(config, moduleDir) {
560
+ const allSkills = await readPaperclipRuntimeSkillEntries(config, moduleDir);
561
+ const desiredSkillNames = resolveLegacyPaperclipDesiredSkillNames(config, allSkills);
562
+ const desiredSet = new Set(desiredSkillNames);
563
+ return {
564
+ allSkills,
565
+ // Missing-source entries never mount: buildSkillSetKey hashes each
566
+ // selected entry's path contents, and a nonexistent source would abort
567
+ // runtime construction over one broken skill.
568
+ selectedSkills: allSkills.filter((entry) => desiredSet.has(entry.key) && !isPaperclipSkillSourceMissing(entry)),
569
+ desiredSkillNames,
570
+ };
571
+ }
572
+ async function prepareClaudeSkillRuntime(input) {
573
+ const { allSkills, selectedSkills, desiredSkillNames } = await resolveSelectedRuntimeSkills(input.config, input.moduleDir);
574
+ const skillSetKey = await buildSkillSetKey({ skills: selectedSkills, label: "claude" });
575
+ const bundleRoot = path.join(input.stateDir, "runtime-skills", "claude", skillSetKey);
576
+ const skillsHome = path.join(bundleRoot, ".claude", "skills");
577
+ await fs.mkdir(skillsHome, { recursive: true });
578
+ // A failed materialization, or a materialized copy with no usable
579
+ // `SKILL.md`, must drop the skill from every advertised list below.
580
+ // Otherwise the prompt and the session identity still name a skill whose
581
+ // `SKILL.md` is not in `skillsHome` — either the whole copy failed, or the
582
+ // copy skipped a symlinked `SKILL.md` — and the agent's read of that file
583
+ // fails with a missing-file error, the same symptom this bundle exists to
584
+ // fix.
585
+ const materializedNames = [];
586
+ for (const entry of selectedSkills) {
587
+ const target = path.join(skillsHome, entry.runtimeName);
588
+ try {
589
+ const result = await materializePaperclipSkillCopy(entry.source, target);
590
+ const skillMdStat = await fs.stat(path.join(target, "SKILL.md")).catch(() => null);
591
+ if (!skillMdStat?.isFile()) {
592
+ await fs.rm(target, { recursive: true, force: true });
593
+ await input.onLog("stderr", `[paperclip] Skipped ACPX Claude skill "${entry.key}": the staged copy at ${target} has no usable SKILL.md.\n`);
594
+ continue;
595
+ }
596
+ materializedNames.push(entry.runtimeName);
597
+ if (result.skippedSymlinks.length > 0) {
598
+ await input.onLog("stdout", `[paperclip] Materialized ACPX Claude skill "${entry.runtimeName}" into ${skillsHome} and skipped ${result.skippedSymlinks.length} symlink(s).\n`);
599
+ }
600
+ }
601
+ catch (err) {
602
+ await input.onLog("stderr", `[paperclip] Failed to materialize ACPX Claude skill "${entry.key}" into ${skillsHome}: ${err instanceof Error ? err.message : String(err)}\n`);
603
+ }
604
+ }
605
+ const selectedNames = materializedNames.sort();
606
+ const promptInstructions = selectedNames.length > 0
607
+ ? [
608
+ "Paperclip has materialized selected runtime skills for this ACPX Claude session.",
609
+ `Skill root: ${skillsHome}`,
610
+ `Selected skills: ${selectedNames.join(", ")}`,
611
+ "When a task calls for one of these skills, read its SKILL.md from that root and follow it.",
612
+ ].join("\n")
613
+ : "";
614
+ return {
615
+ identity: {
616
+ mode: "claude",
617
+ skillSetKey,
618
+ desiredSkillNames,
619
+ selectedSkills: selectedNames,
620
+ skillRoot: selectedNames.length > 0 ? skillsHome : null,
621
+ },
622
+ promptInstructions,
623
+ commandNotes: selectedNames.length > 0
624
+ ? [`Materialized ${selectedNames.length} Paperclip skill(s) for ACPX Claude at ${skillsHome}.`]
625
+ : [],
626
+ bundleDir: selectedNames.length > 0 ? skillsHome : null,
627
+ };
628
+ }
629
+ async function readManagedCodexSkillsManifest(skillsHome) {
630
+ const manifestPath = path.join(skillsHome, PAPERCLIP_MANAGED_CODEX_SKILLS_MANIFEST);
631
+ try {
632
+ const raw = JSON.parse(await fs.readFile(manifestPath, "utf8"));
633
+ const parsed = parseObject(raw);
634
+ const skills = Array.isArray(parsed.managedSkillNames)
635
+ ? parsed.managedSkillNames.filter((value) => typeof value === "string" && value.trim().length > 0)
636
+ : [];
637
+ return new Set(skills);
638
+ }
639
+ catch {
640
+ return new Set();
641
+ }
642
+ }
643
+ async function writeManagedCodexSkillsManifest(skillsHome, skillNames) {
644
+ const managedSkillNames = Array.from(new Set(skillNames)).sort();
645
+ await fs.writeFile(path.join(skillsHome, PAPERCLIP_MANAGED_CODEX_SKILLS_MANIFEST), `${JSON.stringify({ version: 1, managedSkillNames }, null, 2)}\n`, "utf8");
646
+ }
647
+ async function removeSkillTarget(target) {
648
+ const existing = await fs.lstat(target).catch(() => null);
649
+ if (!existing)
650
+ return false;
651
+ await fs.rm(target, { recursive: true, force: true });
652
+ return true;
653
+ }
654
+ async function reconcileManagedCodexSkills(input) {
655
+ const desired = new Set(input.selectedSkills.map((entry) => entry.runtimeName));
656
+ const managed = await readManagedCodexSkillsManifest(input.skillsHome);
657
+ const availableByRuntimeName = new Map(input.allSkills.map((entry) => [entry.runtimeName, entry]));
658
+ for (const name of managed) {
659
+ if (desired.has(name))
660
+ continue;
661
+ if (await removeSkillTarget(path.join(input.skillsHome, name))) {
662
+ await input.onLog("stdout", `[paperclip] Revoked ACPX Codex skill "${name}" from ${input.skillsHome}\n`);
663
+ }
664
+ }
665
+ for (const entry of input.allSkills) {
666
+ if (desired.has(entry.runtimeName) || managed.has(entry.runtimeName))
667
+ continue;
668
+ const target = path.join(input.skillsHome, entry.runtimeName);
669
+ const existing = await fs.lstat(target).catch(() => null);
670
+ if (!existing?.isSymbolicLink())
671
+ continue;
672
+ const linkedPath = await fs.readlink(target).catch(() => null);
673
+ if (!linkedPath)
674
+ continue;
675
+ const resolvedLinkedPath = path.resolve(path.dirname(target), linkedPath);
676
+ if (resolvedLinkedPath !== path.resolve(entry.source))
677
+ continue;
678
+ if (await removeSkillTarget(target)) {
679
+ await input.onLog("stdout", `[paperclip] Revoked legacy ACPX Codex skill "${entry.runtimeName}" from ${input.skillsHome}\n`);
680
+ }
681
+ }
682
+ for (const name of managed) {
683
+ if (desired.has(name) || availableByRuntimeName.has(name))
684
+ continue;
685
+ if (await removeSkillTarget(path.join(input.skillsHome, name))) {
686
+ await input.onLog("stdout", `[paperclip] Revoked unavailable ACPX Codex skill "${name}" from ${input.skillsHome}\n`);
687
+ }
688
+ }
689
+ }
690
+ async function prepareCodexSkillRuntime(input) {
691
+ const now = input.now ?? (() => Date.now());
692
+ const envConfig = parseObject(input.config.env);
693
+ const configuredCodexHome = typeof envConfig.CODEX_HOME === "string" && envConfig.CODEX_HOME.trim().length > 0
694
+ ? path.resolve(envConfig.CODEX_HOME.trim())
695
+ : null;
696
+ const sourceCodexHome = typeof process.env.CODEX_HOME === "string" && process.env.CODEX_HOME.trim().length > 0
697
+ ? path.resolve(process.env.CODEX_HOME.trim())
698
+ : path.join(os.homedir(), ".codex");
699
+ const managedCodexHome = resolveManagedCodexHomeDir(input.companyId);
700
+ const effectiveCodexHome = configuredCodexHome ??
701
+ await prepareManagedCodexHome({
702
+ companyId: input.companyId,
703
+ sourceHome: sourceCodexHome,
704
+ targetHome: managedCodexHome,
705
+ onLog: input.onLog,
706
+ });
707
+ const { allSkills, selectedSkills, desiredSkillNames } = await resolveSelectedRuntimeSkills(input.config, input.moduleDir);
708
+ const skillSetKey = await buildSkillSetKey({ skills: selectedSkills, label: "codex" });
709
+ const skillsHome = path.join(effectiveCodexHome, "skills");
710
+ await fs.mkdir(skillsHome, { recursive: true });
711
+ // Step 3 — skills.reconcile: nested inside the codex-home seed (step 2), so it
712
+ // emits its own boundary event and span at this call-site. It must NOT add its
713
+ // wall time to the root work sum. The enclosing step 2 wall already covers this
714
+ // interval, so a second `onWallMs` call would count the same milliseconds
715
+ // twice. Drop `onWallMs` for the nested step; keep every other attribution
716
+ // field.
717
+ const nestedStepMetrics = {
718
+ ...(input.stepMetrics ?? {}),
719
+ onWallMs: undefined,
720
+ };
721
+ await measureStartupStep({ onEvent: input.onEvent }, now, "skills.reconcile", () => reconcileManagedCodexSkills({
722
+ skillsHome,
723
+ allSkills,
724
+ selectedSkills,
725
+ onLog: input.onLog,
726
+ }), nestedStepMetrics);
727
+ for (const entry of selectedSkills) {
728
+ const target = path.join(skillsHome, entry.runtimeName);
729
+ try {
730
+ const result = await materializePaperclipSkillCopy(entry.source, target);
731
+ if (result.skippedSymlinks.length > 0) {
732
+ await input.onLog("stdout", `[paperclip] Materialized ACPX Codex skill "${entry.runtimeName}" into ${skillsHome} and skipped ${result.skippedSymlinks.length} symlink(s).\n`);
733
+ }
734
+ }
735
+ catch (err) {
736
+ await input.onLog("stderr", `[paperclip] Failed to inject ACPX Codex skill "${entry.key}" into ${skillsHome}: ${err instanceof Error ? err.message : String(err)}\n`);
737
+ }
738
+ }
739
+ await writeManagedCodexSkillsManifest(skillsHome, selectedSkills.map((entry) => entry.runtimeName));
740
+ input.env.CODEX_HOME = effectiveCodexHome;
741
+ return {
742
+ identity: {
743
+ mode: "codex",
744
+ skillSetKey,
745
+ desiredSkillNames,
746
+ selectedSkills: selectedSkills.map((entry) => entry.runtimeName).sort(),
747
+ codexHome: effectiveCodexHome,
748
+ skillsHome,
749
+ },
750
+ commandNotes: [`Prepared ACPX Codex skill home at ${skillsHome}.`],
751
+ };
752
+ }
753
+ function resolveGeminiSkillsHome(config) {
754
+ const envConfig = parseObject(config.env);
755
+ const configuredHome = typeof envConfig.HOME === "string" && envConfig.HOME.trim().length > 0
756
+ ? path.resolve(envConfig.HOME.trim())
757
+ : os.homedir();
758
+ return path.join(configuredHome, ".gemini", "skills");
759
+ }
760
+ async function prepareGeminiSkillRuntime(input) {
761
+ const { selectedSkills, desiredSkillNames } = await resolveSelectedRuntimeSkills(input.config, input.moduleDir);
762
+ const skillSetKey = await buildSkillSetKey({ skills: selectedSkills, label: "gemini" });
763
+ const skillsHome = resolveGeminiSkillsHome(input.config);
764
+ await fs.mkdir(skillsHome, { recursive: true });
765
+ const allowedSkillNames = selectedSkills.map((entry) => entry.runtimeName);
766
+ const removedSkills = await removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames);
767
+ for (const skillName of removedSkills) {
768
+ await input.onLog("stdout", `[paperclip] Removed maintainer-only ACPX Gemini skill "${skillName}" from ${skillsHome}\n`);
769
+ }
770
+ for (const entry of selectedSkills) {
771
+ const target = path.join(skillsHome, entry.runtimeName);
772
+ try {
773
+ const result = await ensurePaperclipSkillSymlink(entry.source, target);
774
+ if (result === "created" || result === "repaired") {
775
+ await input.onLog("stdout", `[paperclip] ${result === "repaired" ? "Repaired" : "Linked"} ACPX Gemini skill "${entry.runtimeName}" into ${skillsHome}\n`);
776
+ }
777
+ }
778
+ catch (err) {
779
+ if (isErrnoException(err, "EPERM")) {
780
+ const result = await materializePaperclipSkillCopy(entry.source, target);
781
+ await input.onLog("stdout", `[paperclip] Copied ACPX Gemini skill "${entry.runtimeName}" into ${skillsHome} because symlinks are unavailable.${result.skippedSymlinks.length > 0 ? ` Skipped ${result.skippedSymlinks.length} nested symlink(s).` : ""}\n`);
782
+ continue;
783
+ }
784
+ await input.onLog("stderr", `[paperclip] Failed to link ACPX Gemini skill "${entry.key}" into ${skillsHome}: ${err instanceof Error ? err.message : String(err)}\n`);
785
+ }
786
+ }
787
+ return {
788
+ identity: {
789
+ mode: "gemini",
790
+ skillSetKey,
791
+ desiredSkillNames,
792
+ selectedSkills: selectedSkills.map((entry) => entry.runtimeName).sort(),
793
+ skillsHome,
794
+ },
795
+ commandNotes: selectedSkills.length > 0
796
+ ? [`Prepared ${selectedSkills.length} ACPX Gemini skill(s) at ${skillsHome}.`]
797
+ : [],
798
+ };
799
+ }
800
+ function normalizeMode(config) {
801
+ return asString(config.mode, DEFAULT_ACP_ENGINE_MODE) === "oneshot" ? "oneshot" : "persistent";
802
+ }
803
+ function normalizePermissionMode(config) {
804
+ const value = asString(config.permissionMode, DEFAULT_ACP_ENGINE_PERMISSION_MODE).trim();
805
+ if (value === "approve-reads" || value === "deny-all")
806
+ return value;
807
+ if (value === "default")
808
+ return DEFAULT_ACP_ENGINE_PERMISSION_MODE;
809
+ return "approve-all";
810
+ }
811
+ function normalizeNonInteractivePermissions(config) {
812
+ return asString(config.nonInteractivePermissions, DEFAULT_ACP_ENGINE_NON_INTERACTIVE_PERMISSIONS) === "fail"
813
+ ? "fail"
814
+ : "deny";
815
+ }
816
+ function normalizeRequestedThinkingEffort(config) {
817
+ return (asString(config.modelReasoningEffort, "") ||
818
+ asString(config.reasoningEffort, "") ||
819
+ asString(config.thinkingEffort, "") ||
820
+ asString(config.effort, "")).trim();
821
+ }
822
+ function buildCodexStartupConfig(input) {
823
+ const hasRuntimeConfig = Boolean(input.requestedModel || input.requestedThinkingEffort || input.fastMode);
824
+ if (!hasRuntimeConfig)
825
+ return { value: null, invalidExistingConfig: false };
826
+ let existing = {};
827
+ let invalidExistingConfig = false;
828
+ if (input.existingConfig) {
829
+ try {
830
+ existing = parseObject(JSON.parse(input.existingConfig));
831
+ }
832
+ catch {
833
+ invalidExistingConfig = true;
834
+ existing = {};
835
+ }
836
+ }
837
+ return {
838
+ value: JSON.stringify({
839
+ ...existing,
840
+ ...(input.requestedModel ? { model: input.requestedModel } : {}),
841
+ ...(input.requestedThinkingEffort
842
+ ? { model_reasoning_effort: input.requestedThinkingEffort }
843
+ : {}),
844
+ ...(input.fastMode
845
+ ? {
846
+ service_tier: "fast",
847
+ features: {
848
+ ...parseObject(existing.features),
849
+ fast_mode: true,
850
+ },
851
+ }
852
+ : {}),
853
+ }),
854
+ invalidExistingConfig,
855
+ };
856
+ }
857
+ function isCompatibleSession(params, runtime) {
858
+ if (asString(params.configFingerprint, "") !== runtime.fingerprint)
859
+ return false;
860
+ if (asString(params.sessionKey, "") !== runtime.sessionKey)
861
+ return false;
862
+ if (asString(params.agent, "") !== runtime.acpxAgent)
863
+ return false;
864
+ if (asString(params.mode, "") !== runtime.mode)
865
+ return false;
866
+ const savedCwd = asString(params.cwd, "");
867
+ if (!savedCwd || path.resolve(savedCwd) !== path.resolve(runtime.cwd))
868
+ return false;
869
+ const savedRemote = parseObject(params.remoteExecution);
870
+ return stableJson(savedRemote) === stableJson(runtime.remoteExecutionIdentity ?? {});
871
+ }
872
+ function buildSessionParams(input) {
873
+ const { prepared, handle } = input;
874
+ return {
875
+ sessionKey: prepared.sessionKey,
876
+ runtimeSessionName: handle.runtimeSessionName,
877
+ acpxRecordId: handle.acpxRecordId,
878
+ acpSessionId: handle.backendSessionId,
879
+ agentSessionId: handle.agentSessionId,
880
+ agent: prepared.acpxAgent,
881
+ cwd: prepared.cwd,
882
+ mode: prepared.mode,
883
+ stateDir: prepared.stateDir,
884
+ configFingerprint: prepared.fingerprint,
885
+ ...(prepared.requestedModel ? { model: prepared.requestedModel } : {}),
886
+ ...(prepared.requestedThinkingEffort ? { thinkingEffort: prepared.requestedThinkingEffort } : {}),
887
+ ...(prepared.fastMode ? { fastMode: true } : {}),
888
+ skills: prepared.skillsIdentity,
889
+ mcpServers: prepared.mcpIdentity,
890
+ ...(prepared.workspaceId ? { workspaceId: prepared.workspaceId } : {}),
891
+ ...(prepared.workspaceRepoUrl ? { repoUrl: prepared.workspaceRepoUrl } : {}),
892
+ ...(prepared.workspaceRepoRef ? { repoRef: prepared.workspaceRepoRef } : {}),
893
+ ...(prepared.remoteExecutionIdentity ? { remoteExecution: prepared.remoteExecutionIdentity } : {}),
894
+ };
895
+ }
896
+ function uniqueSorted(values) {
897
+ return [...new Set(values.filter((value) => typeof value === "string" && value.length > 0))].sort();
898
+ }
899
+ // The Claude Code SDK that `claude-agent-acp` runs uses
900
+ // `settingSources: ["user", "project", "local"]`. By writing a per-worktree
901
+ // `.claude/settings.local.json` we override the user's potentially-restrictive
902
+ // `~/.claude/settings.json` (e.g. `defaultMode: "dontAsk"`, which silently
903
+ // denies every non-allowlisted tool and never reaches `canUseTool`), and we
904
+ // widen the SDK's Read sandbox to include the Paperclip state dirs the agent
905
+ // needs to talk to its own control plane.
906
+ async function writePaperclipClaudeSettings(input) {
907
+ const filePath = path.join(input.cwd, ".claude", "settings.local.json");
908
+ const instanceRoot = defaultPaperclipInstanceDir();
909
+ const companyRoot = path.join(instanceRoot, "companies", input.companyId);
910
+ const paperclipAdditionalDirectories = uniqueSorted([
911
+ input.stateDir,
912
+ input.agentHome,
913
+ companyRoot,
914
+ ]);
915
+ const paperclipAllow = uniqueSorted([
916
+ "Bash(curl:*)",
917
+ "Bash(env:*)",
918
+ "Bash(env)",
919
+ `Bash(${input.cwd}/scripts/paperclip-issue-update.sh:*)`,
920
+ `Bash(${input.cwd}/scripts/paperclip:*)`,
921
+ ]);
922
+ let existing = {};
923
+ const existingRaw = await fs.readFile(filePath, "utf8").catch(() => null);
924
+ if (existingRaw) {
925
+ try {
926
+ const parsed = JSON.parse(existingRaw);
927
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
928
+ existing = parsed;
929
+ }
930
+ catch {
931
+ // Malformed settings file — leave it alone in `existing` and our merge will replace it with a valid one.
932
+ }
933
+ }
934
+ const existingPerms = existing.permissions && typeof existing.permissions === "object" && !Array.isArray(existing.permissions)
935
+ ? existing.permissions
936
+ : {};
937
+ const existingAllow = Array.isArray(existingPerms.allow)
938
+ ? existingPerms.allow.filter((value) => typeof value === "string")
939
+ : [];
940
+ const existingAdditionalDirectories = Array.isArray(existingPerms.additionalDirectories)
941
+ ? existingPerms.additionalDirectories.filter((value) => typeof value === "string")
942
+ : [];
943
+ const mergedAllow = uniqueSorted([...existingAllow, ...paperclipAllow]);
944
+ const mergedAdditionalDirectories = uniqueSorted([
945
+ ...existingAdditionalDirectories,
946
+ ...paperclipAdditionalDirectories,
947
+ ]);
948
+ const existingDefaultMode = typeof existingPerms.defaultMode === "string" ? existingPerms.defaultMode : "";
949
+ const defaultMode = existingDefaultMode && existingDefaultMode !== "dontAsk" ? existingDefaultMode : "default";
950
+ const overrodeDontAsk = existingDefaultMode === "dontAsk";
951
+ const nextPermissions = {
952
+ ...existingPerms,
953
+ allow: mergedAllow,
954
+ additionalDirectories: mergedAdditionalDirectories,
955
+ defaultMode,
956
+ };
957
+ const next = { ...existing, permissions: nextPermissions };
958
+ await writeFileAtomically({
959
+ target: filePath,
960
+ contents: `${JSON.stringify(next, null, 2)}\n`,
961
+ mode: 0o600,
962
+ });
963
+ return {
964
+ filePath,
965
+ allow: mergedAllow,
966
+ additionalDirectories: mergedAdditionalDirectories,
967
+ defaultMode,
968
+ overrodeDontAsk,
969
+ };
970
+ }
971
+ // Cross the CLI's staging seam for a runner-backed remote sandbox: ship the
972
+ // workspace (and, in PR 2, the per-adapter managed-home `assets`) into the
973
+ // sandbox and obtain the in-sandbox `workspaceRemoteDir` plus the non-null
974
+ // `runtimeRootDir`/`assetDirs` the bridges and the home remap consume. This is
975
+ // the shared-engine mirror of the CLI lanes (codex/claude/gemini
976
+ // `*-local/execute.ts`). PR 1 shipped the workspace + cwd only; PR 2 threads
977
+ // the home `assets` (built by the per-adapter `prepareRemoteManagedHome` seam,
978
+ // carrying the codex `provision`/`restore` auth seams) through `assets` here so
979
+ // `assetDirs.<key>` resolves to the seeded in-sandbox home. The returned
980
+ // `restoreWorkspace` fires the per-asset `restore` (codex copy-back) at
981
+ // teardown.
982
+ async function stageAcpRemoteRuntime(input) {
983
+ await input.onLog("stdout", `[paperclip] Syncing workspace to ${describeAdapterExecutionTarget(input.target)}.\n`);
984
+ return await prepareAdapterExecutionTargetRuntime({
985
+ runId: input.runId,
986
+ target: input.target,
987
+ adapterKey: input.adapterKey,
988
+ timeoutSec: input.timeoutSec,
989
+ workspaceLocalDir: input.workspaceLocalDir,
990
+ ...(input.workspaceRemoteDir ? { workspaceRemoteDir: input.workspaceRemoteDir } : {}),
991
+ ...(input.assets && input.assets.length > 0 ? { assets: input.assets } : {}),
992
+ ...(input.additionalSources && input.additionalSources.length > 0
993
+ ? { additionalSources: input.additionalSources }
994
+ : {}),
995
+ onProgress: (line) => input.onLog("stdout", line),
996
+ onRuntimeProgress: input.onRuntimeProgress,
997
+ runtimeSpan: input.runtimeSpan,
998
+ });
999
+ }
1000
+ // Dispose a freshly staged in-sandbox runtime after a managed-home seam threw. The
1001
+ // seam shipped the workspace/home into the sandbox through `stage()` but threw
1002
+ // before it could return its `disposeStaged`, so the engine removes the managed
1003
+ // runtime root here — otherwise the abandoned in-sandbox managed home (with its
1004
+ // staged credentials) leaks in a persistent sandbox. Fail-open: a cleanup miss on
1005
+ // an already-failing run is logged and never re-thrown, so it cannot mask the seam
1006
+ // error.
1007
+ async function disposeFreshStagedRuntime(input) {
1008
+ const runtimeRootDir = input.stagedRuntime.runtimeRootDir;
1009
+ if (!runtimeRootDir)
1010
+ return;
1011
+ try {
1012
+ await runAdapterExecutionTargetShellCommand(input.runId, input.target, `rm -rf ${shellQuote(runtimeRootDir)}`, {
1013
+ cwd: input.cwd,
1014
+ env: {},
1015
+ timeoutSec: Math.max(input.timeoutSec, 15),
1016
+ graceSec: 20,
1017
+ onLog: input.onLog,
1018
+ });
1019
+ }
1020
+ catch (err) {
1021
+ await input.onLog("stderr", `[paperclip] Failed to dispose the fresh staged runtime after a managed-home seam error: ${err instanceof Error ? err.message : String(err)}\n`);
1022
+ }
1023
+ }
1024
+ async function buildRuntime(input) {
1025
+ const { runId, agent, config, context, authToken } = input.ctx;
1026
+ // Injectable monotonic clock for per-step startup timing. Hoisted above the
1027
+ // first instrumented boundary (step 1 `workspace.resolve`, below) so every
1028
+ // `measureStartupStep` call in this function shares one deterministic clock.
1029
+ const nowMs = input.deps.now ?? (() => Date.now());
1030
+ const workspaceContext = parseObject(context.paperclipWorkspace);
1031
+ const secretsContext = parseObject(context.paperclipSecrets);
1032
+ const secretManifest = Array.isArray(secretsContext.manifest) ? secretsContext.manifest : [];
1033
+ const workspaceCwd = asString(workspaceContext.cwd, "");
1034
+ const workspaceSource = asString(workspaceContext.source, "");
1035
+ const workspaceStrategy = asString(workspaceContext.strategy, "");
1036
+ const workspaceId = asString(workspaceContext.workspaceId, "");
1037
+ const workspaceRepoUrl = asString(workspaceContext.repoUrl, "");
1038
+ const workspaceRepoRef = asString(workspaceContext.repoRef, "");
1039
+ const workspaceBranch = asString(workspaceContext.branchName, "");
1040
+ const workspaceWorktreePath = asString(workspaceContext.worktreePath, "");
1041
+ const agentHome = asString(workspaceContext.agentHome, "");
1042
+ const configuredCwd = asString(config.cwd, "");
1043
+ const useConfiguredInsteadOfAgentHome = workspaceSource === "agent_home" && configuredCwd.length > 0;
1044
+ const effectiveWorkspaceCwd = useConfiguredInsteadOfAgentHome ? "" : workspaceCwd;
1045
+ const cwd = effectiveWorkspaceCwd || configuredCwd || process.cwd();
1046
+ // Referenced (additional) projects to stage into the sandbox alongside the
1047
+ // anchor workspace, read from the workspace realization record. The list is
1048
+ // empty unless run prep resolved referenced projects — gated upstream by the
1049
+ // multi-project workspace-sync kill-switch — so the anchor-only path is
1050
+ // unchanged.
1051
+ const realizationContext = parseObject(workspaceContext.realization);
1052
+ const additionalSourceRecords = (Array.isArray(realizationContext.additional) ? realizationContext.additional : []).map((entry) => parseObject(entry));
1053
+ const additionalSourceCandidates = additionalSourceRecords
1054
+ .map((entry) => ({
1055
+ localPath: asString(entry.path, ""),
1056
+ projectId: asString(entry.projectId, ""),
1057
+ projectWorkspaceId: asString(entry.projectWorkspaceId, ""),
1058
+ repoUrl: asString(entry.repoUrl, ""),
1059
+ repoRef: asString(entry.repoRef, ""),
1060
+ }))
1061
+ .filter((entry) => entry.localPath.length > 0 && entry.projectId.length > 0);
1062
+ // Resolve each referenced project's Git-ignored paths ONCE, here, before any
1063
+ // staging site runs. The sandbox lane, the SSH lane, and the content signature
1064
+ // below all consume this SAME resolution per project, so they can never apply
1065
+ // a different exclusion set to the same project. See
1066
+ // `resolveReferencedSourceIgnore` for the fail-closed rules.
1067
+ const additionalSourcesWithIgnore = await Promise.all(additionalSourceCandidates.map(async (entry) => ({
1068
+ ...entry,
1069
+ ignoreResolution: await resolveReferencedSourceIgnore(entry.localPath),
1070
+ })));
1071
+ const additionalSources = additionalSourcesWithIgnore.map((entry) => ({
1072
+ localPath: entry.localPath,
1073
+ projectId: entry.projectId,
1074
+ ignoreResolution: entry.ignoreResolution,
1075
+ }));
1076
+ // Stable identity of the referenced-project set for the session fingerprint.
1077
+ // The staged-runtime cache reuses already-staged referenced-project trees on a
1078
+ // compatible resume, so the fingerprint must change when the set OR a project's
1079
+ // pinned checkout changes. Without this, a resume reuses a stale staged tree.
1080
+ // Fold in each project's id, host path, workspace id, and pinned ref; sort by
1081
+ // projectId so the identity depends on the set, not the record order.
1082
+ const additionalSourcesIdentityBase = additionalSourcesWithIgnore
1083
+ .map((entry) => ({
1084
+ projectId: entry.projectId,
1085
+ localPath: entry.localPath,
1086
+ projectWorkspaceId: entry.projectWorkspaceId,
1087
+ repoUrl: entry.repoUrl,
1088
+ repoRef: entry.repoRef,
1089
+ ignoreResolution: entry.ignoreResolution,
1090
+ }))
1091
+ .sort((a, b) => (a.projectId < b.projectId ? -1 : a.projectId > b.projectId ? 1 : 0));
1092
+ // Metadata alone does not change on a content-only checkout change (same host
1093
+ // path and pinned ref, new file bytes). Fold in each tree's content signature so
1094
+ // a file add, remove, or edit busts the fingerprint and the resume re-stages.
1095
+ // The signature reads the same `ignoreResolution` the staging sites above use,
1096
+ // so it never disagrees with what was actually shipped.
1097
+ const additionalSourcesIdentity = await Promise.all(additionalSourcesIdentityBase.map(async ({ ignoreResolution, ...entry }) => ({
1098
+ ...entry,
1099
+ contentSignature: await referencedSourceContentSignature(entry.localPath, ignoreResolution),
1100
+ })));
1101
+ // Referenced-project workspace hints exposed to the agent through PAPERCLIP_WORKSPACES_JSON. The
1102
+ // list joins the anchor project's alternative workspaces with the referenced (mentioned) projects.
1103
+ // On the confined sandbox lane the run repoints each referenced hint at its staged directory after
1104
+ // staging below. Empty unless run prep resolved referenced projects or alternative workspaces.
1105
+ const workspaceHints = Array.isArray(context.paperclipWorkspaces)
1106
+ ? context.paperclipWorkspaces.filter((value) => typeof value === "object" && value !== null)
1107
+ : [];
1108
+ const executionTarget = readAdapterExecutionTarget({
1109
+ executionTarget: input.ctx.executionTarget,
1110
+ legacyRemoteExecution: input.ctx.executionTransport?.remoteExecution,
1111
+ });
1112
+ const remoteExecutionIdentity = adapterExecutionTargetSessionIdentity(executionTarget);
1113
+ const effectiveExecutionCwd = remoteExecutionIdentity && typeof remoteExecutionIdentity.remoteCwd === "string"
1114
+ ? remoteExecutionIdentity.remoteCwd
1115
+ : cwd;
1116
+ const executionTargetIsRemote = remoteExecutionIdentity !== null;
1117
+ // Merge the injected tracer + root parent-context into every step option set,
1118
+ // so each boundary span parents to the root span. With no injected trace
1119
+ // context both fields are no-ops and the span path stays inert.
1120
+ const stepMetrics = {
1121
+ ...input.spanParent,
1122
+ };
1123
+ // The two bridge-start steps intentionally overlap. A shared `batch` tag marks
1124
+ // the two spans as one parallel batch, and `criticalPath: false` keeps their
1125
+ // inner exec spans off the critical path (their wall time overlaps).
1126
+ const concurrentBridgeStepMetrics = {
1127
+ ...input.spanParent,
1128
+ batch: STARTUP_BRIDGE_BATCH,
1129
+ criticalPath: false,
1130
+ };
1131
+ const shapedWorkspaceEnv = shapePaperclipWorkspaceEnvForExecution({
1132
+ workspaceCwd: effectiveWorkspaceCwd,
1133
+ workspaceWorktreePath,
1134
+ executionTargetIsRemote,
1135
+ executionCwd: effectiveExecutionCwd,
1136
+ });
1137
+ // Step 1 — workspace.resolve: the workspace resolution/fallback chain closes
1138
+ // here on the awaited directory materialization.
1139
+ await measureStartupStep(input.ctx, nowMs, "workspace.resolve", () => ensureAbsoluteDirectory(cwd, { createIfMissing: true }), stepMetrics);
1140
+ const acpxAgent = normalizeAgent(config);
1141
+ // Run summaries always fail closed to the final output segment so internal
1142
+ // thought text and intermediate narration cannot become issue comments.
1143
+ const coalescePlaceholderToolUpdates = config.coalescePlaceholderToolUpdates === true;
1144
+ const mode = normalizeMode(config);
1145
+ const permissionMode = normalizePermissionMode(config);
1146
+ const nonInteractivePermissions = normalizeNonInteractivePermissions(config);
1147
+ const requestedModel = asString(config.model, "").trim();
1148
+ const requestedThinkingEffort = normalizeRequestedThinkingEffort(config);
1149
+ const fastMode = acpxAgent === "codex" && config.fastMode === true;
1150
+ const runtimeMcpServers = input.ctx.runtimeMcp?.getServers() ?? [];
1151
+ const mcpIdentity = runtimeMcpServers.map(({ name, url, connectionId }) => ({
1152
+ name,
1153
+ url,
1154
+ connectionId,
1155
+ }));
1156
+ const mcpServers = runtimeMcpServers.map((server) => ({
1157
+ type: "http",
1158
+ name: server.name,
1159
+ url: server.url,
1160
+ headers: [{ name: "Authorization", value: `Bearer ${server.token}` }],
1161
+ }));
1162
+ // Resolve the wall-clock timeout through the shared execution-target
1163
+ // resolver so sandbox-backed runs pick up the 4h backstop default while
1164
+ // local/SSH runs keep the historical "0 = no adapter timeout" behavior.
1165
+ const timeoutResolution = resolveAdapterExecutionTargetTimeout(executionTarget, asNumber(config.timeoutSec, DEFAULT_ACP_ENGINE_TIMEOUT_SEC));
1166
+ const timeoutSec = timeoutResolution.timeoutSec;
1167
+ const stateDir = path.resolve(asString(config.stateDir, "") || defaultStateDir(agent.companyId, agent.id));
1168
+ await fs.mkdir(stateDir, { recursive: true });
1169
+ const envConfig = parseObject(config.env);
1170
+ const env = { ...buildPaperclipEnv(agent), PAPERCLIP_RUN_ID: runId };
1171
+ const wakeTaskId = (typeof context.taskId === "string" && context.taskId.trim()) ||
1172
+ (typeof context.issueId === "string" && context.issueId.trim()) ||
1173
+ "";
1174
+ const wakeReason = typeof context.wakeReason === "string" ? context.wakeReason.trim() : "";
1175
+ const wakeCommentId = (typeof context.wakeCommentId === "string" && context.wakeCommentId.trim()) ||
1176
+ (typeof context.commentId === "string" && context.commentId.trim()) ||
1177
+ "";
1178
+ const approvalId = typeof context.approvalId === "string" ? context.approvalId.trim() : "";
1179
+ const approvalStatus = typeof context.approvalStatus === "string" ? context.approvalStatus.trim() : "";
1180
+ const linkedIssueIds = Array.isArray(context.issueIds)
1181
+ ? context.issueIds.filter((value) => typeof value === "string" && value.trim().length > 0)
1182
+ : [];
1183
+ const issueWorkMode = readPaperclipIssueWorkModeFromContext(context);
1184
+ if (wakeTaskId)
1185
+ env.PAPERCLIP_TASK_ID = wakeTaskId;
1186
+ if (issueWorkMode)
1187
+ env.PAPERCLIP_ISSUE_WORK_MODE = issueWorkMode;
1188
+ if (wakeReason)
1189
+ env.PAPERCLIP_WAKE_REASON = wakeReason;
1190
+ if (wakeCommentId)
1191
+ env.PAPERCLIP_WAKE_COMMENT_ID = wakeCommentId;
1192
+ if (approvalId)
1193
+ env.PAPERCLIP_APPROVAL_ID = approvalId;
1194
+ if (approvalStatus)
1195
+ env.PAPERCLIP_APPROVAL_STATUS = approvalStatus;
1196
+ if (linkedIssueIds.length > 0)
1197
+ env.PAPERCLIP_LINKED_ISSUE_IDS = linkedIssueIds.join(",");
1198
+ applyPaperclipWorkspaceEnv(env, {
1199
+ workspaceCwd: shapedWorkspaceEnv.workspaceCwd,
1200
+ workspaceSource,
1201
+ workspaceStrategy,
1202
+ workspaceId,
1203
+ workspaceRepoUrl,
1204
+ workspaceRepoRef,
1205
+ workspaceBranch,
1206
+ workspaceWorktreePath: shapedWorkspaceEnv.workspaceWorktreePath,
1207
+ agentHome,
1208
+ });
1209
+ const shapedEnvConfig = rewriteWorkspaceCwdEnvVarsForExecution({
1210
+ env: envConfig,
1211
+ workspaceCwd: effectiveWorkspaceCwd,
1212
+ executionCwd: shapedWorkspaceEnv.workspaceCwd,
1213
+ executionTargetIsRemote,
1214
+ });
1215
+ // Resolved adapter env (plain + server-resolved secret_ref values) that we
1216
+ // forward to the spawned agent process. Captured so a stable hash of it can be
1217
+ // folded into the session fingerprint below — a change here must invalidate a
1218
+ // warm/resumable session so the next launch picks up the latest env. Only
1219
+ // user/adapter-configured env flows through this loop; per-wake PAPERCLIP_*
1220
+ // runtime vars (PAPERCLIP_RUN_ID, wake/approval ids, ...) were assigned to
1221
+ // `env` above and are never present in shapedEnvConfig, so they inherently
1222
+ // stay out of the hash and don't reset the session every heartbeat.
1223
+ const resolvedAdapterEnv = {};
1224
+ const scratch = parseObject(context.paperclipScratch);
1225
+ const scratchKeys = scratch.type === "heartbeat_run" && typeof scratch.dir === "string"
1226
+ ? new Set(["PAPERCLIP_RUN_SCRATCH_DIR", "PAPERCLIP_TASK_SCRATCH_DIR", "PAPERCLIP_SCRATCH_DIR", "PAPERCLIP_TMPDIR",
1227
+ ...(Array.isArray(scratch.tempKeysApplied) ? scratch.tempKeysApplied.filter((key) => typeof key === "string" && ["TMPDIR", "TEMP", "TMP"].includes(key)) : [])])
1228
+ : new Set();
1229
+ for (const [key, value] of Object.entries(shapedEnvConfig)) {
1230
+ if (typeof value !== "string")
1231
+ continue;
1232
+ // Runtime PAPERCLIP_* always wins over config: skip a PAPERCLIP_* key that
1233
+ // Paperclip has already assigned this run. PAPERCLIP_API_KEY is never
1234
+ // accepted from config — the harness-minted run token is the only source.
1235
+ // A PAPERCLIP_* key Paperclip did NOT set is stable per-run config, so it
1236
+ // applies and feeds the fingerprint hash below.
1237
+ if (isForbiddenConfigEnvKey(key))
1238
+ continue;
1239
+ if (isPaperclipRuntimeEnvKey(key) && key in env)
1240
+ continue;
1241
+ env[key] = value;
1242
+ // The server rotates run-owned scratch paths on every wake. Still forward
1243
+ // them, but only hash actual adapter settings. User-supplied temp overrides
1244
+ // are absent from tempKeysApplied and keep their compatibility protection.
1245
+ if (!scratchKeys.has(key) || value !== scratch.dir)
1246
+ resolvedAdapterEnv[key] = value;
1247
+ }
1248
+ if (authToken)
1249
+ env.PAPERCLIP_API_KEY = authToken;
1250
+ // For the claude agent, set model via ANTHROPIC_MODEL at startup rather than
1251
+ // via session/set_config_option — the ACP server's set_config_option handler
1252
+ // validates the value against its internal available-models list and rejects
1253
+ // bare model IDs (e.g. "claude-opus-4-7") that don't exactly match a model
1254
+ // entry in some versions. ANTHROPIC_MODEL is read during initialization, so
1255
+ // it reliably sets the model before any turns are run.
1256
+ if (requestedModel && acpxAgent === "claude" && !env.ANTHROPIC_MODEL) {
1257
+ env.ANTHROPIC_MODEL = requestedModel;
1258
+ }
1259
+ if (acpxAgent === "codex") {
1260
+ const codexStartupConfig = buildCodexStartupConfig({
1261
+ existingConfig: env.CODEX_CONFIG,
1262
+ requestedModel,
1263
+ requestedThinkingEffort,
1264
+ fastMode,
1265
+ });
1266
+ if (codexStartupConfig.invalidExistingConfig) {
1267
+ await input.ctx.onLog("stderr", "[paperclip] Ignoring invalid user CODEX_CONFIG while applying runtime Codex settings; expected a JSON object.\n");
1268
+ }
1269
+ if (codexStartupConfig.value)
1270
+ env.CODEX_CONFIG = codexStartupConfig.value;
1271
+ }
1272
+ let skillPromptInstructions = "";
1273
+ let skillsIdentity = { mode: "unsupported" };
1274
+ const skillCommandNotes = [];
1275
+ // The host directory a remote run stages as the `skills` asset. The engine
1276
+ // uses it to rewrite `skillPromptInstructions` and `skillsIdentity` onto
1277
+ // the in-sandbox copy, once `stagedRuntime` is known (see the rewrite
1278
+ // below, after `placeWorkspace` returns). This field is `null` for every
1279
+ // non-Claude agent, and for a Claude run with no skill selected.
1280
+ let claudeSkillsBundleDir = null;
1281
+ let paperclipClaudeSettings = null;
1282
+ if (acpxAgent === "claude") {
1283
+ const preparedSkills = await prepareClaudeSkillRuntime({
1284
+ stateDir,
1285
+ config,
1286
+ moduleDir: input.engine.moduleDir,
1287
+ onLog: input.ctx.onLog,
1288
+ });
1289
+ skillPromptInstructions = preparedSkills.promptInstructions;
1290
+ skillsIdentity = preparedSkills.identity;
1291
+ skillCommandNotes.push(...preparedSkills.commandNotes);
1292
+ claudeSkillsBundleDir = preparedSkills.bundleDir;
1293
+ paperclipClaudeSettings = await writePaperclipClaudeSettings({
1294
+ cwd,
1295
+ stateDir,
1296
+ agentHome,
1297
+ companyId: agent.companyId,
1298
+ });
1299
+ skillCommandNotes.push(`Wrote Paperclip-managed Claude settings to ${paperclipClaudeSettings.filePath} (defaultMode=${paperclipClaudeSettings.defaultMode}${paperclipClaudeSettings.overrodeDontAsk ? "; overrode user dontAsk" : ""}, +${paperclipClaudeSettings.additionalDirectories.length} read root(s), +${paperclipClaudeSettings.allow.length} allow rule(s)).`);
1300
+ }
1301
+ else if (acpxAgent === "codex") {
1302
+ // Step 2 — codex-home.seed: the codex managed-home + skills preparation.
1303
+ // The nested skills.reconcile boundary (step 3) is timed inside via the
1304
+ // threaded onEvent/now seam.
1305
+ const preparedSkills = await measureStartupStep(input.ctx, nowMs, "codex-home.seed", () => prepareCodexSkillRuntime({
1306
+ companyId: agent.companyId,
1307
+ config,
1308
+ env,
1309
+ moduleDir: input.engine.moduleDir,
1310
+ onLog: input.ctx.onLog,
1311
+ onEvent: input.ctx.onEvent,
1312
+ now: nowMs,
1313
+ stepMetrics,
1314
+ }), stepMetrics);
1315
+ skillsIdentity = preparedSkills.identity;
1316
+ skillCommandNotes.push(...preparedSkills.commandNotes);
1317
+ }
1318
+ else if (acpxAgent === "gemini") {
1319
+ const preparedSkills = await prepareGeminiSkillRuntime({
1320
+ config,
1321
+ moduleDir: input.engine.moduleDir,
1322
+ onLog: input.ctx.onLog,
1323
+ });
1324
+ skillsIdentity = preparedSkills.identity;
1325
+ skillCommandNotes.push(...preparedSkills.commandNotes);
1326
+ }
1327
+ else {
1328
+ // A minimal, separate Grok seam: only the company-scoped `GROK_HOME`
1329
+ // binding, so a Grok run authenticates from the credential a completed
1330
+ // device login wrote. This never touches `prepareCodexSkillRuntime` above
1331
+ // — that function stays Codex-only — and every other custom ACPX agent
1332
+ // (for example `kimi`) falls through this branch unaffected.
1333
+ if (acpxAgent === "grok" && !config.managedAiConnection) {
1334
+ env.GROK_HOME = resolveManagedGrokHomeDir(agent.companyId);
1335
+ }
1336
+ const desired = resolveLegacyPaperclipDesiredSkillNames(config, await readPaperclipRuntimeSkillEntries(config, input.engine.moduleDir));
1337
+ skillsIdentity = { mode: "custom_unsupported", desiredSkillNames: desired };
1338
+ if (desired.length > 0) {
1339
+ skillCommandNotes.push("Selected Paperclip skills are tracked only; ACPX custom commands do not expose a runtime skill contract yet.");
1340
+ }
1341
+ }
1342
+ const configuredCommand = asString(config.agentCommand, "").trim();
1343
+ const builtInCommand = await resolveBuiltInAgentCommand({
1344
+ agent: acpxAgent,
1345
+ packageRootDir: input.engine.packageRootDir,
1346
+ executionTargetIsRemote,
1347
+ });
1348
+ let agentCommand = configuredCommand || builtInCommand?.command || null;
1349
+ let agentCommandShell = configuredCommand || builtInCommand?.shellCommand || "";
1350
+ // A runner-backed remote sandbox is the only lane that crosses the staging
1351
+ // and serialized-launch-env seam. Runner-less ACP→CLI fallback, SSH, and
1352
+ // local runs keep their historical host-provider compatibility behavior.
1353
+ const useRemoteProcessSession = executionTarget?.kind === "remote" &&
1354
+ executionTarget.transport === "sandbox" &&
1355
+ Boolean(executionTarget.runner) &&
1356
+ Boolean(agentCommandShell);
1357
+ if (acpxAgent === "gemini" && agentCommandShell) {
1358
+ const normalized = await normalizeGeminiAcpCommandShell(agentCommandShell, resolveRuntimeEnv(env, acpxAgent, {
1359
+ inheritHostEnvironment: !useRemoteProcessSession,
1360
+ }));
1361
+ if (normalized !== agentCommandShell) {
1362
+ agentCommandShell = normalized;
1363
+ agentCommand = normalized;
1364
+ }
1365
+ }
1366
+ const childStderrDir = path.join(stateDir, "run-stderr");
1367
+ const childStderrLogPath = agentCommand ? path.join(childStderrDir, `${runId}.log`) : null;
1368
+ // Stream the agent output through the persistent session log stream instead of
1369
+ // the host output-file poll. The decision comes from the effective capability
1370
+ // snapshot alone: the provider must declare and verify incremental session
1371
+ // output. `incrementalSessionOutput` is an opt-in capability, so a generic
1372
+ // one-shot provider that keeps persistent process sessions and runs independent
1373
+ // control commands, yet never emits incremental session output, keeps the poll
1374
+ // path. The snapshot resolves this key false when it is absent, undeclared, or
1375
+ // capability resolution failed, so this fails closed to the poll path.
1376
+ const streamAgentSessionOutput = executionTarget?.kind === "remote" &&
1377
+ executionTarget.transport === "sandbox" &&
1378
+ executionTarget.effectiveCapabilities?.incrementalSessionOutput === true;
1379
+ // The ACP `session/new` cwd and every cwd-keyed session-state site
1380
+ // (fingerprint, compat, persist, ensureSession, error) bind to THIS single
1381
+ // value so a warm/resumable session created with the in-sandbox cwd is reused
1382
+ // — not invalidated — on the next run. Remote runner-backed → the in-sandbox
1383
+ // workspace dir; local and the runner-less fallback → the HOST cwd,
1384
+ // byte-identical to today.
1385
+ //
1386
+ // PR 3: the staging transport derives the in-sandbox workspace dir
1387
+ // deterministically from the target's `remoteCwd` (it is exactly `remoteCwd`
1388
+ // for the sandbox transport), so we resolve `sessionCwd` — and therefore the
1389
+ // session fingerprint / cache key — BEFORE staging. That lets a compatible
1390
+ // resume decide to reuse an already-staged runtime instead of re-shipping the
1391
+ // workspace / re-seeding the managed home. The stage call below pins its
1392
+ // `workspaceRemoteDir` to this same value, so the staged cwd can never
1393
+ // diverge from the cwd that fed the fingerprint.
1394
+ const sessionCwd = useRemoteProcessSession && executionTarget?.kind === "remote"
1395
+ ? executionTarget.remoteCwd
1396
+ : cwd;
1397
+ // The 17 fields the session fingerprint hashes. Company, agent, and task
1398
+ // identifiers are NOT here; they scope the outer session key only (see
1399
+ // `keyIdentity`). The fingerprint builder accepts only this identity.
1400
+ const fingerprintIdentity = {
1401
+ acpxAgent,
1402
+ agentCommand: agentCommand ?? acpxAgent,
1403
+ cwd: path.resolve(sessionCwd),
1404
+ mode,
1405
+ permissionMode,
1406
+ nonInteractivePermissions,
1407
+ requestedModel,
1408
+ requestedThinkingEffort,
1409
+ fastMode,
1410
+ remoteExecutionIdentity,
1411
+ // Referenced-project set + pinned-checkout identity. A change here (a project
1412
+ // added, removed, or re-pinned) invalidates a warm/resumable session so the
1413
+ // next launch stages the current referenced-project trees instead of reusing
1414
+ // a stale staged tree.
1415
+ additionalSourcesIdentity: additionalSourcesIdentity,
1416
+ skillsIdentity,
1417
+ skillPromptInstructions,
1418
+ paperclipClaudeSettings: paperclipClaudeSettings
1419
+ ? {
1420
+ allow: paperclipClaudeSettings.allow,
1421
+ additionalDirectories: paperclipClaudeSettings.additionalDirectories,
1422
+ defaultMode: paperclipClaudeSettings.defaultMode,
1423
+ }
1424
+ : null,
1425
+ mcpServers: mcpIdentity,
1426
+ secretManifestHash: shortHash(secretManifest),
1427
+ // Fold the resolved adapter env (all applied user-configured values —
1428
+ // plain, secret_ref, and stable PAPERCLIP_* config such as an explicit
1429
+ // PAPERCLIP_API_KEY) into the fingerprint so a change to any forwarded value
1430
+ // invalidates a warm handle / resumable session and forces a fresh launch
1431
+ // that sources the latest env. secretManifestHash alone misses plain-value
1432
+ // edits and same-version secret rotations. Per-wake runtime vars never enter
1433
+ // resolvedAdapterEnv, so they don't churn the fingerprint every heartbeat.
1434
+ adapterEnvHash: shortHash(resolvedAdapterEnv),
1435
+ };
1436
+ const fingerprint = buildSessionFingerprint(fingerprintIdentity);
1437
+ const taskKey = asString(input.ctx.runtime.taskKey, "") || wakeTaskId || workspaceId || "default";
1438
+ // Company, agent, and task identity for the outer session key. These parts
1439
+ // stay out of the fingerprint hash.
1440
+ const keyIdentity = {
1441
+ companyId: agent.companyId,
1442
+ agentId: agent.id,
1443
+ taskKey,
1444
+ };
1445
+ const sessionKey = buildSessionKey(keyIdentity, fingerprint);
1446
+ // Ship the workspace into the sandbox and capture `{ workspaceRemoteDir,
1447
+ // runtimeRootDir, assetDirs, restoreWorkspace }`. Done once here, before the
1448
+ // bridges, so both bridges receive the real (non-null) `runtimeRootDir`.
1449
+ //
1450
+ // PR 2: on the remote lane, delegate staging to the per-adapter
1451
+ // `prepareRemoteManagedHome` seam when the adapter supplies one. The seam
1452
+ // ships the adapter's managed home as an `assets` entry (through the `stage`
1453
+ // callback = `stageAcpRemoteRuntime`), repoints the home env var (`env`) onto
1454
+ // the in-sandbox `assetDirs.*` path, and returns a `teardown` (per-run codex
1455
+ // auth copy-back via `restoreWorkspace()`) plus a `disposeStaged` (one-time
1456
+ // staged-temp cleanup). Without a seam (custom agents / shared-engine tests)
1457
+ // the engine stages the workspace with no home asset — identical to PR-1.
1458
+ //
1459
+ // PR 3 (stage once per session): a COMPATIBLE resume whose fingerprint matches
1460
+ // this exact `sessionKey` reuses the already-staged in-sandbox runtime — no
1461
+ // workspace re-ship, no home re-seed — while an incompatible fingerprint (a
1462
+ // different key) misses the cache and stages fresh. The `sessionKey`
1463
+ // (`companyId:agentId:taskKey:fingerprint`) is the single scoping key, so one
1464
+ // session can never read another session's staged credentials. The cache is
1465
+ // populated by the executor only after a clean turn and dropped on
1466
+ // failure/cancel/timeout, so it always holds a known-good staged runtime.
1467
+ //
1468
+ // Two guards close the concurrency / cross-session windows Greptile flagged:
1469
+ // * Compatibility gate: reuse only when the supplied session params actually
1470
+ // resume THIS staged session (the same `isCompatibleSession` predicate the
1471
+ // warm-handle path uses). A fresh invocation with missing/cleared
1472
+ // `sessionParams` starts a new ACP session via `session/new`, so it must
1473
+ // NOT inherit the prior session's staged home/credentials — it stages
1474
+ // fresh even when company/agent/task/fingerprint (and hence sessionKey)
1475
+ // collide.
1476
+ // * Per-key staging lock: the stage-or-reuse decision runs under a
1477
+ // `sessionKey` mutex so two overlapping runs of the same session can never
1478
+ // ship into the same remote workspace at once (the loser waits, then
1479
+ // re-checks the cache before deciding).
1480
+ const stagedRuntimes = input.deps.stagedRuntimes ?? defaultStagedRuntimes;
1481
+ const stagingLocks = input.deps.stagingLocks ?? defaultStagingLocks;
1482
+ const previousParams = parseObject(input.ctx.runtime.sessionParams);
1483
+ const isCompatibleResume = isCompatibleSession(previousParams, {
1484
+ fingerprint,
1485
+ sessionKey,
1486
+ cwd: sessionCwd,
1487
+ mode,
1488
+ acpxAgent,
1489
+ remoteExecutionIdentity,
1490
+ });
1491
+ let stagedRuntime = null;
1492
+ let remoteManagedHomeTeardown = null;
1493
+ let remoteStagingDispose = null;
1494
+ let remoteStagingEnvDelta = null;
1495
+ let sessionStagingLeaseRelease = null;
1496
+ // The sandbox run site owns the sandbox lane's staging, the per-session
1497
+ // staging lease, both host-side bridges, the launch-environment contribution,
1498
+ // and sync-back. `buildRuntime` injects the leaf primitives (the workspace
1499
+ // stage call, the managed-home seam, the two bridge starts, the step timers,
1500
+ // and the launch-env finalizer) and reads the site's results back onto the
1501
+ // prepared-runtime fields, so the existing settlement path stays unchanged.
1502
+ let sandboxSite = null;
1503
+ if (useRemoteProcessSession && executionTarget?.kind === "remote") {
1504
+ const remoteTarget = executionTarget;
1505
+ sandboxSite = createSandboxRunSite({
1506
+ ledger: input.ledger,
1507
+ stagedRuntimes,
1508
+ stagingLocks,
1509
+ now: nowMs,
1510
+ idleMs: input.stagedIdleMs,
1511
+ sessionCwd,
1512
+ spawnCwd: cwd,
1513
+ target: remoteTarget,
1514
+ env,
1515
+ isCompatibleResume,
1516
+ stage: (assets) => stageAcpRemoteRuntime({
1517
+ runId,
1518
+ target: remoteTarget,
1519
+ adapterKey: input.engine.adapterType,
1520
+ workspaceLocalDir: cwd,
1521
+ workspaceRemoteDir: sessionCwd,
1522
+ timeoutSec,
1523
+ assets,
1524
+ additionalSources,
1525
+ onLog: input.ctx.onLog,
1526
+ onRuntimeProgress: input.ctx.onRuntimeProgress,
1527
+ runtimeSpan: input.stageRuntimeSpan,
1528
+ }),
1529
+ seedManagedHome: input.deps.prepareRemoteManagedHome
1530
+ ? async (stage) => {
1531
+ const seeded = await input.deps.prepareRemoteManagedHome({
1532
+ acpxAgent,
1533
+ companyId: agent.companyId,
1534
+ runId,
1535
+ config,
1536
+ executionTarget: remoteTarget,
1537
+ workspaceLocalDir: cwd,
1538
+ timeoutSec,
1539
+ env,
1540
+ onLog: input.ctx.onLog,
1541
+ onRuntimeProgress: input.ctx.onRuntimeProgress,
1542
+ skillsBundleDir: claudeSkillsBundleDir,
1543
+ stage,
1544
+ });
1545
+ return {
1546
+ stagedRuntime: seeded.stagedRuntime,
1547
+ teardown: seeded.teardown ?? null,
1548
+ dispose: seeded.disposeStaged ?? null,
1549
+ };
1550
+ }
1551
+ : undefined,
1552
+ disposeFreshStagedRuntime: (freshStagedRuntime) => disposeFreshStagedRuntime({
1553
+ runId,
1554
+ target: remoteTarget,
1555
+ stagedRuntime: freshStagedRuntime,
1556
+ cwd: sessionCwd,
1557
+ timeoutSec,
1558
+ onLog: input.ctx.onLog,
1559
+ }),
1560
+ measureStageStep: (run) => measureStartupStep(input.ctx, nowMs, "stage.sync", run, stepMetrics),
1561
+ publishStagedProjectHints: (stagedProjectDirs) => {
1562
+ const shapedHints = shapePaperclipWorkspaceEnvForExecution({
1563
+ workspaceCwd: effectiveWorkspaceCwd,
1564
+ workspaceWorktreePath,
1565
+ workspaceHints,
1566
+ executionTargetIsRemote,
1567
+ executionCwd: effectiveExecutionCwd,
1568
+ stagedProjectDirs,
1569
+ }).workspaceHints;
1570
+ if (shapedHints.length > 0) {
1571
+ env.PAPERCLIP_WORKSPACES_JSON = JSON.stringify(shapedHints);
1572
+ }
1573
+ },
1574
+ onReuseLog: () => input.ctx.onLog("stdout", "[paperclip] Reusing the staged in-sandbox runtime for this resumed session (no workspace re-ship / managed-home re-seed).\n"),
1575
+ startPaperclipBridge: (runtimeRootDir) => startAdapterExecutionTargetPaperclipBridge({
1576
+ runId,
1577
+ target: { ...remoteTarget, streamRunLogs: false },
1578
+ runtimeRootDir,
1579
+ adapterKey: input.engine.adapterType,
1580
+ timeoutSec,
1581
+ hostApiToken: env.PAPERCLIP_API_KEY,
1582
+ enableSandboxDuplexBridge: adapterExecutionTargetEnablesSandboxDuplexBridge(remoteTarget),
1583
+ duplexObservabilityRecorder: adapterExecutionTargetDuplexObservabilityRecorder(remoteTarget),
1584
+ onLog: input.ctx.onLog,
1585
+ getRuntimeParentContext: input.getRuntimeParentContext,
1586
+ runtimeSpan: input.runtimeSpan,
1587
+ }),
1588
+ startProcessSessionBridge: ({ runtimeRootDir, launchEnv }) => startAdapterExecutionTargetProcessSessionBridge({
1589
+ runId,
1590
+ target: remoteTarget,
1591
+ runtimeRootDir,
1592
+ adapterKey: input.engine.adapterType,
1593
+ command: "sh",
1594
+ args: ["-lc", `exec ${agentCommandShell}`],
1595
+ cwd: sessionCwd,
1596
+ env: launchEnv,
1597
+ timeoutSec,
1598
+ onLog: input.ctx.onLog,
1599
+ getRuntimeParentContext: input.getRuntimeParentContext,
1600
+ runtimeSpan: input.runtimeSpan,
1601
+ streamOutputViaSession: streamAgentSessionOutput,
1602
+ }),
1603
+ measureBridgeStep: (step, run) => measureStartupStep(input.ctx, nowMs, step, run, concurrentBridgeStepMetrics),
1604
+ finalizeLaunchEnv: (contributions) => finalizeLaunchEnvironment(env, contributions, {
1605
+ acpxAgent,
1606
+ inheritHostEnvironment: !useRemoteProcessSession,
1607
+ }).env,
1608
+ onPaperclipBridgeLog: () => input.ctx.onLog("stdout", "[paperclip] Sandbox ACP API callback bridge enabled for this run.\n"),
1609
+ stopBridges: async ({ controlBridge, agentBridge }) => {
1610
+ await Promise.allSettled([agentBridge?.stop(), controlBridge?.stop()]);
1611
+ if (remoteManagedHomeTeardown) {
1612
+ await remoteManagedHomeTeardown().catch(() => { });
1613
+ }
1614
+ },
1615
+ });
1616
+ // Place the workspace (stage fresh or reuse the already-staged runtime) under
1617
+ // the per-session staging lease, then read the staged result back onto the
1618
+ // prepared-runtime fields the settlement path consumes.
1619
+ const placeWorkspaceStart = nowMs();
1620
+ await sandboxSite.placeWorkspace({ sessionKey });
1621
+ await emitRunPhaseTiming(input.ctx, "place_workspace", nowMs() - placeWorkspaceStart, "ok");
1622
+ const placedStaged = sandboxSite.staged;
1623
+ stagedRuntime = placedStaged?.stagedRuntime ?? null;
1624
+ remoteManagedHomeTeardown = placedStaged?.teardown ?? null;
1625
+ remoteStagingDispose = placedStaged?.dispose ?? null;
1626
+ remoteStagingEnvDelta = placedStaged?.envDelta ?? null;
1627
+ sessionStagingLeaseRelease = sandboxSite.stagingLeaseRelease;
1628
+ }
1629
+ // Once the skill bundle is staged, rewrite the prompt and the identity onto
1630
+ // the in-sandbox copy. This code runs here, after `placeWorkspace` resolves
1631
+ // `stagedRuntime`. It runs on every invocation, both a fresh stage and a
1632
+ // compatible resume. It never runs inside the `prepareRemoteManagedHome`
1633
+ // seam, because a compatible resume never calls that seam again.
1634
+ // `skillPromptInstructions` and `skillsIdentity` already fed `fingerprint`
1635
+ // above, with the host-independent identity. So this rewrite never reaches
1636
+ // the fingerprint: it only replaces the host bundle path with the
1637
+ // in-sandbox path, in the local copies used for the returned prompt,
1638
+ // identity, and command notes.
1639
+ if (acpxAgent === "claude" && stagedRuntime && claudeSkillsBundleDir) {
1640
+ const inSandboxSkillsRoot = stagedRuntime.assetDirs.skills ??
1641
+ path.posix.join(stagedRuntime.runtimeRootDir ??
1642
+ path.posix.join(stagedRuntime.workspaceRemoteDir ?? cwd, ".paperclip-runtime", acpxAgent), "skills");
1643
+ const rebaseToSandbox = (value) => value.split(claudeSkillsBundleDir).join(inSandboxSkillsRoot);
1644
+ skillPromptInstructions = rebaseToSandbox(skillPromptInstructions);
1645
+ skillsIdentity = {
1646
+ ...skillsIdentity,
1647
+ skillRoot: typeof skillsIdentity.skillRoot === "string"
1648
+ ? rebaseToSandbox(skillsIdentity.skillRoot)
1649
+ : skillsIdentity.skillRoot,
1650
+ };
1651
+ for (let i = 0; i < skillCommandNotes.length; i += 1) {
1652
+ skillCommandNotes[i] = rebaseToSandbox(skillCommandNotes[i]);
1653
+ }
1654
+ }
1655
+ // Both bridge starts run under one try so a failure at EITHER — including the
1656
+ // paperclip callback bridge — fires the same abandon-path cleanup. The
1657
+ // paperclip bridge starts after the workspace + managed home were already
1658
+ // staged and the per-session staging lease is already held, so leaving it
1659
+ // outside the catch would strand the lease (and the staged temp) on a
1660
+ // start failure and deadlock the next run of this session.
1661
+ let paperclipBridge = null;
1662
+ let processSessionBridge = null;
1663
+ let runtimeEnv = {};
1664
+ const startTransportStart = nowMs();
1665
+ try {
1666
+ if (useRemoteProcessSession && sandboxSite) {
1667
+ // The sandbox run site brings up both host-side bridges concurrently, keeps
1668
+ // the one paperclip-env → process-session-launch dependency at a single
1669
+ // sequencing point, settles both starts, and returns the started handles
1670
+ // plus the finalized launch env. On a partial failure it stops nothing and
1671
+ // rethrows; the catch below stops whichever bridge the site started.
1672
+ const transport = await sandboxSite.startTransport({ sessionKey });
1673
+ paperclipBridge = transport.controlBridge;
1674
+ processSessionBridge = transport.agentBridge;
1675
+ runtimeEnv = transport.launchEnv;
1676
+ await emitRunPhaseTiming(input.ctx, "start_transport", nowMs() - startTransportStart, "ok");
1677
+ }
1678
+ else {
1679
+ // Local / runner-less lanes never start a bridge, so they add no
1680
+ // contribution. `finalizeLaunchEnvironment` still produces the one branded
1681
+ // launch env the prepared runtime and the log builder read.
1682
+ runtimeEnv = finalizeLaunchEnvironment(env, [], {
1683
+ acpxAgent,
1684
+ inheritHostEnvironment: !useRemoteProcessSession,
1685
+ }).env;
1686
+ }
1687
+ }
1688
+ catch (err) {
1689
+ // On a partial concurrent bring-up failure, ONE bridge may have started while
1690
+ // the other threw; `Promise.allSettled` stops whichever started so no live
1691
+ // bridge leaks (mirrors the settlement `stopTransport` step). The site sets its
1692
+ // started bridges before it rethrows, so read them from the site here (the
1693
+ // local handles stay null when `startTransport` throws before it returns).
1694
+ const startedControl = sandboxSite?.controlBridge ?? paperclipBridge;
1695
+ const startedAgent = sandboxSite?.agentBridge ?? processSessionBridge;
1696
+ await Promise.allSettled([startedControl?.stop(), startedAgent?.stop()]);
1697
+ // The staged home / copy-back teardown must run even if a bridge fails to
1698
+ // start after the workspace + managed home were already staged into the
1699
+ // sandbox, so a refreshed credential is copied back on this error path too.
1700
+ // This run never reaches the executor, so also fire the one-time staged-temp
1701
+ // dispose here (it no longer rides the per-run copy-back) — the run is being
1702
+ // abandoned, so its staged temp must be released — and release the per-session
1703
+ // staging lease so the abandoned run does not strand the next same-session run
1704
+ // (the run root `finally`, which normally releases it, is never reached here).
1705
+ //
1706
+ // Route the dispose through the same ownership-guarded removal
1707
+ // `discardStagedRuntime` uses. When this run BORROWED the cached staged runtime
1708
+ // (a compatible resume), the dispose releases the shared host staged-temp, so
1709
+ // the map entry must go too — otherwise a later resume reuses an entry whose
1710
+ // temp is already gone. The identity guard removes the entry only when the map
1711
+ // still holds this exact staged runtime, so a fresh stage (no cache entry) and
1712
+ // a concurrent run's entry are both left untouched.
1713
+ await remoteManagedHomeTeardown?.().catch(() => { });
1714
+ await releaseStagedRuntimeEntry({
1715
+ handles: stagedRuntimes,
1716
+ sessionKey,
1717
+ stagedRuntime,
1718
+ dispose: remoteStagingDispose,
1719
+ });
1720
+ sessionStagingLeaseRelease?.();
1721
+ await emitRunPhaseTiming(input.ctx, "start_transport", nowMs() - startTransportStart, "failed");
1722
+ throw err;
1723
+ }
1724
+ // The relay runs on the host with the sanitized remote launch environment.
1725
+ // Its /usr/bin/env node shebang cannot rely on that environment's PATH.
1726
+ const overrideCommand = processSessionBridge?.agentCommand
1727
+ ? [process.execPath, processSessionBridge.agentCommand]
1728
+ .map((part) => JSON.stringify(part.replaceAll("\\", "/"))).join(" ")
1729
+ : agentCommand;
1730
+ const overrides = overrideCommand ? { [acpxAgent]: overrideCommand } : undefined;
1731
+ const agentRegistry = createAgentRegistry({ overrides });
1732
+ const loggedEnv = buildInvocationEnvForLogs(env, {
1733
+ runtimeEnv,
1734
+ includeRuntimeKeys: ["HOME"],
1735
+ resolvedCommand: agentCommand ?? acpxAgent,
1736
+ });
1737
+ return {
1738
+ acpxAgent,
1739
+ coalescePlaceholderToolUpdates,
1740
+ mode,
1741
+ // Remote runner-backed → the in-sandbox workspace dir; local / runner-less
1742
+ // → the HOST cwd (`sessionCwd` resolves both). Every cwd-keyed session site
1743
+ // reads `prepared.cwd`, so binding it once here keeps them consistent.
1744
+ cwd: sessionCwd,
1745
+ // Only the remote process-session lane needs the host proxy's `spawn()`
1746
+ // `chdir` redirected off the in-sandbox `sessionCwd` and onto the host
1747
+ // `cwd` (which is where the workspace was staged FROM, so it is host-valid).
1748
+ // Every other lane leaves it `undefined` → acpx falls back to `cwd`.
1749
+ hostSpawnCwd: useRemoteProcessSession ? cwd : undefined,
1750
+ workspaceId,
1751
+ workspaceRepoUrl,
1752
+ workspaceRepoRef,
1753
+ env: runtimeEnv,
1754
+ loggedEnv,
1755
+ stateDir,
1756
+ permissionMode,
1757
+ nonInteractivePermissions,
1758
+ requestedModel,
1759
+ requestedThinkingEffort,
1760
+ fastMode,
1761
+ timeoutSec,
1762
+ timeoutResolution,
1763
+ sessionKey,
1764
+ fingerprint,
1765
+ agentCommand,
1766
+ agentRegistry,
1767
+ processSessionBridge,
1768
+ paperclipBridge,
1769
+ stagedRuntime,
1770
+ remoteManagedHomeTeardown,
1771
+ remoteStagingDispose,
1772
+ remoteStagingEnvDelta,
1773
+ sessionStagingLeaseRelease,
1774
+ remoteExecutionIdentity,
1775
+ skillPromptInstructions,
1776
+ skillsIdentity: {
1777
+ ...skillsIdentity,
1778
+ commandNotes: skillCommandNotes,
1779
+ },
1780
+ childStderrLogPath,
1781
+ paperclipClaudeSettings,
1782
+ mcpServers,
1783
+ mcpIdentity,
1784
+ stepMetrics,
1785
+ };
1786
+ }
1787
+ function sessionConfigOptions(prepared) {
1788
+ const options = [];
1789
+ // Claude and Codex runtime config is pre-set via startup env vars; skip
1790
+ // set_config_option to avoid ACP-server picker validation rejecting valid
1791
+ // backend model IDs that are not advertised by the local ACP server.
1792
+ if (prepared.requestedModel &&
1793
+ prepared.acpxAgent !== "claude" &&
1794
+ prepared.acpxAgent !== "codex") {
1795
+ options.push({ key: "model", value: prepared.requestedModel });
1796
+ }
1797
+ if (prepared.requestedThinkingEffort && prepared.acpxAgent !== "codex") {
1798
+ options.push({
1799
+ key: "effort",
1800
+ value: prepared.requestedThinkingEffort,
1801
+ });
1802
+ }
1803
+ if (prepared.fastMode && prepared.acpxAgent !== "codex") {
1804
+ options.push({ key: "service_tier", value: "fast" }, { key: "features.fast_mode", value: "true" });
1805
+ }
1806
+ return options;
1807
+ }
1808
+ async function applySessionConfigOptions(input) {
1809
+ const options = sessionConfigOptions(input.prepared);
1810
+ if (options.length === 0)
1811
+ return;
1812
+ if (!input.runtime.setConfigOption) {
1813
+ const message = "ACPX runtime does not expose session config controls; upgrade ACPX or remove configured model, effort, and fast mode overrides.";
1814
+ await input.onLog("stderr", `[paperclip] ${message}\n`);
1815
+ throw new Error(message);
1816
+ }
1817
+ for (const option of options) {
1818
+ await input.runtime.setConfigOption({
1819
+ handle: input.handle,
1820
+ key: option.key,
1821
+ value: option.value,
1822
+ });
1823
+ await input.onLog("stdout", `[paperclip] Applied ACPX ${input.prepared.acpxAgent} config ${option.key}=${option.value}\n`);
1824
+ }
1825
+ }
1826
+ /**
1827
+ * Build the process-session launch env: the target-specific host projection
1828
+ * overlaid with the run's explicit `env` (so adapter config, runtime variables,
1829
+ * and bridge contributions win), narrowed to string values. Host-side launches
1830
+ * get the closed projection plus a default `PATH` when the host did not provide
1831
+ * one. Runner-backed remote sandbox launches get no ambient host state or
1832
+ * synthesized host `PATH`; omitting `PATH` preserves the sandbox-native value.
1833
+ */
1834
+ function resolveRuntimeEnv(env, acpxAgent, options) {
1835
+ const inheritedEnv = options.inheritedEnv ?? process.env;
1836
+ const projectedHostEnv = projectAcpxInheritedHostEnvironment(inheritedEnv, acpxAgent, options.inheritHostEnvironment);
1837
+ const inheritedLaunchEnv = options.inheritHostEnvironment
1838
+ ? ensurePathInEnv(projectedHostEnv)
1839
+ : projectedHostEnv;
1840
+ const mergedEnv = mergeRuntimeEnvironment(inheritedLaunchEnv, env, (options.platform ?? process.platform) === "win32");
1841
+ const finalEnv = Object.fromEntries(Object.entries(mergedEnv).filter((entry) => typeof entry[1] === "string"));
1842
+ // codex-acp supports both key names, but ACP clients must select its
1843
+ // api-key authentication method during session creation. Without this
1844
+ // request, the server advertises authentication and rejects session/new even
1845
+ // though the credential is present in the launched process environment. Check
1846
+ // the final merged environment, not just the explicit run config, so a host
1847
+ // key the local launch inherits still selects this default.
1848
+ if (acpxAgent === "codex" &&
1849
+ (finalEnv.OPENAI_API_KEY || finalEnv.CODEX_API_KEY) &&
1850
+ !finalEnv.DEFAULT_AUTH_REQUEST) {
1851
+ finalEnv.DEFAULT_AUTH_REQUEST = JSON.stringify({ methodId: "api-key" });
1852
+ }
1853
+ return finalEnv;
1854
+ }
1855
+ function mergeRuntimeEnvironment(inheritedEnv, explicitEnv, caseInsensitiveKeys) {
1856
+ if (!caseInsensitiveKeys) {
1857
+ return { ...inheritedEnv, ...explicitEnv };
1858
+ }
1859
+ const merged = {};
1860
+ const keyByCanonicalName = new Map();
1861
+ const apply = (source) => {
1862
+ for (const [key, value] of Object.entries(source)) {
1863
+ if (typeof value !== "string")
1864
+ continue;
1865
+ const canonicalName = key.toUpperCase();
1866
+ const previousKey = keyByCanonicalName.get(canonicalName);
1867
+ if (previousKey !== undefined && previousKey !== key) {
1868
+ delete merged[previousKey];
1869
+ }
1870
+ merged[key] = value;
1871
+ keyByCanonicalName.set(canonicalName, key);
1872
+ }
1873
+ };
1874
+ apply(inheritedEnv);
1875
+ apply(explicitEnv);
1876
+ return merged;
1877
+ }
1878
+ // Stop both host-side bridges in one `allSettled`. This is the settlement
1879
+ // `stopTransport` effect. The bridge tokens are run-scoped, so they die with the
1880
+ // bridges here (Amendment B).
1881
+ async function stopRunTransport(prepared) {
1882
+ await Promise.allSettled([
1883
+ prepared.processSessionBridge?.stop(),
1884
+ prepared.paperclipBridge?.stop(),
1885
+ ]);
1886
+ }
1887
+ // The site sync-back. This is the settlement `syncBack` effect. It runs AFTER the
1888
+ // bridges stop (mirrors the CLI finally: stop bridge → restore workspace). It
1889
+ // fires the codex auth copy-back via `restoreWorkspace()` and removes staged temp
1890
+ // dirs. The seam logs and swallows its own failures — an unclean-teardown
1891
+ // copy-back miss is the accepted, loud `refresh_token_reused` residual on the
1892
+ // next host Codex use, never silent HOST-credential corruption — so a teardown
1893
+ // fault never masks or fails the run result here. It still returns the restore
1894
+ // outcome, so the caller can record a failure on the run record; the run's exit
1895
+ // code and status stay exactly what the turn produced.
1896
+ // The per-session staging lease does NOT release here. The settlement releases
1897
+ // it last, in its own `finally`, so a same-session second run cannot re-stage
1898
+ // until this run fully settles and the caller observes the result.
1899
+ async function syncBackManagedHome(prepared) {
1900
+ if (!prepared.remoteManagedHomeTeardown) {
1901
+ return { ok: true };
1902
+ }
1903
+ // The teardown closure already catches and logs its own error (fail-soft);
1904
+ // this `.catch` is defense in depth for the case where it rejects anyway, so
1905
+ // a teardown fault can never propagate out of settlement.
1906
+ return await prepared
1907
+ .remoteManagedHomeTeardown()
1908
+ .catch(() => ({ ok: false, code: "restore_failed" }));
1909
+ }
1910
+ // ACP startup handshake guard and late-completion fence.
1911
+ //
1912
+ // `runtime.ensureSession()` comes from the external `acpx/runtime` package. It
1913
+ // takes no `AbortSignal`, so the engine cannot cancel it; it can only walk
1914
+ // away from the promise. `guardEnsureSession` races the call against a fixed
1915
+ // deadline and a poll of the duplex control-channel disposition, and rejects
1916
+ // with one of these two host-generated, typed errors the moment either
1917
+ // condition trips. The error type — not its message — is what
1918
+ // `classifyError` reads, so a startup timeout can never collapse into the
1919
+ // session-identity failure code.
1920
+ //
1921
+ // A guard rejection does not stop the external call. `HandshakeAbandonmentFence`
1922
+ // gives that abandoned promise one owner: a resolution that arrives after the
1923
+ // engine walked away can never become the live session handle, the promise
1924
+ // stays observed so it can never raise an unhandled rejection, and the real
1925
+ // handle is closed exactly once, whichever side — the settlement step or the
1926
+ // fence itself — ends up seeing it first.
1927
+ class AcpxHandshakeTimeoutError extends Error {
1928
+ acpxHandshakeGuardKind = "timeout";
1929
+ constructor() {
1930
+ super("The ACP startup handshake did not finish before the startup deadline.");
1931
+ this.name = "AcpxHandshakeTimeoutError";
1932
+ }
1933
+ }
1934
+ class AcpxHandshakeTransportLostError extends Error {
1935
+ acpxHandshakeGuardKind = "transport_lost";
1936
+ constructor() {
1937
+ super("The sandbox duplex control channel was lost during the ACP startup handshake.");
1938
+ this.name = "AcpxHandshakeTransportLostError";
1939
+ }
1940
+ }
1941
+ function isAcpxHandshakeTimeoutError(err) {
1942
+ return err instanceof AcpxHandshakeTimeoutError;
1943
+ }
1944
+ function isAcpxHandshakeTransportLostError(err) {
1945
+ return err instanceof AcpxHandshakeTransportLostError;
1946
+ }
1947
+ /**
1948
+ * Gives one abandoned `ensureSession` promise one owner. Construct one fence
1949
+ * per run and reuse it across both `ensureSession` call sites (the first
1950
+ * attempt and the fresh-session retry): only one of those two calls can ever
1951
+ * be the one a guard rejection abandons, because the retry only runs after
1952
+ * the first call already settled on its own.
1953
+ *
1954
+ * `seal()` is the idempotent boundary: only its first call can flip the
1955
+ * state and hand back a handle that arrived before it ran. A later call is a
1956
+ * no-op, so `endSession` can call it unconditionally as its first operation
1957
+ * on every settlement path, not only a handshake-guard one.
1958
+ */
1959
+ class HandshakeAbandonmentFence {
1960
+ sealedFlag = false;
1961
+ lateHandle = null;
1962
+ get isSealed() {
1963
+ return this.sealedFlag;
1964
+ }
1965
+ /** Record a late resolution that arrived before `seal()` ran. */
1966
+ storeLateHandle(handle) {
1967
+ if (this.sealedFlag)
1968
+ return;
1969
+ this.lateHandle = handle;
1970
+ }
1971
+ /** Idempotent. Only the first call can return a handle that beat it here. */
1972
+ seal() {
1973
+ if (this.sealedFlag)
1974
+ return null;
1975
+ this.sealedFlag = true;
1976
+ const handle = this.lateHandle;
1977
+ this.lateHandle = null;
1978
+ return handle;
1979
+ }
1980
+ }
1981
+ /**
1982
+ * Race one `ensureSession()` call against the startup guard. Whichever
1983
+ * settles first wins; `outcome` records which one, so the promise's own
1984
+ * `.then` (attached synchronously, right here, before this function returns)
1985
+ * can tell a genuinely late settlement apart from the one the race already
1986
+ * delivered through its normal return value.
1987
+ *
1988
+ * The deadline and the transport-loss poll both start now, at the call site,
1989
+ * not earlier. The bridge disposition this polls is a latch: once a loss
1990
+ * orders, every later read still reports it, so a loss that happened before
1991
+ * this call started polling is still caught on the first tick.
1992
+ */
1993
+ function guardEnsureSession(params) {
1994
+ const sessionPromise = params.call();
1995
+ let outcome = "pending";
1996
+ // Attached the moment the promise exists. A guard-won race still leaves
1997
+ // this promise observed for as long as it takes to settle, so it can never
1998
+ // raise an unhandled rejection.
1999
+ sessionPromise.then((handle) => {
2000
+ if (outcome !== "guard")
2001
+ return;
2002
+ if (params.fence.isSealed) {
2003
+ params.onLateHandleAfterSeal(handle);
2004
+ }
2005
+ else {
2006
+ params.fence.storeLateHandle(handle);
2007
+ }
2008
+ }, () => {
2009
+ if (outcome !== "guard")
2010
+ return;
2011
+ params.onLateRejection();
2012
+ });
2013
+ return new Promise((resolve, reject) => {
2014
+ let settled = false;
2015
+ const timer = setTimeout(() => {
2016
+ if (settled)
2017
+ return;
2018
+ settled = true;
2019
+ outcome = "guard";
2020
+ clearInterval(poll);
2021
+ reject(new AcpxHandshakeTimeoutError());
2022
+ }, ACPX_HANDSHAKE_TIMEOUT_MS);
2023
+ const poll = setInterval(() => {
2024
+ if (settled)
2025
+ return;
2026
+ if (!params.isTransportLost())
2027
+ return;
2028
+ settled = true;
2029
+ outcome = "guard";
2030
+ clearTimeout(timer);
2031
+ clearInterval(poll);
2032
+ reject(new AcpxHandshakeTransportLostError());
2033
+ }, ACPX_HANDSHAKE_TRANSPORT_POLL_MS);
2034
+ sessionPromise.then((handle) => {
2035
+ if (settled)
2036
+ return;
2037
+ settled = true;
2038
+ outcome = "session";
2039
+ clearTimeout(timer);
2040
+ clearInterval(poll);
2041
+ resolve(handle);
2042
+ }, (err) => {
2043
+ if (settled)
2044
+ return;
2045
+ settled = true;
2046
+ outcome = "session";
2047
+ clearTimeout(timer);
2048
+ clearInterval(poll);
2049
+ reject(err);
2050
+ });
2051
+ });
2052
+ }
2053
+ function renderPaperclipEnvNote(env) {
2054
+ const paperclipKeys = Object.keys(env)
2055
+ .filter((key) => key.startsWith("PAPERCLIP_"))
2056
+ .sort();
2057
+ if (paperclipKeys.length === 0)
2058
+ return "";
2059
+ return [
2060
+ "Paperclip runtime note:",
2061
+ `The following PAPERCLIP_* environment variables are available in this run: ${paperclipKeys.join(", ")}`,
2062
+ "Do not assume these variables are missing without checking your shell environment.",
2063
+ ].join("\n");
2064
+ }
2065
+ function renderApiAccessNote(env) {
2066
+ if (!env.PAPERCLIP_API_URL || !env.PAPERCLIP_API_KEY)
2067
+ return "";
2068
+ const lines = [
2069
+ "Paperclip API access note:",
2070
+ "Use terminal commands with curl to make Paperclip API requests.",
2071
+ "Normalize the base URL before adding API paths:",
2072
+ ` PAPERCLIP_API_BASE="\${PAPERCLIP_API_URL%/}"; PAPERCLIP_API_BASE="\${PAPERCLIP_API_BASE%/api}"`,
2073
+ "GET example:",
2074
+ ` curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" "$PAPERCLIP_API_BASE/api/agents/me"`,
2075
+ ];
2076
+ if (env.PAPERCLIP_TASK_ID) {
2077
+ lines.push("Scoped issue comment example:", ` curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" -H "Content-Type: application/json" -H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" -d '{"body":"Status update from agent."}' "$PAPERCLIP_API_BASE/api/issues/$PAPERCLIP_TASK_ID/comments"`);
2078
+ }
2079
+ else {
2080
+ lines.push("Use a real issue id from the current context before making issue write requests.");
2081
+ }
2082
+ return lines.join("\n");
2083
+ }
2084
+ async function buildPrompt(ctx, resumedSession, env) {
2085
+ const { agent, runId, config, context, onLog } = ctx;
2086
+ const configuredPromptTemplate = asString(config.promptTemplate, "");
2087
+ const hasCustomPromptTemplate = configuredPromptTemplate.trim().length > 0;
2088
+ const promptTemplate = hasCustomPromptTemplate
2089
+ ? configuredPromptTemplate
2090
+ : context.conversationMode === true
2091
+ ? DEFAULT_PAPERCLIP_CONVERSATION_PROMPT_TEMPLATE
2092
+ : DEFAULT_PAPERCLIP_AGENT_PROMPT_TEMPLATE;
2093
+ const instructionsFilePath = asString(config.instructionsFilePath, "").trim();
2094
+ const instructionsDir = instructionsFilePath ? `${path.dirname(instructionsFilePath)}/` : "";
2095
+ let instructionsPrefix = "";
2096
+ const commandNotes = [];
2097
+ if (instructionsFilePath) {
2098
+ try {
2099
+ const instructionsContents = await fs.readFile(instructionsFilePath, "utf8");
2100
+ instructionsPrefix =
2101
+ `${instructionsContents}\n\n` +
2102
+ `The above agent instructions were loaded from ${instructionsFilePath}. ` +
2103
+ `Resolve any relative file references from ${instructionsDir}.\n\n`;
2104
+ commandNotes.push(`Loaded agent instructions from ${instructionsFilePath}`, `Prepended instructions + path directive to the ACPX prompt (relative references from ${instructionsDir}).`);
2105
+ }
2106
+ catch (err) {
2107
+ const reason = err instanceof Error ? err.message : String(err);
2108
+ await onLog("stderr", `[paperclip] Warning: could not read agent instructions file "${instructionsFilePath}": ${reason}\n`);
2109
+ commandNotes.push(`Configured instructionsFilePath ${instructionsFilePath}, but file could not be read.`);
2110
+ }
2111
+ }
2112
+ const bootstrapPromptTemplate = asString(config.bootstrapPromptTemplate, "");
2113
+ const templateData = {
2114
+ agentId: agent.id,
2115
+ companyId: agent.companyId,
2116
+ runId,
2117
+ company: { id: agent.companyId },
2118
+ agent,
2119
+ run: { id: runId, source: "on_demand" },
2120
+ context,
2121
+ };
2122
+ const renderedBootstrapPrompt = !resumedSession && bootstrapPromptTemplate.trim().length > 0
2123
+ ? renderTemplate(bootstrapPromptTemplate, templateData).trim()
2124
+ : "";
2125
+ const taskContextNote = selectPaperclipTaskMarkdown(context, { resumedSession });
2126
+ const externalChatTurn = isPaperclipExternalChatTurn(context.paperclipWake);
2127
+ const wakePrompt = renderPaperclipWakePrompt(context.paperclipWake, {
2128
+ resumedSession,
2129
+ conversationMode: context.conversationMode === true,
2130
+ // The task-context markdown is the authoritative brief on this lane; keep
2131
+ // the wake prompt's description copy out so the prompt carries it once.
2132
+ suppressIssueDescription: taskContextNote.length > 0,
2133
+ });
2134
+ const shouldUseResumeDeltaPrompt = resumedSession && wakePrompt.length > 0;
2135
+ const promptInstructionsPrefix = shouldUseResumeDeltaPrompt ? "" : instructionsPrefix;
2136
+ const renderedPrompt = shouldUseResumeDeltaPrompt || (externalChatTurn && !hasCustomPromptTemplate)
2137
+ ? ""
2138
+ : renderTemplate(promptTemplate, templateData);
2139
+ const sessionHandoffNote = asString(context.paperclipSessionHandoffMarkdown, "").trim();
2140
+ const paperclipEnvNote = externalChatTurn ? "" : renderPaperclipEnvNote(env);
2141
+ const apiAccessNote = externalChatTurn ? "" : renderApiAccessNote(env);
2142
+ const prompt = joinPromptSections([
2143
+ promptInstructionsPrefix,
2144
+ renderedBootstrapPrompt,
2145
+ wakePrompt,
2146
+ sessionHandoffNote,
2147
+ taskContextNote,
2148
+ paperclipEnvNote,
2149
+ apiAccessNote,
2150
+ renderedPrompt,
2151
+ ]);
2152
+ return {
2153
+ prompt,
2154
+ commandNotes,
2155
+ promptMetrics: {
2156
+ promptChars: prompt.length,
2157
+ instructionsChars: promptInstructionsPrefix.length,
2158
+ bootstrapPromptChars: renderedBootstrapPrompt.length,
2159
+ wakePromptChars: wakePrompt.length,
2160
+ sessionHandoffChars: sessionHandoffNote.length,
2161
+ taskContextChars: taskContextNote.length,
2162
+ runtimeNoteChars: paperclipEnvNote.length + apiAccessNote.length,
2163
+ heartbeatPromptChars: renderedPrompt.length,
2164
+ },
2165
+ };
2166
+ }
2167
+ async function emitAcpxLog(ctx, payload) {
2168
+ await ctx.onLog("stdout", `${JSON.stringify(payload)}\n`);
2169
+ }
2170
+ /**
2171
+ * Build the short run summary that Paperclip may auto-post as an issue comment
2172
+ * when the agent leaves no comment of its own.
2173
+ *
2174
+ * Prefer the last non-empty *output* segment after a tool call. Intermediate
2175
+ * "let me check…" narration between tools must not become a 50k-char dump.
2176
+ * Thought-stream text is never included (callers must not push it into segments).
2177
+ */
2178
+ export function buildAcpxRunSummary(input) {
2179
+ for (let i = input.outputSegments.length - 1; i >= 0; i -= 1) {
2180
+ const text = (input.outputSegments[i] ?? "").trim();
2181
+ if (text)
2182
+ return text;
2183
+ }
2184
+ const fallback = (input.fallback ?? "").trim();
2185
+ return fallback;
2186
+ }
2187
+ // acpx substitutes a literal "tool call" title when an ACP tool_call_update
2188
+ // omits one, which would persist a generic name over the real one ("Terminal",
2189
+ // "Read", …) in the stored run log. Remember each call's real title so update
2190
+ // lines keep the name durably. Some ACP backends also stream partial tool
2191
+ // arguments as one in-progress update per token under this placeholder;
2192
+ // adapters that opt into coalescePlaceholderToolUpdates in their acpx config
2193
+ // have those updates coalesced until a real title is available.
2194
+ const GENERIC_ACP_TOOL_TITLE = "tool call";
2195
+ async function emitRuntimeEvent(ctx, event, toolTitles, coalescePlaceholderToolUpdates) {
2196
+ if (event.type === "text_delta") {
2197
+ await emitAcpxLog(ctx, {
2198
+ type: "acpx.text_delta",
2199
+ text: event.text,
2200
+ channel: event.stream === "thought" ? "thought" : "output",
2201
+ tag: event.tag,
2202
+ });
2203
+ return;
2204
+ }
2205
+ if (event.type === "tool_call") {
2206
+ // Coalesce token-by-token argument streaming for adapters that opt in via
2207
+ // their acpx config: skip in-progress updates that still carry only the
2208
+ // unresolved placeholder title. Backends that stream tool arguments
2209
+ // otherwise emit tens of thousands of these per run, flooding the
2210
+ // transcript and pinning the live activity indicator to a generic
2211
+ // "tool call" instead of the real tool. The initial pending event, the
2212
+ // resolved-title in-progress update, and the terminal
2213
+ // completed/failed/cancelled update all still flow through. Adapters that
2214
+ // do not opt in never have an event dropped.
2215
+ if (coalescePlaceholderToolUpdates &&
2216
+ event.status === "in_progress" &&
2217
+ (event.title ?? "").trim() === GENERIC_ACP_TOOL_TITLE) {
2218
+ return;
2219
+ }
2220
+ const eventRecord = event;
2221
+ const toolInput = eventRecord.input;
2222
+ let name = event.title ?? "acp_tool";
2223
+ const toolCallId = typeof event.toolCallId === "string" ? event.toolCallId : "";
2224
+ if (toolTitles && toolCallId) {
2225
+ if (event.title && event.title !== GENERIC_ACP_TOOL_TITLE) {
2226
+ // First real title is the call's identity; later retitles (ACP swaps
2227
+ // in the invocation, e.g. "Terminal" → "ls -la") keep their own line
2228
+ // but don't become the remembered name.
2229
+ if (!toolTitles.has(toolCallId))
2230
+ toolTitles.set(toolCallId, event.title);
2231
+ }
2232
+ else {
2233
+ name = toolTitles.get(toolCallId) ?? name;
2234
+ }
2235
+ }
2236
+ await emitAcpxLog(ctx, {
2237
+ type: "acpx.tool_call",
2238
+ name,
2239
+ toolCallId: event.toolCallId,
2240
+ status: event.status,
2241
+ text: event.text,
2242
+ tag: event.tag,
2243
+ ...(toolInput !== undefined ? { input: toolInput } : {}),
2244
+ });
2245
+ return;
2246
+ }
2247
+ if (event.type === "status") {
2248
+ await emitAcpxLog(ctx, {
2249
+ type: "acpx.status",
2250
+ text: event.text,
2251
+ tag: event.tag,
2252
+ used: event.used,
2253
+ size: event.size,
2254
+ ...(event.cost ? { cost: event.cost } : {}),
2255
+ ...(event.breakdown ? { breakdown: event.breakdown } : {}),
2256
+ });
2257
+ return;
2258
+ }
2259
+ if (event.type === "done") {
2260
+ await emitAcpxLog(ctx, {
2261
+ type: "acpx.result",
2262
+ summary: event.stopReason ?? "completed",
2263
+ stopReason: event.stopReason,
2264
+ });
2265
+ return;
2266
+ }
2267
+ if (event.type === "error") {
2268
+ await emitAcpxLog(ctx, {
2269
+ type: "acpx.error",
2270
+ message: event.message,
2271
+ code: event.code,
2272
+ retryable: event.retryable,
2273
+ });
2274
+ }
2275
+ }
2276
+ function resultErrorMessage(result) {
2277
+ if (result.status !== "failed")
2278
+ return null;
2279
+ return result.error.message;
2280
+ }
2281
+ function usageBreakdownsEqual(left, right) {
2282
+ return (asNumber(left.inputTokens, 0) === asNumber(right.inputTokens, 0) &&
2283
+ asNumber(left.outputTokens, 0) === asNumber(right.outputTokens, 0) &&
2284
+ asNumber(left.cachedReadTokens, 0) === asNumber(right.cachedReadTokens, 0) &&
2285
+ asNumber(left.cachedWriteTokens, 0) === asNumber(right.cachedWriteTokens, 0) &&
2286
+ asNumber(left.thoughtTokens, 0) === asNumber(right.thoughtTokens, 0) &&
2287
+ asNumber(left.totalTokens, 0) === asNumber(right.totalTokens, 0));
2288
+ }
2289
+ function usdCostAmount(cost) {
2290
+ if (!cost || typeof cost.amount !== "number" || !Number.isFinite(cost.amount))
2291
+ return null;
2292
+ if (cost.currency && cost.currency.trim().toUpperCase() !== "USD")
2293
+ return null;
2294
+ return cost.amount;
2295
+ }
2296
+ async function readRuntimeStatus(runtime, handle) {
2297
+ if (!runtime.getStatus)
2298
+ return null;
2299
+ try {
2300
+ return (await runtime.getStatus({ handle })) ?? null;
2301
+ }
2302
+ catch {
2303
+ return null;
2304
+ }
2305
+ }
2306
+ /**
2307
+ * Fold the ACP runtime's post-turn usage into the adapter execution result
2308
+ * shape. The runtime persists the latest turn's token breakdown (adapters like
2309
+ * claude-agent-acp report per-turn accumulated usage in the prompt response),
2310
+ * so tokens are per-run. Cost is reported by agents as a cumulative session
2311
+ * amount, so the per-run cost is the delta against the pre-turn snapshot; a
2312
+ * decrease means the agent process restarted and its counter reset, in which
2313
+ * case the post-turn amount alone covers this run.
2314
+ */
2315
+ export function summarizeAcpxTurnUsage(input) {
2316
+ // The persisted breakdown is overwritten per turn, so an unchanged value
2317
+ // is stale for this turn. Prefer an in-turn event breakdown when available;
2318
+ // otherwise suppress the stale value so it cannot be double-counted.
2319
+ const preBreakdown = input.preStatus?.usage?.cumulative ?? null;
2320
+ const postBreakdown = input.postStatus?.usage?.cumulative ?? null;
2321
+ const postBreakdownIsStale = preBreakdown != null &&
2322
+ postBreakdown != null &&
2323
+ usageBreakdownsEqual(preBreakdown, postBreakdown);
2324
+ const breakdown = postBreakdownIsStale
2325
+ ? input.eventBreakdown
2326
+ : postBreakdown ?? input.eventBreakdown ?? null;
2327
+ const inputTokens = Math.max(0, Math.floor(asNumber(breakdown?.inputTokens, 0)));
2328
+ const outputTokens = Math.max(0, Math.floor(asNumber(breakdown?.outputTokens, 0)));
2329
+ const cachedReadTokens = Math.max(0, Math.floor(asNumber(breakdown?.cachedReadTokens, 0)));
2330
+ const cachedWriteTokens = Math.max(0, Math.floor(asNumber(breakdown?.cachedWriteTokens, 0)));
2331
+ const hasTokens = inputTokens > 0 || outputTokens > 0 || cachedReadTokens > 0 || cachedWriteTokens > 0;
2332
+ // Cache-write tokens are prompt tokens the provider billed to create cache
2333
+ // entries; UsageSummary has no dedicated field, so count them as input.
2334
+ const usage = hasTokens
2335
+ ? {
2336
+ inputTokens: inputTokens + cachedWriteTokens,
2337
+ outputTokens,
2338
+ cachedInputTokens: cachedReadTokens,
2339
+ }
2340
+ : null;
2341
+ const usageDetail = breakdown
2342
+ ? Object.fromEntries(Object.entries({
2343
+ inputTokens: breakdown.inputTokens,
2344
+ outputTokens: breakdown.outputTokens,
2345
+ cachedReadTokens: breakdown.cachedReadTokens,
2346
+ cachedWriteTokens: breakdown.cachedWriteTokens,
2347
+ thoughtTokens: breakdown.thoughtTokens,
2348
+ totalTokens: breakdown.totalTokens,
2349
+ }).filter((entry) => typeof entry[1] === "number"))
2350
+ : null;
2351
+ const previousCostUsd = usdCostAmount(input.preStatus?.usage?.cost);
2352
+ const postCostUsd = usdCostAmount(input.postStatus?.usage?.cost);
2353
+ const postCostIsStale = input.eventCostUsd != null &&
2354
+ previousCostUsd != null &&
2355
+ postCostUsd != null &&
2356
+ postCostUsd === previousCostUsd;
2357
+ const cumulativeCostUsd = postCostIsStale ? input.eventCostUsd : postCostUsd ?? input.eventCostUsd;
2358
+ let costUsd = null;
2359
+ if (cumulativeCostUsd != null) {
2360
+ costUsd =
2361
+ previousCostUsd != null && cumulativeCostUsd >= previousCostUsd
2362
+ ? cumulativeCostUsd - previousCostUsd
2363
+ : cumulativeCostUsd;
2364
+ }
2365
+ return { usage, usageDetail, costUsd, cumulativeCostUsd };
2366
+ }
2367
+ function describeErrorDiagnostics(err) {
2368
+ const errorName = err instanceof Error ? err.name || err.constructor.name : typeof err;
2369
+ const maybeCode = err && typeof err === "object" && typeof err.code === "string"
2370
+ ? err.code
2371
+ : null;
2372
+ const acpCode = isAcpRuntimeError(err) || (maybeCode?.startsWith("ACP_") ?? false) ? maybeCode : null;
2373
+ const cause = err && typeof err === "object" && err.cause !== undefined
2374
+ ? err.cause
2375
+ : undefined;
2376
+ const causeMessage = cause instanceof Error
2377
+ ? cause.message
2378
+ : typeof cause === "string"
2379
+ ? cause
2380
+ : null;
2381
+ const retryable = err && typeof err === "object" && typeof err.retryable === "boolean"
2382
+ ? err.retryable
2383
+ : null;
2384
+ const stack = err instanceof Error && typeof err.stack === "string" ? err.stack : "";
2385
+ const stackPreview = stack ? stack.split("\n").slice(0, 6).join("\n") : null;
2386
+ return { errorName, acpCode, causeMessage, retryable, stackPreview };
2387
+ }
2388
+ function classifyError(err, phase) {
2389
+ const message = err instanceof Error ? err.message : String(err);
2390
+ const diagnostics = describeErrorDiagnostics(err);
2391
+ const { acpCode, errorName, causeMessage, retryable, stackPreview } = diagnostics;
2392
+ const baseMeta = {
2393
+ errorName,
2394
+ ...(acpCode ? { acpCode } : {}),
2395
+ ...(causeMessage ? { causeMessage } : {}),
2396
+ ...(retryable !== null ? { retryable } : {}),
2397
+ ...(stackPreview ? { stackPreview } : {}),
2398
+ ...(phase ? { phase } : {}),
2399
+ };
2400
+ // A host-generated handshake-guard error is classified by its type, never
2401
+ // by message text, and runs before the message-driven heuristics below. A
2402
+ // startup timeout or a duplex control-channel loss must report its own
2403
+ // closed code, not the generic session-identity failure `phase ===
2404
+ // "ensure_session"` would otherwise produce a few lines down.
2405
+ if (isAcpxHandshakeTimeoutError(err)) {
2406
+ return {
2407
+ errorCode: "acpx_handshake_timeout",
2408
+ errorMeta: { category: "runtime", ...baseMeta },
2409
+ };
2410
+ }
2411
+ if (isAcpxHandshakeTransportLostError(err)) {
2412
+ return {
2413
+ errorCode: "acpx_handshake_transport_lost",
2414
+ errorMeta: { category: "runtime", ...baseMeta },
2415
+ };
2416
+ }
2417
+ const lower = message.toLowerCase();
2418
+ const authLike = lower.includes("auth") || lower.includes("login") || lower.includes("credential");
2419
+ if (authLike) {
2420
+ return {
2421
+ errorCode: "acpx_auth_required",
2422
+ errorMeta: { category: "auth", ...baseMeta },
2423
+ };
2424
+ }
2425
+ const phaseCode = (() => {
2426
+ if (acpCode === "ACP_SESSION_INIT_FAILED")
2427
+ return "acpx_session_init_failed";
2428
+ if (acpCode === "ACP_TURN_FAILED")
2429
+ return "acpx_turn_failed";
2430
+ if (acpCode === "ACP_BACKEND_MISSING")
2431
+ return "acpx_backend_missing";
2432
+ if (acpCode === "ACP_BACKEND_UNAVAILABLE")
2433
+ return "acpx_backend_unavailable";
2434
+ if (phase === "ensure_session")
2435
+ return "acpx_session_init_failed";
2436
+ if (phase === "configure_session")
2437
+ return "acpx_session_config_failed";
2438
+ if (phase === "turn")
2439
+ return "acpx_turn_failed";
2440
+ return null;
2441
+ })();
2442
+ if (phaseCode) {
2443
+ return {
2444
+ errorCode: phaseCode,
2445
+ errorMeta: { category: acpCode ? "protocol" : "runtime", ...baseMeta },
2446
+ };
2447
+ }
2448
+ if (acpCode) {
2449
+ return {
2450
+ errorCode: "acpx_protocol_error",
2451
+ errorMeta: { category: "protocol", ...baseMeta },
2452
+ };
2453
+ }
2454
+ return {
2455
+ errorCode: "acpx_runtime_error",
2456
+ errorMeta: { category: "runtime", ...baseMeta },
2457
+ };
2458
+ }
2459
+ async function readChildStderrTail(input) {
2460
+ if (!input.logPath)
2461
+ return null;
2462
+ const maxBytes = input.maxBytes ?? 4096;
2463
+ let handle = null;
2464
+ try {
2465
+ const stat = await fs.stat(input.logPath);
2466
+ if (stat.size === 0)
2467
+ return null;
2468
+ handle = await fs.open(input.logPath, "r");
2469
+ const readBytes = Math.min(stat.size, maxBytes);
2470
+ const buffer = Buffer.alloc(readBytes);
2471
+ await handle.read(buffer, 0, readBytes, Math.max(0, stat.size - readBytes));
2472
+ const tail = buffer.toString("utf8").trim();
2473
+ return tail.length > 0 ? tail : null;
2474
+ }
2475
+ catch {
2476
+ return null;
2477
+ }
2478
+ finally {
2479
+ if (handle)
2480
+ await handle.close().catch(() => { });
2481
+ }
2482
+ }
2483
+ async function emitAcpxFailure(input) {
2484
+ const { ctx, prepared, err, phase, messageOverride, suppressChildStderrTail } = input;
2485
+ const rawMessage = err instanceof Error ? err.message : String(err);
2486
+ const message = messageOverride ?? rawMessage;
2487
+ const classified = classifyError(err, phase);
2488
+ const childStderrTail = suppressChildStderrTail
2489
+ ? null
2490
+ : await readChildStderrTail({ logPath: prepared.childStderrLogPath });
2491
+ if (childStderrTail) {
2492
+ await ctx.onLog("stderr", `[paperclip] ACPX child stderr tail (${phase}):\n${childStderrTail}\n`);
2493
+ }
2494
+ await emitAcpxLog(ctx, {
2495
+ type: "acpx.error",
2496
+ message,
2497
+ phase,
2498
+ ...classified.errorMeta,
2499
+ ...(childStderrTail ? { childStderrTail } : {}),
2500
+ });
2501
+ return { classified, message, childStderrTail };
2502
+ }
2503
+ function isResumeFailure(err) {
2504
+ const message = err instanceof Error ? err.message : String(err);
2505
+ return /resume|load|not found|no session|unknown session|conversation/i.test(message);
2506
+ }
2507
+ // Drop staged-runtime entries the session has not touched within the warm-idle
2508
+ // window, so the cache does not accumulate abandoned sessions (e.g. every time
2509
+ // a config change shifts the fingerprint to a new key). The per-run copy-back
2510
+ // already ran on the entry's last run's settlement `syncBack`; eviction fires
2511
+ // the entry's one-time `dispose` (host staged-temp cleanup) — the only place
2512
+ // the staged temp is removed now that it no longer rides the per-run teardown.
2513
+ // A later run of the same session simply re-stages fresh (re-shipping into the
2514
+ // still-persistent sandbox, which the inbound monotonic auth-merge keeps safe).
2515
+ async function cleanupIdleStagedRuntimes(input) {
2516
+ if (input.idleMs <= 0)
2517
+ return;
2518
+ const stale = [];
2519
+ for (const entry of input.handles.entries()) {
2520
+ if (input.now() - entry[1].lastUsedAt >= input.idleMs)
2521
+ stale.push(entry);
2522
+ }
2523
+ for (const [key, entry] of stale) {
2524
+ const lease = await withSessionStagingLease(input.locks, key, async () => {
2525
+ const current = input.handles.get(key);
2526
+ if (current !== entry)
2527
+ return;
2528
+ if (input.now() - current.lastUsedAt < input.idleMs)
2529
+ return;
2530
+ input.handles.delete(key);
2531
+ if (entry.dispose)
2532
+ await entry.dispose().catch(() => { });
2533
+ });
2534
+ lease.release();
2535
+ }
2536
+ }
2537
+ // Persist a remote runner-backed session's staged runtime for reuse on the next
2538
+ // compatible resume. Called ONLY after a clean turn, so the cache never offers a
2539
+ // half-staged or failed session for reuse. Non-remote lanes carry a null
2540
+ // stagedRuntime / null envDelta and are skipped.
2541
+ function saveStagedRuntimeAfterCleanTurn(input) {
2542
+ const { prepared } = input;
2543
+ if (!prepared.stagedRuntime || prepared.remoteStagingEnvDelta === null)
2544
+ return;
2545
+ input.handles.set(prepared.sessionKey, {
2546
+ stagedRuntime: prepared.stagedRuntime,
2547
+ envDelta: prepared.remoteStagingEnvDelta,
2548
+ teardown: prepared.remoteManagedHomeTeardown,
2549
+ dispose: prepared.remoteStagingDispose,
2550
+ lastUsedAt: input.now,
2551
+ });
2552
+ }
2553
+ // Drop the staged-runtime entry a finished run owns and release its host-side
2554
+ // staged resources. Two guards make this safe under overlapping runs of the same
2555
+ // session key (PR 3 fix — "Concurrent Runs Corrupt Cache Ownership"):
2556
+ // 1. Ownership guard: only delete the map entry when it is still the exact
2557
+ // staged runtime THIS run installed/reused (object identity). A concurrent
2558
+ // run that installed a different clean entry keeps it — a failed run can no
2559
+ // longer evict another run's good cache entry.
2560
+ // 2. `dispose` is fired for THIS run's own staged resources regardless, so a
2561
+ // failed/cancelled run always frees its own staged temp. `dispose` is
2562
+ // idempotent, so a shared closure re-fired across a reuse chain is safe.
2563
+ async function discardStagedRuntime(input) {
2564
+ const { handles, prepared } = input;
2565
+ await releaseStagedRuntimeEntry({
2566
+ handles,
2567
+ sessionKey: prepared.sessionKey,
2568
+ stagedRuntime: prepared.stagedRuntime,
2569
+ dispose: prepared.remoteStagingDispose,
2570
+ });
2571
+ }
2572
+ // Ownership-guarded removal + dispose for a staged-runtime cache entry a run owns.
2573
+ // Shared by `discardStagedRuntime` (clean-run drop) and the partial-bring-up
2574
+ // rollback so both release a borrowed entry the same way:
2575
+ // 1. Delete the map entry ONLY when it still holds the exact staged runtime this
2576
+ // run installed or reused (object identity). A concurrent run that replaced
2577
+ // the entry with its own keeps it.
2578
+ // 2. Fire this run's own one-time host staged-temp dispose regardless. The
2579
+ // dispose closure is idempotent, so a shared closure re-fired across a reuse
2580
+ // chain is safe.
2581
+ async function releaseStagedRuntimeEntry(input) {
2582
+ const { handles, sessionKey, stagedRuntime, dispose } = input;
2583
+ const existing = handles.get(sessionKey);
2584
+ if (existing && stagedRuntime && existing.stagedRuntime === stagedRuntime) {
2585
+ handles.delete(sessionKey);
2586
+ }
2587
+ if (dispose)
2588
+ await dispose().catch(() => { });
2589
+ }
2590
+ // Per-`sessionKey` async lease: chains each caller after the previous one so
2591
+ // the stage-or-reuse decision for a session runs serially, then keeps the
2592
+ // lease held until the active turn finishes and bridge cleanup runs. That means
2593
+ // overlapping runs of the same session can never stage fresh into the same
2594
+ // remote workspace while a prior turn is still using it: the loser waits, then
2595
+ // re-checks the cache before deciding to reuse or re-stage.
2596
+ async function withSessionStagingLease(locks, key, fn) {
2597
+ const prev = locks.get(key) ?? Promise.resolve();
2598
+ let releaseGate;
2599
+ const gate = new Promise((resolve) => {
2600
+ releaseGate = resolve;
2601
+ });
2602
+ // The next waiter's `prev` is this promise; it settles only once we release
2603
+ // the gate below, so callers run one at a time.
2604
+ const mine = prev.then(() => gate);
2605
+ locks.set(key, mine);
2606
+ await prev.catch(() => { });
2607
+ let released = false;
2608
+ const release = () => {
2609
+ if (released)
2610
+ return;
2611
+ released = true;
2612
+ releaseGate();
2613
+ // GC the lock if no later caller has chained after us.
2614
+ if (locks.get(key) === mine)
2615
+ locks.delete(key);
2616
+ };
2617
+ try {
2618
+ return { value: await fn(), release };
2619
+ }
2620
+ catch (error) {
2621
+ if (!released)
2622
+ release();
2623
+ throw error;
2624
+ }
2625
+ }
2626
+ function clearWarmHandleTimer(entry) {
2627
+ if (!entry.cleanupTimer)
2628
+ return;
2629
+ clearTimeout(entry.cleanupTimer);
2630
+ entry.cleanupTimer = undefined;
2631
+ }
2632
+ async function closeWarmHandle(input) {
2633
+ if (input.handles.get(input.key) === input.entry) {
2634
+ input.handles.delete(input.key);
2635
+ }
2636
+ clearWarmHandleTimer(input.entry);
2637
+ await input.entry.runtime.close({
2638
+ handle: input.entry.handle,
2639
+ reason: input.reason,
2640
+ discardPersistentState: input.discardPersistentState ?? false,
2641
+ }).catch(() => { });
2642
+ flushChildStderr(input.entry.childStderrState);
2643
+ }
2644
+ function warmHandleMatches(entry, runtime, handle) {
2645
+ return entry !== undefined && entry.runtime === runtime && entry.handle === handle;
2646
+ }
2647
+ /** The stable name of the one root span for a sandbox bring-up. It is a fixed
2648
+ * low-cardinality constant, never derived from run/user data. */
2649
+ const STARTUP_ROOT_SPAN_NAME = "sandbox.startup";
2650
+ /** The shared batch tag for the two parallel bridge steps. It is a fixed
2651
+ * low-cardinality literal, so it marks the two spans as one batch without
2652
+ * carrying run or user data. */
2653
+ const STARTUP_BRIDGE_BATCH = "bridge";
2654
+ /**
2655
+ * Open the one root span for a sandbox bring-up and return its parent-context
2656
+ * token plus a guarded `end`. The span parents to the run root span through
2657
+ * `runParentContext`, so `sandbox.startup` becomes a child of `task.run`. The
2658
+ * span then parents every startup boundary span in turn: the engine forwards
2659
+ * `parentContext` to each `measureStartupStep` call. The `end` closure runs at
2660
+ * most once (bring-up complete OR a bring-up failure) and swallows every tracer
2661
+ * error, so observability never changes startup control flow. With no injected
2662
+ * trace context, the tracer is a no-op and the span is a no-op.
2663
+ */
2664
+ function openStartupRootSpan(tracing, nowMs,
2665
+ // The run root span parent context. `sandbox.startup` opens as a child of it,
2666
+ // so the whole bring-up parents to `task.run`. It is an opaque token here.
2667
+ runParentContext,
2668
+ // Return the final root-span numbers and context at end time. The work sum
2669
+ // and the cold-start flag are known only after the bring-up runs, so the
2670
+ // caller reads them lazily here.
2671
+ finalize) {
2672
+ let span;
2673
+ try {
2674
+ span = tracing.tracer.startSpan(STARTUP_ROOT_SPAN_NAME, undefined, runParentContext);
2675
+ }
2676
+ catch {
2677
+ span = NOOP_STARTUP_SPAN;
2678
+ }
2679
+ let parentContext;
2680
+ try {
2681
+ parentContext = tracing.contextWithSpan(span);
2682
+ }
2683
+ catch {
2684
+ parentContext = undefined;
2685
+ }
2686
+ const startedAtMs = nowMs();
2687
+ let ended = false;
2688
+ return {
2689
+ parentContext,
2690
+ end: (failed) => {
2691
+ if (ended)
2692
+ return;
2693
+ ended = true;
2694
+ try {
2695
+ // The root span records its own wall time, the step-work sum, and the
2696
+ // bounded context. `setSandboxRootSpanAttributes` sets only the closed
2697
+ // allowlist, so no raw id or image reference rides the span.
2698
+ const { workMs, context } = finalize();
2699
+ setSandboxRootSpanAttributes(span, { wallMs: nowMs() - startedAtMs, workMs }, context);
2700
+ // `2` is `SpanStatusCode.ERROR`. `adapter-utils` stays OTel-free, so it
2701
+ // uses the numeric value that a real injected span reads as the error
2702
+ // status.
2703
+ if (failed)
2704
+ span.setStatus({ code: 2 });
2705
+ span.end();
2706
+ }
2707
+ catch {
2708
+ // Observability must not change startup control flow.
2709
+ }
2710
+ },
2711
+ };
2712
+ }
2713
+ /** The stable name of the one root span for a whole run. It is a fixed
2714
+ * low-cardinality constant, never derived from run or user data. */
2715
+ const RUN_ROOT_SPAN_NAME = "task.run";
2716
+ /** The stable name of the one span for the agent turn. It is a fixed
2717
+ * low-cardinality constant, never derived from run or user data. The turn span
2718
+ * is a child of the run root span. */
2719
+ const TURN_SPAN_NAME = "agent.turn";
2720
+ /** The attribute prefix for the run root span. It groups the run-level span
2721
+ * attributes under one namespace, the same shape as the sandbox startup
2722
+ * prefix. */
2723
+ const RUN_ROOT_SPAN_ATTR_PREFIX = "paperclip.task.run.";
2724
+ /** The attribute prefix for the agent turn span. It groups the turn-level span
2725
+ * attributes under one namespace, the same shape as the run root prefix. */
2726
+ const TURN_SPAN_ATTR_PREFIX = "paperclip.agent.turn.";
2727
+ /** Map a run id to a non-reversible 12-hex hash for a span attribute. The raw
2728
+ * run id never rides a span; only this hash does. This mirrors the id-hash rule
2729
+ * that `clampSpanLabel` uses for the startup ids. */
2730
+ function hashRunId(runId) {
2731
+ return createHash("sha256").update(runId).digest("hex").slice(0, 12);
2732
+ }
2733
+ /**
2734
+ * Open the one root span for a whole run and return its parent-context token
2735
+ * plus a guarded `end`. The run root span is the trace root: the sandbox
2736
+ * bring-up span (`sandbox.startup`) parents to it, so the engine forwards
2737
+ * `parentContext` into `openStartupRootSpan`. The `end` closure runs at most
2738
+ * once and swallows every tracer error, so observability never changes run
2739
+ * control flow. With no injected trace context the tracer is a no-op and the
2740
+ * span is a no-op.
2741
+ *
2742
+ * The span carries only a bounded, non-reversible run-id hash and its own wall
2743
+ * time. It never carries the prompt, the command, or any user text, so no raw
2744
+ * run text rides the span. This follows the same allowlist rule as
2745
+ * `openStartupRootSpan`.
2746
+ */
2747
+ function openRunRootSpan(tracing, nowMs, runId) {
2748
+ let span;
2749
+ try {
2750
+ span = tracing.tracer.startSpan(RUN_ROOT_SPAN_NAME);
2751
+ }
2752
+ catch {
2753
+ span = NOOP_STARTUP_SPAN;
2754
+ }
2755
+ let parentContext;
2756
+ try {
2757
+ parentContext = tracing.contextWithSpan(span);
2758
+ }
2759
+ catch {
2760
+ parentContext = undefined;
2761
+ }
2762
+ const startedAtMs = nowMs();
2763
+ let ended = false;
2764
+ return {
2765
+ parentContext,
2766
+ end: (failed) => {
2767
+ if (ended)
2768
+ return;
2769
+ ended = true;
2770
+ try {
2771
+ // The run id rides only as a non-reversible short hash, never as the raw
2772
+ // id. The wall time is a plain duration. No raw run text rides the span.
2773
+ span.setAttribute(`${RUN_ROOT_SPAN_ATTR_PREFIX}run_id`, hashRunId(runId));
2774
+ span.setAttribute(`${RUN_ROOT_SPAN_ATTR_PREFIX}wall_ms`, nowMs() - startedAtMs);
2775
+ // `2` is `SpanStatusCode.ERROR`. `adapter-utils` stays OTel-free, so it
2776
+ // uses the numeric value that a real injected span reads as the error
2777
+ // status.
2778
+ if (failed)
2779
+ span.setStatus({ code: 2 });
2780
+ span.end();
2781
+ }
2782
+ catch {
2783
+ // Observability must not change run control flow.
2784
+ }
2785
+ },
2786
+ };
2787
+ }
2788
+ /**
2789
+ * Open the one span for the agent turn and return its parent-context token plus
2790
+ * a guarded `end`. The span parents to the run root span through
2791
+ * `runParentContext`, so `agent.turn` becomes a child of `task.run`. The
2792
+ * executor holds the returned `parentContext` for later exec parenting. The
2793
+ * `end` closure runs at most once and swallows every tracer error, so
2794
+ * observability never changes turn control flow. With no injected trace context
2795
+ * the tracer is a no-op and the span is a no-op.
2796
+ *
2797
+ * The span carries only its own wall time. It never carries the prompt, the
2798
+ * command, or any user text, so no raw run text rides the span. This follows the
2799
+ * same allowlist rule as `openStartupRootSpan`.
2800
+ */
2801
+ function openTurnSpan(tracing, nowMs,
2802
+ // The run root span parent context. `agent.turn` opens as a child of it, so
2803
+ // the turn parents to `task.run`. It is an opaque token here.
2804
+ runParentContext) {
2805
+ let span;
2806
+ try {
2807
+ span = tracing.tracer.startSpan(TURN_SPAN_NAME, undefined, runParentContext);
2808
+ }
2809
+ catch {
2810
+ span = NOOP_STARTUP_SPAN;
2811
+ }
2812
+ let parentContext;
2813
+ try {
2814
+ parentContext = tracing.contextWithSpan(span);
2815
+ }
2816
+ catch {
2817
+ parentContext = undefined;
2818
+ }
2819
+ const startedAtMs = nowMs();
2820
+ let ended = false;
2821
+ return {
2822
+ parentContext,
2823
+ end: (failed) => {
2824
+ if (ended)
2825
+ return;
2826
+ ended = true;
2827
+ try {
2828
+ // The wall time is a plain duration. No raw run text rides the span.
2829
+ span.setAttribute(`${TURN_SPAN_ATTR_PREFIX}wall_ms`, nowMs() - startedAtMs);
2830
+ // `2` is `SpanStatusCode.ERROR`. `adapter-utils` stays OTel-free, so it
2831
+ // uses the numeric value that a real injected span reads as the error
2832
+ // status.
2833
+ if (failed)
2834
+ span.setStatus({ code: 2 });
2835
+ span.end();
2836
+ }
2837
+ catch {
2838
+ // Observability must not change turn control flow.
2839
+ }
2840
+ },
2841
+ };
2842
+ }
2843
+ export function createAcpxEngineExecutor(deps = {}) {
2844
+ const createRuntime = deps.createRuntime ?? createAcpRuntime;
2845
+ const now = deps.now ?? (() => Date.now());
2846
+ const duplexLossCancelDeadlineMs = deps.duplexLossCancelDeadlineMs ?? ACPX_DUPLEX_LOSS_CANCEL_DEADLINE_MS;
2847
+ const warmHandles = deps.warmHandles ?? defaultWarmHandles;
2848
+ const stagedRuntimes = deps.stagedRuntimes ?? defaultStagedRuntimes;
2849
+ const stagingLocks = deps.stagingLocks ?? defaultStagingLocks;
2850
+ const engine = resolveEngineSettings(deps);
2851
+ return async function executeAcpxEngine(ctx) {
2852
+ let billingIdentity = null;
2853
+ try {
2854
+ billingIdentity = (await deps.resolveBillingIdentity?.(ctx)) ?? null;
2855
+ }
2856
+ catch {
2857
+ billingIdentity = null;
2858
+ }
2859
+ const billingFields = {
2860
+ provider: billingIdentity?.provider ?? "acpx",
2861
+ ...(billingIdentity?.biller ? { biller: billingIdentity.biller } : {}),
2862
+ billingType: billingIdentity?.billingType ?? "unknown",
2863
+ };
2864
+ const warmIdleMs = asNumber(ctx.config.warmHandleIdleMs, DEFAULT_ACP_ENGINE_WARM_HANDLE_IDLE_MS);
2865
+ // The host run site owns the warm-handle store on this run. It operates over
2866
+ // the engine's persistent `warmHandles` map, so a warm runtime stays live for
2867
+ // the next compatible resume. The store arms the per-entry idle timer on a
2868
+ // warm save, sweeps idle entries at run start, and closes a released entry
2869
+ // through `closeWarmEntry`. The run reads `warmHandleIdleMs` per run, so the
2870
+ // site takes this run's bound.
2871
+ const hostSite = createHostRunSite({
2872
+ warmHandles,
2873
+ now,
2874
+ idleMs: warmIdleMs,
2875
+ closeWarmEntry: async (entry) => {
2876
+ await entry.runtime
2877
+ .close({ handle: entry.handle, reason: "paperclip idle cleanup", discardPersistentState: false })
2878
+ .catch(() => { });
2879
+ flushChildStderr(entry.childStderrState);
2880
+ },
2881
+ });
2882
+ const hostStore = hostSite.reuse();
2883
+ // The `task.run` and `sandbox.startup` spans must not cover a local or SSH
2884
+ // run: those runs have no sandbox, so they stay out of sandbox telemetry.
2885
+ // Open a real root span only when the target is a remote sandbox and the
2886
+ // server injected a trace context; every other target forces the no-op
2887
+ // trace context, so the whole span path stays inert.
2888
+ const startupExecutionTarget = readAdapterExecutionTarget({
2889
+ executionTarget: ctx.executionTarget,
2890
+ legacyRemoteExecution: ctx.executionTransport?.remoteExecution,
2891
+ });
2892
+ const sandboxTarget = startupExecutionTarget?.kind === "remote" && startupExecutionTarget.transport === "sandbox"
2893
+ ? startupExecutionTarget
2894
+ : null;
2895
+ const targetsRemoteSandbox = sandboxTarget !== null;
2896
+ const tracing = targetsRemoteSandbox && ctx.startupTraceContext
2897
+ ? ctx.startupTraceContext
2898
+ : NOOP_STARTUP_TRACE_CONTEXT;
2899
+ // Open the one run root span at the engine first line, before any bring-up
2900
+ // work. It is the trace root for the whole run: the sandbox bring-up and the
2901
+ // agent turn parent to it. The engine forwards its parent context into the
2902
+ // startup span. `runRootSpan.end` runs exactly once, in the `finally` below,
2903
+ // on every return and on a throw.
2904
+ const runRootSpan = openRunRootSpan(tracing, now, ctx.runId);
2905
+ // Hold the current-run parent-context token for the whole run. It starts as
2906
+ // the `task.run` token, switches to the `agent.turn` token during the turn,
2907
+ // and switches back to the `task.run` token after the turn. It is never
2908
+ // `undefined` while the run is live. The holder is a run-scoped local, so two
2909
+ // concurrent runs in one host process keep separate tokens. A detached exec
2910
+ // reads it through `getRuntimeParentContext` to parent to the live span.
2911
+ let currentRunParentContext = runRootSpan.parentContext;
2912
+ const getRuntimeParentContext = () => currentRunParentContext;
2913
+ // Wrap each unit of bridge run-time work (one outbound ACP message, one poll
2914
+ // tick, one callback request) in its own named span, parented to the live run
2915
+ // span. The runner reads the run parent per call through
2916
+ // `getRuntimeParentContext`, so a wrapper span always parents to the current
2917
+ // run span. On a no-op trace context the runner opens no real span.
2918
+ const runRuntimeSpan = createRuntimeSpanRunner(tracing, getRuntimeParentContext);
2919
+ // Wrap the host workspace tarball build in one `pack` span. This runner
2920
+ // parents each span to the ACTIVE startup step (not the run span), so the
2921
+ // `pack` span nests under the `stage.sync` step that runs the staging seam.
2922
+ // The staging seam runs inside `stage.sync`'s measured step, so
2923
+ // `getActiveStepContext()` returns that step's child context at pack time.
2924
+ // On a no-op trace context the runner opens no real span.
2925
+ const runStageSpan = createRuntimeSpanRunner(tracing, () => getActiveStepContext()?.parentContext);
2926
+ // `runFailed` marks the run root span status at end time. It stays `true`
2927
+ // until the run reaches a clean completed turn, so every failure and every
2928
+ // early exit closes the span with error status.
2929
+ let runFailed = true;
2930
+ // The run's per-session staging lease release. `buildRuntime` returns it on
2931
+ // `prepared` after it acquires the lease; the startup step captures it here so
2932
+ // the run root `finally` can release it as the final settlement act. It stays
2933
+ // null on the host lane (no staging) and on a build failure (where
2934
+ // `buildRuntime` already released its own partial lease).
2935
+ let releaseStagingLease = null;
2936
+ let stopTimer;
2937
+ let removeStopListener;
2938
+ // Unregisters the sandbox duplex bridge's loss listener (below, in
2939
+ // `stepTurnStart`). Set only on a sandbox target whose bridge exposes
2940
+ // `onLoss`; stays undefined everywhere else, so the cleanup call is a
2941
+ // no-op there.
2942
+ let removeLossListener;
2943
+ // Bounds the wait after a latched terminal duplex loss so a silent agent
2944
+ // cannot hold the run open on the cooperative `turn.cancel()` request
2945
+ // alone. `stepTurnStart` arms `lossDeadlineTimer` the moment a loss
2946
+ // latches; it stays undefined everywhere else, so the cleanup call below
2947
+ // is a no-op there. `stepEventRelay` races the turn against
2948
+ // `lossDeadline` and, once it fires, ends the event drain and hands
2949
+ // `turnFinalize` a host-built terminal instead of the agent's.
2950
+ let lossDeadlineTimer;
2951
+ let lossDeadlineTripped = false;
2952
+ let resolveLossDeadline;
2953
+ const lossDeadline = new Promise((resolve) => {
2954
+ resolveLossDeadline = resolve;
2955
+ });
2956
+ let forcedStop = false;
2957
+ let runtimeStopConfirmed = false;
2958
+ let safeInterruptedSession = false;
2959
+ let preserveInterruptedSession = false;
2960
+ const interruptionTools = new Map();
2961
+ let incompleteToolInventory = false;
2962
+ try {
2963
+ await ctx.onCancellationReady?.();
2964
+ if (ctx.signal?.aborted)
2965
+ return {
2966
+ exitCode: null, signal: null, timedOut: false,
2967
+ errorCode: "cancelled", errorMessage: "Stopped before provider startup",
2968
+ executionRecovery: { kind: "bootstrap", providerWorkStarted: false },
2969
+ resultJson: { executionCancellation: { state: "acknowledged", acknowledgedAt: new Date().toISOString(), forced: false } },
2970
+ };
2971
+ // Evict idle staged runtimes BEFORE building the runtime, since buildRuntime
2972
+ // consults the staged cache to decide whether a compatible resume may reuse
2973
+ // an already-staged runtime — an expired entry must not be reused.
2974
+ await cleanupIdleStagedRuntimes({
2975
+ handles: stagedRuntimes,
2976
+ locks: stagingLocks,
2977
+ now,
2978
+ idleMs: warmIdleMs,
2979
+ });
2980
+ // The sum of the step wall times. The root span records it as `root.work_ms`
2981
+ // and the difference from its own wall time as `root.diff_ms` (the overlap
2982
+ // the parallel steps saved). Every step reports its wall time through
2983
+ // `onWallMs`; a skipped step adds zero.
2984
+ let stepWallSumMs = 0;
2985
+ // Whether this bring-up is a cold start (no warm handle). Set once the warm-
2986
+ // handle lookup runs below; it stays undefined on an early build failure, so
2987
+ // the root span omits the attribute (fail open).
2988
+ let coldStart;
2989
+ const rootSpan = openStartupRootSpan(tracing, now, runRootSpan.parentContext, () => ({
2990
+ workMs: stepWallSumMs,
2991
+ context: {
2992
+ coldStart,
2993
+ // The provider key and the lease id are the only low-cardinality
2994
+ // context values this provider-agnostic layer holds. The region, the
2995
+ // image id, and the sandbox id are not threaded here, so the root span
2996
+ // omits them (fail open). The lease id rides only as a hash.
2997
+ provider: sandboxTarget?.providerKey ?? undefined,
2998
+ leaseId: sandboxTarget?.leaseId ?? undefined,
2999
+ },
3000
+ }));
3001
+ const spanParent = {
3002
+ tracer: tracing.tracer,
3003
+ parentContext: rootSpan.parentContext,
3004
+ // Each step uses this to publish its own child context, so an inner exec
3005
+ // span parents to the step span, not to the root.
3006
+ contextWithSpan: (span) => tracing.contextWithSpan(span),
3007
+ // Accumulate each step wall time into the root work sum.
3008
+ onWallMs: (wallMs) => {
3009
+ stepWallSumMs += wallMs;
3010
+ },
3011
+ };
3012
+ // `startupFailed` drives the sandbox.startup span end status. It stays true
3013
+ // until the bring-up establishes a session handle. The one `finally` below
3014
+ // ends the span exactly once, on every return and on every throw.
3015
+ let startupFailed = true;
3016
+ // `buildRuntimeSettled` marks that buildRuntime returned. A failure before it
3017
+ // settled has nothing to settle here; a failure after it settled routes
3018
+ // through the create_runtime handler below.
3019
+ let buildRuntimeSettled = false;
3020
+ // The bring-up assigns these. The turn and teardown paths read them after
3021
+ // the sandbox.startup span closes, so they are declared here.
3022
+ let prepared;
3023
+ let runtime;
3024
+ let sessionHandle;
3025
+ let childStderrState;
3026
+ let processIdentitySink;
3027
+ let resumedSession = false;
3028
+ let clearSession = false;
3029
+ let referencedProjectStagingFailuresField = {};
3030
+ // Set only when the settlement sync-back reports a failed workspace
3031
+ // restore. `reproduceResult` merges this into the returned result's
3032
+ // `resultJson`, so a clean run adds no new key. The run's exit code and
3033
+ // status stay exactly what the turn produced — this is a signal, not an
3034
+ // outcome change.
3035
+ let workspaceRestoreFailureField = {};
3036
+ // The one settlement step name whose error can be the same workspace-
3037
+ // restore failure the adapter teardown closure already classifies (a
3038
+ // caught error from `syncBackManagedHome`). The closure already
3039
+ // sanitizes its own `onLog` line; this is a second, independent layer,
3040
+ // so a defect in that closure (or a future call site that forgets to
3041
+ // sanitize) still cannot put a raw `Error.message` — and the host path
3042
+ // or process id it can carry — on the run log.
3043
+ const SYNC_BACK_SETTLEMENT_STEP = "settlement-sync_back";
3044
+ const recordTeardownError = async (step, teardownErr) => {
3045
+ const reason = step === SYNC_BACK_SETTLEMENT_STEP
3046
+ ? describeWorkspaceRestoreFailure(classifyWorkspaceRestoreFailure(teardownErr))
3047
+ : teardownErr instanceof Error
3048
+ ? teardownErr.message
3049
+ : String(teardownErr);
3050
+ await ctx
3051
+ .onLog("stderr", `[paperclip] ACPX teardown step "${step}" failed: ${reason}\n`)
3052
+ .catch(() => { });
3053
+ };
3054
+ // Emit one per-phase timing run-log event. It is not an OpenTelemetry
3055
+ // export and it is not a Telemetry event: it carries the phase name
3056
+ // (from the closed allowlist), the wall time, and the outcome, and
3057
+ // never a command, a path, an environment value, or an identifier. A
3058
+ // failure to emit this event never fails the run.
3059
+ const emitPhase = (phase, startMs, outcome) => emitRunPhaseTiming(ctx, phase, now() - startMs, outcome);
3060
+ // Time a settlement step and emit its phase timing on every path. A step
3061
+ // error still emits `failed` before it re-throws to the Phase 3 error policy.
3062
+ const timedPhase = async (phase, run) => {
3063
+ const start = now();
3064
+ try {
3065
+ await run();
3066
+ }
3067
+ catch (error) {
3068
+ await emitPhase(phase, start, "failed");
3069
+ throw error;
3070
+ }
3071
+ await emitPhase(phase, start, "ok");
3072
+ };
3073
+ // The turn the run started. It is hoisted to the run scope so the settlement
3074
+ // `endSession` step can cancel a running turn before it closes the runtime
3075
+ // (the cancel-before-close order). The turn wrapper assigns it in `turnStart`.
3076
+ let activeTurn = null;
3077
+ let terminalFailureClassification = null;
3078
+ // How the settlement `endSession` step must release the runtime for the path
3079
+ // this run took. Each exit path that acquired the runtime records it before it
3080
+ // returns; a build or create-runtime failure never registers the runtime, so
3081
+ // the step no-ops on the empty slot and this stays null.
3082
+ let runtimeSettlement = null;
3083
+ // True only when a handshake-guard rejection (a startup timeout or a
3084
+ // duplex transport loss) abandoned an `ensureSession` call. It tells the
3085
+ // settlement `endSession` step to defer the close to `handshakeFence`
3086
+ // when no late handle has arrived yet, instead of closing the synthetic
3087
+ // placeholder itself — closing both would close the same session twice.
3088
+ let handshakeAbandoned = false;
3089
+ // The one owner of an abandoned `ensureSession` promise for this run. See
3090
+ // `HandshakeAbandonmentFence` above for the state machine.
3091
+ const handshakeFence = new HandshakeAbandonmentFence();
3092
+ // A synthetic close handle from the prepared identity, for a close that has no
3093
+ // established session handle (a cold `ensureSession` throw or a missing
3094
+ // handle). `runtime.close` reads the session key and the runtime session name.
3095
+ const syntheticCloseHandle = () => ({
3096
+ sessionKey: prepared.sessionKey,
3097
+ backend: prepared.acpxAgent,
3098
+ runtimeSessionName: prepared.sessionKey,
3099
+ });
3100
+ // The run coordinator owns the one ledger for the attempt. The engine
3101
+ // creates it here, passes it into `buildRuntime` (the sandbox site registers
3102
+ // what it acquires into it), and hands it to `runAttempt` as the plan
3103
+ // ledger. The turn and settlement code still runs inline in the step
3104
+ // wrappers below; Phases 21-22 move it into its own modules.
3105
+ const runResourceLedger = createRunResourceLedger();
3106
+ // The step wrappers build the external result inline (today's behavior) and
3107
+ // record it here; the coordinator reproduces it after settlement. An empty
3108
+ // consumed set satisfies the `settle` result shape without claiming the run
3109
+ // ledger, because the inline teardown reads `prepared.*`, not the ledger, in
3110
+ // this phase.
3111
+ let capturedResult = null;
3112
+ const emptyConsumed = createRunResourceLedger().takeForSettlement();
3113
+ // Build the `settle` startup result for a pre-turn failure. The step wrapper
3114
+ // already recorded the external result and ran the inline teardown; this
3115
+ // carries only the routing kind and the cause for the coordinator.
3116
+ const settleFor = (phase, error) => ({
3117
+ kind: "settle",
3118
+ cause: {
3119
+ kind: "pre_turn_failed",
3120
+ phase,
3121
+ error: error instanceof Error ? error : new Error(String(error)),
3122
+ },
3123
+ resources: emptyConsumed,
3124
+ });
3125
+ // The startup step: bring up the runtime, establish the session, and apply
3126
+ // the session config. It returns `ready` on success, `settle` on a pre-turn
3127
+ // failure after the ledger acquired resources, and throws on a build failure
3128
+ // or a partial-bridge failure (the coordinator owns that rollback).
3129
+ const startup = async () => {
3130
+ try {
3131
+ // Publish the `sandbox.startup` context to the runtime-parent store for
3132
+ // the whole bring-up. A startup-body exec that runs outside a measured
3133
+ // step reads this token and parents its span to `sandbox.startup`, not to
3134
+ // a detached root. A measured step nests its own `activeStepContextStorage`
3135
+ // run inside this wrap and overrides the store, so an in-step exec still
3136
+ // parents to its step span. On a local or SSH target
3137
+ // `spanParent.parentContext` is a no-op token, so the wrap is inert.
3138
+ const startupCancellation = cancellableSandboxStartup(ctx);
3139
+ try {
3140
+ prepared = await runWithRuntimeParent(spanParent.parentContext, () => buildRuntime({
3141
+ ctx: startupCancellation.context,
3142
+ engine,
3143
+ deps,
3144
+ ledger: runResourceLedger,
3145
+ stagedIdleMs: warmIdleMs,
3146
+ spanParent,
3147
+ getRuntimeParentContext,
3148
+ runtimeSpan: runRuntimeSpan,
3149
+ stageRuntimeSpan: runStageSpan,
3150
+ }));
3151
+ buildRuntimeSettled = true;
3152
+ // Capture acquired resources before the cancellation boundary so the
3153
+ // normal settlement path also releases a just-completed build.
3154
+ releaseStagingLease = prepared.sessionStagingLeaseRelease;
3155
+ }
3156
+ finally {
3157
+ await startupCancellation.finish();
3158
+ }
3159
+ // Per-project staging outcomes for the referenced (mentioned) projects, surfaced back to the
3160
+ // server on the run result. A referenced project that failed to stage into the sandbox is a
3161
+ // first-class, counted failure in the requested-vs-synced observability, not only a warning. The
3162
+ // list is empty on a local target, on a transport that does not stage referenced projects, or
3163
+ // when every staged referenced project succeeded, so the spread adds the field only when there
3164
+ // is a failure to report.
3165
+ const referencedProjectStagingFailures = (prepared.stagedRuntime?.additionalSourceFailures ?? []).map((failure) => ({ projectId: failure.projectId, error: failure.error }));
3166
+ referencedProjectStagingFailuresField =
3167
+ referencedProjectStagingFailures.length > 0 ? { referencedProjectStagingFailures } : {};
3168
+ // Write one run-log line per failure, so a reader of the run log alone sees
3169
+ // each dropped referenced project and its reason. The run continues without
3170
+ // the failed project (per-project failure isolation). Goes to stderr: the
3171
+ // acpx stdout log stream carries machine-parseable JSON event payloads.
3172
+ for (const failure of referencedProjectStagingFailures) {
3173
+ await ctx.onLog("stderr", `[paperclip] Referenced project ${failure.projectId} failed to stage; the run continues without it: ${failure.error}\n`);
3174
+ }
3175
+ // State the effective wall-clock timeout and its source up front so a
3176
+ // later timeout is diagnosable from the run log alone. Goes to stderr:
3177
+ // the acpx stdout log stream carries JSON acpx.* event payloads and must
3178
+ // stay machine-parseable line by line.
3179
+ await ctx.onLog("stderr", `[paperclip] ${formatAdapterExecutionTimeoutStartLogLine(prepared.timeoutResolution)}\n`);
3180
+ await hostStore.evictIdle(now());
3181
+ const previousParams = parseObject(ctx.runtime.sessionParams);
3182
+ const canResume = isCompatibleSession(previousParams, prepared);
3183
+ const resumeSessionId = canResume ? asString(previousParams.acpSessionId, "") || undefined : undefined;
3184
+ // Borrow the warm entry without removing it, so an overlapping run of the
3185
+ // same session still sees it. The borrow clears the entry's idle timer, so
3186
+ // the reused runtime cannot expire under its own timer while this run uses
3187
+ // it (today's `clearWarmHandleTimer(cached)` after the reuse decision).
3188
+ const cached = canResume ? hostStore.borrow(prepared.sessionKey) : undefined;
3189
+ childStderrState = cached?.childStderrState ?? { logPath: null, pendingLiveLine: "" };
3190
+ processIdentitySink = cached?.processIdentitySink ?? {
3191
+ current: ctx.onSpawn,
3192
+ latest: null,
3193
+ };
3194
+ // ACPX runtimes can stay warm across heartbeat runs. Keep the callback
3195
+ // target mutable so a later agent respawn records identity on the current
3196
+ // heartbeat instead of the run that originally created the runtime.
3197
+ processIdentitySink.current = ctx.onSpawn;
3198
+ flushChildStderr(childStderrState);
3199
+ childStderrState.logPath = prepared.childStderrLogPath;
3200
+ const persistedRuntimeStore = createRuntimeStore({ stateDir: prepared.stateDir });
3201
+ const runtimeStore = {
3202
+ async load(id) {
3203
+ const record = await persistedRuntimeStore.load(id);
3204
+ if (!record)
3205
+ return undefined;
3206
+ // ACPX resumes from the stored session options rather than the
3207
+ // options passed to ensureSession. Keep conversation state, but
3208
+ // launch the provider with this run's credentials and scratch paths.
3209
+ return {
3210
+ ...record,
3211
+ acpx: {
3212
+ ...record.acpx,
3213
+ session_options: {
3214
+ ...record.acpx?.session_options,
3215
+ env: { ...prepared.env },
3216
+ },
3217
+ },
3218
+ };
3219
+ },
3220
+ save: (record) => persistedRuntimeStore.save(record),
3221
+ };
3222
+ const runtimeOptions = {
3223
+ cwd: prepared.cwd,
3224
+ // Host-only spawn cwd for the relay proxy on the remote process-session
3225
+ // lane; `undefined` elsewhere so acpx falls back to `cwd` (byte-identical).
3226
+ // The advertised `session/new` cwd (`prepared.cwd` = `remoteCwd`) and the
3227
+ // fingerprint / compat key are unaffected — this redirects ONLY the host
3228
+ // `spawn()` `chdir`, not the in-sandbox data path.
3229
+ spawnCwd: prepared.hostSpawnCwd,
3230
+ sessionStore: runtimeStore,
3231
+ agentRegistry: prepared.agentRegistry,
3232
+ permissionMode: prepared.permissionMode,
3233
+ nonInteractivePermissions: prepared.nonInteractivePermissions,
3234
+ mcpServers: prepared.mcpServers,
3235
+ timeoutMs: prepared.timeoutSec > 0 ? prepared.timeoutSec * 1000 : undefined,
3236
+ // Scope ACPX runtime verbose logs to the claude agent only. Codex
3237
+ // and custom agents already emit their own per-tool output and don't
3238
+ // benefit from doubling the log volume.
3239
+ verbose: prepared.acpxAgent === "claude",
3240
+ // The engine passes a complete, sanitized launch environment. ACPX
3241
+ // must not merge the Paperclip server's ambient environment back in
3242
+ // when it spawns the provider child.
3243
+ inheritProcessEnv: false,
3244
+ onAgentStderr: prepared.childStderrLogPath
3245
+ ? (chunk) => routeChildStderr(childStderrState, chunk)
3246
+ : undefined,
3247
+ onAgentSpawn: async (meta) => {
3248
+ processIdentitySink.latest = meta;
3249
+ processIdentitySink.localProcess = prepared.processSessionBridge ? undefined : captureLocalProcess(meta.pid);
3250
+ await processIdentitySink.current?.({
3251
+ pid: meta.pid,
3252
+ processGroupId: null,
3253
+ startedAt: meta.startedAt,
3254
+ });
3255
+ },
3256
+ getRuntimeParentContext,
3257
+ };
3258
+ // Open Q2: split the ~7s `acp.handshake` into the two in-repo-observable
3259
+ // sub-phases — the ACP runtime construction (`createRuntime`) vs the session
3260
+ // establishment envelope (`ensureSession`). The patched spawn lifecycle
3261
+ // hook records process identity, but the finer spawn/`initialize`/
3262
+ // `session/new` timing split still lives inside external `acpx`.
3263
+ // `createRuntime` runs once and only on a cold start; a warm-handle hit
3264
+ // reuses `cached.runtime`, so `createRuntimeMs` stays undefined and the
3265
+ // split reports nothing for it.
3266
+ let createRuntimeMs;
3267
+ // A warm handle reuses the running ACP runtime; a miss constructs one. The
3268
+ // root span records this as `cold_start`.
3269
+ coldStart = !cached?.runtime;
3270
+ if (cached?.runtime) {
3271
+ runtime = cached.runtime;
3272
+ }
3273
+ else {
3274
+ const createRuntimeStart = now();
3275
+ runtime = createRuntime(runtimeOptions);
3276
+ createRuntimeMs = now() - createRuntimeStart;
3277
+ // The create_runtime phase runs only on a cold start.
3278
+ await emitRunPhaseTiming(ctx, "create_runtime", createRuntimeMs, "ok");
3279
+ }
3280
+ // Register the runtime composite in the ledger now that it exists. The
3281
+ // settlement `endSession` step closes it on every path the reuse decision
3282
+ // does not transfer. Registration here (before `ensureSession`) closes the
3283
+ // cold-handshake leak: a cold `ensureSession` throw before a handle exists
3284
+ // still leaves the runtime in the ledger for `endSession` to close. A
3285
+ // create-runtime failure throws before this line, so the runtime never
3286
+ // enters the ledger and the settlement no-ops on the empty slot. The
3287
+ // session handle fills in below; the payload carries the best-known handle
3288
+ // for the composite (`runtime.close` is the one release boundary).
3289
+ runResourceLedger.register({
3290
+ id: "acp_runtime",
3291
+ payload: {
3292
+ runtime,
3293
+ sessionHandle: cached?.handle ?? syntheticCloseHandle(),
3294
+ childProcessPid: processIdentitySink.latest?.pid ?? null,
3295
+ },
3296
+ scope: "per_run",
3297
+ });
3298
+ if (!canResume && asString(previousParams.runtimeSessionName, "")) {
3299
+ await ctx.onLog("stdout", `[paperclip] ACPX session "${asString(previousParams.runtimeSessionName, "")}" does not match the current agent/cwd/mode/runtime identity; starting fresh in "${prepared.cwd}".\n`);
3300
+ }
3301
+ let handle = cached?.handle ?? null;
3302
+ // The ensure_session phase covers the handshake (session establishment or
3303
+ // resume) up to the established handle.
3304
+ const ensureSessionPhaseStart = now();
3305
+ resumedSession = Boolean(handle ?? resumeSessionId);
3306
+ const isHandshakeTransportLost = () => prepared.paperclipBridge?.readRunDisposition?.().failed ?? false;
3307
+ // The fence's own close, for a real handle that resolves after
3308
+ // `endSession` already sealed. `endSession` closed the synthetic
3309
+ // placeholder by then (or skipped closing because the channel was
3310
+ // already known lost), so this is the one close for this real handle,
3311
+ // not a second one.
3312
+ // A rejected close comes from inside the sandbox (a forged late handle
3313
+ // can make `runtime.close` reject with chosen bytes). It crosses the
3314
+ // sandbox-to-host trust boundary, so it must never reach the run log,
3315
+ // the result, or a classification: log the fixed closed code only.
3316
+ // The catch stays attached so the promise stays observed and no
3317
+ // unhandled rejection can occur.
3318
+ const closeLateHandshakeHandle = (lateHandle) => {
3319
+ if (isHandshakeTransportLost())
3320
+ return;
3321
+ void runtime
3322
+ .close({
3323
+ handle: lateHandle,
3324
+ reason: "paperclip late handshake cleanup",
3325
+ discardPersistentState: false,
3326
+ })
3327
+ .catch(() => ctx
3328
+ .onLog("stderr", "[paperclip] ACPX handshake late close failed: acpx_handshake_late_close_failed\n")
3329
+ .catch(() => { }));
3330
+ };
3331
+ // The late rejection comes from inside the sandbox. It crosses the
3332
+ // sandbox-to-host trust boundary, so it must never reach the run log,
3333
+ // the result, or a classification: log the fixed closed code only.
3334
+ const recordLateHandshakeRejection = () => {
3335
+ void ctx
3336
+ .onLog("stderr", "[paperclip] ACPX handshake late rejection: acpx_handshake_late_rejection\n")
3337
+ .catch(() => { });
3338
+ };
3339
+ try {
3340
+ if (!handle) {
3341
+ try {
3342
+ // Step 7 — acp.handshake: ACP session establishment (session/new or
3343
+ // resume). A throwing handshake still reports its duration before the
3344
+ // resume-retry path below runs. The createRuntime/ensureSession
3345
+ // sub-split rides the step span as fixed, closed keys (Open Q2).
3346
+ let ensureSessionMs;
3347
+ handle = await measureStartupStep(ctx, now, "acp.handshake", async () => {
3348
+ const ensureSessionStart = now();
3349
+ const established = await guardEnsureSession({
3350
+ call: () => runtime.ensureSession({
3351
+ sessionKey: prepared.sessionKey,
3352
+ agent: prepared.acpxAgent,
3353
+ mode: prepared.mode,
3354
+ cwd: prepared.cwd,
3355
+ resumeSessionId,
3356
+ sessionOptions: { env: prepared.env },
3357
+ }),
3358
+ fence: handshakeFence,
3359
+ isTransportLost: isHandshakeTransportLost,
3360
+ onLateHandleAfterSeal: closeLateHandshakeHandle,
3361
+ onLateRejection: recordLateHandshakeRejection,
3362
+ });
3363
+ ensureSessionMs = now() - ensureSessionStart;
3364
+ return established;
3365
+ }, {
3366
+ ...prepared.stepMetrics,
3367
+ // The two sub-times ride the span as fixed, closed keys.
3368
+ spanWallTimes: () => ({
3369
+ createRuntime: createRuntimeMs,
3370
+ ensureSession: ensureSessionMs,
3371
+ }),
3372
+ });
3373
+ }
3374
+ catch (err) {
3375
+ if (!resumeSessionId || !isResumeFailure(err))
3376
+ throw err;
3377
+ clearSession = true;
3378
+ resumedSession = false;
3379
+ await ctx.onLog("stdout", `[paperclip] ACPX resume session "${resumeSessionId}" is unavailable; retrying with a fresh session.\n`);
3380
+ // Fresh-session retry: the runtime was already constructed on the
3381
+ // first attempt (never re-created), so this event reports only its
3382
+ // own `ensureSessionMs` — no `createRuntimeMs`.
3383
+ let retryEnsureSessionMs;
3384
+ handle = await measureStartupStep(ctx, now, "acp.handshake", async () => {
3385
+ const ensureSessionStart = now();
3386
+ const established = await guardEnsureSession({
3387
+ call: () => runtime.ensureSession({
3388
+ sessionKey: prepared.sessionKey,
3389
+ agent: prepared.acpxAgent,
3390
+ mode: prepared.mode,
3391
+ cwd: prepared.cwd,
3392
+ sessionOptions: { env: prepared.env },
3393
+ }),
3394
+ fence: handshakeFence,
3395
+ isTransportLost: isHandshakeTransportLost,
3396
+ onLateHandleAfterSeal: closeLateHandshakeHandle,
3397
+ onLateRejection: recordLateHandshakeRejection,
3398
+ });
3399
+ retryEnsureSessionMs = now() - ensureSessionStart;
3400
+ return established;
3401
+ }, {
3402
+ ...prepared.stepMetrics,
3403
+ // The retry reuses the runtime from the first attempt, so it reports
3404
+ // only its own ensure-session sub-time on the span.
3405
+ spanWallTimes: () => ({ ensureSession: retryEnsureSessionMs }),
3406
+ });
3407
+ }
3408
+ }
3409
+ else {
3410
+ // Warm-handle hit: a compatible cached handle reuses the running ACP
3411
+ // agent, so the `acp.handshake` step does no work. Emit a step span and
3412
+ // event with `outcome = skipped` and a zero wall time, so the trace and
3413
+ // the run log show the skip as a distinct outcome, never a misleading
3414
+ // zero-work `ok` step.
3415
+ await emitSkippedStartupStep(ctx, "acp.handshake", {
3416
+ tracer: prepared.stepMetrics.tracer,
3417
+ parentContext: prepared.stepMetrics.parentContext,
3418
+ });
3419
+ }
3420
+ if (resumeSessionId && handle?.backendSessionId !== resumeSessionId) {
3421
+ resumedSession = false;
3422
+ clearSession = true;
3423
+ }
3424
+ // A compatible warm handle reuses the already-running ACP agent and does
3425
+ // not emit another spawn event. Persist its known identity on this run
3426
+ // before the next prompt starts so every running heartbeat is adoptable.
3427
+ if (handle && cached && processIdentitySink.latest && ctx.onSpawn) {
3428
+ await ctx.onSpawn({
3429
+ pid: processIdentitySink.latest.pid,
3430
+ processGroupId: null,
3431
+ startedAt: processIdentitySink.latest.startedAt,
3432
+ });
3433
+ }
3434
+ }
3435
+ catch (err) {
3436
+ // Record how the settlement closes the runtime on a pre-turn handshake
3437
+ // failure: a direct close that drops a matching warm entry, the same as
3438
+ // the configuration-failure path. A warm-hit reuse already cleared the
3439
+ // idle timer before the handshake ran, so the failure must close and
3440
+ // remove the entry, never re-arm it. A cold `ensureSession` throw has no
3441
+ // established handle, so the settlement closes the synthetic handle — the
3442
+ // ledger now holds the runtime, so `endSession` closes it and the former
3443
+ // cold-handshake leak is gone. The settlement stops the bridges, releases
3444
+ // the staging lease, and flushes the child stderr on every exit path.
3445
+ //
3446
+ // A handshake-guard rejection is the one exception: `handle` stays
3447
+ // null, but the abandoned `ensureSession` call can still resolve
3448
+ // into a real handle later. `handshakeAbandoned` tells `endSession`
3449
+ // to defer that placeholder close to `handshakeFence` when no late
3450
+ // handle has arrived yet, instead of closing it here and possibly
3451
+ // again later.
3452
+ const guardTripped = isAcpxHandshakeTimeoutError(err) || isAcpxHandshakeTransportLostError(err);
3453
+ handshakeAbandoned = guardTripped;
3454
+ runtimeSettlement = {
3455
+ mode: "direct",
3456
+ handle: handle ?? syntheticCloseHandle(),
3457
+ reason: "paperclip handshake cleanup",
3458
+ discardPersistentState: false,
3459
+ dropWarmEntry: true,
3460
+ recordCloseError: true,
3461
+ cancelTurnReason: null,
3462
+ skipRemoteClose: false,
3463
+ };
3464
+ await emitPhase("ensure_session", ensureSessionPhaseStart, "failed");
3465
+ const { classified, message } = await emitAcpxFailure({
3466
+ ctx,
3467
+ prepared,
3468
+ err,
3469
+ phase: "ensure_session",
3470
+ // The guard's own message is already generic and host-authored, so
3471
+ // this replaces the underlying error text with the same closed
3472
+ // message `classifyError` classified — the ensure_session phase
3473
+ // never gets a chance to summarize it differently.
3474
+ ...(guardTripped && err instanceof Error ? { messageOverride: err.message } : {}),
3475
+ // A compromised child can hold ensureSession open until the guard
3476
+ // fires, then write chosen bytes to its own stderr. Suppress the
3477
+ // child stderr tail on this route only, so those sandbox-provided
3478
+ // bytes never reach the run log or the acpx.error payload.
3479
+ ...(guardTripped ? { suppressChildStderrTail: true } : {}),
3480
+ });
3481
+ capturedResult = {
3482
+ exitCode: 1,
3483
+ signal: null,
3484
+ timedOut: false,
3485
+ errorMessage: message,
3486
+ ...classified,
3487
+ ...billingFields,
3488
+ ...referencedProjectStagingFailuresField,
3489
+ model: prepared.requestedModel || null,
3490
+ clearSession,
3491
+ resultJson: { phase: "ensure_session" },
3492
+ summary: message,
3493
+ };
3494
+ return settleFor("handshake", err);
3495
+ }
3496
+ if (!handle) {
3497
+ // ensureSession returned no session handle. The ledger holds the runtime
3498
+ // the run constructed, so record a direct close of the synthetic handle
3499
+ // for the settlement `endSession` step (the child process cannot leak).
3500
+ runtimeSettlement = {
3501
+ mode: "direct",
3502
+ handle: syntheticCloseHandle(),
3503
+ reason: "paperclip missing-handle cleanup",
3504
+ discardPersistentState: false,
3505
+ dropWarmEntry: false,
3506
+ recordCloseError: true,
3507
+ cancelTurnReason: null,
3508
+ skipRemoteClose: false,
3509
+ };
3510
+ await emitPhase("ensure_session", ensureSessionPhaseStart, "failed");
3511
+ capturedResult = {
3512
+ exitCode: 1,
3513
+ signal: null,
3514
+ timedOut: false,
3515
+ errorMessage: "ACPX did not return a runtime session handle.",
3516
+ errorCode: "acpx_runtime_error",
3517
+ ...billingFields,
3518
+ ...referencedProjectStagingFailuresField,
3519
+ model: prepared.requestedModel || null,
3520
+ resultJson: { phase: "ensure_session" },
3521
+ summary: "ACPX did not return a runtime session handle.",
3522
+ };
3523
+ return settleFor("session_handle_missing", new Error("ACPX did not return a runtime session handle."));
3524
+ }
3525
+ sessionHandle = handle;
3526
+ startupFailed = false;
3527
+ await emitPhase("ensure_session", ensureSessionPhaseStart, "ok");
3528
+ }
3529
+ catch (err) {
3530
+ if (!buildRuntimeSettled) {
3531
+ // buildRuntime failed before it staged or bridged anything, so there is
3532
+ // nothing to settle here. The finally below ends the sandbox.startup
3533
+ // span; let the failure propagate.
3534
+ throw err;
3535
+ }
3536
+ // The post-build runtime-creation window failed after buildRuntime returned
3537
+ // live bridges and a held staging lease. `createRuntime` throws before the
3538
+ // ledger registers the runtime, so the settlement `endSession` step no-ops
3539
+ // on the empty slot (create_runtime never closes a runtime). The settlement
3540
+ // still stops the bridges, discards the staged runtime, releases the staging
3541
+ // lease, and flushes the child stderr.
3542
+ const { classified, message } = await emitAcpxFailure({
3543
+ ctx,
3544
+ prepared,
3545
+ err,
3546
+ phase: "create_runtime",
3547
+ });
3548
+ capturedResult = {
3549
+ exitCode: 1,
3550
+ signal: null,
3551
+ timedOut: false,
3552
+ errorMessage: message,
3553
+ ...classified,
3554
+ ...billingFields,
3555
+ ...referencedProjectStagingFailuresField,
3556
+ model: prepared.requestedModel || null,
3557
+ clearSession,
3558
+ resultJson: { phase: "create_runtime" },
3559
+ summary: message,
3560
+ };
3561
+ return settleFor("runtime_create", err);
3562
+ }
3563
+ finally {
3564
+ // End the sandbox.startup span exactly once, on every return and on every
3565
+ // throw. It covers buildRuntime through acp.handshake and no further; the
3566
+ // agent turn runs after and is out of the span's scope.
3567
+ rootSpan.end(startupFailed);
3568
+ }
3569
+ const configureSessionStart = now();
3570
+ try {
3571
+ await applySessionConfigOptions({
3572
+ runtime,
3573
+ handle: sessionHandle,
3574
+ prepared,
3575
+ onLog: ctx.onLog,
3576
+ });
3577
+ await emitPhase("configure_session", configureSessionStart, "ok");
3578
+ }
3579
+ catch (err) {
3580
+ // Record a direct close that drops the matching warm entry for the
3581
+ // settlement `endSession` step. The settlement stops the bridges, discards
3582
+ // the staged runtime, releases the staging lease, and flushes the child
3583
+ // stderr on this exit path too.
3584
+ runtimeSettlement = {
3585
+ mode: "direct",
3586
+ handle: sessionHandle,
3587
+ reason: "paperclip config cleanup",
3588
+ discardPersistentState: false,
3589
+ dropWarmEntry: true,
3590
+ recordCloseError: true,
3591
+ cancelTurnReason: null,
3592
+ skipRemoteClose: false,
3593
+ };
3594
+ await emitPhase("configure_session", configureSessionStart, "failed");
3595
+ const { classified, message } = await emitAcpxFailure({
3596
+ ctx,
3597
+ prepared,
3598
+ err,
3599
+ phase: "configure_session",
3600
+ });
3601
+ capturedResult = {
3602
+ exitCode: 1,
3603
+ signal: null,
3604
+ timedOut: false,
3605
+ errorMessage: message,
3606
+ ...classified,
3607
+ ...billingFields,
3608
+ ...referencedProjectStagingFailuresField,
3609
+ model: prepared.requestedModel || null,
3610
+ clearSession,
3611
+ resultJson: {
3612
+ phase: "configure_session",
3613
+ agent: prepared.acpxAgent,
3614
+ requestedModel: prepared.requestedModel || null,
3615
+ requestedThinkingEffort: prepared.requestedThinkingEffort || null,
3616
+ fastMode: prepared.fastMode,
3617
+ },
3618
+ summary: message,
3619
+ };
3620
+ return settleFor("session_configuration", err);
3621
+ }
3622
+ // Startup succeeded: seal the ledger (promotes the startup_rollback entries
3623
+ // that survived to `per_run`) and hand the ready resources to the turn. The
3624
+ // turn wrapper reads the run state through the shared closure locals in this
3625
+ // phase, so the sealed view and the context are the contract carriers only.
3626
+ return {
3627
+ kind: "ready",
3628
+ resources: runResourceLedger.seal([]),
3629
+ context: { sessionKey: prepared.sessionKey },
3630
+ };
3631
+ };
3632
+ // The turn step: run the turn sequence and settle the runtime inline
3633
+ // (today's behavior). The sequence owns the wall-clock timer and the abort
3634
+ // controller and never rejects; it returns a `TurnCompletion`. The step
3635
+ // bodies below record the external result for the coordinator to reproduce.
3636
+ const runTurn = async (_ready) => {
3637
+ // Summary accumulation collects output text only (never thought stream),
3638
+ // segmented on tool starts so multi-step narration is not glued into one
3639
+ // automatic comment dump.
3640
+ const outputSegments = [];
3641
+ let currentOutputChunk = [];
3642
+ const flushOutputSegment = () => {
3643
+ if (currentOutputChunk.length === 0)
3644
+ return;
3645
+ outputSegments.push(currentOutputChunk.join(""));
3646
+ currentOutputChunk = [];
3647
+ };
3648
+ let eventBreakdown = null;
3649
+ let eventCostUsd = null;
3650
+ // The turn-local state the sequence steps share. `promptBuild` sets the
3651
+ // prompt, `preTurnUsage` sets the pre-turn status, `turnStart` sets the
3652
+ // active turn, and `turnFinalize` reads all three. `activeTurn` is the run-
3653
+ // scoped hoisted local, so the settlement `endSession` step can cancel it.
3654
+ let runPrompt = "";
3655
+ let preTurnStatus = null;
3656
+ // Phase-timing markers for the prepare_turn and turn phases. The prepare
3657
+ // phase covers the prompt build and the pre-turn usage snapshot; the turn
3658
+ // phase covers the started turn and the event relay.
3659
+ let preparePhaseStart = now();
3660
+ let turnPhaseStart = now();
3661
+ // Open the agent turn span as a child of the run root span. It wraps the
3662
+ // whole turn: the executor holds `turnSpan.parentContext` for later exec
3663
+ // parenting, and the `finally` below ends the span once on every path. The
3664
+ // span is declared before the `try` so the `finally` can reach it.
3665
+ const turnSpan = openTurnSpan(tracing, now, runRootSpan.parentContext);
3666
+ // Switch the current-run holder to the `agent.turn` token for the turn, so
3667
+ // a detached exec during the turn parents to `agent.turn`. The turn
3668
+ // `finally` resets the holder to the `task.run` token.
3669
+ currentRunParentContext = turnSpan.parentContext;
3670
+ const stepPromptBuild = async (_signal) => {
3671
+ // Build the prompt and emit the run metadata inside the turn failure
3672
+ // boundary. A failure here returns an error result with phase
3673
+ // `prepare_turn`.
3674
+ preparePhaseStart = now();
3675
+ const { prompt, promptMetrics, commandNotes } = await buildPrompt(ctx, resumedSession, prepared.env);
3676
+ runPrompt = joinPromptSections([prepared.skillPromptInstructions, prompt]);
3677
+ await emitAcpxLog(ctx, {
3678
+ type: "acpx.session",
3679
+ agent: prepared.acpxAgent,
3680
+ sessionId: sessionHandle.backendSessionId,
3681
+ acpSessionId: sessionHandle.backendSessionId,
3682
+ agentSessionId: sessionHandle.agentSessionId,
3683
+ runtimeSessionName: sessionHandle.runtimeSessionName,
3684
+ mode: prepared.mode,
3685
+ permissionMode: prepared.permissionMode,
3686
+ model: prepared.requestedModel || null,
3687
+ thinkingEffort: prepared.requestedThinkingEffort || null,
3688
+ fastMode: prepared.fastMode,
3689
+ });
3690
+ if (ctx.onMeta) {
3691
+ await ctx.onMeta({
3692
+ adapterType: engine.adapterType,
3693
+ command: prepared.agentCommand ?? prepared.acpxAgent,
3694
+ cwd: prepared.cwd,
3695
+ commandNotes: [
3696
+ `ACPX runtime embedded in Paperclip with ${prepared.mode} session mode.`,
3697
+ `Effective ACPX permission mode: ${prepared.permissionMode}.`,
3698
+ ...(prepared.requestedModel
3699
+ ? [
3700
+ prepared.acpxAgent === "claude"
3701
+ ? `Requested ACPX model: ${prepared.requestedModel} (set via ANTHROPIC_MODEL env at startup).`
3702
+ : prepared.acpxAgent === "codex"
3703
+ ? `Requested ACPX model: ${prepared.requestedModel} (set via CODEX_CONFIG at startup).`
3704
+ : `Requested ACPX model: ${prepared.requestedModel}.`,
3705
+ ]
3706
+ : []),
3707
+ ...(prepared.requestedThinkingEffort ? [`Requested ACPX thinking effort: ${prepared.requestedThinkingEffort}.`] : []),
3708
+ ...(prepared.fastMode ? ["Requested ACPX Codex fast mode."] : []),
3709
+ ...(Array.isArray(prepared.skillsIdentity.commandNotes)
3710
+ ? prepared.skillsIdentity.commandNotes.filter((note) => typeof note === "string")
3711
+ : []),
3712
+ ...commandNotes,
3713
+ ],
3714
+ env: prepared.loggedEnv,
3715
+ prompt: runPrompt,
3716
+ promptMetrics,
3717
+ context: ctx.context,
3718
+ });
3719
+ }
3720
+ };
3721
+ const stepPreTurnUsage = async () => {
3722
+ // Snapshot pre-turn usage so cumulative agent-reported cost can be
3723
+ // attributed to this run alone.
3724
+ preTurnStatus = await readRuntimeStatus(runtime, sessionHandle);
3725
+ // The prepare phase (prompt build + usage snapshot) finished; the turn
3726
+ // phase starts next.
3727
+ await emitPhase("prepare_turn", preparePhaseStart, "ok");
3728
+ turnPhaseStart = now();
3729
+ };
3730
+ const stepTurnStart = (signal, startTimeoutMs) => {
3731
+ ctx.signal?.throwIfAborted();
3732
+ const turn = runtime.startTurn({
3733
+ handle: sessionHandle,
3734
+ text: runPrompt,
3735
+ mode: "prompt",
3736
+ requestId: ctx.runId,
3737
+ timeoutMs: startTimeoutMs,
3738
+ signal,
3739
+ // The callback belongs to this turn, including when a runtime is reused.
3740
+ // Raw provider text must never enter the result or the run log.
3741
+ ...(deps.classifyTerminalSessionFailure
3742
+ ? {
3743
+ onTerminalSessionFailure: (failure) => {
3744
+ terminalFailureClassification = deps.classifyTerminalSessionFailure(failure, new Date(now()));
3745
+ },
3746
+ }
3747
+ : {}),
3748
+ });
3749
+ activeTurn = turn;
3750
+ // A latched sandbox duplex-channel loss otherwise has no way to reach
3751
+ // this turn: the bridge only exposes a pull read, and the engine
3752
+ // pulls it at the terminal-finalization boundary, which runs only
3753
+ // after the turn already returned a terminal result. A channel that
3754
+ // dies mid-turn then leaves the turn with no terminal result to
3755
+ // return, so it waits for the wall-clock adapter execution timeout
3756
+ // instead of failing fast. Cancel the turn the moment a terminal loss
3757
+ // latches — whether it latches from here on, or already latched
3758
+ // before this turn started — so the turn returns a terminal result
3759
+ // right away. `turnFinalize` reads the same latch and builds the
3760
+ // failure from the typed loss reason alone.
3761
+ const bridge = prepared.paperclipBridge;
3762
+ if (bridge?.onLoss) {
3763
+ const cancelForLoss = (reason) => {
3764
+ void turn.cancel({ reason: `paperclip sandbox duplex channel lost (${reason})` }).catch(() => { });
3765
+ // `cancel()` only asks the agent to end the turn; it does not end
3766
+ // the turn by itself. Start the fail-fast deadline the moment the
3767
+ // loss latches, so the run does not wait past this bound for an
3768
+ // agent that stopped answering.
3769
+ if (!lossDeadlineTimer && !lossDeadlineTripped) {
3770
+ lossDeadlineTimer = setTimeout(() => {
3771
+ lossDeadlineTripped = true;
3772
+ resolveLossDeadline?.();
3773
+ }, duplexLossCancelDeadlineMs);
3774
+ }
3775
+ };
3776
+ removeLossListener = bridge.onLoss(cancelForLoss);
3777
+ const alreadyLatched = bridge.readRunDisposition?.();
3778
+ if (alreadyLatched?.failed)
3779
+ cancelForLoss(alreadyLatched.lossReason ?? "other");
3780
+ }
3781
+ // ACP can resolve the turn before its provider exits. Keep the Stop
3782
+ // deadline armed through settlement, including provider cleanup.
3783
+ const armStopDeadline = () => {
3784
+ stopTimer = setTimeout(() => {
3785
+ forcedStop = true;
3786
+ const providerPid = processIdentitySink.latest?.pid;
3787
+ if (providerPid && !prepared.processSessionBridge) {
3788
+ // Escalate the owned local process directly. A second ACP close
3789
+ // can wait behind the same hung cleanup (or open a new client).
3790
+ killCapturedLocalProcess(processIdentitySink.localProcess);
3791
+ }
3792
+ else {
3793
+ void runtime.close({ handle: sessionHandle, reason: "operator stop deadline", discardPersistentState: true })
3794
+ .catch(() => { });
3795
+ }
3796
+ }, Math.max(1, asNumber(ctx.config.graceSec, 15)) * 1000);
3797
+ };
3798
+ ctx.signal?.addEventListener("abort", armStopDeadline, { once: true });
3799
+ removeStopListener = () => ctx.signal?.removeEventListener("abort", armStopDeadline);
3800
+ if (ctx.signal?.aborted)
3801
+ armStopDeadline();
3802
+ return {
3803
+ cancel: async (reason) => {
3804
+ await turn.cancel({ reason });
3805
+ },
3806
+ };
3807
+ };
3808
+ // The host-built terminal `stepEventRelay` hands to `turnFinalize` once
3809
+ // the fail-fast deadline fires with no agent-supplied terminal. Its
3810
+ // `status` mirrors the shape a real cooperative cancel already
3811
+ // produces, so `turnFinalize` needs no change: it reads the latched
3812
+ // loss disposition, not this `stopReason`, to build the reported
3813
+ // failure and its message.
3814
+ const LOSS_DEADLINE_TERMINAL = {
3815
+ status: "cancelled",
3816
+ stopReason: "paperclip_duplex_loss_deadline",
3817
+ };
3818
+ const stepEventRelay = async () => {
3819
+ const turn = activeTurn;
3820
+ const toolTitles = new Map();
3821
+ const drainEvents = (async () => {
3822
+ for await (const event of turn.events) {
3823
+ // ACPX currently flattens client-side filesystem/terminal receipts
3824
+ // into status text. They cannot establish complete action outcomes.
3825
+ if (event.type === "status" && /^(fs|terminal)\//.test(event.text))
3826
+ incompleteToolInventory = true;
3827
+ if (event.type === "tool_call") {
3828
+ if (!event.toolCallId)
3829
+ incompleteToolInventory = true;
3830
+ else {
3831
+ const previous = interruptionTools.get(event.toolCallId);
3832
+ interruptionTools.set(event.toolCallId, {
3833
+ kind: event.kind ?? previous?.kind,
3834
+ status: event.status ?? previous?.status,
3835
+ });
3836
+ }
3837
+ }
3838
+ if (event.type === "text_delta" && event.stream !== "thought") {
3839
+ currentOutputChunk.push(event.text);
3840
+ }
3841
+ else if (event.type === "tool_call" && event.tag !== "tool_call_update") {
3842
+ // ACP makes tool-call status optional. The normalized event tag is
3843
+ // the reliable boundary between an initial call and its updates,
3844
+ // so a statusless initial call must still end the preceding output
3845
+ // segment while updates must not create extra boundaries.
3846
+ flushOutputSegment();
3847
+ }
3848
+ if (event.type === "status" && event.tag === "usage_update") {
3849
+ eventBreakdown = event.breakdown ?? eventBreakdown;
3850
+ eventCostUsd = usdCostAmount(event.cost) ?? eventCostUsd;
3851
+ }
3852
+ await emitRuntimeEvent(ctx, event, toolTitles, prepared.coalescePlaceholderToolUpdates);
3853
+ }
3854
+ })();
3855
+ // A latched loss already asked the agent to cancel (above, in
3856
+ // `cancelForLoss`); that request settles neither `turn.events` nor
3857
+ // `turn.result` by itself. Race the event drain against the fail-fast
3858
+ // deadline so a silent agent cannot hold this wait open.
3859
+ const eventsEnded = await Promise.race([
3860
+ drainEvents.then(() => true),
3861
+ lossDeadline.then(() => false),
3862
+ ]);
3863
+ if (!eventsEnded) {
3864
+ // The deadline won: stop waiting on the agent. `closeStream` ends
3865
+ // the event drain locally, with no agent cooperation required. Await
3866
+ // both the close call and the drain it unblocks before this step
3867
+ // returns, so no late runtime event can still mutate shared state
3868
+ // (output segments, tool inventory) after finalization reads it.
3869
+ await turn.closeStream({ reason: "paperclip duplex loss cancel deadline" }).catch(() => { });
3870
+ await drainEvents.catch(() => { });
3871
+ flushOutputSegment();
3872
+ return LOSS_DEADLINE_TERMINAL;
3873
+ }
3874
+ flushOutputSegment();
3875
+ // `turn.result` settles only when the agent's provider process
3876
+ // returns or rejects; a latched loss that armed the deadline after
3877
+ // the event drain already ended must still bound this wait.
3878
+ return await Promise.race([turn.result, lossDeadline.then(() => LOSS_DEADLINE_TERMINAL)]);
3879
+ };
3880
+ const stepTurnFinalize = async (input) => {
3881
+ if (input.kind === "terminal") {
3882
+ const terminal = input.terminal;
3883
+ const timedOut = input.timedOut;
3884
+ // Read the sandbox duplex control-channel disposition at the ACP
3885
+ // terminal-finalization boundary, before the bridge teardown, on every
3886
+ // terminal outcome. A control channel that died before this point
3887
+ // latches a failure with a typed loss reason; a healthy channel or a
3888
+ // normal-teardown loss reports a success. The read and the mark of the
3889
+ // host-observed orderly completion happen atomically in one broker
3890
+ // step, with no `await` between them, so a teardown loss cannot slip
3891
+ // in between. This stops a later teardown `channel_exit` from latching
3892
+ // a false loss. The mark no-ops once a loss already latched, so a real
3893
+ // mid-turn loss still fails the run — including a loss that arrived
3894
+ // through the in-flight-turn cancel this seam issues, which surfaces
3895
+ // here as a `cancelled` (not `completed`) terminal, not just through a
3896
+ // nominally completed terminal. The file bridge path never sets this
3897
+ // method, so the optional call no-ops there.
3898
+ let duplexLossReason = null;
3899
+ const disposition = prepared.paperclipBridge?.settleRunDisposition?.() ?? null;
3900
+ if (disposition?.failed) {
3901
+ duplexLossReason = disposition.lossReason ?? "other";
3902
+ }
3903
+ // A terminal that reports "completed" but whose duplex control channel
3904
+ // died before the completion is not a success. The seam fails it closed.
3905
+ const channelLost = duplexLossReason !== null;
3906
+ // Build the boundary failure message only from the closed loss-reason
3907
+ // enum, so no raw provider text rides the message.
3908
+ const channelLostMessage = duplexLossReason
3909
+ ? `The sandbox duplex control channel was lost (${duplexLossReason}) before the run completed.`
3910
+ : null;
3911
+ // A completed, non-timed-out turn whose channel stayed live is the one
3912
+ // success path. Every other outcome — a failed, cancelled, or timed-out
3913
+ // terminal, or a completed terminal with a lost channel — is a failure.
3914
+ const turnSucceeded = terminal.status === "completed" && !timedOut && !channelLost;
3915
+ // Read usage before the settlement can discard runtime state.
3916
+ const postTurnStatus = await readRuntimeStatus(runtime, sessionHandle);
3917
+ const turnUsage = summarizeAcpxTurnUsage({
3918
+ preStatus: preTurnStatus,
3919
+ postStatus: postTurnStatus,
3920
+ eventBreakdown,
3921
+ eventCostUsd,
3922
+ });
3923
+ const failedTurn = terminal.status === "failed" || terminal.status === "cancelled" || timedOut;
3924
+ // ACPX can defer session/load until runTurn. Forget an unavailable
3925
+ // session so the next bounded turn receives the full task conversation.
3926
+ const sessionUnavailable = terminal.status === "failed" &&
3927
+ terminal.error.detailCode === "SESSION_RESUME_REQUIRED";
3928
+ if (sessionUnavailable)
3929
+ clearSession = true;
3930
+ // Saving a conversation is independent from certifying tool outcomes.
3931
+ // Its next turn receives history, not a replay of pending tool calls.
3932
+ preserveInterruptedSession = ctx.signal?.aborted === true && !forcedStop && !timedOut && !channelLost
3933
+ && (terminal.status === "cancelled" || terminal.status === "completed")
3934
+ && prepared.mode === "persistent" && !prepared.processSessionBridge
3935
+ && Boolean(sessionHandle.backendSessionId);
3936
+ safeInterruptedSession = preserveInterruptedSession
3937
+ && !incompleteToolInventory
3938
+ && [...interruptionTools.values()].every((tool) => tool.kind === "read" && tool.status === "completed");
3939
+ // Record how the settlement `endSession` step closes the runtime for this
3940
+ // outcome. A clean persistent host turn is save-eligible, but the
3941
+ // Amendment B credential gate fails on the host lane (the run API key is
3942
+ // never revoked), so the reuse decision discards and `endSession` closes it
3943
+ // (close-and-relaunch). The sandbox lane always closes the runtime; its
3944
+ // staged files are the reuse. A matching warm entry closes through the warm
3945
+ // store (which also flushes its stderr); otherwise the runtime closes
3946
+ // directly. The settlement then stops the bridges, saves or discards the
3947
+ // staged runtime, releases the staging lease, and flushes the child stderr.
3948
+ runtimeSettlement = {
3949
+ mode: "warm_or_close",
3950
+ handle: sessionHandle,
3951
+ reason: timedOut
3952
+ ? "paperclip timeout cleanup"
3953
+ : channelLost
3954
+ ? "paperclip duplex channel lost cleanup"
3955
+ : failedTurn
3956
+ ? `paperclip turn ${terminal.status}`
3957
+ : "paperclip completed turn cleanup",
3958
+ discardPersistentState: sessionUnavailable || (terminal.status === "cancelled" && !preserveInterruptedSession) || timedOut || channelLost,
3959
+ dropWarmEntry: false,
3960
+ recordCloseError: false,
3961
+ cancelTurnReason: null,
3962
+ skipRemoteClose: channelLost,
3963
+ };
3964
+ const errorMessage = timedOut
3965
+ ? formatAdapterExecutionTimeoutErrorMessage(prepared.timeoutResolution)
3966
+ : channelLost
3967
+ ? channelLostMessage
3968
+ : resultErrorMessage(terminal);
3969
+ const terminalStopReason = terminal.status === "failed" ? terminal.error.message : terminal.stopReason;
3970
+ const classifiedFailure = !timedOut && !channelLost && terminal.status === "failed"
3971
+ ? terminalFailureClassification
3972
+ : null;
3973
+ await emitAcpxLog(ctx, {
3974
+ type: turnSucceeded ? "acpx.result" : "acpx.error",
3975
+ summary: channelLost ? "duplex_channel_lost" : terminal.status,
3976
+ stopReason: terminalStopReason,
3977
+ message: errorMessage,
3978
+ });
3979
+ // The one clean-completion path clears the run failure flag; every other
3980
+ // path keeps it set, so the run root span closes with error status. A
3981
+ // completed terminal with a lost duplex channel keeps the flag set.
3982
+ runFailed = turnSucceeded ? false : true;
3983
+ capturedResult = {
3984
+ exitCode: turnSucceeded ? 0 : 1,
3985
+ signal: timedOut ? "SIGTERM" : null,
3986
+ timedOut,
3987
+ errorMessage,
3988
+ errorCode: timedOut
3989
+ ? "acpx_timeout"
3990
+ : channelLost
3991
+ ? DUPLEX_CHANNEL_LOST_ERROR_CODE
3992
+ : terminal.status === "failed"
3993
+ ? classifiedFailure?.errorCode ?? "acpx_turn_failed"
3994
+ : null,
3995
+ ...(classifiedFailure?.errorFamily ? { errorFamily: classifiedFailure.errorFamily } : {}),
3996
+ ...(classifiedFailure?.retryNotBefore ? { retryNotBefore: classifiedFailure.retryNotBefore } : {}),
3997
+ sessionId: sessionHandle.backendSessionId ?? sessionHandle.runtimeSessionName,
3998
+ sessionParams: buildSessionParams({ prepared, handle: sessionHandle }),
3999
+ sessionDisplayId: sessionHandle.agentSessionId ?? sessionHandle.backendSessionId ?? sessionHandle.runtimeSessionName,
4000
+ ...billingFields,
4001
+ ...referencedProjectStagingFailuresField,
4002
+ model: prepared.requestedModel || null,
4003
+ ...(turnUsage.usage ? { usage: turnUsage.usage, usageBasis: "per_run" } : {}),
4004
+ costUsd: turnUsage.costUsd,
4005
+ resultJson: {
4006
+ status: channelLost ? "failed" : terminal.status,
4007
+ ...(classifiedFailure?.errorFamily ? { errorFamily: classifiedFailure.errorFamily } : {}),
4008
+ ...(classifiedFailure?.retryNotBefore
4009
+ ? {
4010
+ retryNotBefore: classifiedFailure.retryNotBefore,
4011
+ ...(classifiedFailure.errorFamily === "provider_quota"
4012
+ ? { providerQuotaRetryNotBefore: classifiedFailure.retryNotBefore }
4013
+ : {}),
4014
+ }
4015
+ : {}),
4016
+ stopReason: terminalStopReason,
4017
+ permissionMode: prepared.permissionMode,
4018
+ mode: prepared.mode,
4019
+ requestedModel: prepared.requestedModel || null,
4020
+ requestedThinkingEffort: prepared.requestedThinkingEffort || null,
4021
+ fastMode: prepared.fastMode,
4022
+ ...(turnUsage.usageDetail ? { usage: turnUsage.usageDetail } : {}),
4023
+ ...(turnUsage.cumulativeCostUsd != null
4024
+ ? { cumulativeCostUsd: turnUsage.cumulativeCostUsd }
4025
+ : {}),
4026
+ },
4027
+ summary: buildAcpxRunSummary({
4028
+ outputSegments,
4029
+ fallback: terminalStopReason || terminal.status,
4030
+ }),
4031
+ clearSession,
4032
+ };
4033
+ // The turn phase finished. A completed, non-timed-out turn with a live
4034
+ // duplex channel is `ok`; every other terminal outcome is `failed`.
4035
+ await emitPhase("turn", turnPhaseStart, turnSucceeded ? "ok" : "failed");
4036
+ // Return the typed turn completion so the coordinator settles for the right
4037
+ // cause. The completion carries no live resources; the settlement claims the
4038
+ // ledger and owns the release. A clean completed turn carries no cause, so
4039
+ // the reuse decision can permit a save; a timed-out, failed, or cancelled
4040
+ // turn carries its cause, which forbids the save.
4041
+ if (timedOut) {
4042
+ return {
4043
+ kind: "timed_out",
4044
+ cause: { kind: "turn_timed_out", timeoutSec: prepared.timeoutSec },
4045
+ resources: emptyConsumed,
4046
+ };
4047
+ }
4048
+ if (terminal.status === "cancelled") {
4049
+ return {
4050
+ kind: "cancelled",
4051
+ cause: { kind: "turn_cancelled", reason: terminal.stopReason ?? "cancelled" },
4052
+ resources: emptyConsumed,
4053
+ };
4054
+ }
4055
+ // A duplex control-channel loss outranks a provider-reported failure or
4056
+ // completion: the loss reason explains why the provider terminal reads
4057
+ // the way it does, not the other way round. This also covers a
4058
+ // "completed" terminal whose channel died mid-turn. The message carries
4059
+ // only the typed loss reason, so no raw provider text rides the cause,
4060
+ // even when the provider terminal itself reports `failed`.
4061
+ if (channelLost) {
4062
+ return {
4063
+ kind: "failed",
4064
+ cause: {
4065
+ kind: "turn_failed",
4066
+ error: new Error(channelLostMessage ?? "The sandbox duplex control channel was lost."),
4067
+ },
4068
+ resources: emptyConsumed,
4069
+ };
4070
+ }
4071
+ if (terminal.status === "failed") {
4072
+ return {
4073
+ kind: "failed",
4074
+ cause: {
4075
+ kind: "turn_failed",
4076
+ error: terminal.error instanceof Error ? terminal.error : new Error(String(terminal.error)),
4077
+ },
4078
+ resources: emptyConsumed,
4079
+ };
4080
+ }
4081
+ return { kind: "finalized" };
4082
+ }
4083
+ const err = input.error;
4084
+ const timedOut = input.timedOut;
4085
+ // The failure phase comes from the sequence: a failure before the turn
4086
+ // started is `prepare_turn`; a failure after it is `turn`. The teardown is
4087
+ // the same; only the reported phase differs.
4088
+ const phase = input.phase;
4089
+ // Emit the failed phase timing for the phase the sequence reported.
4090
+ if (phase === "prepare_turn") {
4091
+ await emitPhase("prepare_turn", preparePhaseStart, "failed");
4092
+ }
4093
+ else {
4094
+ await emitPhase("turn", turnPhaseStart, "failed");
4095
+ }
4096
+ const messageOverride = timedOut
4097
+ ? formatAdapterExecutionTimeoutErrorMessage(prepared.timeoutResolution)
4098
+ : undefined;
4099
+ const preEmitMessage = messageOverride ?? (err instanceof Error ? err.message : String(err));
4100
+ // Record a direct close for the settlement `endSession` step: cancel the
4101
+ // running turn first (cancel-before-close), then close the runtime and drop
4102
+ // a matching warm entry. The settlement discards the staged runtime, stops
4103
+ // the bridges, releases the staging lease, and flushes the child stderr.
4104
+ runtimeSettlement = {
4105
+ mode: "direct",
4106
+ handle: sessionHandle,
4107
+ reason: timedOut ? "paperclip timeout cleanup" : "paperclip error cleanup",
4108
+ discardPersistentState: timedOut,
4109
+ dropWarmEntry: true,
4110
+ recordCloseError: true,
4111
+ cancelTurnReason: preEmitMessage,
4112
+ skipRemoteClose: false,
4113
+ };
4114
+ // Emit the failure best-effort. `turnFinalize` must not reject, so a
4115
+ // failing emission never propagates: the settlement owns the teardown, and
4116
+ // it runs only after this returns a completion. On an emission failure the
4117
+ // run records a degraded result from the pre-emit message.
4118
+ let emitted = null;
4119
+ try {
4120
+ emitted = await emitAcpxFailure({ ctx, prepared, err, phase, messageOverride });
4121
+ }
4122
+ catch {
4123
+ emitted = null;
4124
+ }
4125
+ const message = emitted?.message ?? preEmitMessage;
4126
+ capturedResult = {
4127
+ exitCode: 1,
4128
+ signal: timedOut ? "SIGTERM" : null,
4129
+ timedOut,
4130
+ errorMessage: message,
4131
+ errorCode: timedOut ? "acpx_timeout" : (emitted?.classified.errorCode ?? null),
4132
+ errorMeta: emitted?.classified.errorMeta,
4133
+ ...billingFields,
4134
+ ...referencedProjectStagingFailuresField,
4135
+ model: prepared.requestedModel || null,
4136
+ clearSession: clearSession || timedOut,
4137
+ resultJson: { phase },
4138
+ summary: message,
4139
+ };
4140
+ // Return a typed failed completion so the coordinator settles for a cause
4141
+ // that forbids the save. The reported phase lives on the recorded result.
4142
+ return {
4143
+ kind: "failed",
4144
+ cause: { kind: "turn_failed", error: err instanceof Error ? err : new Error(String(err)) },
4145
+ resources: emptyConsumed,
4146
+ };
4147
+ };
4148
+ try {
4149
+ return await runTurnSequence({
4150
+ signal: ctx.signal,
4151
+ timeoutMs: prepared.timeoutSec > 0 ? prepared.timeoutSec * 1000 : undefined,
4152
+ timeoutMessage: formatAdapterExecutionTimeoutErrorMessage(prepared.timeoutResolution),
4153
+ promptBuild: stepPromptBuild,
4154
+ preTurnUsage: stepPreTurnUsage,
4155
+ turnStart: stepTurnStart,
4156
+ eventRelay: stepEventRelay,
4157
+ turnFinalize: stepTurnFinalize,
4158
+ });
4159
+ }
4160
+ finally {
4161
+ // End the agent turn span exactly once, on every return and on a throw.
4162
+ // `runFailed` is `false` only on a completed, non-timed-out turn, so the
4163
+ // span status is correct for success, error, and timeout.
4164
+ turnSpan.end(runFailed);
4165
+ // Reset the current-run holder to the `task.run` token after the turn.
4166
+ // The run stays live here, so the holder is never `undefined`. A detached
4167
+ // exec after the turn parents to `task.run`.
4168
+ currentRunParentContext = runRootSpan.parentContext;
4169
+ }
4170
+ };
4171
+ // A live run-scoped credential marker. The host-lane reuse candidate carries
4172
+ // it, so the Amendment B credential gate blocks the host warm save: the
4173
+ // run-minted API key is never revoked, so it stays valid and forces a
4174
+ // close-and-relaunch. It is a non-secret marker; the settlement reads only its
4175
+ // presence, and no report or reuse payload carries it.
4176
+ const LIVE_RUN_SCOPED_API_KEY = "paperclip-run-scoped-api-key";
4177
+ // Record the final per-resource disposition report where a test can observe
4178
+ // it. The engine never reads it back.
4179
+ const recordDispositionReport = (report) => {
4180
+ deps.onSettlementDisposition?.(report);
4181
+ };
4182
+ // The settlement sequence is the one live cleanup owner for every settled path
4183
+ // (Phase 22). It claims the ledger once, makes the pure reuse decision, then
4184
+ // runs the ordered steps: `endSession`, `settleReuse`, `stopTransport`,
4185
+ // `syncBack`, and `releaseStagingLease` (in a finally). Each step reads the run
4186
+ // state through the shared closure locals and no-ops on an empty ledger slot.
4187
+ // The Phase 3 error policy governs every step. The coordinator alone owns the
4188
+ // startup rollback.
4189
+ const settlementSteps = {
4190
+ // Derive the reuse candidate. Only a clean turn (no cause) can save. The
4191
+ // host lane would save the live runtime, but the Amendment B credential gate
4192
+ // fails there: the run-minted API key is never revoked, so a live run-scoped
4193
+ // credential stays valid and the decision discards (close-and-relaunch). The
4194
+ // sandbox lane saves its staged files, which carry no credential.
4195
+ reuseCandidate: (slots, cause) => {
4196
+ const clean = cause === null;
4197
+ if (prepared.processSessionBridge) {
4198
+ // Sandbox lane: the staged files are the reuse. Save only after a clean
4199
+ // turn and only when the run holds a staged runtime.
4200
+ if (!clean || !slots.has("staged_runtime"))
4201
+ return null;
4202
+ return { kind: "sandbox", causePermitsSave: true, liveRunScopedCredentials: [] };
4203
+ }
4204
+ // Host lane: save a clean persistent warm-eligible turn, but carry the
4205
+ // live run-scoped credential so the gate blocks the transfer.
4206
+ if (!slots.has("acp_runtime"))
4207
+ return null;
4208
+ const permits = clean && prepared.mode === "persistent" && warmIdleMs > 0;
4209
+ if (!permits)
4210
+ return null;
4211
+ return { kind: "host", causePermitsSave: true, liveRunScopedCredentials: [LIVE_RUN_SCOPED_API_KEY] };
4212
+ },
4213
+ // Close every runtime the decision did not transfer, and drop the warm entry
4214
+ // on close.
4215
+ endSession: (slots, decision) => timedPhase("end_session", async () => {
4216
+ // The fence is the one idempotent owner of the abandoned promise's
4217
+ // eventual handle. Seal it first, before every other decision
4218
+ // below, including the empty-slot and save early returns: a late
4219
+ // handle that already arrived must never race this step.
4220
+ const lateHandle = handshakeFence.seal();
4221
+ if (!slots.has("acp_runtime"))
4222
+ return;
4223
+ if (decision.kind === "save" && decision.savedId === "acp_runtime")
4224
+ return;
4225
+ const baseSettlement = runtimeSettlement ?? {
4226
+ mode: "direct",
4227
+ handle: syntheticCloseHandle(),
4228
+ reason: "paperclip cleanup",
4229
+ discardPersistentState: false,
4230
+ dropWarmEntry: false,
4231
+ recordCloseError: true,
4232
+ cancelTurnReason: null,
4233
+ skipRemoteClose: false,
4234
+ };
4235
+ // A late handle that arrived before this seal replaces the synthetic
4236
+ // placeholder, so the one close call below uses the real identity
4237
+ // `ensureSession` eventually returned.
4238
+ const settlement = lateHandle
4239
+ ? { ...baseSettlement, handle: lateHandle }
4240
+ : baseSettlement;
4241
+ // Cancel a running turn before the close (the turn-error path).
4242
+ if (settlement.cancelTurnReason && activeTurn) {
4243
+ await activeTurn.cancel({ reason: settlement.cancelTurnReason }).catch(() => { });
4244
+ }
4245
+ const existing = warmHandles.get(prepared.sessionKey);
4246
+ // Re-read the duplex control-channel disposition here, at the
4247
+ // boundary that places the remote call. The settlement snapshot
4248
+ // above can predate a channel loss the bridge latches during the
4249
+ // awaited finalization work between the snapshot and this point, so
4250
+ // a stale `false` on the snapshot must not force a call onto a
4251
+ // channel that is dead by now. The read is non-mutating and only
4252
+ // adds a later-observed loss; it never clears the snapshot's `true`.
4253
+ const remoteChannelLost = settlement.skipRemoteClose || (prepared.paperclipBridge?.readRunDisposition?.().failed ?? false);
4254
+ // The control channel is already known lost, so no remote call can
4255
+ // reach the backend. Release the local bookkeeping only and place no
4256
+ // `runtime.close(...)` call — that call has no deadline of its own
4257
+ // and would block on the dead channel.
4258
+ if (remoteChannelLost) {
4259
+ if (warmHandleMatches(existing, runtime, settlement.handle) && existing) {
4260
+ clearWarmHandleTimer(existing);
4261
+ warmHandles.delete(prepared.sessionKey);
4262
+ flushChildStderr(existing.childStderrState);
4263
+ }
4264
+ return;
4265
+ }
4266
+ // The handshake guard abandoned this `ensureSession` call and no late
4267
+ // handle has arrived yet. Do not close the synthetic placeholder now:
4268
+ // the real handle can still resolve later, and closing it too would
4269
+ // close the same session twice. `handshakeFence`'s own late-arrival
4270
+ // hook (armed at the `ensureSession` call site) closes it once,
4271
+ // whenever it shows up. A guarded call only ever runs on a cold
4272
+ // start, so there is no matching warm entry here to drop.
4273
+ if (handshakeAbandoned && !lateHandle)
4274
+ return;
4275
+ if (settlement.mode === "warm_or_close" &&
4276
+ warmHandleMatches(existing, runtime, settlement.handle) &&
4277
+ existing) {
4278
+ // A matching warm entry closes through the warm store, which also
4279
+ // clears its idle timer and flushes its child stderr.
4280
+ await closeWarmHandle({
4281
+ handles: warmHandles,
4282
+ key: prepared.sessionKey,
4283
+ entry: existing,
4284
+ reason: settlement.reason,
4285
+ discardPersistentState: settlement.discardPersistentState,
4286
+ });
4287
+ return;
4288
+ }
4289
+ const onCloseError = settlement.recordCloseError || ctx.signal?.aborted
4290
+ ? (closeErr) => recordTeardownError("runtime-close", closeErr)
4291
+ : () => { };
4292
+ await runtime
4293
+ .close({
4294
+ handle: settlement.handle,
4295
+ reason: settlement.reason,
4296
+ discardPersistentState: settlement.discardPersistentState,
4297
+ })
4298
+ .then(() => { runtimeStopConfirmed = true; })
4299
+ .catch(onCloseError);
4300
+ if (settlement.dropWarmEntry && warmHandleMatches(existing, runtime, settlement.handle) && existing) {
4301
+ clearWarmHandleTimer(existing);
4302
+ warmHandles.delete(prepared.sessionKey);
4303
+ }
4304
+ }),
4305
+ // Perform the reuse decision. A save transfers the staged files to the site
4306
+ // store (which arms the idle policy); every other case discards the staged
4307
+ // runtime. The host warm save is closed in `endSession`.
4308
+ settleReuse: (slots, decision) => timedPhase("settle_reuse", async () => {
4309
+ if (!slots.has("staged_runtime"))
4310
+ return;
4311
+ if (decision.kind === "save" && decision.savedId === "staged_runtime") {
4312
+ saveStagedRuntimeAfterCleanTurn({ handles: stagedRuntimes, prepared, now: now() });
4313
+ return;
4314
+ }
4315
+ await discardStagedRuntime({ handles: stagedRuntimes, prepared });
4316
+ }),
4317
+ // Stop both bridges in one allSettled.
4318
+ stopTransport: () => timedPhase("stop_transport", async () => {
4319
+ await stopRunTransport(prepared);
4320
+ }),
4321
+ // The site sync-back (the managed-home copy-back). The run-parented span
4322
+ // runner wraps the restore in a `sandbox.syncBack` span. The runner also
4323
+ // publishes the run parent into the runtime-parent store while the restore
4324
+ // runs, so the host mints a `traceparent` for the provider spans, and the
4325
+ // per-task restore spans parent to `sandbox.syncBack`.
4326
+ syncBack: () => timedPhase("sync_back", async () => {
4327
+ await runRuntimeSpan("sandbox.syncBack", async () => {
4328
+ const restoreOutcome = await syncBackManagedHome(prepared);
4329
+ if (!restoreOutcome.ok) {
4330
+ workspaceRestoreFailureField = { workspaceRestoreFailure: restoreOutcome.code };
4331
+ }
4332
+ });
4333
+ }),
4334
+ // The staging lease releases as the run's final act, AFTER the coordinator
4335
+ // reproduces the result, in the run root `finally` below. This step stays a
4336
+ // no-op in the live engine: a same-session second run must stay blocked on
4337
+ // the lease until this run fully returns, not merely until the settlement
4338
+ // sync-back finishes, so the release cannot move earlier into settlement.
4339
+ releaseStagingLease: () => { },
4340
+ recordError: async (step, error) => {
4341
+ await recordTeardownError(`settlement-${step}`, error);
4342
+ },
4343
+ };
4344
+ // Drive the attempt through the coordinator routing table. The startup step
4345
+ // returns `ready` or `settle` (or throws on a build or partial-bridge
4346
+ // failure); the coordinator runs the turn on the ready path, settles, then
4347
+ // reproduces the recorded result.
4348
+ const plan = {
4349
+ ledger: runResourceLedger,
4350
+ startup,
4351
+ runTurn,
4352
+ // The coordinator owns the startup rollback: `buildRuntime` runs its own
4353
+ // partial-bring-up rollback and throws, so no ledger resource is left to
4354
+ // release here.
4355
+ rollbackStartup: () => { },
4356
+ recordDisposition: recordDispositionReport,
4357
+ // The settlement sequence is the one live cleanup owner. It claims the
4358
+ // ledger, releases every settled resource, and returns the final
4359
+ // disposition report. The child stderr flushes after the sync-back on every
4360
+ // settled exit path (it stays null before the run reads the warm entry).
4361
+ settle: async (reason) => {
4362
+ const cause = reason.kind === "pre_turn"
4363
+ ? reason.cause
4364
+ : reason.completion.kind === "finalized"
4365
+ ? null
4366
+ : reason.completion.cause;
4367
+ const report = await settleAcpRun(runResourceLedger, cause, settlementSteps);
4368
+ recordDispositionReport(report);
4369
+ if (childStderrState)
4370
+ flushChildStderr(childStderrState);
4371
+ },
4372
+ reproduceResult: async () => {
4373
+ if (!capturedResult) {
4374
+ throw new Error("run coordinator reproduced a result before the run recorded one");
4375
+ }
4376
+ clearTimeout(stopTimer);
4377
+ let providerExited = false;
4378
+ const providerPid = processIdentitySink?.latest?.pid;
4379
+ if (ctx.signal?.aborted && providerPid && !prepared.processSessionBridge) {
4380
+ for (let attempt = 0; attempt < 40; attempt += 1) {
4381
+ providerExited = capturedProcessExited(processIdentitySink.localProcess);
4382
+ if (providerExited || !forcedStop)
4383
+ break;
4384
+ await new Promise((resolve) => setTimeout(resolve, 25));
4385
+ }
4386
+ }
4387
+ // A forced process exit can reject ACP close after the OS has already
4388
+ // confirmed termination. Acknowledge Stop, but never grant replay.
4389
+ if (ctx.signal?.aborted && providerExited && (runtimeStopConfirmed || forcedStop)) {
4390
+ capturedResult = {
4391
+ ...capturedResult,
4392
+ ...(safeInterruptedSession && !forcedStop && !("workspaceRestoreFailure" in workspaceRestoreFailureField)
4393
+ ? { executionRecovery: { kind: "interrupted", providerStopped: true, sessionPreserved: true, actionOutcomes: "settled" },
4394
+ sessionParams: { ...capturedResult.sessionParams, interruptedCheckpoint: true } }
4395
+ : {}),
4396
+ resultJson: { ...capturedResult.resultJson, executionCancellation: {
4397
+ state: "acknowledged", acknowledgedAt: new Date().toISOString(), forced: forcedStop,
4398
+ } },
4399
+ };
4400
+ }
4401
+ // The sync-back settlement step runs before this reproduces the result
4402
+ // (settlement precedes reproduction), so a failed workspace restore is
4403
+ // already recorded by the time we get here. Merge it into `resultJson`
4404
+ // only on a failure — a clean restore adds no new key.
4405
+ if (!("workspaceRestoreFailure" in workspaceRestoreFailureField)) {
4406
+ return capturedResult;
4407
+ }
4408
+ return {
4409
+ ...capturedResult,
4410
+ resultJson: {
4411
+ ...(capturedResult.resultJson ?? {}),
4412
+ ...workspaceRestoreFailureField,
4413
+ },
4414
+ };
4415
+ },
4416
+ };
4417
+ return await runAttempt(plan);
4418
+ }
4419
+ finally {
4420
+ clearTimeout(stopTimer);
4421
+ removeStopListener?.();
4422
+ removeLossListener?.();
4423
+ clearTimeout(lossDeadlineTimer);
4424
+ // End the run root span exactly once, on every return and on a throw.
4425
+ runRootSpan.end(runFailed);
4426
+ // Release the per-session staging lease as the run's final act, AFTER the
4427
+ // coordinator settled every other resource and reproduced the result. It runs
4428
+ // last, in this `finally`, so a same-session second run stays blocked on the
4429
+ // lease until this run fully returns (not merely until the settlement
4430
+ // sync-back finishes) and an earlier teardown fault never strands the lease.
4431
+ // It stays null on a build failure (where `buildRuntime` released its own
4432
+ // partial lease) and on the host lane (no staging). The settlement stopped the
4433
+ // bridges and ran the sync-back before this point, so the ordering is
4434
+ // bridge-stop → sync-back → lease release.
4435
+ const leaseRelease = releaseStagingLease;
4436
+ if (leaseRelease) {
4437
+ const leaseStart = now();
4438
+ leaseRelease();
4439
+ // Fire-and-forget the phase timing: the release is the run's final act, so
4440
+ // the run must not await telemetry here. `emitRunPhaseTiming` swallows a
4441
+ // sink failure, so this never rejects.
4442
+ void emitRunPhaseTiming(ctx, "release_staging_lease", now() - leaseStart, "ok");
4443
+ }
4444
+ }
4445
+ };
4446
+ }
4447
+ export const execute = createAcpxEngineExecutor();
4448
+ //# sourceMappingURL=execute.js.map