@timurproko/a1 0.2.6-dev.736 → 0.2.7-dev.1221

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 (423) hide show
  1. package/README.md +41 -3
  2. package/bin/automatic-update.js +7 -0
  3. package/bin/cli.js +4 -0
  4. package/bin/guardian.js +5 -0
  5. package/bin/tab-launch-context.d.ts +27 -0
  6. package/bin/tab-launch-context.js +94 -0
  7. package/bin/ui.js +107 -59
  8. package/dist/app/session-shell/clipboard-diagnostics.js +1 -1
  9. package/dist/app/session-shell/dialog-coordinator.d.ts +42 -0
  10. package/dist/app/session-shell/dialog-coordinator.js +133 -0
  11. package/dist/app/session-shell/dock-chrome.d.ts +34 -0
  12. package/dist/app/session-shell/dock-chrome.js +109 -0
  13. package/dist/app/session-shell/exit-notice.d.ts +1 -1
  14. package/dist/app/session-shell/exit-notice.js +1 -1
  15. package/dist/app/session-shell/extension-surface.d.ts +30 -0
  16. package/dist/app/session-shell/extension-surface.js +105 -0
  17. package/dist/app/session-shell/helper-pool.d.ts +3 -5
  18. package/dist/app/session-shell/helper-pool.js +3 -5
  19. package/dist/app/session-shell/input-surface-switch.d.ts +36 -0
  20. package/dist/app/session-shell/input-surface-switch.js +85 -0
  21. package/dist/app/session-shell/login-interaction.d.ts +27 -0
  22. package/dist/app/session-shell/login-interaction.js +91 -0
  23. package/dist/app/session-shell/prompt-submission.d.ts +48 -0
  24. package/dist/app/session-shell/prompt-submission.js +276 -0
  25. package/dist/app/session-shell/resident-tab-coordinator.d.ts +38 -0
  26. package/dist/app/session-shell/resident-tab-coordinator.js +77 -0
  27. package/dist/app/session-shell/response-copy-helper.js +21 -6
  28. package/dist/app/session-shell/response-copy-protocol.d.ts +1 -1
  29. package/dist/app/session-shell/response-copy-transport.d.ts +16 -6
  30. package/dist/app/session-shell/response-copy-transport.js +69 -17
  31. package/dist/app/session-shell/session-concern.d.ts +22 -0
  32. package/dist/app/session-shell/session-concern.js +29 -0
  33. package/dist/app/session-shell/session-lifecycle.d.ts +29 -0
  34. package/dist/app/session-shell/session-lifecycle.js +49 -0
  35. package/dist/app/session-shell/session-shell-root.d.ts +42 -6
  36. package/dist/app/session-shell/session-shell-root.js +196 -908
  37. package/dist/app/session-shell/session-shell.d.ts +3 -8
  38. package/dist/app/session-shell/session-shell.js +324 -835
  39. package/dist/app/session-shell/session-viewport-controller.d.ts +2 -2
  40. package/dist/app/session-shell/session-viewport-controller.js +29 -15
  41. package/dist/app/session-shell/settings-bindings.d.ts +17 -0
  42. package/dist/app/session-shell/settings-bindings.js +91 -0
  43. package/dist/app/session-shell/shell-editor-factory.d.ts +27 -0
  44. package/dist/app/session-shell/shell-editor-factory.js +102 -0
  45. package/dist/app/session-shell/shell-host.d.ts +6 -0
  46. package/dist/app/session-shell/shell-host.js +34 -7
  47. package/dist/app/session-shell/shell-layout.d.ts +64 -0
  48. package/dist/app/session-shell/shell-layout.js +18 -0
  49. package/dist/app/session-shell/suggestion-surface.d.ts +25 -0
  50. package/dist/app/session-shell/suggestion-surface.js +63 -0
  51. package/dist/app/session-shell/transcript-document.d.ts +78 -0
  52. package/dist/app/session-shell/transcript-document.js +365 -0
  53. package/dist/app/session-shell/viewport-layout.d.ts +31 -0
  54. package/dist/app/session-shell/viewport-layout.js +160 -0
  55. package/dist/app/session-shell/workflow-output.d.ts +33 -0
  56. package/dist/app/session-shell/workflow-output.js +174 -0
  57. package/dist/app/session-shell/workflow-router.d.ts +25 -0
  58. package/dist/app/session-shell/workflow-router.js +39 -0
  59. package/dist/cli/dispatch.d.ts +11 -1
  60. package/dist/cli/dispatch.js +79 -5
  61. package/dist/cli/tabs-request.d.ts +7 -0
  62. package/dist/cli/tabs-request.js +1 -0
  63. package/dist/cli/tabs.d.ts +14 -0
  64. package/dist/cli/tabs.js +151 -0
  65. package/dist/cli/version-stats.js +38 -3
  66. package/dist/composition/owned-settings.d.ts +12 -0
  67. package/dist/composition/owned-settings.js +36 -0
  68. package/dist/composition/owned-ui.d.ts +27 -28
  69. package/dist/composition/owned-ui.js +205 -153
  70. package/dist/composition/resident-tab-bridge.d.ts +25 -0
  71. package/dist/composition/resident-tab-bridge.js +180 -0
  72. package/dist/composition/session-backend.d.ts +19 -0
  73. package/dist/composition/session-backend.js +1 -0
  74. package/dist/composition/session-runtime-registry.d.ts +20 -0
  75. package/dist/composition/session-runtime-registry.js +85 -0
  76. package/dist/composition/settings-route-host.d.ts +16 -1
  77. package/dist/composition/settings-route-host.js +29 -10
  78. package/dist/composition/settings-search-input.d.ts +9 -0
  79. package/dist/composition/settings-search-input.js +58 -0
  80. package/dist/composition/startup-route.d.ts +32 -0
  81. package/dist/composition/startup-route.js +72 -0
  82. package/dist/composition/utility-tab.d.ts +28 -0
  83. package/dist/composition/utility-tab.js +242 -0
  84. package/dist/contracts/owned-ui/index.d.ts +1 -1
  85. package/dist/contracts/owned-ui/model.d.ts +3 -0
  86. package/dist/contracts/owned-ui/session-backend.d.ts +3 -0
  87. package/dist/contracts/owned-ui/session-workflows.d.ts +11 -4
  88. package/dist/contracts/presentation/index.d.ts +11 -0
  89. package/dist/contracts/presentation/index.js +12 -0
  90. package/dist/features/launch/development-launch.js +6 -0
  91. package/dist/features/launch/index.d.ts +1 -1
  92. package/dist/features/launch/intent.d.ts +3 -1
  93. package/dist/features/launch/intent.js +7 -4
  94. package/dist/features/launch/profile-paths.js +4 -0
  95. package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
  96. package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
  97. package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
  98. package/dist/features/owned-ui/project-trust-dialog.js +6 -8
  99. package/dist/features/owned-ui/project-trust-prompt.d.ts +1 -0
  100. package/dist/features/owned-ui/project-trust-prompt.js +7 -3
  101. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
  102. package/dist/features/owned-ui/reference-screen-app.js +12 -2
  103. package/dist/features/owned-ui/resources/release-notes.json +4 -0
  104. package/dist/features/owned-ui/settings-app.d.ts +12 -2
  105. package/dist/features/owned-ui/settings-app.js +119 -84
  106. package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
  107. package/dist/features/owned-ui/settings-search-input.js +1 -0
  108. package/dist/features/prompt-history/index.d.ts +1 -1
  109. package/dist/features/prompt-history/paths.d.ts +2 -1
  110. package/dist/features/prompt-history/paths.js +10 -2
  111. package/dist/features/prompt-history/service.d.ts +5 -0
  112. package/dist/features/prompt-history/service.js +1 -1
  113. package/dist/features/resident-tab/auto-name.d.ts +11 -0
  114. package/dist/features/resident-tab/auto-name.js +44 -0
  115. package/dist/features/resident-tab/index.d.ts +5 -0
  116. package/dist/features/resident-tab/index.js +7 -0
  117. package/dist/features/resident-tab/shortcut-bindings-file.d.ts +8 -0
  118. package/dist/features/resident-tab/shortcut-bindings-file.js +40 -0
  119. package/dist/features/resident-tab/shortcut-declarations.d.ts +13 -0
  120. package/dist/features/resident-tab/shortcut-declarations.js +26 -0
  121. package/dist/features/resident-tab/shortcut-projection.d.ts +29 -0
  122. package/dist/features/resident-tab/shortcut-projection.js +64 -0
  123. package/dist/features/resident-tab/status-deriver.d.ts +10 -0
  124. package/dist/features/resident-tab/status-deriver.js +12 -0
  125. package/dist/foundation/launch-context/index.d.ts +12 -0
  126. package/dist/foundation/launch-context/index.js +26 -0
  127. package/dist/foundation/launch-guardian/exit-watcher.d.ts +14 -0
  128. package/dist/foundation/launch-guardian/exit-watcher.js +94 -0
  129. package/dist/foundation/launch-guardian/main.d.ts +7 -1
  130. package/dist/foundation/launch-guardian/main.js +60 -7
  131. package/dist/foundation/lifecycle/session-repository-context.js +16 -2
  132. package/dist/foundation/process-containment/{native-guardian-containment.d.ts → addon-containment.d.ts} +7 -5
  133. package/dist/foundation/process-containment/addon-containment.js +139 -0
  134. package/dist/foundation/process-containment/addon-process-inspector.d.ts +11 -0
  135. package/dist/foundation/process-containment/addon-process-inspector.js +36 -0
  136. package/dist/foundation/process-containment/addon.d.ts +42 -0
  137. package/dist/foundation/process-containment/addon.js +57 -0
  138. package/dist/foundation/process-containment/artifact.d.ts +1 -1
  139. package/dist/foundation/process-containment/artifact.js +6 -5
  140. package/dist/foundation/process-containment/contracts.d.ts +2 -0
  141. package/dist/foundation/process-containment/index.d.ts +4 -4
  142. package/dist/foundation/process-containment/index.js +3 -3
  143. package/dist/foundation/process-containment/native-process-inspector.d.ts +2 -1
  144. package/dist/foundation/process-containment/native-process-inspector.js +10 -10
  145. package/dist/foundation/release/automatic-update.d.ts +37 -0
  146. package/dist/foundation/release/automatic-update.js +265 -0
  147. package/dist/foundation/release/bootstrap.d.ts +7 -9
  148. package/dist/foundation/release/bootstrap.js +107 -24
  149. package/dist/foundation/release/cohort-state.d.ts +1 -1
  150. package/dist/foundation/release/cohort-state.js +6 -3
  151. package/dist/foundation/release/dependency-certification.js +54 -19
  152. package/dist/foundation/release/early-accent.d.ts +12 -0
  153. package/dist/foundation/release/early-accent.js +59 -0
  154. package/dist/foundation/release/index.d.ts +2 -2
  155. package/dist/foundation/release/index.js +2 -2
  156. package/dist/foundation/release/latest-release.d.ts +24 -2
  157. package/dist/foundation/release/latest-release.js +51 -1
  158. package/dist/foundation/release/progress-palette.generated.d.ts +137 -3
  159. package/dist/foundation/release/progress-palette.generated.js +137 -3
  160. package/dist/foundation/release/release-gc.d.ts +3 -0
  161. package/dist/foundation/release/release-gc.js +59 -8
  162. package/dist/foundation/release/release-store.d.ts +12 -0
  163. package/dist/foundation/release/release-store.js +124 -2
  164. package/dist/foundation/release/update-execution-lease.d.ts +48 -0
  165. package/dist/foundation/release/update-execution-lease.js +161 -0
  166. package/dist/foundation/release/update.d.ts +12 -7
  167. package/dist/foundation/release/update.js +281 -196
  168. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +18 -0
  169. package/dist/foundation/resident-tabs/bounded-host-command.js +39 -0
  170. package/dist/foundation/resident-tabs/bridge-client.d.ts +70 -0
  171. package/dist/foundation/resident-tabs/bridge-client.js +321 -0
  172. package/dist/foundation/resident-tabs/doctor.d.ts +28 -0
  173. package/dist/foundation/resident-tabs/doctor.js +166 -0
  174. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
  175. package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
  176. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +36 -0
  177. package/dist/foundation/resident-tabs/ensure-terminal-host.js +151 -0
  178. package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
  179. package/dist/foundation/resident-tabs/fault-injection.js +26 -0
  180. package/dist/foundation/resident-tabs/fixture-control.d.ts +7 -0
  181. package/dist/foundation/resident-tabs/fixture-control.js +35 -0
  182. package/dist/foundation/resident-tabs/host-entry.d.ts +1 -0
  183. package/dist/foundation/resident-tabs/host-entry.js +17 -0
  184. package/dist/foundation/resident-tabs/index.d.ts +12 -0
  185. package/dist/foundation/resident-tabs/index.js +6 -0
  186. package/dist/foundation/resident-tabs/maintenance.d.ts +39 -0
  187. package/dist/foundation/resident-tabs/maintenance.js +75 -0
  188. package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
  189. package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
  190. package/dist/foundation/resident-tabs/progress-observation.d.ts +4 -0
  191. package/dist/foundation/resident-tabs/progress-observation.js +150 -0
  192. package/dist/foundation/resident-tabs/prompt-journal.d.ts +63 -0
  193. package/dist/foundation/resident-tabs/prompt-journal.js +213 -0
  194. package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
  195. package/dist/foundation/resident-tabs/protocol.js +195 -0
  196. package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
  197. package/dist/foundation/resident-tabs/recovery.js +205 -0
  198. package/dist/foundation/resident-tabs/release-holds.d.ts +28 -0
  199. package/dist/foundation/resident-tabs/release-holds.js +75 -0
  200. package/dist/foundation/resident-tabs/routes.d.ts +5 -0
  201. package/dist/foundation/resident-tabs/routes.js +5 -0
  202. package/dist/foundation/resident-tabs/routing.d.ts +11 -0
  203. package/dist/foundation/resident-tabs/routing.js +10 -0
  204. package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
  205. package/dist/foundation/resident-tabs/session-sync.js +37 -0
  206. package/dist/foundation/resident-tabs/settings.d.ts +12 -0
  207. package/dist/foundation/resident-tabs/settings.js +58 -0
  208. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +14 -0
  209. package/dist/foundation/resident-tabs/terminal-host-artifact.js +78 -0
  210. package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
  211. package/dist/foundation/resident-tabs/zip-archive.js +56 -0
  212. package/dist/foundation/session-writer-lock/index.d.ts +30 -0
  213. package/dist/foundation/session-writer-lock/index.js +203 -0
  214. package/dist/foundation/startup/startup-descriptor.js +2 -2
  215. package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
  216. package/dist/foundation/terminal-cleanup/index.js +1 -1
  217. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +6 -0
  218. package/dist/foundation/terminal-cleanup/terminal-reset.js +6 -0
  219. package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
  220. package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
  221. package/dist/integrations/pi/components/modal-frame.js +48 -9
  222. package/dist/integrations/pi/components/models-dialog.js +4 -52
  223. package/dist/integrations/pi/components/owned-editor-ux.d.ts +6 -1
  224. package/dist/integrations/pi/components/owned-editor-ux.js +90 -6
  225. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
  226. package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
  227. package/dist/integrations/pi/components/share-operation-dialog.d.ts +2 -0
  228. package/dist/integrations/pi/components/share-operation-dialog.js +2 -1
  229. package/dist/integrations/pi/components/shell-editor-autocomplete.js +58 -36
  230. package/dist/integrations/pi/components/shell-extension-ui.js +79 -60
  231. package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
  232. package/dist/integrations/pi/components/shell-footer-status.js +21 -5
  233. package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
  234. package/dist/integrations/pi/components/shell-presenters-info.d.ts +5 -2
  235. package/dist/integrations/pi/components/shell-presenters-info.js +18 -4
  236. package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
  237. package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -1
  238. package/dist/integrations/pi/components/shell-shared-facade.d.ts +12 -1
  239. package/dist/integrations/pi/components/skills-dialog.js +2 -2
  240. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  241. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
  242. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  243. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  244. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  245. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  246. package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
  247. package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
  248. package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
  249. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  250. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -6
  251. package/dist/integrations/pi/components/upstream/components/owned-editor.js +16 -9
  252. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  253. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +4 -4
  254. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
  255. package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
  256. package/dist/integrations/pi/components/upstream/components/session-footer.js +31 -25
  257. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  258. package/dist/integrations/pi/components/upstream/components/session-selector.js +8 -7
  259. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
  260. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
  261. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
  262. package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
  263. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  264. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  265. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +8 -7
  266. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +10 -9
  267. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +2 -6
  268. package/dist/integrations/pi/components/upstream/components/tool-execution.js +103 -101
  269. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +9 -9
  270. package/dist/integrations/pi/components/upstream/components/tree-selector.js +13 -21
  271. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  272. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  273. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
  274. package/dist/integrations/pi/components/upstream/history/editor-core.js +7 -5
  275. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  276. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  277. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  278. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  279. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  280. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  281. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  282. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  283. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
  284. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  285. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +5 -0
  286. package/dist/integrations/pi/components/upstream/theme/theme.js +29 -2
  287. package/dist/integrations/pi/engine/adapter.d.ts +5 -77
  288. package/dist/integrations/pi/engine/adapter.js +95 -394
  289. package/dist/integrations/pi/engine/command-dispatch.d.ts +13 -0
  290. package/dist/integrations/pi/engine/command-dispatch.js +57 -0
  291. package/dist/integrations/pi/engine/host.js +2 -2
  292. package/dist/integrations/pi/engine/offline-settings-port.d.ts +23 -0
  293. package/dist/integrations/pi/engine/offline-settings-port.js +53 -0
  294. package/dist/integrations/pi/engine/pi-settings-metadata.json +14 -2
  295. package/dist/integrations/pi/engine/ports.d.ts +56 -0
  296. package/dist/integrations/pi/engine/ports.js +93 -0
  297. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
  298. package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
  299. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  300. package/dist/integrations/pi/engine/runtime-integration.js +102 -29
  301. package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
  302. package/dist/integrations/pi/engine/session-runtime.js +13 -5
  303. package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
  304. package/dist/integrations/pi/engine/session-selection.js +45 -9
  305. package/dist/integrations/pi/engine/settings-bridge.d.ts +5 -0
  306. package/dist/integrations/pi/engine/settings-bridge.js +15 -0
  307. package/dist/integrations/pi/engine/settings-port.js +11 -15
  308. package/dist/integrations/pi/engine/view-state.d.ts +42 -0
  309. package/dist/integrations/pi/engine/view-state.js +126 -0
  310. package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
  311. package/dist/integrations/pi/engine/workflow-runner.js +73 -4
  312. package/dist/integrations/pi/engine/workflow-support.d.ts +12 -0
  313. package/dist/integrations/pi/engine/workflow-support.js +36 -0
  314. package/dist/integrations/pi/engine/workflows.d.ts +3 -1
  315. package/dist/integrations/pi/engine/workflows.js +1 -0
  316. package/dist/integrations/pi/startup-public.js +257 -310
  317. package/dist/integrations/pi/startup-public.manifest.json +564 -440
  318. package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
  319. package/dist/integrations/pi/tui-runtime/adapter.js +32 -1
  320. package/dist/integrations/pi/tui-runtime/contracts.d.ts +5 -0
  321. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +12 -1
  322. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +31 -2
  323. package/dist/native/darwin-arm64/manifest.json +10 -7
  324. package/dist/native/darwin-arm64/process-guardian.node +0 -0
  325. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
  326. package/dist/native/darwin-arm64/terminal-host.manifest.json +46 -0
  327. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  328. package/dist/native/darwin-x64/manifest.json +23 -0
  329. package/dist/native/darwin-x64/process-guardian.node +0 -0
  330. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
  331. package/dist/native/darwin-x64/terminal-host.manifest.json +46 -0
  332. package/dist/native/darwin-x64/terminal-host.node +0 -0
  333. package/dist/native/linux-x64/manifest.json +10 -7
  334. package/dist/native/linux-x64/process-guardian.node +0 -0
  335. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
  336. package/dist/native/linux-x64/terminal-host.manifest.json +46 -0
  337. package/dist/native/linux-x64/terminal-host.node +0 -0
  338. package/dist/native/win32-x64/manifest.json +10 -7
  339. package/dist/native/win32-x64/process-guardian.node +0 -0
  340. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
  341. package/dist/native/win32-x64/terminal-host.manifest.json +46 -0
  342. package/dist/native/win32-x64/terminal-host.node +0 -0
  343. package/dist/product-identity.d.ts +3 -3
  344. package/dist/product-identity.js +4 -4
  345. package/dist/product-identity.json +16 -2
  346. package/dist/runtime-payload-inventory.json +6330 -6677
  347. package/dist/ui/apps/host.d.ts +2 -0
  348. package/dist/ui/apps/host.js +7 -1
  349. package/dist/ui/components/dialog-panel.js +35 -30
  350. package/dist/ui/components/index.d.ts +2 -2
  351. package/dist/ui/components/index.js +1 -1
  352. package/dist/ui/components/line-input.d.ts +4 -1
  353. package/dist/ui/components/line-input.js +3 -1
  354. package/dist/ui/components/prompt-input.d.ts +11 -8
  355. package/dist/ui/components/prompt-input.js +15 -13
  356. package/dist/ui/components/selection-copy.d.ts +2 -0
  357. package/dist/ui/components/selection-copy.js +13 -0
  358. package/dist/ui/components/spans.js +2 -2
  359. package/dist/ui/components/submitted-prompt.d.ts +9 -6
  360. package/dist/ui/components/submitted-prompt.js +20 -12
  361. package/dist/ui/components/surface.js +4 -2
  362. package/dist/ui/components/text.d.ts +3 -6
  363. package/dist/ui/components/text.js +6 -8
  364. package/dist/ui/components/transcript-viewport.d.ts +9 -2
  365. package/dist/ui/components/transcript-viewport.js +176 -55
  366. package/dist/ui/components/value-menu.d.ts +6 -3
  367. package/dist/ui/components/value-menu.js +6 -5
  368. package/dist/ui/settings/declarations.d.ts +90 -16
  369. package/dist/ui/settings/declarations.js +67 -14
  370. package/dist/ui/settings/early-accent.d.ts +81 -0
  371. package/dist/ui/settings/early-accent.js +50 -0
  372. package/dist/ui/settings/index.d.ts +4 -2
  373. package/dist/ui/settings/index.js +2 -1
  374. package/dist/ui/settings/manager.d.ts +16 -3
  375. package/dist/ui/settings/manager.js +122 -33
  376. package/dist/ui/settings/migrations.d.ts +6 -0
  377. package/dist/ui/settings/migrations.js +73 -0
  378. package/dist/ui/settings/profile-document.d.ts +26 -0
  379. package/dist/ui/settings/profile-document.js +164 -0
  380. package/dist/ui/settings/progress-palette.generated.d.ts +139 -0
  381. package/dist/ui/settings/progress-palette.generated.js +139 -0
  382. package/dist/ui/settings/sections.d.ts +4 -0
  383. package/dist/ui/settings/sections.js +9 -3
  384. package/docs/architecture/boundaries.md +25 -10
  385. package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
  386. package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
  387. package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
  388. package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
  389. package/docs/architecture/evidence/terminal-host-spike/manual-one-pane-terminal-host.md +70 -0
  390. package/docs/architecture/evidence/terminal-host-spike/manual-two-by-two-basic-terminal-host.md +32 -0
  391. package/docs/architecture/evidence/terminal-host-spike/milestone-baseline.json +115 -0
  392. package/docs/architecture/evidence/terminal-host-spike/owned-ui-foundation-prerequisite.json +48 -0
  393. package/docs/architecture/evidence/terminal-host-spike/terminal-host-provenance.json +125 -0
  394. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-1.json +56 -0
  395. package/docs/architecture/evidence/terminal-host-spike/terminal-host-task-5-3.json +174 -0
  396. package/docs/architecture/evidence/terminal-host-spike/terminal-spike-acceptance-record.json +18 -0
  397. package/docs/architecture/internal-naming.md +3 -1
  398. package/docs/architecture/process-guardian-provenance.md +18 -10
  399. package/docs/architecture/project-structure.md +8 -0
  400. package/docs/architecture/resident-tabs-requirements.md +18 -0
  401. package/docs/architecture/resident-tabs.md +648 -0
  402. package/docs/architecture/response-copy-delivery.md +1 -1
  403. package/docs/architecture/terminal-host-proof-gate.md +3 -1
  404. package/docs/architecture/terminal-host-provenance.md +16 -4
  405. package/docs/architecture/terminal-host-spike-evidence.md +1 -1
  406. package/docs/architecture/tool-image-presentation.md +2 -1
  407. package/docs/architecture/toolchain.md +11 -9
  408. package/docs/ci-release-runbook.md +14 -7
  409. package/docs/features/launch-profiles.md +11 -8
  410. package/docs/features/prompt-history.md +41 -20
  411. package/docs/manual-resident-tabs-acceptance.md +51 -0
  412. package/docs/openspec-archive-automation.md +49 -22
  413. package/docs/releases/0.2.6.md +44 -0
  414. package/docs/validation.md +49 -9
  415. package/package.json +19 -6
  416. package/dist/foundation/process-containment/darwin-process-inspector.d.ts +0 -5
  417. package/dist/foundation/process-containment/darwin-process-inspector.js +0 -7
  418. package/dist/foundation/process-containment/native-guardian-containment.js +0 -152
  419. package/dist/foundation/process-containment/windows-process-inspector.d.ts +0 -19
  420. package/dist/foundation/process-containment/windows-process-inspector.js +0 -56
  421. package/dist/native/darwin-arm64/process-guardian +0 -0
  422. package/dist/native/linux-x64/process-guardian +0 -0
  423. package/dist/native/win32-x64/process-guardian.exe +0 -0
@@ -14,6 +14,7 @@ import { UpdateTransactionStore } from "./update-transaction.js";
14
14
  import { cleanupUpdateRecoveryCapsules } from "./update-recovery.js";
15
15
  import { PRODUCT_IDENTITY } from "../../product-identity.js";
16
16
  import { collectCompileCaches, startupCompileCachePath } from "../startup/index.js";
17
+ import { readResidentReleaseHolds } from "../resident-tabs/index.js";
17
18
  const DEFAULT_CANDIDATE_AGE_MS = 60 * 60 * 1_000;
18
19
  const DEFAULT_LIMITS = { maxItems: 8, maxDurationMs: 2_000, concurrency: 1 };
19
20
  const DEFAULT_WORKER_DURATION_MS = 10 * 60 * 1_000;
@@ -30,7 +31,7 @@ export async function prepareReleaseCleanup(dataDir, paths, options = {}) {
30
31
  const store = new CohortStateStore(dataDir);
31
32
  const discovered = await discoverCleanupInputs(dataDir, store, options);
32
33
  const transactionStore = options.transactionStore ?? new UpdateTransactionStore(dataDir);
33
- await store.reconcileRetention(async () => await protectionInputs(paths, transactionStore, options.externalHolds ?? []), discovered.orphans);
34
+ await store.reconcileRetention(async () => await protectionInputs(dataDir, paths, transactionStore, options.externalHolds ?? [], options.residentHolds), discovered.orphans);
34
35
  for (const diagnostic of discovered.diagnostics.slice(0, 16)) {
35
36
  await store.recordCleanupFailure(diagnostic.releaseId, diagnostic.stage, diagnostic.error);
36
37
  }
@@ -77,7 +78,7 @@ export async function runBoundedReleaseCleanup(dataDir, paths, options = {}) {
77
78
  maximum -= 1;
78
79
  const certificationId = certificationReleaseId(artifact);
79
80
  if (certificationId !== null) {
80
- const inputs = await protectionInputs(paths, transactionStore, options.externalHolds ?? []);
81
+ const inputs = await protectionInputs(dataDir, paths, transactionStore, options.externalHolds ?? [], options.residentHolds);
81
82
  if (isProtectedDetachedRelease(await store.read(), inputs, certificationId))
82
83
  continue;
83
84
  }
@@ -232,7 +233,7 @@ export async function collectRelease(store, dataDir, releaseId, externalReferenc
232
233
  const state = await store.read();
233
234
  if (!state.releases[releaseId])
234
235
  throw new Error(`unknown release ${releaseId}`);
235
- await store.reconcileRetention(async () => ({ ...(await protectionInputs(paths, new UpdateTransactionStore(dataDir), holds)) }));
236
+ await store.reconcileRetention(async () => ({ ...(await protectionInputs(dataDir, paths, new UpdateTransactionStore(dataDir), holds)) }));
236
237
  const disposition = (await store.read()).cleanup.pending[releaseId];
237
238
  if (!disposition)
238
239
  throw new Error(`release ${releaseId} is still referenced and cannot be collected`);
@@ -243,7 +244,7 @@ export async function collectRelease(store, dataDir, releaseId, externalReferenc
243
244
  async function collectDisposition(store, dataDir, paths, disposition, options) {
244
245
  const releaseId = disposition.release.releaseId;
245
246
  try {
246
- const inputs = await protectionInputs(paths, options.transactionStore ?? new UpdateTransactionStore(dataDir), options.externalHolds ?? []);
247
+ const inputs = await protectionInputs(dataDir, paths, options.transactionStore ?? new UpdateTransactionStore(dataDir), options.externalHolds ?? [], options.residentHolds);
247
248
  const current = await store.read();
248
249
  if (isProtectedDetachedRelease(current, inputs, releaseId))
249
250
  return false;
@@ -289,12 +290,62 @@ function isProtectedDetachedRelease(state, inputs, releaseId) {
289
290
  || (inputs.externalHolds ?? []).some(hold => hold.releaseId === releaseId)
290
291
  || (inputs.transaction?.status === "active" && inputs.transaction.priorActiveReleaseId === releaseId);
291
292
  }
292
- async function protectionInputs(paths, transactionStore, externalHolds) {
293
- const [liveIds, transaction] = await Promise.all([
293
+ async function protectionInputs(dataDir, paths, transactionStore, externalHolds, residentHolds) {
294
+ const [liveIds, transaction, resident] = await Promise.all([
294
295
  paths ? liveReleaseIds(paths) : Promise.resolve([]),
295
296
  transactionStore.read(),
297
+ residentReleaseHolds(dataDir, residentHolds),
296
298
  ]);
297
- return { liveReleaseIds: liveIds, externalHolds, transaction: transactionReference(transaction) };
299
+ return { liveReleaseIds: liveIds, externalHolds: [...externalHolds, ...resident], transaction: transactionReference(transaction) };
300
+ }
301
+ /**
302
+ * Holds every release a live resident server, holder, or tab child runs from. Retention fails
303
+ * closed: an incomplete report, or a recorded root inside the release store that names no known
304
+ * release, holds every known release.
305
+ */
306
+ async function residentReleaseHolds(dataDir, reader) {
307
+ const state = await new CohortStateStore(dataDir).read();
308
+ const records = [
309
+ ...Object.values(state.releases),
310
+ ...Object.values(state.cleanup.pending).map(disposition => disposition.release),
311
+ ];
312
+ const preferred = [state.references.active, state.references.rollback]
313
+ .flatMap(id => id && state.releases[id] ? [state.releases[id].releaseRoot] : []);
314
+ const roots = [...new Set([...preferred, ...Object.values(state.releases).map(record => record.releaseRoot)])];
315
+ const report = await (reader ?? ((directory, releaseRoots) => readResidentReleaseHolds({ dataDir: directory, releaseRoots })))(dataDir, roots);
316
+ const everything = () => [...new Set(records.map(record => record.releaseId))].map(releaseId => ({ authority: "resident", releaseId }));
317
+ if (!report.complete)
318
+ return everything();
319
+ if (report.releaseRoots.length === 0)
320
+ return [];
321
+ // Rationale: native Windows extended paths and Unix aliases must match the catalog's spelling.
322
+ const byRoot = new Map();
323
+ for (const record of records) {
324
+ const canonical = await realpath(record.releaseRoot).catch(() => record.releaseRoot);
325
+ byRoot.set(comparablePath(canonical), record.releaseId);
326
+ }
327
+ const directory = await realpath(resolve(dataDir, "releases")).catch(() => null);
328
+ if (directory === null)
329
+ return everything();
330
+ const store = comparablePath(directory);
331
+ const holds = new Set();
332
+ for (const root of report.releaseRoots) {
333
+ // Security: an unresolved live root may alias managed content; uncertainty must retain it.
334
+ const canonical = await realpath(root).catch(() => null);
335
+ if (canonical === null)
336
+ return everything();
337
+ const key = comparablePath(canonical);
338
+ const releaseId = byRoot.get(key);
339
+ if (releaseId)
340
+ holds.add(releaseId);
341
+ else if (key.startsWith(`${store}${sep}`))
342
+ return everything();
343
+ }
344
+ return [...holds].sort().map(releaseId => ({ authority: "resident", releaseId }));
345
+ }
346
+ function comparablePath(path) {
347
+ const absolute = resolve(path);
348
+ return process.platform === "win32" ? absolute.toLowerCase() : absolute;
298
349
  }
299
350
  function transactionReference(transaction) {
300
351
  return transaction === null ? null : { status: transaction.status, priorActiveReleaseId: transaction.priorActiveReleaseId };
@@ -745,7 +796,7 @@ function parseWorkerHolds(value) {
745
796
  if (!Array.isArray(parsed))
746
797
  return [];
747
798
  return parsed.filter((hold) => Boolean(hold) && typeof hold === "object"
748
- && (hold.authority === "agent" || hold.authority === "migration")
799
+ && ["agent", "migration", "resident"].includes(hold.authority)
749
800
  && typeof hold.releaseId === "string");
750
801
  }
751
802
  catch {
@@ -31,12 +31,24 @@ export interface CertifiedReleaseRecord {
31
31
  /** Absent on records written before releases declared a handoff contract. */
32
32
  readonly launchContract?: string;
33
33
  }
34
+ /** Reports that installed package bytes cannot safely reproduce an approved release. */
35
+ export declare class ReleaseRepairUnavailableError extends Error {
36
+ readonly code: "RELEASE_REPAIR_UNAVAILABLE";
37
+ constructor(message: string, options?: ErrorOptions);
38
+ }
34
39
  /** Process-authority compatibility controls for metadata-only release loading. */
35
40
  export type ReadCertifiedReleaseManifestOptions = ReadCertifiedDependencyLayerOptions;
36
41
  export declare function materializeRelease(packageRoot: string, dataDir: string, options?: MaterializeReleaseOptions): Promise<MaterializedRelease>;
37
42
  /** Consume proof that this exact object was freshly materialized or fully verified in this process. */
38
43
  export declare function consumeMaterializationProof(release: MaterializedRelease): boolean;
39
44
  export declare function readMaterializedRelease(releaseRoot: string, selectedStoreRoot?: string): Promise<MaterializedRelease>;
45
+ /** Fast presence check before trusted certification permits a release to skip full verification. */
46
+ export declare function assertCertifiedFilesPresent(releaseRoot: string): Promise<void>;
47
+ /**
48
+ * Restore changed or missing product payload files from the installed package without replacing the
49
+ * immutable release directory. Dependency layers retain their own verification and repair policy.
50
+ */
51
+ export declare function repairMaterializedRelease(packageRoot: string, releaseRoot: string): Promise<MaterializedRelease>;
40
52
  /**
41
53
  * Load metadata for a release whose bytes were already certified by the
42
54
  * current parent process or an authenticated live supervisor. Callers must
@@ -10,6 +10,14 @@ import { dependencyReference, materializeDependencyLayer, readCertifiedDependenc
10
10
  const RELEASE_FILE_IO_CONCURRENCY = 32;
11
11
  const certificationReady = new WeakSet();
12
12
  export const RELEASE_MANIFEST_FILENAME = PRODUCT_IDENTITY.manifest.releaseFilename;
13
+ /** Reports that installed package bytes cannot safely reproduce an approved release. */
14
+ export class ReleaseRepairUnavailableError extends Error {
15
+ code = "RELEASE_REPAIR_UNAVAILABLE";
16
+ constructor(message, options = {}) {
17
+ super(message, options);
18
+ this.name = "ReleaseRepairUnavailableError";
19
+ }
20
+ }
13
21
  export async function materializeRelease(packageRoot, dataDir, options = {}) {
14
22
  const payload = await discoverReleasePayload(packageRoot, {
15
23
  onSourceRead: (path, bytes) => options.onOperation?.({ operation: "source-read", path, bytes }),
@@ -71,7 +79,7 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
71
79
  const releaseRoot = resolveWithin(storeRoot, identity.releaseId);
72
80
  if (await lstat(releaseRoot).catch(() => null)) {
73
81
  await rm(candidate, { recursive: true, force: true });
74
- return certificationReadyRelease(await verifyMaterializedRelease(releaseRoot, identity));
82
+ return certificationReadyRelease(await verifyOrRepairMaterializedRelease(payload.packageRoot, releaseRoot, identity));
75
83
  }
76
84
  await writeFile(resolve(candidate, RELEASE_MANIFEST_FILENAME), JSON.stringify(identity, null, 2), { mode: 0o400, flag: "wx" });
77
85
  try {
@@ -81,7 +89,7 @@ export async function materializeRelease(packageRoot, dataDir, options = {}) {
81
89
  if (!await lstat(releaseRoot).catch(() => null))
82
90
  throw error;
83
91
  await rm(candidate, { recursive: true, force: true });
84
- return certificationReadyRelease(await verifyMaterializedRelease(releaseRoot, identity));
92
+ return certificationReadyRelease(await verifyOrRepairMaterializedRelease(payload.packageRoot, releaseRoot, identity));
85
93
  }
86
94
  return certificationReadyRelease({ ...identity, releaseRoot: await realpath(releaseRoot) });
87
95
  }
@@ -102,6 +110,61 @@ export async function readMaterializedRelease(releaseRoot, selectedStoreRoot) {
102
110
  const manifest = JSON.parse(await readFile(resolve(canonical, RELEASE_MANIFEST_FILENAME), "utf8"));
103
111
  return certificationReadyRelease(await verifyMaterializedRelease(canonical, manifest, selectedStoreRoot));
104
112
  }
113
+ /** Fast presence check before trusted certification permits a release to skip full verification. */
114
+ export async function assertCertifiedFilesPresent(releaseRoot) {
115
+ const canonical = await realpath(releaseRoot);
116
+ const manifest = await readReleaseManifest(canonical);
117
+ await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
118
+ const metadata = await lstat(resolveWithin(canonical, file.path));
119
+ if (!metadata.isFile() || metadata.isSymbolicLink())
120
+ throw new Error(`certified release file is missing: ${file.path}`);
121
+ });
122
+ }
123
+ /**
124
+ * Restore changed or missing product payload files from the installed package without replacing the
125
+ * immutable release directory. Dependency layers retain their own verification and repair policy.
126
+ */
127
+ export async function repairMaterializedRelease(packageRoot, releaseRoot) {
128
+ const canonical = await realpath(releaseRoot);
129
+ let manifest;
130
+ let payload;
131
+ try {
132
+ manifest = await readReleaseManifest(canonical);
133
+ payload = await discoverReleasePayload(packageRoot);
134
+ assertCurrentLaunchContract(payload);
135
+ }
136
+ catch (error) {
137
+ throw repairUnavailable("release repair metadata is unavailable", error);
138
+ }
139
+ if (payload.packageVersion !== manifest.packageVersion) {
140
+ throw repairUnavailable(`installed package version ${payload.packageVersion} does not match release ${manifest.packageVersion}`);
141
+ }
142
+ const payloadPaths = new Set(payload.paths.filter(path => !path.startsWith("node_modules/")));
143
+ try {
144
+ await mapWithConcurrency(manifest.files, RELEASE_FILE_IO_CONCURRENCY, async (file) => {
145
+ const destination = resolveWithin(canonical, file.path);
146
+ if (!await certifiedFileDiffers(destination, file))
147
+ return;
148
+ if (!payloadPaths.has(file.path))
149
+ throw repairUnavailable(`installed package does not carry ${file.path}`);
150
+ const source = resolveWithin(payload.packageRoot, file.path);
151
+ const sourceMetadata = await lstat(source);
152
+ if (!sourceMetadata.isFile() || sourceMetadata.isSymbolicLink())
153
+ throw repairUnavailable(`installed package file is not regular: ${file.path}`);
154
+ const bytes = payload.cachedFiles.get(file.path) ?? await readFile(source);
155
+ if (bytes.length !== file.bytes || createHash("sha256").update(bytes).digest("hex") !== file.sha256) {
156
+ throw repairUnavailable(`installed package bytes do not match release file ${file.path}`);
157
+ }
158
+ await replaceReleaseFile(destination, bytes, file);
159
+ });
160
+ return await verifyMaterializedRelease(canonical, manifest);
161
+ }
162
+ catch (error) {
163
+ if (error instanceof ReleaseRepairUnavailableError)
164
+ throw error;
165
+ throw repairUnavailable("release repair could not restore the certified payload", error);
166
+ }
167
+ }
105
168
  /**
106
169
  * Load metadata for a release whose bytes were already certified by the
107
170
  * current parent process or an authenticated live supervisor. Callers must
@@ -197,6 +260,65 @@ async function verifyFile(root, file, options) {
197
260
  if (digest !== file.sha256)
198
261
  throw new Error(`release file digest mismatch: ${file.path}`);
199
262
  }
263
+ async function verifyOrRepairMaterializedRelease(packageRoot, releaseRoot, expected) {
264
+ try {
265
+ return await verifyMaterializedRelease(releaseRoot, expected);
266
+ }
267
+ catch {
268
+ await repairMaterializedRelease(packageRoot, releaseRoot);
269
+ return await verifyMaterializedRelease(releaseRoot, expected);
270
+ }
271
+ }
272
+ async function readReleaseManifest(releaseRoot) {
273
+ const manifest = JSON.parse(await readFile(resolveWithin(releaseRoot, RELEASE_MANIFEST_FILENAME), "utf8"));
274
+ validateManifest(manifest);
275
+ if (releaseRoot.split(sep).at(-1) !== manifest.releaseId)
276
+ throw new Error(`release directory does not match identity ${manifest.releaseId}`);
277
+ return manifest;
278
+ }
279
+ async function certifiedFileDiffers(path, file) {
280
+ const metadata = await lstat(path).catch((error) => {
281
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
282
+ return null;
283
+ throw error;
284
+ });
285
+ if (metadata === null || !metadata.isFile() || metadata.isSymbolicLink() || metadata.size !== file.bytes)
286
+ return true;
287
+ if ((metadata.mode & 0o222) !== 0 || ((metadata.mode & 0o111) !== 0) !== file.executable)
288
+ return true;
289
+ return createHash("sha256").update(await readFile(path)).digest("hex") !== file.sha256;
290
+ }
291
+ async function replaceReleaseFile(path, bytes, file) {
292
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
293
+ const temporary = resolve(dirname(path), `.a1-repair-${randomUUID()}`);
294
+ const mode = file.executable ? 0o500 : 0o400;
295
+ let existing = null;
296
+ try {
297
+ await writeFile(temporary, bytes, { flag: "wx", mode: 0o600 });
298
+ existing = await lstat(path).catch((error) => {
299
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
300
+ return null;
301
+ throw error;
302
+ });
303
+ if (existing !== null) {
304
+ if (!existing.isFile() || existing.isSymbolicLink())
305
+ throw repairUnavailable(`release file cannot be safely replaced: ${file.path}`);
306
+ // Platform: Windows refuses rename-over for a read-only destination.
307
+ await chmod(path, 0o600);
308
+ }
309
+ await rename(temporary, path);
310
+ await chmod(path, mode);
311
+ }
312
+ catch (error) {
313
+ await rm(temporary, { force: true }).catch(() => { });
314
+ if (existing !== null)
315
+ await chmod(path, mode).catch(() => { });
316
+ throw error;
317
+ }
318
+ }
319
+ function repairUnavailable(message, cause) {
320
+ return new ReleaseRepairUnavailableError(message, cause === undefined ? {} : { cause });
321
+ }
200
322
  function certificationReadyRelease(release) {
201
323
  certificationReady.add(release);
202
324
  return release;
@@ -0,0 +1,48 @@
1
+ import type { UpdateChannel } from "./types.js";
2
+ import { type UpdateTransaction } from "./update-transaction.js";
3
+ export declare const UPDATE_EXECUTION_LEASE_FILENAME = "update-execution.json";
4
+ declare const LEASE_SCHEMA = 1;
5
+ export type UpdateExecutionMode = "manual" | "automatic";
6
+ export interface UpdateExecutionOwner {
7
+ readonly schema: typeof LEASE_SCHEMA;
8
+ readonly ownerId: string;
9
+ readonly pid: number;
10
+ readonly pidStartIdentity: string;
11
+ readonly packageRoot: string;
12
+ readonly channel: UpdateChannel;
13
+ readonly targetVersion: string;
14
+ readonly mode: UpdateExecutionMode;
15
+ readonly acquiredAt: string;
16
+ }
17
+ export type UpdateExecutionLeaseResult = {
18
+ readonly status: "acquired";
19
+ readonly owner: UpdateExecutionOwner;
20
+ release(): Promise<void>;
21
+ } | {
22
+ readonly status: "busy";
23
+ readonly owner: UpdateExecutionOwner;
24
+ }
25
+ /** A dead owner whose active transaction names another target; only a manual update may settle it. */
26
+ | {
27
+ readonly status: "interrupted";
28
+ readonly owner: UpdateExecutionOwner;
29
+ readonly transaction: UpdateTransaction;
30
+ };
31
+ /**
32
+ * Serializes every package replacement for one A1 data root. A dead owner is moved aside before a
33
+ * new exclusive create, so concurrent recovery attempts cannot both acquire the installation.
34
+ */
35
+ export declare function acquireUpdateExecutionLease(input: {
36
+ readonly dataDir: string;
37
+ readonly packageRoot: string;
38
+ readonly channel: UpdateChannel;
39
+ readonly targetVersion: string;
40
+ readonly mode: UpdateExecutionMode;
41
+ }): Promise<UpdateExecutionLeaseResult>;
42
+ export declare function readUpdateExecutionOwner(dataDir: string): Promise<UpdateExecutionOwner | null>;
43
+ /**
44
+ * Removes a dead owner's lease once its transaction has been settled. A lease that another owner
45
+ * holds by now, or whose process turned out to be alive, is left alone.
46
+ */
47
+ export declare function reclaimDeadLease(dataDir: string, owner: UpdateExecutionOwner): Promise<void>;
48
+ export {};
@@ -0,0 +1,161 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+ import lockfile from "proper-lockfile";
5
+ import { processIsAlive } from "./process-cleanup.js";
6
+ import { UpdateTransactionStore } from "./update-transaction.js";
7
+ export const UPDATE_EXECUTION_LEASE_FILENAME = "update-execution.json";
8
+ const LEASE_SCHEMA = 1;
9
+ const MAX_LEASE_BYTES = 8_192;
10
+ /**
11
+ * Serializes every package replacement for one A1 data root. A dead owner is moved aside before a
12
+ * new exclusive create, so concurrent recovery attempts cannot both acquire the installation.
13
+ */
14
+ export async function acquireUpdateExecutionLease(input) {
15
+ const path = resolve(input.dataDir, UPDATE_EXECUTION_LEASE_FILENAME);
16
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
17
+ const releaseAcquisition = await lockfile.lock(path, {
18
+ realpath: false,
19
+ stale: 60_000,
20
+ update: 20_000,
21
+ retries: { retries: 20, factor: 1.2, minTimeout: 20, maxTimeout: 100, randomize: true },
22
+ });
23
+ const owner = {
24
+ schema: LEASE_SCHEMA,
25
+ ownerId: randomUUID(),
26
+ pid: process.pid,
27
+ pidStartIdentity: currentProcessStartIdentity(),
28
+ packageRoot: resolve(input.packageRoot),
29
+ channel: input.channel,
30
+ targetVersion: input.targetVersion,
31
+ mode: input.mode,
32
+ acquiredAt: new Date().toISOString(),
33
+ };
34
+ try {
35
+ for (let attempt = 0; attempt < 4; attempt += 1) {
36
+ try {
37
+ const file = await open(path, "wx", 0o600);
38
+ try {
39
+ await file.writeFile(`${JSON.stringify(owner)}\n`);
40
+ await file.sync();
41
+ }
42
+ finally {
43
+ await file.close();
44
+ }
45
+ return {
46
+ status: "acquired",
47
+ owner,
48
+ release: async () => await releaseLease(path, owner),
49
+ };
50
+ }
51
+ catch (error) {
52
+ if (!hasCode(error, "EEXIST"))
53
+ throw error;
54
+ }
55
+ const existing = await readUpdateExecutionOwner(input.dataDir);
56
+ if (existing === null)
57
+ continue;
58
+ // Security: an uninspectable or reused pid stays conservative. A process known to be absent is
59
+ // reclaimed only when its durable transaction agrees with both owners; a transaction that names
60
+ // another target is handed to a manual caller to settle, while automatic work fails closed.
61
+ if (processIsAlive(existing.pid))
62
+ return { status: "busy", owner: existing };
63
+ const foreign = await foreignActiveTransaction(input.dataDir, existing, input);
64
+ if (foreign !== null) {
65
+ return input.mode === "manual"
66
+ ? { status: "interrupted", owner: existing, transaction: foreign }
67
+ : { status: "busy", owner: existing };
68
+ }
69
+ const stale = `${path}.stale-${existing.ownerId}-${randomUUID()}`;
70
+ try {
71
+ await rename(path, stale);
72
+ await rm(stale, { force: true });
73
+ }
74
+ catch (error) {
75
+ if (!hasCode(error, "ENOENT"))
76
+ throw error;
77
+ }
78
+ }
79
+ const existing = await readUpdateExecutionOwner(input.dataDir);
80
+ if (existing !== null)
81
+ return { status: "busy", owner: existing };
82
+ throw new Error("could not acquire update execution lease");
83
+ }
84
+ finally {
85
+ await releaseAcquisition();
86
+ }
87
+ }
88
+ export async function readUpdateExecutionOwner(dataDir) {
89
+ const path = resolve(dataDir, UPDATE_EXECUTION_LEASE_FILENAME);
90
+ let source;
91
+ try {
92
+ source = await readFile(path, "utf8");
93
+ }
94
+ catch (error) {
95
+ if (hasCode(error, "ENOENT"))
96
+ return null;
97
+ throw error;
98
+ }
99
+ if (Buffer.byteLength(source, "utf8") > MAX_LEASE_BYTES)
100
+ throw new Error("update execution lease is oversized");
101
+ const value = JSON.parse(source);
102
+ validateOwner(value);
103
+ return value;
104
+ }
105
+ async function releaseLease(path, owner) {
106
+ const current = await readUpdateExecutionOwner(dirname(path));
107
+ if (current === null || current.ownerId !== owner.ownerId || current.pid !== owner.pid
108
+ || current.pidStartIdentity !== owner.pidStartIdentity)
109
+ return;
110
+ const released = `${path}.released-${owner.ownerId}-${randomUUID()}`;
111
+ try {
112
+ await rename(path, released);
113
+ await rm(released, { force: true });
114
+ }
115
+ catch (error) {
116
+ if (!hasCode(error, "ENOENT"))
117
+ throw error;
118
+ }
119
+ }
120
+ function validateOwner(value) {
121
+ if (value.schema !== LEASE_SCHEMA || typeof value.ownerId !== "string" || value.ownerId.length === 0
122
+ || !Number.isSafeInteger(value.pid) || value.pid <= 0 || typeof value.pidStartIdentity !== "string"
123
+ || typeof value.packageRoot !== "string" || !["stable", "next"].includes(value.channel)
124
+ || typeof value.targetVersion !== "string" || !["manual", "automatic"].includes(value.mode)
125
+ || typeof value.acquiredAt !== "string") {
126
+ throw new Error("invalid update execution lease");
127
+ }
128
+ }
129
+ /**
130
+ * Removes a dead owner's lease once its transaction has been settled. A lease that another owner
131
+ * holds by now, or whose process turned out to be alive, is left alone.
132
+ */
133
+ export async function reclaimDeadLease(dataDir, owner) {
134
+ if (processIsAlive(owner.pid))
135
+ return;
136
+ await releaseLease(resolve(dataDir, UPDATE_EXECUTION_LEASE_FILENAME), owner);
137
+ }
138
+ /** The active transaction when it or the request disagrees with the dead owner, else null. */
139
+ async function foreignActiveTransaction(dataDir, owner, requested) {
140
+ const transaction = await new UpdateTransactionStore(dataDir).read();
141
+ if (transaction?.status !== "active")
142
+ return null;
143
+ const matches = transaction.channel === owner.channel && transaction.targetVersion === owner.targetVersion
144
+ && samePath(transaction.packageRoot, owner.packageRoot)
145
+ && requested.channel === owner.channel && requested.targetVersion === owner.targetVersion
146
+ && samePath(requested.packageRoot, owner.packageRoot);
147
+ return matches ? null : transaction;
148
+ }
149
+ function samePath(left, right) {
150
+ const canonicalLeft = resolve(left);
151
+ const canonicalRight = resolve(right);
152
+ return process.platform === "win32"
153
+ ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase()
154
+ : canonicalLeft === canonicalRight;
155
+ }
156
+ function currentProcessStartIdentity() {
157
+ return `${process.pid}:${Math.floor(Date.now() - process.uptime() * 1_000)}`;
158
+ }
159
+ function hasCode(error, code) {
160
+ return error instanceof Error && "code" in error && error.code === code;
161
+ }
@@ -1,6 +1,8 @@
1
1
  import type { UpdateChannel } from "./types.js";
2
+ import { type RegistryFetcher } from "./latest-release.js";
2
3
  import { type UpdateActivationPhase, type UpdateMaterializationProgress } from "./update-activation.js";
3
4
  import { type UpdateRecoveryState, type UpdateTransaction, type UpdateTransactionPhase } from "./update-transaction.js";
5
+ import { type UpdateExecutionOwner } from "./update-execution-lease.js";
4
6
  import { resolveNpmCli, type ProtectedPackageReplacementResult } from "./update-recovery.js";
5
7
  export declare const PRODUCT_PACKAGE: string;
6
8
  export type { UpdateChannel } from "./types.js";
@@ -61,10 +63,17 @@ export interface SelfUpdateOptions {
61
63
  channel?: UpdateChannel;
62
64
  /** A specific preview to install, named by its development number or full version. */
63
65
  target?: string;
66
+ /** Internal exact channel target selected by the automatic update probe. */
67
+ resolvedTarget?: string;
68
+ mode?: "manual" | "automatic";
69
+ onLeaseAcquired?: (owner: UpdateExecutionOwner) => void;
70
+ onLeaseBusy?: (owner: UpdateExecutionOwner) => void;
64
71
  environment?: NodeJS.ProcessEnv;
65
72
  fileSystem?: UpdateFileSystem;
66
73
  output?: UpdateOutput;
67
74
  runner?: UpdateProcessRunner;
75
+ /** Reads the registry's package document when a preview is named; defaults to a bounded fetch. */
76
+ fetcher?: RegistryFetcher;
68
77
  lifecycle?: UpdateLifecycleCoordinator;
69
78
  transactionStore?: UpdateTransactionJournal;
70
79
  /** Test seam for protected global package replacement. */
@@ -80,6 +89,7 @@ export interface SelfUpdateOptions {
80
89
  export type { UpdateActivationPhase, UpdateMaterializationProgress } from "./update-activation.js";
81
90
  export interface UpdateLifecycleCoordinator {
82
91
  targetIsActive(targetVersion: string): Promise<boolean>;
92
+ verifyAutomaticSafety?(): Promise<void>;
83
93
  shutdownVerifiedOwners(targetVersion: string): Promise<{
84
94
  priorActiveVersion: string | null;
85
95
  }>;
@@ -123,6 +133,7 @@ export interface UpdatePackageReplacementInput {
123
133
  * The update therefore keeps checking for this long before it calls the package locked.
124
134
  */
125
135
  export declare const PACKAGE_UNLOCK_PATIENCE_MS = 15000;
136
+ export declare const UPDATE_ALREADY_RUNNING_EXIT_CODE = 75;
126
137
  /** Whether a rename failure means something holds the tree, as opposed to the tree being wrong. */
127
138
  export declare function isPackageLockError(error: unknown): boolean;
128
139
  /** What the user reads when the package stayed held for the whole patience window. */
@@ -131,15 +142,9 @@ export declare function lockedPackageDiagnostic(packageRoot: string, waitedMs: n
131
142
  export declare const CHILD_DIAGNOSTIC_LIMIT = 8000;
132
143
  export declare function createNpmProcessRunner(platform?: NodeJS.Platform): UpdateProcessRunner;
133
144
  export type UpdateOwnershipAction = "clean-dead-record" | "leave-running" | "end-session";
134
- /**
135
- * What an update does about the owner it found. A cohort running from retained immutable
136
- * content keeps its work: the installation replaces files it does not read, and it listens on
137
- * its own endpoint. A cohort running from the mutable installation is the one exception,
138
- * because that installation is exactly what is about to be replaced under it.
139
- */
140
145
  export declare function planUpdateOwnership(ownership: "live-verified" | "dead", runsFromRetainedRelease: boolean): UpdateOwnershipAction;
141
146
  export declare function createUpdateLifecycleCoordinator(environment?: NodeJS.ProcessEnv, fileSystem?: UpdateFileSystem, output?: UpdateOutput, patience?: UpdateUnlockPatience): UpdateLifecycleCoordinator;
142
147
  /** Renders the terminal-only update meter; the visual preview shares this exact frame. */
143
- export declare function renderUpdateProgressBar(percent: number): string;
148
+ export declare function renderUpdateProgressBar(percent: number, accent?: string): string;
144
149
  export declare function runSelfUpdate(options: SelfUpdateOptions): Promise<number>;
145
150
  export declare function assertUpdatePerformanceBudget(evidence: UpdatePerformanceEvidence, maximumPostNpmDurationMs?: number): void;