@timurproko/a1 0.2.7-dev.1249 → 0.2.7-dev.1252

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 (340) hide show
  1. package/README.md +11 -36
  2. package/bin/cli.js +2 -17
  3. package/bin/ui.js +7 -48
  4. package/dist/app/session-shell/exit-notice.d.ts +2 -2
  5. package/dist/app/session-shell/exit-notice.js +10 -10
  6. package/dist/app/session-shell/prompt-submission.d.ts +2 -4
  7. package/dist/app/session-shell/prompt-submission.js +3 -23
  8. package/dist/app/session-shell/response-copy-transport.js +1 -2
  9. package/dist/app/session-shell/session-lifecycle.d.ts +1 -2
  10. package/dist/app/session-shell/session-lifecycle.js +2 -4
  11. package/dist/app/session-shell/session-shell-root.d.ts +0 -31
  12. package/dist/app/session-shell/session-shell-root.js +3 -4
  13. package/dist/app/session-shell/session-shell.js +6 -47
  14. package/dist/app/session-shell/session-viewport-controller.js +3 -6
  15. package/dist/app/session-shell/shell-host.js +2 -18
  16. package/dist/app/session-shell/transcript-document.d.ts +1 -2
  17. package/dist/app/session-shell/transcript-document.js +3 -28
  18. package/dist/app/session-shell/workflow-output.js +1 -1
  19. package/dist/app/session-shell/workflow-router.d.ts +1 -3
  20. package/dist/app/session-shell/workflow-router.js +1 -4
  21. package/dist/cli/dispatch.d.ts +1 -11
  22. package/dist/cli/dispatch.js +5 -92
  23. package/dist/cli/session-context.js +5 -7
  24. package/dist/cli/uninstall.d.ts +1 -5
  25. package/dist/cli/uninstall.js +6 -35
  26. package/dist/cli/version-stats.js +4 -7
  27. package/dist/composition/owned-settings.d.ts +0 -1
  28. package/dist/composition/owned-settings.js +0 -1
  29. package/dist/composition/owned-ui.d.ts +2 -24
  30. package/dist/composition/owned-ui.js +11 -87
  31. package/dist/composition/release-update-observation.d.ts +4 -4
  32. package/dist/composition/release-update-observation.js +17 -47
  33. package/dist/composition/session-backend.d.ts +0 -1
  34. package/dist/composition/session-runtime-registry.d.ts +3 -3
  35. package/dist/composition/settings-route-host.d.ts +0 -7
  36. package/dist/composition/settings-route-host.js +4 -10
  37. package/dist/composition/startup-route.d.ts +0 -1
  38. package/dist/composition/startup-route.js +0 -1
  39. package/dist/contracts/owned-ui/index.d.ts +1 -1
  40. package/dist/contracts/owned-ui/model.d.ts +0 -3
  41. package/dist/contracts/owned-ui/session-workflows.d.ts +1 -4
  42. package/dist/features/launch/development-launch.js +0 -1
  43. package/dist/features/launch/index.d.ts +1 -1
  44. package/dist/features/launch/intent.d.ts +1 -3
  45. package/dist/features/launch/intent.js +2 -4
  46. package/dist/features/launch/session-context.d.ts +2 -2
  47. package/dist/features/owned-ui/reference-screen-app.d.ts +0 -2
  48. package/dist/features/owned-ui/reference-screen-app.js +2 -9
  49. package/dist/features/owned-ui/settings-app.d.ts +0 -6
  50. package/dist/features/owned-ui/settings-app.js +5 -36
  51. package/dist/foundation/launch-context/index.d.ts +0 -12
  52. package/dist/foundation/launch-context/index.js +0 -26
  53. package/dist/foundation/lifecycle/claim-gate.d.ts +8 -0
  54. package/dist/foundation/lifecycle/claim-gate.js +37 -0
  55. package/dist/foundation/lifecycle/index.d.ts +5 -9
  56. package/dist/foundation/lifecycle/index.js +3 -4
  57. package/dist/foundation/lifecycle/model.d.ts +2 -59
  58. package/dist/foundation/lifecycle/model.js +0 -35
  59. package/dist/foundation/lifecycle/paths.d.ts +0 -24
  60. package/dist/foundation/lifecycle/paths.js +0 -40
  61. package/dist/foundation/lifecycle/process-liveness.d.ts +8 -0
  62. package/dist/foundation/lifecycle/process-liveness.js +18 -0
  63. package/dist/foundation/lifecycle/session-claim-lock.d.ts +1 -1
  64. package/dist/foundation/lifecycle/session-claim-lock.js +2 -2
  65. package/dist/foundation/lifecycle/session-repository-context.d.ts +6 -6
  66. package/dist/foundation/lifecycle/session-repository-context.js +2 -3
  67. package/dist/foundation/release/automatic-update.d.ts +1 -3
  68. package/dist/foundation/release/automatic-update.js +9 -30
  69. package/dist/foundation/release/bootstrap.d.ts +2 -25
  70. package/dist/foundation/release/bootstrap.js +33 -344
  71. package/dist/foundation/release/cohort-state.d.ts +21 -46
  72. package/dist/foundation/release/cohort-state.js +30 -62
  73. package/dist/foundation/release/dependency-certification.d.ts +1 -1
  74. package/dist/foundation/release/index.d.ts +10 -17
  75. package/dist/foundation/release/index.js +7 -11
  76. package/dist/foundation/release/latest-release.d.ts +1 -2
  77. package/dist/foundation/release/latest-release.js +1 -2
  78. package/dist/foundation/release/process-liveness.d.ts +2 -0
  79. package/dist/foundation/release/process-liveness.js +12 -0
  80. package/dist/foundation/release/release-gc.d.ts +12 -9
  81. package/dist/foundation/release/release-gc.js +34 -98
  82. package/dist/foundation/release/release-store.d.ts +2 -2
  83. package/dist/foundation/release/release-store.js +2 -2
  84. package/dist/foundation/release/restart-certification.js +1 -1
  85. package/dist/foundation/release/update-activation.d.ts +2 -9
  86. package/dist/foundation/release/update-activation.js +28 -43
  87. package/dist/foundation/release/update-execution-lease.js +1 -1
  88. package/dist/foundation/release/update-launch.d.ts +0 -2
  89. package/dist/foundation/release/update-launch.js +0 -7
  90. package/dist/foundation/release/update-recovery.js +1 -1
  91. package/dist/foundation/release/update-transaction.d.ts +1 -1
  92. package/dist/foundation/release/update-transaction.js +5 -2
  93. package/dist/foundation/release/update.d.ts +2 -10
  94. package/dist/foundation/release/update.js +23 -200
  95. package/dist/foundation/session-writer-lock/index.d.ts +14 -6
  96. package/dist/foundation/session-writer-lock/index.js +48 -103
  97. package/dist/foundation/startup/startup-runtime.d.ts +1 -1
  98. package/dist/foundation/terminal-cleanup/exit-notice.d.ts +6 -0
  99. package/dist/foundation/terminal-cleanup/exit-notice.js +36 -0
  100. package/dist/foundation/terminal-cleanup/index.d.ts +2 -1
  101. package/dist/foundation/terminal-cleanup/index.js +2 -1
  102. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +0 -5
  103. package/dist/foundation/terminal-cleanup/terminal-reset.js +0 -5
  104. package/dist/integrations/pi/components/index.d.ts +1 -1
  105. package/dist/integrations/pi/components/index.js +1 -1
  106. package/dist/integrations/pi/components/shell-editor-autocomplete.js +1 -4
  107. package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
  108. package/dist/integrations/pi/components/shell-footer-status.js +2 -2
  109. package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
  110. package/dist/integrations/pi/components/shell-presenters-info.d.ts +2 -5
  111. package/dist/integrations/pi/components/shell-presenters-info.js +4 -18
  112. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +1 -1
  113. package/dist/integrations/pi/components/shell-presenters-transcript.js +1 -1
  114. package/dist/integrations/pi/components/shell-shared-facade.d.ts +0 -2
  115. package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +2 -5
  116. package/dist/integrations/pi/components/submitted-prompt-adapter.js +2 -16
  117. package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +1 -2
  118. package/dist/integrations/pi/components/upstream/components/session-footer.js +6 -8
  119. package/dist/integrations/pi/engine/adapter.d.ts +0 -1
  120. package/dist/integrations/pi/engine/adapter.js +1 -16
  121. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -1
  122. package/dist/integrations/pi/engine/diagnostics-state.js +0 -14
  123. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +0 -1
  124. package/dist/integrations/pi/engine/prompt-suggestions.js +0 -40
  125. package/dist/integrations/pi/engine/session-runtime.js +0 -2
  126. package/dist/integrations/pi/engine/workflow-runner.js +1 -1
  127. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -2
  128. package/dist/integrations/pi/tui-runtime/adapter.js +2 -2
  129. package/dist/product-identity.d.ts +4 -6
  130. package/dist/product-identity.js +6 -11
  131. package/dist/product-identity.json +1 -40
  132. package/dist/runtime-payload-inventory.json +0 -3
  133. package/dist/ui/components/index.d.ts +2 -2
  134. package/dist/ui/components/index.js +1 -1
  135. package/dist/ui/components/submitted-prompt.d.ts +3 -13
  136. package/dist/ui/components/submitted-prompt.js +8 -41
  137. package/dist/ui/components/transcript-viewport.d.ts +1 -3
  138. package/dist/ui/components/transcript-viewport.js +1 -4
  139. package/dist/ui/settings/declarations.d.ts +3 -97
  140. package/dist/ui/settings/declarations.js +3 -78
  141. package/dist/ui/settings/migrations.js +18 -2
  142. package/docs/architecture/boundaries.md +22 -39
  143. package/docs/architecture/code-documentation.md +2 -2
  144. package/docs/architecture/internal-naming.md +9 -17
  145. package/docs/architecture/project-structure.md +9 -14
  146. package/docs/architecture/resource-and-data-policy.md +16 -31
  147. package/docs/architecture/toolchain.md +14 -19
  148. package/docs/architecture/ui-reference-provenance.md +2 -2
  149. package/docs/ci-release-runbook.md +5 -5
  150. package/docs/features/launch-profiles.md +11 -17
  151. package/docs/features/prompt-history.md +10 -11
  152. package/docs/local-worktree-cleanup.md +28 -1
  153. package/docs/manual-owned-ui-checkpoint.md +0 -1
  154. package/docs/validation.md +5 -11
  155. package/docs/worktree-link-recovery.md +6 -6
  156. package/package.json +7 -19
  157. package/bin/guardian.js +0 -42
  158. package/bin/supervisor.js +0 -7
  159. package/bin/tab-launch-context.d.ts +0 -27
  160. package/bin/tab-launch-context.js +0 -94
  161. package/dist/app/session-shell/resident-tab-coordinator.d.ts +0 -38
  162. package/dist/app/session-shell/resident-tab-coordinator.js +0 -77
  163. package/dist/cli/tabs-request.d.ts +0 -7
  164. package/dist/cli/tabs-request.js +0 -1
  165. package/dist/cli/tabs.d.ts +0 -14
  166. package/dist/cli/tabs.js +0 -151
  167. package/dist/composition/resident-tab-bridge.d.ts +0 -41
  168. package/dist/composition/resident-tab-bridge.js +0 -209
  169. package/dist/composition/utility-tab.d.ts +0 -28
  170. package/dist/composition/utility-tab.js +0 -244
  171. package/dist/features/owned-ui/plus-menu-dialog.d.ts +0 -28
  172. package/dist/features/owned-ui/plus-menu-dialog.js +0 -89
  173. package/dist/features/resident-tab/auto-name.d.ts +0 -11
  174. package/dist/features/resident-tab/auto-name.js +0 -44
  175. package/dist/features/resident-tab/index.d.ts +0 -5
  176. package/dist/features/resident-tab/index.js +0 -7
  177. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +0 -8
  178. package/dist/features/resident-tab/shortcut-bindings-file.js +0 -40
  179. package/dist/features/resident-tab/shortcut-declarations.d.ts +0 -13
  180. package/dist/features/resident-tab/shortcut-declarations.js +0 -26
  181. package/dist/features/resident-tab/shortcut-projection.d.ts +0 -29
  182. package/dist/features/resident-tab/shortcut-projection.js +0 -64
  183. package/dist/features/resident-tab/status-deriver.d.ts +0 -10
  184. package/dist/features/resident-tab/status-deriver.js +0 -12
  185. package/dist/foundation/launch-guardian/exit-watcher.d.ts +0 -14
  186. package/dist/foundation/launch-guardian/exit-watcher.js +0 -103
  187. package/dist/foundation/launch-guardian/index.d.ts +0 -2
  188. package/dist/foundation/launch-guardian/index.js +0 -1
  189. package/dist/foundation/launch-guardian/main.d.ts +0 -39
  190. package/dist/foundation/launch-guardian/main.js +0 -232
  191. package/dist/foundation/lifecycle/commands.d.ts +0 -40
  192. package/dist/foundation/lifecycle/commands.js +0 -1
  193. package/dist/foundation/lifecycle/launch-instance.d.ts +0 -46
  194. package/dist/foundation/lifecycle/launch-instance.js +0 -134
  195. package/dist/foundation/lifecycle/native-claim-gate.d.ts +0 -5
  196. package/dist/foundation/lifecycle/native-claim-gate.js +0 -41
  197. package/dist/foundation/lifecycle/supervisor-startup.d.ts +0 -30
  198. package/dist/foundation/lifecycle/supervisor-startup.js +0 -106
  199. package/dist/foundation/process-containment/addon-containment.d.ts +0 -20
  200. package/dist/foundation/process-containment/addon-containment.js +0 -139
  201. package/dist/foundation/process-containment/addon-process-inspector.d.ts +0 -11
  202. package/dist/foundation/process-containment/addon-process-inspector.js +0 -36
  203. package/dist/foundation/process-containment/addon.d.ts +0 -47
  204. package/dist/foundation/process-containment/addon.js +0 -57
  205. package/dist/foundation/process-containment/artifact.d.ts +0 -1
  206. package/dist/foundation/process-containment/artifact.js +0 -35
  207. package/dist/foundation/process-containment/contracts.d.ts +0 -33
  208. package/dist/foundation/process-containment/contracts.js +0 -35
  209. package/dist/foundation/process-containment/index.d.ts +0 -9
  210. package/dist/foundation/process-containment/index.js +0 -7
  211. package/dist/foundation/process-containment/linux-process-inspector.d.ts +0 -7
  212. package/dist/foundation/process-containment/linux-process-inspector.js +0 -27
  213. package/dist/foundation/process-containment/native-process-inspector.d.ts +0 -4
  214. package/dist/foundation/process-containment/native-process-inspector.js +0 -21
  215. package/dist/foundation/protocol/client.d.ts +0 -18
  216. package/dist/foundation/protocol/client.js +0 -130
  217. package/dist/foundation/protocol/index.d.ts +0 -3
  218. package/dist/foundation/protocol/index.js +0 -2
  219. package/dist/foundation/protocol/messages.d.ts +0 -90
  220. package/dist/foundation/protocol/messages.js +0 -100
  221. package/dist/foundation/release/cohort-selection.d.ts +0 -33
  222. package/dist/foundation/release/cohort-selection.js +0 -67
  223. package/dist/foundation/release/endpoint-lock.d.ts +0 -2
  224. package/dist/foundation/release/endpoint-lock.js +0 -37
  225. package/dist/foundation/release/endpoint-recovery.d.ts +0 -10
  226. package/dist/foundation/release/endpoint-recovery.js +0 -45
  227. package/dist/foundation/release/endpoints.d.ts +0 -32
  228. package/dist/foundation/release/endpoints.js +0 -152
  229. package/dist/foundation/release/prepared-update-activation.d.ts +0 -3
  230. package/dist/foundation/release/prepared-update-activation.js +0 -27
  231. package/dist/foundation/release/process-cleanup.d.ts +0 -14
  232. package/dist/foundation/release/process-cleanup.js +0 -76
  233. package/dist/foundation/release/supervisor-identity.d.ts +0 -15
  234. package/dist/foundation/release/supervisor-identity.js +0 -63
  235. package/dist/foundation/resident-tabs/boot-identity.d.ts +0 -6
  236. package/dist/foundation/resident-tabs/boot-identity.js +0 -16
  237. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +0 -20
  238. package/dist/foundation/resident-tabs/bounded-host-command.js +0 -62
  239. package/dist/foundation/resident-tabs/bridge-client.d.ts +0 -73
  240. package/dist/foundation/resident-tabs/bridge-client.js +0 -327
  241. package/dist/foundation/resident-tabs/continuity.d.ts +0 -12
  242. package/dist/foundation/resident-tabs/continuity.js +0 -63
  243. package/dist/foundation/resident-tabs/doctor.d.ts +0 -28
  244. package/dist/foundation/resident-tabs/doctor.js +0 -166
  245. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +0 -27
  246. package/dist/foundation/resident-tabs/draft-checkpoint.js +0 -145
  247. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +0 -36
  248. package/dist/foundation/resident-tabs/ensure-terminal-host.js +0 -151
  249. package/dist/foundation/resident-tabs/fault-injection.d.ts +0 -8
  250. package/dist/foundation/resident-tabs/fault-injection.js +0 -26
  251. package/dist/foundation/resident-tabs/fixture-control.d.ts +0 -7
  252. package/dist/foundation/resident-tabs/fixture-control.js +0 -35
  253. package/dist/foundation/resident-tabs/host-entry.d.ts +0 -1
  254. package/dist/foundation/resident-tabs/host-entry.js +0 -17
  255. package/dist/foundation/resident-tabs/index.d.ts +0 -15
  256. package/dist/foundation/resident-tabs/index.js +0 -10
  257. package/dist/foundation/resident-tabs/maintenance.d.ts +0 -39
  258. package/dist/foundation/resident-tabs/maintenance.js +0 -75
  259. package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +0 -34
  260. package/dist/foundation/resident-tabs/progress-observation-schema.js +0 -1
  261. package/dist/foundation/resident-tabs/progress-observation.d.ts +0 -4
  262. package/dist/foundation/resident-tabs/progress-observation.js +0 -150
  263. package/dist/foundation/resident-tabs/prompt-journal.d.ts +0 -63
  264. package/dist/foundation/resident-tabs/prompt-journal.js +0 -213
  265. package/dist/foundation/resident-tabs/protocol.d.ts +0 -12
  266. package/dist/foundation/resident-tabs/protocol.js +0 -195
  267. package/dist/foundation/resident-tabs/recovery.d.ts +0 -26
  268. package/dist/foundation/resident-tabs/recovery.js +0 -205
  269. package/dist/foundation/resident-tabs/release-holds.d.ts +0 -28
  270. package/dist/foundation/resident-tabs/release-holds.js +0 -78
  271. package/dist/foundation/resident-tabs/routes.d.ts +0 -5
  272. package/dist/foundation/resident-tabs/routes.js +0 -5
  273. package/dist/foundation/resident-tabs/routing.d.ts +0 -11
  274. package/dist/foundation/resident-tabs/routing.js +0 -10
  275. package/dist/foundation/resident-tabs/runtime-control.d.ts +0 -38
  276. package/dist/foundation/resident-tabs/runtime-control.js +0 -74
  277. package/dist/foundation/resident-tabs/session-sync.d.ts +0 -14
  278. package/dist/foundation/resident-tabs/session-sync.js +0 -37
  279. package/dist/foundation/resident-tabs/settings.d.ts +0 -16
  280. package/dist/foundation/resident-tabs/settings.js +0 -60
  281. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +0 -14
  282. package/dist/foundation/resident-tabs/terminal-host-artifact.js +0 -78
  283. package/dist/foundation/resident-tabs/zip-archive.d.ts +0 -9
  284. package/dist/foundation/resident-tabs/zip-archive.js +0 -56
  285. package/dist/foundation/storage/control-store.d.ts +0 -20
  286. package/dist/foundation/storage/control-store.js +0 -331
  287. package/dist/foundation/storage/index.d.ts +0 -1
  288. package/dist/foundation/storage/index.js +0 -1
  289. package/dist/foundation/supervision/index.d.ts +0 -3
  290. package/dist/foundation/supervision/index.js +0 -3
  291. package/dist/foundation/supervision/main.d.ts +0 -1
  292. package/dist/foundation/supervision/main.js +0 -86
  293. package/dist/foundation/supervision/paths.d.ts +0 -1
  294. package/dist/foundation/supervision/paths.js +0 -1
  295. package/dist/foundation/supervision/server.d.ts +0 -46
  296. package/dist/foundation/supervision/server.js +0 -611
  297. package/dist/integrations/pi/engine/offline-settings-port.d.ts +0 -23
  298. package/dist/integrations/pi/engine/offline-settings-port.js +0 -54
  299. package/dist/native/darwin-arm64/manifest.json +0 -23
  300. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  301. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10500
  302. package/dist/native/darwin-arm64/terminal-host.manifest.json +0 -46
  303. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  304. package/dist/native/darwin-x64/manifest.json +0 -23
  305. package/dist/native/darwin-x64/process-guardian.node +0 -0
  306. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10737
  307. package/dist/native/darwin-x64/terminal-host.manifest.json +0 -46
  308. package/dist/native/darwin-x64/terminal-host.node +0 -0
  309. package/dist/native/linux-x64/manifest.json +0 -23
  310. package/dist/native/linux-x64/process-guardian.node +0 -0
  311. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -11226
  312. package/dist/native/linux-x64/terminal-host.manifest.json +0 -46
  313. package/dist/native/linux-x64/terminal-host.node +0 -0
  314. package/dist/native/win32-x64/manifest.json +0 -23
  315. package/dist/native/win32-x64/process-guardian.node +0 -0
  316. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10757
  317. package/dist/native/win32-x64/terminal-host.manifest.json +0 -46
  318. package/dist/native/win32-x64/terminal-host.node +0 -0
  319. package/docs/architecture/evidence/resident-tabs/linux-x64.md +0 -37
  320. package/docs/architecture/evidence/resident-tabs/macos-arm64.md +0 -36
  321. package/docs/architecture/evidence/resident-tabs/macos-x64.md +0 -35
  322. package/docs/architecture/evidence/resident-tabs/windows-x64.md +0 -36
  323. package/docs/architecture/evidence/terminal-host-spike/manual-one-pane-terminal-host.md +0 -70
  324. package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md +0 -32
  325. package/docs/architecture/evidence/terminal-host-spike/milestone-baseline.json +0 -115
  326. package/docs/architecture/evidence/terminal-host-spike/owned-ui-foundation-prerequisite.json +0 -48
  327. package/docs/architecture/evidence/terminal-host-spike/terminal-host-provenance.json +0 -125
  328. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-1.json +0 -56
  329. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-3.json +0 -174
  330. package/docs/architecture/evidence/terminal-host-spike/terminal-spike-acceptance-record.json +0 -18
  331. package/docs/architecture/native-fixture-isolation.md +0 -23
  332. package/docs/architecture/process-guardian-provenance.md +0 -36
  333. package/docs/architecture/resident-tabs-requirements.md +0 -18
  334. package/docs/architecture/resident-tabs.md +0 -668
  335. package/docs/architecture/terminal-host-proof-gate.md +0 -34
  336. package/docs/architecture/terminal-host-provenance.md +0 -81
  337. package/docs/architecture/terminal-host-spike-evidence.md +0 -39
  338. package/docs/launch-recovery.md +0 -23
  339. package/docs/manual-launch-instance-acceptance.md +0 -43
  340. package/docs/manual-resident-tabs-acceptance.md +0 -51
@@ -1,17 +1,11 @@
1
1
  import { spawn } from "node:child_process";
2
- import { RESIDENT_DETACH_INTERVAL_MS, restoreAfterOwnedExit } from "../terminal-cleanup/index.js";
3
- import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
- import { connect } from "node:net";
2
+ import { randomUUID } from "node:crypto";
3
+ import { deliverExitNotice, restoreAfterOwnedExit } from "../terminal-cleanup/index.js";
4
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
5
5
  import { resolve } from "node:path";
6
- import { selectCohortLaunch } from "./cohort-selection.js";
7
- import { recoverSupervisorEndpoint } from "./endpoint-recovery.js";
8
6
  import { selectOrdinaryLaunchReleaseId } from "./ordinary-launch-selection.js";
9
- import { releaseChannelOf } from "./latest-release.js";
10
7
  import { CohortStateStore } from "./cohort-state.js";
11
- import { assertLaunchProfileId, createSupervisorStartupAttempt, readSupervisorStartupResult, resolveCohortEndpoint, resolveProductPaths, sessionSelectionArguments } from "../lifecycle/index.js";
12
- import { encodeFrame, LineFrameDecoder } from "../protocol/index.js";
13
- import { cleanupProvenIdleOwner, processIsAlive } from "./process-cleanup.js";
14
- import { probeOwnership, readEndpointMetadata, removeEndpointArtifacts, sweepDeadEndpoints } from "./endpoints.js";
8
+ import { assertLaunchProfileId, resolveProductPaths, sessionSelectionArguments } from "../lifecycle/index.js";
15
9
  import { UpdateTransactionStore } from "./update-transaction.js";
16
10
  import { selectUpdateLaunchRelease } from "./update-launch.js";
17
11
  import { assertCertifiedFilesPresent, consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, repairMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease } from "./release-store.js";
@@ -19,25 +13,14 @@ import { scheduleReleaseCleanup } from "./release-gc.js";
19
13
  import { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument } from "./restart-certification.js";
20
14
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
21
15
  import { markStartupPhase } from "../startup/index.js";
22
- import { PRIVATE_ENVIRONMENT, isCurrentLaunchContract, launchContractTarget, readLaunchContext, withLaunchContext } from "../launch-context/index.js";
23
- import { assertResidentContinuity, ensureTerminalHost, readResidentTabLaunchSettings, selectResidentHost } from "../resident-tabs/index.js";
16
+ import { launchContractTarget, readLaunchContext, withLaunchContext } from "../launch-context/index.js";
24
17
  const RELEASE_RESELECTION_MESSAGE = "a1-release-reselection";
25
18
  export async function runBootstrap(options) {
26
- try {
27
- return await runBootstrapAttempt(options, 1);
28
- }
29
- catch (error) {
30
- if (!(error instanceof Error && "code" in error && typeof error.code === "string" && error.code.startsWith("SUPERVISOR_RECOVERY_")))
31
- throw error;
32
- // Rationale: actionable recovery failures are a single line, not an uncaught Node stack trace.
33
- (options.output ?? process.stderr).write(`${PRODUCT_TEXT.diagnostic(error.message)}\n`);
34
- return 1;
35
- }
19
+ return await runBootstrapAttempt(options, 1);
36
20
  }
37
21
  async function runBootstrapAttempt(options, retriesRemaining) {
38
22
  const launchProfileId = options.launchIntent?.profileId ?? "a1";
39
- const launchMode = options.launchIntent?.launchMode ?? (launchProfileId === "a1" ? "tabs" : "solo");
40
- const environment = withLaunchContext(options.environment ?? process.env, { launchProfile: launchProfileId, launchMode });
23
+ const environment = withLaunchContext(options.environment ?? process.env, { launchProfile: launchProfileId });
41
24
  await markStartupPhase(environment, "bootstrap-start");
42
25
  assertLaunchProfileId(launchProfileId);
43
26
  const sessionArgs = sessionSelectionArguments(options.launchIntent?.sessionSelection);
@@ -48,8 +31,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
48
31
  const paths = resolveProductPaths(environment);
49
32
  await mkdir(paths.runtimeDir, { recursive: true, mode: 0o700 });
50
33
  const launchSelected = async (release) => {
51
- const residentContext = await residentLaunchContext(options, release, environment);
52
- const result = await launchUi(release, environment, sessionArgs, residentContext);
34
+ const result = await launchUi(release, environment, sessionArgs, paths.runtimeDir);
53
35
  if (!result.releaseSuperseded)
54
36
  return result.code;
55
37
  if (retriesRemaining > 0)
@@ -68,41 +50,16 @@ async function runBootstrapAttempt(options, retriesRemaining) {
68
50
  if (!prior)
69
51
  return null;
70
52
  const retained = await readCertifiedReleaseManifest(prior, resolve(paths.dataDir, "releases"));
71
- await ensureSupervisor(retained, environment);
72
53
  return await launchSelected(retained);
73
54
  };
74
55
  const duringUpdate = await launchDuringUpdate();
75
56
  if (duringUpdate !== null)
76
57
  return duringUpdate;
77
- // Invariant: records left by cohorts whose processes are gone say nothing about ownership, and there
78
- // can now be several of them. Clearing them first keeps the decision below about what is
79
- // actually running.
80
- await sweepDeadEndpoints(paths).catch(() => []);
81
58
  // Performance: reconciliation is serialized by cohort state, but launch never awaits a physical
82
59
  // backlog. The scheduler commits a plan and starts a detached bounded worker.
83
60
  const scheduleMaintenance = options.scheduleMaintenance ?? scheduleReleaseCleanup;
84
61
  void scheduleMaintenance(paths.dataDir, paths).catch(() => { });
85
- // Protocol: each cohort keeps its own endpoint, so a launch looks for the endpoint of the release it
86
- // is about to run rather than for the one endpoint the runtime directory used to have.
87
- const activeReleaseId = state.references.active;
88
- let endpointPaths = activeReleaseId === null
89
- ? { endpoint: paths.endpoint, endpointMetadataPath: paths.endpointMetadataPath }
90
- : resolveCohortEndpoint(paths, activeReleaseId, environment);
91
- let endpoint = await readEndpointMetadata(endpointPaths.endpointMetadataPath);
92
- if (endpoint === null) {
93
- // Compatibility: a release that predates cohort-scoped endpoints published one endpoint for the whole
94
- // runtime directory. Recognizing it is what lets a session started by that release keep
95
- // working through the first launch that knows about cohorts.
96
- const legacy = await readEndpointMetadata(paths.endpointMetadataPath);
97
- if (legacy !== null) {
98
- endpoint = legacy;
99
- endpointPaths = { endpoint: paths.endpoint, endpointMetadataPath: paths.endpointMetadataPath };
100
- }
101
- }
102
- let probe = endpoint ? await probeOwnership(endpoint) : "dead";
103
62
  const installedVersion = await readInstalledVersion(options.packageRoot);
104
- if (releaseChannelOf(installedVersion) !== null)
105
- environment[PRIVATE_ENVIRONMENT.updateVersion] = installedVersion;
106
63
  const activeId = state.references.active;
107
64
  const active = activeId === null ? undefined : state.releases[activeId];
108
65
  let activeIsLaunchable = active === undefined || (options.releaseIsLaunchable?.(active.releaseRoot) ?? true);
@@ -123,16 +80,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
123
80
  }
124
81
  }
125
82
  if (activeIsLaunchable && active?.approval === "approved" && active.packageVersion === installedVersion) {
126
- const endpointMatches = endpoint?.releaseId === active.releaseId
127
- && endpoint.releaseRoot === active.releaseRoot
128
- && endpoint.contentDigest === active.contentDigest;
129
- if (endpointMatches && probe === "live-verified") {
130
- const retained = await readCertifiedReleaseManifest(active, resolve(paths.dataDir, "releases"));
131
- return await launchSelected(retained);
132
- }
133
- if (endpoint === null || probe === "dead") {
134
- if (endpoint)
135
- await removeEndpointArtifacts(endpointPaths.endpointMetadataPath, endpointPaths.endpoint, endpoint);
83
+ {
136
84
  await markStartupPhase(environment, "durable-validation-start");
137
85
  const retained = await readRestartCertifiedRelease(active, paths.dataDir).catch(async () => {
138
86
  let verified;
@@ -149,9 +97,6 @@ async function runBootstrapAttempt(options, retriesRemaining) {
149
97
  }).catch(() => null);
150
98
  if (retained !== null) {
151
99
  await markStartupPhase(environment, "durable-validation-complete");
152
- await markStartupPhase(environment, "replacement-supervisor-start");
153
- await ensureSupervisor(retained, environment);
154
- await markStartupPhase(environment, "replacement-supervisor-ready");
155
100
  return await launchSelected(retained);
156
101
  }
157
102
  activeIsLaunchable = false;
@@ -167,7 +112,7 @@ async function runBootstrapAttempt(options, retriesRemaining) {
167
112
  candidate = await materializeRelease(options.packageRoot, paths.dataDir);
168
113
  }
169
114
  catch (error) {
170
- const fallback = await launchRetainedActive(state, paths, environment, launchSelected, excludedRetainedReleaseId);
115
+ const fallback = await launchRetainedActive(state, paths, launchSelected, excludedRetainedReleaseId);
171
116
  if (fallback === null)
172
117
  throw error;
173
118
  return fallback;
@@ -185,7 +130,6 @@ async function runBootstrapAttempt(options, retriesRemaining) {
185
130
  if (!retainedRecord || retainedRecord.approval !== "approved")
186
131
  throw new Error("selected active release is not approved");
187
132
  const retained = await readCertifiedReleaseManifest(retainedRecord, resolve(paths.dataDir, "releases"));
188
- await ensureSupervisor(retained, environment);
189
133
  return await launchSelected(retained);
190
134
  }
191
135
  await stateStore.recordCandidate(candidate);
@@ -194,133 +138,15 @@ async function runBootstrapAttempt(options, retriesRemaining) {
194
138
  const diagnosticsPath = await certifyMaterializedRelease(candidate, paths.dataDir);
195
139
  await stateStore.approve(candidate.releaseId, diagnosticsPath);
196
140
  }
197
- try {
198
- await assertResidentContinuity({ releaseRoot: candidate.releaseRoot, dataDir: paths.dataDir, environment,
199
- profileRoot: environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir, runtimeDir: paths.runtimeDir });
200
- }
201
- catch (error) {
202
- const fallback = await launchRetainedActive(await stateStore.read(), paths, environment, launchSelected, excludedRetainedReleaseId);
203
- if (fallback !== null)
204
- return fallback;
205
- throw error;
206
- }
207
141
  state = await stateStore.activateForLaunch(candidate.releaseId, !activeIsLaunchable ? activeId ?? undefined : undefined);
208
142
  if (state.references.active !== candidate.releaseId) {
209
143
  const retainedRecord = state.references.active === null ? undefined : state.releases[state.references.active];
210
144
  if (!retainedRecord || retainedRecord.approval !== "approved")
211
145
  throw new Error("ordinary launch has no approved active release");
212
146
  const retained = await readCertifiedReleaseManifest(retainedRecord, resolve(paths.dataDir, "releases"));
213
- await ensureSupervisor(retained, environment);
214
147
  return await launchSelected(retained);
215
148
  }
216
- // Protocol: the candidate's own endpoint decides whether this launch attaches or starts a supervisor.
217
- // A cohort other than this one is not in the way: it listens somewhere else.
218
- endpointPaths = resolveCohortEndpoint(paths, candidate.releaseId, environment);
219
- endpoint = await readEndpointMetadata(endpointPaths.endpointMetadataPath);
220
- if (endpoint === null) {
221
- const legacy = await readEndpointMetadata(paths.endpointMetadataPath);
222
- if (legacy?.releaseId === candidate.releaseId) {
223
- endpoint = legacy;
224
- endpointPaths = { endpoint: paths.endpoint, endpointMetadataPath: paths.endpointMetadataPath };
225
- }
226
- }
227
- probe = endpoint ? await probeOwnership(endpoint) : "dead";
228
- let decision = selectCohortLaunch(candidate, state, endpoint, probe);
229
- if (decision.action === "blocked") {
230
- // Concurrency: reread through the common recovery path rather than fail on an old snapshot.
231
- await ensureSupervisor(candidate, environment);
232
- return await launchSelected(candidate);
233
- }
234
- if (endpoint && probe === "dead") {
235
- await removeEndpointArtifacts(endpointPaths.endpointMetadataPath, endpointPaths.endpoint, endpoint);
236
- }
237
- if (decision.action === "launch-retained-ui") {
238
- const retainedRecord = state.releases[decision.releaseId];
239
- const retained = retainedRecord?.approval === "approved"
240
- ? await readCertifiedReleaseManifest(retainedRecord, resolve(paths.dataDir, "releases"))
241
- : await verifyMaterializedRelease(decision.releaseRoot, undefined, resolve(paths.dataDir, "releases"));
242
- if (decision.recordPending && endpoint) {
243
- await stateStore.blockPending("candidate activation deferred by live non-resumable instances", endpoint.ownership.nonResumableInstanceIds);
244
- }
245
- const code = await launchSelected(retained);
246
- if (decision.recordPending)
247
- await activatePendingAfterBlockerExit(candidate, stateStore, paths, environment);
248
- return code;
249
- }
250
- if (decision.action === "replace-idle-cohort" && endpoint) {
251
- const diagnosticsPath = await certifyMaterializedRelease(candidate, paths.dataDir);
252
- await stateStore.approve(candidate.releaseId, diagnosticsPath);
253
- const released = await requestIdleOwnershipRelease(endpoint, candidate.releaseId, 2_000);
254
- if (!released) {
255
- await stateStore.blockPending("idle supervisor did not verify ownership release", endpoint.ownership.liveInstanceIds);
256
- output.write(`${PRODUCT_TEXT.diagnostic("could not verify idle cohort ownership release; the retained release remains selected.")}\n`);
257
- return 1;
258
- }
259
- if (!await releaseVerifiedIdleOwner(endpoint, paths.dataDir)) {
260
- await stateStore.blockPending("idle supervisor acknowledged release but did not terminate", endpoint.ownership.liveInstanceIds);
261
- output.write(`${PRODUCT_TEXT.diagnostic("could not complete bounded idle cohort shutdown; the candidate remains pending.")}\n`);
262
- return 1;
263
- }
264
- await removeEndpointArtifacts(endpointPaths.endpointMetadataPath, endpointPaths.endpoint, endpoint);
265
- await stateStore.activate(candidate.releaseId);
266
- decision = { action: "activate-candidate", releaseId: candidate.releaseId, releaseRoot: candidate.releaseRoot, reason: "idle cohort released ownership" };
267
- }
268
- let selected;
269
- if (decision.action === "activate-candidate") {
270
- const current = await stateStore.read();
271
- if (current.releases[candidate.releaseId]?.approval !== "approved") {
272
- const diagnosticsPath = await certifyMaterializedRelease(candidate, paths.dataDir);
273
- await stateStore.approve(candidate.releaseId, diagnosticsPath);
274
- }
275
- const activated = (await stateStore.read()).references.active === candidate.releaseId
276
- ? await stateStore.read()
277
- : await stateStore.activateForLaunch(candidate.releaseId);
278
- if (activated.references.active === candidate.releaseId)
279
- selected = candidate;
280
- else {
281
- const retainedRecord = activated.references.active === null ? undefined : activated.releases[activated.references.active];
282
- if (!retainedRecord || retainedRecord.approval !== "approved")
283
- throw new Error("ordinary launch has no approved active release");
284
- selected = await readCertifiedReleaseManifest(retainedRecord, resolve(paths.dataDir, "releases"));
285
- }
286
- }
287
- else {
288
- selected = await readMaterializedRelease(decision.releaseRoot);
289
- }
290
- await ensureSupervisor(selected, environment);
291
- return await launchSelected(selected);
292
- }
293
- /** Select only client routing; a resident reconnect never asks execution to restart. */
294
- async function residentLaunchContext(options, release, environment) {
295
- const paths = resolveProductPaths(environment);
296
- const profileId = options.launchIntent?.profileId ?? "a1";
297
- const mode = options.launchIntent?.launchMode ?? (profileId === "a1" ? "tabs" : "solo");
298
- const agentProfileDir = environment.PI_CODING_AGENT_DIR ?? paths.configDir;
299
- const residentLaunchSettings = profileId === "a1" ? readResidentTabLaunchSettings(agentProfileDir, PRODUCT_IDENTITY.commandName, paths.configDir, profileId) : null;
300
- const resident = await selectResidentHost({
301
- profileId, currentLaunchContract: isCurrentLaunchContract(release), solo: mode === "solo",
302
- interactive: options.interactiveTerminal ?? (process.stdin.isTTY === true && process.stdout.isTTY === true),
303
- ensureHost: async () => (options.ensureTerminalHost ?? ensureTerminalHost)({
304
- releaseRoot: release.releaseRoot, profileRoot: environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir,
305
- dataDir: paths.dataDir, runtimeDir: paths.runtimeDir, environment,
306
- maxConcurrentStarts: (await residentLaunchSettings).maxConcurrentStarts, unresponsiveRestartSeconds: (await residentLaunchSettings).unresponsiveRestartSeconds,
307
- }),
308
- });
309
- if (resident === null)
310
- return {};
311
- if (resident.status !== "ready")
312
- return { residentFallback: resident.reason };
313
- return {
314
- residentRootExecutable: resident.executable.executable,
315
- residentRootArguments: JSON.stringify([
316
- resident.executable.entry, "attach", "--workspace", "--endpoint", resident.endpoint,
317
- "--cwd", process.cwd(), "--clear-exit-ms", String(RESIDENT_DETACH_INTERVAL_MS),
318
- "--profile", environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir,
319
- "--settings-file", resolve(agentProfileDir, "settings.json"), "--accent-color", (await residentLaunchSettings).accentColor,
320
- "--data-dir", paths.dataDir, "--runtime-dir", paths.runtimeDir,
321
- "--release-root", release.releaseRoot, "--node-executable", process.execPath,
322
- ]),
323
- };
149
+ return await launchSelected(candidate);
324
150
  }
325
151
  /**
326
152
  * Start the retained active cohort when the installed payload cannot be taken. The
@@ -328,14 +154,13 @@ async function residentLaunchContext(options, release, environment) {
328
154
  * certifying anything new, and reports no selection when there is nothing safe
329
155
  * to fall back to.
330
156
  */
331
- async function launchRetainedActive(state, paths, environment, launch, excludedReleaseId) {
157
+ async function launchRetainedActive(state, paths, launch, excludedReleaseId) {
332
158
  const activeId = state.references.active;
333
159
  const active = activeId === null ? undefined : state.releases[activeId];
334
160
  if (!active || active.approval !== "approved" || active.releaseId === excludedReleaseId)
335
161
  return null;
336
162
  try {
337
163
  const retained = await readCertifiedReleaseManifest(active, resolve(paths.dataDir, "releases"));
338
- await ensureSupervisor(retained, environment);
339
164
  return await launch(retained);
340
165
  }
341
166
  catch {
@@ -376,50 +201,23 @@ export async function recordParentCertifiedRelease(release, dataDir) {
376
201
  await chmod(path, 0o400);
377
202
  return path;
378
203
  }
379
- /** Reuse a verified owner, or join the winner if another launch starts the same cohort first. */
380
- export async function ensureSupervisor(release, environment) {
381
- const paths = resolveCohortEndpoint(resolveProductPaths(environment), release.releaseId, environment);
382
- await recoverSupervisorEndpoint({
383
- metadataPath: paths.endpointMetadataPath,
384
- endpoint: paths.endpoint,
385
- matches: metadata => endpointMatchesRelease(metadata, release),
386
- startAndWait: async () => {
387
- const startup = await startSupervisor(release, environment);
388
- await waitForVerifiedEndpoint(paths.endpointMetadataPath, release, 8_000, startup);
389
- },
390
- });
391
- }
392
- function endpointMatchesRelease(metadata, release) {
393
- return metadata.releaseId === release.releaseId && metadata.releaseRoot === release.releaseRoot
394
- && metadata.contentDigest === release.contentDigest;
395
- }
396
- export async function startSupervisor(release, environment) {
397
- const entry = await resolveReleaseEntryPoint(release, "bin/supervisor.js");
398
- const paths = resolveProductPaths(environment);
399
- const attempt = await createSupervisorStartupAttempt(paths.runtimeDir, release.releaseId);
400
- const child = spawn(process.execPath, [entry, "--startup-attempt", attempt.attemptId], {
401
- detached: true,
402
- env: releaseEnvironment(environment, release),
403
- stdio: "ignore",
404
- windowsHide: true,
405
- });
406
- const childOutcome = new Promise(resolvePromise => {
407
- child.once("close", (exitCode, signal) => resolvePromise({ exitCode, signal }));
408
- });
409
- await new Promise((resolvePromise, rejectPromise) => {
410
- child.once("spawn", resolvePromise);
411
- child.once("error", rejectPromise);
412
- });
413
- child.unref();
414
- return { ...attempt, childOutcome };
415
- }
416
- async function launchUi(release, environment, sessionArgs, residentContext = {}) {
204
+ /**
205
+ * Run the selected release's UI as the one attached child. The launcher still owns the terminal,
206
+ * so when the child ends uncleanly it restores the terminal and prints any notice the UI left armed.
207
+ */
208
+ async function launchUi(release, environment, sessionArgs, runtimeDir) {
417
209
  await markStartupPhase(environment, "bootstrap-selected");
418
- const entry = await resolveReleaseEntryPoint(release, "bin/guardian.js");
210
+ const entry = await resolveReleaseEntryPoint(release, "bin/ui.js");
211
+ const ownsTerminal = readLaunchContext(environment).launchProfile === "a1";
212
+ const noticeDirectory = resolve(runtimeDir, "exit-notices");
213
+ const noticePath = resolve(noticeDirectory, `${randomUUID()}.notice`);
214
+ if (ownsTerminal)
215
+ await mkdir(noticeDirectory, { recursive: true, mode: 0o700 });
216
+ const launchEnvironment = releaseEnvironment(environment, release, readLaunchContext(environment, "profile").launchProfile);
217
+ if (ownsTerminal)
218
+ launchEnvironment[PRODUCT_IDENTITY.environment.exitNoticePath] = noticePath;
419
219
  return await new Promise((resolvePromise, rejectPromise) => {
420
220
  let releaseSuperseded = false;
421
- const releaseEnv = releaseEnvironment(environment, release, readLaunchContext(environment, "profile").launchProfile);
422
- const launchEnvironment = Object.keys(residentContext).length === 0 ? releaseEnv : withLaunchContext(releaseEnv, { ...readLaunchContext(releaseEnv), ...residentContext }, process.platform, launchContractTarget(release));
423
221
  const child = spawn(process.execPath, [entry, ...sessionArgs], {
424
222
  env: launchEnvironment,
425
223
  stdio: ["inherit", "inherit", "inherit", "ipc"],
@@ -431,12 +229,13 @@ async function launchUi(release, environment, sessionArgs, residentContext = {})
431
229
  }
432
230
  });
433
231
  child.once("error", rejectPromise);
434
- child.once("close", (code, signal) => resolvePromise({
435
- code: releaseSuperseded
436
- ? code ?? 1
437
- : restoreAfterOwnedExit(readLaunchContext(environment).launchProfile === "a1", code, signal),
438
- releaseSuperseded,
439
- }));
232
+ child.once("close", (code, signal) => {
233
+ const outcome = releaseSuperseded ? code ?? 1 : restoreAfterOwnedExit(ownsTerminal, code, signal);
234
+ // Invariant: the UI removes its notice on a clean exit; one still armed means the UI died.
235
+ if (ownsTerminal)
236
+ deliverExitNotice(noticePath, message => process.stderr.write(message));
237
+ resolvePromise({ code: outcome, releaseSuperseded });
238
+ });
440
239
  });
441
240
  }
442
241
  /**
@@ -445,121 +244,11 @@ async function launchUi(release, environment, sessionArgs, residentContext = {})
445
244
  * version instead of failing while an installation is being replaced.
446
245
  */
447
246
  export function releaseEnvironment(environment, release, profile) {
448
- const context = readLaunchContext(environment);
449
247
  return withLaunchContext(environment, {
450
248
  releaseId: release.releaseId,
451
249
  releaseLayers: (release.dependencyLayers ?? []).map(layer => layer.layerId).join(","),
452
250
  releaseRoot: release.releaseRoot,
453
251
  releaseDigest: release.contentDigest,
454
252
  ...(profile === undefined ? {} : { launchProfile: profile }),
455
- ...(context.launchMode === undefined ? {} : { launchMode: context.launchMode }),
456
- ...(context.updateVersion === undefined ? {} : { updateVersion: context.updateVersion }),
457
253
  }, process.platform, launchContractTarget(release));
458
254
  }
459
- export async function waitForVerifiedEndpoint(path, release, timeoutMs, startup) {
460
- const deadline = Date.now() + timeoutMs;
461
- let childOutcome = null;
462
- let collision = null;
463
- void startup?.childOutcome.then(outcome => { childOutcome = outcome; });
464
- while (Date.now() < deadline) {
465
- const metadata = await readEndpointMetadata(path);
466
- if (metadata && endpointMatchesRelease(metadata, release) && await probeOwnership(metadata, { attempts: 1 }) === "live-verified") {
467
- if (startup)
468
- await rm(startup.resultPath, { force: true });
469
- return;
470
- }
471
- if (startup) {
472
- const result = await readSupervisorStartupResult(startup.resultPath, startup.attemptId, startup.releaseId);
473
- if (result?.outcome === "failure") {
474
- const error = Object.assign(new Error(PRODUCT_TEXT.diagnostic(`supervisor startup failed at ${result.stage}: ${result.message}`)), {
475
- code: result.code ?? "SUPERVISOR_STARTUP_FAILED",
476
- });
477
- if (result.code !== "EADDRINUSE")
478
- throw error;
479
- // Concurrency: the winning process may not have published its authenticated metadata yet.
480
- collision = error;
481
- }
482
- }
483
- if (childOutcome && !collision)
484
- throw new Error(PRODUCT_TEXT.diagnostic(`supervisor exited before readiness: ${JSON.stringify(childOutcome)}`));
485
- await new Promise(resolvePromise => setTimeout(resolvePromise, 40));
486
- }
487
- if (collision)
488
- throw collision;
489
- throw new Error(PRODUCT_TEXT.diagnostic(`supervisor did not publish verified endpoint metadata within ${timeoutMs}ms`));
490
- }
491
- async function activatePendingAfterBlockerExit(candidate, stateStore, paths, environment) {
492
- const deadline = Date.now() + 2_000;
493
- let endpoint = null;
494
- while (Date.now() < deadline) {
495
- endpoint = await readEndpointMetadata(paths.endpointMetadataPath);
496
- if (!endpoint || endpoint.ownership.liveInstanceIds.length === 0)
497
- break;
498
- await new Promise(resolvePromise => setTimeout(resolvePromise, 40));
499
- }
500
- if (!endpoint || endpoint.ownership.liveInstanceIds.length > 0 || await probeOwnership(endpoint) !== "live-verified")
501
- return;
502
- const diagnosticsPath = await certifyMaterializedRelease(candidate, paths.dataDir);
503
- await stateStore.approve(candidate.releaseId, diagnosticsPath);
504
- await assertResidentContinuity({ releaseRoot: candidate.releaseRoot, dataDir: paths.dataDir, environment,
505
- profileRoot: environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir, runtimeDir: paths.runtimeDir });
506
- if (!await requestIdleOwnershipRelease(endpoint, candidate.releaseId, 2_000))
507
- return;
508
- if (!await releaseVerifiedIdleOwner(endpoint, paths.dataDir))
509
- return;
510
- await removeEndpointArtifacts(paths.endpointMetadataPath, paths.endpoint, endpoint);
511
- await stateStore.activate(candidate.releaseId);
512
- await ensureSupervisor(candidate, environment);
513
- }
514
- export async function releaseVerifiedIdleOwner(metadata, dataDir, operations = {}) {
515
- try {
516
- // Protocol: the owner rechecks its boot nonce and live instances before cooperative exit.
517
- // A durable idle snapshot alone never authorizes a signal to a still-live process.
518
- await requestIdleOwnershipRelease(metadata, metadata.releaseId, 2_000);
519
- await (operations.waitForExit ?? waitForProcessExit)(metadata.pid, 3_000);
520
- return true;
521
- }
522
- catch {
523
- // Security: a lingering Windows handle can keep numeric PID probes positive after exit.
524
- // Native observation may retire that exact dead boot, but never signals a still-live owner.
525
- const diagnostics = await (operations.cleanup ?? cleanupProvenIdleOwner)(metadata);
526
- await writeFile(resolve(dataDir, `cleanup-${Date.now()}.json`), JSON.stringify(diagnostics, null, 2));
527
- return diagnostics.terminated;
528
- }
529
- }
530
- async function requestIdleOwnershipRelease(metadata, candidateReleaseId, timeoutMs) {
531
- return await new Promise(resolvePromise => {
532
- const socket = connect(metadata.endpoint);
533
- const decoder = new LineFrameDecoder();
534
- let settled = false;
535
- const finish = (released) => {
536
- if (settled)
537
- return;
538
- settled = true;
539
- socket.destroy();
540
- resolvePromise(released);
541
- };
542
- socket.once("error", () => finish(false));
543
- socket.once("connect", () => socket.write(encodeFrame({ type: "release-idle-ownership", bootNonce: metadata.bootNonce, candidateReleaseId })));
544
- socket.on("data", chunk => {
545
- try {
546
- const message = decoder.push(chunk)[0];
547
- if (message?.type === "release-ownership-result")
548
- finish(message.released === true);
549
- }
550
- catch {
551
- finish(false);
552
- }
553
- });
554
- setTimeout(() => finish(false), timeoutMs).unref();
555
- });
556
- }
557
- export async function waitForProcessExit(pid, timeoutMs) {
558
- const deadline = Date.now() + timeoutMs;
559
- while (Date.now() < deadline) {
560
- if (!processIsAlive(pid))
561
- return;
562
- await new Promise(resolvePromise => setTimeout(resolvePromise, 25));
563
- }
564
- throw new Error(PRODUCT_TEXT.diagnostic(`supervisor ${pid} did not release process ownership within ${timeoutMs}ms`));
565
- }
@@ -1,5 +1,7 @@
1
1
  import type { MaterializedRelease } from "./release-store.js";
2
2
  export declare const RELEASE_COHORT_SCHEMA: string;
3
+ /** How long a release stays on disk after its approval, so processes started from it keep their files. */
4
+ export declare const RELEASE_RETENTION_MS: number;
3
5
  export type ReleaseApproval = "candidate" | "approved" | "rejected";
4
6
  export interface ReleaseRecord {
5
7
  readonly releaseId: string;
@@ -9,6 +11,7 @@ export interface ReleaseRecord {
9
11
  readonly launchContract?: string;
10
12
  readonly approval: ReleaseApproval;
11
13
  readonly materializedAt: string;
14
+ /** When the release was approved; retention measures its age from this instant. */
12
15
  readonly certifiedAt: string | null;
13
16
  readonly diagnosticsPath: string | null;
14
17
  }
@@ -19,12 +22,6 @@ export interface ReleaseReferences {
19
22
  readonly rollback: string | null;
20
23
  readonly retention: readonly string[];
21
24
  }
22
- export type ExternalReleaseHoldAuthority = "agent" | "migration" | "resident";
23
- /** A current hold supplied by an authority that must participate in every reconciliation. */
24
- export interface ExternalReleaseHold {
25
- readonly authority: ExternalReleaseHoldAuthority;
26
- readonly releaseId: string;
27
- }
28
25
  export interface ActiveReleaseTransactionReference {
29
26
  readonly status: "active" | "completed" | "rolled-back" | "failed";
30
27
  readonly priorActiveReleaseId: string | null;
@@ -75,43 +72,15 @@ export interface CohortState {
75
72
  readonly releases: Readonly<Record<string, ReleaseRecord>>;
76
73
  readonly references: ReleaseReferences;
77
74
  readonly cleanup: ReleaseCleanupState;
78
- readonly activation: {
79
- readonly state: "idle" | "pending" | "draining" | "blocked" | "failed";
80
- readonly reason: string | null;
81
- readonly blockerGenerationIds: readonly string[];
82
- readonly updatedAt: string;
83
- };
84
- }
85
- export interface EndpointOwnership {
86
- readonly state: "idle" | "busy" | "draining" | "released" | "blocked";
87
- readonly liveInstanceIds: readonly string[];
88
- readonly nonResumableInstanceIds: readonly string[];
89
- readonly uncertainInstanceIds: readonly string[];
90
- }
91
- export interface SupervisorEndpointMetadata {
92
- readonly schema: string;
93
- readonly supervisorId: string;
94
- readonly endpoint: string;
95
- readonly pid: number;
96
- readonly pidStartIdentity: string;
97
- /** Exact OS creation token; absent on historical supervisor metadata. */
98
- readonly nativeStartIdentity?: string;
99
- readonly bootNonce: string;
100
- readonly startedAt: string;
101
- readonly releaseId: string;
102
- readonly releaseRoot: string;
103
- readonly contentDigest: string;
104
- readonly ownership: EndpointOwnership;
105
- readonly envelope: string;
106
- readonly envelopeRevision: number;
107
- readonly requiredFeatures: readonly string[];
108
- readonly optionalFeatures: readonly string[];
109
- readonly contractDigest: string;
110
75
  }
111
76
  export interface ProtectedReleaseInputs {
112
- readonly liveReleaseIds?: readonly string[];
113
- readonly externalHolds?: readonly ExternalReleaseHold[];
77
+ /** Releases the reconciling command itself runs from; they are never collected under it. */
78
+ readonly ownReleaseIds?: readonly string[];
114
79
  readonly transaction?: ActiveReleaseTransactionReference | null;
80
+ /** Clock for the age window; defaults to the wall clock. */
81
+ readonly now?: number;
82
+ /** Age window measured from approval; defaults to seven days. */
83
+ readonly retentionMs?: number;
115
84
  }
116
85
  export interface ProtectedReleasePlan {
117
86
  readonly protectedReleaseIds: readonly string[];
@@ -129,10 +98,19 @@ export interface ReleaseRetentionReconciliation {
129
98
  readonly detachedReleaseIds: readonly string[];
130
99
  }
131
100
  /**
132
- * Derive protection exclusively from current selectors and ownership authorities.
133
- * The historical retention snapshot is deliberately not an input to this plan.
101
+ * Age-based retention. A release stays while a selector names it (active, its rollback, or a
102
+ * candidate still being certified), while an unfinished update may return to it, while the
103
+ * reconciling command runs from it, or while its approval is younger than the retention window.
104
+ * Nothing asks which processes are running: a session that outlives the window and two updates
105
+ * can lose its release files, which is the documented trade-off.
134
106
  */
135
107
  export declare function planProtectedReleases(state: CohortState, inputs?: ProtectedReleaseInputs): ProtectedReleasePlan;
108
+ /**
109
+ * Whether a release is young enough to keep. An approved release counts from its approval; a
110
+ * candidate still awaiting certification counts from its materialization so concurrent work is
111
+ * not collected under it. A record without a readable timestamp is treated as old.
112
+ */
113
+ export declare function withinRetention(release: ReleaseRecord, now: number, retentionMs?: number): boolean;
136
114
  /** Serializes validated cohort-state revisions under a lock and commits each update atomically. */
137
115
  export declare class CohortStateStore {
138
116
  readonly path: string;
@@ -144,9 +122,7 @@ export declare class CohortStateStore {
144
122
  activate(releaseId: string): Promise<CohortState>;
145
123
  /** Atomically advances ordinary launch without allowing an older installation to roll active state back. */
146
124
  activateForLaunch(releaseId: string, replaceUnlaunchableActiveId?: string): Promise<CohortState>;
147
- blockPending(reason: string, blockerGenerationIds: readonly string[]): Promise<CohortState>;
148
- rollback(ownershipReleased: boolean): Promise<CohortState>;
149
- setRetention(releaseIds: readonly string[]): Promise<CohortState>;
125
+ rollback(): Promise<CohortState>;
150
126
  /** Atomically replace legacy retention history and detach every currently collectible record. */
151
127
  reconcileRetention(resolveInputs: (current: CohortState) => ProtectedReleaseInputs | Promise<ProtectedReleaseInputs>, orphans?: readonly OrphanReleaseDisposition[]): Promise<ReleaseRetentionReconciliation>;
152
128
  markCleanupTrash(releaseId: string, trashPath: string): Promise<CohortState>;
@@ -156,6 +132,5 @@ export declare class CohortStateStore {
156
132
  recordCleanupWorkerStarted(runId: string, pid: number): Promise<CohortState>;
157
133
  recordCleanupWorkerProgress(runId: string, summary: ReleaseCleanupWorkerSummary): Promise<CohortState>;
158
134
  recordCleanupWorkerFinished(runId: string, status: Exclude<ReleaseCleanupWorkerStatus, "scheduled" | "running">, summary: ReleaseCleanupWorkerSummary, error?: unknown): Promise<CohortState>;
159
- removeUnreferencedRelease(releaseId: string, externalReferences: readonly string[]): Promise<CohortState>;
160
135
  }
161
136
  export declare function emptyState(): CohortState;