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
@@ -0,0 +1,152 @@
1
+ import { createHash } from "node:crypto";
2
+ import { projectWorkLedger, parseWorkLedgerText, type WorkProjectionContext, type WorkFrozen, type RevisionRef } from "./work-ledger.ts";
3
+ import { canonicalWorkJson, copyWorkJson } from "./work-ledger-json.ts";
4
+ import { readDailySnapshot, parseArchiveProjection, freezeDaily, type ArchiveExecution } from "./daily-view-input.ts";
5
+
6
+ export const DAILY_VIEW_VERSION = "pi-daddy-daily-view-v1" as const;
7
+ export interface DailyViewOptions {
8
+ archiveProjectionPath?: string; workLedgerPath?: string;
9
+ /** Explicit retained adapter bytes; never authority. Mutually exclusive with the corresponding path. */
10
+ archiveBytes?: Uint8Array; workBytes?: Uint8Array;
11
+ sourceManifestBytes?: Readonly<Record<string, Uint8Array>>;
12
+ /** Genuine independently selected host authority only. Never populated from P03 or a context file. */
13
+ workContext?: WorkFrozen<WorkProjectionContext>;
14
+ /** Policy-authorized exact paths keyed by expected raw-manifest SHA256. No producer paths followed. */
15
+ sourceManifestFiles?: Readonly<Record<string, string>>;
16
+ }
17
+ export interface DailyAttempt {
18
+ executionId: string; logicalChildId: string | null; intentRuntime: string;
19
+ archive: ArchiveExecution | null; sourceAvailability: Array<{ sha256: string; state: "available" | "missing" | "mismatch" | "error" }>;
20
+ issues: string[]; activeBranch: null;
21
+ }
22
+ export interface DailyObligation {
23
+ key: string; intent: RevisionRef; obligation: RevisionRef; policy: RevisionRef;
24
+ acceptance: "accepted-under-supplied-authority" | "unaccepted" | "unresolved";
25
+ artifactCoverage: string; evidenceCoverage: string; receiptIds: string[]; claimReferences: string[];
26
+ attempts: string[]; activity: "unstarted" | "attempts-attached"; obstacles: string[];
27
+ }
28
+ export interface DailyView {
29
+ version: typeof DAILY_VIEW_VERSION; readOnly: true; freshness: "snapshot-unknown";
30
+ model: "not-consulted-connectivity-unknown"; continuity: "initial-snapshot" | "resnapshot" | "reconnected-gap";
31
+ scopeChanged: boolean; scope: RevisionRef | null; selectedSnapshot: WorkProjectionContext["selectedSnapshot"];
32
+ scopeState: string; progress: { accepted: number; total: number } | null;
33
+ authority: "supplied-host-context" | "unavailable" | "stale-for-selection"; coverage: "partial";
34
+ sources: { archive: string; work: string; archiveSha256: string | null; workSha256: string | null };
35
+ obligations: DailyObligation[]; attempts: DailyAttempt[]; issues: string[]; narrative: string;
36
+ }
37
+ const refKey = (r: WorkFrozen<RevisionRef>) => `${r.kind}:${r.id}:${r.revision}:${r.digest}`;
38
+ const sorted = (values: readonly string[]) => [...new Set(values)].sort();
39
+ function contextCopy(value: DailyViewOptions["workContext"]): WorkProjectionContext {
40
+ const copied = JSON.parse(canonicalWorkJson(copyWorkJson(value ?? { selectedSnapshot: null, authority: null }))) as WorkProjectionContext;
41
+ if (projectWorkLedger("", copied).errors.some(e => e.code === "WORK_CONTEXT_INVALID")) throw new TypeError("invalid independent P01 context");
42
+ return copied;
43
+ }
44
+ const readers = new WeakSet<object>();
45
+ /** Only actual factory readers may carry continuity state through the dashboard; not arbitrary callbacks. */
46
+ export function isDailyViewReader(value: unknown): value is ReturnType<typeof createDailyViewReader> {
47
+ return typeof value === "function" && readers.has(value);
48
+ }
49
+ /** Each read starts from complete bounded snapshots. The tracker holds only gap/scope metadata, not authority. */
50
+ export function createDailyViewReader() {
51
+ let seen = false, gap = false, lastSelection = "";
52
+ const read = async (options: DailyViewOptions): Promise<DailyView> => {
53
+ try {
54
+ const archivePath = options.archiveProjectionPath, workPath = options.workLedgerPath;
55
+ const context = contextCopy(options.workContext);
56
+ const files = Object.fromEntries(Object.entries(options.sourceManifestFiles ?? {}));
57
+ if (Object.keys(files).length > 128 || Object.entries(files).some(([key, value]) => !/^[a-f0-9]{64}$/.test(key) || typeof value !== "string")) throw new TypeError("bounded explicit source file map required");
58
+ const snapshot = (bytes: Uint8Array | undefined, path: string | undefined, limit: number) => {
59
+ if (bytes === undefined) return readDailySnapshot(path, limit);
60
+ if (path !== undefined || !(bytes instanceof Uint8Array) || bytes.length > limit) throw new TypeError("bounded exclusive retained snapshot required");
61
+ const copy=Buffer.from(bytes); return { status: "read" as const, bytes: copy, sha256: createHash("sha256").update(copy).digest("hex") };
62
+ };
63
+ const entries=Object.entries(options.sourceManifestBytes??{});if(entries.length>128)throw Error("source byte map bound");
64
+ const manifestBytes=Object.fromEntries(entries.map(([key,bytes])=>{
65
+ if(!/^[a-f0-9]{64}$/.test(key)||!(bytes instanceof Uint8Array)||bytes.length>65536)throw Error("bounded source bytes required");return [key,Buffer.from(bytes)];
66
+ }));
67
+ const [archiveRead, workRead] = await Promise.all([snapshot(options.archiveBytes,archivePath, 2*1024*1024), snapshot(options.workBytes,workPath, 16 * 1024 * 1024)]);
68
+ let archiveStatus: string = archiveRead.status, workStatus: string = workRead.status;
69
+ let archive: ArchiveExecution[] = [], work = projectWorkLedger("", context), workText = "";
70
+ const issues: string[] = [];
71
+ if (archiveRead.status === "read") {
72
+ try { archive = parseArchiveProjection(new TextDecoder("utf-8", { fatal: true }).decode(archiveRead.bytes)).executions; }
73
+ catch { archiveStatus = "error"; issues.push("archive-invalid-or-unsupported-version"); }
74
+ }
75
+ if (workRead.status === "read") {
76
+ try { workText = new TextDecoder("utf-8", { fatal: true }).decode(workRead.bytes); work = projectWorkLedger(workText, context); }
77
+ catch { workStatus = "error"; issues.push("work-input-invalid"); }
78
+ if (work.errors.length) { workStatus = "error"; issues.push("work-projection-errors"); }
79
+ }
80
+ const selection = JSON.stringify(context.selectedSnapshot), scopeChanged = seen && selection !== lastSelection;
81
+ const unavailable = archiveStatus !== "read" || ((Boolean(workPath) || options.workBytes !== undefined) && workStatus !== "read");
82
+ if (unavailable) gap = true;
83
+ const continuity = seen ? gap && !unavailable ? "reconnected-gap" : "resnapshot" : "initial-snapshot";
84
+ seen = true; lastSelection = selection;
85
+ if (scopeChanged) issues.push("scope-selection-changed; prior acceptance not carried forward");
86
+ if (gap) issues.push("observation-gap; resnapshot is not continuity");
87
+ if (archiveStatus !== "read") issues.push(`archive-${archiveStatus}`);
88
+ if (workStatus !== "read") issues.push(`work-${workStatus}`);
89
+ // Authority snapshot and selected work snapshot inhabit different identity namespaces.
90
+ // Applicability comes from P01's exact claim/selection/receipt validation, never ID equality.
91
+ const stale = !work.claims.some(c => c.matchedReceiptIds.length) && work.claims.some(c =>
92
+ c.applicability === "superseded" || c.problems.some(p => p.code === "RECEIPT_MISMATCH"));
93
+ const authority: DailyView["authority"] = !context.authority ? "unavailable" : stale ? "stale-for-selection" : "supplied-host-context";
94
+ if (authority !== "supplied-host-context") issues.push(`host-authority-${authority}`);
95
+ const events = workText && work.scopeState === "valid" ? parseWorkLedgerText(workText).events : [];
96
+ const selected = events.find(e => e.event === "work_snapshot" && e.eventId === context.selectedSnapshot?.event.eventId && e.digest === context.selectedSnapshot.event.digest);
97
+ const scope = selected?.event === "work_snapshot" ? selected.payload.snapshot.scope : null;
98
+ const available = new Map<string, DailyAttempt["sourceAvailability"][number]["state"]>();
99
+ const requested = sorted(archive.flatMap(e => e.sourceReferences));
100
+ for (const sha256 of requested) {
101
+ if (manifestBytes[sha256]) { available.set(sha256,createHash("sha256").update(manifestBytes[sha256]).digest("hex")===sha256?"available":"mismatch"); continue; }
102
+ if (!files[sha256]) { available.set(sha256, "missing"); continue; }
103
+ const source = await readDailySnapshot(files[sha256], 64 * 1024);
104
+ available.set(sha256, source.status === "read" ? source.sha256 === sha256 ? "available" : "mismatch" : source.status === "error" ? "error" : "missing");
105
+ }
106
+ const attempts = new Map<string, DailyAttempt>();
107
+ for (const attempt of work.runtime?.attempts ?? []) attempts.set(attempt.executionId, {
108
+ executionId: attempt.executionId, logicalChildId: attempt.childId, intentRuntime: attempt.state, archive: null,
109
+ sourceAvailability: [], issues: attempt.problems.map(p => `P01:${p.code}`), activeBranch: null,
110
+ });
111
+ for (const entry of archive) {
112
+ const a = attempts.get(entry.executionId) ?? { executionId: entry.executionId, logicalChildId: null, intentRuntime: "unknown",
113
+ archive: null, sourceAvailability: [], issues: [], activeBranch: null } as DailyAttempt;
114
+ a.archive = entry; a.issues.push(...entry.issues.map(i => `P03:${i}`));
115
+ a.sourceAvailability = entry.sourceReferences.map(sha256 => ({ sha256, state: available.get(sha256) ?? "missing" }));
116
+ if (!entry.sourceReferences.length) a.issues.push("source-hashes-absent");
117
+ if (a.sourceAvailability.some(r => r.state !== "available")) a.issues.push("source-bytes-unavailable-or-mismatched");
118
+ const intent = work.runtime?.attempts.find(r => r.executionId === entry.executionId);
119
+ if (intent && (entry.parentExecutionIds.length !== 1 || entry.parentExecutionIds[0] !== intent.parentExecutionId)) a.issues.push("P01-P03-parent-disagreement");
120
+ if (intent && intent.state === "running" && entry.runtime === "terminal") a.issues.push("P01-P03-runtime-disagreement");
121
+ a.issues = sorted(a.issues); attempts.set(a.executionId, a);
122
+ }
123
+ const obligations: DailyObligation[] = work.obligations.map(o => {
124
+ const attached = (work.runtime?.attempts ?? []).filter(a => a.bindings.some(b => scope && refKey(b.scope) === refKey(scope) && refKey(b.obligation) === refKey(o.binding.obligation)));
125
+ return {
126
+ key: canonicalWorkJson(copyWorkJson(o.binding)), intent: o.binding.intent, obligation: o.binding.obligation, policy: o.binding.policy,
127
+ acceptance: authority === "supplied-host-context" ? o.acceptance : "unresolved", artifactCoverage: o.artifactCoverage.state,
128
+ evidenceCoverage: o.evidenceCoverage.state,
129
+ receiptIds: sorted(work.claims.filter(c => o.claims.some(r => r.eventId === c.claim.eventId && r.digest === c.claim.digest)).flatMap(c => [...c.matchedReceiptIds])),
130
+ claimReferences: o.claims.map(c => `${c.eventId}:${c.digest}`).sort(), attempts: attached.map(a => a.executionId).sort(),
131
+ activity: attached.length ? "attempts-attached" : "unstarted", obstacles: sorted(o.problems.map(p => `P01:${p.code}`)),
132
+ };
133
+ });
134
+ obligations.sort((a, b) => {
135
+ const left = `${a.obligation.id}:${a.key}`, right = `${b.obligation.id}:${b.key}`;
136
+ return left < right ? -1 : left > right ? 1 : 0;
137
+ });
138
+ const progress = authority === "supplied-host-context" && workStatus === "read" ? work.progress : null;
139
+ issues.push(...work.problems.map(p => `P01:${p.code}`));
140
+ const view: DailyView = { version: DAILY_VIEW_VERSION, readOnly: true, freshness: "snapshot-unknown", model: "not-consulted-connectivity-unknown",
141
+ continuity, scopeChanged, scope, selectedSnapshot: context.selectedSnapshot, scopeState: work.scopeState,
142
+ progress, authority, coverage: "partial",
143
+ sources: { archive: archiveStatus, work: workStatus, archiveSha256: archiveRead.status === "read" ? archiveRead.sha256 : null, workSha256: workRead.status === "read" ? workRead.sha256 : null },
144
+ obligations, attempts: [...attempts.values()].sort((a, b) => a.executionId < b.executionId ? -1 : a.executionId > b.executionId ? 1 : 0), issues: sorted(issues),
145
+ narrative: `${obligations.length} scoped obligation(s); ${progress ? `${progress.accepted}/${progress.total} accepted under supplied P01 authority` : "acceptance unresolved"}; ${attempts.size} distinct attempt(s). Runtime exits do not establish acceptance. Snapshot freshness unknown.`,
146
+ };
147
+ return freezeDaily(view);
148
+ } catch (error) { gap = true; seen = true; throw error; }
149
+ };
150
+ readers.add(read); return read;
151
+ }
152
+ export const readDailyView = (options: DailyViewOptions): Promise<DailyView> => createDailyViewReader()(options);
@@ -1,10 +1,33 @@
1
1
  #!/usr/bin/env node
2
+ import { connectDashboardHost, isDashboardConnection, ENV_DASHBOARD_HOST_SOCKET, type DashboardConnection } from "./dashboard-host-transport.ts";
3
+ import { dashboardHostRequest, type DashboardHostRequest } from "./dashboard-host.ts";
4
+ export { ENV_DASHBOARD_HOST_SOCKET } from "./dashboard-host-transport.ts";
2
5
  import { realpathSync } from "node:fs";
3
6
  import { readFile } from "node:fs/promises";
4
7
  import { resolve } from "node:path";
5
8
  import { pathToFileURL } from "node:url";
6
9
  import { parseDashboardLedger } from "./dashboard-projection.ts";
7
10
  import { renderDashboard } from "./dashboard-render.ts";
11
+ import { createDailyViewReader, isDailyViewReader, readDailyView, type DailyViewOptions } from "./daily-view.ts";
12
+ import { renderDailyView } from "./daily-view-render.ts";
13
+ import { projectWorkLedger, type WorkProjectionContext } from "./work-ledger.ts";
14
+ import { parseRetentionJson } from "./retention-json.ts";
15
+ import { createInterface } from "node:readline";
16
+ import { isDebriefPresenter, type DebriefPresenter } from "./debrief.ts";
17
+ import { renderDebrief, debriefAction } from "./debrief-render.ts";
18
+ import { createFixtureDebrief } from "./debrief-fixture.ts";
19
+ export const ENV_DEBRIEF_FIXTURE = "PI_DADDY_DEBRIEF_FIXTURE";
20
+ export { debriefAction as dashboardDebriefAction } from "./debrief-render.ts";
21
+
22
+ export const ENV_DAILY_ARCHIVE = "PI_DADDY_ARCHIVE_PROJECTION";
23
+ export const ENV_DAILY_WORK = "PI_DADDY_WORK_LEDGER";
24
+ export const ENV_DAILY_SELECTION = "PI_DADDY_WORK_SNAPSHOT";
25
+ function dailySelection(text: string | undefined): WorkProjectionContext["selectedSnapshot"] {
26
+ if (!text) return null;
27
+ const selection = parseRetentionJson(text) as WorkProjectionContext["selectedSnapshot"];
28
+ if (projectWorkLedger("", { selectedSnapshot: selection, authority: null }).errors.some(e => e.code === "WORK_CONTEXT_INVALID")) throw new TypeError("invalid P01 snapshot selection");
29
+ return selection;
30
+ }
8
31
 
9
32
  export const DASHBOARD_PROTOCOL_VERSION = 1 as const;
10
33
  export const ENV_DASHBOARD_LEDGER = "PI_DADDY_LEDGER";
@@ -20,6 +43,19 @@ export interface DashboardFrameOptions {
20
43
  width?: number;
21
44
  details?: boolean;
22
45
  now?: Date;
46
+ dailyView?: DailyViewOptions;
47
+ dailyReader?: ReturnType<typeof createDailyViewReader>;
48
+ dailyJson?: boolean;
49
+ debrief?: DebriefPresenter;
50
+ debriefJson?: boolean;
51
+ connected?: DashboardConnection;
52
+ }
53
+ export async function dashboardHostAction(host:DashboardConnection,line:string){
54
+ if(!isDashboardConnection(host)||Buffer.byteLength(line)>60000)throw Error("genuine bounded dashboard connection required");
55
+ const command=line.trim();
56
+ if(command.startsWith("{"))return host.action(dashboardHostRequest(parseRetentionJson(command,60000) as DashboardHostRequest));
57
+ if(!/^[a-zA-Z0-9:_-]{1,64}$/.test(command))throw Error("dashboard action must be a listed command key");
58
+ return host.humanAction(command);
23
59
  }
24
60
 
25
61
  function shellQuote(value: string): string {
@@ -57,6 +93,31 @@ export async function dashboardFrame(options: DashboardFrameOptions): Promise<st
57
93
  if (options.protocol !== undefined && options.protocol !== DASHBOARD_PROTOCOL_VERSION) {
58
94
  return incompatibleFrame(options.protocol);
59
95
  }
96
+ if(options.connected){
97
+ if(!isDashboardConnection(options.connected))throw Error("original dashboard connection required");
98
+ let view:Awaited<ReturnType<DashboardConnection["frame"]>>;
99
+ try{view=await options.connected.frame();}catch{return "PI-DADDY — CONNECTED HOST UNAVAILABLE\nNo prior acceptance, presentation or continuity substituted. No action retried.";}
100
+ if(options.dailyJson||options.debriefJson)return JSON.stringify(view);
101
+ const source=view.source as {daily?:Parameters<typeof renderDailyView>[0]}|null;
102
+ const actions=(view.actions as {key:string;label:string}[]|undefined)??[];
103
+ return ["PI-DADDY — CONNECTED HOST (snapshot; identity/acceptance not authenticated)",source?.daily?renderDailyView(source.daily,options.width):"Source unavailable; no previous acceptance substituted.",
104
+ view.debrief?renderDebrief(view.debrief as Parameters<typeof renderDebrief>[0],options.width):"Debrief deferred/closed. No implicit presentation or steering.",
105
+ `Attention reserved ${view.attention.attentionUsed}/5 (not proof of delivery). Tip ${view.tip}.`,
106
+ actions.length?`ACTIONS — type one command and Enter\n${actions.map(action=>` ${action.key} — ${action.label}`).join("\n")}`:"ACTIONS — none currently authorized; refresh never acts."].join("\n\n");
107
+ }
108
+ if (options.debrief || options.debriefJson) {
109
+ if (!isDebriefPresenter(options.debrief)) return "PI-DADDY — DEBRIEF UNAVAILABLE: genuine presenter/host missing";
110
+ const view = options.debrief.view(); return options.debriefJson ? JSON.stringify(view) : renderDebrief(view, options.width);
111
+ }
112
+ if (options.dailyView) {
113
+ try {
114
+ if (options.dailyReader !== undefined && !isDailyViewReader(options.dailyReader)) throw new TypeError("untrusted daily reader callback");
115
+ const view = await (options.dailyReader ?? readDailyView)(options.dailyView);
116
+ return options.dailyJson ? JSON.stringify(view) : renderDailyView(view, options.width);
117
+ } catch {
118
+ return "PI-DADDY — DAILY VIEW UNAVAILABLE\nInvalid or unavailable read-only input. No previous acceptance or continuity was substituted.";
119
+ }
120
+ }
60
121
  if (!options.ledgerPath) return setupFrame(options.cwd);
61
122
 
62
123
  const ledgerPath = resolve(options.cwd, options.ledgerPath);
@@ -90,25 +151,43 @@ interface CliOptions {
90
151
  details: boolean;
91
152
  color: boolean;
92
153
  ledgerPath?: string;
154
+ archivePath?: string;
155
+ workPath?: string;
156
+ selection?: string;
157
+ dailyJson: boolean;
158
+ debriefFixture: boolean;
159
+ debriefJson: boolean;
160
+ hostSocket?: string;
93
161
  }
94
162
 
95
163
  function parseArgs(argv: string[]): CliOptions {
96
164
  let once = false;
97
165
  let details = false;
98
166
  let color = process.stdout.isTTY;
99
- let ledgerPath: string | undefined;
167
+ let ledgerPath: string | undefined, archivePath: string | undefined, workPath: string | undefined, selection: string | undefined;
168
+ let dailyJson = false, debriefFixture = false, debriefJson = false;let hostSocket:string|undefined;
100
169
  for (let index = 0; index < argv.length; index += 1) {
101
170
  const arg = argv[index];
102
171
  if (arg === "--once") once = true;
103
172
  else if (arg === "--details") details = true;
104
173
  else if (arg === "--no-color") color = false;
105
174
  else if (arg === "--ledger") ledgerPath = argv[++index];
175
+ else if (arg === "--daily-json") dailyJson = true;
176
+ else if (arg === "--debrief-fixture") debriefFixture = true;
177
+ else if (arg === "--debrief-json") debriefJson = true;
178
+ else if(arg === "--host-socket"){hostSocket=argv[++index];if(!hostSocket||hostSocket.startsWith("--"))throw Error("explicit host socket required");}
179
+ else if (["--archive-projection", "--work-ledger", "--work-snapshot"].includes(arg)) {
180
+ const value = argv[++index]; if (!value || value.startsWith("--")) throw new Error("missing daily view argument");
181
+ if (arg === "--archive-projection") archivePath = value;
182
+ else if (arg === "--work-ledger") workPath = value;
183
+ else selection = value;
184
+ }
106
185
  else throw new Error(`unknown dashboard argument ${JSON.stringify(arg)}`);
107
186
  }
108
- return { once, details, color, ledgerPath };
187
+ return { once, details, color, ledgerPath, archivePath, workPath, selection, dailyJson, debriefFixture, debriefJson, hostSocket };
109
188
  }
110
189
 
111
- export async function runDashboard(argv = process.argv.slice(2), env: NodeJS.ProcessEnv = process.env): Promise<void> {
190
+ export async function runDashboard(argv = process.argv.slice(2), env: NodeJS.ProcessEnv = process.env, host: { debrief?: DebriefPresenter; connected?:DashboardConnection } = {}): Promise<void> {
112
191
  const cli = parseArgs(argv);
113
192
  const cwd = process.cwd();
114
193
  const ledgerPath = cli.ledgerPath ?? (env[ENV_DASHBOARD_LEDGER]?.trim() || undefined);
@@ -117,6 +196,22 @@ export async function runDashboard(argv = process.argv.slice(2), env: NodeJS.Pro
117
196
  const key = env[ENV_DASHBOARD_KEY]?.trim();
118
197
  process.title = `pi-daddy-dashboard${key ? `:${key.slice(0, 12)}` : ""}`;
119
198
 
199
+ const archivePath = cli.archivePath ?? env[ENV_DAILY_ARCHIVE];
200
+ const workPath = cli.workPath ?? env[ENV_DAILY_WORK];
201
+ const selectedSnapshot = dailySelection(cli.selection ?? env[ENV_DAILY_SELECTION]);
202
+ const dailyView = archivePath || workPath || selectedSnapshot || cli.dailyJson ? {
203
+ archiveProjectionPath: archivePath ? resolve(cwd, archivePath) : undefined,
204
+ workLedgerPath: workPath ? resolve(cwd, workPath) : undefined,
205
+ workContext: { selectedSnapshot, authority: null },
206
+ } : undefined;
207
+ const socket=cli.hostSocket??env[ENV_DASHBOARD_HOST_SOCKET];const connected=host.connected??(socket?connectDashboardHost(socket):undefined);
208
+ if(connected&&(host.debrief||cli.debriefFixture||env[ENV_DEBRIEF_FIXTURE]==="1"))throw Error("connected host and separate debrief are exclusive");
209
+ const debrief = host.debrief ?? (cli.debriefFixture || env[ENV_DEBRIEF_FIXTURE] === "1" ? await createFixtureDebrief() : undefined);
210
+ if (debrief) {
211
+ if (!isDebriefPresenter(debrief)) throw new Error("invalid debrief presenter");
212
+ if (!host.debrief) await debrief.open({ mode: "manual", userPresent: true }); // Explicit CLI fixture only; never override a host deferral.
213
+ }
214
+ const dailyReader = createDailyViewReader();
120
215
  let previous = "";
121
216
  const draw = async (clear: boolean): Promise<void> => {
122
217
  const frame = await dashboardFrame({
@@ -126,6 +221,7 @@ export async function runDashboard(argv = process.argv.slice(2), env: NodeJS.Pro
126
221
  color: cli.color,
127
222
  width: process.stdout.columns || 80,
128
223
  details: cli.details,
224
+ dailyView, dailyReader, dailyJson: cli.dailyJson, debrief, debriefJson: cli.debriefJson, connected,
129
225
  });
130
226
  if (frame === previous && !clear) return;
131
227
  previous = frame;
@@ -143,11 +239,19 @@ export async function runDashboard(argv = process.argv.slice(2), env: NodeJS.Pro
143
239
  drawing = true;
144
240
  void draw(true).finally(() => { drawing = false; });
145
241
  };
242
+ const input = debrief || connected ? createInterface({ input: process.stdin, terminal: false }) : null;
243
+ let acting = false;
244
+ input?.on("line", line => {
245
+ if (acting) return;
246
+ acting = true;
247
+ void (connected?dashboardHostAction(connected,line):debriefAction(debrief!, line)).catch(() => { process.stderr.write("Debrief action refused or acknowledgement unknown; inspect/reconcile explicitly.\n"); })
248
+ .finally(() => { acting = false; redraw(); });
249
+ });
146
250
  const timer = setInterval(redraw, DASHBOARD_REFRESH_MS);
147
251
  process.on("SIGWINCH", redraw);
148
252
  await new Promise<void>((settle) => {
149
253
  const stop = () => {
150
- clearInterval(timer);
254
+ clearInterval(timer); input?.close();
151
255
  process.off("SIGWINCH", redraw);
152
256
  settle();
153
257
  };
@@ -0,0 +1,37 @@
1
+ import { constants } from "node:fs";
2
+ import { lstat,realpath,open,mkdir,mkdtemp,writeFile,symlink } from "node:fs/promises";
3
+ import { dirname,join,isAbsolute } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { dataDigest,byteDigest,detached,sha } from "./debrief-contract.ts";
6
+ import { DASHBOARD_HARNESS_PIN,type DashboardHarness } from "./dashboard-host-contract.ts";
7
+ export interface DashboardHarnessArtifact {version:"dashboard-harness-artifact-v1";sourceCommit:typeof DASHBOARD_HARNESS_PIN;files:Record<string,string>;typeboxRoot:string;typeboxPackageSha256:string}
8
+ const loaded=new WeakMap<object,string>();
9
+ export const DASHBOARD_HARNESS_BRIDGE_SOURCE="127b349310dd8f28e5d6b12148a063fce66a77dd";
10
+ const bridgeFunctions=["learningJournal","createTrustLifecycle","openTrustLifecycle","trustPolicyDigest","archivePolicyBinding","ingestPolicySource","readArchiveCheckpoint","readArchiveSource","retainArchiveSource","captureArchivedWorkSignals","readRetainedExecution","projectRetainedExecutions","createWorkCaseReviewer","createWorkSignalReviewer","retainBlindIntervention","openBlindIntervention"] as const;
11
+ /** Adopt the API published by the already-loaded skill-harness extension. Same-process source identity is not human authentication. */
12
+ export function adoptDashboardHarnessBridge(input:unknown):DashboardHarness{
13
+ const value=input as {version?:unknown;sourceCommit?:unknown;api?:unknown};
14
+ if(!value||typeof value!=="object"||Object.keys(value).sort().join()!=="api,sourceCommit,version"||value.version!=="skill-harness-dashboard-bridge-v1"||value.sourceCommit!==DASHBOARD_HARNESS_BRIDGE_SOURCE)throw Error("exact supported harness bridge required");
15
+ const api=value.api as DashboardHarness;if(!api||typeof api!=="object"||!Object.isFrozen(api)||bridgeFunctions.some(name=>typeof (api as unknown as Record<string,unknown>)[name]!=="function"))throw Error("frozen harness API with required source jobs required");
16
+ const digest=dataDigest({version:value.version,sourceCommit:value.sourceCommit,functions:bridgeFunctions});loaded.set(api,digest);return api;
17
+ }
18
+ export const loadedDashboardHarnessDigest=(h:unknown):string|null=>typeof h==="object"&&h!==null?loaded.get(h)??null:null;
19
+ async function privateDirectory(path:string){const stat=await lstat(path);if(!isAbsolute(path)||await realpath(path)!==path||!stat.isDirectory()||stat.mode&0o077||stat.uid!==process.getuid?.())throw Error("canonical private owned harness directory required");}
20
+ async function bytes(path:string){if(await realpath(path)!==path)throw Error("harness artifact alias");const fd=await open(path,constants.O_RDONLY|constants.O_NOFOLLOW|constants.O_NONBLOCK);try{const stat=await fd.stat();if(!stat.isFile()||stat.nlink!==1||stat.mode&0o022||stat.size>1048576)throw Error("bounded immutable artifact required");const value=Buffer.alloc(stat.size+1),n=await fd.read(value,0,value.length,0),after=await fd.stat();if(n.bytesRead!==stat.size||after.size!==stat.size||after.mtimeMs!==stat.mtimeMs||after.ctimeMs!==stat.ctimeMs)throw Error("artifact changed during read");return value.subarray(0,n.bytesRead);}finally{await fd.close();}}
21
+ /** Load approved real compiled artifacts into an exclusive private tree, avoiding stale module-cache paths.
22
+ * Byte identity is NOT human/module authentication or hostile-same-UID confinement. No build/install here.
23
+ * Typebox is the explicit existing peer; its package metadata is bound, not a whole peer-source attestation. */
24
+ export async function loadDashboardHarness(artifactRoot:string,input:DashboardHarnessArtifact,parent:string){
25
+ const manifest=detached(input);if(manifest.version!=="dashboard-harness-artifact-v1"||manifest.sourceCommit!==DASHBOARD_HARNESS_PIN||!sha(manifest.typeboxPackageSha256))throw Error("exact supported harness artifact required");
26
+ const files=Object.entries(manifest.files);if(!files.length||files.length>128||files.some(([p,h])=>!sha(h)||!/^([a-zA-Z0-9_-]+\/)*[a-zA-Z0-9_.-]+\.(js|json)$/.test(p)))throw Error("bounded closed artifact inventory required");
27
+ await privateDirectory(artifactRoot);await privateDirectory(parent);
28
+ if(await realpath(manifest.typeboxRoot)!==manifest.typeboxRoot||byteDigest(await bytes(join(manifest.typeboxRoot,"package.json")))!==manifest.typeboxPackageSha256)throw Error("retained typebox peer changed");
29
+ const content=new Map<string,Buffer>();let total=0;for(const[p,digest]of files){const b=await bytes(join(artifactRoot,p));total+=b.length;if(total>8*1024*1024||byteDigest(b)!==digest)throw Error("harness artifact digest mismatch");content.set(p,b);}
30
+ if(content.get("package.json")?.toString()!=='{"type":"module"}')throw Error("explicit ESM artifact root required");
31
+ const directory=await mkdtemp(join(parent,"loaded-harness-"));for(const[p,b]of content){await mkdir(dirname(join(directory,p)),{recursive:true,mode:0o700});await writeFile(join(directory,p),b,{flag:"wx",mode:0o400});}
32
+ await mkdir(join(directory,"node_modules"));await symlink(manifest.typeboxRoot,join(directory,"node_modules/typebox"));
33
+ const names=["learning-journal","trust-lifecycle","archive-policy","archive-observer","archive-checkpoint","archived-work","execution-retention-archive","work-case-review","blind-intervention","evidence-archive","work-signal-observation","work-signal-cases","work-case-archive"];
34
+ const api=Object.assign({},...await Promise.all([...names.map(n=>"packages/adapters/src/"+n+".js"),"core.js"].map(p=>{if(!content.has(p))throw Error("missing genuine harness entry");return import(pathToFileURL(join(directory,p)).href);}))) as DashboardHarness;
35
+ for(const[p,digest]of files)if(byteDigest(await bytes(join(directory,p)))!==digest)throw Error("loaded harness bytes changed");
36
+ Object.freeze(api);const digest=dataDigest(manifest);loaded.set(api,digest);return Object.freeze({api,artifactDigest:digest,directory,manifest});
37
+ }
@@ -6,7 +6,7 @@ import {
6
6
  ENV_DASHBOARD_KEY,
7
7
  ENV_DASHBOARD_LEDGER,
8
8
  ENV_DASHBOARD_PROTOCOL,
9
- DASHBOARD_PROTOCOL_VERSION,
9
+ DASHBOARD_PROTOCOL_VERSION, ENV_DAILY_ARCHIVE, ENV_DAILY_WORK, ENV_DAILY_SELECTION, ENV_DEBRIEF_FIXTURE, ENV_DASHBOARD_HOST_SOCKET,
10
10
  } from "./dashboard-cli.ts";
11
11
  import { parseReply, type HerdrExec } from "./herdr-cli.ts";
12
12
  import { withFileLock } from "./file-lock.ts";
@@ -263,8 +263,12 @@ export async function openOrReuseDashboard(input: DashboardOpenInput): Promise<D
263
263
  const exec = input.exec ?? dashboardHerdrExec;
264
264
  const ledgerPath = input.ledgerPath.trim() ? resolve(input.cwd, input.ledgerPath) : "";
265
265
  const cwd = resolve(input.cwd);
266
+ // Explicit operator inputs/owned host endpoint only, never an authority file or ambient snapshot.
267
+ // The endpoint keeps original controller handles in its owning process; it is not TUI attachment.
268
+ const dailyEnv = [ENV_DAILY_ARCHIVE, ENV_DAILY_WORK, ENV_DAILY_SELECTION, ENV_DEBRIEF_FIXTURE, ENV_DASHBOARD_HOST_SOCKET]
269
+ .flatMap(name => process.env[name] ? ["--env", `${name}=${process.env[name]}`] : []);
266
270
  const key = createHash("sha256")
267
- .update(`${input.host.workspaceId}\0${input.host.tabId}\0${ledgerPath}`, "utf8")
271
+ .update(`${input.host.workspaceId}\0${input.host.tabId}\0${ledgerPath}${dailyEnv.length ? JSON.stringify([cwd, ...dailyEnv]) : ""}`, "utf8")
268
272
  .digest("hex");
269
273
  await mkdir(dirname(input.statePath), { recursive: true });
270
274
 
@@ -307,6 +311,7 @@ export async function openOrReuseDashboard(input: DashboardOpenInput): Promise<D
307
311
  ...(ledgerPath ? ["--env", `${ENV_DASHBOARD_LEDGER}=${ledgerPath}`] : []),
308
312
  "--env", `${ENV_DASHBOARD_PROTOCOL}=${DASHBOARD_PROTOCOL_VERSION}`,
309
313
  "--env", `${ENV_DASHBOARD_KEY}=${key}`,
314
+ ...dailyEnv,
310
315
  "--no-focus",
311
316
  ];
312
317
  const opened = parseReply(await exec(args));
@@ -0,0 +1,45 @@
1
+ import { dataDigest, detached, freeze, sha } from "./debrief-contract.ts";
2
+ import { controlShape } from "./dispatch-control.ts";
3
+ import type { DebriefHarness, DurableBlindBinding, CaseSelection } from "./debrief-host.ts";
4
+ import type { WorkProjectionContext, WorkFrozen } from "./work-ledger.ts";
5
+ import type { DispatchAuthority } from "./dispatch-control.ts";
6
+ import type { OrdinaryAuthority } from "./ordinary-children.ts";
7
+ import type { ExperimentAuthority } from "./experiment-contract.ts";
8
+ export const DASHBOARD_HARNESS_PIN = "127b349310dd8f28e5d6b12148a063fce66a77dd";
9
+ export interface HostEvent { id: string; prior: string | null; value: Record<string, unknown> }
10
+ export interface HostJournal { read(): HostEvent[]; append(prior: string, value: Record<string, unknown>): HostEvent }
11
+ /** Loaded trusted ports; an interface or caller-supplied pin is NOT module/human authentication. */
12
+ export interface DashboardHarness extends DebriefHarness {
13
+ learningJournal(path: string, initial?: Record<string, unknown>): HostJournal;
14
+ openTrustLifecycle(path: string): { history(): HostEvent[]; inspect(now: number): { policyId: string; attentionUsed: number; tip: string; grantExpansion: false }; expose(id: string, now: number): { mode: string; reason: string; replayed: boolean } };
15
+ createTrustLifecycle(path:string,input:unknown,authorized:readonly string[]):unknown;
16
+ trustPolicyDigest(input:unknown):string;
17
+ retainArchiveSource(root:string,input:unknown):{manifestId:string};
18
+ archivePolicyBinding(path: string, source: string): { policySha256: string; archiveRoot: string };
19
+ ingestPolicySource(path: string, source: string, previous?: string, expectedPolicy?: string): { checkpointId: string; sourceStatus: string; issues?: string[]; [key: string]: unknown };
20
+ readArchiveCheckpoint(root: string, checkpoint: string): { checkpoint: { sourceManifestId: string; issues: string[]; [key: string]: unknown } };
21
+ readArchiveSource(root: string, id: string): { status: string; bytes?: Uint8Array; reference?: { retention: string; sha256: string; [key: string]: unknown } };
22
+ captureArchivedWorkSignals(root: string, id: string, context: WorkFrozen<WorkProjectionContext>, facts: unknown): { caseBatchId: string; observationId: string; linkageManifestId: string };
23
+ readRetainedExecution(root: string, id: string): { projection: unknown };
24
+ projectRetainedExecutions(values: readonly unknown[]): unknown;
25
+ }
26
+ export interface DashboardHostConfig {
27
+ version: "producer-dashboard-host-v1"; trustDirectory: string; trustPolicyId: string; archiveRoot: string; scope: string; author: string;
28
+ policyPath: string; policySha256: string; sources: { id: string; kind: "work" | "retention" | "facts" }[];
29
+ selection: WorkProjectionContext["selectedSnapshot"]; cases: CaseSelection | null; blind: DurableBlindBinding | null;
30
+ budgetDigest: string; experimentDigest: string | null; harnessArtifactDigest: string;
31
+ ordinaryDigest?: string;
32
+ }
33
+ export interface DashboardHostRequest { version: "1.0"; requestId: string; hostDigest: string; expectedTip: string; selectionDigest: string; operation: "observe" | "present" | "presented" | "defer" | "debrief" | "dispatch" | "intent" | "cancel" | "dispatch-reconcile" | "intent-reconcile" | "ordinary-cancel"; payload: unknown }
34
+ export interface DashboardHostAuthority {
35
+ hostDigests: readonly string[]; requestDigests: readonly string[];
36
+ workContext: WorkFrozen<WorkProjectionContext>; dispatch: DispatchAuthority | null; experiment: ExperimentAuthority | null;
37
+ ordinary?: OrdinaryAuthority | null;
38
+ }
39
+ export const dashboardHostDigest = (c: DashboardHostConfig): string => dataDigest(c);
40
+ export const dashboardSelectionDigest = (selection: DashboardHostConfig["selection"]): string => dataDigest(selection);
41
+ export function dashboardHostRequest(input: DashboardHostRequest) {
42
+ const r=detached(input);controlShape(r,["version","requestId","hostDigest","expectedTip","selectionDigest","operation","payload"]);
43
+ if(r.version!=="1.0"||!/^[a-zA-Z0-9:_-]{1,128}$/.test(r.requestId)||![r.hostDigest,r.expectedTip,r.selectionDigest].every(sha)||!["observe","present","presented","defer","debrief","dispatch","intent","cancel","dispatch-reconcile","intent-reconcile","ordinary-cancel"].includes(r.operation)||Buffer.byteLength(JSON.stringify(r))>60000)throw Error("bounded exact dashboard request required");return freeze(r);
44
+ }
45
+ export const dashboardHostRequestDigest = (r: DashboardHostRequest): string => dataDigest(dashboardHostRequest(r));
@@ -0,0 +1,40 @@
1
+ import { createServer, createConnection, type Socket } from "node:net";
2
+ import { lstat, realpath, chmod } from "node:fs/promises";
3
+ import { dirname, isAbsolute } from "node:path";
4
+ import { parseRetentionJson } from "./retention-json.ts";
5
+ import { isDashboardHost, dashboardHostRequest, type DashboardHost, type DashboardHostRequest } from "./dashboard-host.ts";
6
+ export const ENV_DASHBOARD_HOST_SOCKET="PI_DADDY_HOST_SOCKET";
7
+ export type DashboardConnection={frame:DashboardHost["frame"];action:DashboardHost["action"];humanAction:DashboardHost["humanAction"]};
8
+ const connections=new WeakSet<object>();
9
+ export const isDashboardConnection=(v:unknown):v is DashboardConnection=>isDashboardHost(v)||typeof v==="object"&&v!==null&&connections.has(v);
10
+ async function parent(path:string){if(!isAbsolute(path)||Buffer.byteLength(path)>100||await realpath(dirname(path))!==dirname(path))throw Error("canonical bounded private dashboard socket required");const s=await lstat(dirname(path));if(!s.isDirectory()||s.mode&0o077||s.uid!==process.getuid?.())throw Error("private owned dashboard socket parent required");}
11
+ /** Same original host process. No worker socket, native TUI attach, PID lookup or authority transport. */
12
+ export async function serveDashboardHost(path:string,host:DashboardHost){
13
+ if(!isDashboardHost(host))throw Error("original dashboard host required");await parent(path);const clients=new Set<Socket>(),tasks=new Set<Promise<void>>();
14
+ const server=createServer(socket=>{if(clients.size>=4){socket.destroy();return;}clients.add(socket);socket.on("close",()=>clients.delete(socket));socket.on("error",()=>{});socket.setTimeout(5000,()=>socket.destroy());let size=0,used=false;const chunks:Buffer[]=[];
15
+ socket.on("data",bytes=>{if(used)return;size+=bytes.length;if(size>65536){socket.destroy();return;}chunks.push(bytes);if(bytes.at(-1)!==10)return;used=true;
16
+ const task=(async()=>{try{const text=new TextDecoder("utf-8",{fatal:true}).decode(Buffer.concat(chunks));const r=parseRetentionJson(text,65536) as {operation:string;request?:DashboardHostRequest;key?:string};let result:unknown;
17
+ if(Object.keys(r).join()==="operation"&&r.operation==="frame")result=await host.frame();
18
+ else if(Object.keys(r).sort().join()==="operation,request"&&r.operation==="action")result=await host.action(dashboardHostRequest(r.request!));
19
+ else if(Object.keys(r).sort().join()==="key,operation"&&r.operation==="human-action"&&typeof r.key==="string")result=await host.humanAction(r.key);
20
+ else throw Error("closed dashboard protocol required");
21
+ const output=JSON.stringify({ok:true,result})+"\n";if(Buffer.byteLength(output)>512*1024)throw Error("dashboard frame bound exceeded");socket.end(output);
22
+ }catch{socket.end(JSON.stringify({ok:false,error:"refused or acknowledgement unknown; explicit readback only"})+"\n");}})();tasks.add(task);void task.finally(()=>tasks.delete(task));
23
+ });
24
+ });
25
+ await new Promise<void>((resolve,reject)=>{server.once("error",reject);server.listen(path,()=>{server.off("error",reject);resolve();});});
26
+ try{await chmod(path,0o600);}catch(error){server.close();throw error;}
27
+ return Object.freeze({path,close:()=>new Promise<void>((resolve,reject)=>{for(const c of clients)c.destroy();server.close(e=>e?reject(e):void Promise.allSettled([...tasks]).then(()=>resolve()));})});
28
+ }
29
+ /** Explicit operator-selected endpoint. Socket ownership is access policy, not module/human authentication. */
30
+ export function connectDashboardHost(path:string):DashboardConnection{
31
+ const call=async(operation:string,payload:{request?:DashboardHostRequest;key?:string}={})=>{await parent(path);const s=await lstat(path);if(!s.isSocket()||s.mode&0o077||s.uid!==process.getuid?.())throw Error("private original dashboard endpoint unavailable");
32
+ return new Promise<any>((resolve,reject)=>{const socket=createConnection(path);let size=0,done=false;const chunks:Buffer[]=[];const fail=(e:unknown)=>{if(!done){done=true;socket.destroy();reject(e);}};
33
+ socket.setTimeout(5000,()=>fail(Error("dashboard acknowledgement timeout; do not repeat action")));socket.on("error",fail);socket.on("connect",()=>socket.write(JSON.stringify({operation,...payload})+"\n"));
34
+ socket.on("data",b=>{size+=b.length;if(size>512*1024){fail(Error("dashboard frame limit"));return;}chunks.push(b);});
35
+ socket.on("end",()=>{try{const text=new TextDecoder("utf-8",{fatal:true}).decode(Buffer.concat(chunks));const r=parseRetentionJson(text,512*1024) as {ok:boolean;result:unknown};if(r.ok!==true)throw Error("dashboard refused or acknowledgement unknown");if(operation==="frame"&&(r.result as {version?:string})?.version!=="producer-dashboard-frame-v1")throw Error("incompatible dashboard host");if(!done){done=true;resolve(r.result);}}catch(e){fail(e);}});
36
+ socket.on("close",()=>{if(!done)fail(Error("dashboard disconnected; acknowledgement unknown"));});
37
+ });
38
+ };
39
+ const api={frame:()=>call("frame"),action:(r:DashboardHostRequest)=>call("action",{request:dashboardHostRequest(r)}),humanAction:(key:string)=>call("human-action",{key})};connections.add(api);return Object.freeze(api);
40
+ }