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

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 (296) hide show
  1. package/bin/ui.js +3 -3
  2. package/dist/app/session-shell/clipboard-diagnostics.d.ts +2 -2
  3. package/dist/app/session-shell/clipboard-image.d.ts +0 -1
  4. package/dist/app/session-shell/clipboard-image.js +1 -1
  5. package/dist/app/session-shell/dialog-coordinator.d.ts +6 -5
  6. package/dist/app/session-shell/dialog-coordinator.js +3 -4
  7. package/dist/app/session-shell/dock-chrome.d.ts +11 -6
  8. package/dist/app/session-shell/dock-chrome.js +5 -8
  9. package/dist/app/session-shell/editor-hyperlink-budget.d.ts +0 -2
  10. package/dist/app/session-shell/editor-hyperlink-budget.js +1 -1
  11. package/dist/app/session-shell/exit-notice.d.ts +2 -2
  12. package/dist/app/session-shell/exit-notice.js +1 -1
  13. package/dist/app/session-shell/extension-surface.d.ts +8 -7
  14. package/dist/app/session-shell/helper-pool.d.ts +3 -4
  15. package/dist/app/session-shell/helper-pool.js +1 -1
  16. package/dist/app/session-shell/image-preparation-client.d.ts +4 -3
  17. package/dist/app/session-shell/image-preparation.d.ts +0 -2
  18. package/dist/app/session-shell/image-preparation.js +2 -2
  19. package/dist/app/session-shell/image-source.d.ts +2 -3
  20. package/dist/app/session-shell/image-source.js +2 -2
  21. package/dist/app/session-shell/input-surface-switch.d.ts +9 -7
  22. package/dist/app/session-shell/input-surface-switch.js +1 -2
  23. package/dist/app/session-shell/login-interaction.d.ts +5 -4
  24. package/dist/app/session-shell/login-interaction.js +2 -3
  25. package/dist/app/session-shell/paste-executor.d.ts +4 -8
  26. package/dist/app/session-shell/paste-executor.js +2 -2
  27. package/dist/app/session-shell/paste-protocol.d.ts +2 -2
  28. package/dist/app/session-shell/path-chip-presentation.d.ts +0 -5
  29. package/dist/app/session-shell/path-chip-presentation.js +3 -3
  30. package/dist/app/session-shell/prompt-chips.d.ts +8 -7
  31. package/dist/app/session-shell/prompt-history-controller.d.ts +3 -3
  32. package/dist/app/session-shell/prompt-submission.d.ts +5 -2
  33. package/dist/app/session-shell/prompt-submission.js +13 -14
  34. package/dist/app/session-shell/quit-outro-effects.d.ts +3 -3
  35. package/dist/app/session-shell/quit-outro-effects.js +1 -4
  36. package/dist/app/session-shell/quit-outro.d.ts +9 -12
  37. package/dist/app/session-shell/quit-outro.js +11 -11
  38. package/dist/app/session-shell/resource-labels.d.ts +2 -0
  39. package/dist/app/session-shell/resource-labels.js +158 -0
  40. package/dist/app/session-shell/response-copy-protocol.d.ts +3 -2
  41. package/dist/app/session-shell/response-copy-transport.d.ts +2 -14
  42. package/dist/app/session-shell/response-copy-transport.js +1 -1
  43. package/dist/app/session-shell/session-lifecycle.d.ts +2 -1
  44. package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
  45. package/dist/app/session-shell/session-prompt-history.js +80 -0
  46. package/dist/app/session-shell/session-selectors.d.ts +46 -0
  47. package/dist/app/session-shell/session-selectors.js +269 -0
  48. package/dist/app/session-shell/session-shell-root.d.ts +76 -113
  49. package/dist/app/session-shell/session-shell-root.js +103 -329
  50. package/dist/app/session-shell/session-shell.d.ts +14 -37
  51. package/dist/app/session-shell/session-shell.js +177 -612
  52. package/dist/app/session-shell/session-viewport-controller.d.ts +9 -16
  53. package/dist/app/session-shell/session-viewport-controller.js +42 -320
  54. package/dist/app/session-shell/settings-bindings.d.ts +25 -3
  55. package/dist/app/session-shell/settings-bindings.js +26 -2
  56. package/dist/app/session-shell/share-operation.d.ts +3 -3
  57. package/dist/app/session-shell/share-operation.js +4 -6
  58. package/dist/app/session-shell/shell-clipboard.d.ts +59 -0
  59. package/dist/app/session-shell/shell-clipboard.js +119 -0
  60. package/dist/app/session-shell/shell-editor-factory.d.ts +8 -4
  61. package/dist/app/session-shell/shell-editor-factory.js +13 -15
  62. package/dist/app/session-shell/shell-host.d.ts +16 -17
  63. package/dist/app/session-shell/shell-host.js +18 -19
  64. package/dist/app/session-shell/shell-layout.d.ts +2 -2
  65. package/dist/app/session-shell/shell-prompt-suggestions.d.ts +26 -0
  66. package/dist/app/session-shell/shell-prompt-suggestions.js +48 -0
  67. package/dist/app/session-shell/suggestion-surface.d.ts +1 -6
  68. package/dist/app/session-shell/suggestion-surface.js +1 -1
  69. package/dist/app/session-shell/system-clipboard.d.ts +2 -14
  70. package/dist/app/session-shell/system-clipboard.js +2 -7
  71. package/dist/app/session-shell/transcript-document.d.ts +11 -8
  72. package/dist/app/session-shell/transcript-document.js +13 -14
  73. package/dist/app/session-shell/viewport-layout.d.ts +2 -2
  74. package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
  75. package/dist/app/session-shell/viewport-pointer.js +361 -0
  76. package/dist/app/session-shell/workflow-output.d.ts +5 -3
  77. package/dist/app/session-shell/workflow-output.js +13 -15
  78. package/dist/cli/package-messages.d.ts +0 -1
  79. package/dist/cli/package-messages.js +1 -1
  80. package/dist/cli/session-context.d.ts +1 -1
  81. package/dist/cli/uninstall.d.ts +3 -2
  82. package/dist/composition/owned-settings.d.ts +2 -1
  83. package/dist/composition/owned-settings.js +0 -3
  84. package/dist/composition/owned-ui.d.ts +1 -1
  85. package/dist/composition/owned-ui.js +77 -73
  86. package/dist/composition/reference-selection-options.d.ts +1 -1
  87. package/dist/composition/reference-selection-options.js +1 -1
  88. package/dist/composition/session-runtime-registry.d.ts +2 -1
  89. package/dist/composition/settings-route-host.js +3 -10
  90. package/dist/composition/settings-search-input.d.ts +2 -1
  91. package/dist/composition/startup-route.d.ts +2 -2
  92. package/dist/composition/startup-route.js +2 -2
  93. package/dist/contracts/owned-ui/extension-ui.d.ts +2 -1
  94. package/dist/contracts/owned-ui/index.d.ts +2 -1
  95. package/dist/contracts/owned-ui/index.js +1 -1
  96. package/dist/contracts/owned-ui/session-presenters.d.ts +3 -0
  97. package/dist/contracts/owned-ui/shell-components.d.ts +543 -0
  98. package/dist/contracts/owned-ui/validation.d.ts +1 -11
  99. package/dist/contracts/owned-ui/validation.js +8 -104
  100. package/dist/contracts/presentation/component.d.ts +17 -0
  101. package/dist/contracts/presentation/component.js +8 -0
  102. package/dist/contracts/presentation/index.d.ts +6 -15
  103. package/dist/contracts/presentation/index.js +2 -0
  104. package/dist/contracts/presentation/terminal.d.ts +284 -0
  105. package/dist/contracts/presentation/terminal.js +1 -0
  106. package/dist/contracts/presentation/theme.d.ts +46 -0
  107. package/dist/contracts/presentation/theme.js +1 -0
  108. package/dist/features/launch/development-launch.js +1 -1
  109. package/dist/features/launch/runtime-selection.d.ts +2 -2
  110. package/dist/features/launch/runtime-selection.js +2 -2
  111. package/dist/features/owned-ui/project-trust-prompt.d.ts +2 -2
  112. package/dist/features/owned-ui/project-trust-prompt.js +1 -1
  113. package/dist/features/owned-ui/release-note-state.d.ts +2 -1
  114. package/dist/features/owned-ui/settings-app.js +49 -425
  115. package/dist/features/owned-ui/settings-edits.d.ts +31 -0
  116. package/dist/features/owned-ui/settings-edits.js +139 -0
  117. package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
  118. package/dist/features/owned-ui/settings-model-dialog.js +194 -0
  119. package/dist/features/owned-ui/settings-scroll-rail.d.ts +29 -0
  120. package/dist/features/owned-ui/settings-scroll-rail.js +79 -0
  121. package/dist/features/owned-ui/settings-search-input.d.ts +2 -1
  122. package/dist/features/prompt-history/service.d.ts +3 -2
  123. package/dist/foundation/lifecycle/claim-gate.d.ts +2 -1
  124. package/dist/foundation/release/automatic-update.d.ts +5 -14
  125. package/dist/foundation/release/automatic-update.js +15 -22
  126. package/dist/foundation/release/bootstrap.js +4 -4
  127. package/dist/foundation/release/dependency-certification.d.ts +0 -1
  128. package/dist/foundation/release/dependency-certification.js +1 -1
  129. package/dist/foundation/release/dependency-layer.js +3 -3
  130. package/dist/foundation/release/early-accent.d.ts +2 -2
  131. package/dist/foundation/release/early-accent.js +1 -1
  132. package/dist/foundation/release/index.d.ts +2 -2
  133. package/dist/foundation/release/index.js +1 -1
  134. package/dist/foundation/release/latest-release.d.ts +3 -18
  135. package/dist/foundation/release/latest-release.js +11 -11
  136. package/dist/foundation/release/managed-path.d.ts +10 -0
  137. package/dist/foundation/release/managed-path.js +26 -0
  138. package/dist/foundation/release/ordinary-launch-selection.d.ts +1 -1
  139. package/dist/foundation/release/ordinary-launch-selection.js +1 -1
  140. package/dist/foundation/release/release-gc.d.ts +3 -3
  141. package/dist/foundation/release/release-gc.js +10 -12
  142. package/dist/foundation/release/{cohort-state.d.ts → release-state.d.ts} +23 -23
  143. package/dist/foundation/release/{cohort-state.js → release-state.js} +8 -8
  144. package/dist/foundation/release/restart-certification.js +3 -4
  145. package/dist/foundation/release/update-activation.js +3 -3
  146. package/dist/foundation/release/update-execution-lease.d.ts +2 -3
  147. package/dist/foundation/release/update-execution-lease.js +2 -8
  148. package/dist/foundation/release/update-installer.d.ts +45 -0
  149. package/dist/foundation/release/update-installer.js +57 -0
  150. package/dist/foundation/release/update-launch.d.ts +2 -2
  151. package/dist/foundation/release/update-recovery.js +2 -9
  152. package/dist/foundation/release/update.d.ts +4 -25
  153. package/dist/foundation/release/update.js +164 -183
  154. package/dist/foundation/startup/startup-budget.d.ts +5 -5
  155. package/dist/foundation/startup/startup-budget.js +2 -2
  156. package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
  157. package/dist/integrations/pi/components/bracketed-paste.js +123 -0
  158. package/dist/integrations/pi/components/editor-interaction.d.ts +4 -12
  159. package/dist/integrations/pi/components/index.d.ts +15 -9
  160. package/dist/integrations/pi/components/index.js +9 -5
  161. package/dist/integrations/pi/components/lazy-selectors.d.ts +2 -10
  162. package/dist/integrations/pi/components/lazy-selectors.js +1 -1
  163. package/dist/integrations/pi/components/modal-frame.d.ts +5 -22
  164. package/dist/integrations/pi/components/modal-frame.js +3 -23
  165. package/dist/integrations/pi/components/owned-editor-ux.d.ts +4 -3
  166. package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
  167. package/dist/integrations/pi/components/prompt-input-port.d.ts +2 -19
  168. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -1
  169. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +3 -2
  170. package/dist/integrations/pi/components/shell-footer-status.d.ts +2 -2
  171. package/dist/integrations/pi/components/shell-footer-status.js +3 -2
  172. package/dist/integrations/pi/components/shell-presenters-info.d.ts +6 -5
  173. package/dist/integrations/pi/components/shell-presenters-info.js +2 -1
  174. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +2 -1
  175. package/dist/integrations/pi/components/shell-shared-facade.d.ts +33 -227
  176. package/dist/integrations/pi/components/shell-shared-facade.js +8 -1
  177. package/dist/integrations/pi/components/skills-command.d.ts +1 -9
  178. package/dist/integrations/pi/components/skills-command.js +5 -5
  179. package/dist/integrations/pi/components/theme.d.ts +9 -2
  180. package/dist/integrations/pi/components/theme.js +17 -2
  181. package/dist/integrations/pi/components/thinking-selector-dialog.d.ts +2 -1
  182. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  183. package/dist/integrations/pi/engine/adapter.d.ts +4 -13
  184. package/dist/integrations/pi/engine/adapter.js +9 -61
  185. package/dist/integrations/pi/engine/changelog.d.ts +2 -2
  186. package/dist/integrations/pi/engine/changelog.js +3 -3
  187. package/dist/integrations/pi/engine/compaction-progress.d.ts +2 -3
  188. package/dist/integrations/pi/engine/compaction-progress.js +1 -1
  189. package/dist/integrations/pi/engine/conformance.d.ts +2 -1
  190. package/dist/integrations/pi/engine/default-model-setting.d.ts +0 -2
  191. package/dist/integrations/pi/engine/default-model-setting.js +1 -2
  192. package/dist/integrations/pi/engine/event-delivery.d.ts +2 -1
  193. package/dist/integrations/pi/engine/extension-ui-binding.d.ts +2 -1
  194. package/dist/integrations/pi/engine/host.js +3 -3
  195. package/dist/integrations/pi/engine/message-values.d.ts +0 -1
  196. package/dist/integrations/pi/engine/message-values.js +1 -1
  197. package/dist/integrations/pi/engine/pending-delivery.d.ts +0 -4
  198. package/dist/integrations/pi/engine/pending-delivery.js +3 -3
  199. package/dist/integrations/pi/engine/ports.d.ts +7 -1
  200. package/dist/integrations/pi/engine/ports.js +4 -0
  201. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +2 -1
  202. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +3 -2
  203. package/dist/integrations/pi/engine/provider-authentication.d.ts +1 -1
  204. package/dist/integrations/pi/engine/public-main-entry.d.ts +1 -5
  205. package/dist/integrations/pi/engine/public-main-entry.js +1 -1
  206. package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
  207. package/dist/integrations/pi/engine/repository-metadata.js +189 -0
  208. package/dist/integrations/pi/engine/repository-pr-rest.d.ts +1 -9
  209. package/dist/integrations/pi/engine/repository-pr-rest.js +2 -2
  210. package/dist/integrations/pi/engine/repository-pr.d.ts +1 -3
  211. package/dist/integrations/pi/engine/repository-pr.js +1 -1
  212. package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
  213. package/dist/integrations/pi/engine/session-integration.d.ts +1 -1
  214. package/dist/integrations/pi/engine/session-runtime.d.ts +7 -12
  215. package/dist/integrations/pi/engine/session-runtime.js +21 -178
  216. package/dist/integrations/pi/engine/settings-bridge.d.ts +0 -2
  217. package/dist/integrations/pi/engine/settings-bridge.js +1 -1
  218. package/dist/integrations/pi/engine/settings-port.d.ts +3 -2
  219. package/dist/integrations/pi/engine/transcript-projection.d.ts +2 -1
  220. package/dist/integrations/pi/engine/view-state.d.ts +21 -2
  221. package/dist/integrations/pi/engine/view-state.js +129 -1
  222. package/dist/integrations/pi/engine/workflow-contexts.d.ts +1 -1
  223. package/dist/integrations/pi/engine/workflow-runner.d.ts +1 -2
  224. package/dist/integrations/pi/engine/workflow-runner.js +254 -365
  225. package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
  226. package/dist/integrations/pi/engine/workflow-share.js +168 -0
  227. package/dist/integrations/pi/engine/workflow-support.d.ts +0 -20
  228. package/dist/integrations/pi/engine/workflow-support.js +4 -65
  229. package/dist/integrations/pi/engine/workflows.d.ts +5 -5
  230. package/dist/integrations/pi/session-presenters/components.d.ts +3 -0
  231. package/dist/integrations/pi/session-presenters/components.js +75 -0
  232. package/dist/integrations/pi/session-presenters/index.d.ts +1 -0
  233. package/dist/integrations/pi/session-presenters/index.js +1 -0
  234. package/dist/integrations/pi/session-presenters/presentation.d.ts +3 -0
  235. package/dist/integrations/pi/session-presenters/presentation.js +10 -0
  236. package/dist/integrations/pi/session-presenters/presenters.d.ts +2 -1
  237. package/dist/integrations/pi/session-presenters/presenters.js +3 -1
  238. package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
  239. package/dist/integrations/pi/tui-runtime/adapter.js +56 -174
  240. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +9 -44
  241. package/dist/integrations/pi/tui-runtime/index.d.ts +1 -1
  242. package/dist/integrations/pi/tui-runtime/index.js +1 -0
  243. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +4 -6
  244. package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +4 -10
  245. package/dist/integrations/pi/tui-runtime/overlay-geometry.js +3 -9
  246. package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
  247. package/dist/integrations/pi/tui-runtime/runtime-factory.d.ts +3 -0
  248. package/dist/integrations/pi/tui-runtime/runtime-factory.js +11 -0
  249. package/dist/ui/components/index.d.ts +2 -2
  250. package/dist/ui/components/index.js +1 -1
  251. package/dist/ui/components/selection-copy.d.ts +0 -1
  252. package/dist/ui/components/selection-copy.js +0 -12
  253. package/dist/ui/components/theme.d.ts +4 -19
  254. package/dist/ui/components/theme.js +6 -0
  255. package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
  256. package/dist/ui/components/transcript-viewport-frame.js +314 -0
  257. package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
  258. package/dist/ui/components/transcript-viewport-types.js +1 -0
  259. package/dist/ui/components/transcript-viewport.d.ts +2 -147
  260. package/dist/ui/components/transcript-viewport.js +87 -315
  261. package/dist/ui/settings/declarations.d.ts +2 -3
  262. package/dist/ui/settings/declarations.js +2 -3
  263. package/dist/ui/settings/early-accent.js +1 -1
  264. package/dist/ui/settings/index.d.ts +3 -5
  265. package/dist/ui/settings/index.js +2 -3
  266. package/dist/ui/settings/manager.d.ts +0 -4
  267. package/dist/ui/settings/manager.js +6 -62
  268. package/dist/ui/settings/resolution.d.ts +6 -18
  269. package/dist/ui/settings/resolution.js +4 -103
  270. package/dist/ui/settings/sections.d.ts +0 -4
  271. package/dist/ui/settings/sections.js +1 -1
  272. package/docs/architecture/boundaries.md +2 -2
  273. package/docs/architecture/internal-naming.md +2 -0
  274. package/docs/architecture/project-structure.md +4 -2
  275. package/docs/validation.md +1 -1
  276. package/package.json +3 -4
  277. package/dist/integrations/pi/components/components.d.ts +0 -4
  278. package/dist/integrations/pi/components/components.js +0 -48
  279. package/dist/integrations/pi/components/conformance.d.ts +0 -18
  280. package/dist/integrations/pi/components/conformance.js +0 -70
  281. package/dist/integrations/pi/components/shell-components.d.ts +0 -7
  282. package/dist/integrations/pi/components/shell-components.js +0 -7
  283. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +0 -33
  284. package/dist/integrations/pi/components/upstream/theme/theme-controller.js +0 -114
  285. package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
  286. package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
  287. package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
  288. package/dist/integrations/pi/engine/usage-view.js +0 -56
  289. package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -166
  290. package/dist/ui/settings/migrations.d.ts +0 -19
  291. package/dist/ui/settings/migrations.js +0 -225
  292. package/dist/ui/settings/progress-palette.generated.d.ts +0 -139
  293. package/dist/ui/settings/progress-palette.generated.js +0 -139
  294. /package/dist/{integrations/pi/tui-runtime/contracts.js → contracts/owned-ui/shell-components.js} +0 -0
  295. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.d.ts +0 -0
  296. /package/dist/{foundation/release → contracts/presentation}/progress-palette.generated.js +0 -0
@@ -5,17 +5,16 @@ import crossSpawn from "cross-spawn";
5
5
  import { valid as validSemver } from "semver";
6
6
  import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
7
7
  import { earlyAccentPalette, earlyTerminalAppearance, earlyTerminalColorMode, resolveEarlyAccentColor } from "./early-accent.js";
8
- import { SHARED_PROGRESS_ACCENT_ANSI } from "./progress-palette.generated.js";
8
+ import { SHARED_PROGRESS_ACCENT_ANSI } from "../../contracts/presentation/index.js";
9
9
  import { createRegistryFetcher, invalidateReleaseCheckCache, isNewerRelease, queryPublishedVersions, releaseChannelOf, } from "./latest-release.js";
10
- import { certifyMaterializedRelease } from "./bootstrap.js";
11
10
  import { resolveProductPaths } from "../lifecycle/index.js";
12
- import { CohortStateStore } from "./cohort-state.js";
13
- import { materializeRelease, readMaterializedRelease } from "./release-store.js";
14
- import { scheduleReleaseCleanup } from "./release-gc.js";
11
+ import { ReleaseStateStore } from "./release-state.js";
15
12
  import { UPDATE_ACTIVATION_CONTRACT, activateInstalledRelease, delegateActivation, readActivationContracts, } from "./update-activation.js";
16
13
  import { UpdateTransactionStore } from "./update-transaction.js";
17
14
  import { acquireUpdateExecutionLease, reclaimDeadLease } from "./update-execution-lease.js";
18
- import { npmPrefixForGlobalRoot, removeUpdateRecoveryCapsule, resolveNpmCli, runProtectedPackageReplacement, updateLauncherPaths, updateNpmInstallArguments, } from "./update-recovery.js";
15
+ import { npmPrefixForGlobalRoot, removeUpdateRecoveryCapsule, updateLauncherPaths, } from "./update-recovery.js";
16
+ import { errorMessage, samePath } from "./managed-path.js";
17
+ import { createProtectedUpdateInstaller, } from "./update-installer.js";
19
18
  export const PRODUCT_PACKAGE = PRODUCT_TEXT.packageName;
20
19
  const UPDATE_DIST_TAGS = { stable: "latest", next: "next" };
21
20
  const defaultFileSystem = {
@@ -57,7 +56,7 @@ const defaultOutput = {
57
56
  stderr(message) { process.stderr.write(message); },
58
57
  };
59
58
  /** Bound on the child diagnostics an update keeps: enough for npm's failure reason, never a log. */
60
- export const CHILD_DIAGNOSTIC_LIMIT = 8_000;
59
+ const CHILD_DIAGNOSTIC_LIMIT = 8_000;
61
60
  export function createNpmProcessRunner(platform = process.platform) {
62
61
  return async (command, arguments_, request) => await new Promise((resolvePromise, rejectPromise) => {
63
62
  // Invariant: no child of the updater shares the terminal. The progress bar is the only
@@ -84,7 +83,7 @@ export function createNpmProcessRunner(platform = process.platform) {
84
83
  }
85
84
  export function createUpdateLifecycleCoordinator(environment = process.env, fileSystem = defaultFileSystem, _output = defaultOutput, patience = {}) {
86
85
  const paths = resolveProductPaths(environment);
87
- const stateStore = new CohortStateStore(paths.dataDir);
86
+ const stateStore = new ReleaseStateStore(paths.dataDir);
88
87
  return {
89
88
  async targetIsActive(targetVersion) {
90
89
  const state = await stateStore.read(), activeId = state.references.active;
@@ -410,14 +409,14 @@ function resolveExactAutomaticTarget(target, channel, output) {
410
409
  function reportUnmanagedInstallation(output, packageRoot, activeGlobalRoot) {
411
410
  output.stderr(`${PRODUCT_TEXT.diagnostic(`refused to update automatically because ${packageRoot} could not be verified as an npm-managed ${PRODUCT_PACKAGE} installation; npm's active global package root is ${activeGlobalRoot}. Run npm install --global ${PRODUCT_PACKAGE} to install it manually.`)}\n`);
412
411
  }
413
- async function withUpdateExecutionLease(input, options, output, operation) {
412
+ async function withUpdateExecutionLease(input, options, output, installer, operation) {
414
413
  let lease = await acquireUpdateExecutionLease(input);
415
414
  if (lease.status === "interrupted") {
416
415
  // Rationale: the owner that targeted another version is gone. Its durable transaction is rolled
417
416
  // back to the release that ran before it, so this request proceeds as an ordinary update of its
418
417
  // own target instead of reporting a dead attempt as in progress.
419
418
  const interrupted = lease.owner.targetVersion;
420
- const rollback = await rollbackPriorRelease(input.dataDir, lease.transaction.priorActiveReleaseId)
419
+ const rollback = await installer.rollback(lease.transaction.priorActiveReleaseId)
421
420
  .catch(error => `rollback failed: ${errorMessage(error)}`);
422
421
  const store = new UpdateTransactionStore(input.dataDir);
423
422
  await store.finish("rolled-back", `interrupted ${interrupted} update settled by ${input.targetVersion}; ${rollback}`);
@@ -457,6 +456,19 @@ async function readRunningPackageVersion(packageRoot, fileSystem, output) {
457
456
  }
458
457
  }
459
458
  export async function runSelfUpdate(options) {
459
+ const plan = await planSelfUpdate(options);
460
+ if (typeof plan === "number")
461
+ return plan;
462
+ return await withUpdateExecutionLease({
463
+ dataDir: plan.paths.dataDir,
464
+ packageRoot: plan.installation.packageRoot,
465
+ channel: plan.channel,
466
+ targetVersion: plan.targetVersion,
467
+ mode: plan.mode,
468
+ }, options, plan.output, plan.installer, async () => await runUpdateTransaction(plan));
469
+ }
470
+ /** Resolve the running version, the target and the managed installation, or the exit code that ends the update. */
471
+ async function planSelfUpdate(options) {
460
472
  const fileSystem = options.fileSystem ?? defaultFileSystem;
461
473
  const output = options.output ?? defaultOutput;
462
474
  const runner = options.runner ?? createNpmProcessRunner();
@@ -499,164 +511,155 @@ export async function runSelfUpdate(options) {
499
511
  const managed = await measure("global-root", async () => await resolveManagedNpmInstallation(options.packageRoot, fileSystem, runner, output));
500
512
  if (managed.installation === null)
501
513
  return managed.exitCode;
502
- const { packageRoot, globalRoot, npmCliRoot } = managed.installation;
503
514
  const paths = resolveProductPaths(environment);
504
- const lifecycle = options.lifecycle ?? createUpdateLifecycleCoordinator(environment, fileSystem, output);
505
- const transactionStore = options.transactionStore ?? new UpdateTransactionStore(paths.dataDir);
506
- const mode = options.mode ?? "manual";
507
- const maintenance = options.maintenance ?? (options.lifecycle
508
- ? async () => { }
509
- : async () => await scheduleReleaseCleanup(paths.dataDir, paths));
510
- return await withUpdateExecutionLease({ dataDir: paths.dataDir, packageRoot, channel, targetVersion, mode }, options, output, async () => {
511
- let transaction = await transactionStore.read();
515
+ return {
516
+ output,
517
+ channel,
518
+ targetVersion,
519
+ progress,
520
+ installation: managed.installation,
521
+ paths,
522
+ lifecycle: options.lifecycle ?? createUpdateLifecycleCoordinator(environment, fileSystem, output),
523
+ transactionStore: options.transactionStore ?? new UpdateTransactionStore(paths.dataDir),
524
+ installer: options.installer ?? createProtectedUpdateInstaller(paths, environment),
525
+ mode: options.mode ?? "manual",
526
+ measure,
527
+ now,
528
+ onPhaseTiming: options.onPhaseTiming,
529
+ };
530
+ }
531
+ async function runUpdateTransaction(plan) {
532
+ const { output, progress, installer, lifecycle, transactionStore, targetVersion } = plan;
533
+ const { packageRoot, npmCliRoot } = plan.installation;
534
+ const run = { transaction: await transactionStore.read() };
535
+ try {
536
+ if ((!run.transaction || run.transaction.status === "completed") && await lifecycle.targetIsActive(targetVersion)) {
537
+ await installer.maintenance();
538
+ await transactionStore.clearCompleted();
539
+ output.stdout(`${PRODUCT_TEXT.commandName} is up to date — no update needed.\n`);
540
+ return 0;
541
+ }
512
542
  let npmCli;
513
543
  try {
514
- if ((!transaction || transaction.status === "completed") && await lifecycle.targetIsActive(targetVersion)) {
515
- await maintenance();
516
- await transactionStore.clearCompleted();
517
- output.stdout(`${PRODUCT_TEXT.commandName} is up to date — no update needed.\n`);
518
- return 0;
519
- }
520
- const preflightRequired = options.npmCliResolver !== undefined
521
- || options.packageReplacement === undefined && options.runner === undefined;
522
- if (preflightRequired) {
523
- try {
524
- npmCli = await (options.npmCliResolver ?? resolveNpmCli)(npmCliRoot, environment);
525
- }
526
- catch (error) {
527
- throw new UpdatePreflightFailure(errorMessage(error));
528
- }
529
- }
530
- // Rationale: the bar first appears only after every non-mutating replacement prerequisite passes.
531
- progress.set(3, 15);
532
- const cohortStore = new CohortStateStore(paths.dataDir);
533
- let cohortState = await cohortStore.read();
534
- let priorActiveReleaseId = cohortState.references.active;
535
- if (options.runner === undefined && (priorActiveReleaseId === null || cohortState.releases[priorActiveReleaseId]?.approval !== "approved")) {
536
- // Invariant: protected replacement needs an immutable launch authority that npm cannot rename.
537
- const recoveryRelease = await materializeRelease(packageRoot, paths.dataDir);
538
- await cohortStore.recordCandidate(recoveryRelease);
539
- const diagnostics = await certifyMaterializedRelease(recoveryRelease, paths.dataDir);
540
- await cohortStore.approve(recoveryRelease.releaseId, diagnostics);
541
- await cohortStore.activate(recoveryRelease.releaseId);
542
- cohortState = await cohortStore.read();
543
- priorActiveReleaseId = recoveryRelease.releaseId;
544
- }
545
- transaction = await transactionStore.begin({
546
- channel,
547
- targetVersion,
548
- packageRoot,
549
- priorActiveReleaseId,
550
- });
551
- if (phaseBefore(transaction.phase, "ownership-released")) {
552
- await measure("ownership-release", async () => { await lifecycle.verifyPackageUnlocked(packageRoot); });
553
- transaction = await transactionStore.advance("ownership-released");
554
- }
555
- progress.set(15, 70);
556
- if (phaseBefore(transaction.phase, "package-installed")) {
557
- if (options.packageReplacement || options.runner === undefined) {
558
- const priorReleaseId = transaction.priorActiveReleaseId ?? priorActiveReleaseId;
559
- const recordedPrior = priorReleaseId === null ? undefined : cohortState.releases[priorReleaseId];
560
- if ((!recordedPrior || recordedPrior.approval !== "approved") && !options.packageReplacement) {
561
- throw new Error(PRODUCT_TEXT.diagnostic("cannot protect package replacement because no approved prior release is available"));
562
- }
563
- const priorRelease = recordedPrior ?? {
564
- releaseId: priorReleaseId ?? "injected-prior-release",
565
- releaseRoot: packageRoot,
566
- contentDigest: "0".repeat(64),
567
- };
568
- const replacementTransaction = transaction;
569
- const replacement = await measure("npm-install", async () => await (options.packageReplacement ?? runProtectedPackageReplacement)({
570
- dataDir: paths.dataDir,
571
- globalRoot,
572
- npmCliRoot,
573
- ...(npmCli === undefined ? {} : { npmCli }),
574
- packageRoot,
575
- transaction: replacementTransaction,
576
- priorRelease,
577
- output,
578
- environment,
579
- onRecoveryState: async (recovery) => {
580
- if (transactionStore.setRecovery)
581
- transaction = await transactionStore.setRecovery(recovery) ?? transaction;
582
- },
583
- }));
584
- // Rationale: npm's own text explains a failed replacement and says nothing about a
585
- // successful one, so it reaches the terminal only when the installation did not happen.
586
- if (replacement.outcome !== "installed")
587
- reportChildDiagnostics(output, replacement);
588
- if (replacement.outcome === "installed")
589
- transaction = await transactionStore.advance("package-installed");
590
- if (replacement.cancelled) {
591
- progress.clear();
592
- output.stderr(`${PRODUCT_TEXT.diagnostic("update cancelled safely; the launcher is available and the transaction can be resumed.")}\n`);
593
- return 130;
594
- }
595
- if (replacement.outcome !== "installed") {
596
- throw new UpdateFailure(unsuccessfulCode(replacement.npmExitCode), `npm exited with status ${formatExitCode(replacement.npmExitCode)}`);
597
- }
598
- }
599
- else {
600
- const installation = await measure("npm-install", async () => await runNpm(runner, updateNpmInstallArguments(globalRoot, targetVersion), true, output, "start the global npm installation", false));
601
- if (installation.result === null)
602
- throw new UpdateFailure(installation.exitCode, "npm process failed");
603
- if (installation.result.code !== 0) {
604
- reportChildDiagnostics(output, installation.result);
605
- throw new UpdateFailure(unsuccessfulCode(installation.result.code), `npm exited with status ${formatExitCode(installation.result.code)}`);
606
- }
607
- transaction = await transactionStore.advance("package-installed");
608
- }
609
- }
610
- progress.set(70, MATERIALIZE_PROGRESS.from);
611
- let activationPhaseStartedAt = now();
612
- const recordActivationPhase = async (phase) => {
613
- options.onPhaseTiming?.({ phase, durationMs: Math.max(0, now() - activationPhaseStartedAt) });
614
- transaction = await transactionStore.advance(phase);
615
- progress.set(ACTIVATION_PROGRESS[phase].at, ACTIVATION_PROGRESS[phase].creepTo);
616
- activationPhaseStartedAt = now();
617
- };
618
- await lifecycle.activateInstalled(packageRoot, targetVersion, recordActivationPhase, ({ completed, total }) => progress.set(MATERIALIZE_PROGRESS.from
619
- + (MATERIALIZE_PROGRESS.to - MATERIALIZE_PROGRESS.from) * (total > 0 ? Math.min(1, completed / total) : 0)), state => {
620
- if (state === "started") {
621
- activationPhaseStartedAt = now();
622
- progress.set(98, 99);
623
- }
624
- else {
625
- options.onPhaseTiming?.({ phase: "warmup", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
626
- activationPhaseStartedAt = now();
627
- progress.set(99);
628
- }
629
- });
630
- const transactionStartedAt = now();
631
- if (transaction.recovery?.capsulePath)
632
- await removeUpdateRecoveryCapsule(paths.dataDir, transaction.transactionId);
633
- // Invariant: successful output follows the durable cleanup disposition. Slow recursive
634
- // removal belongs to the detached worker started by this maintenance coordinator.
635
- await maintenance();
636
- // Concurrency: this is the launch cutover. No failure-prone update work follows success.
637
- await transactionStore.finish("completed");
638
- await Promise.all([transactionStore.clearCompleted(), invalidateReleaseCheckCache(paths.configDir)]).catch(() => undefined);
639
- options.onPhaseTiming?.({ phase: "transaction-complete", durationMs: Math.max(0, now() - transactionStartedAt) });
640
- progress.finish();
641
- output.stdout(`${PRODUCT_TEXT.commandName} updated successfully to ${targetVersion}\n`);
642
- return 0;
544
+ npmCli = await installer.preflight(npmCliRoot);
643
545
  }
644
546
  catch (error) {
645
- progress.clear();
646
- const message = errorMessage(error);
647
- if (error instanceof UpdatePreflightFailure) {
648
- output.stderr(`${PRODUCT_TEXT.diagnostic(`update could not start safely: ${message}. Nothing was changed. Retry with npm x -y -- ${PRODUCT_PACKAGE}-install`)}\n`);
649
- return 1;
650
- }
651
- const rollback = options.lifecycle
652
- ? "previous test lifecycle retained"
653
- : await rollbackPriorRelease(paths.dataDir, transaction?.priorActiveReleaseId ?? null).catch(rollbackError => `rollback failed: ${errorMessage(rollbackError)}`);
654
- if (transaction)
655
- await transactionStore.finish(rollback === "rolled back" ? "rolled-back" : "failed", `${message}; ${rollback}`);
656
- output.stderr(updateFailureLine(message, rollback, transactionStore.path));
657
- return error instanceof UpdateFailure ? error.exitCode : 1;
547
+ throw new UpdatePreflightFailure(errorMessage(error));
548
+ }
549
+ // Rationale: the bar first appears only after every non-mutating replacement prerequisite passes.
550
+ progress.set(3, 15);
551
+ const protection = await installer.protectPriorRelease(packageRoot);
552
+ run.transaction = await transactionStore.begin({
553
+ channel: plan.channel,
554
+ targetVersion,
555
+ packageRoot,
556
+ priorActiveReleaseId: protection.priorActiveReleaseId,
557
+ });
558
+ if (phaseBefore(run.transaction.phase, "ownership-released")) {
559
+ await plan.measure("ownership-release", async () => { await lifecycle.verifyPackageUnlocked(packageRoot); });
560
+ run.transaction = await transactionStore.advance("ownership-released");
561
+ }
562
+ progress.set(15, 70);
563
+ if (phaseBefore(run.transaction.phase, "package-installed")) {
564
+ const cancelled = await replaceUpdatePackage(plan, run, protection, npmCli);
565
+ if (cancelled !== null)
566
+ return cancelled;
567
+ }
568
+ return await activateUpdate(plan, run);
569
+ }
570
+ catch (error) {
571
+ return await settleUpdateFailure(plan, run.transaction, error);
572
+ }
573
+ }
574
+ /** Replace the package under protection; returns an exit code when the user cancelled, null once installed. */
575
+ async function replaceUpdatePackage(plan, run, protection, npmCli) {
576
+ const { output, progress, transactionStore } = plan;
577
+ const transaction = run.transaction;
578
+ const priorRelease = protection.approvedRelease(transaction.priorActiveReleaseId ?? protection.priorActiveReleaseId);
579
+ if (priorRelease === undefined) {
580
+ throw new Error(PRODUCT_TEXT.diagnostic("cannot protect package replacement because no approved prior release is available"));
581
+ }
582
+ const replacement = await plan.measure("npm-install", async () => await plan.installer.replacePackage({
583
+ globalRoot: plan.installation.globalRoot,
584
+ npmCliRoot: plan.installation.npmCliRoot,
585
+ ...(npmCli === undefined ? {} : { npmCli }),
586
+ packageRoot: plan.installation.packageRoot,
587
+ transaction,
588
+ priorRelease,
589
+ output,
590
+ onRecoveryState: async (recovery) => {
591
+ if (transactionStore.setRecovery)
592
+ run.transaction = await transactionStore.setRecovery(recovery) ?? run.transaction;
593
+ },
594
+ }));
595
+ // Rationale: npm's own text explains a failed replacement and says nothing about a
596
+ // successful one, so it reaches the terminal only when the installation did not happen.
597
+ if (replacement.outcome !== "installed")
598
+ reportChildDiagnostics(output, replacement);
599
+ if (replacement.outcome === "installed")
600
+ run.transaction = await transactionStore.advance("package-installed");
601
+ if (replacement.cancelled) {
602
+ progress.clear();
603
+ output.stderr(`${PRODUCT_TEXT.diagnostic("update cancelled safely; the launcher is available and the transaction can be resumed.")}\n`);
604
+ return 130;
605
+ }
606
+ if (replacement.outcome !== "installed") {
607
+ throw new UpdateFailure(unsuccessfulCode(replacement.npmExitCode), `npm exited with status ${formatExitCode(replacement.npmExitCode)}`);
608
+ }
609
+ return null;
610
+ }
611
+ /** Activate the installed package, commit maintenance, and only then report success. */
612
+ async function activateUpdate(plan, run) {
613
+ const { output, progress, transactionStore, now, onPhaseTiming, paths } = plan;
614
+ progress.set(70, MATERIALIZE_PROGRESS.from);
615
+ let activationPhaseStartedAt = now();
616
+ const recordActivationPhase = async (phase) => {
617
+ onPhaseTiming?.({ phase, durationMs: Math.max(0, now() - activationPhaseStartedAt) });
618
+ run.transaction = await transactionStore.advance(phase);
619
+ progress.set(ACTIVATION_PROGRESS[phase].at, ACTIVATION_PROGRESS[phase].creepTo);
620
+ activationPhaseStartedAt = now();
621
+ };
622
+ await plan.lifecycle.activateInstalled(plan.installation.packageRoot, plan.targetVersion, recordActivationPhase, ({ completed, total }) => progress.set(MATERIALIZE_PROGRESS.from
623
+ + (MATERIALIZE_PROGRESS.to - MATERIALIZE_PROGRESS.from) * (total > 0 ? Math.min(1, completed / total) : 0)), state => {
624
+ if (state === "started") {
625
+ activationPhaseStartedAt = now();
626
+ progress.set(98, 99);
627
+ }
628
+ else {
629
+ onPhaseTiming?.({ phase: "warmup", durationMs: Math.max(0, now() - activationPhaseStartedAt) });
630
+ activationPhaseStartedAt = now();
631
+ progress.set(99);
658
632
  }
659
633
  });
634
+ const transactionStartedAt = now();
635
+ const transaction = run.transaction;
636
+ if (transaction?.recovery?.capsulePath)
637
+ await removeUpdateRecoveryCapsule(paths.dataDir, transaction.transactionId);
638
+ // Invariant: successful output follows the durable cleanup disposition. Slow recursive
639
+ // removal belongs to the detached worker started by this maintenance coordinator.
640
+ await plan.installer.maintenance();
641
+ // Concurrency: this is the launch cutover. No failure-prone update work follows success.
642
+ await transactionStore.finish("completed");
643
+ await Promise.all([transactionStore.clearCompleted(), invalidateReleaseCheckCache(paths.configDir)]).catch(() => undefined);
644
+ onPhaseTiming?.({ phase: "transaction-complete", durationMs: Math.max(0, now() - transactionStartedAt) });
645
+ progress.finish();
646
+ output.stdout(`${PRODUCT_TEXT.commandName} updated successfully to ${plan.targetVersion}\n`);
647
+ return 0;
648
+ }
649
+ async function settleUpdateFailure(plan, transaction, error) {
650
+ const { output, transactionStore } = plan;
651
+ plan.progress.clear();
652
+ const message = errorMessage(error);
653
+ if (error instanceof UpdatePreflightFailure) {
654
+ output.stderr(`${PRODUCT_TEXT.diagnostic(`update could not start safely: ${message}. Nothing was changed. Retry with npm x -y -- ${PRODUCT_PACKAGE}-install`)}\n`);
655
+ return 1;
656
+ }
657
+ const rollback = await plan.installer.rollback(transaction?.priorActiveReleaseId ?? null)
658
+ .catch(rollbackError => `rollback failed: ${errorMessage(rollbackError)}`);
659
+ if (transaction)
660
+ await transactionStore.finish(rollback === "rolled back" ? "rolled-back" : "failed", `${message}; ${rollback}`);
661
+ output.stderr(updateFailureLine(message, rollback, transactionStore.path));
662
+ return error instanceof UpdateFailure ? error.exitCode : 1;
660
663
  }
661
664
  export function assertUpdatePerformanceBudget(evidence, maximumPostNpmDurationMs = 30_000) {
662
665
  const failures = [];
@@ -698,24 +701,6 @@ function reportChildDiagnostics(output, result) {
698
701
  output.stderr(`${text}\n`);
699
702
  return true;
700
703
  }
701
- async function rollbackPriorRelease(dataDir, priorReleaseId) {
702
- if (!priorReleaseId)
703
- return "no prior release was available for rollback";
704
- const stateStore = new CohortStateStore(dataDir);
705
- const state = await stateStore.read();
706
- const prior = state.releases[priorReleaseId];
707
- if (!prior || prior.approval !== "approved")
708
- return "prior release is not a verified rollback candidate";
709
- if (state.references.active !== priorReleaseId) {
710
- if (state.references.rollback === priorReleaseId)
711
- await stateStore.rollback();
712
- else
713
- throw new Error(`prior release ${priorReleaseId} is not the recorded rollback release`);
714
- }
715
- // Invariant: rollback selects only a prior release whose retained content still verifies.
716
- await readMaterializedRelease(prior.releaseRoot);
717
- return "rolled back";
718
- }
719
704
  /** One reason and one next step; routine rollback detail stays in the journal. */
720
705
  function updateFailureLine(message, rollback, journalPath) {
721
706
  const note = rollback.startsWith("rollback failed") ? ` (${rollback})` : "";
@@ -731,9 +716,5 @@ class UpdateFailure extends Error {
731
716
  }
732
717
  class UpdatePreflightFailure extends Error {
733
718
  }
734
- function samePath(left, right) {
735
- return process.platform === "win32" ? resolve(left).toLowerCase() === resolve(right).toLowerCase() : resolve(left) === resolve(right);
736
- }
737
719
  function unsuccessfulCode(code) { return code === null || code === 0 ? 1 : code; }
738
720
  function formatExitCode(code) { return code === null ? "unknown" : String(code); }
739
- function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
@@ -1,7 +1,7 @@
1
1
  import type { StartupTraceEvent } from "./startup-runtime.js";
2
2
  export interface StartupPerformanceEvidence {
3
3
  readonly profileId: "a1" | "pi";
4
- readonly launchKind: "post-update" | "no-live-supervisor" | "warm";
4
+ readonly launchKind: "post-update" | "restart" | "warm";
5
5
  readonly events: readonly StartupTraceEvent[];
6
6
  readonly moduleGraph?: {
7
7
  readonly loadedFiles: number;
@@ -15,7 +15,7 @@ export interface StartupPerformanceEvidence {
15
15
  }
16
16
  export interface StartupBudgetViolation {
17
17
  readonly profileId: "a1" | "pi";
18
- readonly launchKind: "post-update" | "no-live-supervisor" | "warm";
18
+ readonly launchKind: "post-update" | "restart" | "warm";
19
19
  readonly elapsedMs: number;
20
20
  readonly budgetMs: number;
21
21
  readonly dominantPhases: readonly {
@@ -27,13 +27,13 @@ export interface StartupBudgetViolation {
27
27
  /** Resolve the declared budget for one launch kind so measurement and reporting never restate the numbers. */
28
28
  export declare function resolveStartupBudgetMs(launchKind: StartupPerformanceEvidence["launchKind"], budgets?: {
29
29
  readonly postUpdateMs: number;
30
- readonly noSupervisorMs: number;
30
+ readonly restartMs: number;
31
31
  readonly warmMs: number;
32
32
  }): number;
33
33
  /** Compare measured startup evidence with its budget without deciding whether the channel enforces it. */
34
34
  export declare function evaluateStartupPerformanceBudget(evidence: StartupPerformanceEvidence, budgets?: {
35
35
  readonly postUpdateMs: number;
36
- readonly noSupervisorMs: number;
36
+ readonly restartMs: number;
37
37
  readonly warmMs: number;
38
38
  }): StartupBudgetViolation | null;
39
39
  /** Render one budget violation as the single message used by failures, evidence, and run summaries. */
@@ -41,6 +41,6 @@ export declare function formatStartupBudgetViolation(violation: StartupBudgetVio
41
41
  /** Enforce the startup budget in a channel that fails on an overrun. */
42
42
  export declare function assertStartupPerformanceBudget(evidence: StartupPerformanceEvidence, budgets?: {
43
43
  readonly postUpdateMs: number;
44
- readonly noSupervisorMs: number;
44
+ readonly restartMs: number;
45
45
  readonly warmMs: number;
46
46
  }): void;
@@ -1,7 +1,7 @@
1
- const STARTUP_PERFORMANCE_BUDGETS = { postUpdateMs: 2_000, noSupervisorMs: 2_500, warmMs: 2_000 };
1
+ const STARTUP_PERFORMANCE_BUDGETS = { postUpdateMs: 2_000, restartMs: 2_500, warmMs: 2_000 };
2
2
  /** Resolve the declared budget for one launch kind so measurement and reporting never restate the numbers. */
3
3
  export function resolveStartupBudgetMs(launchKind, budgets = STARTUP_PERFORMANCE_BUDGETS) {
4
- return launchKind === "warm" ? budgets.warmMs : launchKind === "no-live-supervisor" ? budgets.noSupervisorMs : budgets.postUpdateMs;
4
+ return launchKind === "warm" ? budgets.warmMs : launchKind === "restart" ? budgets.restartMs : budgets.postUpdateMs;
5
5
  }
6
6
  /** Compare measured startup evidence with its budget without deciding whether the channel enforces it. */
7
7
  export function evaluateStartupPerformanceBudget(evidence, budgets = STARTUP_PERFORMANCE_BUDGETS) {
@@ -0,0 +1,33 @@
1
+ export declare const PASTE_START = "\u001B[200~";
2
+ /** A bracketed paste whose end marker arrived, with the input that followed it in the same chunk. */
3
+ export interface CompletedPaste {
4
+ readonly text: string;
5
+ /** UTF-8 bytes counted against the cap; zero when the framer has no cap. */
6
+ readonly bytes: number;
7
+ /** The paste exceeded the cap, so its text was dropped. */
8
+ readonly oversized: boolean;
9
+ readonly remaining: string;
10
+ }
11
+ /**
12
+ * Bracketed-paste framing across terminal input chunks. `feed` collects a paste for insertion;
13
+ * `discard` drops pastes while forwarding the input around them, and also holds back a split
14
+ * start marker so a paste can never leak in as typed text when paste is disabled.
15
+ */
16
+ export declare class BracketedPasteFramer {
17
+ #private;
18
+ /** With `maxBytes`, a paste beyond it is reported as oversized and its text is dropped. */
19
+ constructor(maxBytes?: number);
20
+ /** A paste has started and its end marker has not arrived. */
21
+ get open(): boolean;
22
+ clear(): void;
23
+ /**
24
+ * Feed input that continues the open paste, or that begins with the start marker. Returns the
25
+ * completed paste, or undefined while the end marker is still to come.
26
+ */
27
+ feed(data: string): CompletedPaste | undefined;
28
+ /**
29
+ * Drop any bracketed paste in `data`, forwarding the input before and after it. False means
30
+ * `data` holds no paste and nothing was forwarded, so the caller handles it as ordinary input.
31
+ */
32
+ discard(data: string, forward: (data: string) => void): boolean;
33
+ }
@@ -0,0 +1,123 @@
1
+ export const PASTE_START = "\x1b[200~";
2
+ const PASTE_END = "\x1b[201~";
3
+ /**
4
+ * Bracketed-paste framing across terminal input chunks. `feed` collects a paste for insertion;
5
+ * `discard` drops pastes while forwarding the input around them, and also holds back a split
6
+ * start marker so a paste can never leak in as typed text when paste is disabled.
7
+ */
8
+ export class BracketedPasteFramer {
9
+ #maxBytes;
10
+ #open;
11
+ #startTail = "";
12
+ /** With `maxBytes`, a paste beyond it is reported as oversized and its text is dropped. */
13
+ constructor(maxBytes) {
14
+ this.#maxBytes = maxBytes;
15
+ }
16
+ /** A paste has started and its end marker has not arrived. */
17
+ get open() {
18
+ return this.#open !== undefined;
19
+ }
20
+ clear() {
21
+ this.#open = undefined;
22
+ this.#startTail = "";
23
+ }
24
+ /**
25
+ * Feed input that continues the open paste, or that begins with the start marker. Returns the
26
+ * completed paste, or undefined while the end marker is still to come.
27
+ */
28
+ feed(data) {
29
+ return this.#feed(data, true);
30
+ }
31
+ /**
32
+ * Drop any bracketed paste in `data`, forwarding the input before and after it. False means
33
+ * `data` holds no paste and nothing was forwarded, so the caller handles it as ordinary input.
34
+ */
35
+ discard(data, forward) {
36
+ if (this.#open !== undefined) {
37
+ const done = this.#feed(data, false);
38
+ if (done !== undefined && done.remaining.length > 0)
39
+ forward(done.remaining);
40
+ return true;
41
+ }
42
+ const hadStartTail = this.#startTail.length > 0;
43
+ const candidate = this.#startTail + data;
44
+ this.#startTail = "";
45
+ const start = candidate.indexOf(PASTE_START);
46
+ if (start >= 0) {
47
+ if (start > 0)
48
+ forward(candidate.slice(0, start));
49
+ const done = this.#feed(candidate.slice(start), false);
50
+ if (done !== undefined && done.remaining.length > 0)
51
+ forward(done.remaining);
52
+ return true;
53
+ }
54
+ const partial = delimiterTail(candidate, PASTE_START);
55
+ // Compatibility: standalone Escape still cancels; marker fragments stay pending.
56
+ if (partial.length > 1) {
57
+ const before = candidate.slice(0, candidate.length - partial.length);
58
+ if (before.length > 0)
59
+ forward(before);
60
+ this.#startTail = partial;
61
+ return true;
62
+ }
63
+ if (hadStartTail) {
64
+ forward(candidate);
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ #feed(data, collect) {
70
+ if (this.#open === undefined) {
71
+ this.#open = { parts: [], piece: "", tail: "", bytes: 0, oversized: false };
72
+ data = data.slice(data.indexOf(PASTE_START) + PASTE_START.length);
73
+ }
74
+ // Protocol: inspect only the new fragment plus a delimiter-sized suffix, not the whole growing body.
75
+ const pending = this.#open;
76
+ const fragment = pending.tail + data;
77
+ const end = fragment.indexOf(PASTE_END);
78
+ const append = (part) => {
79
+ if (!collect || pending.oversized || part.length === 0)
80
+ return;
81
+ if (this.#maxBytes !== undefined) {
82
+ pending.bytes += Buffer.byteLength(part);
83
+ if (pending.bytes > this.#maxBytes) {
84
+ pending.oversized = true;
85
+ pending.parts = [];
86
+ pending.piece = "";
87
+ return;
88
+ }
89
+ }
90
+ // Performance: bound fragment metadata even if a terminal supplies one character per input chunk.
91
+ pending.piece += part;
92
+ if (pending.piece.length >= 16_384) {
93
+ pending.parts.push(pending.piece);
94
+ pending.piece = "";
95
+ }
96
+ };
97
+ if (end < 0) {
98
+ let cut = Math.max(0, fragment.length - (PASTE_END.length - 1));
99
+ if (cut > 0 && /[\uD800-\uDBFF]/u.test(fragment[cut - 1]) && /[\uDC00-\uDFFF]/u.test(fragment[cut]))
100
+ cut--;
101
+ append(fragment.slice(0, cut));
102
+ pending.tail = fragment.slice(cut);
103
+ return undefined;
104
+ }
105
+ append(fragment.slice(0, end));
106
+ this.#open = undefined;
107
+ return {
108
+ text: pending.oversized ? "" : pending.parts.join("") + pending.piece,
109
+ bytes: pending.bytes,
110
+ oversized: pending.oversized,
111
+ remaining: fragment.slice(end + PASTE_END.length),
112
+ };
113
+ }
114
+ }
115
+ function delimiterTail(text, delimiter) {
116
+ const limit = Math.min(text.length, delimiter.length - 1);
117
+ for (let length = limit; length > 0; length -= 1) {
118
+ const suffix = text.slice(text.length - length);
119
+ if (delimiter.startsWith(suffix))
120
+ return suffix;
121
+ }
122
+ return "";
123
+ }
@@ -1,5 +1,6 @@
1
+ import type { OwnedUiShellEditorRecall } from "../../../contracts/owned-ui/index.js";
1
2
  import type { Editor } from "@earendil-works/pi-tui";
2
- export interface EditorInteractionState {
3
+ interface EditorInteractionState {
3
4
  lines: string[];
4
5
  cursorLine: number;
5
6
  cursorCol: number;
@@ -17,17 +18,7 @@ export interface EditorInteractionPort {
17
18
  }>;
18
19
  setSegmentTransform(transform: (text: string, mode: "word" | "grapheme", segments: Iterable<Intl.SegmentData>) => Iterable<Intl.SegmentData>): void;
19
20
  }
20
- export interface EditorRecallPort {
21
- replace(entries: readonly string[]): void;
22
- position(): {
23
- readonly index: number;
24
- readonly total: number;
25
- };
26
- observe(listener: () => void): () => void;
27
- reset(): void;
28
- /** Exit a recall browse cycle by restoring the unsent draft rather than keeping the recalled entry. */
29
- restoreDraft?(): void;
30
- }
21
+ export type EditorRecallPort = OwnedUiShellEditorRecall;
31
22
  export interface EditorSurface extends Pick<Editor, Exclude<keyof Editor, "setText" | "render" | "handleInput">> {
32
23
  setText(text: string): void;
33
24
  render(width: number): string[];
@@ -38,3 +29,4 @@ export interface EditorSurface extends Pick<Editor, Exclude<keyof Editor, "setTe
38
29
  export type SelectionEditor = Pick<Editor, "getText" | "getCursor" | "setText" | "insertTextAtCursor" | "invalidate" | "onChange" | "getPaddingX" | "isShowingAutocomplete"> & {
39
30
  readonly interaction?: EditorInteractionPort;
40
31
  };
32
+ export {};