@timurproko/a1 0.2.7-dev.754 → 0.2.7-dev.772

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 (217) hide show
  1. package/README.md +30 -0
  2. package/bin/cli.js +4 -0
  3. package/bin/guardian.js +5 -0
  4. package/bin/tab-launch-context.d.ts +26 -0
  5. package/bin/tab-launch-context.js +94 -0
  6. package/bin/ui.js +24 -6
  7. package/dist/app/session-shell/helper-pool.d.ts +3 -5
  8. package/dist/app/session-shell/helper-pool.js +3 -5
  9. package/dist/app/session-shell/session-shell-root.d.ts +28 -1
  10. package/dist/app/session-shell/session-shell-root.js +4 -3
  11. package/dist/app/session-shell/session-shell.js +149 -31
  12. package/dist/app/session-shell/session-viewport-controller.d.ts +1 -1
  13. package/dist/app/session-shell/session-viewport-controller.js +3 -3
  14. package/dist/app/session-shell/shell-host.d.ts +2 -0
  15. package/dist/app/session-shell/shell-host.js +20 -1
  16. package/dist/cli/dispatch.d.ts +11 -1
  17. package/dist/cli/dispatch.js +79 -5
  18. package/dist/cli/tabs-request.d.ts +7 -0
  19. package/dist/cli/tabs-request.js +1 -0
  20. package/dist/cli/tabs.d.ts +14 -0
  21. package/dist/cli/tabs.js +140 -0
  22. package/dist/composition/owned-ui.d.ts +26 -0
  23. package/dist/composition/owned-ui.js +176 -6
  24. package/dist/contracts/owned-ui/session-backend.d.ts +1 -0
  25. package/dist/features/launch/index.d.ts +1 -1
  26. package/dist/features/launch/intent.d.ts +3 -1
  27. package/dist/features/launch/intent.js +7 -4
  28. package/dist/features/owned-ui/settings-app.js +8 -35
  29. package/dist/features/resident-tab/auto-name.d.ts +11 -0
  30. package/dist/features/resident-tab/auto-name.js +42 -0
  31. package/dist/features/resident-tab/index.d.ts +3 -0
  32. package/dist/features/resident-tab/index.js +3 -0
  33. package/dist/features/resident-tab/shortcuts.d.ts +10 -0
  34. package/dist/features/resident-tab/shortcuts.js +35 -0
  35. package/dist/features/resident-tab/status-deriver.d.ts +9 -0
  36. package/dist/features/resident-tab/status-deriver.js +15 -0
  37. package/dist/foundation/launch-context/index.d.ts +10 -0
  38. package/dist/foundation/launch-context/index.js +25 -0
  39. package/dist/foundation/launch-guardian/main.d.ts +5 -0
  40. package/dist/foundation/launch-guardian/main.js +2 -1
  41. package/dist/foundation/release/bootstrap.d.ts +6 -0
  42. package/dist/foundation/release/bootstrap.js +51 -10
  43. package/dist/foundation/release/cohort-state.d.ts +1 -1
  44. package/dist/foundation/release/cohort-state.js +1 -1
  45. package/dist/foundation/release/dependency-certification.js +54 -19
  46. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  47. package/dist/foundation/release/progress-palette.generated.js +2 -2
  48. package/dist/foundation/release/release-gc.d.ts +3 -0
  49. package/dist/foundation/release/release-gc.js +44 -8
  50. package/dist/foundation/resident-tabs/bounded-host-command.d.ts +16 -0
  51. package/dist/foundation/resident-tabs/bounded-host-command.js +35 -0
  52. package/dist/foundation/resident-tabs/bridge-client.d.ts +56 -0
  53. package/dist/foundation/resident-tabs/bridge-client.js +279 -0
  54. package/dist/foundation/resident-tabs/doctor.d.ts +29 -0
  55. package/dist/foundation/resident-tabs/doctor.js +166 -0
  56. package/dist/foundation/resident-tabs/draft-checkpoint.d.ts +27 -0
  57. package/dist/foundation/resident-tabs/draft-checkpoint.js +145 -0
  58. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +31 -0
  59. package/dist/foundation/resident-tabs/ensure-terminal-host.js +147 -0
  60. package/dist/foundation/resident-tabs/fault-injection.d.ts +8 -0
  61. package/dist/foundation/resident-tabs/fault-injection.js +26 -0
  62. package/dist/foundation/resident-tabs/index.d.ts +12 -0
  63. package/dist/foundation/resident-tabs/index.js +6 -0
  64. package/dist/foundation/resident-tabs/maintenance.d.ts +42 -0
  65. package/dist/foundation/resident-tabs/maintenance.js +78 -0
  66. package/dist/foundation/resident-tabs/prompt-journal.d.ts +65 -0
  67. package/dist/foundation/resident-tabs/prompt-journal.js +214 -0
  68. package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
  69. package/dist/foundation/resident-tabs/protocol.js +195 -0
  70. package/dist/foundation/resident-tabs/recovery.d.ts +26 -0
  71. package/dist/foundation/resident-tabs/recovery.js +205 -0
  72. package/dist/foundation/resident-tabs/release-holds.d.ts +27 -0
  73. package/dist/foundation/resident-tabs/release-holds.js +77 -0
  74. package/dist/foundation/resident-tabs/routing.d.ts +11 -0
  75. package/dist/foundation/resident-tabs/routing.js +10 -0
  76. package/dist/foundation/resident-tabs/session-sync.d.ts +14 -0
  77. package/dist/foundation/resident-tabs/session-sync.js +37 -0
  78. package/dist/foundation/resident-tabs/settings.d.ts +14 -0
  79. package/dist/foundation/resident-tabs/settings.js +66 -0
  80. package/dist/foundation/resident-tabs/terminal-host-artifact.d.ts +18 -0
  81. package/dist/foundation/resident-tabs/terminal-host-artifact.js +53 -0
  82. package/dist/foundation/resident-tabs/zip-archive.d.ts +9 -0
  83. package/dist/foundation/resident-tabs/zip-archive.js +56 -0
  84. package/dist/foundation/session-writer-lock/index.d.ts +32 -0
  85. package/dist/foundation/session-writer-lock/index.js +201 -0
  86. package/dist/foundation/startup/startup-descriptor.js +2 -2
  87. package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
  88. package/dist/foundation/terminal-cleanup/index.js +1 -1
  89. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +1 -0
  90. package/dist/foundation/terminal-cleanup/terminal-reset.js +1 -0
  91. package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -1
  92. package/dist/integrations/pi/components/owned-editor-ux.js +2 -2
  93. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +4 -4
  94. package/dist/integrations/pi/components/resources/builtin-themes.js +4 -4
  95. package/dist/integrations/pi/components/shell-hotkey-sections.js +1 -1
  96. package/dist/integrations/pi/components/shell-presenters-info.d.ts +4 -2
  97. package/dist/integrations/pi/components/shell-presenters-info.js +38 -4
  98. package/dist/integrations/pi/components/shell-shared-facade.d.ts +1 -1
  99. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  100. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +17 -4
  101. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  102. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  103. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  104. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  105. package/dist/integrations/pi/components/upstream/components/extension-editor.js +1 -1
  106. package/dist/integrations/pi/components/upstream/components/extension-input.js +1 -1
  107. package/dist/integrations/pi/components/upstream/components/extension-selector.js +1 -1
  108. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  109. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +1 -1
  110. package/dist/integrations/pi/components/upstream/components/owned-editor.js +1 -1
  111. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  112. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  113. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  114. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  115. package/dist/integrations/pi/components/upstream/components/session-selector.js +1 -1
  116. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +1 -1
  117. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +1 -1
  118. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +2 -2
  119. package/dist/integrations/pi/components/upstream/components/status-indicator.js +3 -3
  120. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  121. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  122. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  123. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +1 -1
  124. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +0 -2
  125. package/dist/integrations/pi/components/upstream/components/tool-execution.js +41 -20
  126. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  127. package/dist/integrations/pi/components/upstream/components/tree-selector.js +1 -1
  128. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  129. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  130. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +2 -2
  131. package/dist/integrations/pi/components/upstream/history/editor-core.js +5 -5
  132. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  133. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  134. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  135. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  136. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  137. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  138. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  139. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  140. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +1 -1
  141. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  142. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +3 -0
  143. package/dist/integrations/pi/components/upstream/theme/theme.js +24 -1
  144. package/dist/integrations/pi/engine/adapter.d.ts +1 -0
  145. package/dist/integrations/pi/engine/adapter.js +4 -0
  146. package/dist/integrations/pi/engine/pi-settings-metadata.json +2 -2
  147. package/dist/integrations/pi/engine/prompt-suggestions.d.ts +1 -0
  148. package/dist/integrations/pi/engine/prompt-suggestions.js +40 -0
  149. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  150. package/dist/integrations/pi/engine/runtime-integration.js +102 -29
  151. package/dist/integrations/pi/engine/session-runtime.d.ts +0 -6
  152. package/dist/integrations/pi/engine/session-runtime.js +8 -3
  153. package/dist/integrations/pi/engine/session-selection.d.ts +3 -0
  154. package/dist/integrations/pi/engine/session-selection.js +45 -9
  155. package/dist/integrations/pi/engine/workflow-contexts.js +10 -1
  156. package/dist/integrations/pi/startup-public.js +152 -222
  157. package/dist/integrations/pi/startup-public.manifest.json +266 -177
  158. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  159. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  160. package/dist/native/darwin-arm64/a1_session_lock.node +0 -0
  161. package/dist/native/darwin-arm64/manifest.json +1 -1
  162. package/dist/native/darwin-arm64/terminal-host +0 -0
  163. package/dist/native/darwin-arm64/terminal-host-THIRD-PARTY-NOTICES.txt +10500 -0
  164. package/dist/native/darwin-arm64/terminal-host.manifest.json +44 -0
  165. package/dist/native/darwin-x64/a1_session_lock.node +0 -0
  166. package/dist/native/darwin-x64/manifest.json +20 -0
  167. package/dist/native/darwin-x64/process-guardian +0 -0
  168. package/dist/native/darwin-x64/terminal-host +0 -0
  169. package/dist/native/darwin-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10737 -0
  170. package/dist/native/darwin-x64/terminal-host.manifest.json +44 -0
  171. package/dist/native/linux-x64/a1_session_lock.node +0 -0
  172. package/dist/native/linux-x64/manifest.json +1 -1
  173. package/dist/native/linux-x64/terminal-host +0 -0
  174. package/dist/native/linux-x64/terminal-host-THIRD-PARTY-NOTICES.txt +11226 -0
  175. package/dist/native/linux-x64/terminal-host.manifest.json +44 -0
  176. package/dist/native/win32-x64/a1_session_lock.node +0 -0
  177. package/dist/native/win32-x64/manifest.json +2 -2
  178. package/dist/native/win32-x64/process-guardian.exe +0 -0
  179. package/dist/native/win32-x64/terminal-host-THIRD-PARTY-NOTICES.txt +10757 -0
  180. package/dist/native/win32-x64/terminal-host.exe +0 -0
  181. package/dist/native/win32-x64/terminal-host.manifest.json +44 -0
  182. package/dist/product-identity.d.ts +1 -1
  183. package/dist/product-identity.js +1 -1
  184. package/dist/product-identity.json +5 -0
  185. package/dist/runtime-payload-inventory.json +5349 -6035
  186. package/dist/ui/components/line-input.d.ts +1 -1
  187. package/dist/ui/components/prompt-input.d.ts +7 -5
  188. package/dist/ui/components/prompt-input.js +7 -7
  189. package/dist/ui/components/transcript-viewport.d.ts +6 -3
  190. package/dist/ui/components/transcript-viewport.js +13 -15
  191. package/dist/ui/settings/declarations.d.ts +25 -1
  192. package/dist/ui/settings/declarations.js +21 -1
  193. package/dist/ui/settings/index.d.ts +4 -2
  194. package/dist/ui/settings/index.js +2 -1
  195. package/dist/ui/settings/manager.d.ts +10 -3
  196. package/dist/ui/settings/manager.js +90 -30
  197. package/dist/ui/settings/migrations.d.ts +6 -0
  198. package/dist/ui/settings/migrations.js +44 -0
  199. package/dist/ui/settings/profile-document.d.ts +26 -0
  200. package/dist/ui/settings/profile-document.js +164 -0
  201. package/docs/architecture/evidence/resident-tabs/linux-x64.md +37 -0
  202. package/docs/architecture/evidence/resident-tabs/macos-arm64.md +36 -0
  203. package/docs/architecture/evidence/resident-tabs/macos-x64.md +35 -0
  204. package/docs/architecture/evidence/resident-tabs/windows-x64.md +36 -0
  205. package/docs/architecture/internal-naming.md +3 -1
  206. package/docs/architecture/process-guardian-provenance.md +2 -0
  207. package/docs/architecture/resident-tabs-requirements.md +18 -0
  208. package/docs/architecture/resident-tabs.md +594 -0
  209. package/docs/architecture/terminal-host-proof-gate.md +2 -0
  210. package/docs/architecture/terminal-host-provenance.md +13 -3
  211. package/docs/architecture/toolchain.md +1 -1
  212. package/docs/ci-release-runbook.md +2 -2
  213. package/docs/features/launch-profiles.md +11 -8
  214. package/docs/manual-resident-tabs-acceptance.md +51 -0
  215. package/docs/openspec-archive-automation.md +17 -16
  216. package/docs/validation.md +9 -1
  217. package/package.json +10 -4
@@ -94,12 +94,10 @@ export class SettingsApp {
94
94
  // Invariant: keyboard navigation requests visibility once; pointer scrolling then stays free.
95
95
  #selectionNeedsReveal = true;
96
96
  #reveal;
97
- #notice = null;
98
97
  #filter = null;
99
98
  #scrollBeforeFilter = null;
100
99
  #menu = null;
101
100
  #structured = null;
102
- #interruptArmed = false;
103
101
  // Invariant: pending values remain visible until the source reflects them.
104
102
  #pending = new Map();
105
103
  #undoHistory = [];
@@ -138,7 +136,6 @@ export class SettingsApp {
138
136
  }
139
137
  render(rect, host) {
140
138
  const theme = host.theme ?? PLAIN_THEME;
141
- this.#interruptArmed = host.interruptArmed;
142
139
  const rows = this.#rows();
143
140
  const selected = indexOfKey(rows, this.#selectedKey);
144
141
  const footer = this.#footerLines(rect.width, theme);
@@ -259,7 +256,6 @@ export class SettingsApp {
259
256
  case "open-filter":
260
257
  this.#scrollBeforeFilter = this.#scroll;
261
258
  this.#filter = new LineInput("");
262
- this.#notice = null;
263
259
  return { consumed: true };
264
260
  case "activate": {
265
261
  const row = rows[selected];
@@ -385,7 +381,6 @@ export class SettingsApp {
385
381
  const index = rows.findIndex(candidate => rowKey(candidate) === row.key);
386
382
  if (index >= 0) {
387
383
  // Rationale: the pointer acts where it points; the arrow belongs to the keyboard.
388
- this.#notice = null;
389
384
  if (this.#hoverRegion === "minus")
390
385
  this.#cycle(rows, index, -1);
391
386
  else if (this.#hoverRegion === "plus")
@@ -458,10 +453,8 @@ export class SettingsApp {
458
453
  // and pointing at it is not a request for anything else.
459
454
  if (typeof shown === "number")
460
455
  return;
461
- if (!entry.editable || entry.choices === null || entry.choices.length === 0) {
462
- this.#notice = `${labelOf(entry)} cannot be changed here`;
456
+ if (!entry.editable || entry.choices === null || entry.choices.length === 0)
463
457
  return;
464
- }
465
458
  const current = shown === null ? 0 : Math.max(0, entry.choices.indexOf(shown));
466
459
  const anchorKey = `${entry.backend}:${entry.id}`;
467
460
  // Compatibility: pointer opening already brightens the source value. Enter borrows that
@@ -492,10 +485,8 @@ export class SettingsApp {
492
485
  #openStructured(entry) {
493
486
  // Invariant: the flags come from the declaration, not from the stored value: an unset
494
487
  // flag still has a row, showing the default the source would apply.
495
- if (entry.flags.length === 0) {
496
- this.#notice = `${labelOf(entry)} has nothing to configure`;
488
+ if (entry.flags.length === 0)
497
489
  return;
498
- }
499
490
  const stored = typeof entry.rawValue === "object" && entry.rawValue !== null && !Array.isArray(entry.rawValue)
500
491
  ? entry.rawValue
501
492
  : {};
@@ -648,13 +639,11 @@ export class SettingsApp {
648
639
  const failure = changeFailure(outcome);
649
640
  if (failure === null) {
650
641
  this.#recordUndo({ kind: "structured", sequence, entry: open.entry, previous, forward });
651
- this.#notice = null;
652
642
  return;
653
643
  }
654
644
  // Concurrency: only roll back the dialog if no later press has moved the record on.
655
645
  if (this.#structured === open && sameRecord(open.record, forward))
656
646
  replaceRecord(open.record, previous);
657
- this.#notice = `Could not save ${labelOf(open.entry)}: ${failure}`;
658
647
  });
659
648
  }
660
649
  #menuKey(data) {
@@ -744,10 +733,8 @@ export class SettingsApp {
744
733
  if (row === undefined || row.kind !== "element")
745
734
  return;
746
735
  const entry = row.value;
747
- if (!entry.editable) {
748
- this.#notice = `${labelOf(entry)} cannot be changed here`;
736
+ if (!entry.editable)
749
737
  return;
750
- }
751
738
  const shown = this.#shownValue(entry);
752
739
  if (typeof shown === "number") {
753
740
  // Rationale: at the end of the range there is nothing to say: the arrow already reads
@@ -758,10 +745,8 @@ export class SettingsApp {
758
745
  return;
759
746
  }
760
747
  const choices = entry.choices;
761
- if (choices === null || choices.length === 0) {
762
- this.#notice = `${labelOf(entry)} cannot be changed here`;
748
+ if (choices === null || choices.length === 0)
763
749
  return;
764
- }
765
750
  const current = shown === null ? -1 : choices.indexOf(shown);
766
751
  const next = choices[current < 0
767
752
  ? (delta > 0 ? 0 : choices.length - 1)
@@ -781,7 +766,6 @@ export class SettingsApp {
781
766
  if (failure !== null) {
782
767
  if (this.#pending.get(key) === value)
783
768
  this.#pending.delete(key);
784
- this.#notice = `Could not save ${labelOf(entry)}: ${failure}`;
785
769
  return;
786
770
  }
787
771
  if (previous !== null && previous !== value)
@@ -789,7 +773,6 @@ export class SettingsApp {
789
773
  // Concurrency: a later press may have moved on; only the last request clears itself.
790
774
  if (this.#pending.get(key) === value)
791
775
  this.#pending.delete(key);
792
- this.#notice = null;
793
776
  });
794
777
  }
795
778
  #shownValue(entry) {
@@ -841,13 +824,8 @@ export class SettingsApp {
841
824
  }
842
825
  #finishUndo(record, outcome) {
843
826
  const failure = changeFailure(outcome);
844
- if (failure !== null) {
827
+ if (failure !== null)
845
828
  this.#recordUndo(record);
846
- this.#notice = `Could not restore ${labelOf(record.entry)}: ${failure}`;
847
- }
848
- else {
849
- this.#notice = null;
850
- }
851
829
  this.#undoActive = false;
852
830
  this.#drainUndo();
853
831
  }
@@ -878,7 +856,6 @@ export class SettingsApp {
878
856
  return;
879
857
  this.#selectedKey = rowKey(rows[index]);
880
858
  this.#selectionNeedsReveal = true;
881
- this.#notice = null;
882
859
  }
883
860
  #rows() {
884
861
  const needle = this.#filter?.value.trim().toLowerCase() ?? "";
@@ -1037,13 +1014,9 @@ export class SettingsApp {
1037
1014
  const open = this.#structured;
1038
1015
  if (open !== null)
1039
1016
  return this.#dialogLines(open, width, theme);
1040
- const notice = this.#notice;
1041
- const plainStatus = notice !== null && notice.length > 0
1042
- ? notice
1043
- : this.#interruptArmed ? "press ctrl+c again to exit a1" : null;
1044
- const status = plainStatus === null
1045
- ? renderShortcutHintsWithClose(SETTINGS_SHORTCUTS.hintEntries(SCOPE), theme, width, width > 0 ? 1 : 0)
1046
- : truncateToWidth(`${width > 0 ? " " : ""}${theme.fg("dim", plainStatus)}`, width);
1017
+ // Rationale: standing guidance is more useful than transient feedback; failures still
1018
+ // roll back without displacing the Settings interaction contract.
1019
+ const status = renderShortcutHintsWithClose(SETTINGS_SHORTCUTS.hintEntries(SCOPE), theme, width, width > 0 ? 1 : 0);
1047
1020
  const input = this.#filter;
1048
1021
  if (input === null)
1049
1022
  return [status];
@@ -0,0 +1,11 @@
1
+ export declare const AUTO_NAME_MAX_LENGTH = 16;
2
+ export declare const AUTO_NAME_TIMEOUT_MS = 10000;
3
+ export declare const AUTO_NAME_MAX_TOKENS = 64;
4
+ export declare function normalizeAutoName(value: string): string | null;
5
+ export declare function fallbackAutoName(prompt: string): string;
6
+ export interface AutoNameCompletion {
7
+ readonly prompt: string;
8
+ readonly maxTokens: number;
9
+ readonly signal: AbortSignal;
10
+ }
11
+ export declare function generateAutoName(firstPrompt: string, complete: (request: AutoNameCompletion) => Promise<string | null>, timeoutMs?: number): Promise<string>;
@@ -0,0 +1,42 @@
1
+ export const AUTO_NAME_MAX_LENGTH = 16;
2
+ export const AUTO_NAME_TIMEOUT_MS = 10_000;
3
+ export const AUTO_NAME_MAX_TOKENS = 64;
4
+ export function normalizeAutoName(value) {
5
+ const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
6
+ if (!normalized)
7
+ return null;
8
+ if (normalized.length <= AUTO_NAME_MAX_LENGTH)
9
+ return normalized;
10
+ const clipped = normalized.slice(0, AUTO_NAME_MAX_LENGTH + 1);
11
+ const boundary = clipped.lastIndexOf("-");
12
+ const result = (boundary >= 4 ? clipped.slice(0, boundary) : normalized.slice(0, AUTO_NAME_MAX_LENGTH)).replace(/-+$/g, "");
13
+ return result || null;
14
+ }
15
+ export function fallbackAutoName(prompt) {
16
+ const words = prompt.toLowerCase().match(/[a-z0-9]+/g) ?? [];
17
+ return normalizeAutoName(words.slice(0, 3).join("-")) ?? "new-task";
18
+ }
19
+ export async function generateAutoName(firstPrompt, complete, timeoutMs = AUTO_NAME_TIMEOUT_MS) {
20
+ for (let attempt = 0; attempt < 2; attempt++) {
21
+ const controller = new AbortController();
22
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
23
+ timer.unref?.();
24
+ try {
25
+ const response = await complete({
26
+ prompt: `Name this coding task with a lowercase hyphenated title of at most ${AUTO_NAME_MAX_LENGTH} characters. Reply only with the title.\n\n${firstPrompt.slice(0, 500)}`,
27
+ maxTokens: AUTO_NAME_MAX_TOKENS,
28
+ signal: controller.signal,
29
+ });
30
+ const name = response === null ? null : normalizeAutoName(response);
31
+ if (name !== null)
32
+ return name;
33
+ }
34
+ catch {
35
+ // Rationale: naming is best effort; one retry precedes the deterministic fallback.
36
+ }
37
+ finally {
38
+ clearTimeout(timer);
39
+ }
40
+ }
41
+ return fallbackAutoName(firstPrompt);
42
+ }
@@ -0,0 +1,3 @@
1
+ export { AUTO_NAME_MAX_LENGTH, AUTO_NAME_MAX_TOKENS, AUTO_NAME_TIMEOUT_MS, fallbackAutoName, generateAutoName, normalizeAutoName, type AutoNameCompletion, } from "./auto-name.js";
2
+ export { RESIDENT_TAB_SHORTCUTS, residentTabShortcutRegistry, type ResidentTabAction, type ResidentTabShortcut, } from "./shortcuts.js";
3
+ export { deriveResidentTabStatus, type ResidentTabDerivedStatus, type ResidentTabFacts, } from "./status-deriver.js";
@@ -0,0 +1,3 @@
1
+ export { AUTO_NAME_MAX_LENGTH, AUTO_NAME_MAX_TOKENS, AUTO_NAME_TIMEOUT_MS, fallbackAutoName, generateAutoName, normalizeAutoName, } from "./auto-name.js";
2
+ export { RESIDENT_TAB_SHORTCUTS, residentTabShortcutRegistry, } from "./shortcuts.js";
3
+ export { deriveResidentTabStatus, } from "./status-deriver.js";
@@ -0,0 +1,10 @@
1
+ import { ShortcutRegistry } from "../../ui/components/index.js";
2
+ export type ResidentTabAction = "tabs.new" | "tabs.close" | "tabs.rename" | "tabs.retry" | "tabs.fresh" | "tabs.next" | "tabs.previous" | "tabs.moveRight" | "tabs.moveLeft" | `tabs.jump${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}`;
3
+ export interface ResidentTabShortcut {
4
+ readonly id: ResidentTabAction;
5
+ readonly defaultKey: string | null;
6
+ readonly scope: "global" | "failed-banner";
7
+ readonly description: string;
8
+ }
9
+ export declare const RESIDENT_TAB_SHORTCUTS: readonly ResidentTabShortcut[];
10
+ export declare function residentTabShortcutRegistry(assignments?: Readonly<Partial<Record<ResidentTabAction, string | null>>>): ShortcutRegistry<ResidentTabAction>;
@@ -0,0 +1,35 @@
1
+ import { GLOBAL_SCOPE, ShortcutRegistry } from "../../ui/components/index.js";
2
+ const jumps = Array.from({ length: 10 }, (_, index) => ({
3
+ id: `tabs.jump${index + 1}`,
4
+ defaultKey: `alt+${(index + 1) % 10}`,
5
+ scope: "global",
6
+ description: `View A1 tab ${index + 1}`,
7
+ }));
8
+ export const RESIDENT_TAB_SHORTCUTS = Object.freeze([
9
+ { id: "tabs.new", defaultKey: "alt+a", scope: "global", description: "New A1 tab" },
10
+ { id: "tabs.close", defaultKey: "alt+w", scope: "global", description: "Close A1 tab" },
11
+ { id: "tabs.rename", defaultKey: "f2", scope: "global", description: "Rename A1 tab" },
12
+ ...jumps,
13
+ { id: "tabs.next", defaultKey: null, scope: "global", description: "View next A1 tab" },
14
+ { id: "tabs.previous", defaultKey: null, scope: "global", description: "View previous A1 tab" },
15
+ { id: "tabs.moveRight", defaultKey: null, scope: "global", description: "Move A1 tab right" },
16
+ { id: "tabs.moveLeft", defaultKey: null, scope: "global", description: "Move A1 tab left" },
17
+ { id: "tabs.retry", defaultKey: "r", scope: "failed-banner", description: "Retry failed A1 tab" },
18
+ { id: "tabs.fresh", defaultKey: "f", scope: "failed-banner", description: "Start failed A1 tab fresh" },
19
+ ]);
20
+ export function residentTabShortcutRegistry(assignments = {}) {
21
+ const registry = new ShortcutRegistry();
22
+ for (const shortcut of RESIDENT_TAB_SHORTCUTS) {
23
+ const key = assignments[shortcut.id] === undefined ? shortcut.defaultKey : assignments[shortcut.id];
24
+ if (!key)
25
+ continue;
26
+ const declaration = {
27
+ key,
28
+ scope: shortcut.scope === "global" ? GLOBAL_SCOPE : shortcut.scope,
29
+ section: "Tabs",
30
+ description: shortcut.description,
31
+ };
32
+ registry.declare(declaration, shortcut.id);
33
+ }
34
+ return registry;
35
+ }
@@ -0,0 +1,9 @@
1
+ export type ResidentTabDerivedStatus = "starting" | "working" | "needs-input" | "done-unseen" | "idle" | "failed";
2
+ export interface ResidentTabFacts {
3
+ readonly lifecycle: string;
4
+ readonly pendingRequests: number;
5
+ readonly queuedInput: number;
6
+ readonly stopReason?: string;
7
+ readonly settledUnseen?: boolean;
8
+ }
9
+ export declare function deriveResidentTabStatus(facts: ResidentTabFacts): ResidentTabDerivedStatus;
@@ -0,0 +1,15 @@
1
+ export function deriveResidentTabStatus(facts) {
2
+ if (facts.lifecycle === "failed")
3
+ return "failed";
4
+ if (facts.pendingRequests > 0)
5
+ return "needs-input";
6
+ if (facts.lifecycle === "busy" || facts.queuedInput > 0)
7
+ return "working";
8
+ if (facts.lifecycle === "starting")
9
+ return "starting";
10
+ if (facts.stopReason === "error")
11
+ return "failed";
12
+ if (facts.settledUnseen === true)
13
+ return "done-unseen";
14
+ return "idle";
15
+ }
@@ -6,7 +6,11 @@ export declare const PRIVATE_ENVIRONMENT: Readonly<{
6
6
  releaseDigest: "LAUNCH_CONTEXT_RELEASE_DIGEST";
7
7
  releaseLayers: "LAUNCH_CONTEXT_RELEASE_LAYERS";
8
8
  launchProfile: "LAUNCH_CONTEXT_PROFILE";
9
+ launchMode: "LAUNCH_CONTEXT_MODE";
9
10
  immutableWarmup: "LAUNCH_CONTEXT_WARMUP";
11
+ residentRootExecutable: "LAUNCH_CONTEXT_RESIDENT_ROOT_EXECUTABLE";
12
+ residentRootArguments: "LAUNCH_CONTEXT_RESIDENT_ROOT_ARGUMENTS";
13
+ residentFallback: "LAUNCH_CONTEXT_RESIDENT_FALLBACK";
10
14
  }>;
11
15
  /**
12
16
  * Keys published by releases that predate the neutral cutover. They are read when
@@ -30,8 +34,14 @@ export interface LaunchContext {
30
34
  readonly releaseDigest?: string;
31
35
  readonly releaseLayers?: string;
32
36
  readonly launchProfile?: "a1" | "pi";
37
+ readonly launchMode?: "tabs" | "solo";
33
38
  readonly immutableWarmup?: "1";
39
+ readonly residentRootExecutable?: string;
40
+ readonly residentRootArguments?: string;
41
+ readonly residentFallback?: string;
34
42
  }
43
+ /** The Tabs settings configure resident tabs; a direct-route launch, by `a1 solo` or by a fallback, hides them. */
44
+ export declare function hidesTabSettings(context: Pick<LaunchContext, "launchMode" | "residentFallback">): boolean;
35
45
  export type LaunchContextRequirement = "optional" | "profile" | "release" | "warmup";
36
46
  /** Read current keys, fall back to superseded keys per field, with bounded diagnostics. */
37
47
  export declare function readLaunchContext(environment: NodeJS.ProcessEnv, requirement?: LaunchContextRequirement, platform?: NodeJS.Platform): LaunchContext;
@@ -6,7 +6,11 @@ export const PRIVATE_ENVIRONMENT = Object.freeze({
6
6
  releaseDigest: "LAUNCH_CONTEXT_RELEASE_DIGEST",
7
7
  releaseLayers: "LAUNCH_CONTEXT_RELEASE_LAYERS",
8
8
  launchProfile: "LAUNCH_CONTEXT_PROFILE",
9
+ launchMode: "LAUNCH_CONTEXT_MODE",
9
10
  immutableWarmup: "LAUNCH_CONTEXT_WARMUP",
11
+ residentRootExecutable: "LAUNCH_CONTEXT_RESIDENT_ROOT_EXECUTABLE",
12
+ residentRootArguments: "LAUNCH_CONTEXT_RESIDENT_ROOT_ARGUMENTS",
13
+ residentFallback: "LAUNCH_CONTEXT_RESIDENT_FALLBACK",
10
14
  });
11
15
  /**
12
16
  * Keys published by releases that predate the neutral cutover. They are read when
@@ -27,6 +31,10 @@ const SUPERSEDED_ENTRIES = Object.entries(SUPERSEDED_ENVIRONMENT);
27
31
  const PRIVATE_KEYS = new Set([...Object.values(PRIVATE_ENVIRONMENT), ...Object.values(SUPERSEDED_ENVIRONMENT)]);
28
32
  const LOGICAL_KEYS = new Map(PRIVATE_ENTRIES.map(([logical, key]) => [key, logical]));
29
33
  const SUPERSEDED_LOGICAL_KEYS = new Map(SUPERSEDED_ENTRIES.map(([logical, key]) => [key, logical]));
34
+ /** The Tabs settings configure resident tabs; a direct-route launch, by `a1 solo` or by a fallback, hides them. */
35
+ export function hidesTabSettings(context) {
36
+ return context.launchMode === "solo" || context.residentFallback !== undefined;
37
+ }
30
38
  /** Read current keys, fall back to superseded keys per field, with bounded diagnostics. */
31
39
  export function readLaunchContext(environment, requirement = "optional", platform = process.platform) {
32
40
  const values = {};
@@ -61,8 +69,25 @@ export function readLaunchContext(environment, requirement = "optional", platfor
61
69
  if (values.launchProfile !== undefined && values.launchProfile !== "a1" && values.launchProfile !== "pi") {
62
70
  throw invalidContext("launchProfile", "unsupported profile");
63
71
  }
72
+ if (values.launchMode !== undefined && values.launchMode !== "tabs" && values.launchMode !== "solo") {
73
+ throw invalidContext("launchMode", "unsupported launch mode");
74
+ }
75
+ if (values.launchProfile === "pi" && values.launchMode === "tabs")
76
+ throw invalidContext("launchMode", "Pi comparison cannot use tabs");
64
77
  if (values.immutableWarmup !== undefined && values.immutableWarmup !== "1")
65
78
  throw invalidContext("immutableWarmup", "invalid permission flag");
79
+ if ((values.residentRootExecutable === undefined) !== (values.residentRootArguments === undefined))
80
+ throw invalidContext("residentRootExecutable", "resident root command is incomplete");
81
+ if (values.residentRootArguments !== undefined) {
82
+ try {
83
+ const argumentsValue = JSON.parse(values.residentRootArguments);
84
+ if (!Array.isArray(argumentsValue) || argumentsValue.some(value => typeof value !== "string"))
85
+ throw new Error("not string arguments");
86
+ }
87
+ catch {
88
+ throw invalidContext("residentRootArguments", "invalid JSON argument list");
89
+ }
90
+ }
66
91
  if (values.releaseDigest !== undefined && !/^[a-f0-9]{64}$/.test(values.releaseDigest))
67
92
  throw invalidContext("releaseDigest", "invalid digest");
68
93
  if (requirement === "profile")
@@ -16,11 +16,16 @@ interface GuardianControl {
16
16
  off(event: "disconnect", listener: () => void): unknown;
17
17
  close(): void;
18
18
  }
19
+ export interface LaunchGuardianRootCommand {
20
+ readonly executable: string;
21
+ readonly arguments: readonly string[];
22
+ }
19
23
  export interface LaunchGuardianOptions {
20
24
  readonly profileId: LaunchProfileId;
21
25
  readonly releaseRoot: string;
22
26
  readonly uiEntry: string;
23
27
  readonly sessionSelection?: SessionSelection;
28
+ readonly rootCommand?: LaunchGuardianRootCommand;
24
29
  readonly environment?: NodeJS.ProcessEnv;
25
30
  readonly cwd?: string;
26
31
  readonly helperPath?: string;
@@ -44,7 +44,8 @@ export async function runLaunchGuardian(options) {
44
44
  });
45
45
  let handle;
46
46
  try {
47
- handle = await containment.spawn(process.execPath, [options.uiEntry, ...sessionArgs], {
47
+ const rootCommand = options.rootCommand ?? { executable: process.execPath, arguments: [options.uiEntry, ...sessionArgs] };
48
+ handle = await containment.spawn(rootCommand.executable, [...rootCommand.arguments], {
48
49
  cwd: options.cwd ?? process.cwd(),
49
50
  environment: environmentEntries(environment),
50
51
  ...(environment.TERM ? { terminalType: environment.TERM } : {}),
@@ -2,11 +2,13 @@ import { type SupervisorEndpointMetadata } from "./cohort-state.js";
2
2
  import { resolveProductPaths, type SessionSelection, type LaunchProfileId, type SupervisorStartupAttemptIdentity } from "../lifecycle/index.js";
3
3
  import { cleanupProvenIdleOwner } from "./process-cleanup.js";
4
4
  import { type MaterializedRelease, type VerifyMaterializedReleaseOptions } from "./release-store.js";
5
+ import { type EnsureTerminalHostOptions, type EnsureTerminalHostResult } from "../resident-tabs/index.js";
5
6
  export interface BootstrapOptions {
6
7
  readonly packageRoot: string;
7
8
  readonly launchIntent?: {
8
9
  readonly kind: "interactive";
9
10
  readonly profileId: LaunchProfileId;
11
+ readonly launchMode: "tabs" | "solo";
10
12
  readonly sessionSelection?: SessionSelection;
11
13
  };
12
14
  readonly environment?: NodeJS.ProcessEnv;
@@ -23,6 +25,10 @@ export interface BootstrapOptions {
23
25
  readonly releaseIsLaunchable?: (releaseRoot: string) => boolean;
24
26
  /** Test seam for bounded maintenance scheduled after stale endpoint sweeping. */
25
27
  readonly scheduleMaintenance?: (dataDir: string, paths: ReturnType<typeof resolveProductPaths>) => Promise<void>;
28
+ /** Test seam for resident launch routing before the guardian starts. */
29
+ readonly ensureTerminalHost?: (options: EnsureTerminalHostOptions) => Promise<EnsureTerminalHostResult>;
30
+ /** Test seam for whether this process's standard input and output are terminals; absent reads them. */
31
+ readonly interactiveTerminal?: boolean;
26
32
  }
27
33
  export declare function runBootstrap(options: BootstrapOptions): Promise<number>;
28
34
  export declare function certifyMaterializedRelease(release: MaterializedRelease, dataDir: string, verification?: VerifyMaterializedReleaseOptions): Promise<string>;
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
- import { restoreAfterOwnedExit } from "../terminal-cleanup/index.js";
2
+ import { CLEAR_EXIT_INTERVAL_MS, restoreAfterOwnedExit } from "../terminal-cleanup/index.js";
3
3
  import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
4
  import { connect } from "node:net";
5
5
  import { resolve } from "node:path";
@@ -15,26 +15,63 @@ import { selectUpdateLaunchRelease } from "./update-launch.js";
15
15
  import { consumeMaterializationProof, materializeRelease, readCertifiedReleaseManifest, readMaterializedRelease, resolveReleaseEntryPoint, verifyMaterializedRelease } from "./release-store.js";
16
16
  import { scheduleReleaseCleanup } from "./release-gc.js";
17
17
  import { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument } from "./restart-certification.js";
18
- import { PRODUCT_TEXT } from "../../product-identity.js";
18
+ import { PRODUCT_IDENTITY, PRODUCT_TEXT } from "../../product-identity.js";
19
19
  import { markStartupPhase } from "../startup/index.js";
20
- import { launchContractTarget, readLaunchContext, withLaunchContext } from "../launch-context/index.js";
20
+ import { isCurrentLaunchContract, launchContractTarget, readLaunchContext, withLaunchContext } from "../launch-context/index.js";
21
+ import { ensureTerminalHost, readResidentTabLaunchSettings, selectResidentHost } from "../resident-tabs/index.js";
21
22
  const RELEASE_RESELECTION_MESSAGE = "a1-release-reselection";
22
23
  export async function runBootstrap(options) {
23
24
  return await runBootstrapAttempt(options, 1);
24
25
  }
25
26
  async function runBootstrapAttempt(options, retriesRemaining) {
26
- const environment = withLaunchContext(options.environment ?? process.env, { launchProfile: options.launchIntent?.profileId ?? "a1" });
27
- await markStartupPhase(environment, "bootstrap-start");
28
27
  const launchProfileId = options.launchIntent?.profileId ?? "a1";
28
+ const launchMode = options.launchIntent?.launchMode ?? (launchProfileId === "a1" ? "tabs" : "solo");
29
+ const environment = withLaunchContext(options.environment ?? process.env, { launchProfile: launchProfileId, launchMode });
30
+ await markStartupPhase(environment, "bootstrap-start");
29
31
  assertLaunchProfileId(launchProfileId);
30
32
  const sessionArgs = sessionSelectionArguments(options.launchIntent?.sessionSelection);
31
- if (sessionArgs.length > 0 && launchProfileId !== "a1")
32
- throw new Error("session selection requires the normal A1 profile");
33
+ if (sessionArgs.length > 0 && (launchProfileId !== "a1" || launchMode === "solo")) {
34
+ throw new Error("session selection requires the normal tabbed A1 launch");
35
+ }
33
36
  const output = options.output ?? process.stderr;
34
37
  const paths = resolveProductPaths(environment);
35
38
  await mkdir(paths.runtimeDir, { recursive: true, mode: 0o700 });
36
39
  const launchSelected = async (release) => {
37
- const result = await launchUi(release, environment, sessionArgs);
40
+ let residentContext = {};
41
+ // Compatibility: public launch preparation supplies PI_CODING_AGENT_DIR; the fallback keeps
42
+ // direct SDK/bootstrap callers hermetic while retaining their former config-root read.
43
+ const agentProfileDir = environment.PI_CODING_AGENT_DIR ?? paths.configDir;
44
+ const residentLaunchSettings = launchProfileId === "a1"
45
+ ? readResidentTabLaunchSettings(agentProfileDir, PRODUCT_IDENTITY.commandName, paths.configDir, launchProfileId)
46
+ : null;
47
+ const resident = await selectResidentHost({
48
+ profileId: launchProfileId,
49
+ currentLaunchContract: isCurrentLaunchContract(release),
50
+ solo: launchMode === "solo",
51
+ interactive: options.interactiveTerminal ?? (process.stdin.isTTY === true && process.stdout.isTTY === true),
52
+ ensureHost: async () => (options.ensureTerminalHost ?? ensureTerminalHost)({
53
+ releaseRoot: release.releaseRoot,
54
+ profileRoot: environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir,
55
+ dataDir: paths.dataDir,
56
+ runtimeDir: paths.runtimeDir,
57
+ environment,
58
+ maxConcurrentStarts: (await residentLaunchSettings).maxConcurrentStarts,
59
+ unresponsiveRestartSeconds: (await residentLaunchSettings).unresponsiveRestartSeconds,
60
+ }),
61
+ });
62
+ if (resident !== null) {
63
+ residentContext = resident.status === "ready" ? {
64
+ residentRootExecutable: resident.executable,
65
+ residentRootArguments: JSON.stringify([
66
+ "attach", "--endpoint", resident.endpoint, "--cwd", process.cwd(), "--clear-exit-ms", String(CLEAR_EXIT_INTERVAL_MS),
67
+ "--tabs-max", String((await residentLaunchSettings).tabsMax),
68
+ "--profile", environment[PRODUCT_IDENTITY.environment.profileHome] ?? paths.configDir,
69
+ "--data-dir", paths.dataDir, "--runtime-dir", paths.runtimeDir,
70
+ "--release-root", release.releaseRoot, "--node-executable", process.execPath,
71
+ ]),
72
+ } : { residentFallback: resident.reason };
73
+ }
74
+ const result = await launchUi(release, environment, sessionArgs, residentContext);
38
75
  if (!result.releaseSuperseded)
39
76
  return result.code;
40
77
  if (retriesRemaining > 0)
@@ -327,13 +364,15 @@ export async function startSupervisor(release, environment) {
327
364
  child.unref();
328
365
  return { ...attempt, childOutcome };
329
366
  }
330
- async function launchUi(release, environment, sessionArgs) {
367
+ async function launchUi(release, environment, sessionArgs, residentContext = {}) {
331
368
  await markStartupPhase(environment, "bootstrap-selected");
332
369
  const entry = await resolveReleaseEntryPoint(release, "bin/guardian.js");
333
370
  return await new Promise((resolvePromise, rejectPromise) => {
334
371
  let releaseSuperseded = false;
372
+ const releaseEnv = releaseEnvironment(environment, release, readLaunchContext(environment, "profile").launchProfile);
373
+ const launchEnvironment = Object.keys(residentContext).length === 0 ? releaseEnv : withLaunchContext(releaseEnv, { ...readLaunchContext(releaseEnv), ...residentContext }, process.platform, launchContractTarget(release));
335
374
  const child = spawn(process.execPath, [entry, ...sessionArgs], {
336
- env: releaseEnvironment(environment, release, readLaunchContext(environment, "profile").launchProfile),
375
+ env: launchEnvironment,
337
376
  stdio: ["inherit", "inherit", "inherit", "ipc"],
338
377
  windowsHide: false,
339
378
  });
@@ -357,12 +396,14 @@ async function launchUi(release, environment, sessionArgs) {
357
396
  * version instead of failing while an installation is being replaced.
358
397
  */
359
398
  export function releaseEnvironment(environment, release, profile) {
399
+ const launchMode = readLaunchContext(environment).launchMode;
360
400
  return withLaunchContext(environment, {
361
401
  releaseId: release.releaseId,
362
402
  releaseLayers: (release.dependencyLayers ?? []).map(layer => layer.layerId).join(","),
363
403
  releaseRoot: release.releaseRoot,
364
404
  releaseDigest: release.contentDigest,
365
405
  ...(profile === undefined ? {} : { launchProfile: profile }),
406
+ ...(launchMode === undefined ? {} : { launchMode }),
366
407
  }, process.platform, launchContractTarget(release));
367
408
  }
368
409
  export async function waitForVerifiedEndpoint(path, release, timeoutMs, startup) {
@@ -19,7 +19,7 @@ export interface ReleaseReferences {
19
19
  readonly rollback: string | null;
20
20
  readonly retention: readonly string[];
21
21
  }
22
- export type ExternalReleaseHoldAuthority = "agent" | "migration";
22
+ export type ExternalReleaseHoldAuthority = "agent" | "migration" | "resident";
23
23
  /** A current hold supplied by an authority that must participate in every reconciliation. */
24
24
  export interface ExternalReleaseHold {
25
25
  readonly authority: ExternalReleaseHoldAuthority;
@@ -12,7 +12,7 @@ const RELEASE_ID_PATTERN = /^[0-9A-Za-z.+_-]+-[a-f0-9]{20}$/;
12
12
  export function planProtectedReleases(state, inputs = {}) {
13
13
  const holds = inputs.externalHolds ?? [];
14
14
  for (const hold of holds) {
15
- if (hold.authority !== "agent" && hold.authority !== "migration")
15
+ if (hold.authority !== "agent" && hold.authority !== "migration" && hold.authority !== "resident")
16
16
  throw new Error(`unknown release hold authority: ${String(hold.authority)}`);
17
17
  if (!state.releases[hold.releaseId] && !state.cleanup.pending[hold.releaseId]) {
18
18
  throw new Error(`external ${hold.authority} hold names unknown release ${hold.releaseId}`);