@phyzess/pappra 0.1.0-beta.13 → 0.1.0-beta.18

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 (357) hide show
  1. package/README.md +134 -15
  2. package/assets/integrations/skills/pappra/SKILL.md +28 -7
  3. package/assets/integrations/skills/pappra/references/collaboration.md +633 -0
  4. package/assets/integrations/skills/pappra/references/environment.md +360 -0
  5. package/assets/integrations/skills/pappra/references/hosts.md +20 -0
  6. package/assets/integrations/skills/pappra/references/operations.md +36 -5
  7. package/dist/adapters/pi/recovery-extension.d.ts +65 -0
  8. package/dist/adapters/pi/recovery-extension.js +238 -0
  9. package/dist/adapters/pi/session-transition-extension.d.ts +30 -0
  10. package/dist/adapters/pi/session-transition-extension.js +265 -0
  11. package/dist/app/automatic-handoff-events.d.ts +26 -0
  12. package/dist/app/automatic-handoff-events.js +134 -0
  13. package/dist/app/bounded-file.d.ts +2 -0
  14. package/dist/app/bounded-file.js +2 -0
  15. package/dist/app/capability-evidence/claude.d.ts +37 -0
  16. package/dist/app/capability-evidence/claude.js +206 -0
  17. package/dist/app/capability-evidence/codex.d.ts +35 -0
  18. package/dist/app/capability-evidence/codex.js +193 -0
  19. package/dist/app/capability-evidence/index.d.ts +33 -0
  20. package/dist/app/capability-evidence/index.js +117 -0
  21. package/dist/app/capability-evidence/pi.d.ts +73 -0
  22. package/dist/app/capability-evidence/pi.js +669 -0
  23. package/dist/app/capability-evidence/shared.d.ts +69 -0
  24. package/dist/app/capability-evidence/shared.js +195 -0
  25. package/dist/app/collab-auto-handoff.d.ts +55 -0
  26. package/dist/app/collab-auto-handoff.js +384 -0
  27. package/dist/app/collab-bindings.d.ts +57 -0
  28. package/dist/app/collab-bindings.js +1006 -0
  29. package/dist/app/collab-bootstrap-transport.d.ts +163 -0
  30. package/dist/app/collab-bootstrap-transport.js +769 -0
  31. package/dist/app/collab-bootstrap.d.ts +137 -0
  32. package/dist/app/collab-bootstrap.js +3455 -0
  33. package/dist/app/collab-handoff.d.ts +12 -0
  34. package/dist/app/collab-handoff.js +511 -0
  35. package/dist/app/collab-messages.d.ts +2 -0
  36. package/dist/app/collab-messages.js +2 -0
  37. package/dist/app/collab-notify.d.ts +156 -0
  38. package/dist/app/collab-notify.js +1208 -0
  39. package/dist/app/collab-preflight.d.ts +34 -0
  40. package/dist/app/collab-preflight.js +96 -0
  41. package/dist/app/collab-recovery-executor.d.ts +161 -0
  42. package/dist/app/collab-recovery-executor.js +1153 -0
  43. package/dist/app/collab-recovery-projection.d.ts +103 -0
  44. package/dist/app/collab-recovery-projection.js +659 -0
  45. package/dist/app/collab-recovery.d.ts +25 -0
  46. package/dist/app/collab-recovery.js +429 -0
  47. package/dist/app/collab-resources.d.ts +16 -0
  48. package/dist/app/collab-resources.js +278 -0
  49. package/dist/app/collab-role-events.d.ts +22 -0
  50. package/dist/app/collab-role-events.js +329 -0
  51. package/dist/app/collab-role-orchestration.d.ts +53 -0
  52. package/dist/app/collab-role-orchestration.js +601 -0
  53. package/dist/app/collab-role-plan.d.ts +81 -0
  54. package/dist/app/collab-role-plan.js +504 -0
  55. package/dist/app/collab-role-repair.d.ts +109 -0
  56. package/dist/app/collab-role-repair.js +821 -0
  57. package/dist/app/collab-rollover.d.ts +263 -0
  58. package/dist/app/collab-rollover.js +1112 -0
  59. package/dist/app/collab-route.d.ts +64 -0
  60. package/dist/app/collab-route.js +240 -0
  61. package/dist/app/collab-session-transition.d.ts +221 -0
  62. package/dist/app/collab-session-transition.js +3334 -0
  63. package/dist/app/collab-state.d.ts +7 -0
  64. package/dist/app/collab-state.js +162 -0
  65. package/dist/app/collab-task-store.d.ts +22 -0
  66. package/dist/app/collab-task-store.js +204 -0
  67. package/dist/app/collab-work-execution.d.ts +127 -0
  68. package/dist/app/collab-work-execution.js +1027 -0
  69. package/dist/app/collab-work-plan.d.ts +87 -0
  70. package/dist/app/collab-work-plan.js +437 -0
  71. package/dist/app/collaboration.d.ts +8 -2
  72. package/dist/app/collaboration.js +101 -12
  73. package/dist/app/doctor.js +108 -5
  74. package/dist/app/host-mcp.js +34 -8
  75. package/dist/app/host-probe.js +4 -4
  76. package/dist/app/init.d.ts +58 -4
  77. package/dist/app/init.js +768 -155
  78. package/dist/app/managed-pi-bootstrap-admission.d.ts +14 -0
  79. package/dist/app/managed-pi-bootstrap-admission.js +104 -0
  80. package/dist/app/managed-pi-bootstrap-refresh.d.ts +10 -0
  81. package/dist/app/managed-pi-bootstrap-refresh.js +78 -0
  82. package/dist/app/managed-pi-delivery.d.ts +8 -0
  83. package/dist/app/managed-pi-delivery.js +110 -0
  84. package/dist/app/managed-pi-handoff-receiver.d.ts +15 -0
  85. package/dist/app/managed-pi-handoff-receiver.js +263 -0
  86. package/dist/app/managed-pi-handoff-reference.d.ts +3 -0
  87. package/dist/app/managed-pi-handoff-reference.js +60 -0
  88. package/dist/app/managed-pi-herdr.d.ts +21 -0
  89. package/dist/app/managed-pi-herdr.js +185 -0
  90. package/dist/app/managed-pi-initial-admission.d.ts +28 -0
  91. package/dist/app/managed-pi-initial-admission.js +416 -0
  92. package/dist/app/managed-pi-initial-launch.d.ts +5 -0
  93. package/dist/app/managed-pi-initial-launch.js +61 -0
  94. package/dist/app/managed-pi-native-observation.d.ts +36 -0
  95. package/dist/app/managed-pi-native-observation.js +191 -0
  96. package/dist/app/managed-pi-readiness.d.ts +29 -0
  97. package/dist/app/managed-pi-readiness.js +288 -0
  98. package/dist/app/managed-pi-repair-stop.d.ts +21 -0
  99. package/dist/app/managed-pi-repair-stop.js +371 -0
  100. package/dist/app/managed-pi-resume-admission.d.ts +32 -0
  101. package/dist/app/managed-pi-resume-admission.js +618 -0
  102. package/dist/app/managed-pi-retirement.d.ts +28 -0
  103. package/dist/app/managed-pi-retirement.js +112 -0
  104. package/dist/app/managed-pi-sdk-control.d.ts +7 -0
  105. package/dist/app/managed-pi-sdk-control.js +169 -0
  106. package/dist/app/managed-pi-sdk-runtime.d.ts +3 -0
  107. package/dist/app/managed-pi-sdk-runtime.js +483 -0
  108. package/dist/app/managed-pi-source-admission.d.ts +14 -0
  109. package/dist/app/managed-pi-source-admission.js +323 -0
  110. package/dist/app/managed-pi-transition-host.d.ts +36 -0
  111. package/dist/app/managed-pi-transition-host.js +292 -0
  112. package/dist/app/managed-pi.d.ts +54 -0
  113. package/dist/app/managed-pi.js +248 -0
  114. package/dist/app/shell-readiness.d.ts +66 -0
  115. package/dist/app/shell-readiness.js +368 -0
  116. package/dist/app/task-pin.d.ts +2 -23
  117. package/dist/app/task-pin.js +2 -113
  118. package/dist/cli/commands/collaboration-binding.d.ts +13 -0
  119. package/dist/cli/commands/collaboration-binding.js +118 -0
  120. package/dist/cli/commands/collaboration-bootstrap.d.ts +10 -0
  121. package/dist/cli/commands/collaboration-bootstrap.js +84 -0
  122. package/dist/cli/commands/collaboration-capabilities.d.ts +13 -0
  123. package/dist/cli/commands/collaboration-capabilities.js +127 -0
  124. package/dist/cli/commands/collaboration-handoff.d.ts +13 -0
  125. package/dist/cli/commands/collaboration-handoff.js +102 -0
  126. package/dist/cli/commands/collaboration-message.d.ts +17 -0
  127. package/dist/cli/commands/collaboration-message.js +177 -0
  128. package/dist/cli/commands/collaboration-plan.d.ts +8 -0
  129. package/dist/cli/commands/collaboration-plan.js +108 -0
  130. package/dist/cli/commands/collaboration-preflight.d.ts +10 -0
  131. package/dist/cli/commands/collaboration-preflight.js +167 -0
  132. package/dist/cli/commands/collaboration-recovery-run.d.ts +46 -0
  133. package/dist/cli/commands/collaboration-recovery-run.js +224 -0
  134. package/dist/cli/commands/collaboration-recovery.d.ts +11 -0
  135. package/dist/cli/commands/collaboration-recovery.js +104 -0
  136. package/dist/cli/commands/collaboration-resources.d.ts +11 -0
  137. package/dist/cli/commands/collaboration-resources.js +96 -0
  138. package/dist/cli/commands/collaboration-role-plan.d.ts +9 -0
  139. package/dist/cli/commands/collaboration-role-plan.js +69 -0
  140. package/dist/cli/commands/collaboration-role-run.d.ts +11 -0
  141. package/dist/cli/commands/collaboration-role-run.js +128 -0
  142. package/dist/cli/commands/collaboration-rollover.d.ts +10 -0
  143. package/dist/cli/commands/collaboration-rollover.js +103 -0
  144. package/dist/cli/commands/collaboration-state.d.ts +10 -0
  145. package/dist/cli/commands/collaboration-state.js +96 -0
  146. package/dist/cli/commands/collaboration-transition.d.ts +10 -0
  147. package/dist/cli/commands/collaboration-transition.js +75 -0
  148. package/dist/cli/commands/collaboration-work-run.d.ts +34 -0
  149. package/dist/cli/commands/collaboration-work-run.js +178 -0
  150. package/dist/cli/commands/collaboration.d.ts +4 -0
  151. package/dist/cli/commands/collaboration.js +39 -4
  152. package/dist/cli/commands/continuity.js +2 -6
  153. package/dist/cli/commands/docs.d.ts +2 -0
  154. package/dist/cli/commands/docs.js +89 -15
  155. package/dist/cli/commands/host.d.ts +7 -1
  156. package/dist/cli/commands/host.js +122 -7
  157. package/dist/cli/commands/init.d.ts +20 -3
  158. package/dist/cli/commands/init.js +897 -232
  159. package/dist/cli/commands/mcp.d.ts +1 -0
  160. package/dist/cli/commands/mcp.js +11 -5
  161. package/dist/cli/commands/repair.d.ts +12 -0
  162. package/dist/cli/commands/repair.js +41 -0
  163. package/dist/cli/commands/runtime.d.ts +1 -1
  164. package/dist/cli/commands/runtime.js +139 -38
  165. package/dist/cli/commands/session.d.ts +7 -0
  166. package/dist/cli/commands/session.js +82 -0
  167. package/dist/cli/commands/task.js +19 -5
  168. package/dist/cli/run.js +866 -24
  169. package/dist/code/identity.d.ts +7 -0
  170. package/dist/code/identity.js +7 -3
  171. package/dist/code/registration.js +7 -0
  172. package/dist/code/service.d.ts +9 -0
  173. package/dist/code/service.js +16 -3
  174. package/dist/code/text.js +19 -7
  175. package/dist/collab/admission.d.ts +91 -66
  176. package/dist/collab/admission.js +21 -6
  177. package/dist/collab/canonical.d.ts +1 -1
  178. package/dist/collab/canonical.js +44 -3
  179. package/dist/collab/envelope.d.ts +2 -2
  180. package/dist/collab/envelope.js +12 -12
  181. package/dist/collab/first-work.d.ts +3 -0
  182. package/dist/collab/first-work.js +15 -0
  183. package/dist/collab/handoff.d.ts +16 -0
  184. package/dist/collab/handoff.js +39 -5
  185. package/dist/collab/index.d.ts +97 -1
  186. package/dist/collab/index.js +9 -1
  187. package/dist/collab/messages.js +126 -3
  188. package/dist/collab/persisted-messages.d.ts +123 -0
  189. package/dist/collab/persisted-messages.js +496 -0
  190. package/dist/collab/preflight.d.ts +1 -1
  191. package/dist/collab/resources.d.ts +13 -3
  192. package/dist/collab/resources.js +37 -6
  193. package/dist/collab/rollover.d.ts +311 -0
  194. package/dist/collab/rollover.js +1024 -0
  195. package/dist/collab/store.d.ts +1 -0
  196. package/dist/collab/store.js +1 -4
  197. package/dist/collab/types.d.ts +2 -354
  198. package/dist/collab/validation.d.ts +2 -10
  199. package/dist/collab/validation.js +2 -303
  200. package/dist/collab/work-plan.d.ts +105 -0
  201. package/dist/collab/work-plan.js +843 -0
  202. package/dist/continuity/context.d.ts +11 -2
  203. package/dist/continuity/context.js +100 -13
  204. package/dist/continuity/handoff-bundle.d.ts +296 -0
  205. package/dist/continuity/handoff-bundle.js +477 -0
  206. package/dist/continuity/index.d.ts +2 -0
  207. package/dist/continuity/index.js +1 -0
  208. package/dist/continuity/paths.js +10 -2
  209. package/dist/continuity/service.js +91 -15
  210. package/dist/continuity/task-pin.d.ts +24 -0
  211. package/dist/continuity/task-pin.js +590 -0
  212. package/dist/doctor.d.ts +1 -0
  213. package/dist/doctor.js +6 -0
  214. package/dist/experimental/index.d.ts +13 -0
  215. package/dist/experimental/index.js +25 -0
  216. package/dist/experimental/pi.d.ts +4 -0
  217. package/dist/experimental/pi.js +12 -0
  218. package/dist/hosts/claude/native.js +1 -1
  219. package/dist/hosts/codex/native.js +1 -1
  220. package/dist/hosts/codex/protocol.js +1 -1
  221. package/dist/hosts/integration/bootstrap-config.d.ts +58 -0
  222. package/dist/hosts/integration/bootstrap-config.js +254 -0
  223. package/dist/hosts/integration/bundle-export.d.ts +44 -0
  224. package/dist/hosts/integration/bundle-export.js +366 -3
  225. package/dist/hosts/integration/bundle.js +3 -0
  226. package/dist/hosts/integration/hook.js +36 -9
  227. package/dist/hosts/integration/pappra-bootstrap-extension.mjs +80 -0
  228. package/dist/hosts/integration/pappra-extension-runtime.mjs +59 -24631
  229. package/dist/hosts/integration/pappra-hook.mjs +61 -24749
  230. package/dist/hosts/integration/pappra-recovery-extension.mjs +259 -0
  231. package/dist/hosts/integration/pappra-session-transition-extension.mjs +276 -0
  232. package/dist/hosts/integration/pi-bootstrap-extension.d.ts +36 -0
  233. package/dist/hosts/integration/pi-bootstrap-extension.js +101 -0
  234. package/dist/hosts/integration/pi-bootstrap-guard.d.ts +125 -0
  235. package/dist/hosts/integration/pi-bootstrap-guard.js +438 -0
  236. package/dist/hosts/integration/pi-bootstrap-runtime.d.ts +63 -0
  237. package/dist/hosts/integration/pi-bootstrap-runtime.js +223 -0
  238. package/dist/hosts/integration/pi-native-events.d.ts +112 -0
  239. package/dist/hosts/integration/pi-native-events.js +348 -0
  240. package/dist/hosts/integration/pi-rollover-extension.d.ts +166 -0
  241. package/dist/hosts/integration/pi-rollover-extension.js +431 -0
  242. package/dist/hosts/integration/recovery-config.d.ts +48 -0
  243. package/dist/hosts/integration/recovery-config.js +117 -0
  244. package/dist/hosts/integration/service.d.ts +6 -1
  245. package/dist/hosts/integration/service.js +485 -35
  246. package/dist/hosts/integration/support.d.ts +7 -0
  247. package/dist/hosts/integration/support.js +9 -1
  248. package/dist/hosts/integration/templates.d.ts +3 -0
  249. package/dist/hosts/integration/templates.js +26 -3
  250. package/dist/hosts/pi/managed-host-identity.d.ts +2 -0
  251. package/dist/hosts/pi/managed-host-identity.js +40 -0
  252. package/dist/hosts/pi/managed-provider-diagnostics.d.ts +6 -0
  253. package/dist/hosts/pi/managed-provider-diagnostics.js +184 -0
  254. package/dist/hosts/pi/managed-sdk-session.d.ts +335 -0
  255. package/dist/hosts/pi/managed-sdk-session.js +1639 -0
  256. package/dist/hosts/pi/managed-session-extension.d.ts +20 -0
  257. package/dist/hosts/pi/managed-session-extension.js +84 -0
  258. package/dist/hosts/pi/managed-session.d.ts +177 -0
  259. package/dist/hosts/pi/managed-session.js +1557 -0
  260. package/dist/hosts/pi/protocol.js +1 -1
  261. package/dist/hosts/pi/query-result.js +1 -1
  262. package/dist/hosts/pi/resume-capability.d.ts +41 -0
  263. package/dist/hosts/pi/resume-capability.js +33 -0
  264. package/dist/hosts/pi/rpc.d.ts +182 -134
  265. package/dist/hosts/pi/rpc.js +230 -37
  266. package/dist/hosts/pilot.d.ts +97 -74
  267. package/dist/hosts/pilot.js +5 -1
  268. package/dist/hosts/version.d.ts +3 -0
  269. package/dist/hosts/version.js +18 -0
  270. package/dist/index.d.ts +0 -1
  271. package/dist/index.js +0 -1
  272. package/dist/infra/bounded-file.d.ts +19 -0
  273. package/dist/infra/bounded-file.js +66 -0
  274. package/dist/infra/fs.js +10 -2
  275. package/dist/infra/git.d.ts +29 -0
  276. package/dist/infra/git.js +45 -9
  277. package/dist/infra/json-property.d.ts +2 -0
  278. package/dist/infra/json-property.js +69 -0
  279. package/dist/infra/json.d.ts +4 -0
  280. package/dist/infra/json.js +24 -0
  281. package/dist/infra/owned-process-group.js +1 -1
  282. package/dist/infra/private-lock.d.ts +16 -0
  283. package/dist/infra/private-lock.js +148 -0
  284. package/dist/infra/private-state.d.ts +7 -0
  285. package/dist/infra/private-state.js +146 -0
  286. package/dist/infra/process-identity.d.ts +12 -0
  287. package/dist/infra/process-identity.js +142 -0
  288. package/dist/infra/validation.d.ts +5 -0
  289. package/dist/infra/validation.js +15 -0
  290. package/dist/init.d.ts +1 -0
  291. package/dist/init.js +6 -0
  292. package/dist/install/apply.d.ts +9 -1
  293. package/dist/install/apply.js +176 -12
  294. package/dist/install/dependencies.d.ts +38 -0
  295. package/dist/install/dependencies.js +282 -0
  296. package/dist/install/ownership.js +30 -1
  297. package/dist/install/references.d.ts +31 -0
  298. package/dist/install/references.js +208 -0
  299. package/dist/install/repair.d.ts +37 -0
  300. package/dist/install/repair.js +798 -0
  301. package/dist/install/reset-local.d.ts +16 -0
  302. package/dist/install/reset-local.js +304 -0
  303. package/dist/install/rollback.d.ts +3 -0
  304. package/dist/install/rollback.js +9 -3
  305. package/dist/install/runtime.d.ts +10 -6
  306. package/dist/install/runtime.js +299 -59
  307. package/dist/install/source-fingerprint.d.ts +2 -0
  308. package/dist/install/source-fingerprint.js +103 -0
  309. package/dist/knowledge/catalog.d.ts +1 -0
  310. package/dist/knowledge/catalog.js +143 -10
  311. package/dist/knowledge/journal.js +17 -7
  312. package/dist/knowledge/paths.js +2 -1
  313. package/dist/knowledge/refs.js +4 -0
  314. package/dist/knowledge/register-batch.d.ts +4 -0
  315. package/dist/knowledge/register-batch.js +225 -0
  316. package/dist/knowledge/relocate.d.ts +5 -0
  317. package/dist/knowledge/relocate.js +92 -0
  318. package/dist/knowledge/scan.js +103 -47
  319. package/dist/knowledge/service.d.ts +3 -0
  320. package/dist/knowledge/service.js +33 -1
  321. package/dist/knowledge/write.d.ts +4 -1
  322. package/dist/knowledge/write.js +1 -1
  323. package/dist/mcp/server.d.ts +4 -2
  324. package/dist/mcp/server.js +87 -136
  325. package/dist/schemas/collaboration-state-validation.d.ts +6 -0
  326. package/dist/schemas/collaboration-state-validation.js +342 -0
  327. package/dist/schemas/collaboration-state.d.ts +436 -0
  328. package/dist/schemas/collaboration-state.js +1 -0
  329. package/dist/schemas/collaboration.d.ts +335 -0
  330. package/dist/schemas/collaboration.js +272 -0
  331. package/dist/schemas/continuity.d.ts +8 -0
  332. package/dist/schemas/continuity.js +13 -3
  333. package/dist/schemas/doctor.d.ts +1 -0
  334. package/dist/schemas/doctor.js +1 -0
  335. package/dist/schemas/document.d.ts +61 -0
  336. package/dist/schemas/document.js +9 -0
  337. package/dist/schemas/errors.d.ts +1 -0
  338. package/dist/schemas/errors.js +1 -0
  339. package/dist/schemas/host.d.ts +24 -0
  340. package/dist/schemas/host.js +19 -0
  341. package/dist/schemas/install.d.ts +8 -2
  342. package/dist/schemas/install.js +11 -1
  343. package/dist/schemas/managed-pi-execution.d.ts +16 -0
  344. package/dist/schemas/managed-pi-execution.js +2 -0
  345. package/dist/schemas/outcome.d.ts +9 -5
  346. package/dist/workspace/config-file.js +1 -3
  347. package/dist/workspace/documents.js +12 -2
  348. package/dist/workspace/repositories.d.ts +7 -0
  349. package/dist/workspace/repositories.js +14 -5
  350. package/package.json +35 -35
  351. package/skills/pappra/SKILL.md +28 -7
  352. package/skills/pappra/references/collaboration.md +633 -0
  353. package/skills/pappra/references/environment.md +360 -0
  354. package/skills/pappra/references/hosts.md +20 -0
  355. package/skills/pappra/references/operations.md +36 -5
  356. package/dist/app/collab-from.d.ts +0 -35
  357. package/dist/app/collab-from.js +0 -84
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Opt-in successor-bootstrap managed-tool gate (one rollover prerequisite).
3
+ *
4
+ * A separately opt-in Pi `tool_call` guard for the NEW (successor) writer session during a rollover.
5
+ * Registered before any model work, it starts DENIED for every non-vetted-read managed tool
6
+ * (bash/write/edit and any unknown/custom tool); only the fixed vetted native-read set reused from the
7
+ * accepted freeze module is ever allowed, and only for the exact pinned native session. It permits the
8
+ * successor's first non-read tool ONLY after the authoritative, validated current task record proves the
9
+ * completed writer transfer to the pinned successor — read fresh on EVERY call. It does not require a
10
+ * first-work REPORT to permit the first write; that proof comes afterward.
11
+ *
12
+ * Authority is validated against the record itself for cross-record CONSISTENCY, not merely schema
13
+ * validity: the pinned intent in `record.rollover`, the completed WRITER `record.handoffs` entry (kind,
14
+ * subtask/attempt, coordinator epoch, exact from/to, resources and ownership snapshot consistent with the
15
+ * intent), a transfer boundary bounded by the current record and history, the latest actionable attempt
16
+ * owned by the successor with `ownership:'transferred'` and its full confirmed native binding, and every
17
+ * original claim held by the successor at `original + 1`. Duplicate or ambiguous ids anywhere block rather
18
+ * than letting a first-match lookup resolve contradictory records to allow. Every check is fail-closed: a
19
+ * malformed event, a throwing property/record accessor, or any traversal/validation failure returns an
20
+ * explicit block and never escapes into host default behavior.
21
+ *
22
+ * What it is NOT: it is not a transfer action, a stop/quiescence inference, an authority store, or a
23
+ * filesystem/cross-process write fence. It never mutates claims, epochs, handoffs, bindings, messages,
24
+ * the bundle or the archive; it never feeds observations into the freeze controller; it adds no timer,
25
+ * watcher, spawned command, transcript access, network or model call. There is no model-accessible allow/
26
+ * thaw/release method and no boolean authorization callback. The record comes only from an explicitly
27
+ * trusted, read-only host adapter supplied at registration that returns record DATA (never `allowed:true`,
28
+ * a self-signed token or an invented stop proof); this slice does not authenticate a filesystem/remote
29
+ * source, establish freshness against a dishonest adapter, or install a durable reader — a later real
30
+ * adapter must bind and read the actual task domain safely, and this is not complete native authorization.
31
+ *
32
+ * There is no filesystem lock covering a tool's later execution: the guard answers the call-time question
33
+ * only (check/use gap), and background/external processes and effects outside managed Pi tools are never
34
+ * fenced.
35
+ *
36
+ * Installed Pi 1.3.0 `tool_call` blocking contract and the vetted native-read set are reused from the
37
+ * accepted `pi-rollover-extension.ts`; the running session is read from `ctx.sessionManager.getSessionId()`
38
+ * (pinned API). Structural compatibility is not a shipped-declaration or live-load proof.
39
+ */
40
+ import { VETTED_NATIVE_READ_TOOLS, } from './pi-rollover-extension.js';
41
+ import { latestAttemptNumber } from '../../collab/lifecycle.js';
42
+ import { isTaskRecord } from '../../collab/validation.js';
43
+ export const PI_BOOTSTRAP_GUARD_STATUS_SCHEMA = 'pappra/pi-bootstrap-guard-status/1';
44
+ const MAX_ID_LENGTH = 256;
45
+ const MAX_CLAIMS = 1024;
46
+ /**
47
+ * The only current writer states that may hold first-write authority (active + owned + actionable). It is
48
+ * `ACTIVE_STATES` minus `unknown`; `needs_input`, terminal and any unknown/blocked state can never
49
+ * silently grant authority.
50
+ */
51
+ const PERMITTED_WRITER_STATES = new Set([
52
+ 'assigned',
53
+ 'accepted',
54
+ 'running',
55
+ ]);
56
+ const LIMITATIONS = Object.freeze([
57
+ 'managed_pi_tool_gate_not_a_filesystem_or_cross_process_write_fence',
58
+ 'record_from_an_explicitly_trusted_read_only_adapter_not_authenticated_by_this_slice',
59
+ 'answers_the_call_time_question_only_check_use_gap_background_effects_not_fenced',
60
+ 'not_a_transfer_action_stop_inference_authority_store_or_native_authorization',
61
+ 'no_model_accessible_allow_thaw_or_release_and_no_boolean_authorization_callback',
62
+ 'first_work_is_not_a_prerequisite_for_the_first_write_that_proof_comes_afterward',
63
+ 'cross_record_consistency_validated_not_schema_only_ambiguous_ids_fail_closed',
64
+ ]);
65
+ function isSafeId(value) {
66
+ return (typeof value === 'string' &&
67
+ value.length > 0 &&
68
+ value.length <= MAX_ID_LENGTH);
69
+ }
70
+ function isSafeEpoch(value) {
71
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
72
+ }
73
+ /** A claim epoch must leave room for a safe `+1` (original resource epoch raised at transfer). */
74
+ function isSafeRaisableEpoch(value) {
75
+ return isSafeEpoch(value) && value < Number.MAX_SAFE_INTEGER;
76
+ }
77
+ /** Return the single element, or null when there are zero or more than one (ambiguous). */
78
+ function exactlyOne(items) {
79
+ if (items.length !== 1)
80
+ return null;
81
+ const only = items[0];
82
+ return only === undefined ? null : only;
83
+ }
84
+ /** Read the tool name defensively: a throwing/proxy accessor or a non-string yields null (fail-closed). */
85
+ function readToolName(event) {
86
+ try {
87
+ if (typeof event !== 'object' || event === null)
88
+ return null;
89
+ const name = event.toolName;
90
+ return typeof name === 'string' ? name : null;
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ /** Pinned API: the running session is `ctx.sessionManager.getSessionId()`. Any failure stays null. */
97
+ function readLiveSessionId(context) {
98
+ try {
99
+ if (typeof context !== 'object' || context === null)
100
+ return null;
101
+ const manager = context.sessionManager;
102
+ if (typeof manager !== 'object' || manager === null)
103
+ return null;
104
+ const getter = manager.getSessionId;
105
+ if (typeof getter !== 'function')
106
+ return null;
107
+ const value = getter.call(manager);
108
+ return isSafeId(value) ? value : null;
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ function copyIdentity(identity) {
115
+ return Object.freeze({
116
+ agentId: identity.agentId,
117
+ sessionId: identity.sessionId,
118
+ harness: identity.harness,
119
+ });
120
+ }
121
+ /** Two record identities match on agent id, native session id and harness. */
122
+ function sameIdentity(a, b) {
123
+ return (a !== null &&
124
+ a !== undefined &&
125
+ b !== null &&
126
+ b !== undefined &&
127
+ a.agent_id === b.agent_id &&
128
+ a.session_id === b.session_id &&
129
+ a.harness === b.harness);
130
+ }
131
+ /** A record identity matches the pinned successor on agent id, native session id and harness. */
132
+ function matchesSuccessor(identity, successor) {
133
+ return (identity !== null &&
134
+ identity !== undefined &&
135
+ identity.agent_id === successor.agentId &&
136
+ identity.session_id === successor.sessionId &&
137
+ identity.harness === successor.harness);
138
+ }
139
+ function block(reason) {
140
+ return { allow: false, reason };
141
+ }
142
+ /**
143
+ * Pure decision over the current record. Allows a non-read tool only when the validated record proves a
144
+ * consistent completed WRITER transfer to the pinned successor. First-work is NOT required. Any traversal
145
+ * or accessor failure fails closed.
146
+ */
147
+ export function decideBootstrapWrite(record, pinned, liveSessionId) {
148
+ try {
149
+ if (liveSessionId === null || liveSessionId !== pinned.successor.sessionId)
150
+ return block('native_session_unknown_or_mismatch');
151
+ if (!isTaskRecord(record))
152
+ return block('record_invalid');
153
+ const task = record;
154
+ if (task.task_id !== pinned.taskId)
155
+ return block('task_mismatch');
156
+ // Coordinator owner_epoch, never a per-claim resource epoch.
157
+ if ((task.coordinator?.owner_epoch ?? null) !== pinned.coordinatorEpoch)
158
+ return block('coordinator_epoch_drift');
159
+ // Pinned rollover intent read directly from the record; a duplicate/ambiguous id fails closed.
160
+ const intent = exactlyOne((task.rollover ?? []).filter((item) => item.intent_id === pinned.intentId));
161
+ if (intent === null)
162
+ return block('ambiguous_or_missing_intent');
163
+ if (intent.task_id !== pinned.taskId ||
164
+ intent.handoff_id !== pinned.handoffId ||
165
+ intent.generation !== pinned.generation ||
166
+ intent.subtask_id !== pinned.subtaskId ||
167
+ intent.attempt !== pinned.attempt ||
168
+ intent.input_revision !== pinned.inputRevision ||
169
+ intent.coordinator_epoch !== pinned.coordinatorEpoch ||
170
+ !matchesSuccessor(intent.to, pinned.successor))
171
+ return block('intent_mismatch');
172
+ // The claim set the transfer concerns: the intent originals (deduplicated + pinned-consistent later).
173
+ const intentClaims = intent.claims ?? [];
174
+ const intentIds = new Set(intentClaims.map((item) => item.claim_id));
175
+ if (intentIds.size !== intentClaims.length)
176
+ return block('intent_claims_duplicated');
177
+ // Completed WRITER handoff, consistent with the pinned intent (not merely a completed id match).
178
+ const handoff = exactlyOne((task.handoffs ?? []).filter((item) => item.handoff_id === pinned.handoffId));
179
+ if (handoff === null)
180
+ return block('ambiguous_or_missing_handoff');
181
+ if (handoff.state !== 'completed')
182
+ return block('handoff_not_completed');
183
+ if (handoff.kind !== 'writer')
184
+ return block('handoff_not_writer');
185
+ if (handoff.subtask_id !== pinned.subtaskId ||
186
+ handoff.attempt !== pinned.attempt)
187
+ return block('handoff_target_mismatch');
188
+ if (handoff.owner_epoch !== pinned.coordinatorEpoch)
189
+ return block('handoff_epoch_mismatch');
190
+ if (!sameIdentity(handoff.from, intent.from))
191
+ return block('handoff_from_mismatch');
192
+ if (!matchesSuccessor(handoff.to, pinned.successor))
193
+ return block('handoff_to_mismatch');
194
+ // `beginHandoff` records the pre-transfer resources as claim ids and an ownership snapshot of the
195
+ // original holder/epoch. Both must match the intent originals.
196
+ const resourceIds = new Set(handoff.resources ?? []);
197
+ if (resourceIds.size !== intentIds.size)
198
+ return block('handoff_resources_mismatch');
199
+ for (const id of intentIds)
200
+ if (!resourceIds.has(id))
201
+ return block('handoff_resources_mismatch');
202
+ const snapshot = handoff.ownership_snapshot ?? [];
203
+ const snapIds = new Set(snapshot.map((item) => item.claim_id));
204
+ if (snapIds.size !== snapshot.length)
205
+ return block('ownership_snapshot_duplicated');
206
+ if (snapIds.size !== intentIds.size)
207
+ return block('ownership_snapshot_mismatch');
208
+ for (const id of intentIds)
209
+ if (!snapIds.has(id))
210
+ return block('ownership_snapshot_mismatch');
211
+ // Transfer boundary must be bounded by the current record and history (a record cannot predate its
212
+ // own completed transfer); keep safe-integer checks.
213
+ const boundary = intent.transfer_boundary ?? null;
214
+ if (boundary === null)
215
+ return block('transfer_not_bounded');
216
+ if (!Number.isSafeInteger(boundary.state_revision) ||
217
+ !Number.isSafeInteger(boundary.message_count))
218
+ return block('transfer_boundary_unsafe');
219
+ if (boundary.state_revision > task.state_revision)
220
+ return block('transfer_boundary_state_revision_out_of_range');
221
+ if (boundary.message_count > (task.messages ?? []).length)
222
+ return block('transfer_boundary_message_count_out_of_range');
223
+ // Successor native pinned at the transfer boundary must be the configured Pi successor.
224
+ const successorNative = intent.successor_native ?? null;
225
+ if (successorNative === null ||
226
+ successorNative.agent_id !== pinned.successor.agentId ||
227
+ successorNative.session_id !== pinned.successor.sessionId ||
228
+ successorNative.runner !== pinned.successor.harness)
229
+ return block('successor_native_mismatch');
230
+ // Latest actionable attempt, owned by the successor via a completed transfer, with a full native bind.
231
+ if (latestAttemptNumber(task, pinned.subtaskId) !== pinned.attempt)
232
+ return block('attempt_superseded');
233
+ const attempt = exactlyOne((task.attempts ?? []).filter((item) => item.subtask_id === pinned.subtaskId &&
234
+ item.attempt === pinned.attempt));
235
+ if (attempt === null)
236
+ return block('ambiguous_or_missing_attempt');
237
+ if (!matchesSuccessor(attempt.assignee, pinned.successor))
238
+ return block('attempt_not_reassigned');
239
+ if (attempt.input_revision !== pinned.inputRevision)
240
+ return block('attempt_input_drift');
241
+ if (!PERMITTED_WRITER_STATES.has(attempt.state))
242
+ return block('attempt_not_actionable');
243
+ if (typeof attempt.awaiting === 'string' && attempt.awaiting.length > 0)
244
+ return block('attempt_blocked');
245
+ if (attempt.ownership !== 'transferred')
246
+ return block('ownership_not_transferred');
247
+ if (attempt.native.observation !== 'confirmed')
248
+ return block('native_binding_not_confirmed');
249
+ if (attempt.native.session_id !== pinned.successor.sessionId)
250
+ return block('native_session_binding_mismatch');
251
+ if (attempt.native.runner !== successorNative.runner ||
252
+ attempt.native.session_id !== successorNative.session_id ||
253
+ attempt.native.run_id !== successorNative.run_id ||
254
+ (attempt.native.mission_id ?? null) !==
255
+ (successorNative.mission_id ?? null))
256
+ return block('native_binding_mismatch');
257
+ if (attempt.native.runner !== pinned.successor.harness)
258
+ return block('native_runner_mismatch');
259
+ // Exact set equality: pinned claims == intent originals. Each original epoch matches the intent and the
260
+ // ownership snapshot (original holder was the source), and each current record claim is active, held by
261
+ // the successor, at original + 1. A duplicate/ambiguous claim id fails closed.
262
+ const pinnedOriginals = new Map(pinned.claims.map((item) => [item.claimId, item.originalResourceEpoch]));
263
+ if (pinnedOriginals.size !== intentIds.size)
264
+ return block('claim_set_mismatch');
265
+ for (const id of intentIds)
266
+ if (!pinnedOriginals.has(id))
267
+ return block('claim_set_mismatch');
268
+ for (const intentClaim of intentClaims) {
269
+ const pinnedOriginal = pinnedOriginals.get(intentClaim.claim_id);
270
+ if (pinnedOriginal === undefined)
271
+ return block('claim_set_mismatch');
272
+ if (!isSafeRaisableEpoch(intentClaim.resource_epoch))
273
+ return block('claim_epoch_unsafe');
274
+ if (pinnedOriginal !== intentClaim.resource_epoch)
275
+ return block('claim_original_epoch_mismatch');
276
+ const snap = snapshot.find((item) => item.claim_id === intentClaim.claim_id) ?? null;
277
+ if (snap === null)
278
+ return block('ownership_snapshot_mismatch');
279
+ if ((snap.resource_epoch ?? null) !== intentClaim.resource_epoch)
280
+ return block('ownership_snapshot_epoch_mismatch');
281
+ if (!sameIdentity(snap.holder ?? null, intent.from))
282
+ return block('ownership_snapshot_holder_mismatch');
283
+ const current = exactlyOne((task.claims ?? []).filter((item) => item.claim_id === intentClaim.claim_id));
284
+ if (current === null)
285
+ return block('ambiguous_or_missing_claim');
286
+ if (current.released_at !== null)
287
+ return block('claim_released');
288
+ if (!matchesSuccessor(current.holder, pinned.successor))
289
+ return block('claim_not_held_by_successor');
290
+ if (current.resource_epoch !== intentClaim.resource_epoch + 1)
291
+ return block('claim_resource_epoch_not_raised');
292
+ }
293
+ // No other open handoff may overlap this attempt or any pinned claim id (resources are claim ids).
294
+ for (const other of task.handoffs ?? []) {
295
+ if (other.handoff_id === pinned.handoffId)
296
+ continue;
297
+ if (other.state === 'completed' || other.state === 'aborted')
298
+ continue;
299
+ if (other.subtask_id === pinned.subtaskId &&
300
+ other.attempt === pinned.attempt)
301
+ return block('conflicting_handoff');
302
+ for (const snap of other.ownership_snapshot ?? [])
303
+ if (intentIds.has(snap.claim_id))
304
+ return block('conflicting_handoff');
305
+ for (const resource of other.resources ?? [])
306
+ if (intentIds.has(resource))
307
+ return block('conflicting_handoff');
308
+ }
309
+ return { allow: true, reason: 'completed_transfer_to_successor' };
310
+ }
311
+ catch {
312
+ // Any accessor/traversal/validation exception is a conservative block, never a raw rejection.
313
+ return block('record_traversal_failed');
314
+ }
315
+ }
316
+ class BootstrapGuard {
317
+ #pinned;
318
+ #readRecord;
319
+ constructor(pinned, readRecord) {
320
+ this.#pinned = pinned;
321
+ this.#readRecord = readRecord;
322
+ }
323
+ async decide(event, context) {
324
+ try {
325
+ const liveBefore = readLiveSessionId(context);
326
+ // Unknown/malformed native identity blocks even vetted reads.
327
+ if (liveBefore === null ||
328
+ liveBefore !== this.#pinned.successor.sessionId)
329
+ return deny('native_session_unknown_or_mismatch');
330
+ const toolName = readToolName(event);
331
+ if (toolName === null)
332
+ return deny('malformed_tool_event');
333
+ if (VETTED_NATIVE_READ_TOOLS.includes(toolName))
334
+ return undefined;
335
+ // Non-read tool: read the current full record from the trusted adapter and revalidate every time.
336
+ let record;
337
+ try {
338
+ record = await this.#readRecord();
339
+ }
340
+ catch {
341
+ return deny('trusted_record_read_failed');
342
+ }
343
+ // Recheck the native session after the asynchronous read, before deciding.
344
+ const liveAfter = readLiveSessionId(context);
345
+ if (liveAfter === null || liveAfter !== this.#pinned.successor.sessionId)
346
+ return deny('native_session_drift');
347
+ const decision = decideBootstrapWrite(record, this.#pinned, liveAfter);
348
+ return decision.allow ? undefined : deny(decision.reason);
349
+ }
350
+ catch {
351
+ // No raw exception ever escapes into host default behavior.
352
+ return deny('guard_internal_error');
353
+ }
354
+ }
355
+ status() {
356
+ const status = {
357
+ schema: PI_BOOTSTRAP_GUARD_STATUS_SCHEMA,
358
+ taskId: this.#pinned.taskId,
359
+ intentId: this.#pinned.intentId,
360
+ handoffId: this.#pinned.handoffId,
361
+ generation: this.#pinned.generation,
362
+ subtaskId: this.#pinned.subtaskId,
363
+ attempt: this.#pinned.attempt,
364
+ coordinatorEpoch: this.#pinned.coordinatorEpoch,
365
+ successor: copyIdentity(this.#pinned.successor),
366
+ pinnedClaimCount: this.#pinned.claims.length,
367
+ mode: 'deny-until-completed-transfer',
368
+ vettedReadTools: Object.freeze([...VETTED_NATIVE_READ_TOOLS]),
369
+ limitations: LIMITATIONS,
370
+ };
371
+ return Object.freeze(status);
372
+ }
373
+ }
374
+ function deny(reason) {
375
+ return { block: true, reason: `bootstrap-guard: ${reason}` };
376
+ }
377
+ function validateIdentity(value) {
378
+ if (!isSafeId(value.agentId) ||
379
+ !isSafeId(value.sessionId) ||
380
+ !isSafeId(value.harness))
381
+ throw new RangeError('pi-bootstrap-guard: invalid successor identity');
382
+ return copyIdentity(value);
383
+ }
384
+ /** Deep-copy and bound-check options so a later mutation of the caller object cannot change the config. */
385
+ function pinOptions(options) {
386
+ if (!isSafeId(options.taskId) ||
387
+ !isSafeId(options.intentId) ||
388
+ !isSafeId(options.handoffId) ||
389
+ !isSafeId(options.subtaskId) ||
390
+ !isSafeId(options.inputRevision))
391
+ throw new RangeError('pi-bootstrap-guard: invalid pinned identifiers');
392
+ if (!isSafeEpoch(options.generation) ||
393
+ !isSafeEpoch(options.attempt) ||
394
+ !isSafeEpoch(options.coordinatorEpoch))
395
+ throw new RangeError('pi-bootstrap-guard: invalid generation/attempt/epoch');
396
+ if (!Array.isArray(options.claims) ||
397
+ options.claims.length === 0 ||
398
+ options.claims.length > MAX_CLAIMS)
399
+ throw new RangeError('pi-bootstrap-guard: claims must be a non-empty bounded set');
400
+ const seen = new Set();
401
+ const claims = options.claims.map((claim) => {
402
+ if (!isSafeId(claim.claimId) ||
403
+ !isSafeRaisableEpoch(claim.originalResourceEpoch))
404
+ throw new RangeError('pi-bootstrap-guard: invalid claim reference');
405
+ if (seen.has(claim.claimId))
406
+ throw new RangeError('pi-bootstrap-guard: duplicate pinned claim id');
407
+ seen.add(claim.claimId);
408
+ return Object.freeze({
409
+ claimId: claim.claimId,
410
+ originalResourceEpoch: claim.originalResourceEpoch,
411
+ });
412
+ });
413
+ return Object.freeze({
414
+ taskId: options.taskId,
415
+ intentId: options.intentId,
416
+ handoffId: options.handoffId,
417
+ generation: options.generation,
418
+ subtaskId: options.subtaskId,
419
+ attempt: options.attempt,
420
+ inputRevision: options.inputRevision,
421
+ coordinatorEpoch: options.coordinatorEpoch,
422
+ successor: validateIdentity(options.successor),
423
+ claims: Object.freeze(claims),
424
+ });
425
+ }
426
+ /**
427
+ * Register the opt-in successor bootstrap gate on `pi`. It only subscribes `tool_call`; it registers no
428
+ * model-accessible command, removes no tools/MCP/skills/hooks, and mutates nothing. The returned handle
429
+ * exposes a conservative frozen status only — no allow/thaw/release.
430
+ */
431
+ export function registerPiBootstrapGuard(pi, options) {
432
+ if (typeof options.readRecord !== 'function')
433
+ throw new RangeError('pi-bootstrap-guard: a trusted readRecord adapter is required');
434
+ const pinned = pinOptions(options);
435
+ const guard = new BootstrapGuard(pinned, options.readRecord);
436
+ pi.on('tool_call', (event, context) => guard.decide(event, context));
437
+ return guard;
438
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Concrete local task-record reader + opt-in bootstrap-guard registration (one rollover prerequisite).
3
+ *
4
+ * This supplies the accepted successor bootstrap guard (`pi-bootstrap-guard.ts`) with a real, bounded,
5
+ * read-only task-record dependency and wires the actual `tool_call` gate. It is NOT old-writer stop, a
6
+ * transfer executor, automatic rollover, or an installation; it changes no accepted guard/core/host code.
7
+ *
8
+ * Reader contract (fail-closed): it reads ONLY `<domainRoot>/tasks/<taskId>/task.json` for an explicit,
9
+ * trusted, CANONICAL absolute domain root (rejected at registration if a symlink prefix means
10
+ * `realpathSync(root) !== root`) and a short safe task id. The three ancestor directories (domain root,
11
+ * `tasks`, task root) are pinned by device/inode once and re-checked by no-follow `lstat` both BEFORE and
12
+ * AFTER the bounded read, so a replaced/redirected/symlinked ancestor during the read interval denies the
13
+ * call instead of silently rebinding; it never creates directories (it does NOT use
14
+ * `resolveDomainRoot`/`taskRootFor`, which `mkdir`) and never resolves from cwd or a cache. The record is
15
+ * read via `src/infra/bounded-file.ts` (`readBoundedFile`): a bounded (8 MiB), regular-file,
16
+ * no-follow, non-blocking descriptor read refusing symlink/FIFO/device/directory targets and oversized
17
+ * content. The record file's observable identity and change metadata (inode, size, mtime, ctime) are
18
+ * captured before and after the read; a replacement or in-place change during the read is resolved with a
19
+ * strictly bounded coherent retry (the record inode is never pinned forever, so normal atomic store updates
20
+ * between calls are supported). Only a stable, coherent read is parsed, schema-validated (`isTaskRecord`)
21
+ * and identity-checked: `task_id`, `domain_id` (= `domainIdOf(domainRoot)`), `domain_root` and `task_root`
22
+ * must all equal the selected canonical root. An in-memory revision/digest floor rejects a lower
23
+ * `state_revision` (rollback) or different bytes at the same revision, and is NOT reset by a failed read
24
+ * (same revision + same bytes is repeatable). Any violation/read-failure/malformed result throws, which the
25
+ * guard turns into an explicit block; a fresh parsed object is returned and the floor/pinned identities are
26
+ * primitives, immune to outward mutation.
27
+ *
28
+ * Boundaries (explicit): rollback detection is for THIS runtime only — not durable restart fencing, and not
29
+ * authentication against arbitrary user-owned filesystem changes (explicit local-root trust boundary). The
30
+ * reader detects a change DURING its own read; there is still an UNAVOIDABLE check/use interval AFTER
31
+ * `read()` returns, before the tool actually runs. There is no filesystem lock, atomic global snapshot, OS
32
+ * write fence, native-stop proof, old-writer stop, transfer executor, automatic rollover, or installation
33
+ * here. No writes, locks, sidecars, watchers, timers, process/model/network/transcript calls, new authority
34
+ * store, or automatic recovery. The imported guard/store/validation APIs are read-only dependencies.
35
+ */
36
+ import type { TaskRecord } from '../../collab/types.js';
37
+ import { type BootstrapGuardOptions, type PiBootstrapGuard, type PiBootstrapGuardHost } from './pi-bootstrap-guard.js';
38
+ /** The accepted guard's pinned configuration, minus the reader this slice supplies. */
39
+ export type BootstrapGuardPinned = Omit<BootstrapGuardOptions, 'readRecord'>;
40
+ export interface BootstrapRuntimeConfig {
41
+ /** Canonical, existing, absolute domain root (trusted host integration; never model-supplied). */
42
+ readonly domainRoot: string;
43
+ /** Exact short task id; must equal the guard's `taskId`. */
44
+ readonly taskId: string;
45
+ /** The accepted guard's pinned configuration (no caller reader override). */
46
+ readonly guard: BootstrapGuardPinned;
47
+ }
48
+ /** The bounded local reader. `read()` returns the current validated record or throws (fail-closed). */
49
+ export interface LocalTaskRecordReader {
50
+ read(): TaskRecord;
51
+ }
52
+ /**
53
+ * Build the bounded local reader for an explicit canonical domain root + task id. Requires existing
54
+ * directories; never creates any. Exposed for focused testing and reuse.
55
+ */
56
+ export declare function createLocalTaskRecordReader(domainRoot: string, taskId: string): LocalTaskRecordReader;
57
+ /**
58
+ * Register the opt-in successor bootstrap gate wired to the concrete local reader. Configuration is
59
+ * trusted host integration (never model input); the reader is built here (no caller-reader override). The
60
+ * `tool_call` gate is registered synchronously before any model work. Returns the guard's conservative
61
+ * status handle only — no allow/release surface.
62
+ */
63
+ export declare function registerPiBootstrapRuntime(pi: PiBootstrapGuardHost, config: BootstrapRuntimeConfig): PiBootstrapGuard;