@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,178 @@
1
+ import path from 'node:path';
2
+ import { readBoundedFile } from '../../app/bounded-file.js';
3
+ import { COLLAB_WORK_EXECUTION_SCHEMA, runWorkExecution, } from '../../app/collab-work-execution.js';
4
+ const MAX_REQUEST_BYTES = 64 * 1024;
5
+ export const collaborationWorkRunUsage = 'pappra collab work-run --request <abs work-execution.json> [--apply] [--json]\n\n' +
6
+ 'Without --apply this is a zero-write, zero-notification plan. Apply revalidates the\n' +
7
+ 'pinned work plan, current assignments, capability cards, coordinator epoch, resource\n' +
8
+ 'claims and visible participant binding; it records a durable per-node intent before\n' +
9
+ 'one bounded concurrent notification batch. Existing/unknown intents are reconciliation\n' +
10
+ 'only and are never automatically replayed. This command never launches sessions, merges\n' +
11
+ 'code or accepts a report.';
12
+ export function parseCollaborationWorkRunArgs(argv) {
13
+ const errors = [];
14
+ let requestFile = null;
15
+ let apply = false;
16
+ let json = false;
17
+ let sawApply = false;
18
+ let sawJson = false;
19
+ for (let index = 0; index < argv.length; index += 1) {
20
+ const arg = argv[index];
21
+ if (arg === '--request') {
22
+ if (requestFile !== null)
23
+ errors.push('--request may be given only once.');
24
+ const value = argv[index + 1];
25
+ if (value === undefined || value.startsWith('--'))
26
+ errors.push('--request requires a value.');
27
+ else {
28
+ requestFile = value;
29
+ index += 1;
30
+ }
31
+ }
32
+ else if (arg === '--apply') {
33
+ if (sawApply)
34
+ errors.push('--apply may be given only once.');
35
+ sawApply = true;
36
+ apply = true;
37
+ }
38
+ else if (arg === '--json') {
39
+ if (sawJson)
40
+ errors.push('--json may be given only once.');
41
+ sawJson = true;
42
+ json = true;
43
+ }
44
+ else
45
+ errors.push(`unknown option: ${String(arg)}`);
46
+ }
47
+ return { options: { requestFile, apply, json }, errors };
48
+ }
49
+ function isObject(value) {
50
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
51
+ }
52
+ function isAbsolute(value) {
53
+ return typeof value === 'string' && path.isAbsolute(value);
54
+ }
55
+ function loadRequest(file) {
56
+ const read = readBoundedFile(file, MAX_REQUEST_BYTES);
57
+ if (!read.ok)
58
+ return { ok: false, error: `request_file_${read.code}` };
59
+ let parsed;
60
+ try {
61
+ parsed = JSON.parse(read.text);
62
+ }
63
+ catch {
64
+ return { ok: false, error: 'request_file_invalid_json' };
65
+ }
66
+ if (!isObject(parsed) ||
67
+ parsed.schema !== 'pappra/collab-work-execution-request/1')
68
+ return { ok: false, error: 'request_schema_invalid' };
69
+ const keys = Object.keys(parsed).sort();
70
+ const expected = [
71
+ 'binding_file',
72
+ 'caller',
73
+ 'caller_pane',
74
+ 'capability_cards',
75
+ 'plan_file',
76
+ 'schema',
77
+ 'task_root',
78
+ ];
79
+ const expectedOptional = [...expected, 'max_concurrency'].sort();
80
+ if (keys.join('|') !== expected.sort().join('|') &&
81
+ keys.join('|') !== expectedOptional.join('|'))
82
+ return { ok: false, error: 'request_fields_invalid' };
83
+ if (!isAbsolute(parsed.task_root) ||
84
+ !isAbsolute(parsed.plan_file) ||
85
+ !isAbsolute(parsed.binding_file))
86
+ return { ok: false, error: 'request_paths_must_be_absolute' };
87
+ if (typeof parsed.caller_pane !== 'string' ||
88
+ !/^[A-Za-z0-9]+:p[A-Za-z0-9]+$/.test(parsed.caller_pane))
89
+ return { ok: false, error: 'caller_pane_invalid' };
90
+ if (!isObject(parsed.caller) ||
91
+ Object.keys(parsed.caller).sort().join('|') !==
92
+ 'agent_id|cwd|harness|session_id' ||
93
+ typeof parsed.caller.agent_id !== 'string' ||
94
+ typeof parsed.caller.session_id !== 'string' ||
95
+ typeof parsed.caller.harness !== 'string' ||
96
+ !isAbsolute(parsed.caller.cwd))
97
+ return { ok: false, error: 'caller_identity_invalid' };
98
+ if (!Array.isArray(parsed.capability_cards) ||
99
+ parsed.capability_cards.length > 64)
100
+ return { ok: false, error: 'capability_cards_invalid' };
101
+ const assignmentIds = new Set();
102
+ const cards = [];
103
+ for (const item of parsed.capability_cards) {
104
+ if (!isObject(item) ||
105
+ Object.keys(item).sort().join('|') !==
106
+ 'assignment_id|capabilities_file' ||
107
+ typeof item.assignment_id !== 'string' ||
108
+ !isAbsolute(item.capabilities_file) ||
109
+ assignmentIds.has(item.assignment_id))
110
+ return { ok: false, error: 'capability_cards_invalid' };
111
+ assignmentIds.add(item.assignment_id);
112
+ cards.push({
113
+ assignment_id: item.assignment_id,
114
+ capabilities_file: item.capabilities_file,
115
+ });
116
+ }
117
+ const maxConcurrency = parsed.max_concurrency;
118
+ if (maxConcurrency !== undefined &&
119
+ (typeof maxConcurrency !== 'number' ||
120
+ !Number.isSafeInteger(maxConcurrency) ||
121
+ maxConcurrency < 1 ||
122
+ maxConcurrency > 8))
123
+ return { ok: false, error: 'max_concurrency_invalid' };
124
+ return {
125
+ ok: true,
126
+ value: {
127
+ task_root: parsed.task_root,
128
+ plan_file: parsed.plan_file,
129
+ binding_file: parsed.binding_file,
130
+ caller: parsed.caller,
131
+ caller_pane: parsed.caller_pane,
132
+ capability_cards: cards,
133
+ ...(maxConcurrency === undefined
134
+ ? {}
135
+ : { max_concurrency: maxConcurrency }),
136
+ },
137
+ };
138
+ }
139
+ function invalid(output, options, errors) {
140
+ if (options.json)
141
+ output.stdout(`${JSON.stringify({ schema: COLLAB_WORK_EXECUTION_SCHEMA, ok: false, status: 'invalid', errors })}\n`);
142
+ else {
143
+ output.stdout('Work execution request invalid.\n');
144
+ for (const error of errors)
145
+ output.stdout(` - ${error}\n`);
146
+ output.stdout(`\n${collaborationWorkRunUsage}\n`);
147
+ }
148
+ return 2;
149
+ }
150
+ function emitHuman(report, output) {
151
+ output.stdout(`Work execution ${report.apply ? 'apply' : 'plan'}: ${report.status}${report.task_id ? ` (${report.task_id})` : ''}.\n`);
152
+ output.stdout(` nodes ${report.nodes.length}, concurrent limit ${report.concurrency.limit}, actual overlap ${report.concurrency.actual_overlap ? 'yes' : 'no'}\n`);
153
+ for (const node of report.nodes) {
154
+ output.stdout(` ${node.role} ${node.node_id} (${node.assignment_id}#${node.attempt}): ${node.execution_status}; acceptance ${node.acceptance}\n`);
155
+ for (const blocker of node.blockers)
156
+ output.stdout(` - ${blocker}\n`);
157
+ if (node.intent_message_id)
158
+ output.stdout(` intent: ${node.intent_message_id}\n`);
159
+ }
160
+ output.stdout(` immutable report pointers: ${report.reports.length}; no merge or acceptance performed\n`);
161
+ }
162
+ export async function runCollaborationWorkRunCommand(options, output, deps = {}) {
163
+ if (options.requestFile === null)
164
+ return invalid(output, options, ['--request is required.']);
165
+ if (!path.isAbsolute(options.requestFile))
166
+ return invalid(output, options, ['--request must be absolute.']);
167
+ const request = loadRequest(options.requestFile);
168
+ if (!request.ok)
169
+ return invalid(output, options, [request.error]);
170
+ const report = await runWorkExecution({ request: request.value, apply: options.apply }, deps);
171
+ if (options.json)
172
+ output.stdout(`${JSON.stringify(report)}\n`);
173
+ else
174
+ emitHuman(report, output);
175
+ if (report.status === 'planned' || report.status === 'applied')
176
+ return 0;
177
+ return report.status === 'invalid' ? 2 : 1;
178
+ }
@@ -1,4 +1,6 @@
1
1
  import type { CliOutput } from '../output.js';
2
+ /** One source of truth for the full collaboration command family in help output. */
3
+ export declare const collaborationCommandLine = "pappra collab <prepare|run|work-run|role-run|query|preflight|plan|role-plan|bootstrap|rollover|transition|message|capabilities|state|resource|handoff|recovery|recovery-run|binding> [options]";
2
4
  export declare const collaborationUsage: string;
3
5
  export interface CollaborationCommandOptions {
4
6
  readonly cwd: string;
@@ -9,5 +11,7 @@ export interface CollaborationCommandOptions {
9
11
  readonly capabilitiesFile: string | null;
10
12
  readonly controller: string | null;
11
13
  readonly domainRoot: string | null;
14
+ readonly rpcRequestTimeoutMs?: number;
15
+ readonly rpcOperationTimeoutMs?: number;
12
16
  }
13
17
  export declare function runCollaborationCommand(options: CollaborationCommandOptions, output: CliOutput, input?: NodeJS.ReadableStream): Promise<number>;
@@ -1,10 +1,38 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { FixtureController, PiRpcController, driveRun, inspectPreparedHistory, prepareCollaboration, startPrepared, } from '../../app/collaboration.js';
4
- export const collaborationUsage = 'pappra collab <prepare|run|query> [options]\n' +
4
+ /** One source of truth for the full collaboration command family in help output. */
5
+ export const collaborationCommandLine = 'pappra collab <prepare|run|work-run|role-run|query|preflight|plan|role-plan|bootstrap|rollover|transition|message|capabilities|state|resource|handoff|recovery|recovery-run|binding> [options]';
6
+ export const collaborationUsage = `${collaborationCommandLine}\n` +
5
7
  ' prepare --goal <file> --domain-root <dir> [--json]\n' +
6
8
  ' run --prepared <file> --capabilities <card> --controller <fixture|pi-rpc>\n' +
9
+ ' [--rpc-request-timeout-ms <1..60000>] [--rpc-operation-timeout-ms <1..86400000>]\n' +
10
+ ' work-run --request <abs work-execution.json> [--apply] [--json]\n' +
11
+ ' role-run --request <abs role-orchestration.json> [--apply] [--json]\n' +
12
+ ' Apply records bounded notification intents; plans write and notify nothing.\n' +
7
13
  ' query --prepared <file> [--json]\n' +
14
+ ' preflight --assignment <file> --capabilities <card> [--max-age-minutes <n>] [--json]\n' +
15
+ ' plan --task-root <dir> --config <plan.json> [--json]\n' +
16
+ ' role-plan expand --request <abs role-plan.json> [--json]\n' +
17
+ ' role-plan aggregate --request <abs role-plan.json> --task-root <abs> --config <abs expanded-plan.json> [--json]\n' +
18
+ ' rollover <inspect|plan> --task-root <dir> --request <request.json> [--json]\n' +
19
+ ' bootstrap <plan|apply> --task-root <abs> --request <abs request.json> [--json]\n' +
20
+ ' Plan is read-only; apply launches only a visible Pi sibling or verified task-bound reuse.\n' +
21
+ ' Unknown effects are reconciled, not retried; submission is not acceptance.\n' +
22
+ ' Rollover remains read-only: no apply, native stop proof, route removal or session archive.\n' +
23
+ ' transition <plan|apply> --task-root <abs> --request <abs request.json> [--json]\n' +
24
+ ' Pre-transfer apply requires the opt-in Pi host; first-work and retirement resume from CLI.\n' +
25
+ ' Unsupported native-quiescence proof blocks before successor launch or claim transfer.\n' +
26
+ ' Applied ACK and REPORT pointers are required; retirement retains the original transcript.\n' +
27
+ ' message <send|list|read|notify> --task-root <dir> [options]\n' +
28
+ ' capabilities derive --harness <pi|codex|claude> --assignment <file> --template <file> [--execution <file> | --live] [--session <file> | --events <file>] [--json]\n' +
29
+ ' state <show|doctor|epoch-check> --task-root <dir> [options]\n' +
30
+ ' resource <list|claim|release> --task-root <dir> [options]\n' +
31
+ ' handoff <inventory|begin|stopped|rebind|transfer|confirm|complete|abort|release-dispatch> --task-root <dir> [options]\n' +
32
+ ' recovery <inspect|reconcile|lock-break> --task-root <dir> [options]\n' +
33
+ ' recovery-run --request <abs recovery-run.json> [--apply] [--json]\n' +
34
+ ' Apply requires exact session/cwd/harness/pane and a task-local digest-pinned binding.\n' +
35
+ ' binding <list|resolve|bind|unbind> [--root <dir>] [options]\n' +
8
36
  ' run reads pappra/collaboration-command/1 JSONL from stdin and writes\n' +
9
37
  ' pappra/collaboration-event/1 JSONL to stdout.';
10
38
  function failure(code, message) {
@@ -17,11 +45,18 @@ function readGoal(file) {
17
45
  }
18
46
  return JSON.parse(fs.readFileSync(file, 'utf8'));
19
47
  }
20
- function controllerFor(name) {
48
+ function controllerFor(name, options) {
21
49
  if (name === 'fixture')
22
50
  return new FixtureController();
23
51
  if (name === 'pi-rpc')
24
- return new PiRpcController();
52
+ return new PiRpcController({
53
+ ...(options.rpcRequestTimeoutMs === undefined
54
+ ? {}
55
+ : { requestTimeoutMs: options.rpcRequestTimeoutMs }),
56
+ ...(options.rpcOperationTimeoutMs === undefined
57
+ ? {}
58
+ : { operationTimeoutMs: options.rpcOperationTimeoutMs }),
59
+ });
25
60
  return null;
26
61
  }
27
62
  const MAX_COMMAND_LINE_BYTES = 256 * 1024;
@@ -145,7 +180,7 @@ export async function runCollaborationCommand(options, output, input = process.s
145
180
  output.stderr('pappra: collab run requires --prepared <file> and --capabilities <card>\n');
146
181
  return 2;
147
182
  }
148
- const controller = controllerFor(options.controller);
183
+ const controller = controllerFor(options.controller, options);
149
184
  if (controller === null) {
150
185
  output.stderr('pappra: collab run requires --controller <fixture|pi-rpc>\n');
151
186
  return 2;
@@ -124,10 +124,6 @@ export async function runResumeCommand(options, output) {
124
124
  else {
125
125
  printDiagnostics(output, result.diagnostics);
126
126
  }
127
- return result.ok &&
128
- result.packet !== null &&
129
- result.packet.executionAllowed &&
130
- result.packet.verificationStatus === 'current'
131
- ? 0
132
- : 1;
127
+ // Reading advisory handoff context does not acquire execution authority.
128
+ return result.ok && result.packet?.verificationStatus === 'current' ? 0 : 1;
133
129
  }
@@ -4,6 +4,8 @@ export interface DocsCommandOptions {
4
4
  readonly cwd: string;
5
5
  readonly action: string | null;
6
6
  readonly argument: string | null;
7
+ readonly targetPath?: string | null;
8
+ readonly scope?: string | null;
7
9
  readonly json: boolean;
8
10
  readonly includePrivate: boolean;
9
11
  readonly includeArchived: boolean;
@@ -1,15 +1,18 @@
1
1
  import { resolveWorkspaceRoot } from '../../workspace/resolve.js';
2
2
  import { scanDiscoveredDocuments } from '../../knowledge/scan.js';
3
3
  import { formatDiagnostic } from '../../schemas/diagnostics.js';
4
- import { docsArchiveReportSchema, docsCheckReportSchema, docsListReportSchema, } from '../../schemas/document.js';
4
+ import { docsArchiveReportSchema, docsCheckReportSchema, docsListReportSchema, documentLocationSchema, } from '../../schemas/document.js';
5
5
  import { KnowledgeService, } from '../../knowledge/index.js';
6
- export const docsUsage = 'pappra docs <list|check|archive|scan> [options]\n' +
6
+ export const docsUsage = 'pappra docs <list|check|archive|scan|relocate|forget> [options]\n' +
7
7
  ' list [--include-private] [--include-archived] [--json]\n' +
8
8
  ' check [--include-private] [--json]\n' +
9
9
  ' archive <documentId> [--json]\n' +
10
10
  ' scan [--apply] [--json]\n\n' +
11
- 'scan registers documents discovered under the configured knowledge sources; without\n' +
12
- '--apply it only reports the candidates.';
11
+ ' relocate <documentId> <workspace-relative-path> [--apply] [--json]\n' +
12
+ ' forget <documentId> [--scope private|shared] [--apply] [--json]\n\n' +
13
+ 'scan registers existing documents discovered under knowledge.sources; newDocuments is\n' +
14
+ 'only the destination for future authored documents. Without --apply scan only reports\n' +
15
+ 'candidates.';
13
16
  async function runDocsScan(options, output, root) {
14
17
  const result = await scanDiscoveredDocuments(root, { apply: options.apply });
15
18
  if ('message' in result) {
@@ -20,20 +23,37 @@ async function runDocsScan(options, output, root) {
20
23
  output.stdout(`${JSON.stringify(result)}\n`);
21
24
  }
22
25
  else {
23
- output.stdout(`Scan candidates: ${result.candidates.length}\n`);
24
- for (const candidate of result.candidates) {
25
- output.stdout(` ${candidate.kind} ${candidate.path}\n`);
26
+ if (result.candidates.length === 0) {
27
+ output.stdout('No new documents found.\n');
26
28
  }
27
- output.stdout('Plan only; rerun with --apply to register them.\n');
29
+ else {
30
+ output.stdout(`Scan candidates: ${result.candidates.length}\n`);
31
+ for (const candidate of result.candidates) {
32
+ output.stdout(` ${candidate.kind} ${candidate.path}\n`);
33
+ }
34
+ }
35
+ printDiagnostics(output, result.diagnostics);
36
+ for (const action of result.nextActions) {
37
+ output.stdout(`${action}\n`);
38
+ }
39
+ output.stdout(result.ok
40
+ ? 'Plan only; rerun with --apply to register them.\n'
41
+ : 'Scan coverage is incomplete; no complete result is claimed.\n');
28
42
  }
29
- return 0;
43
+ return result.ok ? 0 : 1;
30
44
  }
31
45
  if (options.json) {
32
46
  output.stdout(`${JSON.stringify(result)}\n`);
33
47
  }
34
48
  else {
35
49
  output.stdout(`Registered ${result.registered.length} document(s).\n`);
50
+ if (!result.ok) {
51
+ output.stdout('Scan coverage is incomplete; the registration result is not a complete scan.\n');
52
+ }
36
53
  printDiagnostics(output, result.diagnostics);
54
+ for (const action of result.nextActions) {
55
+ output.stdout(`${action}\n`);
56
+ }
37
57
  }
38
58
  return result.ok ? 0 : 1;
39
59
  }
@@ -75,7 +95,7 @@ export async function runDocsCommand(options, output) {
75
95
  if (options.action === null || options.action === 'help') {
76
96
  if (options.json) {
77
97
  output.stdout(`${JSON.stringify({
78
- commands: ['list', 'check', 'archive'],
98
+ commands: ['list', 'check', 'archive', 'scan', 'relocate', 'forget'],
79
99
  usage: docsUsage,
80
100
  })}\n`);
81
101
  }
@@ -84,22 +104,67 @@ export async function runDocsCommand(options, output) {
84
104
  }
85
105
  return 0;
86
106
  }
107
+ if (options.scope !== undefined &&
108
+ options.scope !== null &&
109
+ (options.action !== 'forget' ||
110
+ !['private', 'shared'].includes(options.scope)))
111
+ return invalidArguments(output, options.json, 'Only docs forget accepts --scope private|shared.');
87
112
  const resolution = await resolveWorkspaceRoot({ startPath: options.cwd });
88
113
  const serviceRoot = resolution.root ?? options.cwd;
89
- if (!['list', 'check', 'archive', 'scan'].includes(options.action)) {
114
+ if (!['list', 'check', 'archive', 'scan', 'relocate', 'forget'].includes(options.action)) {
90
115
  return invalidArguments(output, options.json, `unknown docs action: ${options.action}`);
91
116
  }
92
- if (options.action === 'archive' && options.argument === null) {
93
- return invalidArguments(output, options.json, 'docs archive requires a documentId');
117
+ if (['archive', 'relocate', 'forget'].includes(options.action) &&
118
+ options.argument === null) {
119
+ return invalidArguments(output, options.json, `docs ${options.action} requires a documentId`);
94
120
  }
95
- if (options.action !== 'archive' && options.argument !== null) {
121
+ if (!['archive', 'relocate', 'forget'].includes(options.action) &&
122
+ options.argument !== null) {
96
123
  return invalidArguments(output, options.json, `docs ${options.action} does not accept an argument`);
97
124
  }
98
125
  if (options.action === 'scan') {
99
126
  return runDocsScan(options, output, serviceRoot);
100
127
  }
128
+ if ((options.action === 'relocate' || options.action === 'forget') &&
129
+ options.argument !== null) {
130
+ const location = options.action === 'relocate'
131
+ ? documentLocationSchema.safeParse({
132
+ kind: 'workspace',
133
+ path: options.targetPath,
134
+ })
135
+ : null;
136
+ if (location && !location.success)
137
+ return invalidArguments(output, options.json, 'docs relocate requires a normalized workspace-relative target path.');
138
+ if (!options.apply) {
139
+ const plan = {
140
+ schema: 'pappra/docs-metadata-plan/1',
141
+ ok: true,
142
+ action: options.action,
143
+ documentId: options.argument,
144
+ scope: options.scope ?? null,
145
+ location: location?.success ? location.data : null,
146
+ applied: false,
147
+ };
148
+ output.stdout(options.json
149
+ ? `${JSON.stringify(plan)}\n`
150
+ : `Plan only: ${options.action} ${options.argument}; document content is preserved. Rerun with --apply.\n`);
151
+ return 0;
152
+ }
153
+ const service = new KnowledgeService(serviceRoot);
154
+ const result = location?.success
155
+ ? await service.relocate(options.argument, location.data)
156
+ : await service.forget(options.argument, options.scope === 'private' || options.scope === 'shared'
157
+ ? options.scope
158
+ : undefined);
159
+ output.stdout(options.json
160
+ ? `${JSON.stringify({ schema: 'pappra/docs-metadata-result/1', action: options.action, ...result })}\n`
161
+ : `${options.action}: ${result.ok ? 'applied' : result.status}\n`);
162
+ if (!options.json)
163
+ printDiagnostics(output, result.diagnostics);
164
+ return result.ok ? 0 : 1;
165
+ }
101
166
  if (options.apply) {
102
- return invalidArguments(output, options.json, 'only docs scan accepts --apply');
167
+ return invalidArguments(output, options.json, 'only docs scan, relocate and forget accept --apply');
103
168
  }
104
169
  if (options.action === 'archive' && options.argument !== null) {
105
170
  const service = new KnowledgeService(serviceRoot);
@@ -142,6 +207,15 @@ export async function runDocsCommand(options, output) {
142
207
  else {
143
208
  for (const record of result.records)
144
209
  printDocument(output, record);
210
+ if (result.records.length === 0 && !options.includePrivate && result.ok) {
211
+ const command = options.includeArchived
212
+ ? 'pappra docs list --include-private --include-archived'
213
+ : 'pappra docs list --include-private';
214
+ output.stdout(`No documents in the default view. Private documents are hidden; run: ${command}\n`);
215
+ if (!options.includeArchived) {
216
+ output.stdout('Archived documents also remain hidden; add --include-archived to include them.\n');
217
+ }
218
+ }
145
219
  printDiagnostics(output, result.diagnostics);
146
220
  }
147
221
  return result.ok ? 0 : 1;
@@ -4,7 +4,7 @@ export declare const hostUsage: string;
4
4
  export interface HostCommandOptions {
5
5
  readonly cwd: string;
6
6
  readonly json: boolean;
7
- readonly action: 'detect' | 'describe' | 'plan' | 'install' | 'verify' | 'bundle' | 'mcp' | 'probe' | null;
7
+ readonly action: 'detect' | 'describe' | 'plan' | 'install' | 'uninstall' | 'verify' | 'bundle' | 'mcp' | 'probe' | null;
8
8
  readonly host: HostName | null;
9
9
  readonly hostRoot: string | null;
10
10
  readonly hostVersion: string | null;
@@ -13,6 +13,12 @@ export interface HostCommandOptions {
13
13
  readonly subAction: string | null;
14
14
  readonly out: string | null;
15
15
  readonly mode: 'project-settings' | 'plugin' | null;
16
+ /** Explicit private bootstrap binding; Pi bundle exports only. */
17
+ readonly bootstrapConfig: string | null;
18
+ /** Explicit validated task/session binding; recovery exports are Pi-only. */
19
+ readonly recoveryConfig: string | null;
20
+ /** Opt-in Pi session-transition command extension; it is not native takeover. */
21
+ readonly transitionExtension: boolean;
16
22
  readonly marketplace: boolean;
17
23
  readonly scope: 'project' | 'user' | null;
18
24
  }
@@ -1,17 +1,28 @@
1
1
  import { homedir } from 'node:os';
2
- import { join, resolve } from 'node:path';
2
+ import { isAbsolute, join, resolve } from 'node:path';
3
3
  import { HostIntegrationService } from '../../hosts/integration/service.js';
4
- import { exportHostPluginBundle } from '../../hosts/integration/bundle-export.js';
4
+ import { exportHostPluginBundle, } from '../../hosts/integration/bundle-export.js';
5
+ import { readPiBootstrapConfig } from '../../hosts/integration/bootstrap-config.js';
6
+ import { readPiRecoveryConfig } from '../../hosts/integration/recovery-config.js';
5
7
  import { applyHostMcp, planHostMcp, verifyHostMcp, } from '../../app/host-mcp.js';
6
8
  import { probeHost } from '../../app/host-probe.js';
7
9
  import { HOST_SUPPORT, isSupportedVersion, } from '../../hosts/integration/support.js';
8
- import { getPackageInfo } from '../../package-info.js';
9
- export const hostUsage = 'pappra host <detect|describe|plan|install|verify|bundle|mcp> --host <pi|codex|claude>\n' +
10
+ import { getPackageInfo, getPackageRoot } from '../../package-info.js';
11
+ export const hostUsage = 'pappra host <detect|describe|probe|plan|install|uninstall|verify|bundle|mcp> --host <pi|codex|claude>\n' +
10
12
  ' [--host-root <dir>] [--host-version <version>] [--out <dir>]\n' +
11
- ' [--mode project-settings|plugin] [--apply] [--json]\n' +
13
+ ' [--mode project-settings|plugin] [--scope project|user] [--marketplace] [--apply] [--json]\n' +
14
+ ' probe --host <name> [--host-root <dir>] [--json]\n' +
15
+ ' uninstall --host <name> [--host-root <dir> | --scope user] [--apply] [--force]\n' +
16
+ ' --force asserts all host writers are stopped and archives unknown locks; edited assets remain preserved.\n' +
17
+ ' mcp <plan|install|verify> --host <name> [--host-root <dir>] [--apply] [--json]\n' +
18
+ ' bundle --host pi --out <new-private-dir> [--bootstrap-config <abs-file> |\n' +
19
+ ' --recovery-config <abs-file>] [--transition-extension]\n' +
12
20
  '\n' +
13
21
  'bundle exports a host-native plugin/package directory and never registers it or\n' +
14
- 'claims host callability.';
22
+ 'claims host callability. Recovery and transition extensions are opt-in Pi bundle\n' +
23
+ 'artifacts; --recovery-config must pin an exact task-local session and binding.\n' +
24
+ 'Exports do not install, trust, load, launch or transfer; native quiescence and\n' +
25
+ 'interactive session takeover remain unsupported.';
15
26
  /** Codex only loads lifecycle hooks from its user configuration directory. */
16
27
  function codexHome() {
17
28
  const configured = process.env['CODEX_HOME'];
@@ -66,8 +77,9 @@ export async function runHostCommand(options, output) {
66
77
  }
67
78
  if (options.action !== 'plan' &&
68
79
  options.action !== 'install' &&
80
+ options.action !== 'uninstall' &&
69
81
  options.action !== 'verify') {
70
- output.stderr('pappra host: --scope user is only valid for plan, install, or verify.\n');
82
+ output.stderr('pappra host: --scope user is only valid for plan, install, uninstall, or verify.\n');
71
83
  return 2;
72
84
  }
73
85
  }
@@ -197,15 +209,109 @@ export async function runHostCommand(options, output) {
197
209
  }
198
210
  return diagnosticStatus(result.status);
199
211
  }
212
+ if (options.action === 'uninstall') {
213
+ const result = await service.uninstall(options.host, {
214
+ ...target,
215
+ apply: options.apply,
216
+ force: options.force,
217
+ });
218
+ if (options.json)
219
+ output.stdout(`${JSON.stringify(result)}\n`);
220
+ else {
221
+ output.stdout(`Status: ${result.status}\n`);
222
+ for (const file of result.removedFiles)
223
+ output.stdout(` removed ${file}\n`);
224
+ for (const file of result.retainedPaths)
225
+ output.stdout(` preserved ${file}\n`);
226
+ for (const file of result.quarantinedLocks)
227
+ output.stdout(` archived lock ${file}\n`);
228
+ if (!options.apply)
229
+ output.stdout(' plan only; rerun with --apply to remove managed wiring.\n');
230
+ printDiagnostics(output, result.diagnostics);
231
+ }
232
+ return result.ok ? 0 : 1;
233
+ }
200
234
  if (options.action === 'bundle') {
201
235
  if (options.out === null) {
202
236
  output.stderr('pappra host bundle requires --out <dir>.\n');
203
237
  return 2;
204
238
  }
239
+ if (options.bootstrapConfig !== null) {
240
+ if (options.host !== 'pi') {
241
+ output.stderr('pappra host bundle: --bootstrap-config requires --host pi.\n');
242
+ return 2;
243
+ }
244
+ if (!isAbsolute(options.bootstrapConfig)) {
245
+ output.stderr('pappra host bundle: --bootstrap-config must be an absolute path.\n');
246
+ return 2;
247
+ }
248
+ if (options.marketplace || options.mode !== null || options.apply) {
249
+ output.stderr('pappra host bundle: --bootstrap-config cannot be combined with --marketplace, --mode or --apply.\n');
250
+ return 2;
251
+ }
252
+ }
205
253
  if (!isSupportedVersion(options.host, options.hostVersion)) {
206
254
  output.stderr(`Host ${options.host} version ${options.hostVersion ?? '(unknown)'} is outside the pinned ${HOST_SUPPORT[options.host].version} support matrix.\n`);
207
255
  return 1;
208
256
  }
257
+ let bootstrap;
258
+ if (options.bootstrapConfig !== null) {
259
+ const loaded = readPiBootstrapConfig(options.bootstrapConfig);
260
+ if (loaded.ok === false) {
261
+ if (options.json) {
262
+ output.stdout(`${JSON.stringify({
263
+ ok: false,
264
+ command: 'host',
265
+ action: 'bundle',
266
+ code: loaded.code,
267
+ message: loaded.message,
268
+ })}\n`);
269
+ }
270
+ else {
271
+ output.stderr(`pappra host bundle: ${loaded.message}\n`);
272
+ }
273
+ return 1;
274
+ }
275
+ bootstrap = {
276
+ config: loaded.config,
277
+ configBytes: loaded.bytes,
278
+ configDigest: loaded.digest,
279
+ artifactPath: join(getPackageRoot(), 'dist/hosts/integration/pappra-bootstrap-extension.mjs'),
280
+ };
281
+ }
282
+ let recovery;
283
+ if (options.recoveryConfig !== null) {
284
+ if (options.host !== 'pi') {
285
+ output.stderr('pappra host bundle: --recovery-config requires --host pi.\n');
286
+ return 2;
287
+ }
288
+ if (!isAbsolute(options.recoveryConfig) ||
289
+ options.bootstrapConfig !== null ||
290
+ options.marketplace ||
291
+ options.mode !== null ||
292
+ options.apply) {
293
+ output.stderr('pappra host bundle: --recovery-config requires an absolute path and cannot be combined with --bootstrap-config, --marketplace, --mode or --apply.\n');
294
+ return 2;
295
+ }
296
+ const loaded = readPiRecoveryConfig(options.recoveryConfig);
297
+ if (loaded.ok === false) {
298
+ output.stderr(`pappra host bundle: recovery config validation failed (${loaded.code}).\n`);
299
+ return 1;
300
+ }
301
+ recovery = {
302
+ configFile: options.recoveryConfig,
303
+ artifactPath: join(getPackageRoot(), 'dist/hosts/integration/pappra-recovery-extension.mjs'),
304
+ };
305
+ }
306
+ if (options.transitionExtension &&
307
+ (options.host !== 'pi' ||
308
+ options.bootstrapConfig !== null ||
309
+ options.marketplace ||
310
+ options.mode !== null ||
311
+ options.apply)) {
312
+ output.stderr('pappra host bundle: --transition-extension requires a plain Pi bundle and cannot be combined with --bootstrap-config, --marketplace, --mode or --apply.\n');
313
+ return 2;
314
+ }
209
315
  try {
210
316
  const exported = await exportHostPluginBundle({
211
317
  host: options.host,
@@ -213,6 +319,15 @@ export async function runHostCommand(options, output) {
213
319
  outDir: options.out,
214
320
  packageVersion: getPackageInfo().version,
215
321
  marketplace: options.marketplace,
322
+ ...(bootstrap === undefined ? {} : { bootstrap }),
323
+ ...(recovery === undefined ? {} : { recovery }),
324
+ ...(options.transitionExtension
325
+ ? {
326
+ transition: {
327
+ artifactPath: join(getPackageRoot(), 'dist/hosts/integration/pappra-session-transition-extension.mjs'),
328
+ },
329
+ }
330
+ : {}),
216
331
  });
217
332
  if (options.json) {
218
333
  output.stdout(`${JSON.stringify({ ok: true, command: 'host', action: 'bundle', ...exported })}\n`);