@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,16 @@
1
+ export interface LocalResetResult {
2
+ readonly status: 'planned' | 'reset' | 'no-local-state';
3
+ readonly archive: string | null;
4
+ readonly resumed: boolean;
5
+ }
6
+ export interface LocalResetOptions {
7
+ readonly apply: boolean;
8
+ readonly allWritersStopped: boolean;
9
+ readonly assertNoLiveLocks: () => Promise<void>;
10
+ /** Explicit dependency seam used only by hard-crash tests. */
11
+ readonly afterArchive?: () => Promise<void> | void;
12
+ readonly recoverUnknownResetLock?: (file: string) => Promise<void>;
13
+ }
14
+ export declare function inspectLocalResetState(root: string): Promise<'none' | 'pending' | 'complete' | 'unreadable'>;
15
+ /** Explicit fresh beta state; old bytes are archived, never migrated or deleted. */
16
+ export declare function resetLocalState(root: string, options: LocalResetOptions): Promise<LocalResetResult>;
@@ -0,0 +1,304 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readdir, rename } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { z } from 'zod';
5
+ import { observePath, readFileBounded } from '../infra/fs.js';
6
+ import { ensurePrivateState } from '../infra/private-state.js';
7
+ import { inspectPrivateLock, publishPrivateLock, reclaimDeadPrivateLock, } from '../infra/private-lock.js';
8
+ import { captureProcessIdentity, isLocalProcessIdentity, observeProcessIdentity, } from '../infra/process-identity.js';
9
+ import { ensureStatePathBoundary, readJsonDocument, releaseInstallLock, writeJsonAtomic, } from './ownership.js';
10
+ const identitySchema = z.strictObject({
11
+ dev: z.number().int().nonnegative(),
12
+ ino: z.number().int().nonnegative(),
13
+ });
14
+ const resetSchema = z.strictObject({
15
+ schema: z.literal('pappra/local-reset/1'),
16
+ id: z.uuid(),
17
+ root: z.string(),
18
+ archive: z.string(),
19
+ source: identitySchema,
20
+ fresh: identitySchema.nullable(),
21
+ status: z.enum(['prepared', 'archived', 'fresh']),
22
+ createdAt: z.string(),
23
+ allWritersStopped: z.literal(true),
24
+ ownership: z.literal('operator-assertion-and-bounded-live-owner-checks'),
25
+ });
26
+ export async function inspectLocalResetState(root) {
27
+ try {
28
+ const file = join(root, '.pappra/local-archive/pending-reset.json');
29
+ const boundary = await ensureStatePathBoundary(root, file);
30
+ if (!boundary.ok)
31
+ return 'unreadable';
32
+ const read = await readJsonDocument(file, resetSchema);
33
+ if (!read.exists)
34
+ return 'none';
35
+ if (read.value === null || read.value.root !== root)
36
+ return 'unreadable';
37
+ return read.value.status === 'fresh' ? 'complete' : 'pending';
38
+ }
39
+ catch {
40
+ return 'unreadable';
41
+ }
42
+ }
43
+ async function assertNoLiveManagedOwners(root) {
44
+ const local = join(root, '.pappra/local');
45
+ let seen = 0;
46
+ const walk = async (directory, depth) => {
47
+ if (depth > 12)
48
+ throw new Error('Managed ownership inventory exceeds its depth bound.');
49
+ const boundary = await ensureStatePathBoundary(root, directory);
50
+ if (!boundary.ok)
51
+ throw new Error(boundary.message);
52
+ const observation = await observePath(directory);
53
+ if (observation.kind === 'missing')
54
+ return;
55
+ if (observation.kind !== 'directory')
56
+ throw new Error('Private state directory is redirected or unsafe.');
57
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
58
+ if (++seen > 5000)
59
+ throw new Error('Managed ownership inventory exceeds 5000 entries.');
60
+ if (entry.isSymbolicLink())
61
+ continue;
62
+ if (entry.isDirectory()) {
63
+ if (![
64
+ 'transcripts',
65
+ 'pi-sessions',
66
+ 'checkpoints',
67
+ 'patches',
68
+ 'node_modules',
69
+ ].includes(entry.name))
70
+ await walk(join(directory, entry.name), depth + 1);
71
+ continue;
72
+ }
73
+ if (entry.name !== 'task.json' || !entry.isFile())
74
+ continue;
75
+ const read = await readFileBounded(join(directory, entry.name), 1024 * 1024);
76
+ if (!read.ok)
77
+ throw new Error('Managed task metadata cannot be inspected; preserve local state.');
78
+ let record;
79
+ try {
80
+ record = JSON.parse(read.value.bytes.toString('utf8'));
81
+ }
82
+ catch {
83
+ continue;
84
+ }
85
+ if (typeof record !== 'object' ||
86
+ record === null ||
87
+ !('managed_pi_runtime_registry' in record) ||
88
+ !Array.isArray(record.managed_pi_runtime_registry))
89
+ continue;
90
+ for (const owner of record.managed_pi_runtime_registry) {
91
+ if (typeof owner !== 'object' ||
92
+ owner === null ||
93
+ !('pid' in owner) ||
94
+ typeof owner.pid !== 'number' ||
95
+ !Number.isSafeInteger(owner.pid) ||
96
+ owner.pid <= 0)
97
+ continue;
98
+ if ('process_identity' in owner &&
99
+ isLocalProcessIdentity(owner.process_identity)) {
100
+ if (owner.process_identity.pid === owner.pid &&
101
+ observeProcessIdentity(owner.process_identity) === 'alive')
102
+ throw new Error('A verified live managed session still owns local state. Stop it before reset.');
103
+ }
104
+ // Imported PID-only registrations cannot identify a process birth; the
105
+ // explicit stopped-writers assertion covers this unknown, never an adoption.
106
+ }
107
+ }
108
+ };
109
+ await walk(local, 0);
110
+ }
111
+ function sameIdentity(actual, expected) {
112
+ return (actual.kind === 'directory' &&
113
+ actual.stats?.dev === expected.dev &&
114
+ actual.stats.ino === expected.ino);
115
+ }
116
+ /** Explicit fresh beta state; old bytes are archived, never migrated or deleted. */
117
+ export async function resetLocalState(root, options) {
118
+ const local = join(root, '.pappra/local');
119
+ const archiveRoot = join(root, '.pappra/local-archive');
120
+ const journalFile = join(archiveRoot, 'pending-reset.json');
121
+ const lockFile = join(archiveRoot, 'reset.lock');
122
+ const old = await observePath(local);
123
+ const allowed = await ensureStatePathBoundary(root, local);
124
+ if (!allowed.ok || !['missing', 'directory'].includes(old.kind))
125
+ throw new Error('Local state is redirected or is not a directory.');
126
+ if (!options.apply)
127
+ return { status: 'planned', archive: null, resumed: false };
128
+ if (!options.allWritersStopped)
129
+ throw new Error('Local reset requires --apply --all-writers-stopped after stopping every affected writer.');
130
+ await options.assertNoLiveLocks();
131
+ await assertNoLiveManagedOwners(root);
132
+ const privateArchive = await ensurePrivateState(root, 'local-archive');
133
+ if (!privateArchive.ok)
134
+ throw new Error(privateArchive.message);
135
+ const namespace = await Promise.all([root, join(root, '.pappra'), archiveRoot].map(async (path) => {
136
+ const observed = await observePath(path);
137
+ if (observed.kind !== 'directory' || observed.stats === null)
138
+ throw new Error('Reset namespace is not a real directory.');
139
+ return {
140
+ path,
141
+ identity: { dev: observed.stats.dev, ino: observed.stats.ino },
142
+ };
143
+ }));
144
+ let ownedLock = null;
145
+ const token = randomUUID();
146
+ const assertPinned = async () => {
147
+ for (const item of namespace)
148
+ if (!sameIdentity(await observePath(item.path), item.identity))
149
+ throw new Error('Reset namespace identity changed; retained state was preserved.');
150
+ if (ownedLock !== null) {
151
+ const current = await inspectPrivateLock(lockFile);
152
+ if (current.token !== token ||
153
+ current.identity?.dev !== ownedLock.dev ||
154
+ current.identity.ino !== ownedLock.ino)
155
+ throw new Error('Reset ownership changed; no further state is published.');
156
+ }
157
+ };
158
+ const saveJournal = async (journal) => {
159
+ await assertPinned();
160
+ const saved = await writeJsonAtomic(journalFile, journal, root);
161
+ if (!saved.ok)
162
+ throw new Error(saved.message);
163
+ await assertPinned();
164
+ };
165
+ const acquire = async () => publishPrivateLock(lockFile, Buffer.from(`${JSON.stringify({ schema: 'pappra/install-lock/1', token, pid: process.pid, process_identity: captureProcessIdentity() ?? undefined, operation: 'rollback-workspace-files', target: root, createdAt: new Date().toISOString() })}\n`), async () => {
166
+ await assertPinned();
167
+ const boundary = await ensureStatePathBoundary(root, lockFile);
168
+ if (!boundary.ok)
169
+ throw new Error(boundary.message);
170
+ });
171
+ try {
172
+ await acquire();
173
+ }
174
+ catch (error) {
175
+ if (error.code !== 'EEXIST')
176
+ throw error;
177
+ const owner = await inspectPrivateLock(lockFile);
178
+ if (owner.state === 'unknown' &&
179
+ options.recoverUnknownResetLock !== undefined) {
180
+ await options.recoverUnknownResetLock(lockFile);
181
+ }
182
+ else {
183
+ if (owner.state !== 'dead')
184
+ throw new Error('Another reset owner is alive or unverified.');
185
+ await reclaimDeadPrivateLock(lockFile, async () => {
186
+ await assertPinned();
187
+ const boundary = await ensureStatePathBoundary(root, lockFile);
188
+ if (!boundary.ok)
189
+ throw new Error(boundary.message);
190
+ });
191
+ }
192
+ await acquire();
193
+ }
194
+ const acquired = await inspectPrivateLock(lockFile);
195
+ if (acquired.token !== token || acquired.identity === null)
196
+ throw new Error('Reset lock identity is unavailable.');
197
+ ownedLock = acquired.identity;
198
+ try {
199
+ await options.assertNoLiveLocks();
200
+ await assertNoLiveManagedOwners(root);
201
+ const journalBoundary = await ensureStatePathBoundary(root, journalFile);
202
+ if (!journalBoundary.ok)
203
+ throw new Error(journalBoundary.message);
204
+ await assertPinned();
205
+ if (!['missing', 'file'].includes((await observePath(journalFile)).kind))
206
+ throw new Error('Reset journal is redirected or not a regular file.');
207
+ const pending = await readJsonDocument(journalFile, resetSchema);
208
+ if (pending.exists && pending.value === null) {
209
+ if ((await observePath(journalFile)).kind !== 'file')
210
+ throw new Error('Reset journal is not a regular file.');
211
+ await rename(journalFile, join(archiveRoot, `unreadable-reset-${randomUUID()}.json`));
212
+ }
213
+ let journal = pending.value;
214
+ let resumed = journal !== null && journal.status !== 'fresh';
215
+ if (journal !== null && journal.root !== root) {
216
+ // Imported private metadata is evidence, not an instruction to touch its old absolute root.
217
+ await rename(journalFile, join(archiveRoot, `imported-reset-${randomUUID()}.json`));
218
+ journal = null;
219
+ resumed = false;
220
+ }
221
+ if (journal?.status === 'fresh') {
222
+ const now = await observePath(local);
223
+ if (journal.fresh !== null &&
224
+ sameIdentity(now, journal.fresh) &&
225
+ (await readdir(local)).every((name) => name === '.gitignore'))
226
+ return { status: 'reset', archive: journal.archive, resumed: true };
227
+ await rename(journalFile, join(archiveRoot, `reset-${journal.id}.json`));
228
+ journal = null;
229
+ resumed = false;
230
+ }
231
+ if (journal === null) {
232
+ const source = await observePath(local);
233
+ if (source.kind === 'missing') {
234
+ const fresh = await ensurePrivateState(root);
235
+ if (!fresh.ok)
236
+ throw new Error(fresh.message);
237
+ return { status: 'no-local-state', archive: null, resumed: false };
238
+ }
239
+ if (source.kind !== 'directory' || source.stats === null)
240
+ throw new Error('Local state changed before reset.');
241
+ const id = randomUUID();
242
+ journal = {
243
+ schema: 'pappra/local-reset/1',
244
+ id,
245
+ root,
246
+ archive: join(archiveRoot, id),
247
+ source: { dev: source.stats.dev, ino: source.stats.ino },
248
+ fresh: null,
249
+ status: 'prepared',
250
+ createdAt: new Date().toISOString(),
251
+ allWritersStopped: true,
252
+ ownership: 'operator-assertion-and-bounded-live-owner-checks',
253
+ };
254
+ await saveJournal(journal);
255
+ }
256
+ if (journal.archive !== join(archiveRoot, journal.id))
257
+ throw new Error('Reset archive path does not match the pinned local namespace.');
258
+ let archived = await observePath(journal.archive);
259
+ const source = await observePath(local);
260
+ if (archived.kind === 'missing') {
261
+ if (!sameIdentity(source, journal.source))
262
+ throw new Error('Original local state identity changed before archival.');
263
+ await options.assertNoLiveLocks();
264
+ await assertNoLiveManagedOwners(root);
265
+ const boundary = await ensureStatePathBoundary(root, journal.archive);
266
+ if (!boundary.ok)
267
+ throw new Error(boundary.message);
268
+ await assertPinned();
269
+ if (!sameIdentity(await observePath(local), journal.source))
270
+ throw new Error('Original local state changed immediately before archival.');
271
+ await rename(local, journal.archive);
272
+ await assertPinned();
273
+ archived = await observePath(journal.archive);
274
+ if (!sameIdentity(archived, journal.source))
275
+ throw new Error('Archived local state identity changed; preserved for inspection.');
276
+ await options.afterArchive?.();
277
+ }
278
+ else if (!sameIdentity(archived, journal.source) ||
279
+ sameIdentity(source, journal.source))
280
+ throw new Error('Reset archive/source identity is ambiguous; both were preserved.');
281
+ journal = { ...journal, status: 'archived' };
282
+ await saveJournal(journal);
283
+ const fresh = await ensurePrivateState(root);
284
+ if (!fresh.ok)
285
+ throw new Error(fresh.message);
286
+ const created = await observePath(local);
287
+ if (created.kind !== 'directory' ||
288
+ created.stats === null ||
289
+ sameIdentity(created, journal.source))
290
+ throw new Error('Fresh local state identity is unavailable.');
291
+ await saveJournal({
292
+ ...journal,
293
+ status: 'fresh',
294
+ fresh: { dev: created.stats.dev, ino: created.stats.ino },
295
+ });
296
+ return { status: 'reset', archive: journal.archive, resumed };
297
+ }
298
+ finally {
299
+ await assertPinned();
300
+ const released = await releaseInstallLock(lockFile, token);
301
+ if (!released.ok)
302
+ throw new Error(released.message);
303
+ }
304
+ }
@@ -10,6 +10,9 @@ export interface WorkspaceRemovalResult {
10
10
  export interface RollbackWorkspaceOptions {
11
11
  readonly root: string;
12
12
  readonly installId: string;
13
+ /** Fault-injection seam after the rollback journal is durable; CLI never supplies it. */
14
+ readonly beforeRemoval?: () => Promise<void> | void;
15
+ readonly afterRemoval?: () => Promise<void> | void;
13
16
  }
14
17
  export declare function rollbackWorkspaceInstall(options: RollbackWorkspaceOptions): Promise<WorkspaceRemovalResult>;
15
18
  export interface UninstallWorkspaceOptions {
@@ -355,9 +355,13 @@ export async function rollbackWorkspaceInstall(options) {
355
355
  owner: 'pappra',
356
356
  verifiedAt: journal.updatedAt,
357
357
  }));
358
- pendingResidual = journal.entries
359
- .filter((entry) => entry.status === 'pending')
360
- .map((entry) => entry.path);
358
+ for (const entry of journal.entries.filter((entry) => entry.status === 'pending')) {
359
+ const target = join(options.root, entry.path);
360
+ const boundary = await ensureStatePathBoundary(options.root, target);
361
+ if (!boundary.ok || (await observePath(target)).kind !== 'missing') {
362
+ pendingResidual.push(entry.path);
363
+ }
364
+ }
361
365
  if (recovered.length > 0) {
362
366
  entries = recovered;
363
367
  journalRecovered = journal;
@@ -413,6 +417,7 @@ export async function rollbackWorkspaceInstall(options) {
413
417
  current = withStep(current, 'recovered-from-apply-journal', journalRecovered.installId);
414
418
  }
415
419
  await writeInstallJournal(file, current, options.root);
420
+ await options.beforeRemoval?.();
416
421
  const baseline = manifest ?? {
417
422
  schema: 'pappra/install-manifest/1',
418
423
  workspaceRoot: options.root,
@@ -422,6 +427,7 @@ export async function rollbackWorkspaceInstall(options) {
422
427
  updatedAt: now(),
423
428
  };
424
429
  const removal = await removeManifestEntries(options.root, baseline, entries);
430
+ await options.afterRemoval?.();
425
431
  let residual = [...removal.residual, ...pendingResidual];
426
432
  if (manifest !== null) {
427
433
  const remainingManifest = {
@@ -1,6 +1,6 @@
1
1
  import type { Diagnostic } from '../schemas/diagnostics.js';
2
2
  import type { InstallJournal, RuntimeManifest } from '../schemas/install.js';
3
- export type RuntimeInstallStatus = 'installed' | 'reused' | 'locked' | 'corrupt' | 'partial' | 'failed';
3
+ export type RuntimeInstallStatus = 'installed' | 'reused' | 'locked' | 'corrupt' | 'partial' | 'failed' | 'incompatible';
4
4
  export interface RuntimeInstallResult {
5
5
  readonly ok: boolean;
6
6
  readonly status: RuntimeInstallStatus;
@@ -35,31 +35,35 @@ export interface RuntimePaths {
35
35
  }
36
36
  export declare function resolveUserDataDir(environment?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, home?: string): string;
37
37
  export declare function runtimePaths(version: string, dataDir?: string): RuntimePaths;
38
+ /** Repair verifies published content without executing it or ignoring its journal. */
39
+ export declare function verifyRuntimeForRepair(runtimeRoot: string, version: string, packageName: string, installId: string): Promise<boolean>;
38
40
  export declare function installRuntime(options: InstallRuntimeOptions): Promise<RuntimeInstallResult>;
39
41
  export interface RuntimeStateReport {
40
42
  readonly version: string;
41
- readonly status: 'missing' | 'complete' | 'partial' | 'corrupt' | 'locked';
43
+ readonly status: 'missing' | 'complete' | 'partial' | 'corrupt' | 'locked' | 'incompatible';
42
44
  readonly runtimeRoot: string;
43
45
  readonly manifest: RuntimeManifest | null;
44
46
  readonly journal: InstallJournal | null;
45
47
  readonly diagnostics: readonly Diagnostic[];
46
48
  }
47
- export declare function inspectRuntimeState(version: string, dataDir?: string): Promise<RuntimeStateReport>;
49
+ export declare function inspectRuntimeState(version: string, dataDir?: string, heldLockToken?: string): Promise<RuntimeStateReport>;
48
50
  export interface RemoveRuntimeOptions {
49
51
  readonly version: string;
50
52
  readonly dataDir?: string;
51
- /** Caller-provided live workspace/runtime references. Unknown is not empty. */
53
+ /** Extra positive caller references; an empty list never proves complete coverage. */
52
54
  readonly activeReferences?: readonly string[];
55
+ /** Explicit operator policy: accepts unknown coverage, never verified references. */
56
+ readonly forceUnknownReferences?: boolean;
53
57
  }
54
58
  export interface RemoveRuntimeResult {
55
59
  readonly ok: boolean;
56
- readonly status: 'removed' | 'missing' | 'in-use' | 'locked' | 'corrupt' | 'failed';
60
+ readonly status: 'removed' | 'missing' | 'in-use' | 'unknown' | 'locked' | 'corrupt' | 'failed' | 'incompatible';
57
61
  readonly runtimeRoot: string;
58
62
  readonly diagnostics: readonly Diagnostic[];
59
63
  readonly residualPaths: readonly string[];
60
64
  }
61
65
  export declare function removeRuntime(options: RemoveRuntimeOptions): Promise<RemoveRuntimeResult>;
62
- export type RuntimePassiveStatus = 'missing' | 'content-match' | 'version-mismatch' | 'corrupt' | 'locked' | 'partial' | 'unreadable';
66
+ export type RuntimePassiveStatus = 'missing' | 'content-match' | 'version-mismatch' | 'corrupt' | 'locked' | 'partial' | 'unreadable' | 'incompatible';
63
67
  export interface PassiveRuntimeState {
64
68
  readonly version: string;
65
69
  readonly status: RuntimePassiveStatus;