@timurproko/a1 0.2.7-dev.1245 → 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 (342) 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/compaction-progress.js +13 -5
  122. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -1
  123. package/dist/integrations/pi/engine/diagnostics-state.js +0 -14
  124. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +0 -1
  125. package/dist/integrations/pi/engine/prompt-suggestions.js +0 -40
  126. package/dist/integrations/pi/engine/session-runtime.js +0 -2
  127. package/dist/integrations/pi/engine/workflow-runner.js +1 -1
  128. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -2
  129. package/dist/integrations/pi/tui-runtime/adapter.js +2 -2
  130. package/dist/product-identity.d.ts +4 -6
  131. package/dist/product-identity.js +6 -11
  132. package/dist/product-identity.json +1 -40
  133. package/dist/runtime-payload-inventory.json +0 -3
  134. package/dist/ui/components/index.d.ts +2 -2
  135. package/dist/ui/components/index.js +1 -1
  136. package/dist/ui/components/submitted-prompt.d.ts +3 -13
  137. package/dist/ui/components/submitted-prompt.js +8 -41
  138. package/dist/ui/components/transcript-viewport.d.ts +1 -3
  139. package/dist/ui/components/transcript-viewport.js +1 -4
  140. package/dist/ui/settings/declarations.d.ts +3 -97
  141. package/dist/ui/settings/declarations.js +3 -78
  142. package/dist/ui/settings/migrations.js +18 -2
  143. package/docs/architecture/boundaries.md +22 -39
  144. package/docs/architecture/code-documentation.md +2 -2
  145. package/docs/architecture/internal-naming.md +9 -17
  146. package/docs/architecture/project-structure.md +30 -29
  147. package/docs/architecture/resource-and-data-policy.md +16 -31
  148. package/docs/architecture/toolchain.md +14 -19
  149. package/docs/architecture/ui-reference-provenance.md +2 -2
  150. package/docs/ci-release-runbook.md +6 -4
  151. package/docs/features/launch-profiles.md +11 -17
  152. package/docs/features/prompt-history.md +10 -11
  153. package/docs/local-worktree-cleanup.md +32 -1
  154. package/docs/manual-owned-ui-checkpoint.md +0 -1
  155. package/docs/openspec-archive-automation.md +11 -3
  156. package/docs/validation.md +5 -9
  157. package/docs/worktree-link-recovery.md +6 -6
  158. package/package.json +7 -19
  159. package/bin/guardian.js +0 -42
  160. package/bin/supervisor.js +0 -7
  161. package/bin/tab-launch-context.d.ts +0 -27
  162. package/bin/tab-launch-context.js +0 -94
  163. package/dist/app/session-shell/resident-tab-coordinator.d.ts +0 -38
  164. package/dist/app/session-shell/resident-tab-coordinator.js +0 -77
  165. package/dist/cli/tabs-request.d.ts +0 -7
  166. package/dist/cli/tabs-request.js +0 -1
  167. package/dist/cli/tabs.d.ts +0 -14
  168. package/dist/cli/tabs.js +0 -151
  169. package/dist/composition/resident-tab-bridge.d.ts +0 -41
  170. package/dist/composition/resident-tab-bridge.js +0 -209
  171. package/dist/composition/utility-tab.d.ts +0 -28
  172. package/dist/composition/utility-tab.js +0 -244
  173. package/dist/features/owned-ui/plus-menu-dialog.d.ts +0 -28
  174. package/dist/features/owned-ui/plus-menu-dialog.js +0 -89
  175. package/dist/features/resident-tab/auto-name.d.ts +0 -11
  176. package/dist/features/resident-tab/auto-name.js +0 -44
  177. package/dist/features/resident-tab/index.d.ts +0 -5
  178. package/dist/features/resident-tab/index.js +0 -7
  179. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +0 -8
  180. package/dist/features/resident-tab/shortcut-bindings-file.js +0 -40
  181. package/dist/features/resident-tab/shortcut-declarations.d.ts +0 -13
  182. package/dist/features/resident-tab/shortcut-declarations.js +0 -26
  183. package/dist/features/resident-tab/shortcut-projection.d.ts +0 -29
  184. package/dist/features/resident-tab/shortcut-projection.js +0 -64
  185. package/dist/features/resident-tab/status-deriver.d.ts +0 -10
  186. package/dist/features/resident-tab/status-deriver.js +0 -12
  187. package/dist/foundation/launch-guardian/exit-watcher.d.ts +0 -14
  188. package/dist/foundation/launch-guardian/exit-watcher.js +0 -103
  189. package/dist/foundation/launch-guardian/index.d.ts +0 -2
  190. package/dist/foundation/launch-guardian/index.js +0 -1
  191. package/dist/foundation/launch-guardian/main.d.ts +0 -39
  192. package/dist/foundation/launch-guardian/main.js +0 -232
  193. package/dist/foundation/lifecycle/commands.d.ts +0 -40
  194. package/dist/foundation/lifecycle/commands.js +0 -1
  195. package/dist/foundation/lifecycle/launch-instance.d.ts +0 -46
  196. package/dist/foundation/lifecycle/launch-instance.js +0 -134
  197. package/dist/foundation/lifecycle/native-claim-gate.d.ts +0 -5
  198. package/dist/foundation/lifecycle/native-claim-gate.js +0 -41
  199. package/dist/foundation/lifecycle/supervisor-startup.d.ts +0 -30
  200. package/dist/foundation/lifecycle/supervisor-startup.js +0 -106
  201. package/dist/foundation/process-containment/addon-containment.d.ts +0 -20
  202. package/dist/foundation/process-containment/addon-containment.js +0 -139
  203. package/dist/foundation/process-containment/addon-process-inspector.d.ts +0 -11
  204. package/dist/foundation/process-containment/addon-process-inspector.js +0 -36
  205. package/dist/foundation/process-containment/addon.d.ts +0 -47
  206. package/dist/foundation/process-containment/addon.js +0 -57
  207. package/dist/foundation/process-containment/artifact.d.ts +0 -1
  208. package/dist/foundation/process-containment/artifact.js +0 -35
  209. package/dist/foundation/process-containment/contracts.d.ts +0 -33
  210. package/dist/foundation/process-containment/contracts.js +0 -35
  211. package/dist/foundation/process-containment/index.d.ts +0 -9
  212. package/dist/foundation/process-containment/index.js +0 -7
  213. package/dist/foundation/process-containment/linux-process-inspector.d.ts +0 -7
  214. package/dist/foundation/process-containment/linux-process-inspector.js +0 -27
  215. package/dist/foundation/process-containment/native-process-inspector.d.ts +0 -4
  216. package/dist/foundation/process-containment/native-process-inspector.js +0 -21
  217. package/dist/foundation/protocol/client.d.ts +0 -18
  218. package/dist/foundation/protocol/client.js +0 -130
  219. package/dist/foundation/protocol/index.d.ts +0 -3
  220. package/dist/foundation/protocol/index.js +0 -2
  221. package/dist/foundation/protocol/messages.d.ts +0 -90
  222. package/dist/foundation/protocol/messages.js +0 -100
  223. package/dist/foundation/release/cohort-selection.d.ts +0 -33
  224. package/dist/foundation/release/cohort-selection.js +0 -67
  225. package/dist/foundation/release/endpoint-lock.d.ts +0 -2
  226. package/dist/foundation/release/endpoint-lock.js +0 -37
  227. package/dist/foundation/release/endpoint-recovery.d.ts +0 -10
  228. package/dist/foundation/release/endpoint-recovery.js +0 -45
  229. package/dist/foundation/release/endpoints.d.ts +0 -32
  230. package/dist/foundation/release/endpoints.js +0 -152
  231. package/dist/foundation/release/prepared-update-activation.d.ts +0 -3
  232. package/dist/foundation/release/prepared-update-activation.js +0 -27
  233. package/dist/foundation/release/process-cleanup.d.ts +0 -14
  234. package/dist/foundation/release/process-cleanup.js +0 -76
  235. package/dist/foundation/release/supervisor-identity.d.ts +0 -15
  236. package/dist/foundation/release/supervisor-identity.js +0 -63
  237. package/dist/foundation/resident-tabs/boot-identity.d.ts +0 -6
  238. package/dist/foundation/resident-tabs/boot-identity.js +0 -16
  239. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +0 -20
  240. package/dist/foundation/resident-tabs/bounded-host-command.js +0 -62
  241. package/dist/foundation/resident-tabs/bridge-client.d.ts +0 -73
  242. package/dist/foundation/resident-tabs/bridge-client.js +0 -327
  243. package/dist/foundation/resident-tabs/continuity.d.ts +0 -12
  244. package/dist/foundation/resident-tabs/continuity.js +0 -63
  245. package/dist/foundation/resident-tabs/doctor.d.ts +0 -28
  246. package/dist/foundation/resident-tabs/doctor.js +0 -166
  247. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +0 -27
  248. package/dist/foundation/resident-tabs/draft-checkpoint.js +0 -145
  249. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +0 -36
  250. package/dist/foundation/resident-tabs/ensure-terminal-host.js +0 -151
  251. package/dist/foundation/resident-tabs/fault-injection.d.ts +0 -8
  252. package/dist/foundation/resident-tabs/fault-injection.js +0 -26
  253. package/dist/foundation/resident-tabs/fixture-control.d.ts +0 -7
  254. package/dist/foundation/resident-tabs/fixture-control.js +0 -35
  255. package/dist/foundation/resident-tabs/host-entry.d.ts +0 -1
  256. package/dist/foundation/resident-tabs/host-entry.js +0 -17
  257. package/dist/foundation/resident-tabs/index.d.ts +0 -15
  258. package/dist/foundation/resident-tabs/index.js +0 -10
  259. package/dist/foundation/resident-tabs/maintenance.d.ts +0 -39
  260. package/dist/foundation/resident-tabs/maintenance.js +0 -75
  261. package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +0 -34
  262. package/dist/foundation/resident-tabs/progress-observation-schema.js +0 -1
  263. package/dist/foundation/resident-tabs/progress-observation.d.ts +0 -4
  264. package/dist/foundation/resident-tabs/progress-observation.js +0 -150
  265. package/dist/foundation/resident-tabs/prompt-journal.d.ts +0 -63
  266. package/dist/foundation/resident-tabs/prompt-journal.js +0 -213
  267. package/dist/foundation/resident-tabs/protocol.d.ts +0 -12
  268. package/dist/foundation/resident-tabs/protocol.js +0 -195
  269. package/dist/foundation/resident-tabs/recovery.d.ts +0 -26
  270. package/dist/foundation/resident-tabs/recovery.js +0 -205
  271. package/dist/foundation/resident-tabs/release-holds.d.ts +0 -28
  272. package/dist/foundation/resident-tabs/release-holds.js +0 -78
  273. package/dist/foundation/resident-tabs/routes.d.ts +0 -5
  274. package/dist/foundation/resident-tabs/routes.js +0 -5
  275. package/dist/foundation/resident-tabs/routing.d.ts +0 -11
  276. package/dist/foundation/resident-tabs/routing.js +0 -10
  277. package/dist/foundation/resident-tabs/runtime-control.d.ts +0 -38
  278. package/dist/foundation/resident-tabs/runtime-control.js +0 -74
  279. package/dist/foundation/resident-tabs/session-sync.d.ts +0 -14
  280. package/dist/foundation/resident-tabs/session-sync.js +0 -37
  281. package/dist/foundation/resident-tabs/settings.d.ts +0 -16
  282. package/dist/foundation/resident-tabs/settings.js +0 -60
  283. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +0 -14
  284. package/dist/foundation/resident-tabs/terminal-host-artifact.js +0 -78
  285. package/dist/foundation/resident-tabs/zip-archive.d.ts +0 -9
  286. package/dist/foundation/resident-tabs/zip-archive.js +0 -56
  287. package/dist/foundation/storage/control-store.d.ts +0 -20
  288. package/dist/foundation/storage/control-store.js +0 -331
  289. package/dist/foundation/storage/index.d.ts +0 -1
  290. package/dist/foundation/storage/index.js +0 -1
  291. package/dist/foundation/supervision/index.d.ts +0 -3
  292. package/dist/foundation/supervision/index.js +0 -3
  293. package/dist/foundation/supervision/main.d.ts +0 -1
  294. package/dist/foundation/supervision/main.js +0 -86
  295. package/dist/foundation/supervision/paths.d.ts +0 -1
  296. package/dist/foundation/supervision/paths.js +0 -1
  297. package/dist/foundation/supervision/server.d.ts +0 -46
  298. package/dist/foundation/supervision/server.js +0 -606
  299. package/dist/integrations/pi/engine/offline-settings-port.d.ts +0 -23
  300. package/dist/integrations/pi/engine/offline-settings-port.js +0 -54
  301. package/dist/native/darwin-arm64/manifest.json +0 -23
  302. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  303. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10500
  304. package/dist/native/darwin-arm64/terminal-host.manifest.json +0 -46
  305. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  306. package/dist/native/darwin-x64/manifest.json +0 -23
  307. package/dist/native/darwin-x64/process-guardian.node +0 -0
  308. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10737
  309. package/dist/native/darwin-x64/terminal-host.manifest.json +0 -46
  310. package/dist/native/darwin-x64/terminal-host.node +0 -0
  311. package/dist/native/linux-x64/manifest.json +0 -23
  312. package/dist/native/linux-x64/process-guardian.node +0 -0
  313. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -11226
  314. package/dist/native/linux-x64/terminal-host.manifest.json +0 -46
  315. package/dist/native/linux-x64/terminal-host.node +0 -0
  316. package/dist/native/win32-x64/manifest.json +0 -23
  317. package/dist/native/win32-x64/process-guardian.node +0 -0
  318. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +0 -10757
  319. package/dist/native/win32-x64/terminal-host.manifest.json +0 -46
  320. package/dist/native/win32-x64/terminal-host.node +0 -0
  321. package/docs/architecture/evidence/resident-tabs/linux-x64.md +0 -37
  322. package/docs/architecture/evidence/resident-tabs/macos-arm64.md +0 -36
  323. package/docs/architecture/evidence/resident-tabs/macos-x64.md +0 -35
  324. package/docs/architecture/evidence/resident-tabs/windows-x64.md +0 -36
  325. package/docs/architecture/evidence/terminal-host-spike/manual-one-pane-terminal-host.md +0 -70
  326. package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md +0 -32
  327. package/docs/architecture/evidence/terminal-host-spike/milestone-baseline.json +0 -115
  328. package/docs/architecture/evidence/terminal-host-spike/owned-ui-foundation-prerequisite.json +0 -48
  329. package/docs/architecture/evidence/terminal-host-spike/terminal-host-provenance.json +0 -125
  330. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-1.json +0 -56
  331. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-3.json +0 -174
  332. package/docs/architecture/evidence/terminal-host-spike/terminal-spike-acceptance-record.json +0 -18
  333. package/docs/architecture/native-fixture-isolation.md +0 -23
  334. package/docs/architecture/process-guardian-provenance.md +0 -36
  335. package/docs/architecture/resident-tabs-requirements.md +0 -18
  336. package/docs/architecture/resident-tabs.md +0 -668
  337. package/docs/architecture/terminal-host-proof-gate.md +0 -34
  338. package/docs/architecture/terminal-host-provenance.md +0 -81
  339. package/docs/architecture/terminal-host-spike-evidence.md +0 -39
  340. package/docs/launch-recovery.md +0 -23
  341. package/docs/manual-launch-instance-acceptance.md +0 -43
  342. package/docs/manual-resident-tabs-acceptance.md +0 -51
@@ -1,22 +1,18 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { access, lstat, readFile, realpath, rename } from "node:fs/promises";
3
- import { connect } from "node:net";
4
- import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
3
+ import { dirname, resolve } from "node:path";
5
4
  import crossSpawn from "cross-spawn";
6
5
  import { valid as validSemver } from "semver";
7
6
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
8
7
  import { earlyAccentPalette, earlyTerminalAppearance, earlyTerminalColorMode, resolveEarlyAccentColor } from "./early-accent.js";
9
8
  import { SHARED_PROGRESS_ACCENT_ANSI } from "./progress-palette.generated.js";
10
9
  import { createRegistryFetcher, invalidateReleaseCheckCache, isNewerRelease, queryPublishedVersions, releaseChannelOf, } from "./latest-release.js";
11
- import { certifyMaterializedRelease, ensureSupervisor, waitForProcessExit, } from "./bootstrap.js";
12
- import { probeOwnership, readEndpointMetadata, removeEndpointArtifacts } from "./endpoints.js";
13
- import { resolveCohortEndpoint, resolveProductPaths } from "../lifecycle/index.js";
14
- import { encodeFrame, LineFrameDecoder } from "../protocol/index.js";
10
+ import { certifyMaterializedRelease } from "./bootstrap.js";
11
+ import { resolveProductPaths } from "../lifecycle/index.js";
15
12
  import { CohortStateStore } from "./cohort-state.js";
16
- import { cleanupVerifiedOwner, processIsAlive } from "./process-cleanup.js";
17
13
  import { materializeRelease, readMaterializedRelease } from "./release-store.js";
18
14
  import { scheduleReleaseCleanup } from "./release-gc.js";
19
- import { UPDATE_ACTIVATION_CONTRACT, UpdateActivationBlockedError, activateInstalledRelease, delegateActivation, readActivationContracts, } from "./update-activation.js";
15
+ import { UPDATE_ACTIVATION_CONTRACT, activateInstalledRelease, delegateActivation, readActivationContracts, } from "./update-activation.js";
20
16
  import { UpdateTransactionStore } from "./update-transaction.js";
21
17
  import { acquireUpdateExecutionLease, reclaimDeadLease } from "./update-execution-lease.js";
22
18
  import { npmPrefixForGlobalRoot, removeUpdateRecoveryCapsule, resolveNpmCli, runProtectedPackageReplacement, updateLauncherPaths, updateNpmInstallArguments, } from "./update-recovery.js";
@@ -37,7 +33,6 @@ const defaultFileSystem = {
37
33
  */
38
34
  export const PACKAGE_UNLOCK_PATIENCE_MS = 15_000;
39
35
  export const UPDATE_ALREADY_RUNNING_EXIT_CODE = 75;
40
- export const UPDATE_ACTIVATION_BLOCKED_EXIT_CODE = 76;
41
36
  /** A named preview reads the registry's whole package document, which is larger than a dist-tag lookup. */
42
37
  const PREVIEW_LOOKUP_TIMEOUT_MS = 30_000;
43
38
  const PACKAGE_UNLOCK_INITIAL_DELAY_MS = 100;
@@ -53,7 +48,7 @@ export function isPackageLockError(error) {
53
48
  /** What the user reads when the package stayed held for the whole patience window. */
54
49
  export function lockedPackageDiagnostic(packageRoot, waitedMs, attempts, error) {
55
50
  const seconds = Math.max(1, Math.round(waitedMs / 1_000));
56
- return PRODUCT_TEXT.diagnostic(`package remained locked for ${seconds}s after verified shutdown (${attempts} checks): ${errorMessage(error)}. `
51
+ return PRODUCT_TEXT.diagnostic(`package remained locked for ${seconds}s (${attempts} checks): ${errorMessage(error)}. `
57
52
  + `Another program is holding a file under ${packageRoot}: an antivirus scan, a file browser or terminal open inside it, `
58
53
  + `or a session still shutting down. Close it and run the update again; nothing was changed.`);
59
54
  }
@@ -87,103 +82,14 @@ export function createNpmProcessRunner(platform = process.platform) {
87
82
  } });
88
83
  });
89
84
  }
90
- /**
91
- * What an update does about the owner it found. A cohort running from retained immutable
92
- * content keeps its work: the installation replaces files it does not read, and it listens on
93
- * its own endpoint. A cohort running from the mutable installation is the one exception,
94
- * because that installation is exactly what is about to be replaced under it.
95
- */
96
- async function verifyAutomaticUpdateSafety(paths, stateStore, environment) {
97
- const state = await stateStore.read();
98
- const owner = await readActiveEndpoint(paths, state.references.active, environment);
99
- if (!owner)
100
- return;
101
- const ownership = await probeOwnership(owner.metadata);
102
- if (ownership === "dead")
103
- return;
104
- if (ownership !== "live-verified") {
105
- throw new UpdateAutomaticDeferred(PRODUCT_TEXT.diagnostic(`automatic update deferred because supervisor ownership is ${ownership}`));
106
- }
107
- if (!await canonicalImmutableRoot(paths.dataDir, owner.metadata.releaseRoot)) {
108
- throw new UpdateAutomaticDeferred(PRODUCT_TEXT.diagnostic("automatic update deferred because the running session is not using a retained immutable release; run the update command manually"));
109
- }
110
- }
111
- export function planUpdateOwnership(ownership, runsFromRetainedRelease) {
112
- if (ownership === "dead")
113
- return "clean-dead-record";
114
- return runsFromRetainedRelease ? "leave-running" : "end-session";
115
- }
116
- /**
117
- * The endpoint of the active cohort, together with where it is recorded, so a caller that
118
- * finds it can also clean it up. A release that predates cohort-scoped endpoints published
119
- * one endpoint for the runtime directory, and that one is recognized too.
120
- */
121
- async function readActiveEndpoint(paths, activeReleaseId, environment) {
122
- if (activeReleaseId !== null) {
123
- const cohort = resolveCohortEndpoint(paths, activeReleaseId, environment);
124
- const metadata = await readEndpointMetadata(cohort.endpointMetadataPath);
125
- if (metadata)
126
- return { metadata, paths: cohort };
127
- }
128
- const legacyPaths = { endpoint: paths.endpoint, endpointMetadataPath: paths.endpointMetadataPath };
129
- const legacy = await readEndpointMetadata(legacyPaths.endpointMetadataPath);
130
- return legacy ? { metadata: legacy, paths: legacyPaths } : null;
131
- }
132
- export function createUpdateLifecycleCoordinator(environment = process.env, fileSystem = defaultFileSystem, output = defaultOutput, patience = {}) {
85
+ export function createUpdateLifecycleCoordinator(environment = process.env, fileSystem = defaultFileSystem, _output = defaultOutput, patience = {}) {
133
86
  const paths = resolveProductPaths(environment);
134
87
  const stateStore = new CohortStateStore(paths.dataDir);
135
88
  return {
136
89
  async targetIsActive(targetVersion) {
137
90
  const state = await stateStore.read(), activeId = state.references.active;
138
- if (!activeId || state.releases[activeId]?.packageVersion !== targetVersion)
139
- return false;
140
- const endpoint = await readActiveEndpoint(paths, activeId, environment);
141
- return endpoint?.metadata.releaseId === activeId && await probeOwnership(endpoint.metadata) === "live-verified";
142
- },
143
- async verifyAutomaticSafety() { await verifyAutomaticUpdateSafety(paths, stateStore, environment); },
144
- async shutdownVerifiedOwners(targetVersion) {
145
- const state = await stateStore.read();
146
- const activeId = state.references.active;
147
- const priorActiveVersion = activeId ? state.releases[activeId]?.packageVersion ?? null : null;
148
- const owner = await readActiveEndpoint(paths, activeId, environment);
149
- if (!owner)
150
- return { priorActiveVersion };
151
- const endpoint = owner.metadata;
152
- const ownership = await probeOwnership(endpoint);
153
- if (ownership !== "live-verified" && ownership !== "dead") {
154
- throw new Error(PRODUCT_TEXT.diagnostic(`refused update shutdown because supervisor ownership is ${ownership}`));
155
- }
156
- const plan = planUpdateOwnership(ownership, await canonicalImmutableRoot(paths.dataDir, endpoint.releaseRoot));
157
- if (plan === "clean-dead-record") {
158
- await removeEndpointArtifacts(owner.paths.endpointMetadataPath, owner.paths.endpoint, endpoint);
159
- return { priorActiveVersion };
160
- }
161
- // Rationale: leaving sessions alone is the expected outcome, and saying so mid-update tears
162
- // the progress bar; only ending a session (below) is worth interrupting it for.
163
- if (plan === "leave-running")
164
- return { priorActiveVersion };
165
- // Rationale: say whose work is ending before it ends.
166
- const live = endpoint.ownership.liveInstanceIds.length;
167
- if (live > 0) {
168
- output.stderr(`${PRODUCT_TEXT.diagnostic(`ending ${live === 1 ? "one session" : `${live} sessions`} that run from the installation being replaced; a session started by an installed release would have been left alone.`)}\n`);
169
- }
170
- const identity = await requestUpdateShutdown(endpoint, targetVersion, 2_000);
171
- if (!identity.accepted && processIsAlive(endpoint.pid)) {
172
- // Security: explicit update consent permits bounded cleanup of an authenticated
173
- // older owner that predates the update-shutdown message.
174
- const cleanup = await cleanupVerifiedOwner(endpoint, { allowLiveInstances: true, reason: "legacy-mutable-install" });
175
- if (!cleanup.terminated)
176
- throw new Error(`verified ${PRODUCT_TEXT.displayName} owner ${endpoint.pid} rejected shutdown and could not be terminated: ${identity.reason}`);
177
- }
178
- else if (identity.accepted) {
179
- await waitForProcessExit(endpoint.pid, 3_000).catch(async () => {
180
- const cleanup = await cleanupVerifiedOwner(endpoint, { allowLiveInstances: true, reason: "legacy-mutable-install" });
181
- if (!cleanup.terminated)
182
- throw new Error(`verified ${PRODUCT_TEXT.displayName} owner ${endpoint.pid} did not terminate`);
183
- });
184
- }
185
- await removeEndpointArtifacts(owner.paths.endpointMetadataPath, owner.paths.endpoint, endpoint);
186
- return { priorActiveVersion };
91
+ const active = activeId === null ? undefined : state.releases[activeId];
92
+ return active?.approval === "approved" && active.packageVersion === targetVersion;
187
93
  },
188
94
  async verifyPackageUnlocked(packageRoot) {
189
95
  if (fileSystem.access)
@@ -196,7 +102,7 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
196
102
  const startedAt = now();
197
103
  let delay = PACKAGE_UNLOCK_INITIAL_DELAY_MS;
198
104
  let attempts = 0;
199
- // Rationale: a holder found right after ownership was released is almost always transient,
105
+ // Rationale: a holder found before the replacement is almost always transient,
200
106
  // so one failed rename is a reason to look again, not to abandon an update that has not
201
107
  // yet changed anything. The wait is bounded so a genuine lock still fails clearly.
202
108
  const withPatience = async (from, to, describeFailure) => {
@@ -236,13 +142,8 @@ export function createUpdateLifecycleCoordinator(environment = process.env, file
236
142
  else
237
143
  await activateInstalledRelease(request, callbacks);
238
144
  },
239
- async activatePrepared(targetVersion, phase) { await retryPreparedActivation(paths.dataDir, environment, targetVersion, phase); },
240
145
  };
241
146
  }
242
- async function retryPreparedActivation(dataDir, environment, targetVersion, phase) {
243
- await (await import("./prepared-update-activation.js"))
244
- .activatePreparedRelease({ dataDir, targetVersion, environment }, { phase });
245
- }
246
147
  const PROGRESS_BAR_WIDTH = 38;
247
148
  const PROGRESS_TICK_MS = 200;
248
149
  /**
@@ -513,10 +414,10 @@ async function withUpdateExecutionLease(input, options, output, operation) {
513
414
  let lease = await acquireUpdateExecutionLease(input);
514
415
  if (lease.status === "interrupted") {
515
416
  // Rationale: the owner that targeted another version is gone. Its durable transaction is rolled
516
- // back to the cohort that ran before it, so this request proceeds as an ordinary update of its
417
+ // back to the release that ran before it, so this request proceeds as an ordinary update of its
517
418
  // own target instead of reporting a dead attempt as in progress.
518
419
  const interrupted = lease.owner.targetVersion;
519
- const rollback = await rollbackPriorCohort(input.dataDir, options.environment ?? process.env, lease.transaction.priorActiveReleaseId)
420
+ const rollback = await rollbackPriorRelease(input.dataDir, lease.transaction.priorActiveReleaseId)
520
421
  .catch(error => `rollback failed: ${errorMessage(error)}`);
521
422
  const store = new UpdateTransactionStore(input.dataDir);
522
423
  await store.finish("rolled-back", `interrupted ${interrupted} update settled by ${input.targetVersion}; ${rollback}`);
@@ -610,8 +511,6 @@ export async function runSelfUpdate(options) {
610
511
  let transaction = await transactionStore.read();
611
512
  let npmCli;
612
513
  try {
613
- if (mode === "automatic")
614
- await lifecycle.verifyAutomaticSafety?.();
615
514
  if ((!transaction || transaction.status === "completed") && await lifecycle.targetIsActive(targetVersion)) {
616
515
  await maintenance();
617
516
  await transactionStore.clearCompleted();
@@ -650,10 +549,7 @@ export async function runSelfUpdate(options) {
650
549
  priorActiveReleaseId,
651
550
  });
652
551
  if (phaseBefore(transaction.phase, "ownership-released")) {
653
- await measure("ownership-release", async () => {
654
- await lifecycle.shutdownVerifiedOwners(targetVersion);
655
- await lifecycle.verifyPackageUnlocked(packageRoot);
656
- });
552
+ await measure("ownership-release", async () => { await lifecycle.verifyPackageUnlocked(packageRoot); });
657
553
  transaction = await transactionStore.advance("ownership-released");
658
554
  }
659
555
  progress.set(15, 70);
@@ -711,12 +607,7 @@ export async function runSelfUpdate(options) {
711
607
  transaction = await transactionStore.advance("package-installed");
712
608
  }
713
609
  }
714
- progress.set(70, 75);
715
- // Concurrency: ownership can be reacquired after an interrupted installation (for
716
- // example, if bare A1 is launched before the update is resumed). Recheck
717
- // immediately before activation so recovery cannot start a second cohort.
718
- await measure("ownership-release", async () => { await lifecycle.shutdownVerifiedOwners(targetVersion); });
719
- progress.set(75, MATERIALIZE_PROGRESS.from);
610
+ progress.set(70, MATERIALIZE_PROGRESS.from);
720
611
  let activationPhaseStartedAt = now();
721
612
  const recordActivationPhase = async (phase) => {
722
613
  options.onPhaseTiming?.({ phase, durationMs: Math.max(0, now() - activationPhaseStartedAt) });
@@ -724,7 +615,7 @@ export async function runSelfUpdate(options) {
724
615
  progress.set(ACTIVATION_PROGRESS[phase].at, ACTIVATION_PROGRESS[phase].creepTo);
725
616
  activationPhaseStartedAt = now();
726
617
  };
727
- await activateUpdateTransaction(lifecycle, transaction, packageRoot, targetVersion, recordActivationPhase, ({ completed, total }) => progress.set(MATERIALIZE_PROGRESS.from
618
+ await lifecycle.activateInstalled(packageRoot, targetVersion, recordActivationPhase, ({ completed, total }) => progress.set(MATERIALIZE_PROGRESS.from
728
619
  + (MATERIALIZE_PROGRESS.to - MATERIALIZE_PROGRESS.from) * (total > 0 ? Math.min(1, completed / total) : 0)), state => {
729
620
  if (state === "started") {
730
621
  activationPhaseStartedAt = now();
@@ -736,9 +627,7 @@ export async function runSelfUpdate(options) {
736
627
  progress.set(99);
737
628
  }
738
629
  });
739
- options.onPhaseTiming?.({ phase: "supervisor-verified", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
740
630
  const transactionStartedAt = now();
741
- await transactionStore.advance("supervisor-verified");
742
631
  if (transaction.recovery?.capsulePath)
743
632
  await removeUpdateRecoveryCapsule(paths.dataDir, transaction.transactionId);
744
633
  // Invariant: successful output follows the durable cleanup disposition. Slow recursive
@@ -755,18 +644,13 @@ export async function runSelfUpdate(options) {
755
644
  catch (error) {
756
645
  progress.clear();
757
646
  const message = errorMessage(error);
758
- if (error instanceof UpdateAutomaticDeferred)
759
- return 1;
760
- const blockedExit = await preparedActivationExit(error, transaction, mode, output, transactionStore, targetVersion, paths.configDir);
761
- if (blockedExit !== null)
762
- return blockedExit;
763
647
  if (error instanceof UpdatePreflightFailure) {
764
648
  output.stderr(`${PRODUCT_TEXT.diagnostic(`update could not start safely: ${message}. Nothing was changed. Retry with npm x -y -- ${PRODUCT_PACKAGE}-install`)}\n`);
765
649
  return 1;
766
650
  }
767
651
  const rollback = options.lifecycle
768
652
  ? "previous test lifecycle retained"
769
- : await rollbackPriorCohort(paths.dataDir, environment, transaction?.priorActiveReleaseId ?? null).catch(rollbackError => `rollback failed: ${errorMessage(rollbackError)}`);
653
+ : await rollbackPriorRelease(paths.dataDir, transaction?.priorActiveReleaseId ?? null).catch(rollbackError => `rollback failed: ${errorMessage(rollbackError)}`);
770
654
  if (transaction)
771
655
  await transactionStore.finish(rollback === "rolled back" ? "rolled-back" : "failed", `${message}; ${rollback}`);
772
656
  output.stderr(updateFailureLine(message, rollback, transactionStore.path));
@@ -790,35 +674,6 @@ export function assertUpdatePerformanceBudget(evidence, maximumPostNpmDurationMs
790
674
  if (failures.length > 0)
791
675
  throw new Error(`update performance budget failed: ${failures.join("; ")}`);
792
676
  }
793
- async function requestUpdateShutdown(metadata, targetVersion, timeoutMs) {
794
- if (!processIsAlive(metadata.pid))
795
- return { accepted: false, reason: "recorded owner is dead" };
796
- return await new Promise(resolvePromise => {
797
- const socket = connect(metadata.endpoint);
798
- const decoder = new LineFrameDecoder();
799
- let settled = false;
800
- const finish = (value) => {
801
- if (settled)
802
- return;
803
- settled = true;
804
- socket.destroy();
805
- resolvePromise(value);
806
- };
807
- socket.once("error", () => finish({ accepted: false, reason: "verified endpoint did not respond" }));
808
- socket.once("connect", () => socket.write(encodeFrame({ type: "release-update-ownership", bootNonce: metadata.bootNonce, targetVersion })));
809
- socket.on("data", chunk => {
810
- try {
811
- const message = decoder.push(chunk)[0];
812
- if (message?.type === "release-update-result")
813
- finish({ accepted: message.accepted === true, reason: String(message.reason ?? "shutdown rejected") });
814
- }
815
- catch {
816
- finish({ accepted: false, reason: "invalid shutdown response" });
817
- }
818
- });
819
- setTimeout(() => finish({ accepted: false, reason: "verified endpoint shutdown timed out" }), timeoutMs).unref();
820
- });
821
- }
822
677
  async function runNpm(runner, arguments_, captureStdout, output, action, reportNonzero = true) {
823
678
  let result;
824
679
  try {
@@ -843,51 +698,31 @@ function reportChildDiagnostics(output, result) {
843
698
  output.stderr(`${text}\n`);
844
699
  return true;
845
700
  }
846
- async function rollbackPriorCohort(dataDir, environment, priorReleaseId) {
701
+ async function rollbackPriorRelease(dataDir, priorReleaseId) {
847
702
  if (!priorReleaseId)
848
- return "no prior cohort was available for rollback";
703
+ return "no prior release was available for rollback";
849
704
  const stateStore = new CohortStateStore(dataDir);
850
705
  const state = await stateStore.read();
851
706
  const prior = state.releases[priorReleaseId];
852
707
  if (!prior || prior.approval !== "approved")
853
- return "prior cohort is not a verified rollback candidate";
708
+ return "prior release is not a verified rollback candidate";
854
709
  if (state.references.active !== priorReleaseId) {
855
710
  if (state.references.rollback === priorReleaseId)
856
- await stateStore.rollback(true);
711
+ await stateStore.rollback();
857
712
  else
858
- throw new Error(`prior release ${priorReleaseId} is not the recorded rollback cohort`);
713
+ throw new Error(`prior release ${priorReleaseId} is not the recorded rollback release`);
859
714
  }
860
- const release = await readMaterializedRelease(prior.releaseRoot);
861
- // Invariant: rollback reuses a surviving prior cohort rather than racing its occupied endpoint.
862
- await ensureSupervisor(release, environment);
715
+ // Invariant: rollback selects only a prior release whose retained content still verifies.
716
+ await readMaterializedRelease(prior.releaseRoot);
863
717
  return "rolled back";
864
718
  }
865
- async function activateUpdateTransaction(lifecycle, transaction, packageRoot, targetVersion, phase, materializing, warmup) {
866
- if (!phaseBefore(transaction.phase, "certified") && phaseBefore(transaction.phase, "active-reference-committed")
867
- && lifecycle.activatePrepared !== undefined)
868
- await lifecycle.activatePrepared(targetVersion, phase);
869
- else
870
- await lifecycle.activateInstalled(packageRoot, targetVersion, phase, materializing, warmup);
871
- }
872
- async function preparedActivationExit(error, transaction, mode, output, journal, targetVersion, configDir) {
873
- if (!(error instanceof UpdateActivationBlockedError) || transaction === null || phaseBefore(transaction.phase, "certified"))
874
- return null;
875
- await journal.recordActiveError?.(error.message);
876
- if (mode === "automatic")
877
- return UPDATE_ACTIVATION_BLOCKED_EXIT_CODE;
878
- // Invariant: a certified candidate waiting only for resident continuity is an installed update;
879
- // the next startup check retries activation without another installation.
880
- await invalidateReleaseCheckCache(configDir).catch(() => undefined);
881
- output.stdout(`${PRODUCT_TEXT.commandName} updated to ${targetVersion}; it takes over automatically once running Agents can switch\n`);
882
- return 0;
883
- }
884
719
  /** One reason and one next step; routine rollback detail stays in the journal. */
885
720
  function updateFailureLine(message, rollback, journalPath) {
886
721
  const note = rollback.startsWith("rollback failed") ? ` (${rollback})` : "";
887
722
  return `${PRODUCT_TEXT.diagnostic(`update failed: ${message}${note}. Run ${PRODUCT_TEXT.commandName} update to retry. Details: ${journalPath}`)}\n`;
888
723
  }
889
724
  function phaseBefore(current, target) {
890
- const phases = ["shutdown-intent", "ownership-released", "package-installed", "materialized", "certified", "active-reference-committed", "supervisor-verified"];
725
+ const phases = ["shutdown-intent", "ownership-released", "package-installed", "materialized", "certified", "active-reference-committed"];
891
726
  return phases.indexOf(current) < phases.indexOf(target);
892
727
  }
893
728
  class UpdateFailure extends Error {
@@ -896,18 +731,6 @@ class UpdateFailure extends Error {
896
731
  }
897
732
  class UpdatePreflightFailure extends Error {
898
733
  }
899
- class UpdateAutomaticDeferred extends Error {
900
- }
901
- async function canonicalImmutableRoot(dataDir, releaseRoot) {
902
- try {
903
- const [store, selected] = await Promise.all([realpath(resolve(dataDir, "releases")), realpath(releaseRoot)]);
904
- const fromStore = relative(store, selected);
905
- return fromStore.length > 0 && fromStore !== ".." && !fromStore.startsWith(`..${sep}`) && !isAbsolute(fromStore);
906
- }
907
- catch {
908
- return false;
909
- }
910
- }
911
734
  function samePath(left, right) {
912
735
  return process.platform === "win32" ? resolve(left).toLowerCase() === resolve(right).toLowerCase() : resolve(left) === resolve(right);
913
736
  }
@@ -1,11 +1,11 @@
1
- import { existsSync } from "node:fs";
2
- export interface NativeLock {
1
+ /** One held authority; `bindCreated` moves a reservation onto the file it created. */
2
+ export interface WriterLock {
3
3
  readonly bound: boolean;
4
4
  bindCreated(path: string): void;
5
5
  release(): void;
6
6
  }
7
7
  export interface LockBackend {
8
- acquire(path: string): NativeLock;
8
+ acquire(path: string): WriterLock;
9
9
  }
10
10
  /** Reports that a canonical session identity already has a live writer. */
11
11
  export declare class SessionWriterConflict extends Error {
@@ -19,12 +19,20 @@ export interface SessionWriterHandle {
19
19
  bind(createdPath?: string): void;
20
20
  release(): void;
21
21
  }
22
- /** Security: a release that carries its own terminal-host addon never loads one chosen by cwd. */
23
- export declare function nativeCandidates(moduleUrl?: string, env?: NodeJS.ProcessEnv, cwd?: string, exists?: typeof existsSync): string[];
22
+ /**
23
+ * A crashed writer is not released by the kernel; its lock becomes stale once its holder stops
24
+ * refreshing it. The bound keeps resume after a crash a matter of seconds.
25
+ */
26
+ export declare const SESSION_WRITER_STALE_MS = 10000;
27
+ /**
28
+ * Locks live in one per-user directory keyed by canonical identity, not beside the session, so two
29
+ * spellings of one file (case, short names, links, hard links) contend for the same lock.
30
+ */
31
+ export declare function lockfileBackend(root?: string, staleMs?: number): LockBackend;
24
32
  export declare function acquireExisting(path: string): SessionWriterHandle;
25
33
  export declare function reserveNew(parent: string, name: string): SessionWriterHandle;
26
34
  export declare function acquireSessionWriter(path: string): SessionWriterHandle;
27
35
  export declare function bind(handle: SessionWriterHandle, createdPath?: string): void;
28
36
  export declare function release(handle: SessionWriterHandle): void;
29
- /** Test-only backend seam; passing undefined restores native discovery. */
37
+ /** Test-only backend seam; passing undefined restores the default backend. */
30
38
  export declare function setSessionWriterLockBackendForTest(value: LockBackend | undefined): void;
@@ -1,10 +1,8 @@
1
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { createRequire } from "node:module";
1
+ import { existsSync, mkdirSync, realpathSync, statSync } from "node:fs";
3
2
  import { tmpdir } from "node:os";
4
3
  import { basename, dirname, join, resolve } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
4
  import { createHash } from "node:crypto";
7
- import { PRODUCT_IDENTITY } from "../../product-identity.js";
5
+ import lockfile from "proper-lockfile";
8
6
  /** Reports that a canonical session identity already has a live writer. */
9
7
  export class SessionWriterConflict extends Error {
10
8
  path;
@@ -16,107 +14,60 @@ export class SessionWriterConflict extends Error {
16
14
  this.holderKind = holderKind;
17
15
  }
18
16
  }
17
+ /**
18
+ * A crashed writer is not released by the kernel; its lock becomes stale once its holder stops
19
+ * refreshing it. The bound keeps resume after a crash a matter of seconds.
20
+ */
21
+ export const SESSION_WRITER_STALE_MS = 10_000;
19
22
  let backendOverride;
20
23
  let cachedBackend;
21
- /** Security: a release that carries its own terminal-host addon never loads one chosen by cwd. */
22
- export function nativeCandidates(moduleUrl = import.meta.url, env = process.env, cwd = process.cwd(), exists = existsSync) {
23
- const addon = `${PRODUCT_IDENTITY.artifacts.terminalHostAddon}.node`;
24
- // Invariant: this module sits at dist/foundation/session-writer-lock/ inside the release root.
25
- const own = resolve(fileURLToPath(new URL("../../../", moduleUrl)), "dist", "native", `${process.platform}-${process.arch}`, addon);
26
- const override = env[PRODUCT_IDENTITY.environment.developmentInstanceId]
27
- ? env[PRODUCT_IDENTITY.environment.terminalHostPath]?.trim() || undefined
28
- : undefined;
29
- const local = exists(own)
30
- ? [own]
31
- : [resolve(cwd, "dist", "native", `${process.platform}-${process.arch}`, addon), resolve(cwd, ".artifacts", "terminal-host", addon)];
32
- return override ? [override, ...local] : local;
33
- }
34
- function loadNativeBackend() {
35
- const require = createRequire(import.meta.url);
36
- for (const candidate of nativeCandidates()) {
37
- if (!existsSync(candidate))
38
- continue;
39
- try {
40
- const loaded = require(candidate);
41
- if (loaded.SessionLock?.acquire)
42
- return loaded.SessionLock;
43
- }
44
- catch {
45
- // Compatibility: an addon for another Node ABI is unusable; try the next location.
46
- }
47
- }
48
- return undefined;
49
- }
50
- function processIsLive(pid) {
51
- try {
52
- process.kill(pid, 0);
53
- return true;
54
- }
55
- catch {
56
- return false;
57
- }
58
- }
59
- /** Development fallback. Installed releases carry the verified Node-API lock resolved above. */
60
- function portableBackend() {
61
- const root = join(tmpdir(), "a1-session-writer-locks");
62
- mkdirSync(root, { recursive: true, mode: 0o700 });
24
+ /**
25
+ * Locks live in one per-user directory keyed by canonical identity, not beside the session, so two
26
+ * spellings of one file (case, short names, links, hard links) contend for the same lock.
27
+ */
28
+ export function lockfileBackend(root = join(tmpdir(), "a1-session-writer-locks"), staleMs = SESSION_WRITER_STALE_MS) {
63
29
  const authorityPath = (kind, key) => join(root, `${createHash("sha256").update(`${kind}\0${key}`).digest("hex")}.lock`);
30
+ // Platform: Windows file names are case-insensitive, and realpath canonicalizes only existing parents.
64
31
  const locationKey = (path) => `${realpathSync.native(dirname(path))}\0${process.platform === "win32" ? basename(path).toLowerCase() : basename(path)}`;
65
32
  const identityKey = (path) => {
66
- const physical = realpathSync.native(path);
67
- const metadata = statSync(physical, { bigint: true });
33
+ const metadata = statSync(realpathSync.native(path), { bigint: true });
68
34
  return `${metadata.dev}:${metadata.ino}`;
69
35
  };
70
36
  const acquireAuthority = (path) => {
71
- for (let attempt = 0; attempt < 2; attempt += 1) {
72
- try {
73
- const descriptor = openSync(path, "wx", 0o600);
74
- writeFileSync(descriptor, JSON.stringify({ pid: process.pid, kind: process.env.A1_TAB_ID ? "resident tab" : "another A1 process" }));
75
- closeSync(descriptor);
76
- return;
77
- }
78
- catch (error) {
79
- if (attempt === 0) {
80
- try {
81
- const record = JSON.parse(readFileSync(path, "utf8"));
82
- if (typeof record.pid === "number" && !processIsLive(record.pid)) {
83
- rmSync(path, { force: true });
84
- continue;
85
- }
86
- }
87
- catch {
88
- // Security: malformed or racing lock evidence remains authoritative and fails closed.
89
- }
90
- }
91
- throw error;
92
- }
93
- }
37
+ mkdirSync(root, { recursive: true, mode: 0o700 });
38
+ // Rationale: the lock directory is the authority; no target file exists or is needed.
39
+ return lockfile.lockSync(path, {
40
+ realpath: false,
41
+ lockfilePath: path,
42
+ stale: staleMs,
43
+ // Rationale: a missed refresh means this process stalled; the next writer takes over, and the
44
+ // stalled one must not crash for it.
45
+ onCompromised: () => { },
46
+ });
94
47
  };
95
48
  return {
96
49
  acquire(requested) {
97
50
  const absolute = resolve(requested);
98
- const location = authorityPath("location", locationKey(absolute));
99
- acquireAuthority(location);
100
- let identity;
101
- let identityAcquired = false;
51
+ let releaseCurrent = acquireAuthority(authorityPath("location", locationKey(absolute)));
102
52
  let bound = false;
103
- try {
104
- if (existsSync(absolute)) {
105
- const key = identityKey(absolute);
106
- identity = authorityPath("identity", key);
107
- acquireAuthority(identity);
108
- identityAcquired = true;
109
- if (identityKey(absolute) !== key) {
110
- throw new Error("session identity changed while its writer lock was acquired");
111
- }
112
- rmSync(location, { force: true });
113
- bound = true;
53
+ const bindTo = () => {
54
+ const key = identityKey(absolute);
55
+ const releaseIdentity = acquireAuthority(authorityPath("identity", key));
56
+ if (identityKey(absolute) !== key) {
57
+ releaseIdentity();
58
+ throw new Error("session identity changed while its writer lock was acquired");
114
59
  }
60
+ // Invariant: the identity lock is held before the location lock goes, so there is no gap.
61
+ releaseCurrent();
62
+ releaseCurrent = releaseIdentity;
63
+ bound = true;
64
+ };
65
+ try {
66
+ if (existsSync(absolute))
67
+ bindTo();
115
68
  }
116
69
  catch (error) {
117
- rmSync(location, { force: true });
118
- if (identityAcquired && identity !== undefined)
119
- rmSync(identity, { force: true });
70
+ releaseCurrent();
120
71
  throw error;
121
72
  }
122
73
  let released = false;
@@ -126,29 +77,23 @@ function portableBackend() {
126
77
  if (bound || resolve(created) !== absolute || !existsSync(absolute)) {
127
78
  throw new Error("created session does not match its reservation");
128
79
  }
129
- const key = identityKey(absolute);
130
- identity = authorityPath("identity", key);
131
- acquireAuthority(identity);
132
- if (identityKey(absolute) !== key) {
133
- rmSync(identity, { force: true });
134
- identity = undefined;
135
- throw new Error("created session identity changed during bind");
136
- }
137
- rmSync(location, { force: true });
138
- bound = true;
80
+ bindTo();
139
81
  },
140
82
  release() {
141
83
  if (released)
142
84
  throw new Error("session lock was already released");
143
85
  released = true;
144
- rmSync(bound ? identity : location, { force: true });
86
+ try {
87
+ releaseCurrent();
88
+ }
89
+ catch { /* Compatibility: a reclaimed stale lock is already gone. */ }
145
90
  },
146
91
  };
147
92
  },
148
93
  };
149
94
  }
150
95
  function backend() {
151
- return backendOverride ?? (cachedBackend ??= loadNativeBackend() ?? portableBackend());
96
+ return backendOverride ?? (cachedBackend ??= lockfileBackend());
152
97
  }
153
98
  function acquire(path) {
154
99
  const absolute = resolve(path);
@@ -157,7 +102,7 @@ function acquire(path) {
157
102
  lock = backend().acquire(absolute);
158
103
  }
159
104
  catch {
160
- throw new SessionWriterConflict(absolute, process.env.A1_TAB_ID ? "a resident tab" : "another A1 process");
105
+ throw new SessionWriterConflict(absolute);
161
106
  }
162
107
  let released = false;
163
108
  return {
@@ -196,7 +141,7 @@ export function bind(handle, createdPath) {
196
141
  export function release(handle) {
197
142
  handle.release();
198
143
  }
199
- /** Test-only backend seam; passing undefined restores native discovery. */
144
+ /** Test-only backend seam; passing undefined restores the default backend. */
200
145
  export function setSessionWriterLockBackendForTest(value) {
201
146
  backendOverride = value;
202
147
  cachedBackend = undefined;
@@ -1,5 +1,5 @@
1
1
  import { PRODUCT_IDENTITY } from "../../product-identity.js";
2
- export type StartupPhase = "command-invoked" | "bootstrap-start" | "bootstrap-selected" | "durable-validation-start" | "durable-validation-complete" | "replacement-supervisor-start" | "replacement-supervisor-ready" | "guardian-start" | "guardian-connected" | "ui-entry" | "ui-modules-loaded" | "pi-services" | "resource-discovery" | "session-created" | "settings-loaded" | "first-input-ready-render";
2
+ export type StartupPhase = "command-invoked" | "bootstrap-start" | "bootstrap-selected" | "durable-validation-start" | "durable-validation-complete" | "ui-entry" | "ui-modules-loaded" | "pi-services" | "resource-discovery" | "session-created" | "settings-loaded" | "first-input-ready-render";
3
3
  export interface StartupTraceEvent {
4
4
  readonly schema: typeof PRODUCT_IDENTITY.evidence.startupTraceSchema;
5
5
  readonly traceId: string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The launcher's half of the unclean-exit protocol. The UI arms a notice file while it runs and
3
+ * removes it on a clean exit, so a notice that is still present when the UI ends means the UI was
4
+ * stopped unexpectedly: restore the terminal, print the notice as plain text, and consume it.
5
+ */
6
+ export declare function deliverExitNotice(path: string, write: (text: string) => void, restore?: () => void): boolean;