@blxzer/pactile 0.6.0-beta.2 → 0.6.0-beta.5

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 (704) hide show
  1. package/README.md +93 -84
  2. package/README.zh-CN.md +90 -81
  3. package/dist/bin/compat-warning.d.ts +2 -0
  4. package/dist/bin/compat-warning.d.ts.map +1 -0
  5. package/dist/bin/compat-warning.js +8 -0
  6. package/dist/bin/compat-warning.js.map +1 -0
  7. package/dist/bin/cstl.d.ts +3 -0
  8. package/dist/bin/cstl.d.ts.map +1 -0
  9. package/{bin → dist/bin}/cstl.js +2 -3
  10. package/dist/bin/cstl.js.map +1 -0
  11. package/dist/bin/pactile.d.ts +3 -0
  12. package/dist/bin/pactile.d.ts.map +1 -0
  13. package/dist/bin/pactile.js +4 -0
  14. package/dist/bin/pactile.js.map +1 -0
  15. package/dist/cli/index.d.ts.map +1 -1
  16. package/dist/cli/index.js +77 -17
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/commands/capability.d.ts +3 -0
  19. package/dist/commands/capability.d.ts.map +1 -0
  20. package/dist/commands/capability.js +129 -0
  21. package/dist/commands/capability.js.map +1 -0
  22. package/dist/commands/codex.d.ts.map +1 -1
  23. package/dist/commands/codex.js +54 -6
  24. package/dist/commands/codex.js.map +1 -1
  25. package/dist/commands/context.d.ts +2 -0
  26. package/dist/commands/context.d.ts.map +1 -1
  27. package/dist/commands/context.js +367 -41
  28. package/dist/commands/context.js.map +1 -1
  29. package/dist/commands/detach.d.ts.map +1 -1
  30. package/dist/commands/detach.js +5 -4
  31. package/dist/commands/detach.js.map +1 -1
  32. package/dist/commands/init.d.ts.map +1 -1
  33. package/dist/commands/init.js +615 -259
  34. package/dist/commands/init.js.map +1 -1
  35. package/dist/commands/legacy-task.d.ts +3 -0
  36. package/dist/commands/legacy-task.d.ts.map +1 -0
  37. package/dist/commands/legacy-task.js +295 -0
  38. package/dist/commands/legacy-task.js.map +1 -0
  39. package/dist/commands/pi.d.ts +3 -2
  40. package/dist/commands/pi.d.ts.map +1 -1
  41. package/dist/commands/pi.js +333 -10
  42. package/dist/commands/pi.js.map +1 -1
  43. package/dist/commands/session.d.ts.map +1 -1
  44. package/dist/commands/session.js +148 -24
  45. package/dist/commands/session.js.map +1 -1
  46. package/dist/commands/task-schedule.d.ts +17 -0
  47. package/dist/commands/task-schedule.d.ts.map +1 -0
  48. package/dist/commands/task-schedule.js +395 -0
  49. package/dist/commands/task-schedule.js.map +1 -0
  50. package/dist/commands/task-verify-plan.d.ts +3 -0
  51. package/dist/commands/task-verify-plan.d.ts.map +1 -0
  52. package/dist/commands/task-verify-plan.js +503 -0
  53. package/dist/commands/task-verify-plan.js.map +1 -0
  54. package/dist/commands/task-worktree-close.d.ts +4 -0
  55. package/dist/commands/task-worktree-close.d.ts.map +1 -0
  56. package/dist/commands/task-worktree-close.js +81 -0
  57. package/dist/commands/task-worktree-close.js.map +1 -0
  58. package/dist/commands/task.d.ts +3 -0
  59. package/dist/commands/task.d.ts.map +1 -1
  60. package/dist/commands/task.js +1373 -184
  61. package/dist/commands/task.js.map +1 -1
  62. package/dist/commands/tile-selection.d.ts +3 -0
  63. package/dist/commands/tile-selection.d.ts.map +1 -0
  64. package/dist/commands/tile-selection.js +174 -0
  65. package/dist/commands/tile-selection.js.map +1 -0
  66. package/dist/commands/update.d.ts +1 -1
  67. package/dist/commands/update.d.ts.map +1 -1
  68. package/dist/commands/update.js +80 -55
  69. package/dist/commands/update.js.map +1 -1
  70. package/dist/commands/worktree.d.ts +4 -0
  71. package/dist/commands/worktree.d.ts.map +1 -0
  72. package/dist/commands/worktree.js +198 -0
  73. package/dist/commands/worktree.js.map +1 -0
  74. package/dist/configurators/shared.js +1 -1
  75. package/dist/configurators/shared.js.map +1 -1
  76. package/dist/core/compat/environment.d.ts +0 -1
  77. package/dist/core/compat/environment.d.ts.map +1 -1
  78. package/dist/core/compat/environment.js +1 -9
  79. package/dist/core/compat/environment.js.map +1 -1
  80. package/dist/core/pactile/bounded-capabilities.d.ts +127 -0
  81. package/dist/core/pactile/bounded-capabilities.d.ts.map +1 -0
  82. package/dist/core/pactile/bounded-capabilities.js +188 -0
  83. package/dist/core/pactile/bounded-capabilities.js.map +1 -0
  84. package/dist/core/pactile/index.d.ts +1 -0
  85. package/dist/core/pactile/index.d.ts.map +1 -1
  86. package/dist/core/pactile/index.js +1 -0
  87. package/dist/core/pactile/index.js.map +1 -1
  88. package/dist/core/task/index.d.ts +4 -2
  89. package/dist/core/task/index.d.ts.map +1 -1
  90. package/dist/core/task/index.js +2 -1
  91. package/dist/core/task/index.js.map +1 -1
  92. package/dist/core/task/kernel-cli.d.ts +14 -1
  93. package/dist/core/task/kernel-cli.d.ts.map +1 -1
  94. package/dist/core/task/kernel-cli.js +128 -0
  95. package/dist/core/task/kernel-cli.js.map +1 -1
  96. package/dist/core/task/kernel-contract.d.ts +6 -3
  97. package/dist/core/task/kernel-contract.d.ts.map +1 -1
  98. package/dist/core/task/kernel-contract.js +71 -23
  99. package/dist/core/task/kernel-contract.js.map +1 -1
  100. package/dist/core/task/kernel-store.d.ts +6 -0
  101. package/dist/core/task/kernel-store.d.ts.map +1 -1
  102. package/dist/core/task/kernel-store.js +54 -2
  103. package/dist/core/task/kernel-store.js.map +1 -1
  104. package/dist/core/task/legacy-task-migration-reader.d.ts +137 -0
  105. package/dist/core/task/legacy-task-migration-reader.d.ts.map +1 -0
  106. package/dist/core/task/legacy-task-migration-reader.js +512 -0
  107. package/dist/core/task/legacy-task-migration-reader.js.map +1 -0
  108. package/dist/core/task/legacy-task-migration.d.ts +105 -0
  109. package/dist/core/task/legacy-task-migration.d.ts.map +1 -0
  110. package/dist/core/task/legacy-task-migration.js +562 -0
  111. package/dist/core/task/legacy-task-migration.js.map +1 -0
  112. package/dist/core/task/legacy-task-overlay-durable-io.d.ts +18 -0
  113. package/dist/core/task/legacy-task-overlay-durable-io.d.ts.map +1 -0
  114. package/dist/core/task/legacy-task-overlay-durable-io.js +66 -0
  115. package/dist/core/task/legacy-task-overlay-durable-io.js.map +1 -0
  116. package/dist/core/task/legacy-task-reconciliation-reader.d.ts +26 -0
  117. package/dist/core/task/legacy-task-reconciliation-reader.d.ts.map +1 -0
  118. package/dist/core/task/legacy-task-reconciliation-reader.js +324 -0
  119. package/dist/core/task/legacy-task-reconciliation-reader.js.map +1 -0
  120. package/dist/core/task/legacy-task-v2-import.d.ts +67 -0
  121. package/dist/core/task/legacy-task-v2-import.d.ts.map +1 -0
  122. package/dist/core/task/legacy-task-v2-import.js +1046 -0
  123. package/dist/core/task/legacy-task-v2-import.js.map +1 -0
  124. package/dist/core/task/project-file-observer.d.ts +46 -0
  125. package/dist/core/task/project-file-observer.d.ts.map +1 -0
  126. package/dist/core/task/project-file-observer.js +618 -0
  127. package/dist/core/task/project-file-observer.js.map +1 -0
  128. package/dist/core/task/start-authority.d.ts +14 -0
  129. package/dist/core/task/start-authority.d.ts.map +1 -0
  130. package/dist/core/task/start-authority.js +146 -0
  131. package/dist/core/task/start-authority.js.map +1 -0
  132. package/dist/core/task/strategy.d.ts +24 -0
  133. package/dist/core/task/strategy.d.ts.map +1 -0
  134. package/dist/core/task/strategy.js +136 -0
  135. package/dist/core/task/strategy.js.map +1 -0
  136. package/dist/core/task/task-candidate-observer.d.ts +111 -0
  137. package/dist/core/task/task-candidate-observer.d.ts.map +1 -0
  138. package/dist/core/task/task-candidate-observer.js +762 -0
  139. package/dist/core/task/task-candidate-observer.js.map +1 -0
  140. package/dist/core/task/task-kernel-close.d.ts +5 -0
  141. package/dist/core/task/task-kernel-close.d.ts.map +1 -0
  142. package/dist/core/task/task-kernel-close.js +367 -0
  143. package/dist/core/task/task-kernel-close.js.map +1 -0
  144. package/dist/core/task/task-kernel-dependencies.d.ts +3 -0
  145. package/dist/core/task/task-kernel-dependencies.d.ts.map +1 -0
  146. package/dist/core/task/task-kernel-dependencies.js +24 -0
  147. package/dist/core/task/task-kernel-dependencies.js.map +1 -0
  148. package/dist/core/task/task-kernel-host.d.ts +5 -0
  149. package/dist/core/task/task-kernel-host.d.ts.map +1 -0
  150. package/dist/core/task/task-kernel-host.js +136 -0
  151. package/dist/core/task/task-kernel-host.js.map +1 -0
  152. package/dist/core/task/task-kernel-paths.d.ts +19 -0
  153. package/dist/core/task/task-kernel-paths.d.ts.map +1 -0
  154. package/dist/core/task/task-kernel-paths.js +237 -0
  155. package/dist/core/task/task-kernel-paths.js.map +1 -0
  156. package/dist/core/task/task-kernel-reviews.d.ts +3 -0
  157. package/dist/core/task/task-kernel-reviews.d.ts.map +1 -0
  158. package/dist/core/task/task-kernel-reviews.js +100 -0
  159. package/dist/core/task/task-kernel-reviews.js.map +1 -0
  160. package/dist/core/task/task-kernel-runs.d.ts +5 -0
  161. package/dist/core/task/task-kernel-runs.d.ts.map +1 -0
  162. package/dist/core/task/task-kernel-runs.js +196 -0
  163. package/dist/core/task/task-kernel-runs.js.map +1 -0
  164. package/dist/core/task/task-kernel-schema.d.ts +29 -0
  165. package/dist/core/task/task-kernel-schema.d.ts.map +1 -0
  166. package/dist/core/task/task-kernel-schema.js +1169 -0
  167. package/dist/core/task/task-kernel-schema.js.map +1 -0
  168. package/dist/core/task/task-kernel-store-v2.d.ts +33 -0
  169. package/dist/core/task/task-kernel-store-v2.d.ts.map +1 -0
  170. package/dist/core/task/task-kernel-store-v2.js +574 -0
  171. package/dist/core/task/task-kernel-store-v2.js.map +1 -0
  172. package/dist/core/task/task-kernel-types.d.ts +585 -0
  173. package/dist/core/task/task-kernel-types.d.ts.map +1 -0
  174. package/dist/core/task/task-kernel-types.js +31 -0
  175. package/dist/core/task/task-kernel-types.js.map +1 -0
  176. package/dist/core/task/task-kernel-workspaces.d.ts +10 -0
  177. package/dist/core/task/task-kernel-workspaces.d.ts.map +1 -0
  178. package/dist/core/task/task-kernel-workspaces.js +250 -0
  179. package/dist/core/task/task-kernel-workspaces.js.map +1 -0
  180. package/dist/core/task/task-kernel-write-set.d.ts +3 -0
  181. package/dist/core/task/task-kernel-write-set.d.ts.map +1 -0
  182. package/dist/core/task/task-kernel-write-set.js +12 -0
  183. package/dist/core/task/task-kernel-write-set.js.map +1 -0
  184. package/dist/core/task/task-kernel.d.ts +19 -0
  185. package/dist/core/task/task-kernel.d.ts.map +1 -0
  186. package/dist/core/task/task-kernel.js +17 -0
  187. package/dist/core/task/task-kernel.js.map +1 -0
  188. package/dist/core/task/task-pull-request-observer.d.ts +25 -0
  189. package/dist/core/task/task-pull-request-observer.d.ts.map +1 -0
  190. package/dist/core/task/task-pull-request-observer.js +187 -0
  191. package/dist/core/task/task-pull-request-observer.js.map +1 -0
  192. package/dist/core/task/task-review-evidence.d.ts +27 -0
  193. package/dist/core/task/task-review-evidence.d.ts.map +1 -0
  194. package/dist/core/task/task-review-evidence.js +383 -0
  195. package/dist/core/task/task-review-evidence.js.map +1 -0
  196. package/dist/migrations/index.d.ts +1 -1
  197. package/dist/migrations/index.d.ts.map +1 -1
  198. package/dist/migrations/index.js +5 -3
  199. package/dist/migrations/index.js.map +1 -1
  200. package/dist/migrations/manifests/0.6.0-beta.3.json +9 -0
  201. package/dist/migrations/manifests/0.6.0-beta.4.json +9 -0
  202. package/dist/migrations/manifests/0.6.0-beta.5.json +9 -0
  203. package/dist/pactile/artifacts/documents.d.ts +72 -0
  204. package/dist/pactile/artifacts/documents.d.ts.map +1 -0
  205. package/dist/pactile/artifacts/documents.js +406 -0
  206. package/dist/pactile/artifacts/documents.js.map +1 -0
  207. package/dist/pactile/artifacts/index.d.ts +6 -0
  208. package/dist/pactile/artifacts/index.d.ts.map +1 -0
  209. package/dist/pactile/artifacts/index.js +6 -0
  210. package/dist/pactile/artifacts/index.js.map +1 -0
  211. package/dist/pactile/artifacts/projection.d.ts +50 -0
  212. package/dist/pactile/artifacts/projection.d.ts.map +1 -0
  213. package/dist/pactile/artifacts/projection.js +139 -0
  214. package/dist/pactile/artifacts/projection.js.map +1 -0
  215. package/dist/pactile/artifacts/reader.d.ts +20 -0
  216. package/dist/pactile/artifacts/reader.d.ts.map +1 -0
  217. package/dist/pactile/artifacts/reader.js +559 -0
  218. package/dist/pactile/artifacts/reader.js.map +1 -0
  219. package/dist/pactile/artifacts/schema.d.ts +4 -0
  220. package/dist/pactile/artifacts/schema.d.ts.map +1 -0
  221. package/dist/pactile/artifacts/schema.js +217 -0
  222. package/dist/pactile/artifacts/schema.js.map +1 -0
  223. package/dist/pactile/artifacts/types.d.ts +66 -0
  224. package/dist/pactile/artifacts/types.d.ts.map +1 -0
  225. package/dist/pactile/artifacts/types.js +45 -0
  226. package/dist/pactile/artifacts/types.js.map +1 -0
  227. package/dist/pactile/capabilities/claim.d.ts +3 -0
  228. package/dist/pactile/capabilities/claim.d.ts.map +1 -0
  229. package/dist/pactile/capabilities/claim.js +97 -0
  230. package/dist/pactile/capabilities/claim.js.map +1 -0
  231. package/dist/pactile/capabilities/command.d.ts +6 -0
  232. package/dist/pactile/capabilities/command.d.ts.map +1 -0
  233. package/dist/pactile/capabilities/command.js +253 -0
  234. package/dist/pactile/capabilities/command.js.map +1 -0
  235. package/dist/pactile/capabilities/discover.d.ts +6 -0
  236. package/dist/pactile/capabilities/discover.d.ts.map +1 -0
  237. package/dist/pactile/capabilities/discover.js +48 -0
  238. package/dist/pactile/capabilities/discover.js.map +1 -0
  239. package/dist/pactile/capabilities/node.d.ts +15 -0
  240. package/dist/pactile/capabilities/node.d.ts.map +1 -0
  241. package/dist/pactile/capabilities/node.js +292 -0
  242. package/dist/pactile/capabilities/node.js.map +1 -0
  243. package/dist/pactile/capabilities/read.d.ts +6 -0
  244. package/dist/pactile/capabilities/read.d.ts.map +1 -0
  245. package/dist/pactile/capabilities/read.js +100 -0
  246. package/dist/pactile/capabilities/read.js.map +1 -0
  247. package/dist/pactile/capabilities/receipts.d.ts +17 -0
  248. package/dist/pactile/capabilities/receipts.d.ts.map +1 -0
  249. package/dist/pactile/capabilities/receipts.js +90 -0
  250. package/dist/pactile/capabilities/receipts.js.map +1 -0
  251. package/dist/pactile/capabilities/search.d.ts +6 -0
  252. package/dist/pactile/capabilities/search.d.ts.map +1 -0
  253. package/dist/pactile/capabilities/search.js +181 -0
  254. package/dist/pactile/capabilities/search.js.map +1 -0
  255. package/dist/pactile/capabilities/shared.d.ts +29 -0
  256. package/dist/pactile/capabilities/shared.d.ts.map +1 -0
  257. package/dist/pactile/capabilities/shared.js +134 -0
  258. package/dist/pactile/capabilities/shared.js.map +1 -0
  259. package/dist/pactile/capabilities/workspace.d.ts +19 -0
  260. package/dist/pactile/capabilities/workspace.d.ts.map +1 -0
  261. package/dist/pactile/capabilities/workspace.js +273 -0
  262. package/dist/pactile/capabilities/workspace.js.map +1 -0
  263. package/dist/pactile/codex/bridge.d.ts +105 -2
  264. package/dist/pactile/codex/bridge.d.ts.map +1 -1
  265. package/dist/pactile/codex/bridge.js +1266 -92
  266. package/dist/pactile/codex/bridge.js.map +1 -1
  267. package/dist/pactile/codex/dispatch-stop-proof.d.ts +43 -0
  268. package/dist/pactile/codex/dispatch-stop-proof.d.ts.map +1 -0
  269. package/dist/pactile/codex/dispatch-stop-proof.js +177 -0
  270. package/dist/pactile/codex/dispatch-stop-proof.js.map +1 -0
  271. package/dist/pactile/codex/host-stop.d.ts +47 -0
  272. package/dist/pactile/codex/host-stop.d.ts.map +1 -0
  273. package/dist/pactile/codex/host-stop.js +344 -0
  274. package/dist/pactile/codex/host-stop.js.map +1 -0
  275. package/dist/pactile/coordination/index.d.ts +4 -0
  276. package/dist/pactile/coordination/index.d.ts.map +1 -0
  277. package/dist/pactile/coordination/index.js +4 -0
  278. package/dist/pactile/coordination/index.js.map +1 -0
  279. package/dist/pactile/coordination/journal.d.ts +10 -0
  280. package/dist/pactile/coordination/journal.d.ts.map +1 -0
  281. package/dist/pactile/coordination/journal.js +255 -0
  282. package/dist/pactile/coordination/journal.js.map +1 -0
  283. package/dist/pactile/coordination/kernel-resume.d.ts +8 -0
  284. package/dist/pactile/coordination/kernel-resume.d.ts.map +1 -0
  285. package/dist/pactile/coordination/kernel-resume.js +87 -0
  286. package/dist/pactile/coordination/kernel-resume.js.map +1 -0
  287. package/dist/pactile/coordination/model.d.ts +553 -0
  288. package/dist/pactile/coordination/model.d.ts.map +1 -0
  289. package/dist/pactile/coordination/model.js +239 -0
  290. package/dist/pactile/coordination/model.js.map +1 -0
  291. package/dist/pactile/coordination/state.d.ts +46 -0
  292. package/dist/pactile/coordination/state.d.ts.map +1 -0
  293. package/dist/pactile/coordination/state.js +301 -0
  294. package/dist/pactile/coordination/state.js.map +1 -0
  295. package/dist/pactile/coordination/store.d.ts +94 -0
  296. package/dist/pactile/coordination/store.d.ts.map +1 -0
  297. package/dist/pactile/coordination/store.js +396 -0
  298. package/dist/pactile/coordination/store.js.map +1 -0
  299. package/dist/pactile/index.d.ts +7 -1
  300. package/dist/pactile/index.d.ts.map +1 -1
  301. package/dist/pactile/index.js +7 -1
  302. package/dist/pactile/index.js.map +1 -1
  303. package/dist/pactile/jev/contracts.d.ts +104 -0
  304. package/dist/pactile/jev/contracts.d.ts.map +1 -0
  305. package/dist/pactile/jev/contracts.js +53 -0
  306. package/dist/pactile/jev/contracts.js.map +1 -0
  307. package/dist/pactile/jev/decision.d.ts +58 -0
  308. package/dist/pactile/jev/decision.d.ts.map +1 -0
  309. package/dist/pactile/jev/decision.js +149 -0
  310. package/dist/pactile/jev/decision.js.map +1 -0
  311. package/dist/pactile/jev/http.d.ts +25 -0
  312. package/dist/pactile/jev/http.d.ts.map +1 -0
  313. package/dist/pactile/jev/http.js +234 -0
  314. package/dist/pactile/jev/http.js.map +1 -0
  315. package/dist/pactile/jev/index.d.ts +8 -0
  316. package/dist/pactile/jev/index.d.ts.map +1 -0
  317. package/dist/pactile/jev/index.js +4 -0
  318. package/dist/pactile/jev/index.js.map +1 -0
  319. package/dist/pactile/jev/outbound.d.ts +15 -0
  320. package/dist/pactile/jev/outbound.d.ts.map +1 -0
  321. package/dist/pactile/jev/outbound.js +264 -0
  322. package/dist/pactile/jev/outbound.js.map +1 -0
  323. package/dist/pactile/jev/project-policy.d.ts +10 -0
  324. package/dist/pactile/jev/project-policy.d.ts.map +1 -0
  325. package/dist/pactile/jev/project-policy.js +53 -0
  326. package/dist/pactile/jev/project-policy.js.map +1 -0
  327. package/dist/pactile/jev/response.d.ts +17 -0
  328. package/dist/pactile/jev/response.d.ts.map +1 -0
  329. package/dist/pactile/jev/response.js +280 -0
  330. package/dist/pactile/jev/response.js.map +1 -0
  331. package/dist/pactile/jev/scheduler-advice.d.ts +92 -0
  332. package/dist/pactile/jev/scheduler-advice.d.ts.map +1 -0
  333. package/dist/pactile/jev/scheduler-advice.js +323 -0
  334. package/dist/pactile/jev/scheduler-advice.js.map +1 -0
  335. package/dist/pactile/jev/transport.d.ts +6 -0
  336. package/dist/pactile/jev/transport.d.ts.map +1 -0
  337. package/dist/pactile/jev/transport.js +87 -0
  338. package/dist/pactile/jev/transport.js.map +1 -0
  339. package/dist/pactile/lifecycle/deferred-live.d.ts +17 -0
  340. package/dist/pactile/lifecycle/deferred-live.d.ts.map +1 -0
  341. package/dist/pactile/lifecycle/deferred-live.js +173 -0
  342. package/dist/pactile/lifecycle/deferred-live.js.map +1 -0
  343. package/dist/pactile/lifecycle/legacy-import.d.ts +1 -1
  344. package/dist/pactile/lifecycle/legacy-import.js +3 -3
  345. package/dist/pactile/lifecycle/legacy-import.js.map +1 -1
  346. package/dist/pactile/lifecycle/project-files.d.ts.map +1 -1
  347. package/dist/pactile/lifecycle/project-files.js +1 -0
  348. package/dist/pactile/lifecycle/project-files.js.map +1 -1
  349. package/dist/pactile/migration/legacy-task-batch-generation.d.ts +23 -0
  350. package/dist/pactile/migration/legacy-task-batch-generation.d.ts.map +1 -0
  351. package/dist/pactile/migration/legacy-task-batch-generation.js +102 -0
  352. package/dist/pactile/migration/legacy-task-batch-generation.js.map +1 -0
  353. package/dist/pactile/migration/legacy-task-batch-io.d.ts +9 -0
  354. package/dist/pactile/migration/legacy-task-batch-io.d.ts.map +1 -0
  355. package/dist/pactile/migration/legacy-task-batch-io.js +208 -0
  356. package/dist/pactile/migration/legacy-task-batch-io.js.map +1 -0
  357. package/dist/pactile/migration/legacy-task-batch-journal.d.ts +6 -0
  358. package/dist/pactile/migration/legacy-task-batch-journal.d.ts.map +1 -0
  359. package/dist/pactile/migration/legacy-task-batch-journal.js +121 -0
  360. package/dist/pactile/migration/legacy-task-batch-journal.js.map +1 -0
  361. package/dist/pactile/migration/legacy-task-batch-orchestrator.d.ts +18 -0
  362. package/dist/pactile/migration/legacy-task-batch-orchestrator.d.ts.map +1 -0
  363. package/dist/pactile/migration/legacy-task-batch-orchestrator.js +634 -0
  364. package/dist/pactile/migration/legacy-task-batch-orchestrator.js.map +1 -0
  365. package/dist/pactile/migration/legacy-task-batch-source.d.ts +8 -0
  366. package/dist/pactile/migration/legacy-task-batch-source.d.ts.map +1 -0
  367. package/dist/pactile/migration/legacy-task-batch-source.js +158 -0
  368. package/dist/pactile/migration/legacy-task-batch-source.js.map +1 -0
  369. package/dist/pactile/migration/legacy-task-batch-types.d.ts +127 -0
  370. package/dist/pactile/migration/legacy-task-batch-types.d.ts.map +1 -0
  371. package/dist/pactile/migration/legacy-task-batch-types.js +127 -0
  372. package/dist/pactile/migration/legacy-task-batch-types.js.map +1 -0
  373. package/dist/pactile/migration/legacy-task-batch.d.ts +3 -0
  374. package/dist/pactile/migration/legacy-task-batch.d.ts.map +1 -0
  375. package/dist/pactile/migration/legacy-task-batch.js +2 -0
  376. package/dist/pactile/migration/legacy-task-batch.js.map +1 -0
  377. package/dist/pactile/migration/legacy-task-held-source-reader.d.ts +27 -0
  378. package/dist/pactile/migration/legacy-task-held-source-reader.d.ts.map +1 -0
  379. package/dist/pactile/migration/legacy-task-held-source-reader.js +261 -0
  380. package/dist/pactile/migration/legacy-task-held-source-reader.js.map +1 -0
  381. package/dist/pactile/migration/legacy-task-reconciliation.d.ts +46 -0
  382. package/dist/pactile/migration/legacy-task-reconciliation.d.ts.map +1 -0
  383. package/dist/pactile/migration/legacy-task-reconciliation.js +589 -0
  384. package/dist/pactile/migration/legacy-task-reconciliation.js.map +1 -0
  385. package/dist/pactile/migration/legacy-task-update.d.ts +19 -0
  386. package/dist/pactile/migration/legacy-task-update.d.ts.map +1 -0
  387. package/dist/pactile/migration/legacy-task-update.js +164 -0
  388. package/dist/pactile/migration/legacy-task-update.js.map +1 -0
  389. package/dist/pactile/parallel/batch.d.ts +21 -2
  390. package/dist/pactile/parallel/batch.d.ts.map +1 -1
  391. package/dist/pactile/parallel/batch.js +271 -50
  392. package/dist/pactile/parallel/batch.js.map +1 -1
  393. package/dist/pactile/parallel/policy.d.ts +1 -1
  394. package/dist/pactile/parallel/policy.d.ts.map +1 -1
  395. package/dist/pactile/parallel/policy.js +130 -102
  396. package/dist/pactile/parallel/policy.js.map +1 -1
  397. package/dist/pactile/pi/bridge.d.ts +139 -2
  398. package/dist/pactile/pi/bridge.d.ts.map +1 -1
  399. package/dist/pactile/pi/bridge.js +1317 -106
  400. package/dist/pactile/pi/bridge.js.map +1 -1
  401. package/dist/pactile/pi/rpc.d.ts +23 -4
  402. package/dist/pactile/pi/rpc.d.ts.map +1 -1
  403. package/dist/pactile/pi/rpc.js +243 -42
  404. package/dist/pactile/pi/rpc.js.map +1 -1
  405. package/dist/pactile/pi/v2-dispatch.d.ts +113 -0
  406. package/dist/pactile/pi/v2-dispatch.d.ts.map +1 -0
  407. package/dist/pactile/pi/v2-dispatch.js +919 -0
  408. package/dist/pactile/pi/v2-dispatch.js.map +1 -0
  409. package/dist/pactile/projection/planner.js +1 -1
  410. package/dist/pactile/projection/planner.js.map +1 -1
  411. package/dist/pactile/registry.d.ts +63 -1
  412. package/dist/pactile/registry.d.ts.map +1 -1
  413. package/dist/pactile/registry.js +633 -4
  414. package/dist/pactile/registry.js.map +1 -1
  415. package/dist/pactile/retrieval/index.d.ts +1 -0
  416. package/dist/pactile/retrieval/index.d.ts.map +1 -1
  417. package/dist/pactile/retrieval/index.js +1 -0
  418. package/dist/pactile/retrieval/index.js.map +1 -1
  419. package/dist/pactile/retrieval/jev-planning.d.ts +28 -0
  420. package/dist/pactile/retrieval/jev-planning.d.ts.map +1 -0
  421. package/dist/pactile/retrieval/jev-planning.js +96 -0
  422. package/dist/pactile/retrieval/jev-planning.js.map +1 -0
  423. package/dist/pactile/review/contract.d.ts +232 -0
  424. package/dist/pactile/review/contract.d.ts.map +1 -0
  425. package/dist/pactile/review/contract.js +695 -0
  426. package/dist/pactile/review/contract.js.map +1 -0
  427. package/dist/pactile/review/escalation-artifacts.d.ts +41 -0
  428. package/dist/pactile/review/escalation-artifacts.d.ts.map +1 -0
  429. package/dist/pactile/review/escalation-artifacts.js +415 -0
  430. package/dist/pactile/review/escalation-artifacts.js.map +1 -0
  431. package/dist/pactile/review/escalation-contract.d.ts +115 -0
  432. package/dist/pactile/review/escalation-contract.d.ts.map +1 -0
  433. package/dist/pactile/review/escalation-contract.js +151 -0
  434. package/dist/pactile/review/escalation-contract.js.map +1 -0
  435. package/dist/pactile/review/escalation-finalize.d.ts +14 -0
  436. package/dist/pactile/review/escalation-finalize.d.ts.map +1 -0
  437. package/dist/pactile/review/escalation-finalize.js +319 -0
  438. package/dist/pactile/review/escalation-finalize.js.map +1 -0
  439. package/dist/pactile/review/escalation-reply.d.ts +10 -0
  440. package/dist/pactile/review/escalation-reply.d.ts.map +1 -0
  441. package/dist/pactile/review/escalation-reply.js +61 -0
  442. package/dist/pactile/review/escalation-reply.js.map +1 -0
  443. package/dist/pactile/review/escalation-transport.d.ts +32 -0
  444. package/dist/pactile/review/escalation-transport.d.ts.map +1 -0
  445. package/dist/pactile/review/escalation-transport.js +216 -0
  446. package/dist/pactile/review/escalation-transport.js.map +1 -0
  447. package/dist/pactile/review/escalation.d.ts +7 -0
  448. package/dist/pactile/review/escalation.d.ts.map +1 -0
  449. package/dist/pactile/review/escalation.js +6 -0
  450. package/dist/pactile/review/escalation.js.map +1 -0
  451. package/dist/pactile/review/evidence.d.ts +25 -0
  452. package/dist/pactile/review/evidence.d.ts.map +1 -0
  453. package/dist/pactile/review/evidence.js +58 -0
  454. package/dist/pactile/review/evidence.js.map +1 -0
  455. package/dist/pactile/review/jev-escalation-advice.d.ts +77 -0
  456. package/dist/pactile/review/jev-escalation-advice.d.ts.map +1 -0
  457. package/dist/pactile/review/jev-escalation-advice.js +358 -0
  458. package/dist/pactile/review/jev-escalation-advice.js.map +1 -0
  459. package/dist/pactile/scheduler/index.d.ts +6 -0
  460. package/dist/pactile/scheduler/index.d.ts.map +1 -0
  461. package/dist/pactile/scheduler/index.js +5 -0
  462. package/dist/pactile/scheduler/index.js.map +1 -0
  463. package/dist/pactile/scheduler/project-lease-store.d.ts +35 -0
  464. package/dist/pactile/scheduler/project-lease-store.d.ts.map +1 -0
  465. package/dist/pactile/scheduler/project-lease-store.js +189 -0
  466. package/dist/pactile/scheduler/project-lease-store.js.map +1 -0
  467. package/dist/pactile/scheduler/scheduler.d.ts +91 -0
  468. package/dist/pactile/scheduler/scheduler.d.ts.map +1 -0
  469. package/dist/pactile/scheduler/scheduler.js +589 -0
  470. package/dist/pactile/scheduler/scheduler.js.map +1 -0
  471. package/dist/pactile/scheduler/task-kernel-admission.d.ts +173 -0
  472. package/dist/pactile/scheduler/task-kernel-admission.d.ts.map +1 -0
  473. package/dist/pactile/scheduler/task-kernel-admission.js +1560 -0
  474. package/dist/pactile/scheduler/task-kernel-admission.js.map +1 -0
  475. package/dist/pactile/scheduler/task-kernel-wave-dispatch.d.ts +184 -0
  476. package/dist/pactile/scheduler/task-kernel-wave-dispatch.d.ts.map +1 -0
  477. package/dist/pactile/scheduler/task-kernel-wave-dispatch.js +952 -0
  478. package/dist/pactile/scheduler/task-kernel-wave-dispatch.js.map +1 -0
  479. package/dist/pactile/scheduler/task-map-scheduler.d.ts +116 -0
  480. package/dist/pactile/scheduler/task-map-scheduler.d.ts.map +1 -0
  481. package/dist/pactile/scheduler/task-map-scheduler.js +1448 -0
  482. package/dist/pactile/scheduler/task-map-scheduler.js.map +1 -0
  483. package/dist/pactile/scheduler/task-run-dispatch-state.d.ts +7 -0
  484. package/dist/pactile/scheduler/task-run-dispatch-state.d.ts.map +1 -0
  485. package/dist/pactile/scheduler/task-run-dispatch-state.js +69 -0
  486. package/dist/pactile/scheduler/task-run-dispatch-state.js.map +1 -0
  487. package/dist/pactile/task/authorization.d.ts +3 -0
  488. package/dist/pactile/task/authorization.d.ts.map +1 -0
  489. package/dist/pactile/task/authorization.js +43 -0
  490. package/dist/pactile/task/authorization.js.map +1 -0
  491. package/dist/pactile/task/creation.d.ts +22 -0
  492. package/dist/pactile/task/creation.d.ts.map +1 -0
  493. package/dist/pactile/task/creation.js +95 -0
  494. package/dist/pactile/task/creation.js.map +1 -0
  495. package/dist/pactile/task/guards.d.ts +1 -4
  496. package/dist/pactile/task/guards.d.ts.map +1 -1
  497. package/dist/pactile/task/guards.js +5 -57
  498. package/dist/pactile/task/guards.js.map +1 -1
  499. package/dist/pactile/task/session-jev-receipt.d.ts +58 -0
  500. package/dist/pactile/task/session-jev-receipt.d.ts.map +1 -0
  501. package/dist/pactile/task/session-jev-receipt.js +375 -0
  502. package/dist/pactile/task/session-jev-receipt.js.map +1 -0
  503. package/dist/pactile/task/session-jev.d.ts +7 -0
  504. package/dist/pactile/task/session-jev.d.ts.map +1 -0
  505. package/dist/pactile/task/session-jev.js +217 -0
  506. package/dist/pactile/task/session-jev.js.map +1 -0
  507. package/dist/pactile/task/session-pack.d.ts.map +1 -1
  508. package/dist/pactile/task/session-pack.js +155 -16
  509. package/dist/pactile/task/session-pack.js.map +1 -1
  510. package/dist/pactile/task/session-retrieval-jev-receipt.d.ts +49 -0
  511. package/dist/pactile/task/session-retrieval-jev-receipt.d.ts.map +1 -0
  512. package/dist/pactile/task/session-retrieval-jev-receipt.js +97 -0
  513. package/dist/pactile/task/session-retrieval-jev-receipt.js.map +1 -0
  514. package/dist/pactile/task/session-retrieval-jev.d.ts +8 -0
  515. package/dist/pactile/task/session-retrieval-jev.d.ts.map +1 -0
  516. package/dist/pactile/task/session-retrieval-jev.js +352 -0
  517. package/dist/pactile/task/session-retrieval-jev.js.map +1 -0
  518. package/dist/pactile/task/strategy.d.ts +1 -23
  519. package/dist/pactile/task/strategy.d.ts.map +1 -1
  520. package/dist/pactile/task/strategy.js +2 -135
  521. package/dist/pactile/task/strategy.js.map +1 -1
  522. package/dist/pactile/tiles/jev-selection.d.ts +44 -0
  523. package/dist/pactile/tiles/jev-selection.d.ts.map +1 -0
  524. package/dist/pactile/tiles/jev-selection.js +210 -0
  525. package/dist/pactile/tiles/jev-selection.js.map +1 -0
  526. package/dist/pactile/tiles/safe-yaml.d.ts +5 -1
  527. package/dist/pactile/tiles/safe-yaml.d.ts.map +1 -1
  528. package/dist/pactile/tiles/safe-yaml.js +8 -8
  529. package/dist/pactile/tiles/safe-yaml.js.map +1 -1
  530. package/dist/pactile/tiles/selection-receipts.d.ts +71 -0
  531. package/dist/pactile/tiles/selection-receipts.d.ts.map +1 -0
  532. package/dist/pactile/tiles/selection-receipts.js +319 -0
  533. package/dist/pactile/tiles/selection-receipts.js.map +1 -0
  534. package/dist/pactile/tiles/selection.d.ts +185 -0
  535. package/dist/pactile/tiles/selection.d.ts.map +1 -0
  536. package/dist/pactile/tiles/selection.js +985 -0
  537. package/dist/pactile/tiles/selection.js.map +1 -0
  538. package/dist/pactile/verification/git-observer.d.ts +3 -0
  539. package/dist/pactile/verification/git-observer.d.ts.map +1 -0
  540. package/dist/pactile/verification/git-observer.js +3 -0
  541. package/dist/pactile/verification/git-observer.js.map +1 -0
  542. package/dist/pactile/verification/index.d.ts +11 -0
  543. package/dist/pactile/verification/index.d.ts.map +1 -0
  544. package/dist/pactile/verification/index.js +6 -0
  545. package/dist/pactile/verification/index.js.map +1 -0
  546. package/dist/pactile/verification/jev-advice.d.ts +78 -0
  547. package/dist/pactile/verification/jev-advice.d.ts.map +1 -0
  548. package/dist/pactile/verification/jev-advice.js +397 -0
  549. package/dist/pactile/verification/jev-advice.js.map +1 -0
  550. package/dist/pactile/verification/planner.d.ts +15 -0
  551. package/dist/pactile/verification/planner.d.ts.map +1 -0
  552. package/dist/pactile/verification/planner.js +412 -0
  553. package/dist/pactile/verification/planner.js.map +1 -0
  554. package/dist/pactile/verification/receipt.d.ts +101 -0
  555. package/dist/pactile/verification/receipt.d.ts.map +1 -0
  556. package/dist/pactile/verification/receipt.js +400 -0
  557. package/dist/pactile/verification/receipt.js.map +1 -0
  558. package/dist/pactile/verification/types.d.ts +72 -0
  559. package/dist/pactile/verification/types.d.ts.map +1 -0
  560. package/dist/pactile/verification/types.js +21 -0
  561. package/dist/pactile/verification/types.js.map +1 -0
  562. package/dist/pactile/worktree/git-probe.d.ts +32 -0
  563. package/dist/pactile/worktree/git-probe.d.ts.map +1 -0
  564. package/dist/pactile/worktree/git-probe.js +336 -0
  565. package/dist/pactile/worktree/git-probe.js.map +1 -0
  566. package/dist/pactile/worktree/git-removal.d.ts +3 -0
  567. package/dist/pactile/worktree/git-removal.d.ts.map +1 -0
  568. package/dist/pactile/worktree/git-removal.js +11 -0
  569. package/dist/pactile/worktree/git-removal.js.map +1 -0
  570. package/dist/pactile/worktree/index.d.ts +2 -0
  571. package/dist/pactile/worktree/index.d.ts.map +1 -0
  572. package/dist/pactile/worktree/index.js +2 -0
  573. package/dist/pactile/worktree/index.js.map +1 -0
  574. package/dist/pactile/worktree/integration-evidence.d.ts +80 -0
  575. package/dist/pactile/worktree/integration-evidence.d.ts.map +1 -0
  576. package/dist/pactile/worktree/integration-evidence.js +166 -0
  577. package/dist/pactile/worktree/integration-evidence.js.map +1 -0
  578. package/dist/pactile/worktree/manager-core.d.ts +50 -0
  579. package/dist/pactile/worktree/manager-core.d.ts.map +1 -0
  580. package/dist/pactile/worktree/manager-core.js +359 -0
  581. package/dist/pactile/worktree/manager-core.js.map +1 -0
  582. package/dist/pactile/worktree/manager-provenance.d.ts +32 -0
  583. package/dist/pactile/worktree/manager-provenance.d.ts.map +1 -0
  584. package/dist/pactile/worktree/manager-provenance.js +409 -0
  585. package/dist/pactile/worktree/manager-provenance.js.map +1 -0
  586. package/dist/pactile/worktree/manager-types.d.ts +84 -0
  587. package/dist/pactile/worktree/manager-types.d.ts.map +1 -0
  588. package/dist/pactile/worktree/manager-types.js +11 -0
  589. package/dist/pactile/worktree/manager-types.js.map +1 -0
  590. package/dist/pactile/worktree/manager.d.ts +10 -0
  591. package/dist/pactile/worktree/manager.d.ts.map +1 -0
  592. package/dist/pactile/worktree/manager.js +6 -0
  593. package/dist/pactile/worktree/manager.js.map +1 -0
  594. package/dist/pactile/worktree/run-workspace-cleanup.d.ts +10 -0
  595. package/dist/pactile/worktree/run-workspace-cleanup.d.ts.map +1 -0
  596. package/dist/pactile/worktree/run-workspace-cleanup.js +695 -0
  597. package/dist/pactile/worktree/run-workspace-cleanup.js.map +1 -0
  598. package/dist/pactile/worktree/run-workspace-lifecycle.d.ts +65 -0
  599. package/dist/pactile/worktree/run-workspace-lifecycle.d.ts.map +1 -0
  600. package/dist/pactile/worktree/run-workspace-lifecycle.js +276 -0
  601. package/dist/pactile/worktree/run-workspace-lifecycle.js.map +1 -0
  602. package/dist/templates/common/bundled-skills/pactile-meta/references/customize-local/change-hooks.md +1 -1
  603. package/dist/templates/common/bundled-skills/pactile-meta/references/local-architecture/generated-files.md +1 -1
  604. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/hooks-and-settings.md +1 -1
  605. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/overview.md +1 -1
  606. package/dist/templates/common/bundled-skills/pactile-meta/references/platform-files/platform-map.md +1 -1
  607. package/dist/templates/common/bundled-skills/smart-search-cli/SKILL.md +2 -0
  608. package/dist/templates/common/skills/brainstorm.md +19 -13
  609. package/dist/templates/markdown/agents.md +2 -2
  610. package/dist/templates/markdown/framework/execution-strategy.md.txt +1 -1
  611. package/dist/templates/markdown/framework/index.md.txt +1 -1
  612. package/dist/templates/markdown/framework/parallel-first-execution.md.txt +17 -105
  613. package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +3 -3
  614. package/dist/templates/markdown/framework/release-boundary.md.txt +9 -10
  615. package/dist/templates/markdown/framework/retrieval-daily-guide.md.txt +1 -1
  616. package/dist/templates/pactile/CONTEXT.md +48 -36
  617. package/dist/templates/pactile/modules/approval-personal/contract.md +12 -24
  618. package/dist/templates/pactile/modules/close-basic/contract.md +15 -18
  619. package/dist/templates/pactile/modules/debug-recovery/contract.md +4 -4
  620. package/dist/templates/pactile/modules/define-basic/contract.md +15 -19
  621. package/dist/templates/pactile/modules/define-extended/contract.md +3 -3
  622. package/dist/templates/pactile/modules/execute-agent/contract.md +11 -19
  623. package/dist/templates/pactile/modules/intake-basic/contract.md +16 -13
  624. package/dist/templates/pactile/modules/verify-basic/contract.md +11 -20
  625. package/dist/templates/pactile/tasks/templates/release-execution/design.md +5 -5
  626. package/dist/templates/pactile/tasks/templates/release-readiness/design.md +6 -6
  627. package/dist/templates/pactile/tasks/templates/release-readiness/handoff-template.md +8 -2
  628. package/dist/templates/pactile/workflow.md +37 -41
  629. package/dist/templates/template-utils.d.ts +1 -2
  630. package/dist/templates/template-utils.d.ts.map +1 -1
  631. package/dist/templates/template-utils.js +2 -13
  632. package/dist/templates/template-utils.js.map +1 -1
  633. package/dist/types/ai-tools.d.ts +1 -1
  634. package/dist/types/ai-tools.js +1 -1
  635. package/dist/utils/manifest-prune.js +1 -1
  636. package/dist/utils/manifest-prune.js.map +1 -1
  637. package/dist/utils/p36-upgrade.d.ts +2 -2
  638. package/dist/utils/p36-upgrade.d.ts.map +1 -1
  639. package/dist/utils/p36-upgrade.js +2 -10
  640. package/dist/utils/p36-upgrade.js.map +1 -1
  641. package/dist/utils/post-update-smoke.js +1 -1
  642. package/dist/utils/post-update-smoke.js.map +1 -1
  643. package/dist/utils/readiness.d.ts +2 -2
  644. package/dist/utils/readiness.js +2 -2
  645. package/dist/utils/uninstall-scrubbers.js +1 -1
  646. package/dist/utils/workflow-dir.d.ts +1 -1
  647. package/dist/utils/workflow-dir.d.ts.map +1 -1
  648. package/dist/utils/workflow-dir.js +1 -1
  649. package/dist/utils/workflow-dir.js.map +1 -1
  650. package/package.json +42 -37
  651. package/CHANGELOG.md +0 -651
  652. package/bin/compat-warning.js +0 -11
  653. package/bin/pactile.js +0 -3
  654. package/bin/smart-search.js +0 -38
  655. package/dist/migrations/manifests/0.1.0.json +0 -9
  656. package/dist/migrations/manifests/0.1.1.json +0 -9
  657. package/dist/migrations/manifests/0.1.2.json +0 -9
  658. package/dist/migrations/manifests/0.1.3.json +0 -9
  659. package/dist/migrations/manifests/0.1.4.json +0 -9
  660. package/dist/migrations/manifests/0.2.1.json +0 -9
  661. package/dist/migrations/manifests/0.2.10.json +0 -374
  662. package/dist/migrations/manifests/0.2.2.json +0 -9
  663. package/dist/migrations/manifests/0.2.3.json +0 -9
  664. package/dist/migrations/manifests/0.2.4.json +0 -9
  665. package/dist/migrations/manifests/0.2.5.json +0 -9
  666. package/dist/migrations/manifests/0.2.6.json +0 -9
  667. package/dist/migrations/manifests/0.2.7.json +0 -9
  668. package/dist/migrations/manifests/0.2.8.json +0 -9
  669. package/dist/migrations/manifests/0.2.9.json +0 -9
  670. package/dist/migrations/manifests/0.3.0.json +0 -89
  671. package/dist/migrations/manifests/0.3.1.json +0 -32
  672. package/dist/migrations/manifests/0.3.2.json +0 -16
  673. package/dist/migrations/manifests/0.3.3.json +0 -9
  674. package/dist/migrations/manifests/0.3.4.json +0 -9
  675. package/dist/migrations/manifests/0.3.5.json +0 -9
  676. package/dist/migrations/manifests/0.3.6.json +0 -9
  677. package/dist/migrations/manifests/0.4.0.json +0 -9
  678. package/dist/migrations/manifests/0.4.1.json +0 -9
  679. package/dist/migrations/manifests/0.4.2.json +0 -9
  680. package/dist/migrations/manifests/0.4.3.json +0 -9
  681. package/dist/migrations/manifests/0.5.0-beta.0.json +0 -9
  682. package/dist/migrations/manifests/0.5.0-beta.1.json +0 -9
  683. package/dist/migrations/manifests/0.5.0-beta.2.json +0 -9
  684. package/dist/migrations/manifests/0.5.0-beta.3.json +0 -9
  685. package/dist/migrations/manifests/0.5.0-beta.4.json +0 -9
  686. package/dist/migrations/manifests/0.5.0-beta.5.json +0 -9
  687. package/dist/migrations/manifests/0.5.0.json +0 -9
  688. package/dist/pactile/compat/retired-alternate-client.d.ts +0 -42
  689. package/dist/pactile/compat/retired-alternate-client.d.ts.map +0 -1
  690. package/dist/pactile/compat/retired-alternate-client.js +0 -153
  691. package/dist/pactile/compat/retired-alternate-client.js.map +0 -1
  692. package/dist/pactile/compat/update-residue.d.ts +0 -4
  693. package/dist/pactile/compat/update-residue.d.ts.map +0 -1
  694. package/dist/pactile/compat/update-residue.js +0 -55
  695. package/dist/pactile/compat/update-residue.js.map +0 -1
  696. package/dist/utils/cursor-retrieval-env.d.ts +0 -17
  697. package/dist/utils/cursor-retrieval-env.d.ts.map +0 -1
  698. package/dist/utils/cursor-retrieval-env.js +0 -19
  699. package/dist/utils/cursor-retrieval-env.js.map +0 -1
  700. package/dist/utils/proxy.d.ts +0 -25
  701. package/dist/utils/proxy.d.ts.map +0 -1
  702. package/dist/utils/proxy.js +0 -60
  703. package/dist/utils/proxy.js.map +0 -1
  704. package/scripts/postinstall.js +0 -6
@@ -2,23 +2,368 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { execFileSync } from "node:child_process";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
- import { readDependencyGraph, readKernel, unmetRequires } from "../../core/task/index.js";
6
5
  import { readStrategyContract } from "../task/strategy.js";
7
- import { checkStartExecution } from "../task/guards.js";
8
- import { resolveTaskDir } from "../task/session.js";
9
- import { parallelChild, reserveParallelChild, updateParallelChildPid } from "../parallel/policy.js";
6
+ import { approvedExecuteTask } from "../task/authorization.js";
7
+ import { parallelChild, reserveParallelChild, updateParallelChildPid, } from "../parallel/policy.js";
10
8
  import { sameGitRoot } from "../../utils/git-root.js";
11
- import { PiRpcClient } from "./rpc.js";
9
+ import { readTaskKernel, } from "../../core/task/index.js";
10
+ import { isRunCandidateObservationEntry } from "../../core/task/task-candidate-observer.js";
11
+ import { resolveTaskDir } from "../task/session.js";
12
+ import { PiRpcClient, } from "./rpc.js";
13
+ import { buildIndependentPiReviewPrompt, } from "../review/contract.js";
14
+ import { resolvePiReviewEvidenceV1 } from "../review/evidence.js";
15
+ import { bindPiV2RunHost, persistPiV2StopAndRelease, preparePiV2RunDispatch, recheckPiV2RunDispatchBeforeSpawn, recheckPiV2RunDispatchWorkspace, } from "./v2-dispatch.js";
16
+ export { readPiHostStopReceipt, readPiTaskRunEvidence, } from "./v2-dispatch.js";
17
+ function safeReviewEvidencePath(taskDir, reference) {
18
+ if (!reference || path.isAbsolute(reference) || reference.includes("\0")) {
19
+ throw new Error("Pi Review evidence reference is invalid");
20
+ }
21
+ const resolved = path.resolve(taskDir, reference);
22
+ const relative = path.relative(path.resolve(taskDir), resolved);
23
+ if (!relative ||
24
+ relative === ".." ||
25
+ relative.startsWith(`..${path.sep}`) ||
26
+ path.isAbsolute(relative)) {
27
+ throw new Error("Pi Review evidence reference escapes the Task directory");
28
+ }
29
+ const realTaskDir = fs.realpathSync(taskDir);
30
+ const realFile = fs.realpathSync(resolved);
31
+ const realRelative = path.relative(realTaskDir, realFile);
32
+ if (!realRelative ||
33
+ realRelative === ".." ||
34
+ realRelative.startsWith(`..${path.sep}`) ||
35
+ path.isAbsolute(realRelative)) {
36
+ throw new Error("Pi Review evidence resolves outside the Task directory");
37
+ }
38
+ if (!fs.statSync(realFile).isFile())
39
+ throw new Error("Pi Review evidence is not a file");
40
+ return realFile;
41
+ }
42
+ function requireObjectJson(file, label) {
43
+ let parsed;
44
+ try {
45
+ parsed = JSON.parse(fs.readFileSync(file, "utf8"));
46
+ }
47
+ catch {
48
+ throw new Error(`${label} is not valid JSON`);
49
+ }
50
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
51
+ throw new Error(`${label} must be a JSON object`);
52
+ }
53
+ return parsed;
54
+ }
55
+ /** Read the persisted Check receipts and exact structured response bytes before Review parsing. */
56
+ export function readPiCheckRunEvidenceV1(taskDir, record) {
57
+ if (record.role !== "check" ||
58
+ record.outcome !== "settled" ||
59
+ record.tool_errors !== 0 ||
60
+ record.result_redacted === true ||
61
+ !record.session_id ||
62
+ !record.reviewer_id ||
63
+ !record.task_id ||
64
+ !record.task_run_id ||
65
+ !record.candidate_snapshot_id ||
66
+ !record.candidate_fingerprint ||
67
+ !record.start_request_id ||
68
+ !Number.isSafeInteger(record.process_id) ||
69
+ !record.process_id ||
70
+ !record.review_start_receipt_ref ||
71
+ !record.review_stop_receipt_ref ||
72
+ !record.result_file ||
73
+ !record.result_sha256 ||
74
+ !record.review_attempts_ref ||
75
+ !record.review_attempts_sha256 ||
76
+ !record.progress_evidence_ref ||
77
+ !record.kernel_revision_at_dispatch) {
78
+ throw new Error("Pi Check run is incomplete or did not settle safely");
79
+ }
80
+ if (record.review_start_receipt_ref !==
81
+ `pi-bridge/starts/${record.run_id}.json` ||
82
+ record.review_stop_receipt_ref !==
83
+ `pi-bridge/stops/${record.run_id}.json` ||
84
+ record.progress_evidence_ref !==
85
+ `pi-bridge/events/${record.run_id}.jsonl` ||
86
+ record.result_file !== `pi-bridge/results/${record.run_id}.md`) {
87
+ throw new Error("Pi Check evidence references are not bound to this run");
88
+ }
89
+ const startPath = safeReviewEvidencePath(taskDir, record.review_start_receipt_ref);
90
+ const stopPath = safeReviewEvidencePath(taskDir, record.review_stop_receipt_ref);
91
+ const attemptsPath = safeReviewEvidencePath(taskDir, record.review_attempts_ref ?? "");
92
+ const eventPath = safeReviewEvidencePath(taskDir, record.progress_evidence_ref);
93
+ const resultPath = safeReviewEvidencePath(taskDir, record.result_file);
94
+ const start = requireObjectJson(startPath, "Pi Check start receipt");
95
+ const stop = requireObjectJson(stopPath, "Pi Check stop receipt");
96
+ const events = fs
97
+ .readFileSync(eventPath, "utf8")
98
+ .split(/\r?\n/u)
99
+ .filter(Boolean);
100
+ const resultBytes = fs.readFileSync(resultPath);
101
+ const attemptsBytes = fs.readFileSync(attemptsPath);
102
+ const resultSha256 = createHash("sha256").update(resultBytes).digest("hex");
103
+ const progressSha256 = createHash("sha256")
104
+ .update(fs.readFileSync(eventPath))
105
+ .digest("hex");
106
+ const attemptsSha256 = createHash("sha256").update(attemptsBytes).digest("hex");
107
+ if (attemptsBytes.byteLength > MAX_PI_REVIEW_ATTEMPT_MANIFEST_BYTES) {
108
+ throw new Error("Pi Check Review attempt manifest exceeds its evidence bound");
109
+ }
110
+ let attemptSummaries;
111
+ try {
112
+ attemptSummaries = attemptsBytes
113
+ .toString("utf8")
114
+ .split(/\r?\n/u)
115
+ .filter(Boolean)
116
+ .map((line) => JSON.parse(line));
117
+ }
118
+ catch {
119
+ throw new Error("Pi Check Review attempt manifest is not valid JSONL");
120
+ }
121
+ if (attemptSummaries.length < 1 || attemptSummaries.length > 2) {
122
+ throw new Error("Pi Check Review must contain one or two ordered attempts");
123
+ }
124
+ const responseEvidence = [];
125
+ const responseTexts = [];
126
+ for (const [index, attempt] of attemptSummaries.entries()) {
127
+ const number = index + 1;
128
+ const expectedKind = number === 1 ? "initial" : "format-correction";
129
+ const expectedRef = `pi-bridge/review-responses/${record.run_id}-attempt-${number}.txt`;
130
+ if (attempt?.attempt !== number ||
131
+ attempt.kind !== expectedKind ||
132
+ attempt.outcome !== "settled" ||
133
+ attempt.responseRef !== expectedRef ||
134
+ typeof attempt.responseSha256 !== "string" ||
135
+ !/^[a-f0-9]{64}$/u.test(attempt.responseSha256) ||
136
+ !Number.isSafeInteger(attempt.responseBytes) ||
137
+ attempt.responseBytes < 1 ||
138
+ attempt.responseBytes > MAX_PI_REVIEW_RESPONSE_BYTES ||
139
+ typeof attempt.redacted !== "boolean" ||
140
+ attempt.stopReason !== "stop") {
141
+ throw new Error("Pi Check Review attempt order or response binding is invalid");
142
+ }
143
+ const responsePath = safeReviewEvidencePath(taskDir, expectedRef);
144
+ const responseBytes = fs.readFileSync(responsePath);
145
+ if (responseBytes.byteLength !== attempt.responseBytes ||
146
+ createHash("sha256").update(responseBytes).digest("hex") !==
147
+ attempt.responseSha256) {
148
+ throw new Error("Pi Check Review response evidence does not match its attempt");
149
+ }
150
+ let responseText;
151
+ try {
152
+ responseText = new TextDecoder("utf-8", { fatal: true }).decode(responseBytes);
153
+ }
154
+ catch {
155
+ throw new Error("Pi Check Review response evidence is not valid UTF-8");
156
+ }
157
+ if (!Buffer.from(responseText, "utf8").equals(responseBytes)) {
158
+ throw new Error("Pi Check Review response evidence is not canonical UTF-8");
159
+ }
160
+ const format = isStructuredJsonObject(responseText)
161
+ ? "structured-json-object"
162
+ : "non-structured-json";
163
+ if (attempt.format !== format) {
164
+ throw new Error("Pi Check Review response format does not match its evidence");
165
+ }
166
+ responseTexts.push(responseText);
167
+ responseEvidence.push({
168
+ ref: expectedRef,
169
+ sha256: attempt.responseSha256,
170
+ sizeBytes: attempt.responseBytes,
171
+ });
172
+ }
173
+ const correction = record.review_format_correction;
174
+ const firstFormat = attemptSummaries[0]?.format;
175
+ const lastFormat = attemptSummaries.at(-1)?.format;
176
+ if ((attemptSummaries.length === 1 &&
177
+ (firstFormat !== "structured-json-object" ||
178
+ correction?.attempted !== false ||
179
+ correction.outcome !== "not-needed")) ||
180
+ (attemptSummaries.length === 2 &&
181
+ (firstFormat !== "non-structured-json" ||
182
+ lastFormat !== "structured-json-object" ||
183
+ correction?.attempted !== true ||
184
+ correction.outcome !== "accepted"))) {
185
+ throw new Error("Pi Check Review correction sequence is inconsistent");
186
+ }
187
+ const finalResponseText = responseTexts.at(-1);
188
+ if (finalResponseText === undefined ||
189
+ !Buffer.from(`${finalResponseText || "(Pi returned no assistant text.)"}\n`, "utf8")
190
+ .equals(resultBytes)) {
191
+ throw new Error("Pi Check result bytes do not match the final settled response");
192
+ }
193
+ const receiptResponses = stop.reviewResponses;
194
+ if (!Array.isArray(receiptResponses) ||
195
+ receiptResponses.length !== responseEvidence.length ||
196
+ receiptResponses.some((item, index) => {
197
+ const expected = responseEvidence[index];
198
+ return (item?.ref !== expected?.ref ||
199
+ item?.sha256 !== expected?.sha256 ||
200
+ item?.sizeBytes !== expected?.sizeBytes);
201
+ })) {
202
+ throw new Error("Pi Check stop receipt does not bind every response artifact");
203
+ }
204
+ if (start.schemaVersion !== 1 ||
205
+ start.source !== "pactile-pi-review" ||
206
+ start.role !== "check" ||
207
+ stop.schemaVersion !== 1 ||
208
+ stop.source !== "pactile-pi-review" ||
209
+ stop.assurance !== "manager-owned-child-exit" ||
210
+ stop.role !== "check" ||
211
+ start.taskId !== record.task_id ||
212
+ stop.taskId !== record.task_id ||
213
+ start.taskRunId !== record.task_run_id ||
214
+ stop.taskRunId !== record.task_run_id ||
215
+ start.piRunId !== record.run_id ||
216
+ stop.piRunId !== record.run_id ||
217
+ start.sessionId !== record.session_id ||
218
+ stop.sessionId !== record.session_id ||
219
+ start.processId !== record.process_id ||
220
+ stop.processId !== record.process_id ||
221
+ start.startRequestId !== record.start_request_id ||
222
+ stop.startRequestId !== record.start_request_id ||
223
+ start.kernelRevision !== record.kernel_revision_at_dispatch ||
224
+ start.candidateSnapshotId !== record.candidate_snapshot_id ||
225
+ start.candidateFingerprint !== record.candidate_fingerprint ||
226
+ start.evidenceRef !== `pi-bridge/runs/${record.run_id}.json` ||
227
+ stop.evidenceRef !== start.evidenceRef ||
228
+ start.progressEvidenceRef !== record.progress_evidence_ref ||
229
+ stop.progressEvidenceRef !== record.progress_evidence_ref ||
230
+ stop.progressEvidenceSha256 !== progressSha256 ||
231
+ stop.startReceiptRef !== record.review_start_receipt_ref ||
232
+ stop.stopReceiptId !== `review-stop-${record.run_id}` ||
233
+ stop.terminal !== "exited" ||
234
+ stop.exitCode !== 0 ||
235
+ stop.signalCode !== null ||
236
+ stop.cancellationRequestId !== null ||
237
+ stop.resultRef !== record.result_file ||
238
+ stop.resultSha256 !== record.result_sha256 ||
239
+ resultSha256 !== record.result_sha256 ||
240
+ record.review_attempts_ref !==
241
+ `pi-bridge/review-attempts/${record.run_id}.jsonl` ||
242
+ record.review_attempts_sha256 !== attemptsSha256 ||
243
+ stop.reviewAttemptsRef !== record.review_attempts_ref ||
244
+ stop.reviewAttemptsSha256 !== attemptsSha256 ||
245
+ record.event_count < 1 ||
246
+ events.length !== record.event_count ||
247
+ stop.processExit?.terminationVerified !== true ||
248
+ !stop.processExit.exitObservedAt ||
249
+ stop.processExit.processId !== record.process_id ||
250
+ stop.processExit.exitCode !== 0 ||
251
+ stop.processExit.signalCode !== null ||
252
+ record.reviewer_id !== `pi-session:${record.session_id}` ||
253
+ (record.cancellation_request_id !== undefined &&
254
+ record.cancellation_request_id !== null) ||
255
+ record.reviewer_identity_assurance !== "caller-declared") {
256
+ throw new Error("Pi Check receipts, events, or result bytes do not match the persisted run");
257
+ }
258
+ return {
259
+ start,
260
+ stop,
261
+ resultBytes,
262
+ evidenceRefs: [
263
+ record.review_start_receipt_ref,
264
+ record.review_stop_receipt_ref,
265
+ record.progress_evidence_ref,
266
+ record.result_file,
267
+ record.review_attempts_ref,
268
+ ...responseEvidence.map((item) => item.ref),
269
+ ],
270
+ };
271
+ }
272
+ const MAX_PI_REVIEW_RESPONSE_BYTES = 64 * 1024;
273
+ const MAX_PI_REVIEW_ATTEMPT_MANIFEST_BYTES = 16 * 1024;
274
+ /** Build the independent Review binding from the current Verify Kernel state. */
275
+ export function preparePiReviewRoute(root, taskReference) {
276
+ const projectRoot = fs.realpathSync(root);
277
+ const taskDir = resolveTaskDir(projectRoot, taskReference);
278
+ if (!fs.statSync(taskDir, { throwIfNoEntry: false })?.isDirectory()) {
279
+ throw new Error(`Task not found: ${taskReference}`);
280
+ }
281
+ if (!fs.existsSync(path.join(taskDir, "verify.md"))) {
282
+ throw new Error("Pi Review requires verify.md");
283
+ }
284
+ const read = readTaskKernel({ root: projectRoot, taskDir, cwd: projectRoot });
285
+ if (read.kind !== "task-kernel-v2") {
286
+ throw new Error("Pi Review requires a V2 Task Kernel");
287
+ }
288
+ const kernel = read.kernel;
289
+ if (kernel.phase !== "verify") {
290
+ throw new Error("Pi Review requires the Task Kernel Verify phase");
291
+ }
292
+ const run = kernel.runs.at(-1);
293
+ if (run?.state !== "completed" || !run.result || !run.candidateSnapshot) {
294
+ throw new Error("Pi Review requires the latest completed Run candidate");
295
+ }
296
+ const workdir = run.workspace
297
+ ? fs.realpathSync(run.workspace.canonicalPath)
298
+ : projectRoot;
299
+ if (run.workspace) {
300
+ const worktreesRoot = fs.realpathSync(path.join(projectRoot, ".pactile", "worktrees"));
301
+ const relative = path.relative(worktreesRoot, workdir);
302
+ if (!relative ||
303
+ relative === ".." ||
304
+ relative.startsWith(`..${path.sep}`) ||
305
+ path.isAbsolute(relative)) {
306
+ throw new Error("Pi Review Run worktree is outside .pactile/worktrees");
307
+ }
308
+ }
309
+ const binding = {
310
+ kernelRevision: kernel.revision,
311
+ taskId: kernel.identity.taskId,
312
+ runId: run.id,
313
+ candidateSnapshotId: run.candidateSnapshot.id,
314
+ candidateFingerprint: run.candidateSnapshot.fingerprint,
315
+ taskTitle: kernel.definition.title,
316
+ taskDescription: kernel.definition.description,
317
+ deliverable: kernel.definition.deliverable,
318
+ runSummary: run.result.summary,
319
+ runReferences: [...run.input.references],
320
+ authorizationScope: run.authorization.scope,
321
+ writeSetSnapshot: [...run.writeSetSnapshot],
322
+ candidateEntries: run.candidateSnapshot.entries.map((entry) => ({
323
+ ref: entry.ref,
324
+ fingerprint: entry.fingerprint,
325
+ })),
326
+ allowedEvidenceRefs: [...new Set([
327
+ ...run.candidateSnapshot.entries
328
+ .filter((entry) => !isRunCandidateObservationEntry(entry.ref))
329
+ .map((entry) => entry.ref),
330
+ ...run.result.evidenceRefs.filter((reference) => !isRunCandidateObservationEntry(reference)),
331
+ ])].sort(),
332
+ acceptanceCriteria: kernel.definition.acceptanceCriteria.map((criterion) => ({
333
+ id: criterion.id,
334
+ description: criterion.description,
335
+ })),
336
+ };
337
+ return { taskDir, kernel, run, workdir, binding };
338
+ }
339
+ function assertSameReviewBinding(actual, expected) {
340
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
341
+ throw new Error("Pi Review binding is stale or does not match the current Run candidate");
342
+ }
343
+ }
12
344
  function atomicJson(file, value) {
13
345
  fs.mkdirSync(path.dirname(file), { recursive: true });
14
346
  const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
15
- fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
347
+ fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
348
+ encoding: "utf8",
349
+ mode: 0o600,
350
+ });
16
351
  fs.renameSync(temporary, file);
17
352
  }
353
+ function writeExclusiveJson(file, value) {
354
+ fs.mkdirSync(path.dirname(file), { recursive: true });
355
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, {
356
+ encoding: "utf8",
357
+ flag: "wx",
358
+ mode: 0o600,
359
+ });
360
+ }
18
361
  function parseJson(file) {
19
362
  try {
20
363
  const raw = JSON.parse(fs.readFileSync(file, "utf8"));
21
- return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
364
+ return raw && typeof raw === "object" && !Array.isArray(raw)
365
+ ? raw
366
+ : null;
22
367
  }
23
368
  catch {
24
369
  return null;
@@ -37,59 +382,205 @@ function alive(pid) {
37
382
  }
38
383
  function redact(text) {
39
384
  return text
40
- .replace(/\b(?:sk[-_]|ghp_|gho_|glpat-|plane_api_)[A-Za-z0-9_-]{12,}\b/g, "[redacted]")
41
- .replace(/\bBearer\s+[A-Za-z0-9._~-]{12,}\b/gi, "Bearer [redacted]")
385
+ .replace(/\b(?:sk[-_]|gh[pousr]_|glpat-|plane_api_)[A-Za-z0-9_-]{12,}/gi, "[redacted]")
386
+ .replace(/\bBearer\s+[A-Za-z0-9._~+/-]{12,}/gi, "Bearer [redacted]")
42
387
  .replace(/\b(api[_-]?key|token|password|secret)\s*[:=]\s*\S+/gi, "$1=[redacted]");
43
388
  }
389
+ function safeProviderMessage(text) {
390
+ return [...redact(text)]
391
+ .map((character) => {
392
+ const code = character.charCodeAt(0);
393
+ return code <= 0x1f || code === 0x7f ? " " : character;
394
+ })
395
+ .join("")
396
+ .slice(0, 500);
397
+ }
398
+ function safeErrorSummary(value) {
399
+ const message = value.toLowerCase();
400
+ if (message.includes("cancel"))
401
+ return "Pi operation cancelled";
402
+ if (message.includes("timed out") || message.includes("timeout"))
403
+ return "Pi operation timed out";
404
+ if (message.includes("session changed"))
405
+ return "Pi Review session changed";
406
+ if (message.includes("implementation run host session"))
407
+ return "Pi Check session matches the implementation Run host session";
408
+ if (message.includes("candidate") ||
409
+ message.includes("review binding") ||
410
+ message.includes("evidence"))
411
+ return "Pi Review preflight failed";
412
+ if (message.includes("launch failed"))
413
+ return "Pi RPC launch failed";
414
+ if (message.includes("exited"))
415
+ return "Pi RPC process exited";
416
+ if (message.includes("rejected"))
417
+ return "Pi RPC request rejected";
418
+ if (message.includes("streaming") || message.includes("settled without"))
419
+ return "Pi did not settle a final response";
420
+ return "Pi operation failed";
421
+ }
422
+ function normalizeEventType(value) {
423
+ if (typeof value !== "string")
424
+ return "other";
425
+ const allowed = new Set([
426
+ "agent_start",
427
+ "agent_end",
428
+ "agent_settled",
429
+ "auto_retry_start",
430
+ "auto_retry_end",
431
+ "auto_compaction_start",
432
+ "auto_compaction_end",
433
+ "extension_error",
434
+ "message_start",
435
+ "message_update",
436
+ "message_end",
437
+ "session_start",
438
+ "session_shutdown",
439
+ "session_before_compact",
440
+ "session_compact",
441
+ "tool_execution_start",
442
+ "tool_execution_update",
443
+ "tool_execution_end",
444
+ "turn_start",
445
+ "turn_end",
446
+ ]);
447
+ return allowed.has(value) ? value : "other";
448
+ }
449
+ function normalizeToolName(value) {
450
+ const allowed = new Set([
451
+ "bash",
452
+ "edit",
453
+ "find",
454
+ "grep",
455
+ "ls",
456
+ "read",
457
+ "write",
458
+ ]);
459
+ return allowed.has(value) ? value : "other";
460
+ }
461
+ function normalizeMessageRole(value) {
462
+ if (typeof value !== "string")
463
+ return "other";
464
+ const allowed = new Set(["assistant", "system", "tool", "user"]);
465
+ return allowed.has(value) ? value : "other";
466
+ }
467
+ function normalizeProviderError(value) {
468
+ return typeof value === "string" && value.length > 0
469
+ ? "provider-reported-error"
470
+ : null;
471
+ }
472
+ function normalizeStopReason(value) {
473
+ if (typeof value !== "string")
474
+ return null;
475
+ const allowed = new Set([
476
+ "stop",
477
+ "length",
478
+ "error",
479
+ "aborted",
480
+ "cancelled",
481
+ "toolUse",
482
+ "end_turn",
483
+ ]);
484
+ return allowed.has(value) ? value : "other";
485
+ }
44
486
  function assistantText(event) {
45
487
  const messages = Array.isArray(event.messages) ? event.messages : [];
46
- const assistant = [...messages].reverse().find((message) => message && typeof message === "object" && message.role === "assistant");
488
+ const assistant = [...messages]
489
+ .reverse()
490
+ .find((message) => message &&
491
+ typeof message === "object" &&
492
+ message.role === "assistant");
47
493
  if (!assistant)
48
- return { text: "", stopReason: null, errorMessage: null };
494
+ return {
495
+ rawText: "",
496
+ text: "",
497
+ redacted: false,
498
+ stopReason: null,
499
+ errorMessage: null,
500
+ };
49
501
  const content = Array.isArray(assistant.content) ? assistant.content : [];
50
- const text = content.filter((part) => part && typeof part === "object" && part.type === "text")
51
- .map((part) => String(part.text ?? "")).join("\n");
52
- return { text: redact(text), stopReason: typeof assistant.stopReason === "string" ? assistant.stopReason : null,
53
- errorMessage: typeof assistant.errorMessage === "string" ? redact(assistant.errorMessage).slice(0, 500) : null };
502
+ const text = content
503
+ .filter((part) => part &&
504
+ typeof part === "object" &&
505
+ part.type === "text")
506
+ .map((part) => String(part.text ?? ""))
507
+ .join("\n");
508
+ return {
509
+ rawText: text,
510
+ text: redact(text),
511
+ redacted: redact(text) !== text,
512
+ stopReason: normalizeStopReason(assistant.stopReason),
513
+ errorMessage: normalizeProviderError(assistant.errorMessage),
514
+ };
515
+ }
516
+ function isStructuredJsonObject(text) {
517
+ try {
518
+ const parsed = JSON.parse(text.trim());
519
+ return Boolean(parsed && typeof parsed === "object" && !Array.isArray(parsed));
520
+ }
521
+ catch {
522
+ return false;
523
+ }
524
+ }
525
+ function reviewAttemptSummary(attempt, kind, startedAt, response, firstEventMs = null, error, responseRef = null) {
526
+ if (!response) {
527
+ return {
528
+ attempt,
529
+ kind,
530
+ outcome: "transport-error",
531
+ format: "unavailable",
532
+ responseRef: null,
533
+ responseSha256: null,
534
+ responseBytes: 0,
535
+ redacted: false,
536
+ stopReason: null,
537
+ errorMessage: error ? safeErrorSummary(error) : null,
538
+ firstEventMs,
539
+ elapsedMs: Math.round(performance.now() - startedAt),
540
+ recordedAt: new Date().toISOString(),
541
+ };
542
+ }
543
+ return {
544
+ attempt,
545
+ kind,
546
+ outcome: "settled",
547
+ format: isStructuredJsonObject(response.rawText)
548
+ ? "structured-json-object"
549
+ : "non-structured-json",
550
+ responseRef,
551
+ responseSha256: createHash("sha256")
552
+ .update(Buffer.from(response.text, "utf8"))
553
+ .digest("hex"),
554
+ responseBytes: Buffer.byteLength(response.text, "utf8"),
555
+ redacted: response.redacted,
556
+ stopReason: response.stopReason,
557
+ errorMessage: response.errorMessage,
558
+ firstEventMs,
559
+ elapsedMs: Math.round(performance.now() - startedAt),
560
+ recordedAt: new Date().toISOString(),
561
+ };
54
562
  }
55
563
  function evidenceEvent(event) {
56
- const safe = { at: new Date().toISOString(), type: String(event.type ?? "unknown") };
564
+ const safe = {
565
+ at: new Date().toISOString(),
566
+ type: normalizeEventType(event.type),
567
+ };
57
568
  if (typeof event.toolName === "string")
58
- safe.tool = event.toolName;
569
+ safe.tool = normalizeToolName(event.toolName);
59
570
  if (event.type === "tool_execution_end")
60
571
  safe.is_error = event.isError === true;
61
- if (event.type === "message_end" && event.message && typeof event.message === "object") {
572
+ if (event.type === "message_end" &&
573
+ event.message &&
574
+ typeof event.message === "object") {
62
575
  const message = event.message;
63
- safe.role = typeof message.role === "string" ? message.role : "unknown";
576
+ safe.role = normalizeMessageRole(message.role);
64
577
  if (typeof message.stopReason === "string")
65
- safe.stop_reason = message.stopReason;
578
+ safe.stop_reason = normalizeStopReason(message.stopReason) ?? "other";
66
579
  }
67
580
  return safe;
68
581
  }
69
582
  export function approvedTask(root, reference, role) {
70
- const dir = resolveTaskDir(root, reference);
71
- if (!fs.statSync(dir, { throwIfNoEntry: false })?.isDirectory())
72
- throw new Error(`Task not found: ${reference}`);
73
- const { kernel } = readKernel({ taskDir: dir, cwd: root });
74
- if (kernel.phase !== "execute" || kernel.projection?.status !== "in_progress") {
75
- throw new Error("Pi dispatch requires an approved task in Kernel Execute phase");
76
- }
77
- const approval = kernel.projection.extras.execution_approval;
78
- if (!approval || typeof approval !== "object" || approval.approved_by !== "user") {
79
- throw new Error("Pi dispatch requires recorded user execution approval");
80
- }
81
- const stamp = approval;
82
- const current = kernel.projection.record;
83
- const satisfied = Array.isArray(kernel.projection.extras.dependency_satisfied)
84
- ? kernel.projection.extras.dependency_satisfied.filter((item) => typeof item === "string") : [];
85
- const unmet = unmetRequires(readDependencyGraph(kernel.projection.extras), satisfied, current.id);
86
- if (unmet.length)
87
- throw new Error(`requires unmet: ${unmet.join(", ")}`);
88
- const fingerprints = [current, { ...current, status: "planning" }]
89
- .map((candidate) => checkStartExecution(root, dir, candidate))
90
- .some((guard) => guard.contractFingerprint === stamp.contract_fingerprint && guard.artifactFingerprint === stamp.artifact_fingerprint);
91
- if (!fingerprints)
92
- throw new Error("Execution contract changed after approval; return to Define and renew approval");
583
+ const dir = approvedExecuteTask(root, reference);
93
584
  const implementation = path.join(dir, "implement.md");
94
585
  if (role === "implement" && !fs.existsSync(implementation))
95
586
  throw new Error("Pi implement dispatch requires implement.md");
@@ -115,7 +606,9 @@ export function piWorkdir(root, dir, role) {
115
606
  if (parsed.contract.isolation === "main-worktree")
116
607
  return root;
117
608
  const task = JSON.parse(fs.readFileSync(path.join(dir, "task.json"), "utf8"));
118
- const worktreePath = task && typeof task === "object" && "worktree_path" in task ? task.worktree_path : null;
609
+ const worktreePath = task && typeof task === "object" && "worktree_path" in task
610
+ ? task.worktree_path
611
+ : null;
119
612
  if (typeof worktreePath !== "string")
120
613
  throw new Error("git-worktree isolation requires a prepared Child worktree");
121
614
  const worktrees = path.resolve(root, ".pactile", "worktrees");
@@ -128,11 +621,20 @@ export function piWorkdir(root, dir, role) {
128
621
  const realRoot = fs.realpathSync(worktrees);
129
622
  const realCandidate = fs.realpathSync(candidate);
130
623
  const realRelative = path.relative(realRoot, realCandidate);
131
- if (!realRelative || realRelative.startsWith("..") || path.isAbsolute(realRelative))
624
+ if (!realRelative ||
625
+ realRelative.startsWith("..") ||
626
+ path.isAbsolute(realRelative))
132
627
  throw new Error("Child worktree resolves outside .pactile/worktrees");
133
628
  let gitRoot;
134
629
  try {
135
- gitRoot = execFileSync("git", ["-c", `safe.directory=${realCandidate.replaceAll("\\", "/")}`, "-C", realCandidate, "rev-parse", "--show-toplevel"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
630
+ gitRoot = execFileSync("git", [
631
+ "-c",
632
+ `safe.directory=${realCandidate.replaceAll("\\", "/")}`,
633
+ "-C",
634
+ realCandidate,
635
+ "rev-parse",
636
+ "--show-toplevel",
637
+ ], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
136
638
  }
137
639
  catch {
138
640
  throw new Error("Child worktree is not a usable Git checkout");
@@ -154,7 +656,7 @@ export class PiTaskBridge {
154
656
  this.root = root;
155
657
  this.launch = launch;
156
658
  }
157
- async ensureClient(dir, workdir, role, resumeFile) {
659
+ async ensureClient(dir, workdir, role, resumeFile, beforeSpawn, sessionDirOverride, startupTimeoutMs) {
158
660
  if (this.client && this.taskDir !== dir)
159
661
  throw new Error("Pi bridge is bound to one Pactile task");
160
662
  if (this.client && this.workdir !== workdir)
@@ -163,38 +665,103 @@ export class PiTaskBridge {
163
665
  throw new Error("Pi bridge cannot reuse a process across worker roles");
164
666
  if (this.client?.isStarted)
165
667
  return { client: this.client, startupMs: 0, mode: "warm" };
166
- const client = new PiRpcClient({ cwd: workdir, sessionDir: path.join(dir, "pi-bridge", "sessions"), launch: this.launch, readOnly: role !== "implement" });
668
+ const client = new PiRpcClient({
669
+ cwd: workdir,
670
+ sessionDir: sessionDirOverride ?? path.join(dir, "pi-bridge", "sessions"),
671
+ launch: this.launch,
672
+ readOnly: role !== "implement",
673
+ });
167
674
  this.client = client;
168
675
  this.taskDir = dir;
169
676
  this.workdir = workdir;
170
677
  this.role = role;
171
- const startupMs = await client.start();
678
+ const startupMs = await client.start(beforeSpawn, startupTimeoutMs);
172
679
  if (resumeFile)
173
680
  await client.switchSession(resumeFile);
174
681
  return { client, startupMs, mode: "cold" };
175
682
  }
683
+ async closeForDispatch() {
684
+ const client = this.client;
685
+ if (!client)
686
+ return null;
687
+ const receipt = await client.closeAndObserve();
688
+ if (receipt?.terminationVerified) {
689
+ this.client = null;
690
+ this.taskDir = null;
691
+ this.workdir = null;
692
+ this.role = null;
693
+ }
694
+ return receipt;
695
+ }
696
+ async closeForReview() {
697
+ const client = this.client;
698
+ if (!client)
699
+ return null;
700
+ const receipt = await client.closeAndObserve();
701
+ if (receipt?.terminationVerified) {
702
+ this.client = null;
703
+ this.taskDir = null;
704
+ this.workdir = null;
705
+ this.role = null;
706
+ }
707
+ return receipt;
708
+ }
176
709
  async run(input) {
177
710
  if (path.resolve(input.root) !== path.resolve(this.root))
178
711
  throw new Error("Pi bridge root mismatch");
179
712
  if (!input.prompt.trim())
180
713
  throw new Error("Pi prompt is empty");
181
- if (!Number.isInteger(input.timeoutMs) || input.timeoutMs < 1_000 || input.timeoutMs > 86_400_000)
714
+ if (!Number.isInteger(input.timeoutMs) ||
715
+ input.timeoutMs < 1_000 ||
716
+ input.timeoutMs > 86_400_000)
182
717
  throw new Error("Pi timeout must be 1 second to 24 hours");
183
718
  if (this.locked)
184
719
  throw new Error("Pi bridge already has an active run");
720
+ if (input.runId !== undefined) {
721
+ if (input.role !== "implement")
722
+ throw new Error("Pi V2 dispatch currently supports only the implement role");
723
+ if (input.resume)
724
+ throw new Error("Pi V2 dispatch cannot resume a prior session");
725
+ if (this.client)
726
+ throw new Error("Pi V2 dispatch requires a fresh Pi bridge process");
727
+ if (!input.runId.trim())
728
+ throw new Error("Pi V2 --run-id must be non-empty");
729
+ const dispatch = preparePiV2RunDispatch(this.root, input.task, input.runId, input.scheduleReceiptFingerprint);
730
+ return this.runApproved(input, dispatch.taskDir, dispatch.workdir, null, null, dispatch, null);
731
+ }
732
+ if (input.role === "check") {
733
+ if (!input.reviewBinding) {
734
+ throw new Error("Pi Check requires an independent Review binding");
735
+ }
736
+ if (input.runId || input.resume) {
737
+ throw new Error("Pi Review Check cannot resume or reuse an execution Run");
738
+ }
739
+ if (this.client) {
740
+ throw new Error("Pi Review Check requires a fresh independent Pi session");
741
+ }
742
+ const review = preparePiReviewRoute(this.root, input.task);
743
+ assertSameReviewBinding(review.binding, input.reviewBinding);
744
+ return this.runApproved(input, review.taskDir, review.workdir, null, null, null, review);
745
+ }
746
+ if (input.reviewBinding) {
747
+ throw new Error("Pi Review binding is only valid for the Check role");
748
+ }
185
749
  const dir = approvedTask(this.root, input.task, input.role);
186
750
  const workdir = piWorkdir(this.root, dir, input.role);
187
751
  const scope = parallelChild(this.root, dir);
188
752
  const leaseId = randomUUID();
189
- const release = reserveParallelChild(this.root, dir, { id: leaseId });
753
+ const release = reserveParallelChild(this.root, dir, {
754
+ id: leaseId,
755
+ scheduleReceiptFingerprint: input.scheduleReceiptFingerprint,
756
+ });
190
757
  try {
191
- return await this.runApproved(input, dir, workdir, scope?.touches ?? null, leaseId);
758
+ return await this.runApproved(input, dir, workdir, scope?.touches ?? null, leaseId, null, null);
192
759
  }
193
760
  finally {
194
761
  release();
195
762
  }
196
763
  }
197
- async runApproved(input, dir, workdir, touches, leaseId) {
764
+ async runApproved(input, dir, workdir, touches, parallelLeaseId, dispatch, review) {
198
765
  const evidence = path.join(dir, "pi-bridge");
199
766
  const lockFile = path.join(evidence, "active.json");
200
767
  const latestFile = path.join(evidence, "latest.json");
@@ -206,7 +773,12 @@ export class PiTaskBridge {
206
773
  throw new Error("Pi dispatch already active; stop the existing bridge before retrying");
207
774
  const stale = parseJson(latestFile);
208
775
  if (stale?.outcome === "running") {
209
- const recovered = { ...stale, outcome: "interrupted", ended_at: new Date().toISOString(), reason: "bridge process exited before recording a terminal event" };
776
+ const recovered = {
777
+ ...stale,
778
+ outcome: "interrupted",
779
+ ended_at: new Date().toISOString(),
780
+ reason: "bridge process exited before recording a terminal event",
781
+ };
210
782
  atomicJson(path.join(evidence, "runs", `${stale.run_id}.json`), recovered);
211
783
  atomicJson(latestFile, recovered);
212
784
  }
@@ -215,37 +787,142 @@ export class PiTaskBridge {
215
787
  const orphan = parseJson(latestFile);
216
788
  if (orphan?.outcome === "running") {
217
789
  const stale = orphan;
218
- const recovered = { ...stale, outcome: "interrupted", ended_at: new Date().toISOString(), reason: "bridge process exited before recording a terminal event" };
790
+ const recovered = {
791
+ ...stale,
792
+ outcome: "interrupted",
793
+ ended_at: new Date().toISOString(),
794
+ reason: "bridge process exited before recording a terminal event",
795
+ };
219
796
  atomicJson(path.join(evidence, "runs", `${stale.run_id}.json`), recovered);
220
797
  atomicJson(latestFile, recovered);
221
798
  }
222
799
  const previous = parseJson(latestFile);
223
800
  const sessionRoot = path.resolve(evidence, "sessions");
224
- const previousSession = typeof previous?.session_file === "string" ? path.resolve(previous.session_file) : null;
225
- const previousSessionReal = previousSession && fs.statSync(previousSession, { throwIfNoEntry: false })?.isFile() ? fs.realpathSync(previousSession) : null;
226
- const sessionRootReal = fs.existsSync(sessionRoot) ? fs.realpathSync(sessionRoot) : null;
227
- const relativeSession = previousSessionReal && sessionRootReal ? path.relative(sessionRootReal, previousSessionReal) : null;
228
- const insideSessions = relativeSession && relativeSession !== ".." && !relativeSession.startsWith(`..${path.sep}`) && !path.isAbsolute(relativeSession);
229
- if (input.resume && !(previous?.role === input.role && insideSessions && previousSession && fs.statSync(previousSession, { throwIfNoEntry: false })?.isFile())) {
801
+ const previousSession = typeof previous?.session_file === "string"
802
+ ? path.resolve(previous.session_file)
803
+ : null;
804
+ const previousSessionReal = previousSession &&
805
+ fs.statSync(previousSession, { throwIfNoEntry: false })?.isFile()
806
+ ? fs.realpathSync(previousSession)
807
+ : null;
808
+ const sessionRootReal = fs.existsSync(sessionRoot)
809
+ ? fs.realpathSync(sessionRoot)
810
+ : null;
811
+ const relativeSession = previousSessionReal && sessionRootReal
812
+ ? path.relative(sessionRootReal, previousSessionReal)
813
+ : null;
814
+ const insideSessions = relativeSession &&
815
+ relativeSession !== ".." &&
816
+ !relativeSession.startsWith(`..${path.sep}`) &&
817
+ !path.isAbsolute(relativeSession);
818
+ if (input.resume &&
819
+ !(previous?.role === input.role &&
820
+ insideSessions &&
821
+ previousSession &&
822
+ fs.statSync(previousSession, { throwIfNoEntry: false })?.isFile())) {
230
823
  throw new Error("No previous Pi session is available to resume");
231
824
  }
232
825
  const runId = randomUUID();
826
+ const startRequestId = dispatch || review ? randomUUID() : null;
233
827
  const now = new Date();
828
+ const runFile = path.join(evidence, "runs", `${runId}.json`);
829
+ const eventFile = path.join(evidence, "events", `${runId}.jsonl`);
830
+ const runReceiptRef = path.relative(dir, runFile).replaceAll("\\", "/");
831
+ const progressEvidenceRef = path
832
+ .relative(dir, eventFile)
833
+ .replaceAll("\\", "/");
834
+ const startReceiptRef = `pi-bridge/starts/${runId}.json`;
835
+ const reviewStopReceiptRef = `pi-bridge/stops/${runId}.json`;
836
+ const reviewAttemptsRef = `pi-bridge/review-attempts/${runId}.jsonl`;
837
+ const reviewSessionEvidenceId = review
838
+ ? `pirc-${createHash("sha256").update(runId).digest("hex").slice(0, 32)}`
839
+ : null;
234
840
  const record = {
235
- schema_version: 1, run_id: runId, task: path.relative(this.root, dir).replaceAll("\\", "/"), role: input.role,
236
- outcome: "running", started_at: now.toISOString(), ended_at: null,
841
+ schema_version: dispatch ? 2 : 1,
842
+ run_id: runId,
843
+ task: path.relative(this.root, dir).replaceAll("\\", "/"),
844
+ role: input.role,
845
+ outcome: "running",
846
+ started_at: now.toISOString(),
847
+ ended_at: null,
237
848
  prompt_sha256: createHash("sha256").update(input.prompt).digest("hex"),
238
- session_id: null, session_file: null, process_mode: "cold", startup_ms: 0, first_event_ms: null,
239
- elapsed_ms: null, event_count: 0, tool_errors: 0, reason: null, result_file: null,
849
+ session_id: null,
850
+ session_file: null,
851
+ process_mode: "cold",
852
+ startup_ms: 0,
853
+ first_event_ms: null,
854
+ elapsed_ms: null,
855
+ event_count: 0,
856
+ tool_errors: 0,
857
+ reason: null,
858
+ result_file: null,
859
+ ...(review
860
+ ? {
861
+ task_id: review.run.taskId,
862
+ task_run_id: review.run.id,
863
+ candidate_snapshot_id: review.run.candidateSnapshot?.id ?? null,
864
+ candidate_fingerprint: review.run.candidateSnapshot?.fingerprint ?? null,
865
+ kernel_revision_at_dispatch: review.kernel.revision,
866
+ reviewer_identity_assurance: "caller-declared",
867
+ start_request_id: startRequestId,
868
+ process_id: null,
869
+ progress_evidence_ref: progressEvidenceRef,
870
+ result_sha256: null,
871
+ result_redacted: false,
872
+ review_attempts_ref: reviewAttemptsRef,
873
+ review_attempts_sha256: null,
874
+ review_format_correction: null,
875
+ review_status: "pending",
876
+ review_start_receipt_ref: null,
877
+ review_stop_receipt_ref: null,
878
+ review_file: null,
879
+ kernel_review_id: null,
880
+ codex_escalation: null,
881
+ codex_escalation_request_ref: null,
882
+ codex_escalation_request_status: "pending",
883
+ }
884
+ : {}),
885
+ ...(dispatch
886
+ ? {
887
+ task_id: dispatch.taskId,
888
+ task_run_id: dispatch.runId,
889
+ task_host_id: "pi",
890
+ start_request_id: startRequestId,
891
+ process_id: null,
892
+ host_start_receipt_ref: null,
893
+ progress_evidence_ref: progressEvidenceRef,
894
+ settle_receipt_id: null,
895
+ process_stop_receipt: null,
896
+ process_stop_error: null,
897
+ process_exit_receipt: null,
898
+ result_sha256: null,
899
+ result_redacted: false,
900
+ dispatch_lease_id: dispatch.leaseId,
901
+ schedule_receipt_fingerprint: dispatch.scheduleReceiptFingerprint,
902
+ admission_receipt_fingerprint: dispatch.admissionReceiptFingerprint,
903
+ dispatch_stop_proof_ref: null,
904
+ dispatch_lease_released: false,
905
+ dispatch_lease_release_reason: null,
906
+ }
907
+ : {}),
240
908
  };
241
- const runFile = path.join(evidence, "runs", `${runId}.json`);
242
- const eventFile = path.join(evidence, "events", `${runId}.jsonl`);
243
909
  fs.mkdirSync(path.dirname(eventFile), { recursive: true });
910
+ if (dispatch || review)
911
+ fs.writeFileSync(eventFile, "", { flag: "wx", mode: 0o600 });
912
+ if (review) {
913
+ const attemptsFile = path.join(dir, reviewAttemptsRef);
914
+ fs.mkdirSync(path.dirname(attemptsFile), { recursive: true });
915
+ fs.writeFileSync(attemptsFile, "", { flag: "wx", mode: 0o600 });
916
+ }
244
917
  fs.writeFileSync(lockFile, JSON.stringify({ parent_pid: process.pid, run_id: runId }), { flag: "wx", mode: 0o600 });
245
918
  this.locked = true;
246
919
  atomicJson(runFile, record);
247
920
  atomicJson(latestFile, record);
248
921
  const started = performance.now();
922
+ const checkDeadlineAt = started + input.timeoutMs;
923
+ let activeReviewSessionId = null;
924
+ const remainingCheckMs = () => Math.floor(checkDeadlineAt - performance.now());
925
+ const reviewResponseEvidence = [];
249
926
  let detach = () => undefined;
250
927
  let closeFailed = false;
251
928
  const controller = new AbortController();
@@ -254,78 +931,570 @@ export class PiTaskBridge {
254
931
  const onAbort = () => controller.abort();
255
932
  input.signal?.addEventListener("abort", onAbort, { once: true });
256
933
  const pollCancel = setInterval(() => {
257
- if (parseJson(cancelFile)?.run_id === runId)
934
+ const cancellation = parseJson(cancelFile);
935
+ if (cancellation?.run_id === runId) {
936
+ if (review && typeof cancellation.request_id === "string") {
937
+ record.cancellation_request_id = cancellation.request_id;
938
+ atomicJson(runFile, record);
939
+ atomicJson(latestFile, record);
940
+ }
258
941
  controller.abort();
942
+ }
259
943
  }, 200);
944
+ const checkCancellationNow = (message) => {
945
+ const cancellation = parseJson(cancelFile);
946
+ if (cancellation?.run_id === runId) {
947
+ if (review && typeof cancellation.request_id === "string") {
948
+ record.cancellation_request_id = cancellation.request_id;
949
+ atomicJson(runFile, record);
950
+ atomicJson(latestFile, record);
951
+ }
952
+ controller.abort();
953
+ }
954
+ if (controller.signal.aborted)
955
+ throw new Error(message);
956
+ };
260
957
  try {
261
958
  const resumeFile = input.resume ? previousSession : null;
262
- const { client, startupMs, mode } = await this.ensureClient(dir, workdir, input.role, resumeFile);
263
- updateParallelChildPid(this.root, dir, leaseId, client.pid);
264
- fs.writeFileSync(lockFile, JSON.stringify({ parent_pid: process.pid, child_pid: client.pid, run_id: runId }), "utf8");
959
+ if (dispatch) {
960
+ recheckPiV2RunDispatchWorkspace(dispatch);
961
+ }
962
+ const startupTimeoutMs = review ? remainingCheckMs() : undefined;
963
+ if (review && (startupTimeoutMs ?? 0) <= 0)
964
+ throw new Error("Pi Check timeout exhausted before startup");
965
+ const { client, startupMs, mode } = await this.ensureClient(dir, workdir, input.role, resumeFile, dispatch
966
+ ? () => {
967
+ const verifiedWorkdir = recheckPiV2RunDispatchBeforeSpawn(dispatch);
968
+ if (verifiedWorkdir !== workdir) {
969
+ throw new Error("Pi V2 Run worktree changed immediately before process start");
970
+ }
971
+ }
972
+ : undefined, review ? path.join(evidence, "review-sessions", runId) : undefined, startupTimeoutMs);
973
+ if (parallelLeaseId)
974
+ updateParallelChildPid(this.root, dir, parallelLeaseId, client.pid);
975
+ fs.writeFileSync(lockFile, JSON.stringify({
976
+ parent_pid: process.pid,
977
+ child_pid: client.pid,
978
+ run_id: runId,
979
+ }), "utf8");
265
980
  record.process_mode = mode;
266
981
  record.startup_ms = startupMs;
267
- const state = await client.state();
268
- record.session_id = typeof state.sessionId === "string" ? state.sessionId : null;
269
- record.session_file = typeof state.sessionFile === "string" ? state.sessionFile : null;
982
+ const initialStateBudgetMs = review ? remainingCheckMs() : undefined;
983
+ if (review && (initialStateBudgetMs ?? 0) <= 0)
984
+ throw new Error("Pi Check timeout exhausted before session verification");
985
+ const state = await client.state(initialStateBudgetMs);
986
+ if (review && remainingCheckMs() <= 0)
987
+ throw new Error("Pi Check timeout exhausted during session verification");
988
+ activeReviewSessionId =
989
+ typeof state.sessionId === "string" && state.sessionId.length > 0
990
+ ? state.sessionId
991
+ : null;
992
+ record.session_id = review
993
+ ? activeReviewSessionId
994
+ ? reviewSessionEvidenceId
995
+ : null
996
+ : typeof state.sessionId === "string"
997
+ ? state.sessionId
998
+ : null;
999
+ record.session_file = review
1000
+ ? null
1001
+ : typeof state.sessionFile === "string"
1002
+ ? state.sessionFile
1003
+ : null;
270
1004
  atomicJson(runFile, record);
271
1005
  atomicJson(latestFile, record);
1006
+ if (review) {
1007
+ if (!startRequestId ||
1008
+ !record.session_id ||
1009
+ !Number.isSafeInteger(client.pid) ||
1010
+ !client.pid) {
1011
+ throw new Error("Pi Review Check start did not provide process and session identities");
1012
+ }
1013
+ if (activeReviewSessionId === review.run.host?.sessionId) {
1014
+ throw new Error("Pi Check session matches the implementation Run host session");
1015
+ }
1016
+ const currentReview = preparePiReviewRoute(this.root, review.run.taskId);
1017
+ assertSameReviewBinding(currentReview.binding, review.binding);
1018
+ const startReceipt = {
1019
+ schemaVersion: 1,
1020
+ source: "pactile-pi-review",
1021
+ taskId: review.run.taskId,
1022
+ taskRunId: review.run.id,
1023
+ piRunId: runId,
1024
+ role: "check",
1025
+ sessionId: record.session_id,
1026
+ processId: client.pid,
1027
+ startRequestId,
1028
+ kernelRevision: review.kernel.revision,
1029
+ candidateSnapshotId: review.binding.candidateSnapshotId,
1030
+ candidateFingerprint: review.binding.candidateFingerprint,
1031
+ progressEvidenceRef,
1032
+ evidenceRef: runReceiptRef,
1033
+ recordedAt: new Date().toISOString(),
1034
+ };
1035
+ writeExclusiveJson(path.join(dir, startReceiptRef), startReceipt);
1036
+ record.process_id = client.pid;
1037
+ record.start_request_id = startRequestId;
1038
+ record.reviewer_id = `pi-session:${record.session_id}`;
1039
+ record.review_start_receipt_ref = startReceiptRef;
1040
+ atomicJson(runFile, record);
1041
+ atomicJson(latestFile, record);
1042
+ }
1043
+ if (dispatch) {
1044
+ if (!startRequestId ||
1045
+ !record.session_id ||
1046
+ !Number.isSafeInteger(client.pid) ||
1047
+ !client.pid) {
1048
+ throw new Error("Pi V2 host start did not provide a process and session identity");
1049
+ }
1050
+ const startReceipt = {
1051
+ schemaVersion: 1,
1052
+ source: "pactile-pi-rpc",
1053
+ taskId: dispatch.taskId,
1054
+ taskRunId: dispatch.runId,
1055
+ piRunId: runId,
1056
+ role: "implement",
1057
+ sessionId: record.session_id,
1058
+ processId: client.pid,
1059
+ startRequestId,
1060
+ progressEvidenceRef,
1061
+ evidenceRef: runReceiptRef,
1062
+ recordedAt: new Date().toISOString(),
1063
+ };
1064
+ const startFile = path.join(dir, startReceiptRef);
1065
+ fs.mkdirSync(path.dirname(startFile), { recursive: true });
1066
+ fs.writeFileSync(startFile, `${JSON.stringify(startReceipt, null, 2)}\n`, {
1067
+ encoding: "utf8",
1068
+ flag: "wx",
1069
+ mode: 0o600,
1070
+ });
1071
+ record.process_id = client.pid;
1072
+ record.host_start_receipt_ref = startReceiptRef;
1073
+ atomicJson(runFile, record);
1074
+ atomicJson(latestFile, record);
1075
+ bindPiV2RunHost(dispatch, {
1076
+ piRunId: runId,
1077
+ startRequestId,
1078
+ sessionId: record.session_id,
1079
+ processId: client.pid,
1080
+ progressEvidenceRef,
1081
+ });
1082
+ }
272
1083
  detach = client.onEvent((event) => {
273
1084
  if (event.type === "transport_error")
274
1085
  return;
275
1086
  record.event_count += 1;
276
1087
  if (event.type === "tool_execution_end" && event.isError === true)
277
1088
  record.tool_errors += 1;
278
- if (event.type === "extension_error" || (event.type === "auto_retry_end" && event.success === false))
1089
+ if (event.type === "extension_error" ||
1090
+ (event.type === "auto_retry_end" && event.success === false))
279
1091
  record.tool_errors += 1;
280
1092
  const summary = evidenceEvent(event);
281
- fs.appendFileSync(eventFile, `${JSON.stringify(summary)}\n`, { encoding: "utf8", mode: 0o600 });
1093
+ fs.appendFileSync(eventFile, `${JSON.stringify(summary)}\n`, {
1094
+ encoding: "utf8",
1095
+ mode: 0o600,
1096
+ });
282
1097
  try {
283
1098
  input.onProgress?.(summary);
284
1099
  }
285
- catch { /* Observers cannot change the run outcome. */ }
1100
+ catch {
1101
+ /* Observers cannot change the run outcome. */
1102
+ }
286
1103
  });
287
1104
  const taskPath = dir;
288
1105
  const contractPath = path.join(dir, "implement.md");
289
- const contractExcerpt = fs.existsSync(contractPath) ? fs.readFileSync(contractPath, "utf8").slice(0, 1_200) : "(no implement.md)";
290
- const instructions = [
291
- `Pactile task: ${taskPath}`,
292
- `Execution worktree: ${workdir}`,
293
- `Role: ${input.role}`,
294
- `Definition: ${taskPath}/prd.md`,
295
- `Evidence: ${taskPath}/verify.md`,
296
- "Approved execution contract excerpt:", contractExcerpt,
297
- "Follow the approved task contract and its write set. Do not commit, archive, finalize, or mutate Pactile Kernel state. Report evidence and unresolved issues. Do not include credentials in the final answer.",
298
- touches ? `Parent-declared write set: ${touches.join(", ")}. Do not change files outside it. Parent owns integration.` : "",
299
- input.role === "implement" ? "Implementation may change files only inside the approved write set." : "This role is read-only; do not change files.",
300
- "Worker assignment:", input.prompt,
301
- ].join("\n\n");
302
- const result = await client.prompt(instructions, input.timeoutMs, controller.signal);
303
- record.first_event_ms = result.firstEventMs;
304
- const { text, stopReason, errorMessage } = assistantText(result.event);
305
- record.outcome = stopReason === "stop" && text && !record.tool_errors ? "settled" : "needs_review";
306
- if (stopReason && stopReason !== "stop")
307
- record.reason = `Pi stopReason=${stopReason}${errorMessage ? `; ${errorMessage}` : ""}`;
1106
+ const contractExcerpt = fs.existsSync(contractPath)
1107
+ ? fs.readFileSync(contractPath, "utf8").slice(0, 1_200)
1108
+ : "(no implement.md)";
1109
+ const effectiveTouches = touches ??
1110
+ (dispatch
1111
+ ? [
1112
+ ...new Set([
1113
+ ...dispatch.run.writeSetSnapshot,
1114
+ ...(dispatch.run.workspace?.writeSet ?? []),
1115
+ ]),
1116
+ ]
1117
+ : null);
1118
+ const writeSetLabel = dispatch
1119
+ ? "Task Run write set"
1120
+ : "Parent-declared write set";
1121
+ const instructions = review
1122
+ ? [
1123
+ "Task-specific Review instructions:",
1124
+ fs.readFileSync(path.join(dir, "verify.md"), "utf8"),
1125
+ "Additional caller instructions:",
1126
+ input.prompt,
1127
+ buildIndependentPiReviewPrompt(review.binding),
1128
+ ].join("\n\n")
1129
+ : [
1130
+ `Pactile task: ${taskPath}`,
1131
+ `Execution worktree: ${workdir}`,
1132
+ `Role: ${input.role}`,
1133
+ `Definition: ${taskPath}/prd.md`,
1134
+ `Evidence: ${taskPath}/verify.md`,
1135
+ "Approved execution contract excerpt:",
1136
+ contractExcerpt,
1137
+ "Follow the approved task contract and its write set. Do not commit, archive, finalize, or mutate Pactile Kernel state. Report evidence and unresolved issues. Do not include credentials in the final answer.",
1138
+ effectiveTouches?.length
1139
+ ? `${writeSetLabel}: ${effectiveTouches.join(", ")}. Do not change files outside it.`
1140
+ : "",
1141
+ input.role === "implement"
1142
+ ? "Implementation may change files only inside the approved write set."
1143
+ : "This role is read-only; do not change files.",
1144
+ "Worker assignment:",
1145
+ input.prompt,
1146
+ ].join("\n\n");
308
1147
  const resultFile = path.join(evidence, "results", `${runId}.md`);
309
1148
  fs.mkdirSync(path.dirname(resultFile), { recursive: true });
310
- fs.writeFileSync(resultFile, `${text || "(Pi returned no assistant text.)"}\n`, { encoding: "utf8", mode: 0o600 });
311
1149
  record.result_file = path.relative(dir, resultFile).replaceAll("\\", "/");
1150
+ const attemptsFile = review ? path.join(dir, reviewAttemptsRef) : null;
1151
+ const appendAttempt = (summary) => {
1152
+ if (!attemptsFile)
1153
+ return;
1154
+ fs.appendFileSync(attemptsFile, `${JSON.stringify(summary)}\n`, {
1155
+ encoding: "utf8",
1156
+ mode: 0o600,
1157
+ });
1158
+ };
1159
+ const persistReviewResponse = (attempt, response) => {
1160
+ if (!review)
1161
+ return null;
1162
+ const bytes = Buffer.from(response.text, "utf8");
1163
+ if (bytes.byteLength > MAX_PI_REVIEW_RESPONSE_BYTES)
1164
+ return null;
1165
+ const ref = `pi-bridge/review-responses/${runId}-attempt-${attempt}.txt`;
1166
+ const file = path.join(dir, ref);
1167
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1168
+ fs.writeFileSync(file, bytes, { flag: "wx", mode: 0o600 });
1169
+ const stored = {
1170
+ ref,
1171
+ sha256: createHash("sha256").update(bytes).digest("hex"),
1172
+ sizeBytes: bytes.byteLength,
1173
+ };
1174
+ reviewResponseEvidence.push(stored);
1175
+ return stored;
1176
+ };
1177
+ let text = "";
1178
+ let redacted = false;
1179
+ const persistResult = () => {
1180
+ const responseTooLarge = review !== null &&
1181
+ Buffer.byteLength(text, "utf8") > MAX_PI_REVIEW_RESPONSE_BYTES;
1182
+ const resultText = responseTooLarge
1183
+ ? "(Pi Check response omitted because it exceeded the bounded evidence limit.)"
1184
+ : text || "(Pi returned no assistant text.)";
1185
+ fs.writeFileSync(resultFile, `${resultText}\n`, { encoding: "utf8", mode: 0o600 });
1186
+ if (dispatch || review) {
1187
+ const resultBytes = fs.readFileSync(resultFile);
1188
+ record.result_sha256 = createHash("sha256")
1189
+ .update(resultBytes)
1190
+ .digest("hex");
1191
+ record.result_redacted = redacted || responseTooLarge;
1192
+ }
1193
+ };
1194
+ const firstPromptStarted = performance.now();
1195
+ const firstPromptRemainingMs = remainingCheckMs();
1196
+ if (review && firstPromptRemainingMs <= 0)
1197
+ throw new Error("Pi Check timeout exhausted before first prompt");
1198
+ const result = await client.prompt(instructions, review ? firstPromptRemainingMs : input.timeoutMs, controller.signal, undefined, review ? checkDeadlineAt : undefined, review
1199
+ ? () => checkCancellationNow("Pi Check cancelled before prompt dispatch")
1200
+ : undefined);
1201
+ record.first_event_ms = result.firstEventMs;
1202
+ let response = assistantText(result.event);
1203
+ const firstResponseEvidence = review
1204
+ ? persistReviewResponse(1, response)
1205
+ : null;
1206
+ text = response.text;
1207
+ redacted = response.redacted;
1208
+ record.outcome =
1209
+ response.stopReason === "stop" && text && !record.tool_errors
1210
+ ? "settled"
1211
+ : "needs_review";
1212
+ if (response.stopReason && response.stopReason !== "stop")
1213
+ record.reason = `Pi stopReason=${response.stopReason}${response.errorMessage ? `; ${response.errorMessage}` : ""}`;
1214
+ if (review) {
1215
+ appendAttempt(reviewAttemptSummary(1, "initial", firstPromptStarted, response, result.firstEventMs, undefined, firstResponseEvidence?.ref ?? null));
1216
+ record.review_format_correction = {
1217
+ attempted: false,
1218
+ outcome: "not-needed",
1219
+ };
1220
+ }
1221
+ persistResult();
1222
+ if (review && !firstResponseEvidence) {
1223
+ record.review_format_correction = {
1224
+ attempted: false,
1225
+ outcome: "failed",
1226
+ };
1227
+ throw new Error("Pi Check response exceeds the bounded Review evidence limit");
1228
+ }
1229
+ if (review &&
1230
+ record.outcome === "settled" &&
1231
+ !isStructuredJsonObject(response.rawText)) {
1232
+ record.review_format_correction = {
1233
+ attempted: false,
1234
+ outcome: "failed",
1235
+ };
1236
+ const currentReview = preparePiReviewRoute(this.root, review.run.taskId);
1237
+ assertSameReviewBinding(currentReview.binding, review.binding);
1238
+ resolvePiReviewEvidenceV1({
1239
+ root: this.root,
1240
+ taskDir: currentReview.taskDir,
1241
+ candidateRoot: currentReview.workdir,
1242
+ run: currentReview.run,
1243
+ references: [],
1244
+ });
1245
+ checkCancellationNow("Pi run cancelled before format correction");
1246
+ const preflightBudgetMs = remainingCheckMs();
1247
+ if (preflightBudgetMs <= 0)
1248
+ throw new Error("Pi Review format correction exceeded the Check timeout");
1249
+ let correctionPromptInvoked = false;
1250
+ let correctionStarted = performance.now();
1251
+ let correctionResponse;
1252
+ try {
1253
+ const beforeCorrection = await client.state(preflightBudgetMs);
1254
+ if (remainingCheckMs() <= 0)
1255
+ throw new Error("Pi Review format correction exceeded the Check timeout");
1256
+ if (beforeCorrection.sessionId !== activeReviewSessionId)
1257
+ throw new Error("Pi Review Check session changed before format correction");
1258
+ checkCancellationNow("Pi run cancelled before format correction");
1259
+ const correctionPrompt = [
1260
+ "Follow the complete original Check instructions, including Task-specific Review instructions, caller instructions, and Review/Close contract. Reuse the exact original instructions below.",
1261
+ instructions,
1262
+ "Format correction for the same independent Pi Check session.",
1263
+ "Your previous settled answer was not one structured JSON object.",
1264
+ "Return exactly one JSON object matching the Review contract below.",
1265
+ "Do not include prose, Markdown fences, prefixes, suffixes, or a JSON substring embedded in text.",
1266
+ "Keep the same frozen Task Run, candidate snapshot, reviewer identity, evidence boundary, and Review/Close guards.",
1267
+ "Use only the evidence already bound to this Check. Do not call tools or assume tools are available; if the bound evidence is insufficient, return needs-changes.",
1268
+ ].join("\n\n");
1269
+ const promptBudgetMs = remainingCheckMs();
1270
+ if (promptBudgetMs <= 0)
1271
+ throw new Error("Pi Review format correction exceeded the Check timeout");
1272
+ const correction = await client.prompt(correctionPrompt, promptBudgetMs, controller.signal, () => {
1273
+ correctionPromptInvoked = true;
1274
+ }, checkDeadlineAt, () => {
1275
+ checkCancellationNow("Pi run cancelled before format correction");
1276
+ correctionStarted = performance.now();
1277
+ });
1278
+ correctionResponse = assistantText(correction.event);
1279
+ const correctionResponseEvidence = persistReviewResponse(2, correctionResponse);
1280
+ const finalStateBudgetMs = remainingCheckMs();
1281
+ if (finalStateBudgetMs <= 0)
1282
+ throw new Error("Pi Review format correction exceeded the Check timeout");
1283
+ const afterCorrection = await client.state(finalStateBudgetMs);
1284
+ if (remainingCheckMs() <= 0)
1285
+ throw new Error("Pi Review format correction exceeded the Check timeout");
1286
+ if (afterCorrection.sessionId !== activeReviewSessionId)
1287
+ throw new Error("Pi Review Check session changed after format correction");
1288
+ response = correctionResponse;
1289
+ text = response.text;
1290
+ redacted = response.redacted;
1291
+ appendAttempt(reviewAttemptSummary(2, "format-correction", correctionStarted, response, correction.firstEventMs, undefined, correctionResponseEvidence?.ref ?? null));
1292
+ record.outcome =
1293
+ response.stopReason === "stop" && text && !record.tool_errors
1294
+ ? "settled"
1295
+ : "needs_review";
1296
+ if (response.stopReason && response.stopReason !== "stop")
1297
+ record.reason = `Pi stopReason=${response.stopReason}${response.errorMessage ? `; ${response.errorMessage}` : ""}`;
1298
+ record.review_format_correction = {
1299
+ attempted: true,
1300
+ outcome: isStructuredJsonObject(response.rawText)
1301
+ ? "accepted"
1302
+ : "rejected",
1303
+ };
1304
+ persistResult();
1305
+ }
1306
+ catch (error) {
1307
+ const correctionError = error instanceof Error ? error.message : String(error);
1308
+ record.review_format_correction = {
1309
+ attempted: correctionPromptInvoked,
1310
+ outcome: controller.signal.aborted || correctionError.includes("cancelled")
1311
+ ? "cancelled"
1312
+ : correctionError.includes("timed out")
1313
+ ? correctionPromptInvoked
1314
+ ? "timed-out"
1315
+ : "deadline-exceeded"
1316
+ : "failed",
1317
+ };
1318
+ if (correctionPromptInvoked) {
1319
+ appendAttempt(reviewAttemptSummary(2, "format-correction", correctionStarted, correctionResponse
1320
+ ? {
1321
+ ...correctionResponse,
1322
+ errorMessage: safeErrorSummary(correctionError),
1323
+ }
1324
+ : undefined, null, correctionResponse ? undefined : correctionError, reviewResponseEvidence.find((item) => item.ref ===
1325
+ `pi-bridge/review-responses/${runId}-attempt-2.txt`)?.ref ?? null));
1326
+ }
1327
+ throw error;
1328
+ }
1329
+ }
312
1330
  }
313
1331
  catch (error) {
314
1332
  const reason = error instanceof Error ? error.message : String(error);
315
- record.outcome = controller.signal.aborted || reason.includes("cancelled") ? "cancelled" : reason.includes("timed out") ? "timed_out" : reason.includes("exited") ? "interrupted" : "failed";
316
- record.reason = redact(reason);
317
- try {
318
- await this.close();
319
- }
320
- catch (closeError) {
321
- closeFailed = true;
322
- record.reason = `${record.reason}; Pi termination failed: ${redact(closeError instanceof Error ? closeError.message : String(closeError))}`;
1333
+ record.outcome =
1334
+ controller.signal.aborted || reason.includes("cancelled")
1335
+ ? "cancelled"
1336
+ : reason.includes("timed out")
1337
+ ? "timed_out"
1338
+ : reason.includes("exited")
1339
+ ? "interrupted"
1340
+ : "failed";
1341
+ record.reason = review ? safeErrorSummary(reason) : safeProviderMessage(reason);
1342
+ if (!dispatch && !review) {
1343
+ try {
1344
+ await this.close();
1345
+ }
1346
+ catch (closeError) {
1347
+ closeFailed = true;
1348
+ record.reason = `${record.reason}; Pi termination failed: ${safeProviderMessage(closeError instanceof Error ? closeError.message : String(closeError))}`;
1349
+ }
323
1350
  }
324
1351
  }
325
1352
  finally {
326
1353
  detach();
327
1354
  clearInterval(pollCancel);
328
1355
  input.signal?.removeEventListener("abort", onAbort);
1356
+ let processExit = null;
1357
+ let reviewProcessExit = null;
1358
+ if (review) {
1359
+ try {
1360
+ reviewProcessExit = (await this.closeForReview()) ?? null;
1361
+ }
1362
+ catch (closeError) {
1363
+ closeFailed = true;
1364
+ const closeReason = closeError instanceof Error ? closeError.message : String(closeError);
1365
+ record.process_stop_error = review
1366
+ ? safeErrorSummary(closeReason)
1367
+ : safeProviderMessage(closeReason);
1368
+ }
1369
+ if (record.result_sha256 === null) {
1370
+ // A cancelled Check may stop before the first response is persisted.
1371
+ record.result_file = null;
1372
+ }
1373
+ if (reviewProcessExit?.terminationVerified &&
1374
+ reviewProcessExit.exitObservedAt &&
1375
+ startRequestId &&
1376
+ record.session_id &&
1377
+ Number.isSafeInteger(record.process_id) &&
1378
+ record.process_id) {
1379
+ if (record.outcome === "settled" &&
1380
+ (reviewProcessExit.exitCode !== 0 ||
1381
+ reviewProcessExit.signalCode !== null)) {
1382
+ record.outcome = "interrupted";
1383
+ record.reason =
1384
+ "Pi reported a settled Review response but its manager-owned process exited abnormally";
1385
+ }
1386
+ const reviewAttemptsBytes = fs.readFileSync(path.join(dir, reviewAttemptsRef));
1387
+ const reviewAttemptsSha256 = createHash("sha256")
1388
+ .update(reviewAttemptsBytes)
1389
+ .digest("hex");
1390
+ record.review_attempts_sha256 = reviewAttemptsSha256;
1391
+ const receipt = {
1392
+ schemaVersion: 1,
1393
+ source: "pactile-pi-review",
1394
+ assurance: "manager-owned-child-exit",
1395
+ taskId: review.run.taskId,
1396
+ taskRunId: review.run.id,
1397
+ piRunId: runId,
1398
+ role: "check",
1399
+ sessionId: record.session_id,
1400
+ processId: record.process_id,
1401
+ startRequestId,
1402
+ startReceiptRef,
1403
+ stopReceiptId: `review-stop-${runId}`,
1404
+ terminal: record.outcome === "cancelled" ? "cancelled" : "exited",
1405
+ exitCode: reviewProcessExit.exitCode,
1406
+ signalCode: reviewProcessExit.signalCode,
1407
+ cancellationRequestId: record.cancellation_request_id ?? null,
1408
+ evidenceRef: runReceiptRef,
1409
+ progressEvidenceRef,
1410
+ progressEvidenceSha256: createHash("sha256")
1411
+ .update(fs.readFileSync(eventFile))
1412
+ .digest("hex"),
1413
+ resultRef: record.result_file,
1414
+ resultSha256: record.result_sha256 ?? null,
1415
+ reviewAttemptsRef,
1416
+ reviewAttemptsSha256,
1417
+ reviewResponses: reviewResponseEvidence,
1418
+ recordedAt: new Date().toISOString(),
1419
+ processExit: reviewProcessExit,
1420
+ };
1421
+ writeExclusiveJson(path.join(dir, reviewStopReceiptRef), receipt);
1422
+ record.review_stop_receipt_ref = reviewStopReceiptRef;
1423
+ record.process_stop_error = null;
1424
+ }
1425
+ else {
1426
+ closeFailed = true;
1427
+ record.process_stop_error ??=
1428
+ "Pi Check process termination was not verified; Review is not recordable";
1429
+ if (record.outcome === "settled" ||
1430
+ record.outcome === "needs_review") {
1431
+ record.outcome = "interrupted";
1432
+ }
1433
+ }
1434
+ }
1435
+ if (dispatch) {
1436
+ try {
1437
+ processExit = (await this.closeForDispatch()) ?? null;
1438
+ }
1439
+ catch (closeError) {
1440
+ record.process_stop_error = safeProviderMessage(closeError instanceof Error
1441
+ ? closeError.message
1442
+ : String(closeError));
1443
+ }
1444
+ if (processExit)
1445
+ record.process_exit_receipt = processExit;
1446
+ if (record.result_sha256 === null) {
1447
+ // A cancelled prompt may never produce a result file.
1448
+ record.result_file = null;
1449
+ }
1450
+ if (processExit?.terminationVerified &&
1451
+ processExit.exitObservedAt &&
1452
+ startRequestId &&
1453
+ record.session_id &&
1454
+ Number.isSafeInteger(record.process_id) &&
1455
+ record.process_id) {
1456
+ if (record.outcome === "settled" &&
1457
+ (processExit.exitCode !== 0 || processExit.signalCode !== null)) {
1458
+ record.outcome = "interrupted";
1459
+ record.reason =
1460
+ "Pi reported a settled response but the manager-owned process exited abnormally";
1461
+ }
1462
+ const settleReceiptId = `settle-${runId}`;
1463
+ const receipt = {
1464
+ schemaVersion: 1,
1465
+ source: "pactile-pi-rpc",
1466
+ assurance: "manager-owned-child-exit",
1467
+ taskId: dispatch.taskId,
1468
+ taskRunId: dispatch.runId,
1469
+ piRunId: runId,
1470
+ role: "implement",
1471
+ sessionId: record.session_id,
1472
+ processId: record.process_id,
1473
+ startRequestId,
1474
+ settleReceiptId,
1475
+ terminal: record.outcome === "cancelled" ? "cancelled" : "exited",
1476
+ exitCode: processExit.exitCode,
1477
+ signalCode: processExit.signalCode,
1478
+ cancellationRequestId: parseJson(cancelFile)?.run_id === runId &&
1479
+ typeof parseJson(cancelFile)?.request_id === "string"
1480
+ ? String(parseJson(cancelFile)?.request_id)
1481
+ : null,
1482
+ evidenceRef: runReceiptRef,
1483
+ progressEvidenceRef,
1484
+ resultRef: record.result_file,
1485
+ resultSha256: record.result_sha256 ?? null,
1486
+ recordedAt: new Date().toISOString(),
1487
+ processExit,
1488
+ };
1489
+ record.settle_receipt_id = settleReceiptId;
1490
+ record.process_stop_receipt = receipt;
1491
+ record.process_stop_error = null;
1492
+ }
1493
+ else {
1494
+ record.process_stop_error ??=
1495
+ "Pi child close was not verified; dispatch lease retained";
1496
+ }
1497
+ }
329
1498
  if (closeFailed && alive(this.client?.pid)) {
330
1499
  record.outcome = "interrupted";
331
1500
  record.reason = `${record.reason ?? "Pi run interrupted"}; Pi process may still be active`;
@@ -334,10 +1503,52 @@ export class PiTaskBridge {
334
1503
  record.elapsed_ms = Math.round(performance.now() - started);
335
1504
  atomicJson(runFile, record);
336
1505
  atomicJson(latestFile, record);
337
- if (!(closeFailed && alive(this.client?.pid)))
1506
+ const v2ChildClosed = processExit?.terminationVerified === true;
1507
+ if (review && reviewProcessExit?.terminationVerified) {
1508
+ fs.rmSync(lockFile, { force: true });
1509
+ }
1510
+ else if (!review &&
1511
+ ((!dispatch && !(closeFailed && alive(this.client?.pid))) ||
1512
+ (dispatch && v2ChildClosed))) {
338
1513
  fs.rmSync(lockFile, { force: true });
1514
+ }
339
1515
  if (parseJson(cancelFile)?.run_id === runId)
340
1516
  fs.rmSync(cancelFile, { force: true });
1517
+ if (dispatch &&
1518
+ record.process_stop_receipt &&
1519
+ startRequestId &&
1520
+ record.session_id &&
1521
+ record.process_id) {
1522
+ try {
1523
+ const settlement = persistPiV2StopAndRelease(dispatch, {
1524
+ runId,
1525
+ startRequestId,
1526
+ sessionId: record.session_id,
1527
+ processId: record.process_id,
1528
+ progressEvidenceRef,
1529
+ startReceiptRef,
1530
+ runReceiptRef,
1531
+ resultRef: record.result_file,
1532
+ resultSha256: record.result_sha256 ?? null,
1533
+ outcome: record.outcome,
1534
+ cancellationRequestId: record.process_stop_receipt.cancellationRequestId,
1535
+ processExit: record.process_stop_receipt.processExit,
1536
+ processStopReceipt: record.process_stop_receipt,
1537
+ });
1538
+ record.dispatch_stop_proof_ref =
1539
+ settlement.stopReceiptTaskRef || null;
1540
+ record.dispatch_lease_released = settlement.released;
1541
+ record.dispatch_lease_release_reason = settlement.reasonCode;
1542
+ }
1543
+ catch (settleError) {
1544
+ record.dispatch_lease_released = false;
1545
+ record.dispatch_lease_release_reason = safeProviderMessage(settleError instanceof Error
1546
+ ? settleError.message
1547
+ : String(settleError));
1548
+ }
1549
+ atomicJson(runFile, record);
1550
+ atomicJson(latestFile, record);
1551
+ }
341
1552
  this.locked = false;
342
1553
  }
343
1554
  return record;