@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,323 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ import { isAssignment, isCard, } from '../collab/contracts.js';
5
+ import { canonicalJson } from '../collab/envelope.js';
6
+ import { transact } from '../collab/store.js';
7
+ import { ManagedPiSdkSessionController } from '../hosts/pi/managed-sdk-session.js';
8
+ import { validateSuccession } from './collab-route.js';
9
+ import { readBoundedFile } from './bounded-file.js';
10
+ import { openTaskStore, assertTaskStoreCurrent } from './collab-task-store.js';
11
+ const FIELD = 'managed_pi_source_admissions';
12
+ const object = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
13
+ const same = (a, b) => a.agent_id === b.agent_id &&
14
+ a.session_id === b.session_id &&
15
+ a.harness === b.harness;
16
+ const samePath = (a, b) => path.resolve(a) === path.resolve(b);
17
+ const digest = (text) => `sha256:${createHash('sha256').update(text).digest('hex')}`;
18
+ function rows(record) {
19
+ const value = record[FIELD];
20
+ if (value === undefined)
21
+ return [];
22
+ if (!Array.isArray(value) ||
23
+ value.length > 32 ||
24
+ !value.every((row) => object(row) &&
25
+ row.schema === 'pappra/managed-source-admission/1' &&
26
+ object(row.actor) &&
27
+ object(row.scope) &&
28
+ Array.isArray(row.claims) &&
29
+ row.claims.length > 0 &&
30
+ row.claims.length <= 1024))
31
+ throw new Error('managed_source_admissions_invalid');
32
+ return value;
33
+ }
34
+ function origin(attempt) {
35
+ if (!attempt.assignment_ref || !attempt.assignment_digest)
36
+ throw new Error('managed_source_origin_missing');
37
+ const bytes = readBoundedFile(attempt.assignment_ref, 1024 * 1024);
38
+ if (!bytes.ok || bytes.digest !== attempt.assignment_digest)
39
+ throw new Error('managed_source_origin_changed');
40
+ const value = JSON.parse(bytes.text);
41
+ if (!isAssignment(value))
42
+ throw new Error('managed_source_origin_invalid');
43
+ return {
44
+ assignment: value,
45
+ digest: bytes.digest,
46
+ file: attempt.assignment_ref,
47
+ };
48
+ }
49
+ function predecessor(record, actor, handoffId) {
50
+ const matches = (record.successions ?? []).filter((entry) => entry.successor_native.agent_id === actor.agent_id &&
51
+ entry.successor_native.session_id === actor.session_id &&
52
+ (handoffId === undefined || entry.handoff_id === handoffId));
53
+ if (matches.length !== 1)
54
+ throw new Error('managed_source_succession_missing_or_ambiguous');
55
+ const checked = validateSuccession(record, matches[0]);
56
+ if (!checked.ok)
57
+ throw new Error('managed_source_succession_unverified');
58
+ return checked;
59
+ }
60
+ function derived(base, entry) {
61
+ if (base.task_id !== entry.task_id ||
62
+ base.attempt !== entry.attempt ||
63
+ base.coordinator.owner_epoch !== entry.owner_epoch ||
64
+ !samePath(base.assignee.cwd, entry.actor.cwd) ||
65
+ base.resources.length !== entry.claims.length)
66
+ throw new Error('managed_source_derivation_scope_mismatch');
67
+ return {
68
+ ...structuredClone(base),
69
+ assignee: { ...entry.actor },
70
+ resources: base.resources.map((resource) => {
71
+ const claims = entry.claims.filter((claim) => samePath(claim.path, resource.path) &&
72
+ claim.access === resource.access);
73
+ if (claims.length !== 1)
74
+ throw new Error('managed_source_derivation_claim_mismatch');
75
+ return { ...resource, resource_epoch: claims[0].resource_epoch };
76
+ }),
77
+ };
78
+ }
79
+ /** Original ASSIGN pins stay immutable. A completed succession permits a bounded current-source view. */
80
+ export function readManagedSourceAssignment(record, attempt, actor = attempt.assignee) {
81
+ const base = origin(attempt);
82
+ if (same(base.assignment.assignee, actor))
83
+ return { ...base, originalDigest: base.digest };
84
+ const matches = rows(record).filter((entry) => same(entry.actor, actor) &&
85
+ entry.subtask_id === attempt.subtask_id &&
86
+ entry.attempt === attempt.attempt);
87
+ if (matches.length !== 1)
88
+ throw new Error('managed_source_admission_missing_or_ambiguous');
89
+ const entry = matches[0];
90
+ const lineage = predecessor(record, actor, entry.predecessor_handoff_id);
91
+ const runtime = record.managed_pi_runtime_registry;
92
+ if (!Array.isArray(runtime) ||
93
+ !runtime.some((row) => object(row) &&
94
+ row.session_id === actor.session_id &&
95
+ row.controller_run_id === entry.controller_run_id &&
96
+ canonicalJson(row.scope) === canonicalJson(entry.scope)) ||
97
+ entry.origin_ref !== base.file ||
98
+ entry.origin_digest !== base.digest ||
99
+ entry.task_id !== record.task_id ||
100
+ entry.input_revision !== attempt.input_revision ||
101
+ entry.owner_epoch !== attempt.owner_epoch ||
102
+ entry.predecessor_proof_digest !== lineage.exclusion.proof_digest ||
103
+ entry.claims.length !== lineage.intent.claims.length ||
104
+ !entry.claims.every((claim) => lineage.intent.claims.some((old) => old.claim_id === claim.claim_id &&
105
+ old.resource_epoch + 1 === claim.resource_epoch) &&
106
+ record.claims.some((stored) => stored.claim_id === claim.claim_id &&
107
+ stored.key === claim.key &&
108
+ stored.path !== null &&
109
+ stored.path !== undefined &&
110
+ samePath(stored.path, claim.path) &&
111
+ stored.access === claim.access)))
112
+ throw new Error('managed_source_admission_lineage_mismatch');
113
+ const file = readBoundedFile(entry.assignment_ref, 1024 * 1024);
114
+ if (!file.ok || file.digest !== entry.assignment_digest)
115
+ throw new Error('managed_source_assignment_changed');
116
+ const assignment = JSON.parse(file.text);
117
+ if (!isAssignment(assignment) ||
118
+ canonicalJson(assignment) !== canonicalJson(derived(base.assignment, entry)))
119
+ throw new Error('managed_source_assignment_derivation_mismatch');
120
+ return {
121
+ assignment,
122
+ digest: file.digest,
123
+ file: entry.assignment_ref,
124
+ originalDigest: base.digest,
125
+ };
126
+ }
127
+ function writeImmutable(file, value) {
128
+ const parent = fs.lstatSync(path.dirname(file));
129
+ if (!path.isAbsolute(file) ||
130
+ !parent.isDirectory() ||
131
+ parent.isSymbolicLink() ||
132
+ parent.mode & 0o077 ||
133
+ fs.realpathSync(path.dirname(file)) !== path.dirname(file))
134
+ throw new Error('managed_source_output_not_private');
135
+ const text = `${canonicalJson(value)}\n`;
136
+ if (fs.lstatSync(file, { throwIfNoEntry: false })) {
137
+ const old = readBoundedFile(file, 1024 * 1024);
138
+ if (!old.ok || old.digest !== digest(text))
139
+ throw new Error('managed_source_output_conflict');
140
+ }
141
+ else {
142
+ const fd = fs.openSync(file, 'wx', 0o600);
143
+ try {
144
+ fs.writeFileSync(fd, text);
145
+ fs.fsyncSync(fd);
146
+ }
147
+ finally {
148
+ fs.closeSync(fd);
149
+ }
150
+ const directory = fs.openSync(path.dirname(file), 'r');
151
+ try {
152
+ fs.fsyncSync(directory);
153
+ }
154
+ finally {
155
+ fs.closeSync(directory);
156
+ }
157
+ }
158
+ return digest(text);
159
+ }
160
+ /** Called only inside the successor's owned SDK process after its real first work. */
161
+ export async function admitManagedSourceAssignment(controller, scope, readiness) {
162
+ const native = controller.observe();
163
+ if (!(controller instanceof ManagedPiSdkSessionController) ||
164
+ controller.isDisposed ||
165
+ controller.snapshot().process.pid !== process.pid ||
166
+ native.native.sessionId !== scope.session_id ||
167
+ native.cwd !== scope.cwd ||
168
+ !native.native.agentId ||
169
+ !native.native.surface ||
170
+ !native.native.configurationId)
171
+ throw new Error('managed_source_runtime_not_owned');
172
+ const opened = await openTaskStore(scope.task_root);
173
+ if (!opened.ok)
174
+ throw new Error('managed_source_task_unavailable');
175
+ const record = opened.value.record;
176
+ const attempt = record.attempts.find((row) => row.subtask_id === scope.subtask_id && row.attempt === scope.attempt);
177
+ if (!attempt ||
178
+ attempt.assignee.session_id !== native.native.sessionId ||
179
+ attempt.assignee.agent_id !== native.native.agentId ||
180
+ attempt.assignee.harness !== 'pi' ||
181
+ attempt.owner_epoch !== scope.owner_epoch ||
182
+ record.coordinator?.owner_epoch !== scope.owner_epoch ||
183
+ !attempt.input_revision ||
184
+ !['assigned', 'accepted', 'running'].includes(attempt.state) ||
185
+ attempt.awaiting ||
186
+ attempt.cancel_requested_at)
187
+ throw new Error('managed_source_authority_changed');
188
+ const lineage = predecessor(record, attempt.assignee);
189
+ const base = origin(attempt);
190
+ const registered = Array.isArray(record.managed_pi_runtime_registry)
191
+ ? record.managed_pi_runtime_registry.filter((row) => object(row) &&
192
+ row.session_id === scope.session_id &&
193
+ canonicalJson(row.scope) === canonicalJson(scope))
194
+ : [];
195
+ if (registered.length !== 1 ||
196
+ !object(registered[0]) ||
197
+ typeof registered[0].controller_run_id !== 'string')
198
+ throw new Error('managed_source_runtime_unregistered');
199
+ const claims = scope.claim_epochs.map((pin) => {
200
+ const claim = record.claims.find((row) => row.claim_id === pin.claim_id &&
201
+ row.resource_epoch === pin.resource_epoch &&
202
+ !row.released_at &&
203
+ !row.cancel_pending &&
204
+ same(row.holder, attempt.assignee) &&
205
+ row.subtask_id === scope.subtask_id &&
206
+ row.attempt === scope.attempt);
207
+ if (!claim?.path)
208
+ throw new Error('managed_source_claim_changed');
209
+ return {
210
+ claim_id: claim.claim_id,
211
+ resource_epoch: claim.resource_epoch,
212
+ path: claim.path,
213
+ access: claim.access,
214
+ key: claim.key,
215
+ };
216
+ });
217
+ const entryBase = {
218
+ schema: 'pappra/managed-source-admission/1',
219
+ task_id: record.task_id,
220
+ subtask_id: scope.subtask_id,
221
+ attempt: scope.attempt,
222
+ input_revision: attempt.input_revision,
223
+ owner_epoch: scope.owner_epoch,
224
+ origin_ref: base.file,
225
+ origin_digest: base.digest,
226
+ assignment_ref: readiness.assignmentFile,
227
+ actor: {
228
+ ...attempt.assignee,
229
+ harness: 'pi',
230
+ cwd: native.cwd,
231
+ surface: native.native.surface,
232
+ configuration_id: native.native.configurationId,
233
+ },
234
+ scope,
235
+ controller_run_id: registered[0].controller_run_id,
236
+ predecessor_handoff_id: lineage.intent.handoff_id,
237
+ predecessor_proof_digest: lineage.exclusion.proof_digest,
238
+ claims,
239
+ };
240
+ const assignment = derived(base.assignment, {
241
+ ...entryBase,
242
+ assignment_digest: '',
243
+ });
244
+ const entry = {
245
+ ...entryBase,
246
+ assignment_digest: writeImmutable(readiness.assignmentFile, assignment),
247
+ };
248
+ // Reuse the approved parent catalog only as a declaration. The actual child SDK
249
+ // observation must independently promote every capability used by preflight.
250
+ if (!fs.existsSync(readiness.templateFile)) {
251
+ const launch = (record.launch_reservations ?? []).filter((row) => row.requested_session_id === scope.session_id);
252
+ const bootstrap = launch.length === 1 ? launch[0]?.bootstrap : null;
253
+ if (!object(bootstrap) || typeof bootstrap.request_file !== 'string')
254
+ throw new Error('managed_source_parent_request_missing');
255
+ const request = readBoundedFile(bootstrap.request_file, 64 * 1024);
256
+ if (!request.ok || request.digest !== bootstrap.request_digest)
257
+ throw new Error('managed_source_parent_request_changed');
258
+ const parsed = JSON.parse(request.text);
259
+ if (!object(parsed) ||
260
+ !object(parsed.admission) ||
261
+ typeof parsed.admission.capability_card_ref !== 'string')
262
+ throw new Error('managed_source_parent_card_missing');
263
+ const bytes = readBoundedFile(parsed.admission.capability_card_ref, 1024 * 1024);
264
+ if (!bytes.ok || bytes.digest !== parsed.admission.capability_card_digest)
265
+ throw new Error('managed_source_parent_card_changed');
266
+ const card = JSON.parse(bytes.text);
267
+ if (!isCard(card))
268
+ throw new Error('managed_source_parent_card_invalid');
269
+ const declare = (item) => {
270
+ const { evidence_ref: _evidence, ...rest } = item;
271
+ return { ...rest, state: 'declared' };
272
+ };
273
+ writeImmutable(readiness.templateFile, {
274
+ schema: 'harness-collab/capabilities/1',
275
+ ...entry.actor,
276
+ observed_at: new Date().toISOString(),
277
+ evidence_level: 'declared',
278
+ skills: card.skills.map(declare),
279
+ tools: card.tools.map(declare),
280
+ });
281
+ }
282
+ if (await assertTaskStoreCurrent(opened.value))
283
+ throw new Error('managed_source_task_namespace_changed');
284
+ const saved = transact(scope.task_root, {
285
+ actor: native.native.agentId,
286
+ purpose: 'record-managed-source-admission',
287
+ apply(current) {
288
+ const active = current.attempts.find((row) => row.subtask_id === scope.subtask_id && row.attempt === scope.attempt);
289
+ if (!active ||
290
+ !same(active.assignee, entry.actor) ||
291
+ active.owner_epoch !== scope.owner_epoch ||
292
+ active.input_revision !== entry.input_revision ||
293
+ !['assigned', 'accepted', 'running'].includes(active.state) ||
294
+ active.awaiting ||
295
+ active.cancel_requested_at ||
296
+ current.coordinator?.owner_epoch !== scope.owner_epoch ||
297
+ canonicalJson(predecessor(current, active.assignee, entry.predecessor_handoff_id)) !== canonicalJson(lineage) ||
298
+ !Array.isArray(current.managed_pi_runtime_registry) ||
299
+ canonicalJson(current.managed_pi_runtime_registry.filter((row) => object(row) &&
300
+ row.session_id === scope.session_id &&
301
+ canonicalJson(row.scope) === canonicalJson(scope))) !== canonicalJson(registered) ||
302
+ !claims.every((pin) => current.claims.some((row) => row.claim_id === pin.claim_id &&
303
+ row.resource_epoch === pin.resource_epoch &&
304
+ !row.released_at &&
305
+ !row.cancel_pending &&
306
+ same(row.holder, entry.actor))))
307
+ return { ok: false, reason: 'managed_source_admission_stale' };
308
+ const entries = rows(current);
309
+ const old = entries.find((row) => row.actor.session_id === entry.actor.session_id);
310
+ if (old && canonicalJson(old) !== canonicalJson(entry))
311
+ return { ok: false, reason: 'managed_source_admission_conflict' };
312
+ if (!old) {
313
+ if (entries.length >= 32)
314
+ return { ok: false, reason: 'managed_source_admission_limit' };
315
+ entries.push(entry);
316
+ }
317
+ current[FIELD] = entries;
318
+ return { record: current, result: null };
319
+ },
320
+ });
321
+ if (!saved.ok)
322
+ throw new Error(saved.reason);
323
+ }
@@ -0,0 +1,36 @@
1
+ import type { ManagedPiExecutionScope, SessionTransitionHost, SessionTransitionQuiescenceProof } from './collab-session-transition.js';
2
+ import type { ManagedPiSdkRolloverInventory, ManagedPiSdkSessionSnapshot } from '../hosts/pi/managed-sdk-session.js';
3
+ import type { RolloverFreezeController } from '../hosts/integration/pi-rollover-extension.js';
4
+ export declare const MANAGED_PI_TRANSITION_HOST_ID: 'pappra/managed-pi-session-transition-host/2';
5
+ export interface ManagedPiTransitionControllerPort {
6
+ readonly cwd?: string;
7
+ observe(): {
8
+ readonly native: {
9
+ readonly sessionId: string;
10
+ readonly paneId?: string | null;
11
+ };
12
+ readonly cwd: string;
13
+ };
14
+ snapshot(): ManagedPiSdkSessionSnapshot;
15
+ inventory(): Promise<ManagedPiSdkRolloverInventory>;
16
+ quiesce(reason?: string): Promise<ManagedPiSdkSessionSnapshot>;
17
+ fenceIngress(reason?: string): void;
18
+ }
19
+ export interface BoundManagedPiExecutionScope {
20
+ readonly scope: ManagedPiExecutionScope;
21
+ }
22
+ export interface BindManagedPiExecutionScopeInput {
23
+ readonly controller: ManagedPiTransitionControllerPort;
24
+ readonly scope: ManagedPiExecutionScope;
25
+ }
26
+ export interface CreateManagedPiTransitionHostInput {
27
+ readonly controller: ManagedPiTransitionControllerPort;
28
+ readonly freezeController: Pick<RolloverFreezeController, 'sessionId' | 'freeze' | 'status'>;
29
+ readonly binding: BoundManagedPiExecutionScope;
30
+ }
31
+ /** Bind the explicitly selected live SDK runtime to authoritative task pins before TUI startup. */
32
+ export declare function bindManagedPiExecutionScope(input: BindManagedPiExecutionScopeInput): Promise<BoundManagedPiExecutionScope>;
33
+ /** Build an in-process host whose v2 proof can only be minted from fresh live SDK snapshots. */
34
+ export declare function createManagedPiTransitionHost(input: CreateManagedPiTransitionHostInput): SessionTransitionHost;
35
+ /** Used by the guarded transition to reject serialized or caller-constructed v2 proof objects. */
36
+ export declare function isTrustedManagedPiScopedProof(value: unknown): value is SessionTransitionQuiescenceProof;
@@ -0,0 +1,292 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { createHash } from 'node:crypto';
4
+ import { canonicalJson } from '../collab/envelope.js';
5
+ import { findAttempt } from '../collab/lifecycle.js';
6
+ import { openTaskStore } from './collab-task-store.js';
7
+ export const MANAGED_PI_TRANSITION_HOST_ID = 'pappra/managed-pi-session-transition-host/2';
8
+ const OWNED_SCOPE = 'pinned_pi_1_4_owned_runtime';
9
+ const EXCLUDED_SCOPE = Object.freeze([
10
+ 'arbitrary_external_writers',
11
+ 'unowned_processes_and_descendants',
12
+ ]);
13
+ const BINDINGS = new WeakSet();
14
+ const LIVE_PROOFS = new WeakSet();
15
+ function digest(value) {
16
+ return `sha256:${createHash('sha256').update(canonicalJson(value)).digest('hex')}`;
17
+ }
18
+ function samePath(left, right) {
19
+ try {
20
+ return fs.realpathSync(left) === fs.realpathSync(right);
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ function cloneScope(scope) {
27
+ return Object.freeze({
28
+ schema: scope.schema,
29
+ task_id: scope.task_id,
30
+ task_root: scope.task_root,
31
+ session_id: scope.session_id,
32
+ cwd: scope.cwd,
33
+ subtask_id: scope.subtask_id,
34
+ attempt: scope.attempt,
35
+ owner_epoch: scope.owner_epoch,
36
+ claim_epochs: Object.freeze(scope.claim_epochs.map((claim) => Object.freeze({
37
+ claim_id: claim.claim_id,
38
+ resource_epoch: claim.resource_epoch,
39
+ }))),
40
+ });
41
+ }
42
+ function validateScopeShape(scope) {
43
+ if (scope.schema !== 'pappra/managed-pi-execution-scope/1' ||
44
+ !scope.task_id ||
45
+ !path.isAbsolute(scope.task_root) ||
46
+ !scope.session_id ||
47
+ !path.isAbsolute(scope.cwd) ||
48
+ !scope.subtask_id ||
49
+ !Number.isSafeInteger(scope.attempt) ||
50
+ scope.attempt < 1 ||
51
+ !Number.isSafeInteger(scope.owner_epoch) ||
52
+ scope.owner_epoch <= 0 ||
53
+ !Array.isArray(scope.claim_epochs) ||
54
+ scope.claim_epochs.length === 0)
55
+ throw new TypeError('managed_pi_execution_scope_invalid');
56
+ const seen = new Set();
57
+ for (const claim of scope.claim_epochs) {
58
+ if (!claim.claim_id ||
59
+ !Number.isSafeInteger(claim.resource_epoch) ||
60
+ claim.resource_epoch <= 0 ||
61
+ seen.has(claim.claim_id))
62
+ throw new TypeError('managed_pi_execution_scope_claims_invalid');
63
+ seen.add(claim.claim_id);
64
+ }
65
+ }
66
+ /** Bind the explicitly selected live SDK runtime to authoritative task pins before TUI startup. */
67
+ export async function bindManagedPiExecutionScope(input) {
68
+ const { controller, scope } = input;
69
+ validateScopeShape(scope);
70
+ if (path.basename(scope.task_root) !== scope.task_id)
71
+ throw new TypeError('managed_pi_execution_scope_task_mismatch');
72
+ const live = controller.observe();
73
+ const snapshot = controller.snapshot();
74
+ if (live.native.sessionId !== scope.session_id ||
75
+ !samePath(live.cwd, scope.cwd) ||
76
+ snapshot.session.sessionId !== scope.session_id)
77
+ throw new TypeError('managed_pi_execution_scope_runtime_mismatch');
78
+ assertCleanPrelaunchRuntime(snapshot);
79
+ const liveInventory = await controller.inventory();
80
+ if (liveInventory.completeness !== 'complete' ||
81
+ liveInventory.inflight.length !== 0 ||
82
+ liveInventory.pending_approvals.length !== 0 ||
83
+ (liveInventory.pending_messages?.length ?? -1) !== 0)
84
+ throw new TypeError('managed_pi_execution_scope_runtime_busy_or_unknown');
85
+ const opened = await openTaskStore(scope.task_root);
86
+ if (!opened.ok)
87
+ throw new TypeError(`managed_pi_execution_scope_task_unavailable:${opened.code}`);
88
+ const record = opened.value.record;
89
+ if (record.task_id !== scope.task_id ||
90
+ !samePath(record.task_root, scope.task_root))
91
+ throw new TypeError('managed_pi_execution_scope_task_mismatch');
92
+ if (!Number.isSafeInteger(scope.owner_epoch) ||
93
+ scope.owner_epoch <= 0 ||
94
+ record.coordinator?.owner_epoch !== scope.owner_epoch)
95
+ throw new TypeError('managed_pi_execution_scope_owner_epoch_stale');
96
+ const attempt = findAttempt(record, scope.subtask_id, scope.attempt);
97
+ const latestAttempt = (record.attempts ?? [])
98
+ .filter((candidate) => candidate.subtask_id === scope.subtask_id)
99
+ .reduce((latest, candidate) => Math.max(latest, candidate.attempt), 0);
100
+ if (attempt === null ||
101
+ latestAttempt !== scope.attempt ||
102
+ !['assigned', 'accepted', 'running'].includes(attempt.state) ||
103
+ attempt.owner_epoch !== scope.owner_epoch ||
104
+ attempt.assignee.harness !== 'pi' ||
105
+ attempt.assignee.session_id !== scope.session_id ||
106
+ (attempt.assignee.cwd !== undefined && attempt.assignee.cwd !== scope.cwd))
107
+ throw new TypeError('managed_pi_execution_scope_attempt_mismatch');
108
+ if (scope.claim_epochs.length === 0)
109
+ throw new TypeError('managed_pi_execution_scope_claims_empty');
110
+ const currentClaims = (record.claims ?? []).filter((claim) => scope.claim_epochs.some((pin) => pin.claim_id === claim.claim_id));
111
+ if (currentClaims.length !== scope.claim_epochs.length)
112
+ throw new TypeError('managed_pi_execution_scope_claim_set_mismatch');
113
+ for (const pin of scope.claim_epochs) {
114
+ const claim = currentClaims.find((candidate) => candidate.claim_id === pin.claim_id);
115
+ if (pin.resource_epoch <= 0 ||
116
+ claim === undefined ||
117
+ claim.released_at !== null ||
118
+ claim.subtask_id !== scope.subtask_id ||
119
+ claim.attempt !== scope.attempt ||
120
+ (claim.resource_epoch ?? 0) !== pin.resource_epoch ||
121
+ claim.holder.agent_id !== attempt.assignee.agent_id ||
122
+ claim.holder.session_id !== attempt.assignee.session_id ||
123
+ claim.holder.harness !== 'pi')
124
+ throw new TypeError('managed_pi_execution_scope_claim_epoch_stale');
125
+ }
126
+ const frozen = Object.freeze({ scope: cloneScope(scope) });
127
+ BINDINGS.add(frozen);
128
+ return frozen;
129
+ }
130
+ function assertCleanPrelaunchRuntime(snapshot) {
131
+ const { queues, work, extensionDialogs, builtInUiApprovals } = snapshot.ownedRuntime;
132
+ const internal = queues.internal;
133
+ const queueCounts = [
134
+ internal.pendingNextTurn?.length,
135
+ internal.pendingCustom?.length,
136
+ internal.pendingBash?.length,
137
+ internal.deferredSettledActionCount,
138
+ internal.pendingUserInputs?.length,
139
+ internal.compactionMessages?.length,
140
+ queues.steering.length,
141
+ queues.followUp.length,
142
+ ];
143
+ if (!snapshot.ingress.healthy ||
144
+ snapshot.ingress.state !== 'open' ||
145
+ snapshot.ownedRuntime.queues.pendingMessageCount !== 0 ||
146
+ queues.completeness !== 'complete' ||
147
+ work.observation !== 'complete' ||
148
+ extensionDialogs.completeness !== 'complete' ||
149
+ builtInUiApprovals.observation !== 'complete' ||
150
+ !work.isIdle ||
151
+ work.isStreaming ||
152
+ work.isCompacting ||
153
+ work.isBashRunning !== false ||
154
+ work.hasPendingBashMessages !== false ||
155
+ work.pendingToolCalls !== 0 ||
156
+ queueCounts.some((count) => count === null || count === undefined || count !== 0) ||
157
+ extensionDialogs.pending.length !== 0 ||
158
+ internal.extensionDialogCount !== 0 ||
159
+ internal.activeSelector !== false)
160
+ throw new TypeError('managed_pi_execution_scope_runtime_busy_or_unknown');
161
+ }
162
+ function isBound(binding) {
163
+ return (typeof binding === 'object' && binding !== null && BINDINGS.has(binding));
164
+ }
165
+ function deepFreeze(value) {
166
+ if (typeof value !== 'object' || value === null || Object.isFrozen(value))
167
+ return value;
168
+ for (const child of Object.values(value))
169
+ deepFreeze(child);
170
+ return Object.freeze(value);
171
+ }
172
+ function asRolloverInventory(value) {
173
+ return deepFreeze({
174
+ completeness: value.completeness,
175
+ inflight: [...value.inflight],
176
+ pending_approvals: [...value.pending_approvals],
177
+ pending_messages: value.pending_messages === undefined
178
+ ? undefined
179
+ : [...value.pending_messages],
180
+ partial_outputs: value.partial_outputs === undefined
181
+ ? undefined
182
+ : [...value.partial_outputs],
183
+ });
184
+ }
185
+ function freezeStatus(controller) {
186
+ return controller.status();
187
+ }
188
+ /** Build an in-process host whose v2 proof can only be minted from fresh live SDK snapshots. */
189
+ export function createManagedPiTransitionHost(input) {
190
+ if (!isBound(input.binding))
191
+ throw new TypeError('managed_pi_execution_scope_binding_untrusted');
192
+ const scope = input.binding.scope;
193
+ if (input.freezeController.sessionId !== scope.session_id)
194
+ throw new TypeError('managed_pi_transition_freeze_session_mismatch');
195
+ let observationSequence = 0;
196
+ const identity = () => {
197
+ const observed = input.controller.observe();
198
+ const snapshot = input.controller.snapshot();
199
+ return { observed, snapshot };
200
+ };
201
+ return Object.freeze({
202
+ id: MANAGED_PI_TRANSITION_HOST_ID,
203
+ async observe() {
204
+ const { observed, snapshot } = identity();
205
+ if (observed.native.sessionId !== scope.session_id ||
206
+ !samePath(observed.cwd, scope.cwd) ||
207
+ snapshot.session.sessionId !== scope.session_id)
208
+ return {
209
+ session_id: observed.native.sessionId ?? null,
210
+ cwd: observed.cwd ?? null,
211
+ pane_id: observed.native.paneId ?? null,
212
+ proof_support: 'unavailable',
213
+ proof: null,
214
+ freeze_status: freezeStatus(input.freezeController),
215
+ native_facts: null,
216
+ unavailable_code: 'managed_pi_scope_runtime_mismatch',
217
+ detail: 'The live SDK runtime no longer matches its pre-start task-owned binding.',
218
+ };
219
+ const status = freezeStatus(input.freezeController);
220
+ let proof = null;
221
+ if (status.frozen &&
222
+ status.token !== null &&
223
+ status.token.sessionId === scope.session_id) {
224
+ const inventory = await input.controller.inventory();
225
+ const fresh = input.controller.snapshot();
226
+ const sequence = Math.max(++observationSequence, fresh.ingress.sequence);
227
+ observationSequence = sequence;
228
+ const proofValue = deepFreeze({
229
+ adapter_id: MANAGED_PI_TRANSITION_HOST_ID,
230
+ session_id: fresh.session.sessionId,
231
+ cwd: observed.cwd,
232
+ observed_at: new Date().toISOString(),
233
+ sequence,
234
+ evidence_ref: `managed-pi-sdk://${scope.session_id}/${sequence}/${fresh.ingress.headDigest}`,
235
+ freeze_token: Object.freeze({ ...status.token }),
236
+ inventory: asRolloverInventory(inventory),
237
+ scope: OWNED_SCOPE,
238
+ execution_scope: scope,
239
+ execution_scope_digest: digest(scope),
240
+ excluded_scope: EXCLUDED_SCOPE,
241
+ runtime_quiescent: fresh.scopedQuiescent,
242
+ runtime_blockers: Object.freeze([...fresh.blockers]),
243
+ controller_sequence: fresh.ingress.sequence,
244
+ controller_head_digest: `sha256:${fresh.ingress.headDigest}`,
245
+ controller_journal_healthy: fresh.ingress.healthy,
246
+ });
247
+ LIVE_PROOFS.add(proofValue);
248
+ proof = proofValue;
249
+ }
250
+ return {
251
+ session_id: observed.native.sessionId,
252
+ cwd: observed.cwd,
253
+ pane_id: observed.native.paneId ?? null,
254
+ proof_support: 'available',
255
+ proof,
256
+ freeze_status: status,
257
+ native_facts: null,
258
+ unavailable_code: null,
259
+ detail: null,
260
+ };
261
+ },
262
+ async freeze(token) {
263
+ if (token.sessionId !== scope.session_id ||
264
+ token.taskId !== scope.task_id ||
265
+ input.freezeController.sessionId !== token.sessionId)
266
+ return {
267
+ ok: false,
268
+ reason: 'wrong_session',
269
+ detail: 'The freeze token does not match the bound task-owned SDK runtime.',
270
+ };
271
+ const { observed, snapshot } = identity();
272
+ if (observed.native.sessionId !== scope.session_id ||
273
+ !samePath(observed.cwd, scope.cwd) ||
274
+ snapshot.session.sessionId !== scope.session_id)
275
+ return {
276
+ ok: false,
277
+ reason: 'wrong_session',
278
+ detail: 'Live SDK identity drifted from the bound task scope.',
279
+ };
280
+ const outcome = input.freezeController.freeze(token);
281
+ if (!outcome.ok)
282
+ return outcome;
283
+ // This is the live controller operation that closes ingress and waits for the pinned SDK runtime.
284
+ await input.controller.quiesce(`owned_native_takeover:${token.handoffId}:${token.generation}`);
285
+ return outcome;
286
+ },
287
+ });
288
+ }
289
+ /** Used by the guarded transition to reject serialized or caller-constructed v2 proof objects. */
290
+ export function isTrustedManagedPiScopedProof(value) {
291
+ return typeof value === 'object' && value !== null && LIVE_PROOFS.has(value);
292
+ }