@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,223 @@
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 fs from 'node:fs';
37
+ import path from 'node:path';
38
+ import { readBoundedFile } from '../../infra/bounded-file.js';
39
+ import { RECORD_FILE, domainIdOf } from '../../collab/store.js';
40
+ import { isTaskRecord } from '../../collab/validation.js';
41
+ import { registerPiBootstrapGuard, } from './pi-bootstrap-guard.js';
42
+ const TASKS_DIRNAME = 'tasks';
43
+ const MAX_RECORD_BYTES = 8 * 1024 * 1024;
44
+ const MAX_READ_ATTEMPTS = 8;
45
+ const SAFE_TASK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
46
+ function pinDirectory(dir) {
47
+ let stat;
48
+ try {
49
+ stat = fs.lstatSync(dir);
50
+ }
51
+ catch {
52
+ throw new Error(`pi-bootstrap-runtime: required directory is missing: ${dir}`);
53
+ }
54
+ if (stat.isSymbolicLink() || !stat.isDirectory())
55
+ throw new Error(`pi-bootstrap-runtime: path is not a real directory: ${dir}`);
56
+ return { dev: stat.dev, ino: stat.ino };
57
+ }
58
+ function recheckDirectory(dir, pinned) {
59
+ let stat;
60
+ try {
61
+ stat = fs.lstatSync(dir);
62
+ }
63
+ catch {
64
+ throw new Error(`pi-bootstrap-runtime: directory disappeared: ${dir}`);
65
+ }
66
+ if (stat.isSymbolicLink() ||
67
+ !stat.isDirectory() ||
68
+ stat.dev !== pinned.dev ||
69
+ stat.ino !== pinned.ino)
70
+ throw new Error(`pi-bootstrap-runtime: directory identity changed (redirect): ${dir}`);
71
+ }
72
+ function sameObservation(a, b) {
73
+ return (a.ino === b.ino &&
74
+ a.size === b.size &&
75
+ a.mtimeMs === b.mtimeMs &&
76
+ a.ctimeMs === b.ctimeMs);
77
+ }
78
+ class BoundedLocalReader {
79
+ #domainRoot;
80
+ #taskId;
81
+ #tasksDir;
82
+ #taskRoot;
83
+ #recordPath;
84
+ #expectedDomainId;
85
+ #domainIdentity;
86
+ #tasksIdentity;
87
+ #taskIdentity;
88
+ #floorRevision = null;
89
+ #floorDigest = null;
90
+ constructor(domainRoot, taskId) {
91
+ if (typeof domainRoot !== 'string' || !path.isAbsolute(domainRoot))
92
+ throw new RangeError('pi-bootstrap-runtime: domain root must be an absolute path');
93
+ if (typeof taskId !== 'string' || !SAFE_TASK_ID.test(taskId))
94
+ throw new RangeError('pi-bootstrap-runtime: task id must be a short safe identifier');
95
+ // Require a canonical root: a symlink anywhere in the prefix makes realpath differ from the input.
96
+ let canonical;
97
+ try {
98
+ canonical = fs.realpathSync(domainRoot);
99
+ }
100
+ catch {
101
+ throw new Error(`pi-bootstrap-runtime: domain root does not exist: ${domainRoot}`);
102
+ }
103
+ if (canonical !== domainRoot)
104
+ throw new RangeError('pi-bootstrap-runtime: domain root must be canonical (no symlink prefix or alias)');
105
+ this.#domainRoot = domainRoot;
106
+ this.#taskId = taskId;
107
+ this.#tasksDir = path.join(domainRoot, TASKS_DIRNAME);
108
+ this.#taskRoot = path.join(this.#tasksDir, taskId);
109
+ this.#recordPath = path.join(this.#taskRoot, RECORD_FILE);
110
+ this.#expectedDomainId = domainIdOf(domainRoot);
111
+ // Require existing directories and pin their identities once; never create them.
112
+ this.#domainIdentity = pinDirectory(this.#domainRoot);
113
+ this.#tasksIdentity = pinDirectory(this.#tasksDir);
114
+ this.#taskIdentity = pinDirectory(this.#taskRoot);
115
+ }
116
+ #recheckAncestors() {
117
+ recheckDirectory(this.#domainRoot, this.#domainIdentity);
118
+ recheckDirectory(this.#tasksDir, this.#tasksIdentity);
119
+ recheckDirectory(this.#taskRoot, this.#taskIdentity);
120
+ }
121
+ #observeRecordFile() {
122
+ try {
123
+ const stat = fs.lstatSync(this.#recordPath);
124
+ if (stat.isSymbolicLink() || !stat.isFile())
125
+ return null;
126
+ return {
127
+ ino: stat.ino,
128
+ size: stat.size,
129
+ mtimeMs: stat.mtimeMs,
130
+ ctimeMs: stat.ctimeMs,
131
+ };
132
+ }
133
+ catch {
134
+ return null;
135
+ }
136
+ }
137
+ read() {
138
+ let lastReason = 'record unstable during read';
139
+ for (let attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt += 1) {
140
+ // Pinned ancestors must still be the same real directories before the read.
141
+ this.#recheckAncestors();
142
+ const before = this.#observeRecordFile();
143
+ const result = readBoundedFile(this.#recordPath, MAX_RECORD_BYTES);
144
+ if (!result.ok)
145
+ throw new Error(`pi-bootstrap-runtime: record read failed (${result.code})`);
146
+ const after = this.#observeRecordFile();
147
+ // A pinned ancestor replaced DURING the read interval denies this call (namespace redirect).
148
+ this.#recheckAncestors();
149
+ // The record file replaced or changed in place during the read interval: bounded coherent retry.
150
+ if (before === null ||
151
+ after === null ||
152
+ !sameObservation(before, after)) {
153
+ lastReason = 'record changed during read';
154
+ continue;
155
+ }
156
+ let parsed;
157
+ try {
158
+ parsed = JSON.parse(result.text);
159
+ }
160
+ catch {
161
+ throw new Error('pi-bootstrap-runtime: record is not valid JSON');
162
+ }
163
+ if (!isTaskRecord(parsed))
164
+ throw new Error('pi-bootstrap-runtime: record failed schema validation');
165
+ const record = parsed;
166
+ // Complete task-domain identity against the selected canonical root (not just derived domain_id).
167
+ if (record.task_id !== this.#taskId)
168
+ throw new Error('pi-bootstrap-runtime: record task id mismatch');
169
+ if (record.domain_id !== this.#expectedDomainId)
170
+ throw new Error('pi-bootstrap-runtime: record domain id does not derive from the selected root');
171
+ if (record.domain_root !== this.#domainRoot)
172
+ throw new Error('pi-bootstrap-runtime: record domain_root does not match the selected root');
173
+ if (record.task_root !== this.#taskRoot)
174
+ throw new Error('pi-bootstrap-runtime: record task_root does not match the selected task');
175
+ const revision = record.state_revision;
176
+ if (typeof revision !== 'number' ||
177
+ !Number.isSafeInteger(revision) ||
178
+ revision < 0)
179
+ throw new Error('pi-bootstrap-runtime: record has an invalid state_revision');
180
+ const digest = result.digest;
181
+ if (this.#floorRevision !== null) {
182
+ if (revision < this.#floorRevision)
183
+ throw new Error('pi-bootstrap-runtime: record state_revision rolled back');
184
+ if (revision === this.#floorRevision && digest !== this.#floorDigest)
185
+ throw new Error('pi-bootstrap-runtime: record bytes changed at the same state_revision');
186
+ }
187
+ // Advance the floor only after a stable, coherent, validated read; a failure never resets it.
188
+ if (this.#floorRevision === null || revision > this.#floorRevision) {
189
+ this.#floorRevision = revision;
190
+ this.#floorDigest = digest;
191
+ }
192
+ // Fresh parsed data; the floor and pinned identities are primitives, immune to outward mutation.
193
+ return record;
194
+ }
195
+ throw new Error(`pi-bootstrap-runtime: ${lastReason} after ${MAX_READ_ATTEMPTS} attempts`);
196
+ }
197
+ }
198
+ /**
199
+ * Build the bounded local reader for an explicit canonical domain root + task id. Requires existing
200
+ * directories; never creates any. Exposed for focused testing and reuse.
201
+ */
202
+ export function createLocalTaskRecordReader(domainRoot, taskId) {
203
+ return new BoundedLocalReader(domainRoot, taskId);
204
+ }
205
+ /**
206
+ * Register the opt-in successor bootstrap gate wired to the concrete local reader. Configuration is
207
+ * trusted host integration (never model input); the reader is built here (no caller-reader override). The
208
+ * `tool_call` gate is registered synchronously before any model work. Returns the guard's conservative
209
+ * status handle only — no allow/release surface.
210
+ */
211
+ export function registerPiBootstrapRuntime(pi, config) {
212
+ if (typeof config !== 'object' || config === null)
213
+ throw new RangeError('pi-bootstrap-runtime: configuration is required');
214
+ if (config.taskId !== config.guard.taskId)
215
+ throw new RangeError('pi-bootstrap-runtime: runtime task id must equal the guard task id');
216
+ const reader = createLocalTaskRecordReader(config.domainRoot, config.taskId);
217
+ // registerPiBootstrapGuard copies/validates the pinned config; the reader is supplied here, not by a
218
+ // caller, and a reader throw/malformed result becomes the guard's explicit block.
219
+ return registerPiBootstrapGuard(pi, {
220
+ ...config.guard,
221
+ readRecord: () => reader.read(),
222
+ });
223
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * In-process native Pi event-fact capture (rollover observation source).
3
+ *
4
+ * A small, bounded, opt-in producer that subscribes to a fixed set of supported installed Pi 1.3.0
5
+ * extension callbacks and derives conservative in-process facts plus invalidation notifications. It is
6
+ * NOT authority, external control, writer fencing, or a native-stop/quiescence/transfer proof. It never
7
+ * calls the accepted freeze controller's `observe()`, never synthesizes queue/approval numbers, and
8
+ * never infers a stop from balanced tool events. Facts come only from live callback contexts, never from
9
+ * caller-supplied fact JSON.
10
+ *
11
+ * It holds no resources: no timer, watcher, filesystem, process, transcript, network, controller, or
12
+ * rollover import. In-memory only: a restart or reload loses every fact. There is no thaw and no
13
+ * transfer behavior.
14
+ *
15
+ * Conservative identity handling: an unknown, throwing, or malformed session read invalidates the prior
16
+ * session-derived facts (settled confidence and tool-tracking health) and never revalidates them on the
17
+ * next matching identity read; only a later trustworthy supported boundary can re-establish settled, and
18
+ * only a native session change starts fresh tool tracking. Payload reads are guarded so a throwing or
19
+ * proxy accessor is treated as malformed and still notifies subscribers.
20
+ *
21
+ * Notifications are delivered in chronological order under bounded reentrancy. If the bounded buffer or
22
+ * per-drain budget is exceeded, delivery is lost; this is a sticky delivery gap. Rather than ending any
23
+ * subscriber on an older healthy snapshot, the drain discards stale buffered facts and delivers one
24
+ * terminal, conservative gap invalidation to every still-subscribed listener. The gap is sticky: while it
25
+ * is unresolved every snapshot reports `notificationBacklogOverflow`, `settled: 'unknown'` and
26
+ * `toolTrackingHealthy: false`, and no ordinary later event restores healthy confidence.
27
+ *
28
+ * Installed Pi 1.3.0 API pinned from the installed docs/examples (the package ships no .d.ts):
29
+ * docs/extensions.md (sha256:a2b5ff0f95e7e470ed0846d7c4fbfc5781fe7e694b8e7f51ea08859f3ad7325d):
30
+ * lifecycle events `session_start`, `before_agent_start` (a run becomes busy), `agent_settled`
31
+ * (final, "Pi will not continue automatically"), and nested `tool_execution_start` /
32
+ * `tool_execution_end` carrying a string `toolCallId` (`<parent id>/<n>` for nested calls); the
33
+ * running session is read from `ctx.sessionManager.getSessionId()` and idleness from `ctx.isIdle()`
34
+ * (examples/extensions/send-user-message.ts
35
+ * sha256:9261e433c782f354530105e1c9723a19798868f72fd1f5d68af3a4624ad7e446). The narrow structural
36
+ * host interface below matches that shape; structural compatibility is not a shipped-declaration or
37
+ * live-load proof.
38
+ */
39
+ export declare const PI_NATIVE_EVENTS_SCHEMA = "pappra/pi-native-events/1";
40
+ /** The only installed callbacks this producer subscribes. Nothing else is observed. */
41
+ export declare const SUPPORTED_CALLBACKS: readonly ["session_start", "before_agent_start", "agent_settled", "tool_execution_start", "tool_execution_end"];
42
+ export type SupportedCallback = (typeof SUPPORTED_CALLBACKS)[number];
43
+ /** Explicit, non-inferable facts: there is no supported extension API for either. */
44
+ export type UnknownFact = 'unknown';
45
+ /**
46
+ * Honest boundaries of these facts. Shared frozen; a snapshot exposes the same immutable array.
47
+ */
48
+ export declare const NATIVE_EVENT_LIMITATIONS: readonly string[];
49
+ /** A single subscribed callback handler, shaped like the installed `pi.on(event, handler)`. */
50
+ export type PiNativeCallbackHandler = (event: unknown, context: unknown) => void;
51
+ /** Narrow structural host: only `on` for the five supported callbacks is required. */
52
+ export interface PiNativeEventHost {
53
+ on(event: SupportedCallback, handler: PiNativeCallbackHandler): void;
54
+ }
55
+ export interface NativeEventProducerOptions {
56
+ /** The native session expected at registration; every callback re-derives identity from context. */
57
+ readonly sessionId: string;
58
+ /** Bound on tracked open tool ids; exceeding it marks tracking unhealthy (default 4096). */
59
+ readonly maxTrackedTools?: number;
60
+ /** Sequence saturation bound; reaching it stops advancement to avoid a replayable wrap. */
61
+ readonly maxSequence?: number;
62
+ /** Bound on buffered/drained reentrant notifications; exceeding it is a delivery gap (default 1024). */
63
+ readonly maxNotifyQueue?: number;
64
+ }
65
+ /**
66
+ * An immutable snapshot of derived facts. `settled`/`idle` are tri-state: a boolean only when cleanly
67
+ * observed, else `'unknown'`. `observedOpenToolCount` is a local, non-authoritative observation; true
68
+ * in-flight completeness is never established, so it is reported as `inFlightCompleteness: 'unknown'`.
69
+ * Once `notificationBacklogOverflow` is set (a delivery gap), `settled` is forced to `'unknown'` and
70
+ * `toolTrackingHealthy` to `false` and stays so for every later snapshot.
71
+ */
72
+ export interface NativeFactsSnapshot {
73
+ readonly schema: typeof PI_NATIVE_EVENTS_SCHEMA;
74
+ /** Last identity read from a callback context; trust it only when `sessionKnown` is true. */
75
+ readonly sessionId: string | null;
76
+ readonly sessionKnown: boolean;
77
+ readonly settled: boolean | UnknownFact;
78
+ readonly idle: boolean | UnknownFact;
79
+ /** Always `'unknown'`: no supported invariant establishes complete in-flight knowledge. */
80
+ readonly inFlightCompleteness: UnknownFact;
81
+ /** Count of tool ids observed open since the last clean session start; NOT a complete count. */
82
+ readonly observedOpenToolCount: number;
83
+ /**
84
+ * False after any duplicate start, unmatched end, malformed/oversized id, tracking overflow, an
85
+ * unknown-session interval, or a notification delivery gap. Only a native session change can reset the
86
+ * tool-tracking causes; a delivery gap keeps it false.
87
+ */
88
+ readonly toolTrackingHealthy: boolean;
89
+ readonly queue: UnknownFact;
90
+ readonly approvals: UnknownFact;
91
+ /** Never inferred from events; always false. */
92
+ readonly nativeStop: false;
93
+ /** Strictly increasing across every subscribed callback and session change until saturation. */
94
+ readonly sequence: number;
95
+ /** True once the sequence bound is reached; the sequence then stays fixed (no replayable wrap). */
96
+ readonly sequenceSaturated: boolean;
97
+ /** Sticky: true once notification delivery was lost; confidence is then permanently suspect. */
98
+ readonly notificationBacklogOverflow: boolean;
99
+ readonly limitations: readonly string[];
100
+ }
101
+ export interface NativeEventProducer {
102
+ /** Current derived facts as an immutable snapshot. Never fed blindly into a controller. */
103
+ snapshot(): NativeFactsSnapshot;
104
+ /** Subscribe to invalidation; fires in chronological order after every callback. Returns unsubscribe. */
105
+ onInvalidate(listener: (facts: NativeFactsSnapshot) => void): () => void;
106
+ }
107
+ /**
108
+ * Subscribe a fresh producer to the five supported callbacks on `pi`. It only touches the supplied
109
+ * host (`pi.on`); it starts, installs, exports, and wires nothing else. The returned producer yields
110
+ * observations and invalidation notifications only — never authority, control, or a stop proof.
111
+ */
112
+ export declare function registerPiNativeEvents(pi: PiNativeEventHost, options: NativeEventProducerOptions): NativeEventProducer;