pi-daddy 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +12 -6
  3. package/contracts/daily-view/v1/README.md +114 -0
  4. package/contracts/daily-view/v1/fixtures/view.json +525 -0
  5. package/contracts/daily-view/v1/fixtures/view.txt +75 -0
  6. package/contracts/daily-view/v1/fixtures/work.jsonl +17 -0
  7. package/contracts/daily-view/v1/p03/README.md +26 -0
  8. package/contracts/daily-view/v1/p03/fixtures/retained-executions.json +241 -0
  9. package/contracts/daily-view/v1/p03/projection.schema.json +194 -0
  10. package/contracts/daily-view/v1/p03/provenance.json +21 -0
  11. package/contracts/dashboard-host/v1/README.md +176 -0
  12. package/contracts/debrief/v1/README.md +146 -0
  13. package/contracts/debrief/v1/intervention/README.md +32 -0
  14. package/contracts/debrief/v1/intervention/fixtures/artifact-3ee11136beb864f518252e5024dae9197421fbf97b6e981d3243c782b5c44bd0.txt +1 -0
  15. package/contracts/debrief/v1/intervention/fixtures/artifact-58905d872dd3c54a9705f67b05fa82edbad7af6476ae8993faf1a7b9fadfb905.txt +1 -0
  16. package/contracts/debrief/v1/intervention/fixtures/assessment.json +29 -0
  17. package/contracts/debrief/v1/intervention/fixtures/blind-view.json +20 -0
  18. package/contracts/debrief/v1/intervention/fixtures/choice.json +7 -0
  19. package/contracts/debrief/v1/intervention/fixtures/manifest.json +57 -0
  20. package/contracts/debrief/v1/intervention/fixtures/reveal.json +39 -0
  21. package/contracts/debrief/v1/provenance.json +89 -0
  22. package/contracts/debrief/v1/work-capture/README.md +38 -0
  23. package/contracts/debrief/v1/work-capture/fixtures/batch.json +17 -0
  24. package/contracts/debrief/v1/work-capture/fixtures/cases.json +184 -0
  25. package/contracts/debrief/v1/work-capture/fixtures/review.json +184 -0
  26. package/contracts/debrief/v1/work-capture/review-request.schema.json +41 -0
  27. package/contracts/debrief/v1/work-capture/work-case.schema.json +190 -0
  28. package/contracts/debrief/v2/README.md +71 -0
  29. package/contracts/debrief/v2/input-pin.json +66 -0
  30. package/contracts/debrief/v2/intervention-upstream.md +40 -0
  31. package/contracts/debrief/v2/work-signals-upstream.md +22 -0
  32. package/contracts/dispatch-control/v1/README.md +109 -0
  33. package/contracts/dispatch-control/v1/request.schema.json +19 -0
  34. package/contracts/effect-profile/v1/README.md +124 -0
  35. package/contracts/execution-retention/v1/README.md +109 -0
  36. package/contracts/execution-retention/v2/README.md +148 -0
  37. package/contracts/execution-retention/v2/fixtures/interrupted-native-bytes.json +91 -0
  38. package/contracts/execution-retention/v2/fixtures/missing-native-session.json +91 -0
  39. package/contracts/execution-retention/v2/fixtures/native-file-unknown-branch.json +91 -0
  40. package/contracts/execution-retention/v2/fixtures/native-id-without-bytes.json +91 -0
  41. package/contracts/execution-retention/v2/fixtures/native-live-branch.json +90 -0
  42. package/contracts/execution-retention/v2/fixtures/replaced-native-session.json +92 -0
  43. package/contracts/execution-retention/v2/fixtures/result-82c620cdfeb8ca6b858cf653d7f5e2eedca5315d6244216d60e8b36f03223d6a.bin +1 -0
  44. package/contracts/execution-retention/v2/fixtures/session-2ec1f134bc739984434f7fca689e1c3d1c39fce2dcadae709e9b6faedfcc4ac7.bin +3 -0
  45. package/contracts/execution-retention/v2/fixtures/session-9bdb4bd16a901da31580acf056bda525225259e15ec409e0410480109011772f.bin +3 -0
  46. package/contracts/execution-retention/v2/manifest.schema.json +858 -0
  47. package/contracts/experiment/v1/README.md +164 -0
  48. package/contracts/factory-order/v1/README.md +151 -0
  49. package/contracts/factory-order/v1/adoption-pin.json +8 -0
  50. package/contracts/intent-control/v1/README.md +135 -0
  51. package/contracts/intent-control/v1/request.schema.json +1096 -0
  52. package/contracts/intent-control/v2/README.md +34 -0
  53. package/contracts/intent-control/v2/request.schema.json +1070 -0
  54. package/contracts/ledger/v4/README.md +241 -0
  55. package/contracts/ledger/v4/fixtures/layout-options.json +428 -0
  56. package/contracts/ledger/v4/fixtures/work-acceptance.json +57 -0
  57. package/contracts/ledger/v4/fixtures/work-occurrence.json +44 -0
  58. package/contracts/ledger/v4/fixtures/work-revision.json +26 -0
  59. package/contracts/ledger/v4/fixtures/work-snapshot.json +73 -0
  60. package/contracts/ledger/v4/ledger-event.schema.json +162 -0
  61. package/contracts/ordinary-control/v1/README.md +59 -0
  62. package/contracts/producer-ipc/v1/README.md +137 -0
  63. package/dist/check-runner.d.ts +5 -0
  64. package/dist/check-runner.d.ts.map +1 -1
  65. package/dist/check-runner.js +16 -2
  66. package/dist/check-runner.js.map +1 -1
  67. package/dist/cli.d.ts +3 -1
  68. package/dist/cli.d.ts.map +1 -1
  69. package/dist/cli.js +49 -3
  70. package/dist/cli.js.map +1 -1
  71. package/dist/control-journal.d.ts +20 -0
  72. package/dist/control-journal.d.ts.map +1 -0
  73. package/dist/control-journal.js +73 -0
  74. package/dist/control-journal.js.map +1 -0
  75. package/dist/daily-dashboard-host.d.ts +99 -0
  76. package/dist/daily-dashboard-host.d.ts.map +1 -0
  77. package/dist/daily-dashboard-host.js +71 -0
  78. package/dist/daily-dashboard-host.js.map +1 -0
  79. package/dist/daily-view-input.d.ts +44 -0
  80. package/dist/daily-view-input.d.ts.map +1 -0
  81. package/dist/daily-view-input.js +68 -0
  82. package/dist/daily-view-input.js.map +1 -0
  83. package/dist/daily-view-render.d.ts +4 -0
  84. package/dist/daily-view-render.d.ts.map +1 -0
  85. package/dist/daily-view-render.js +41 -0
  86. package/dist/daily-view-render.js.map +1 -0
  87. package/dist/daily-view.d.ts +74 -0
  88. package/dist/daily-view.d.ts.map +1 -0
  89. package/dist/daily-view.js +170 -0
  90. package/dist/daily-view.js.map +1 -0
  91. package/dist/dashboard-cli.d.ts +26 -1
  92. package/dist/dashboard-cli.d.ts.map +1 -1
  93. package/dist/dashboard-cli.js +128 -3
  94. package/dist/dashboard-cli.js.map +1 -1
  95. package/dist/dashboard-harness.d.ts +22 -0
  96. package/dist/dashboard-harness.d.ts.map +1 -0
  97. package/dist/dashboard-harness.js +89 -0
  98. package/dist/dashboard-harness.js.map +1 -0
  99. package/dist/dashboard-herdr.d.ts.map +1 -1
  100. package/dist/dashboard-herdr.js +7 -2
  101. package/dist/dashboard-herdr.js.map +1 -1
  102. package/dist/dashboard-host-contract.d.ts +116 -0
  103. package/dist/dashboard-host-contract.d.ts.map +1 -0
  104. package/dist/dashboard-host-contract.js +14 -0
  105. package/dist/dashboard-host-contract.js.map +1 -0
  106. package/dist/dashboard-host-transport.d.ts +16 -0
  107. package/dist/dashboard-host-transport.d.ts.map +1 -0
  108. package/dist/dashboard-host-transport.js +124 -0
  109. package/dist/dashboard-host-transport.js.map +1 -0
  110. package/dist/dashboard-host.d.ts +203 -0
  111. package/dist/dashboard-host.d.ts.map +1 -0
  112. package/dist/dashboard-host.js +376 -0
  113. package/dist/dashboard-host.js.map +1 -0
  114. package/dist/dashboard-observation.d.ts +28 -0
  115. package/dist/dashboard-observation.d.ts.map +1 -0
  116. package/dist/dashboard-observation.js +86 -0
  117. package/dist/dashboard-observation.js.map +1 -0
  118. package/dist/debrief-contract.d.ts +65 -0
  119. package/dist/debrief-contract.d.ts.map +1 -0
  120. package/dist/debrief-contract.js +151 -0
  121. package/dist/debrief-contract.js.map +1 -0
  122. package/dist/debrief-fixture.d.ts +16 -0
  123. package/dist/debrief-fixture.d.ts.map +1 -0
  124. package/dist/debrief-fixture.js +41 -0
  125. package/dist/debrief-fixture.js.map +1 -0
  126. package/dist/debrief-host.d.ts +59 -0
  127. package/dist/debrief-host.d.ts.map +1 -0
  128. package/dist/debrief-host.js +49 -0
  129. package/dist/debrief-host.js.map +1 -0
  130. package/dist/debrief-render.d.ts +5 -0
  131. package/dist/debrief-render.d.ts.map +1 -0
  132. package/dist/debrief-render.js +65 -0
  133. package/dist/debrief-render.js.map +1 -0
  134. package/dist/debrief.d.ts +90 -0
  135. package/dist/debrief.d.ts.map +1 -0
  136. package/dist/debrief.js +263 -0
  137. package/dist/debrief.js.map +1 -0
  138. package/dist/delegate-types.d.ts +2 -0
  139. package/dist/delegate-types.d.ts.map +1 -1
  140. package/dist/delegate-types.js.map +1 -1
  141. package/dist/delegate.d.ts.map +1 -1
  142. package/dist/delegate.js +1 -0
  143. package/dist/delegate.js.map +1 -1
  144. package/dist/dispatch-control.d.ts +45 -0
  145. package/dist/dispatch-control.d.ts.map +1 -0
  146. package/dist/dispatch-control.js +76 -0
  147. package/dist/dispatch-control.js.map +1 -0
  148. package/dist/effect-profile-runtime.d.ts +40 -0
  149. package/dist/effect-profile-runtime.d.ts.map +1 -0
  150. package/dist/effect-profile-runtime.js +51 -0
  151. package/dist/effect-profile-runtime.js.map +1 -0
  152. package/dist/effect-profile.d.ts +40 -0
  153. package/dist/effect-profile.d.ts.map +1 -0
  154. package/dist/effect-profile.js +152 -0
  155. package/dist/effect-profile.js.map +1 -0
  156. package/dist/execution-retention.d.ts +90 -0
  157. package/dist/execution-retention.d.ts.map +1 -0
  158. package/dist/execution-retention.js +247 -0
  159. package/dist/execution-retention.js.map +1 -0
  160. package/dist/experiment-contract.d.ts +56 -0
  161. package/dist/experiment-contract.d.ts.map +1 -0
  162. package/dist/experiment-contract.js +80 -0
  163. package/dist/experiment-contract.js.map +1 -0
  164. package/dist/experiment-state.d.ts +22 -0
  165. package/dist/experiment-state.d.ts.map +1 -0
  166. package/dist/experiment-state.js +79 -0
  167. package/dist/experiment-state.js.map +1 -0
  168. package/dist/experiment-store.d.ts +30 -0
  169. package/dist/experiment-store.d.ts.map +1 -0
  170. package/dist/experiment-store.js +147 -0
  171. package/dist/experiment-store.js.map +1 -0
  172. package/dist/experiment.d.ts +65 -0
  173. package/dist/experiment.d.ts.map +1 -0
  174. package/dist/experiment.js +357 -0
  175. package/dist/experiment.js.map +1 -0
  176. package/dist/factory-contract.d.ts +79 -0
  177. package/dist/factory-contract.d.ts.map +1 -0
  178. package/dist/factory-contract.js +89 -0
  179. package/dist/factory-contract.js.map +1 -0
  180. package/dist/factory-migration.d.ts +20 -0
  181. package/dist/factory-migration.d.ts.map +1 -0
  182. package/dist/factory-migration.js +55 -0
  183. package/dist/factory-migration.js.map +1 -0
  184. package/dist/factory-order.d.ts +848 -0
  185. package/dist/factory-order.d.ts.map +1 -0
  186. package/dist/factory-order.js +83 -0
  187. package/dist/factory-order.js.map +1 -0
  188. package/dist/factory-registry.d.ts +121 -0
  189. package/dist/factory-registry.d.ts.map +1 -0
  190. package/dist/factory-registry.js +133 -0
  191. package/dist/factory-registry.js.map +1 -0
  192. package/dist/file-lock.d.ts +8 -2
  193. package/dist/file-lock.d.ts.map +1 -1
  194. package/dist/file-lock.js +78 -19
  195. package/dist/file-lock.js.map +1 -1
  196. package/dist/herdr-poll.d.ts +8 -0
  197. package/dist/herdr-poll.d.ts.map +1 -1
  198. package/dist/herdr-poll.js +23 -0
  199. package/dist/herdr-poll.js.map +1 -1
  200. package/dist/index.d.ts +20 -0
  201. package/dist/index.d.ts.map +1 -1
  202. package/dist/index.js +20 -0
  203. package/dist/index.js.map +1 -1
  204. package/dist/intent-application.d.ts +17 -0
  205. package/dist/intent-application.d.ts.map +1 -0
  206. package/dist/intent-application.js +113 -0
  207. package/dist/intent-application.js.map +1 -0
  208. package/dist/intent-control.d.ts +201 -0
  209. package/dist/intent-control.d.ts.map +1 -0
  210. package/dist/intent-control.js +110 -0
  211. package/dist/intent-control.js.map +1 -0
  212. package/dist/intent-scheduling.d.ts +9 -0
  213. package/dist/intent-scheduling.d.ts.map +1 -0
  214. package/dist/intent-scheduling.js +15 -0
  215. package/dist/intent-scheduling.js.map +1 -0
  216. package/dist/ledger-append.d.ts +15 -0
  217. package/dist/ledger-append.d.ts.map +1 -0
  218. package/dist/ledger-append.js +94 -0
  219. package/dist/ledger-append.js.map +1 -0
  220. package/dist/ledger.d.ts +11 -0
  221. package/dist/ledger.d.ts.map +1 -1
  222. package/dist/ledger.js +3 -7
  223. package/dist/ledger.js.map +1 -1
  224. package/dist/native-session-target.d.ts +9 -0
  225. package/dist/native-session-target.d.ts.map +1 -0
  226. package/dist/native-session-target.js +29 -0
  227. package/dist/native-session-target.js.map +1 -0
  228. package/dist/native-session.d.ts +59 -0
  229. package/dist/native-session.d.ts.map +1 -0
  230. package/dist/native-session.js +154 -0
  231. package/dist/native-session.js.map +1 -0
  232. package/dist/order-schedule.d.ts +37 -0
  233. package/dist/order-schedule.d.ts.map +1 -0
  234. package/dist/order-schedule.js +81 -0
  235. package/dist/order-schedule.js.map +1 -0
  236. package/dist/ordinary-children.d.ts +52 -0
  237. package/dist/ordinary-children.d.ts.map +1 -0
  238. package/dist/ordinary-children.js +93 -0
  239. package/dist/ordinary-children.js.map +1 -0
  240. package/dist/producer-ipc-contract.d.ts +29 -0
  241. package/dist/producer-ipc-contract.d.ts.map +1 -0
  242. package/dist/producer-ipc-contract.js +29 -0
  243. package/dist/producer-ipc-contract.js.map +1 -0
  244. package/dist/producer-ipc.d.ts +55 -0
  245. package/dist/producer-ipc.d.ts.map +1 -0
  246. package/dist/producer-ipc.js +143 -0
  247. package/dist/producer-ipc.js.map +1 -0
  248. package/dist/resource-budget.d.ts +334 -0
  249. package/dist/resource-budget.d.ts.map +1 -0
  250. package/dist/resource-budget.js +415 -0
  251. package/dist/resource-budget.js.map +1 -0
  252. package/dist/retention-contract.d.ts +73 -0
  253. package/dist/retention-contract.d.ts.map +1 -0
  254. package/dist/retention-contract.js +104 -0
  255. package/dist/retention-contract.js.map +1 -0
  256. package/dist/retention-json.d.ts +3 -0
  257. package/dist/retention-json.d.ts.map +1 -0
  258. package/dist/retention-json.js +32 -0
  259. package/dist/retention-json.js.map +1 -0
  260. package/dist/run-child.d.ts +4 -0
  261. package/dist/run-child.d.ts.map +1 -1
  262. package/dist/run-child.js +16 -2
  263. package/dist/run-child.js.map +1 -1
  264. package/dist/run-herdr.d.ts +6 -0
  265. package/dist/run-herdr.d.ts.map +1 -1
  266. package/dist/run-herdr.js +19 -6
  267. package/dist/run-herdr.js.map +1 -1
  268. package/dist/vendor/adoption.d.ts +129 -0
  269. package/dist/vendor/adoption.d.ts.map +1 -0
  270. package/dist/vendor/adoption.js +102 -0
  271. package/dist/vendor/adoption.js.map +1 -0
  272. package/dist/work-command.d.ts +30 -0
  273. package/dist/work-command.d.ts.map +1 -0
  274. package/dist/work-command.js +148 -0
  275. package/dist/work-command.js.map +1 -0
  276. package/dist/work-ledger-destination.d.ts +20 -0
  277. package/dist/work-ledger-destination.d.ts.map +1 -0
  278. package/dist/work-ledger-destination.js +131 -0
  279. package/dist/work-ledger-destination.js.map +1 -0
  280. package/dist/work-ledger-json.d.ts +25 -0
  281. package/dist/work-ledger-json.d.ts.map +1 -0
  282. package/dist/work-ledger-json.js +267 -0
  283. package/dist/work-ledger-json.js.map +1 -0
  284. package/dist/work-ledger-occurrences.d.ts +227 -0
  285. package/dist/work-ledger-occurrences.d.ts.map +1 -0
  286. package/dist/work-ledger-occurrences.js +115 -0
  287. package/dist/work-ledger-occurrences.js.map +1 -0
  288. package/dist/work-ledger-projection.d.ts +295 -0
  289. package/dist/work-ledger-projection.d.ts.map +1 -0
  290. package/dist/work-ledger-projection.js +237 -0
  291. package/dist/work-ledger-projection.js.map +1 -0
  292. package/dist/work-ledger-snapshot.d.ts +29 -0
  293. package/dist/work-ledger-snapshot.d.ts.map +1 -0
  294. package/dist/work-ledger-snapshot.js +267 -0
  295. package/dist/work-ledger-snapshot.js.map +1 -0
  296. package/dist/work-ledger-types.d.ts +261 -0
  297. package/dist/work-ledger-types.d.ts.map +1 -0
  298. package/dist/work-ledger-types.js +13 -0
  299. package/dist/work-ledger-types.js.map +1 -0
  300. package/dist/work-ledger-validation.d.ts +14 -0
  301. package/dist/work-ledger-validation.d.ts.map +1 -0
  302. package/dist/work-ledger-validation.js +226 -0
  303. package/dist/work-ledger-validation.js.map +1 -0
  304. package/dist/work-ledger.d.ts +43 -0
  305. package/dist/work-ledger.d.ts.map +1 -0
  306. package/dist/work-ledger.js +225 -0
  307. package/dist/work-ledger.js.map +1 -0
  308. package/extensions/daily-dashboard-session.ts +30 -0
  309. package/extensions/daily-work-session.ts +20 -0
  310. package/extensions/delegate-chain.ts +1 -0
  311. package/extensions/delegation-native.ts +12 -0
  312. package/extensions/delegation.ts +25 -11
  313. package/extensions/execute-child.ts +44 -5
  314. package/extensions/executor-session.ts +9 -0
  315. package/extensions/grants-command.ts +14 -15
  316. package/extensions/grants-connected-command.ts +19 -0
  317. package/extensions/grants.ts +26 -2
  318. package/extensions/ordinary-runtime.ts +14 -0
  319. package/extensions/primary-shadow.ts +11 -0
  320. package/extensions/run-delegation.ts +9 -7
  321. package/extensions/session.ts +10 -20
  322. package/extensions/work-runtime.ts +44 -0
  323. package/package.json +78 -4
  324. package/src/check-runner.ts +20 -2
  325. package/src/cli.ts +41 -4
  326. package/src/control-journal.ts +26 -0
  327. package/src/daily-dashboard-host.ts +49 -0
  328. package/src/daily-view-input.ts +59 -0
  329. package/src/daily-view-render.ts +44 -0
  330. package/src/daily-view.ts +152 -0
  331. package/src/dashboard-cli.ts +108 -4
  332. package/src/dashboard-harness.ts +37 -0
  333. package/src/dashboard-herdr.ts +7 -2
  334. package/src/dashboard-host-contract.ts +45 -0
  335. package/src/dashboard-host-transport.ts +40 -0
  336. package/src/dashboard-host.ts +191 -0
  337. package/src/dashboard-observation.ts +45 -0
  338. package/src/debrief-contract.ts +127 -0
  339. package/src/debrief-fixture.ts +29 -0
  340. package/src/debrief-host.ts +53 -0
  341. package/src/debrief-render.ts +49 -0
  342. package/src/debrief.ts +198 -0
  343. package/src/delegate-types.ts +2 -0
  344. package/src/delegate.ts +1 -0
  345. package/src/dispatch-control.ts +81 -0
  346. package/src/effect-profile-runtime.ts +46 -0
  347. package/src/effect-profile.ts +129 -0
  348. package/src/execution-retention.ts +230 -0
  349. package/src/experiment-contract.ts +66 -0
  350. package/src/experiment-state.ts +44 -0
  351. package/src/experiment-store.ts +98 -0
  352. package/src/experiment.ts +231 -0
  353. package/src/factory-contract.ts +47 -0
  354. package/src/factory-migration.ts +29 -0
  355. package/src/factory-order.ts +56 -0
  356. package/src/factory-registry.ts +60 -0
  357. package/src/file-lock.ts +47 -11
  358. package/src/herdr-poll.ts +18 -0
  359. package/src/index.ts +32 -0
  360. package/src/intent-application.ts +90 -0
  361. package/src/intent-control.ts +107 -0
  362. package/src/intent-scheduling.ts +14 -0
  363. package/src/ledger-append.ts +72 -0
  364. package/src/ledger.ts +3 -9
  365. package/src/native-session-target.ts +23 -0
  366. package/src/native-session.ts +139 -0
  367. package/src/order-schedule.ts +42 -0
  368. package/src/ordinary-children.ts +59 -0
  369. package/src/producer-ipc-contract.ts +26 -0
  370. package/src/producer-ipc.ts +107 -0
  371. package/src/resource-budget.ts +349 -0
  372. package/src/retention-contract.ts +84 -0
  373. package/src/retention-json.ts +20 -0
  374. package/src/run-child.ts +13 -2
  375. package/src/run-herdr.ts +11 -7
  376. package/src/vendor/adoption.ts +109 -0
  377. package/src/work-command.ts +179 -0
  378. package/src/work-ledger-destination.ts +113 -0
  379. package/src/work-ledger-json.ts +228 -0
  380. package/src/work-ledger-occurrences.ts +99 -0
  381. package/src/work-ledger-projection.ts +262 -0
  382. package/src/work-ledger-snapshot.ts +246 -0
  383. package/src/work-ledger-types.ts +229 -0
  384. package/src/work-ledger-validation.ts +174 -0
  385. package/src/work-ledger.ts +186 -0
@@ -29,6 +29,7 @@ import { isCriticalAssuranceBlock, type DelegationOutcome } from "./execute-chil
29
29
  import { type GrantsSession } from "./session.ts";
30
30
  import { newDelegationOccurrence } from "./execution-occurrence.ts";
31
31
  import { correlationShape as buildCorrelationShape } from "./correlation-shape.ts";
32
+ import { completePrimary } from "./primary-shadow.ts";
32
33
 
33
34
  /**
34
35
  * Wire a set of children to pi's partial-result channel — ADR-0032.
@@ -171,11 +172,16 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
171
172
  access: Type.Union([Type.Literal("read"), Type.Literal("write")]),
172
173
  });
173
174
 
175
+ const thinkingShape = Type.Optional(Type.Union(
176
+ ["off", "minimal", "low", "medium", "high", "xhigh", "max"].map(level => Type.Literal(level)),
177
+ { description: "Requested Pi thinking level; unsupported model/level combinations fail in the child." },
178
+ ));
174
179
  const childShape = Type.Object({
175
180
  task: Type.String({ description: "The task for this sub-agent. It receives only this." }),
176
181
  agent: Type.Optional(Type.String({ description: describeAgent(spawnable()) })),
177
182
  tools: Type.Optional(Type.Array(Type.String(), { description: "Capabilities, when no 'agent' fits." })),
178
183
  model: Type.Optional(Type.String({ description: "Model as provider/id. Defaults to this session's." })),
184
+ thinking: thinkingShape,
179
185
  correlation: Type.Optional(correlationShape),
180
186
  workspace: Type.Optional(workspaceShape),
181
187
  });
@@ -186,6 +192,8 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
186
192
  maxItems: MAX_CHILDREN_PER_CALL,
187
193
  description: "The sub-agents to run concurrently. Each is independent and unaware of the others.",
188
194
  }),
195
+ completion: Type.Optional(Type.Literal("primary", {description:"Return when the selected primary settles; shadows continue under the original owner."})),
196
+ primary: Type.Optional(Type.Integer({minimum:1,maximum:MAX_CHILDREN_PER_CALL,description:"1-based primary child; required with completion=primary."})),
189
197
  });
190
198
 
191
199
  const delegateParams = Type.Object({
@@ -207,6 +215,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
207
215
  "Defaults to this session's model, already provider-qualified.",
208
216
  }),
209
217
  ),
218
+ thinking: thinkingShape,
210
219
  correlation: Type.Optional(correlationShape),
211
220
  workspace: Type.Optional(workspaceShape),
212
221
  });
@@ -231,6 +240,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
231
240
  agent: params.agent,
232
241
  tools: params.tools,
233
242
  model: params.model,
243
+ thinking: params.thinking,
234
244
  correlation: params.correlation,
235
245
  workspace: params.workspace,
236
246
  },
@@ -241,7 +251,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
241
251
  session.fanoutBudget,
242
252
  ctx,
243
253
  signal,
244
- { onProgress: progress.sink(0) },
254
+ { onProgress: progress.sink(0), toolCallId: _toolCallId },
245
255
  );
246
256
  progress.settle([outcome]);
247
257
 
@@ -259,7 +269,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
259
269
 
260
270
  return {
261
271
  content: [{ type: "text", text: outcome.text || "(no output)" }],
262
- details: { granted: outcome.granted, depth: outcome.depth, exitCode: outcome.exitCode },
272
+ details: { granted: outcome.granted, depth: outcome.depth, exitCode: outcome.exitCode, retention: outcome.retention },
263
273
  };
264
274
  },
265
275
  });
@@ -288,10 +298,13 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
288
298
  `At most ${MAX_CHILDREN_PER_CALL} children per call, and a session-wide budget bounds the total ` +
289
299
  "across the whole delegation subtree. Children cannot see each other or share context. Use this " +
290
300
  "when independent tasks can proceed in parallel — several reviewers over one diff, say — and read " +
291
- "every child's outcome, because one can be refused while the others succeed.",
301
+ "every child's outcome, because one can be refused while the others succeed. For independent variants, " +
302
+ "completion=primary with a 1-based primary returns that result while shadows remain owned/accounted.",
292
303
  parameters: delegateAllParams,
293
304
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
294
- const children = params.children ?? [];
305
+ const children = params.children ?? [],primaryMode=params.completion==="primary";
306
+ if(primaryMode!==Number.isInteger(params.primary)||primaryMode&&(params.primary!<1||params.primary!>children.length))throw new GovernanceRefusal(refusal("FANOUT_EXCEEDED","primary fan-out requires one in-range 1-based primary"));
307
+ if(primaryMode&&session.variantRuns.size>=128)throw new GovernanceRefusal(refusal("FANOUT_EXCEEDED","primary/shadow accounting capacity exhausted"));
295
308
  const split = splitBudget(session.fanoutBudget, children.length);
296
309
  if (!split.ok) {
297
310
  // Thrown, not returned: a returned `isError` is discarded by pi, so a refusal that came back as a
@@ -310,21 +323,21 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
310
323
  // live and its writer lease is deliberately retained" — a resource-retention notice, not a per-child
311
324
  // failure. Losing it meant nobody was told a lease is held with no owner until the process exits
312
325
  // (R-116).
313
- const infrastructureErrors: unknown[] = [];
314
- const outcomes = await Promise.all(
315
- children.map(async (child, index): Promise<DelegationOutcome> => {
326
+ const infrastructureErrors: unknown[] = [],occurrences=children.map((_,index)=>newDelegationOccurrence(session,index));
327
+ const pending = children.map(async (child, index): Promise<DelegationOutcome> => {
316
328
  try {
317
329
  return await runOneDelegation(
318
330
  session, child,
319
- newDelegationOccurrence(session, index),
320
- split.perChild, ctx, signal, { onProgress: progress.sink(index) },
331
+ occurrences[index],
332
+ split.perChild, ctx, signal, { onProgress: progress.sink(index), toolCallId: _toolCallId },
321
333
  );
322
334
  } catch (error) {
323
335
  infrastructureErrors.push(error);
324
336
  return childFailureOutcome(error, session.depth + 1);
325
337
  }
326
- }),
327
- );
338
+ });
339
+ if(primaryMode){const completed=await completePrimary({session,primaryIndex:params.primary!-1,occurrences,pending,settle:progress.settle}),primary=completed.primary;if(!primary.ok){if(isCriticalAssuranceBlock(primary))throw new Error(primary.text);throw totalFanoutFailure([primary],`primary child failed: ${primary.reason}`);}return {content:[{type:"text",text:primary.text||"(no output)"}],details:{primary:params.primary,shadows:children.length-1,primaryExecutionId:completed.runId,shadowSettlement:"retained-by-original-owner",granted:primary.granted,retention:primary.retention}};}
340
+ const outcomes=await Promise.all(pending);
328
341
  progress.settle(outcomes);
329
342
  // The upstream controller's verdict outranks our own infrastructure noise — it is the answer the
330
343
  // caller is waiting for, and ADR-0034 requires it to pass through unchanged. But an infrastructure
@@ -353,6 +366,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
353
366
  budgetPerChild: split.perChild,
354
367
  granted: outcomes.map((o) => o.granted),
355
368
  refusals: outcomes.map((o) => o.refusal ?? null),
369
+ retention: outcomes.map((o) => o.retention ?? null),
356
370
  },
357
371
  };
358
372
  },
@@ -1,4 +1,6 @@
1
+ import { withOrdinaryChild } from "./ordinary-runtime.ts";
1
2
  import type { Delegation } from "../src/delegate.ts";
3
+ import { beginExecutionRetention, retentionConfigurationDigest, type RetentionStatus } from "../src/execution-retention.ts";
2
4
  import { appendLedgerEvent, buildChildLifecycleEvent } from "../src/ledger.ts";
3
5
  import { hasFinalizerError } from "../src/finalization.ts";
4
6
  import { mergeChildEnv } from "../src/propagation.ts";
@@ -9,6 +11,7 @@ import { HerdrWriterCloseError, runHerdrPane } from "../src/run-herdr.ts";
9
11
  import { GovernanceRefusal, refusal, type StructuredRefusal } from "../src/refusals.ts";
10
12
  import { ENV_HERDR_KEEP_PANE, type GrantsSession } from "./session.ts";
11
13
  import { releaseDelegationWorkspace, type PreparedWorkspace } from "./workspace-runtime.ts";
14
+ import { beginDeclaredWorkAttempt } from "./work-runtime.ts";
12
15
 
13
16
  export interface DelegationOutcome {
14
17
  ok: boolean;
@@ -23,6 +26,9 @@ export interface DelegationOutcome {
23
26
  aborted?: boolean;
24
27
  truncated?: boolean;
25
28
  spawnFailed?: boolean;
29
+ retention?: RetentionStatus;
30
+ /** A loud best-effort post-execution observation failure; execution ownership is still settled. */
31
+ control?: "failed";
26
32
  }
27
33
 
28
34
  /**
@@ -78,13 +84,17 @@ export interface ChildProgressUpdate {
78
84
  * has already run, so failing closed there prevents nothing and used to discard completed work while
79
85
  * blaming "ledger" (R-99). The failure is reported alongside the outcome instead of replacing it.
80
86
  */
81
- export async function executePlannedChild(input: {
87
+ export function executePlannedChild(input: Parameters<typeof executeChildBody>[0]): Promise<DelegationOutcome> {
88
+ return withOrdinaryChild(input, signal => executeChildBody({ ...input, signal }));
89
+ }
90
+ async function executeChildBody(input: {
82
91
  session: GrantsSession;
83
92
  plan: Delegation;
84
93
  agent?: string;
85
94
  childId: string;
86
95
  executionId: string;
87
96
  parentExecutionId: string | null;
97
+ toolCallId?: string;
88
98
  cwd: string;
89
99
  preparedWorkspace?: PreparedWorkspace;
90
100
  signal?: AbortSignal;
@@ -117,6 +127,10 @@ export async function executePlannedChild(input: {
117
127
  throw error;
118
128
  }
119
129
  }
130
+ const workAttempt = await beginDeclaredWorkAttempt({
131
+ session, plan, childId, executionId, parentExecutionId, toolCallId: input.toolCallId,
132
+ preparedWorkspace, configuredTimeoutMs, startedAt,
133
+ });
120
134
 
121
135
  // The recorded deadline and executor timer are one fact. Waiting for the strict starting append consumes
122
136
  // the budget; handing the child a fresh full timeout would leave it live after the dashboard truthfully
@@ -134,9 +148,16 @@ export async function executePlannedChild(input: {
134
148
  const executionSignal = signal
135
149
  ? AbortSignal.any([signal, leaseAbort.signal])
136
150
  : writerLease ? leaseAbort.signal : undefined;
151
+ const retention = beginExecutionRetention({ executionId, parentExecutionId, childId,
152
+ toolCallId: input.toolCallId ?? null, executor: session.executor.kind, taskDigest: plan.taskDigest,
153
+ definitionDigest: plan.definitionDigest?.sha256 ?? null,
154
+ configurationDigest: retentionConfigurationDigest({ args: plan.args, effective: plan.effective, timeoutMs: configuredTimeoutMs }),
155
+ workspaceId: preparedWorkspace?.workspace.workspaceId ?? null });
156
+ const sessionFlag = plan.args.indexOf("--session");
157
+ if (sessionFlag >= 0) retention.observeSession({ source: "pi-session-file", value: plan.args[sessionFlag + 1] });
137
158
  let releaseReason = "failed";
138
159
  let retainWriterLease = false;
139
- let terminalAttempted = false;
160
+ let terminalAttempted = false, workTerminalAttempted = false;
140
161
  const teardownFailures: string[] = [];
141
162
  let runtimeRecord: Promise<void> | undefined;
142
163
  const recordRunning = (executor: "process" | "herdr", pane?: { id: string; agentName: string }): void => {
@@ -171,7 +192,12 @@ export async function executePlannedChild(input: {
171
192
  timeoutMs: remainingTimeoutMs,
172
193
  keepPane: writerLease ? false : process.env[ENV_HERDR_KEEP_PANE] === "1",
173
194
  closeOnSettle: Boolean(writerLease),
174
- onPane: (paneId, agentName) => onProgress?.({ paneId, agentName, state: "starting" }),
195
+ onPane: (paneId, agentName) => {
196
+ retention.native({ paneId, agentName });
197
+ onProgress?.({ paneId, agentName, state: "starting" });
198
+ },
199
+ onObservation: (bytes) => retention.capture("paneSnapshot", bytes, true),
200
+ onSessionReference: (reference) => retention.observeSession(reference),
175
201
  onRunning: (paneId, agentName) => {
176
202
  // Record first: runHerdrPane isolates this display callback, so a renderer exception after the
177
203
  // observation cannot suppress the authoritative running event.
@@ -179,6 +205,7 @@ export async function executePlannedChild(input: {
179
205
  onProgress?.({ paneId, agentName, state: "running" });
180
206
  },
181
207
  onTab: preparedWorkspace ? (tabId) => preparedWorkspace.lease.attachHerdrTab(tabId) : undefined,
208
+ onNativeTab: (tabId) => retention.native({ tabId }),
182
209
  onSnapshot: onProgress ? (snapshot) => onProgress({ snapshot }) : undefined,
183
210
  })
184
211
  : await runChild({
@@ -192,18 +219,26 @@ export async function executePlannedChild(input: {
192
219
  timeoutMs: Math.max(1, remainingTimeoutMs - terminationGraceMs),
193
220
  hardDeadlineAt: Date.parse(deadlineAt),
194
221
  onOutput: onProgress ? (chunk) => onProgress({ chunk }) : undefined,
222
+ onObservation: (stream, bytes) => retention.capture(stream, bytes),
195
223
  onSpawn: (pid) => {
196
224
  // Lease attachment is a security hook and may fail the spawn. The shared reporters isolate
197
225
  // display exceptions before they reach this callback; runChild deliberately kills on any error
198
226
  // here, so presentation must never be added directly without that reporter boundary.
199
227
  preparedWorkspace?.lease.attachProcess(pid);
228
+ retention.native({ pid });
200
229
  recordRunning("process");
201
230
  onProgress?.({ state: "running" });
202
231
  },
203
232
  });
204
233
 
234
+ if (sessionFlag >= 0) retention.observeSession({ source: "pi-session-file", value: plan.args[sessionFlag + 1] });
235
+ retention.capture("result", Buffer.from(output.text), true);
205
236
  const childFailed = Boolean(output.spawnError || output.aborted || output.timedOut || output.code !== 0);
237
+ retention.finish({ code: output.code, signal: output.signal ?? null, timedOut: output.timedOut,
238
+ aborted: output.aborted, truncated: output.truncated, failed: childFailed });
206
239
  releaseReason = output.timedOut ? "timeout" : output.aborted ? "cancelled" : childFailed ? "failed" : "completed";
240
+ if (workAttempt) try { workTerminalAttempted = true; await workAttempt.finish(childFailed ? "failed" : "completed"); }
241
+ catch (error) { teardownFailures.push(`declared work terminal record failed: ${String(error)}`); }
207
242
  if (ledgerPath) {
208
243
  terminalAttempted = true;
209
244
  await appendAfterRuntimeRecord(runtimeRecord, () => appendLedgerEvent(
@@ -286,6 +321,9 @@ export async function executePlannedChild(input: {
286
321
  await teardown();
287
322
  return withTeardownNotes(succeeded);
288
323
  } catch (error) {
324
+ retention.finish({ code: null, signal: null, timedOut: false, aborted: Boolean(signal?.aborted), truncated: false, failed: true });
325
+ if (workAttempt && !workTerminalAttempted) try { workTerminalAttempted = true; await workAttempt.finish("failed"); }
326
+ catch (cause) { teardownFailures.push(`declared work terminal record failed: ${String(cause)}`); }
289
327
  retainWriterLease = Boolean(writerLease && isHerdrWriterCloseFailure(error));
290
328
  if (ledgerPath && !terminalAttempted) {
291
329
  // Best-effort: this records the failure, so it must not REPLACE the failure. A strict append that
@@ -338,8 +376,9 @@ function errorWithTeardownNotes(error: unknown, notes: readonly string[]): unkno
338
376
  }
339
377
 
340
378
  function withTeardownNotes(outcome: DelegationOutcome): DelegationOutcome {
341
- if (teardownFailures.length === 0) return outcome;
342
- return { ...outcome, reason: [outcome.reason, ...teardownFailures].filter(Boolean).join("; ") };
379
+ const observed = { ...outcome, retention: retention.status() };
380
+ if (teardownFailures.length === 0) return observed;
381
+ return { ...observed, control: "failed", reason: [outcome.reason, ...teardownFailures].filter(Boolean).join("; ") };
343
382
  }
344
383
 
345
384
  async function teardown(): Promise<void> {
@@ -0,0 +1,9 @@
1
+ import { chooseExecutor, needsProbe, ENV_HERDR } from "../src/executor.ts";
2
+ import { probeHerdr } from "../src/herdr-cli.ts";
3
+ import type { GrantsSession } from "./session.ts";
4
+
5
+ /** Settle one executor at session start; a later daemon change never relocates siblings. */
6
+ export async function resolveExecutor(session:GrantsSession):Promise<void>{
7
+ const raw=process.env[ENV_HERDR];
8
+ session.executor=chooseExecutor(raw,needsProbe(raw)?await probeHerdr():null);
9
+ }
@@ -17,6 +17,8 @@ import type { ExecutorChoice } from "../src/executor.ts";
17
17
  import type { Catalog } from "../src/catalog.ts";
18
18
  import type { SkillDefinition } from "../src/definitions.ts";
19
19
  import type { GatedPlan } from "./run-delegation.ts";
20
+ import type { VariantRunAccounting } from "./session.ts";
21
+ import { handleConnectedCommand } from "./grants-connected-command.ts";
20
22
  import { loadApprovals, revokeAll, revokeApproval, type SubjectLookup } from "../src/approval-store.ts";
21
23
  import { verifyLedger } from "../src/ledger.ts";
22
24
 
@@ -62,6 +64,9 @@ export interface GrantsCommandContext {
62
64
  runInit: () => Promise<void>;
63
65
  /** Open or reuse the read-only Herdr dashboard; injected so this diagnostic never becomes enforcement. */
64
66
  openDashboard: () => Promise<{ kind: "opened" | "reused"; paneId: string; visibleBesideCaller: boolean }>;
67
+ /** Explicit same-process production host lifecycle; owns no model call and never cancels a child on stop. */
68
+ runHost: (target: string) => Promise<string>;
69
+ variantRuns: Map<string, VariantRunAccounting>;
65
70
  }
66
71
 
67
72
  /**
@@ -71,18 +76,18 @@ export interface GrantsCommandContext {
71
76
  const PREVIEW_LIMIT = 12;
72
77
 
73
78
  /** The verbs `/grants` answers to. Anything else is refused rather than silently treated as no verb. */
74
- const KNOWN_SUBCOMMANDS: readonly string[] = ["init", "dashboard", "ledger", "approvals", "revoke"];
79
+ const KNOWN_SUBCOMMANDS: readonly string[] = ["init", "host", "variants", "dashboard", "ledger", "approvals", "revoke"];
75
80
 
76
81
  export const grantsCommand = {
77
82
  description:
78
83
  "Show this session's capability grant, delegation depth, and known agent-type ceilings; " +
79
- "/grants dashboard | /grants approvals | /grants ledger | /grants revoke <key>|--all",
84
+ "/grants host <fresh-id>|stop | /grants variants | /grants dashboard | /grants approvals | /grants ledger | /grants revoke <key>|--all",
80
85
  handler: async (args: string, ctx: any) => {
81
86
  // Everything this command may see, named in one place. Previously these were whatever happened to be in
82
87
  // the enclosing closure — which is how a diagnostic came to disagree with the enforcer (R-28).
83
88
  const {
84
89
  cwd, governed, ownGrant, executor, observed, depth, maxDepth, ledgerPath,
85
- catalog, definitions, sessionApprovals, inheritedApprovals, snapshotOf, previewDelegation,
90
+ catalog, definitions, sessionApprovals, inheritedApprovals, snapshotOf, previewDelegation, variantRuns,
86
91
  } = ctx.grants as GrantsCommandContext;
87
92
 
88
93
  const [sub, target] = args.trim().split(/\s+/).filter(Boolean);
@@ -92,18 +97,12 @@ handler: async (args: string, ctx: any) => {
92
97
  return;
93
98
  }
94
99
 
95
- if (sub === "dashboard") {
96
- try {
97
- const opened = await ctx.grants.openDashboard();
98
- ctx.ui.notify(
99
- `grants: dashboard ${opened.kind} in Herdr pane ${opened.paneId} without changing focus` +
100
- (opened.visibleBesideCaller ? "." : " (the existing pane is in another tab)."),
101
- "info",
102
- );
103
- } catch (error) {
104
- ctx.ui.notify(`grants: dashboard unavailable — ${error instanceof Error ? error.message : String(error)}`, "error");
105
- }
106
- return;
100
+ if(await handleConnectedCommand(sub,target,{runHost:ctx.grants.runHost,openDashboard:ctx.grants.openDashboard,ui:ctx.ui}))return;
101
+
102
+ if (sub === "variants") {
103
+ const lines=[`grants: ${variantRuns.size} retained primary/shadow run(s)`];
104
+ for(const run of variantRuns.values())lines.push(` ${run.runId} ${run.state} — primary ${run.primaryExecutionId}; ${run.shadowExecutionIds.length} shadow(s)`+(!run.outcomes?"":` — ${run.outcomes.map(x=>`${x.role}:${x.ok?"completed":"failed"}`).join(", ")}`));
105
+ ctx.ui.notify(lines.join("\n"),"info");return;
107
106
  }
108
107
 
109
108
  if (sub === "ledger") {
@@ -0,0 +1,19 @@
1
+ export interface GrantsConnectedCommandContext {
2
+ runHost:(target:string)=>Promise<string>;
3
+ openDashboard:()=>Promise<{kind:"opened"|"reused";paneId:string;visibleBesideCaller:boolean}>;
4
+ ui:{notify(message:string,level:"info"|"error"):void};
5
+ }
6
+ /** Host lifecycle and pane placement live together; neither belongs in the capability-report renderer. */
7
+ export async function handleConnectedCommand(sub:string|undefined,target:string|undefined,ctx:GrantsConnectedCommandContext):Promise<boolean>{
8
+ if(sub==="host"){
9
+ try{ctx.ui.notify(`grants: ${await ctx.runHost(target??"")}`,"info");}
10
+ catch(error){ctx.ui.notify(`grants: host unavailable — ${error instanceof Error?error.message:String(error)}`,"error");}
11
+ return true;
12
+ }
13
+ if(sub==="dashboard"){
14
+ try{const opened=await ctx.openDashboard();ctx.ui.notify(`grants: dashboard ${opened.kind} in Herdr pane ${opened.paneId} without changing focus`+(opened.visibleBesideCaller?".":" (the existing pane is in another tab)."),"info");}
15
+ catch(error){ctx.ui.notify(`grants: dashboard unavailable — ${error instanceof Error?error.message:String(error)}`,"error");}
16
+ return true;
17
+ }
18
+ return false;
19
+ }
@@ -37,7 +37,8 @@ import { registerDelegationTools } from "./delegation.ts";
37
37
  import { grantsCommand } from "./grants-command.ts";
38
38
  import { runInit } from "./init-command.ts";
39
39
  import { planWithApprovals } from "./run-delegation.ts";
40
- import { createGrantsSession, loadProjectDefinitions, resolveExecutor, type GrantsSession } from "./session.ts";
40
+ import { createGrantsSession, loadProjectDefinitions, type GrantsSession } from "./session.ts";
41
+ import { resolveExecutor } from "./executor-session.ts";
41
42
  import { reportSessionStart } from "./session-report.ts";
42
43
  import { SPAWN_TOOLS, tripwireReason } from "./tripwire.ts";
43
44
  import { reportGrantStoreRefusal } from "./grant-store-refusal.ts";
@@ -47,6 +48,10 @@ import {
47
48
  openDashboardCommand,
48
49
  } from "../src/dashboard-handshake.ts";
49
50
 
51
+ import { associateOrdinaryHost, ordinaryChildrenFor } from "../src/ordinary-children.ts";
52
+ import { loadDeclaredWork } from "../src/work-command.ts";
53
+ import { createDailyDashboardSession } from "./daily-dashboard-session.ts";
54
+ import { replacePublishedDailyWork, type PublishedDailyWork } from "./daily-work-session.ts";
50
55
  export default function (pi: ExtensionAPI) {
51
56
  // The path pi loads as the extension, so a child granted `tool:delegate` can be started with `-e <this>`.
52
57
  // Only this file can say so about itself, which is why the session takes it rather than deriving it.
@@ -59,6 +64,8 @@ export default function (pi: ExtensionAPI) {
59
64
  })();
60
65
 
61
66
  const session = createGrantsSession(extensionPath);
67
+ associateOrdinaryHost(pi,session);
68
+ const dailyHost=createDailyDashboardSession({ordinary:()=>ordinaryChildrenFor(pi),declared:()=>session.declaredWork,cwd:()=>session.cwd,env:process.env,author:"local-operator"});
62
69
  const dashboardPluginRoot = fileURLToPath(new URL("../herdr-plugin/", import.meta.url));
63
70
  const dashboardPaths = defaultDashboardPaths(
64
71
  process.env.PI_CODING_AGENT_DIR?.trim() || join(homedir(), ".pi", "agent"),
@@ -68,6 +75,7 @@ export default function (pi: ExtensionAPI) {
68
75
  // because the hooks below have to be registered before the tools and both need to call it — the tool
69
76
  // schemas describe which definitions are spawnable, and nothing knows that until a hook has run (R-39).
70
77
  const delegation = { refreshSpawnable: () => {} };
78
+ const publishedDailyWork: PublishedDailyWork = {};
71
79
 
72
80
  pi.on("session_start", async (_event, ctx) => {
73
81
  session.cwd = ctx.cwd;
@@ -75,6 +83,19 @@ export default function (pi: ExtensionAPI) {
75
83
  // execution tree across workspaces. Resolve it once at the root's actual pi cwd; descendants inherit
76
84
  // this absolute identity verbatim, and resolve(abs) remains abs at every deeper session start.
77
85
  if (session.ledgerPath) session.ledgerPath = resolve(ctx.cwd, session.ledgerPath);
86
+ try {
87
+ try {
88
+ replacePublishedDailyWork(process.env, publishedDailyWork, undefined);
89
+ session.declaredWork = (await loadDeclaredWork(join(ctx.cwd, ".pi", "work-current.json"))) ?? undefined;
90
+ replacePublishedDailyWork(process.env, publishedDailyWork, session.declaredWork);
91
+ } catch (error) {
92
+ session.declaredWork = undefined;
93
+ ctx.ui.notify(
94
+ `grants: declared work is invalid (${error instanceof Error ? error.message : String(error)}); ` +
95
+ `delegations remain unbound and no previous selection was substituted.`,
96
+ "error",
97
+ );
98
+ }
78
99
  // The one case the stored-grant lookup can get wrong (ADR-0030). The factory reads the store keyed by
79
100
  // `process.cwd()` because it runs before any hook and therefore before `ctx` exists — and S-5 forces
80
101
  // that ordering, since whether `delegate` is registered is decided there. Almost always the two agree.
@@ -93,7 +114,6 @@ export default function (pi: ExtensionAPI) {
93
114
  "warning",
94
115
  );
95
116
  }
96
- try {
97
117
  try {
98
118
  await reportGrantStoreRefusal(session, ctx.ui);
99
119
  } catch (error) {
@@ -230,6 +250,8 @@ export default function (pi: ExtensionAPI) {
230
250
  * `exit` remains the backstop. SIGKILL still orphans panes, exactly as R-62 records, and no signal handler is
231
251
  * installed here for the reason R-62 gives: it would turn pi's "interrupt this turn" into "exit pi".
232
252
  */
253
+ pi.on("session_shutdown", async () => { await dailyHost.close().catch(() => undefined); });
254
+
233
255
  pi.on("agent_settled", async () => {
234
256
  try {
235
257
  if (session.executor.kind !== "herdr" || openPaneCount() === 0) return undefined;
@@ -348,6 +370,7 @@ export default function (pi: ExtensionAPI) {
348
370
  definitions: session.definitions,
349
371
  sessionApprovals: session.sessionApprovals,
350
372
  inheritedApprovals: session.inheritedApprovals,
373
+ variantRuns: session.variantRuns,
351
374
  snapshotOf: (subject: string) => snapshotOf(session, subject),
352
375
  // The REAL delegation path, minus the one thing a diagnostic must never do. `ctx: null` is what
353
376
  // says so: stored approvals count exactly as they would for a spawn, and no human is asked
@@ -358,6 +381,7 @@ export default function (pi: ExtensionAPI) {
358
381
  await loadProjectDefinitions(session, ctx.cwd);
359
382
  delegation.refreshSpawnable();
360
383
  }),
384
+ runHost: (target: string) => dailyHost.run(target),
361
385
  openDashboard: () => openDashboardCommand({
362
386
  env: process.env,
363
387
  pid: process.pid,
@@ -0,0 +1,14 @@
1
+ import { retainSessionChild } from "../src/ordinary-children.ts";
2
+ import { dataDigest } from "../src/debrief-contract.ts";
3
+ import { runWithFinalizers } from "../src/finalization.ts";
4
+ import { releaseDelegationWorkspace,type PreparedWorkspace } from "./workspace-runtime.ts";
5
+ import type { GrantsSession } from "./session.ts";
6
+ import type { DelegationOutcome } from "./execute-child.ts";
7
+ /** One wrapper around the original executor. It neither consumes nor replaces the caller's promise. */
8
+ export async function withOrdinaryChild(input:{session:GrantsSession;executionId:string;parentExecutionId:string|null;childId:string;toolCallId?:string;signal?:AbortSignal;preparedWorkspace?:PreparedWorkspace},operation:(signal?:AbortSignal)=>Promise<DelegationOutcome>){
9
+ let retained:ReturnType<typeof retainSessionChild>;
10
+ try{retained=retainSessionChild(input.session,{executionId:input.executionId,parentExecutionId:input.parentExecutionId,toolCallId:input.toolCallId??null},input.signal);}
11
+ catch(error){return runWithFinalizers(async()=>{throw error;},[{label:"ordinary admission workspace release",run:async()=>{await releaseDelegationWorkspace({prepared:input.preparedWorkspace,childId:input.childId,executionId:input.executionId,parentExecutionId:input.parentExecutionId,ledgerPath:input.session.ledgerPath,reason:"refused"});}}]);}
12
+ try{const outcome=await operation(retained?.signal??input.signal);retained?.settle({ok:outcome.ok,exitCode:outcome.exitCode,aborted:outcome.aborted??false,outputDigest:dataDigest(outcome.text)},outcome.control??"not-assessed",outcome.control==="failed"?"not-assessed":outcome.control??"not-assessed");return outcome;}
13
+ catch(error){retained?.settle({failure:"original executor threw; inspect original caller"},"unknown");throw error;}
14
+ }
@@ -0,0 +1,11 @@
1
+ import type { DelegationOutcome } from "./execute-child.ts";
2
+ import type { ExecutionOccurrenceIds } from "./execution-occurrence.ts";
3
+ import type { GrantsSession, VariantRunAccounting } from "./session.ts";
4
+
5
+ /** Return one primary while the original session retains and settles every shadow outcome. */
6
+ export async function completePrimary(input:{session:GrantsSession;primaryIndex:number;occurrences:ExecutionOccurrenceIds[];pending:Promise<DelegationOutcome>[];settle:(outcomes:{ok:boolean}[])=>void}){
7
+ const runId=input.occurrences[input.primaryIndex].executionId,record:VariantRunAccounting={runId,primaryExecutionId:runId,shadowExecutionIds:input.occurrences.filter((_,i)=>i!==input.primaryIndex).map(x=>x.executionId),state:"running",outcomes:null};
8
+ input.session.variantRuns.set(runId,record);
9
+ void Promise.all(input.pending).then(outcomes=>{record.outcomes=outcomes.map((outcome,index)=>({executionId:input.occurrences[index].executionId,role:index===input.primaryIndex?"primary":"shadow",ok:outcome.ok,reason:outcome.ok?null:outcome.reason??"failed"}));record.state="settled";input.settle(outcomes);});
10
+ return {runId,primary:await input.pending[input.primaryIndex]};
11
+ }
@@ -11,6 +11,7 @@
11
11
  * tool surface is observed.
12
12
  */
13
13
 
14
+ import { nativeDelegationContext } from "./delegation-native.ts";
14
15
  import { DELEGATE_SUBJECT, shouldSeekApproval } from "../src/approval.ts";
15
16
  import { planDelegation } from "../src/delegate.ts";
16
17
  import {
@@ -43,6 +44,7 @@ interface ChildSpec {
43
44
  agent?: string;
44
45
  tools?: string[];
45
46
  model?: string;
47
+ thinking?: string;
46
48
  correlation?: CorrelationMetadata;
47
49
  workspace?: DelegationWorkspaceSpec;
48
50
  }
@@ -201,6 +203,8 @@ export async function runOneDelegation(
201
203
  * mistake unspellable.
202
204
  */
203
205
  options: {
206
+ /** Actual public execute argument, never read from model-authored correlation or output. */
207
+ toolCallId?: string;
204
208
  /** Progress for the parent's status block (ADR-0032). Display only. */
205
209
  onProgress?: (update: {
206
210
  /** Appended (process executor: a genuine byte stream). */
@@ -245,6 +249,7 @@ export async function runOneDelegation(
245
249
  agent: spec.agent,
246
250
  tools: spec.tools,
247
251
  model: spec.model ?? defaultModel,
252
+ thinking: spec.thinking,
248
253
  correlation: spec.workspace
249
254
  ? { ...(spec.correlation ?? {}), workspace_id: spec.workspace.workspace_id }
250
255
  : spec.correlation,
@@ -254,12 +259,6 @@ export async function runOneDelegation(
254
259
  boundWorkspaceId: spec.workspace?.workspace_id,
255
260
  boundContextId: spec.correlation?.context_id,
256
261
  };
257
- const extra = {
258
- fanoutBudget: budget,
259
- spawnId: ids.parentId,
260
- childSpawnId: ids.childId,
261
- childExecutionId: ids.executionId,
262
- };
263
262
 
264
263
  // ADR-0031: herdr was DEMANDED (`PI_GRANTS_HERDR=1`) and is not answering. Refused rather than relocated —
265
264
  // the operator chose that over falling back, so the ledger can never name a child that ran somewhere nobody
@@ -275,13 +274,15 @@ export async function runOneDelegation(
275
274
  // `ctx: null` rather than skipping the plan entirely: the ledger still gets a full, honest record of what was
276
275
  // requested and refused, and stored approvals still count toward it — nothing is *hidden*, only nobody is
277
276
  // *asked*. It is the same argument `/grants` uses for its preview.
278
- const executorRefusal = session.executor.refusal;
277
+ let executorRefusal = session.executor.refusal;
279
278
  const modelRefusal = preflightModel(
280
279
  request.model,
281
280
  ctx.modelRegistry,
282
281
  session.modelResolutionCache,
283
282
  session.allowUnresolvedModels,
284
283
  );
284
+ const {extra,refusal:nativeRefusal}=await nativeDelegationContext(session,ids,budget,Boolean(executorRefusal||modelRefusal));
285
+ executorRefusal ||= nativeRefusal;
285
286
  let preparedWorkspace: PreparedWorkspace | undefined;
286
287
  let approvalOutcome: ApprovalOutcome | undefined;
287
288
  let plan: ReturnType<typeof planDelegation>;
@@ -388,6 +389,7 @@ export async function runOneDelegation(
388
389
  childId: ids.childId,
389
390
  executionId: ids.executionId,
390
391
  parentExecutionId: ids.parentExecutionId,
392
+ toolCallId: options.toolCallId,
391
393
  cwd: ctx.cwd,
392
394
  preparedWorkspace,
393
395
  signal,
@@ -21,8 +21,7 @@ import { makeCatalog, skillPathsFromCatalog, type Catalog } from "../src/catalog
21
21
  import type { SkillDefinition } from "../src/definitions.ts";
22
22
  import { DELEGATE_CAPABILITY, type DelegationContext } from "../src/delegate.ts";
23
23
  import { budgetFromEnv } from "../src/fanout.ts";
24
- import { chooseExecutor, needsProbe, ENV_HERDR, type ExecutorChoice } from "../src/executor.ts";
25
- import { probeHerdr } from "../src/herdr-cli.ts";
24
+ import { chooseExecutor, ENV_HERDR, type ExecutorChoice } from "../src/executor.ts";
26
25
  import { WILDCARD } from "../src/pi-tools.ts";
27
26
  import {
28
27
  childEnv,
@@ -47,8 +46,9 @@ import { ENV_WORKSPACE_REGISTRY } from "../src/workspace.ts";
47
46
  import type { GrantStoreRefusalReason } from "../src/grant-store.ts";
48
47
  import { republishable } from "./approvals.ts";
49
48
  import { storedGrantSessionState } from "./stored-grant-session.ts";
49
+ import { nativeSessionRootFromEnv, type NativeSessionHost } from "../src/native-session-target.ts";
50
50
  import { ENV_ALLOW_UNRESOLVED_MODELS } from "../src/model-preflight.ts";
51
-
51
+ import type { DeclaredWorkState } from "../src/work-command.ts";
52
52
  /**
53
53
  * Run governed children in herdr panes instead of captured child processes.
54
54
  *
@@ -79,7 +79,8 @@ export { ENV_HERDR_WORKSPACE } from "../src/herdr-cli.ts";
79
79
  /** Keep each child's pane after it finishes, for inspection. Off by default: fan-out would flood it. */
80
80
  export const ENV_HERDR_KEEP_PANE = "PI_GRANTS_HERDR_KEEP_PANE";
81
81
 
82
- export interface GrantsSession {
82
+ export interface VariantRunAccounting {runId:string;primaryExecutionId:string;shadowExecutionIds:string[];state:"running"|"settled";outcomes:null|{executionId:string;role:"primary"|"shadow";ok:boolean;reason:string|null}[]}
83
+ export interface GrantsSession extends NativeSessionHost {
83
84
  /**
84
85
  * False when neither `PI_GRANTS_GRANT` nor a stored grant applies: the session holds the wildcard and
85
86
  * nothing is governed.
@@ -126,6 +127,9 @@ export interface GrantsSession {
126
127
  readonly modelResolutionCache: Map<string, boolean>;
127
128
  /** Path to this extension, so a child granted `tool:delegate` can delegate in turn. */
128
129
  readonly extensionPath?: string;
130
+ declaredWork?: DeclaredWorkState; // Explicit operator selection; absence leaves execution visibly unbound.
131
+ /** Primary-return fan-outs retained by this original session; bounded and human-readable via /grants variants. */
132
+ readonly variantRuns: Map<string, VariantRunAccounting>;
129
133
 
130
134
  /** Approval keys approved for this session. In memory only — this dies with the process. */
131
135
  readonly sessionApprovals: Set<string>;
@@ -234,22 +238,6 @@ export async function loadProjectDefinitions(session: GrantsSession, cwd: string
234
238
  session.catalog = await session.catalogReady;
235
239
  }
236
240
 
237
- /**
238
- * Probe for herdr and settle this session's executor — ADR-0031.
239
- *
240
- * **Once, at session start, and never per spawn.** A fan-out whose children ran under two executors would put
241
- * two different things under one call in the ledger, and the two plans differ (`--print` is withheld on the
242
- * herdr path). A herdr server that dies mid-session therefore surfaces as a failed `tab create`, reported as
243
- * the spawn error it is, rather than as a silent relocation of the remaining children.
244
- *
245
- * `probeHerdr` never throws, so this cannot either — which matters because it runs *before* the line that
246
- * discloses what it decided (R-60: a throw here would cancel that line and every control after it).
247
- */
248
- export async function resolveExecutor(session: GrantsSession): Promise<void> {
249
- const raw = process.env[ENV_HERDR];
250
- session.executor = chooseExecutor(raw, needsProbe(raw) ? await probeHerdr() : null);
251
- }
252
-
253
241
  export function createGrantsSession(extensionPath: string | undefined): GrantsSession {
254
242
  // Governance is opt-in: with PI_GRANTS_GRANT unset AND no stored grant for this directory, the session
255
243
  // holds the wildcard and nothing is blocked. This extension must never silently tighten a normal
@@ -318,8 +306,10 @@ export function createGrantsSession(extensionPath: string | undefined): GrantsSe
318
306
  */
319
307
  mayDelegate: !governed || inherited.includes(DELEGATE_CAPABILITY) || inherited.includes(WILDCARD),
320
308
  allowUnresolvedModels: process.env[ENV_ALLOW_UNRESOLVED_MODELS] === "1",
309
+ nativeSessionRoot: nativeSessionRootFromEnv(process.env),
321
310
  modelResolutionCache: new Map<string, boolean>(),
322
311
  extensionPath,
312
+ variantRuns: new Map(),
323
313
 
324
314
  sessionApprovals: new Set<string>(),
325
315
  sessionApprovalBindings: new Map<string, ApprovalBinding>(),