@timurproko/a1 0.2.6 → 0.2.7-dev.746

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 (61) hide show
  1. package/bin/guardian.js +5 -0
  2. package/bin/tab-launch-context.d.ts +18 -0
  3. package/bin/tab-launch-context.js +36 -0
  4. package/bin/ui.js +14 -3
  5. package/dist/app/session-shell/helper-pool.d.ts +3 -5
  6. package/dist/app/session-shell/helper-pool.js +3 -5
  7. package/dist/app/session-shell/session-shell-root.d.ts +6 -1
  8. package/dist/app/session-shell/session-shell-root.js +3 -2
  9. package/dist/app/session-shell/session-shell.js +41 -24
  10. package/dist/app/session-shell/session-viewport-controller.d.ts +1 -1
  11. package/dist/app/session-shell/session-viewport-controller.js +3 -3
  12. package/dist/composition/owned-ui.d.ts +17 -0
  13. package/dist/composition/owned-ui.js +28 -1
  14. package/dist/foundation/launch-context/index.d.ts +6 -0
  15. package/dist/foundation/launch-context/index.js +15 -0
  16. package/dist/foundation/launch-guardian/main.d.ts +5 -0
  17. package/dist/foundation/launch-guardian/main.js +2 -1
  18. package/dist/foundation/release/bootstrap.d.ts +4 -0
  19. package/dist/foundation/release/bootstrap.js +33 -6
  20. package/dist/foundation/resident-tabs/bridge-client.d.ts +36 -0
  21. package/dist/foundation/resident-tabs/bridge-client.js +157 -0
  22. package/dist/foundation/resident-tabs/ensure-terminal-host.d.ts +28 -0
  23. package/dist/foundation/resident-tabs/ensure-terminal-host.js +115 -0
  24. package/dist/foundation/resident-tabs/index.d.ts +5 -0
  25. package/dist/foundation/resident-tabs/index.js +3 -0
  26. package/dist/foundation/resident-tabs/protocol.d.ts +12 -0
  27. package/dist/foundation/resident-tabs/protocol.js +195 -0
  28. package/dist/foundation/resident-tabs/routing.d.ts +10 -0
  29. package/dist/foundation/resident-tabs/routing.js +8 -0
  30. package/dist/foundation/resident-tabs/settings.d.ts +2 -0
  31. package/dist/foundation/resident-tabs/settings.js +17 -0
  32. package/dist/foundation/terminal-cleanup/index.d.ts +1 -1
  33. package/dist/foundation/terminal-cleanup/index.js +1 -1
  34. package/dist/foundation/terminal-cleanup/terminal-reset.d.ts +1 -0
  35. package/dist/foundation/terminal-cleanup/terminal-reset.js +1 -0
  36. package/dist/integrations/pi/components/owned-editor-ux.d.ts +1 -1
  37. package/dist/integrations/pi/components/owned-editor-ux.js +2 -2
  38. package/dist/integrations/pi/components/shell-shared-facade.d.ts +1 -1
  39. package/dist/native/darwin-arm64/manifest.json +1 -1
  40. package/dist/native/linux-x64/manifest.json +1 -1
  41. package/dist/native/win32-x64/manifest.json +2 -2
  42. package/dist/native/win32-x64/process-guardian.exe +0 -0
  43. package/dist/product-identity.d.ts +1 -1
  44. package/dist/product-identity.js +1 -1
  45. package/dist/product-identity.json +5 -0
  46. package/dist/ui/components/transcript-viewport.d.ts +6 -3
  47. package/dist/ui/components/transcript-viewport.js +13 -15
  48. package/dist/ui/settings/declarations.d.ts +13 -1
  49. package/dist/ui/settings/declarations.js +10 -1
  50. package/dist/ui/settings/index.d.ts +1 -1
  51. package/dist/ui/settings/index.js +1 -1
  52. package/dist/ui/settings/migrations.js +7 -0
  53. package/dist/ui/settings/resolution.d.ts +2 -0
  54. package/dist/ui/settings/resolution.js +13 -1
  55. package/docs/architecture/internal-naming.md +1 -1
  56. package/docs/architecture/resident-tabs-requirements.md +813 -0
  57. package/docs/architecture/resident-tabs.md +502 -0
  58. package/docs/architecture/terminal-host-proof-gate.md +2 -0
  59. package/docs/openspec-archive-automation.md +12 -11
  60. package/docs/validation.md +1 -1
  61. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { parseOwnedUiSettingsDocument, resolveResidentTabsSetting } from "../../ui/settings/index.js";
4
+ const MAX_SETTINGS_BYTES = 256 * 1024;
5
+ /** Read only the routing flag before any UI or extension module is loaded. */
6
+ export async function readResidentTabsSetting(configDir, profileId = "a1") {
7
+ const file = resolve(configDir, "settings", `${profileId}.json`);
8
+ try {
9
+ const raw = await readFile(file, "utf8");
10
+ if (Buffer.byteLength(raw, "utf8") > MAX_SETTINGS_BYTES)
11
+ return false;
12
+ return resolveResidentTabsSetting(parseOwnedUiSettingsDocument(raw));
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
@@ -1,3 +1,3 @@
1
- export { EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
1
+ export { CLEAR_EXIT_INTERVAL_MS, EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
2
2
  export { installFatalExit, writeFatalDiagnostic } from "./fatal-exit.js";
3
3
  export type { FatalExitOptions } from "./fatal-exit.js";
@@ -1,2 +1,2 @@
1
- export { EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
1
+ export { CLEAR_EXIT_INTERVAL_MS, EMERGENCY_TERMINAL_RESET, boundedCleanup, restoreAfterOwnedExit, restoreProcessTerminal } from "./terminal-reset.js";
2
2
  export { installFatalExit, writeFatalDiagnostic } from "./fatal-exit.js";
@@ -1,3 +1,4 @@
1
+ export declare const CLEAR_EXIT_INTERVAL_MS = 500;
1
2
  export declare const EMERGENCY_TERMINAL_RESET: string;
2
3
  export declare function restoreProcessTerminal(): void;
3
4
  export declare function restoreAfterOwnedExit(ownsTerminal: boolean, code: number | null, signal: string | null, restore?: () => void): number;
@@ -1,4 +1,5 @@
1
1
  import { writeSync } from "node:fs";
2
+ export const CLEAR_EXIT_INTERVAL_MS = 500;
2
3
  // Protocol: reset modes on both screens; setting keyboard flags to zero is idempotent,
3
4
  // unlike repeatedly popping a keyboard-protocol stack owned by a parent application.
4
5
  // Resetting parent-screen margins homes the cursor, so preserve its restored position.
@@ -44,7 +44,7 @@ export declare class OwnedEditorUxInterception {
44
44
  pasteClipboard(): boolean;
45
45
  }
46
46
  export interface PromptSelectionUxOptions {
47
- readonly copyText: (text: string) => void;
47
+ readonly copyText: (text: string, cut: boolean) => void;
48
48
  readonly readClipboardContent: () => Promise<PiShellClipboardContent | null>;
49
49
  readonly beginClipboardPaste?: () => PiShellPasteReservation;
50
50
  readonly beginTextPaste?: (text: string) => PiShellPasteReservation;
@@ -88,12 +88,12 @@ class PromptSelectionInterceptor {
88
88
  return;
89
89
  }
90
90
  if (this.keybindings.matches(data, "tui.input.copy") && this.hasSelection()) {
91
- this.options.copyText(this.options.expandCopiedText(this.#selectedText()));
91
+ this.options.copyText(this.options.expandCopiedText(this.#selectedText()), false);
92
92
  this.#collapseCopiedSelection();
93
93
  return;
94
94
  }
95
95
  if (this.keybindings.matches(data, "owned.editor.cut") && this.hasSelection()) {
96
- this.options.copyText(this.options.expandCopiedText(this.#selectedText()));
96
+ this.options.copyText(this.options.expandCopiedText(this.#selectedText()), true);
97
97
  if (!this.#deleteSelection())
98
98
  this.#deleteAtomicFocus();
99
99
  return;
@@ -185,7 +185,7 @@ export interface PiShellEditorOptions {
185
185
  readonly onFollowUp?: (() => void) | undefined;
186
186
  readonly onDequeue?: (() => void) | undefined;
187
187
  readonly onPromptSuggestionAccepted?: (text: string) => void;
188
- readonly onCopyText?: (text: string) => void;
188
+ readonly onCopyText?: (text: string, cut: boolean) => void;
189
189
  readonly readClipboardContent?: (signal?: AbortSignal) => Promise<PiShellClipboardContent | null>;
190
190
  readonly beginClipboardPaste?: () => PiShellPasteReservation;
191
191
  readonly beginTextPaste?: (text: string) => PiShellPasteReservation;
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-10T17:42:20.005Z",
8
+ "builtAt": "2026-10-12T06:07:21.173Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "51b0b2dc1c9ae577f2586c4217066929278726c2c15308d9144eb6f538e118ff",
@@ -5,7 +5,7 @@
5
5
  "platform": "linux",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-10T17:42:10.255Z",
8
+ "builtAt": "2026-10-12T06:06:54.072Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "ef62679e83bc1fad3d38f9f21675d9256f08d20e67fd4791571032115ca1b0e9",
@@ -5,10 +5,10 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-10-10T17:42:54.136Z",
8
+ "builtAt": "2026-10-12T06:07:34.911Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "bdafa323144718cc73b6e5a970608fd58420ec343f6fa7fac76aeb216edae485",
11
+ "sha256": "3f4e2d9c37a775b8a12f15539af5421e64f11149293ab2adc1bf3b06ce85b66d",
12
12
  "size": 288256
13
13
  },
14
14
  "provenance": {
@@ -1,4 +1,4 @@
1
- declare const ENVIRONMENT_KEYS: readonly ["clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "exitNoticePath", "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "terminalSessionId"];
1
+ declare const ENVIRONMENT_KEYS: readonly ["clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "exitNoticePath", "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "tabEndpoint", "tabId", "tabToken", "tabsEndpoint", "terminalHostPath", "terminalSessionId"];
2
2
  declare const FILESYSTEM_KEYS: readonly ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
3
3
  declare const STATE_KEYS: readonly ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "sessionRepositoryDirectory", "sessionWorktreeClaimDirectory"];
4
4
  declare const ENDPOINT_KEYS: readonly ["windowsPipeStem", "unixSocketFilename", "metadataFilename", "supervisorLogFilename", "databaseFilename"];
@@ -1,7 +1,7 @@
1
1
  import rawIdentity from "./product-identity.json" with { type: "json" };
2
2
  const ENVIRONMENT_KEYS = [
3
3
  "clipboardDiagnostics", "configDir", "dataDir", "databasePath", "developmentInstanceId", "developmentRoot", "endpoint", "exitNoticePath",
4
- "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "terminalSessionId",
4
+ "paneId", "probeTrace", "processGuardianPath", "prFooterPreview", "profileHome", "runtimeDir", "sessionRuntimeId", "skipVersionCheck", "startupTrace", "suggestionDiagnostics", "tabEndpoint", "tabId", "tabToken", "tabsEndpoint", "terminalHostPath", "terminalSessionId",
5
5
  ];
6
6
  const FILESYSTEM_KEYS = ["slug", "windowsDirectory", "unixDirectory", "temporaryPrefix"];
7
7
  const STATE_KEYS = ["windowsControlDirectory", "unixControlDirectory", "developmentDirectory", "piAgentProfile", "piVanillaProfile", "sessionRepositoryDirectory", "sessionWorktreeClaimDirectory"];
@@ -28,6 +28,11 @@
28
28
  "skipVersionCheck": "A1_SKIP_VERSION_CHECK",
29
29
  "startupTrace": "A1_STARTUP_TRACE",
30
30
  "suggestionDiagnostics": "A1_SUGGESTION_DIAGNOSTICS",
31
+ "tabEndpoint": "A1_TAB_ENDPOINT",
32
+ "tabId": "A1_TAB_ID",
33
+ "tabToken": "A1_TAB_TOKEN",
34
+ "tabsEndpoint": "A1_TABS_ENDPOINT",
35
+ "terminalHostPath": "A1_TERMINAL_HOST_PATH",
31
36
  "terminalSessionId": "A1_TERMINAL_SESSION_ID"
32
37
  },
33
38
  "state": {
@@ -36,6 +36,7 @@ export interface TranscriptViewportFrameInput {
36
36
  /** Final transient rows to bottom-align with unused viewport space while content fits. */
37
37
  readonly bottomAlignedTailRowCount?: number;
38
38
  readonly dockRows: readonly string[];
39
+ readonly explicitCopyDockRows?: number;
39
40
  readonly promptAnchors: readonly TranscriptPromptAnchor[];
40
41
  readonly width: number;
41
42
  readonly height: number;
@@ -141,6 +142,7 @@ export interface TranscriptViewportFrame {
141
142
  readonly cacheEntries: number;
142
143
  };
143
144
  }
145
+ type SelectionCopyIntent = "automatic" | "explicit";
144
146
  /**
145
147
  * A1-owned viewport state. It knows only rows and semantic prompt anchors; Pi
146
148
  * components and transcript payloads remain outside this neutral boundary.
@@ -170,9 +172,9 @@ export declare class TranscriptViewport {
170
172
  extendSelection(column: number, frameRow: number, now?: number, autoScroll?: boolean): boolean;
171
173
  releaseSelection(): boolean;
172
174
  clearSelection(): boolean;
173
- selectedText(): string | null;
175
+ selectedText(intent?: SelectionCopyIntent): string | null;
174
176
  /** Captures visible selected text, not off-screen history. */
175
- captureSelectedText(): SelectionCopySnapshot | null;
177
+ captureSelectedText(intent?: SelectionCopyIntent): SelectionCopySnapshot | null;
176
178
  scrollBy(lines: number, now?: number): boolean;
177
179
  scrollTo(position: number, now?: number): boolean;
178
180
  scrollToEnd(now?: number): boolean;
@@ -184,6 +186,7 @@ export declare class TranscriptViewport {
184
186
  clearTransient(): void;
185
187
  compose(input: TranscriptViewportFrameInput): TranscriptViewportFrame;
186
188
  /** Reuses an established transcript frame when only same-height dock rows changed. */
187
- composeDockOnly(dockRows: readonly string[], width: number, height: number): TranscriptViewportFrame | null;
189
+ composeDockOnly(dockRows: readonly string[], width: number, height: number, explicitCopyDockRows?: number): TranscriptViewportFrame | null;
188
190
  }
189
191
  export declare function assertTranscriptViewportFrameDescriptor(descriptor: TranscriptViewportFrameDescriptor): void;
192
+ export {};
@@ -63,6 +63,7 @@ export class TranscriptViewport {
63
63
  #contentWidth = 0;
64
64
  #selectionWidth = 0;
65
65
  #viewportHeight = 0;
66
+ #explicitCopyDockRows = 0;
66
67
  #firstSelectableRow = 0;
67
68
  #selectionRowAnchors = [];
68
69
  #frameId = 0;
@@ -178,22 +179,19 @@ export class TranscriptViewport {
178
179
  this.#selectionRevision += 1;
179
180
  return true;
180
181
  }
181
- selectedText() {
182
+ selectedText(intent = "automatic") {
182
183
  const selection = this.#visibleSelection();
183
184
  if (selection === undefined)
184
185
  return null;
185
- // Invariant: the blue range spans the complete frame, but automatic and Ctrl+C frame copy
186
- // admit only persistent transcript rows. Transient status, suggestions, input, and footer
187
- // chrome remain visually selectable without becoming clipboard payload.
188
186
  const copiedLines = Array.from({ length: Math.max(0, selection.end.line - selection.start.line + 1) }, (_value, index) => selection.start.line + index).filter(line => {
189
187
  const anchor = this.#selectionRowAnchors[line];
190
- return anchor?.kind === "document" && anchor.row < this.#selectableDocumentRowCount;
188
+ return anchor?.kind === "document" && anchor.row < this.#selectableDocumentRowCount
189
+ || intent === "explicit" && anchor?.kind === "dock" && anchor.fromBottom < this.#explicitCopyDockRows;
191
190
  });
192
191
  if (copiedLines.length === 0)
193
192
  return null;
194
- // Rationale: a prompt selected by itself uses semantic prompt text. Once surrounding transcript text
195
- // participates, preserve the prompt's visible prefix, alignment, and timestamp in the bulk range.
196
- const prompt = this.#promptAnchors.find(candidate => copiedLines.every(line => {
193
+ const transcriptLines = copiedLines.filter(line => this.#selectionRowAnchors[line]?.kind === "document");
194
+ const prompt = transcriptLines.length === 0 ? undefined : this.#promptAnchors.find(candidate => transcriptLines.every(line => {
197
195
  const anchor = this.#selectionRowAnchors[line];
198
196
  return anchor?.kind === "document" && anchor.row >= candidate.firstRow && anchor.row <= candidate.lastRow;
199
197
  }));
@@ -208,11 +206,11 @@ export class TranscriptViewport {
208
206
  return text.length === 0 ? null : text;
209
207
  }
210
208
  /** Captures visible selected text, not off-screen history. */
211
- captureSelectedText() {
212
- if (!this.#copyableSelection)
209
+ captureSelectedText(intent = "automatic") {
210
+ if (intent === "automatic" && !this.#copyableSelection)
213
211
  return null;
214
212
  const started = performance.now();
215
- let text = this.selectedText();
213
+ let text = this.selectedText(intent);
216
214
  if (text === null)
217
215
  return null;
218
216
  text = text.trim();
@@ -315,6 +313,7 @@ export class TranscriptViewport {
315
313
  this.#contentWidth = 0;
316
314
  this.#selectionWidth = 0;
317
315
  this.#viewportHeight = 0;
316
+ this.#explicitCopyDockRows = 0;
318
317
  this.clearTransient();
319
318
  this.#frame = null;
320
319
  }
@@ -330,6 +329,7 @@ export class TranscriptViewport {
330
329
  const theme = input.theme ?? PLAIN_THEME;
331
330
  const previousDescriptor = this.#frame?.descriptor ?? null;
332
331
  const dock = input.dockRows.length > height ? input.dockRows.slice(-height) : [...input.dockRows];
332
+ this.#explicitCopyDockRows = clamp(input.explicitCopyDockRows ?? 0, 0, dock.length);
333
333
  const viewportHeight = Math.max(0, height - dock.length);
334
334
  const bottomAlignedTailRowCount = clamp(input.bottomAlignedTailRowCount ?? 0, 0, input.documentRows.length);
335
335
  const transientAlignmentGapRows = bottomAlignedTailRowCount > 0
@@ -414,8 +414,6 @@ export class TranscriptViewport {
414
414
  visible[0] = quiet && !this.#stickyHovered ? theme.quietSticky(sticky) : sticky;
415
415
  visibleSource[0] = sourceRow;
416
416
  }
417
- // Invariant: selection uses exact visible source rows within the rendered
418
- // content boundary; paint-only guards and the rail gutter stay presentation-only.
419
417
  this.#selectionRows = [
420
418
  ...visibleSource.map(row => truncateToWidth(row, contentWidth)),
421
419
  ...dock,
@@ -426,7 +424,6 @@ export class TranscriptViewport {
426
424
  ? viewportSelectionAnchor(documentRow, documentRows[documentRow] ?? "", this.#selectableDocumentRowCount, documentRows.length)
427
425
  : { kind: "screen", row, source: this.#selectionRows[row] ?? "" };
428
426
  });
429
- // Invariant: a pinned prompt is chrome over its hidden source row, which clips like off-screen rows.
430
427
  this.#firstSelectableRow = stickyActive && viewportHeight > 0 ? 1 : 0;
431
428
  this.#selectionRowAnchors = [
432
429
  ...visibleAnchors,
@@ -609,7 +606,7 @@ export class TranscriptViewport {
609
606
  return frame;
610
607
  }
611
608
  /** Reuses an established transcript frame when only same-height dock rows changed. */
612
- composeDockOnly(dockRows, width, height) {
609
+ composeDockOnly(dockRows, width, height, explicitCopyDockRows = 0) {
613
610
  const previous = this.#frame;
614
611
  const boundedWidth = Math.max(1, width);
615
612
  const boundedHeight = Math.max(0, height);
@@ -645,6 +642,7 @@ export class TranscriptViewport {
645
642
  };
646
643
  assertTranscriptViewportFrameDescriptor(descriptor);
647
644
  this.#selectionRows = [...this.#selectionRows.slice(0, viewportHeight), ...dock].slice(0, boundedHeight);
645
+ this.#explicitCopyDockRows = clamp(explicitCopyDockRows, 0, dock.length);
648
646
  const frame = {
649
647
  rows: [...previous.rows.slice(0, viewportHeight), ...dock].slice(0, boundedHeight),
650
648
  contentWidth: previous.contentWidth,
@@ -1,4 +1,4 @@
1
- export declare const OWNED_UI_SETTINGS_VERSION = 13;
1
+ export declare const OWNED_UI_SETTINGS_VERSION = 14;
2
2
  export type OwnedUiSettingValue = string | number | boolean;
3
3
  export type OwnedUiSettingApplication = "live" | "restart";
4
4
  export interface OwnedUiSettingDeclaration {
@@ -164,6 +164,18 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
164
164
  defaultValue: 8;
165
165
  allowedValues: readonly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
166
166
  }>;
167
+ readonly residentTabs: Readonly<{
168
+ id: "residentTabs";
169
+ label: "Resident tabs";
170
+ section: Readonly<{
171
+ id: "generic";
172
+ title: "Generic";
173
+ }>;
174
+ description: "Keep bare A1 running when its terminal closes and reattach on the next launch. Resident tabs have no stop command yet; turning this off prevents new resident launches but does not stop an existing tab.";
175
+ application: "restart";
176
+ defaultValue: false;
177
+ allowedValues: readonly [true, false];
178
+ }>;
167
179
  }>;
168
180
  export type OwnedSettingId = keyof typeof OWNED_SETTING_DECLARATIONS;
169
181
  /** The value type a setting's declaration allows: a literal union for a choice, `number` for a stepped range. */
@@ -1,4 +1,4 @@
1
- export const OWNED_UI_SETTINGS_VERSION = 13;
1
+ export const OWNED_UI_SETTINGS_VERSION = 14;
2
2
  const UI_ACCENT_COLOR_CHOICES = Object.freeze([
3
3
  "purple", "blue", "cyan", "green", "orange", "pink",
4
4
  ]);
@@ -124,6 +124,15 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
124
124
  defaultValue: 8,
125
125
  allowedValues: Object.freeze([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
126
126
  }),
127
+ residentTabs: Object.freeze({
128
+ id: "residentTabs",
129
+ label: "Resident tabs",
130
+ section: GENERIC_SECTION,
131
+ description: "Keep bare A1 running when its terminal closes and reattach on the next launch. Resident tabs have no stop command yet; turning this off prevents new resident launches but does not stop an existing tab.",
132
+ application: "restart",
133
+ defaultValue: false,
134
+ allowedValues: Object.freeze([true, false]),
135
+ }),
127
136
  });
128
137
  /** The declarations in declaration order, for resolution, persistence, and the settings screen. */
129
138
  export const OWNED_UI_SETTING_DECLARATIONS = Object.freeze(Object.values(OWNED_SETTING_DECLARATIONS));
@@ -2,7 +2,7 @@ export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING
2
2
  export type { OwnedSettingId, OwnedSettingValueOf, OwnedUiSettingApplication, OwnedUiSettingDeclaration, OwnedUiSettingValue } from "./declarations.js";
3
3
  export { OWNED_UI_SETTINGS_MIGRATIONS, assertOwnedUiSettingsMigrations, migrationsFrom } from "./migrations.js";
4
4
  export type { OwnedUiSettingsMigration } from "./migrations.js";
5
- export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, settingValue } from "./resolution.js";
5
+ export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, resolveResidentTabsSetting, settingValue } from "./resolution.js";
6
6
  export type { OwnedUiResolvedSetting, OwnedUiSettingSource, OwnedUiSettingsDocument, OwnedUiSettingsNotice, OwnedUiSettingsNoticeCode, OwnedUiSettingsResolution, ResolveOwnedUiSettingsInput, } from "./resolution.js";
7
7
  export { AGENT_SECTION_ID, buildOwnedUiSettingsSections, findOwnedUiSettingsEntry } from "./sections.js";
8
8
  export type { AgentSettingsSnapshot, BuildOwnedUiSettingsSectionsInput, OwnedUiSettingsBackend, OwnedUiSettingsEntry, OwnedUiSettingsSection, } from "./sections.js";
@@ -1,5 +1,5 @@
1
1
  export { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, OWNED_UI_SETTING_DECLARATIONS, assertOwnedUiSettingDeclarations, findOwnedUiSettingDeclaration, isOwnedSettingId, } from "./declarations.js";
2
2
  export { OWNED_UI_SETTINGS_MIGRATIONS, assertOwnedUiSettingsMigrations, migrationsFrom } from "./migrations.js";
3
- export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, settingValue } from "./resolution.js";
3
+ export { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, resolveResidentTabsSetting, settingValue } from "./resolution.js";
4
4
  export { AGENT_SECTION_ID, buildOwnedUiSettingsSections, findOwnedUiSettingsEntry } from "./sections.js";
5
5
  export { OwnedSettingsManager } from "./manager.js";
@@ -104,6 +104,13 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
104
104
  return { ...values };
105
105
  },
106
106
  }),
107
+ Object.freeze({
108
+ to: 14,
109
+ description: "Introduce the opt-in resident tabs preview with a disabled default.",
110
+ migrate(values) {
111
+ return { ...values };
112
+ },
113
+ }),
107
114
  ]);
108
115
  export function assertOwnedUiSettingsMigrations(migrations, currentVersion = OWNED_UI_SETTINGS_VERSION) {
109
116
  const firstProduced = currentVersion - migrations.length + 1;
@@ -42,4 +42,6 @@ export interface ResolveOwnedUiSettingsInput {
42
42
  */
43
43
  export declare function resolveOwnedUiSettings(input: ResolveOwnedUiSettingsInput): OwnedUiSettingsResolution;
44
44
  export declare function settingValue(resolution: OwnedUiSettingsResolution, id: string): OwnedUiSettingValue | null;
45
+ /** Resolve the launch-routing flag without constructing the UI settings manager. */
46
+ export declare function resolveResidentTabsSetting(document: OwnedUiSettingsDocument | null): boolean;
45
47
  export declare function documentFrom(resolution: OwnedUiSettingsResolution): OwnedUiSettingsDocument;
@@ -1,4 +1,4 @@
1
- import { OWNED_UI_SETTINGS_VERSION, findOwnedUiSettingDeclaration, } from "./declarations.js";
1
+ import { OWNED_SETTING_DECLARATIONS, OWNED_UI_SETTINGS_VERSION, findOwnedUiSettingDeclaration, } from "./declarations.js";
2
2
  import { migrationsFrom } from "./migrations.js";
3
3
  export function parseOwnedUiSettingsDocument(raw) {
4
4
  let parsed;
@@ -122,6 +122,18 @@ export function resolveOwnedUiSettings(input) {
122
122
  export function settingValue(resolution, id) {
123
123
  return resolution.settings.find(setting => setting.declaration.id === id)?.value ?? null;
124
124
  }
125
+ /** Resolve the launch-routing flag without constructing the UI settings manager. */
126
+ export function resolveResidentTabsSetting(document) {
127
+ if (document === null || document.version !== OWNED_UI_SETTINGS_VERSION)
128
+ return false;
129
+ const resolution = resolveOwnedUiSettings({
130
+ declarations: [OWNED_SETTING_DECLARATIONS.residentTabs],
131
+ migrations: [],
132
+ document,
133
+ currentVersion: OWNED_UI_SETTINGS_VERSION,
134
+ });
135
+ return settingValue(resolution, "residentTabs") === true;
136
+ }
125
137
  export function documentFrom(resolution) {
126
138
  const values = { ...resolution.preserved };
127
139
  for (const setting of resolution.settings) {
@@ -31,7 +31,7 @@ The exact externally branded key/member authority is `config/internal-naming-pol
31
31
  | `A1_PROCESS_GUARDIAN_PATH` | Explicit executable override in `src/foundation/process-containment/native-guardian-containment.ts`, also configured by CI; exact existing exposure-review entry. |
32
32
  | `A1_PROBE_TRACE` | Opt-in diagnostics read by native terminal-host code; exact existing exposure-review entry. |
33
33
  | `A1_BUILD_VERBOSE`, `A1_DEV_SHELL` | Developer invocation controls in the build helper and development launcher; exact existing exposure-review entries. |
34
- | `A1_PANE_ID`, `A1_TERMINAL_SESSION_ID` | Produced by `native/terminal-host/src/workspace.rs` and exported to launched programs; external integration names, unchanged. |
34
+ | `A1_PANE_ID`, `A1_TERMINAL_SESSION_ID` | Reserved compatibility identities retained in the product identity manifest; the resident host does not currently emit them. |
35
35
 
36
36
  Pending review preserves a possible external interface, not an obsolete private alias. Once a setting is established as private, it cannot keep its branded spelling through that category.
37
37