@timurproko/a1 0.2.7-dev.1238 → 0.2.7-dev.1239

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.
@@ -14,7 +14,7 @@ export function createOwnedSettingsManager(options) {
14
14
  legacyConfigDir: productPaths.configDir,
15
15
  ...(options.agent === undefined ? {} : { agent: options.agent }),
16
16
  ...(options.agentProvider === undefined ? {} : { agentProvider: options.agentProvider }),
17
- ...(options.hideTabSettings ? { hiddenOwnedSettingIds: ["tabsAutoName", "tabsPosition", "tabBarPlusMenu"] } : {}),
17
+ ...(options.hideTabSettings ? { hiddenOwnedSettingIds: ["tabsAutoName", "tabsPosition", "tabBarPlusMenu", "tabBarRightItems"] } : {}),
18
18
  hiddenAgentSettingIds: ["fullscreenWheelScrollLines"],
19
19
  agentSettingLabelOverrides: { fullscreenCopyOnSelect: "Copy on select" },
20
20
  });
@@ -4,13 +4,41 @@ class ReleaseUpdateObservation {
4
4
  #controller = new AbortController();
5
5
  #timer;
6
6
  #announced = null;
7
+ #installedAnnounced = false;
7
8
  #eligible;
8
9
  #cadence = null;
9
10
  #nextDelay = null;
10
11
  #started = false;
11
12
  #running = false;
12
13
  #refresh = false;
14
+ #stopActiveReleaseObservation = () => { };
13
15
  constructor(input) { this.#input = input; this.#eligible = input.mode === "auto"; }
16
+ #announce(status, version) {
17
+ const key = `${status}:${version}`;
18
+ if (key === this.#announced || (status === "installed" && this.#installedAnnounced) || this.#controller.signal.aborted)
19
+ return;
20
+ this.#announced = key;
21
+ if (status === "installed") {
22
+ this.#installedAnnounced = true;
23
+ this.#eligible = false;
24
+ if (this.#timer !== undefined)
25
+ clearTimeout(this.#timer);
26
+ }
27
+ this.#input.backend.extensions.announceReleaseUpdate({ status, version });
28
+ }
29
+ async #startActiveReleaseObservation() {
30
+ const runningVersion = this.#input.packageVersion ?? await this.#input.readPackageVersion();
31
+ if (this.#controller.signal.aborted)
32
+ return;
33
+ const module = await import("../foundation/release/automatic-update.js");
34
+ if (this.#controller.signal.aborted)
35
+ return;
36
+ this.#stopActiveReleaseObservation = module.observeNewerActiveRelease({
37
+ runningVersion,
38
+ signal: this.#controller.signal,
39
+ onNewer: version => this.#announce("installed", version),
40
+ });
41
+ }
14
42
  async #observe() {
15
43
  // Performance: registry modules and their locks stay outside the first-frame startup graph.
16
44
  const releases = await import("../foundation/release/latest-release.js");
@@ -41,22 +69,18 @@ class ReleaseUpdateObservation {
41
69
  }
42
70
  if (input.mode !== "auto")
43
71
  return;
44
- const announce = (status, version) => {
45
- const actual = input.clientVersion?.();
46
- const key = `${status}:${version}`;
47
- if (key === this.#announced || this.#controller.signal.aborted
48
- || status !== "blocked-cleared" && actual && !releases.isNewerRelease(version, actual))
49
- return;
50
- this.#announced = key;
51
- input.backend.extensions.announceReleaseUpdate({ status, version });
52
- };
53
72
  const installed = await import("../foundation/release/automatic-update.js")
54
73
  .then(module => module.requestAutomaticUpdate({
55
74
  release, runningVersion, signal: this.#controller.signal,
56
- onActivationBlocked: version => announce(version === null ? "blocked-cleared" : "blocked", version ?? release.version),
75
+ onActivationBlocked: version => {
76
+ const clientVersion = input.clientVersion?.();
77
+ if (version !== null && clientVersion && !releases.isNewerRelease(version, clientVersion))
78
+ return;
79
+ this.#announce(version === null ? "blocked-cleared" : "blocked", version ?? release.version);
80
+ },
57
81
  }));
58
82
  if (installed !== null)
59
- announce("installed", installed);
83
+ this.#announce("installed", installed);
60
84
  }
61
85
  async #run() {
62
86
  if (this.#running) {
@@ -83,13 +107,18 @@ class ReleaseUpdateObservation {
83
107
  if (this.#started || this.#controller.signal.aborted)
84
108
  return;
85
109
  this.#started = true;
110
+ void this.#startActiveReleaseObservation().catch(() => undefined);
86
111
  void this.#run();
87
112
  }
88
- stop() { this.#controller.abort(); if (this.#timer !== undefined)
89
- clearTimeout(this.#timer); }
113
+ stop() {
114
+ this.#controller.abort();
115
+ this.#stopActiveReleaseObservation();
116
+ if (this.#timer !== undefined)
117
+ clearTimeout(this.#timer);
118
+ }
90
119
  clientChanged() {
91
120
  this.#announced = null;
92
- if (!this.#started || this.#controller.signal.aborted || this.#input.mode === "no")
121
+ if (!this.#started || this.#controller.signal.aborted || this.#installedAnnounced || this.#input.mode === "no")
93
122
  return;
94
123
  if (this.#timer !== undefined)
95
124
  clearTimeout(this.#timer);
@@ -6,17 +6,23 @@ export interface PlusMenuKind {
6
6
  readonly key: string;
7
7
  readonly label: string;
8
8
  }
9
- /**
10
- * The plus-menu dialog: every tab kind with `●` when `+` offers it and `○` when not, filtered by typed
11
- * text. It owns only its filter and the row in hand; the enabled record and its saving stay with the
12
- * Settings screen that opened it.
13
- */
14
- export declare class PlusMenuDialog {
9
+ /** Presentation copy for a searchable ordered flag multi-select dialog. */
10
+ export interface FlagToggleDialogPresentation {
11
+ readonly title: string;
12
+ readonly description: string;
13
+ readonly empty: string;
14
+ }
15
+ /** Owns only filtering and selection; the Settings screen owns enabled state and persistence. */
16
+ export declare class FlagToggleDialog {
15
17
  #private;
16
- constructor(kinds: readonly PlusMenuKind[]);
18
+ constructor(kinds: readonly PlusMenuKind[], presentation: FlagToggleDialogPresentation);
17
19
  /** The kinds the filter keeps, in kind order. */
18
20
  shown(): readonly PlusMenuKind[];
19
21
  /** Handles one key by its resolved name; `toggle` flips a kind. Returns whether the dialog closed. */
20
22
  handle(name: string, data: string, toggle: (key: string) => void): "close" | "handled";
21
23
  lines(enabled: Readonly<Record<string, boolean | string>>, width: number, theme: UiTheme): readonly string[];
22
24
  }
25
+ /** The tab bar's specialized flag dialog, retained as the public Plus Menu surface. */
26
+ export declare class PlusMenuDialog extends FlagToggleDialog {
27
+ constructor(kinds: readonly PlusMenuKind[]);
28
+ }
@@ -1,8 +1,11 @@
1
1
  import { LineInput, handleLineInputKey, padToWidth, renderInputRow, renderShortcutHintsWithClose, truncateToWidth, } from "../../ui/components/index.js";
2
2
  /** The A1 setting holding the tab kinds the strip's `+` offers. */
3
3
  export const PLUS_MENU_SETTING_ID = "tabBarPlusMenu";
4
- const TITLE = "Plus Menu";
5
- const DESCRIPTION = "Choose what the + button in the tab bar can add.";
4
+ const PLUS_MENU_PRESENTATION = Object.freeze({
5
+ title: "Plus Menu",
6
+ description: "Choose what the + button in the tab bar can add.",
7
+ empty: "No tabs match",
8
+ });
6
9
  const HINTS = [
7
10
  { key: "Type", action: "search" },
8
11
  { key: "↑↓", action: "navigate" },
@@ -10,17 +13,15 @@ const HINTS = [
10
13
  { key: "esc", action: "close" },
11
14
  ];
12
15
  const PADDING_X = 2;
13
- /**
14
- * The plus-menu dialog: every tab kind with `●` when `+` offers it and `○` when not, filtered by typed
15
- * text. It owns only its filter and the row in hand; the enabled record and its saving stay with the
16
- * Settings screen that opened it.
17
- */
18
- export class PlusMenuDialog {
16
+ /** Owns only filtering and selection; the Settings screen owns enabled state and persistence. */
17
+ export class FlagToggleDialog {
19
18
  #kinds;
19
+ #presentation;
20
20
  #filter = new LineInput("");
21
21
  #index = 0;
22
- constructor(kinds) {
22
+ constructor(kinds, presentation) {
23
23
  this.#kinds = kinds;
24
+ this.#presentation = presentation;
24
25
  }
25
26
  /** The kinds the filter keeps, in kind order. */
26
27
  shown() {
@@ -60,7 +61,7 @@ export class PlusMenuDialog {
60
61
  const shown = this.shown();
61
62
  const index = Math.min(this.#index, Math.max(0, shown.length - 1));
62
63
  const rows = shown.length === 0
63
- ? [row(theme.fg("dim", "No tabs match"))]
64
+ ? [row(theme.fg("dim", this.#presentation.empty))]
64
65
  : shown.map((kind, position) => {
65
66
  const selected = position === index;
66
67
  const cursor = selected ? "→ " : " ";
@@ -70,8 +71,8 @@ export class PlusMenuDialog {
70
71
  });
71
72
  return [
72
73
  blank,
73
- row(theme.bold(theme.fg("accent", TITLE))),
74
- row(theme.fg("dim", DESCRIPTION)),
74
+ row(theme.bold(theme.fg("accent", this.#presentation.title))),
75
+ row(theme.fg("dim", this.#presentation.description)),
75
76
  blank,
76
77
  ...renderInputRow(this.#filter, contentWidth, { ruled: false, promptGlyph: "> ", theme }).lines.map(line => row(line)),
77
78
  blank,
@@ -82,3 +83,7 @@ export class PlusMenuDialog {
82
83
  ];
83
84
  }
84
85
  }
86
+ /** The tab bar's specialized flag dialog, retained as the public Plus Menu surface. */
87
+ export class PlusMenuDialog extends FlagToggleDialog {
88
+ constructor(kinds) { super(kinds, PLUS_MENU_PRESENTATION); }
89
+ }
@@ -1,6 +1,6 @@
1
1
  import { DIALOG_BACK_SHORTCUT_HINT, DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
2
2
  import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, configureDialogChoiceCapacity, configureDialogTextRows, configureDialogWidth, numericValues, RAIL_COLUMNS, renderCenteredDialogOverlay, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, formatLocalTime, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
3
- import { PLUS_MENU_SETTING_ID, PlusMenuDialog } from "./plus-menu-dialog.js";
3
+ import { FlagToggleDialog, PLUS_MENU_SETTING_ID, PlusMenuDialog } from "./plus-menu-dialog.js";
4
4
  import { SETTINGS_APP_ID } from "./settings-route.js";
5
5
  export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
6
6
  const SCOPE = SETTINGS_APP_ID;
@@ -22,6 +22,7 @@ const SCROLL_LINGER_MS = 900;
22
22
  const SCROLL_LINGER_REPAINT_MS = 925;
23
23
  /** What a structured value offers instead of printing itself. */
24
24
  const CONFIGURE = "configure";
25
+ const TAB_BAR_RIGHT_ITEMS_SETTING_ID = "tabBarRightItems";
25
26
  export const SETTINGS_SHORTCUTS = new ShortcutRegistry();
26
27
  SETTINGS_SHORTCUTS.declare({ key: "edit", scope: SCOPE, description: "Search settings", section: "Change", hint: { keys: "Type", does: "search" } }, "edit-filter");
27
28
  SETTINGS_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Previous setting", section: "Navigate", hint: { keys: "↑↓", does: "navigate" } }, "move-up");
@@ -539,7 +540,13 @@ export class SettingsApp {
539
540
  entry,
540
541
  flags: entry.flags.map(flag => flag.key),
541
542
  modelDialog,
542
- plusMenu: entry.backend === "a1" && entry.id === PLUS_MENU_SETTING_ID ? new PlusMenuDialog(entry.flags) : null,
543
+ flagDialog: entry.backend !== "a1" ? null
544
+ : entry.id === PLUS_MENU_SETTING_ID ? new PlusMenuDialog(entry.flags)
545
+ : entry.id === TAB_BAR_RIGHT_ITEMS_SETTING_ID ? new FlagToggleDialog(entry.flags, {
546
+ title: "Tab Bar Right Side",
547
+ description: "Choose what the right side of the tab bar shows.",
548
+ empty: "No items match",
549
+ }) : null,
543
550
  filter: modelDialog === null ? null : new LineInput(""),
544
551
  step: "model",
545
552
  modelKey: null,
@@ -555,8 +562,8 @@ export class SettingsApp {
555
562
  return { consumed: false };
556
563
  if (open.modelDialog !== null)
557
564
  return this.#modelDialogKey(open, data);
558
- if (open.plusMenu !== null) {
559
- const outcome = open.plusMenu.handle(KEYS[data] ?? data, data, key => this.#toggleFlag(open.flags.indexOf(key)));
565
+ if (open.flagDialog !== null) {
566
+ const outcome = open.flagDialog.handle(KEYS[data] ?? data, data, key => this.#toggleFlag(open.flags.indexOf(key)));
560
567
  if (outcome === "close")
561
568
  this.#structured = null;
562
569
  return { consumed: true };
@@ -1029,8 +1036,8 @@ export class SettingsApp {
1029
1036
  #dialogLines(open, width, height, theme) {
1030
1037
  if (open.modelDialog !== null)
1031
1038
  return this.#modelDialogLines(open, width, height, theme);
1032
- if (open.plusMenu !== null)
1033
- return open.plusMenu.lines(open.record, width, theme);
1039
+ if (open.flagDialog !== null)
1040
+ return open.flagDialog.lines(open.record, width, theme);
1034
1041
  const rows = open.flags.map(key => {
1035
1042
  const declared = open.entry.flags.find(flag => flag.key === key);
1036
1043
  return {
@@ -2,6 +2,7 @@ import { type AvailableRelease, type ReleaseChannel } from "./latest-release.js"
2
2
  export declare const AUTOMATIC_UPDATE_STATE_SCHEMA = 1;
3
3
  export declare const AUTOMATIC_UPDATE_RETRY_MS: number;
4
4
  export declare const AUTOMATIC_UPDATE_ACTIVATION_RETRY_MS: number;
5
+ export declare const ACTIVE_RELEASE_FALLBACK_MS = 5000;
5
6
  export interface AutomaticUpdateState {
6
7
  readonly schema: typeof AUTOMATIC_UPDATE_STATE_SCHEMA;
7
8
  readonly channel: ReleaseChannel;
@@ -18,6 +19,17 @@ export declare class AutomaticUpdateStateStore {
18
19
  read(): Promise<AutomaticUpdateState | null>;
19
20
  write(value: AutomaticUpdateState): Promise<void>;
20
21
  }
22
+ export interface ActiveReleaseObservationOptions {
23
+ readonly runningVersion: string;
24
+ readonly environment?: NodeJS.ProcessEnv;
25
+ readonly signal?: AbortSignal;
26
+ readonly onNewer: (version: string) => void;
27
+ readonly fallbackMs?: number;
28
+ }
29
+ /** Read the approved active release only when this process executes an older release on that channel. */
30
+ export declare function readNewerActiveRelease(runningVersion: string, environment?: NodeJS.ProcessEnv): Promise<string | null>;
31
+ /** Observe local release activation promptly, with a bounded fallback for missed filesystem events. */
32
+ export declare function observeNewerActiveRelease(options: ActiveReleaseObservationOptions): () => void;
21
33
  export interface AutomaticUpdateRequestOptions {
22
34
  readonly release: AvailableRelease;
23
35
  readonly runningVersion: string;
@@ -1,7 +1,8 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
- import { mkdir, open, readFile, rename, rm } from "node:fs/promises";
4
- import { dirname, resolve } from "node:path";
3
+ import { watch } from "node:fs";
4
+ import { mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
5
+ import { basename, dirname, resolve } from "node:path";
5
6
  import { valid as validSemver } from "semver";
6
7
  import { readLaunchContext } from "../launch-context/index.js";
7
8
  import { resolveProductPaths } from "../lifecycle/index.js";
@@ -19,6 +20,7 @@ const AUTOMATIC_UPDATE_STATE_FILENAME = "automatic-update.json";
19
20
  const MAX_STATE_BYTES = 16_384;
20
21
  const OBSERVER_POLL_MS = 250;
21
22
  const WORKER_START_GRACE_MS = 5_000;
23
+ export const ACTIVE_RELEASE_FALLBACK_MS = 5_000;
22
24
  export class AutomaticUpdateStateStore {
23
25
  path;
24
26
  constructor(dataDir) { this.path = resolve(dataDir, AUTOMATIC_UPDATE_STATE_FILENAME); }
@@ -59,6 +61,82 @@ export class AutomaticUpdateStateStore {
59
61
  }
60
62
  }
61
63
  }
64
+ /** Read the approved active release only when this process executes an older release on that channel. */
65
+ export async function readNewerActiveRelease(runningVersion, environment = process.env) {
66
+ const channel = releaseChannelOf(runningVersion);
67
+ if (channel === null)
68
+ return null;
69
+ try {
70
+ const paths = resolveProductPaths(environment);
71
+ const active = await activeRelease(paths.dataDir);
72
+ return activeNewerPackageVersion(active, runningVersion, channel) ?? null;
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ /** Observe local release activation promptly, with a bounded fallback for missed filesystem events. */
79
+ export function observeNewerActiveRelease(options) {
80
+ if (releaseChannelOf(options.runningVersion) === null || options.signal?.aborted)
81
+ return () => { };
82
+ const paths = resolveProductPaths(options.environment ?? process.env);
83
+ const store = new CohortStateStore(paths.dataDir);
84
+ let closed = false;
85
+ let checking = false;
86
+ let requested = false;
87
+ let watcher = null;
88
+ let signature;
89
+ const stop = () => {
90
+ if (closed)
91
+ return;
92
+ closed = true;
93
+ clearInterval(fallback);
94
+ watcher?.close();
95
+ options.signal?.removeEventListener("abort", stop);
96
+ };
97
+ const check = async () => {
98
+ if (closed)
99
+ return;
100
+ if (checking) {
101
+ requested = true;
102
+ return;
103
+ }
104
+ checking = true;
105
+ try {
106
+ const version = await readNewerActiveRelease(options.runningVersion, options.environment);
107
+ if (version !== null && !closed) {
108
+ stop();
109
+ options.onNewer(version);
110
+ }
111
+ }
112
+ finally {
113
+ checking = false;
114
+ if (requested && !closed) {
115
+ requested = false;
116
+ void check();
117
+ }
118
+ }
119
+ };
120
+ const checkChanged = async (force) => {
121
+ const next = await stat(store.path).then(value => `${value.dev}:${value.ino}:${value.ctimeMs}:${value.mtimeMs}:${value.size}`, () => null);
122
+ if (!force && next === signature)
123
+ return;
124
+ signature = next;
125
+ await check();
126
+ };
127
+ const fallback = setInterval(() => { void checkChanged(false); }, options.fallbackMs ?? ACTIVE_RELEASE_FALLBACK_MS);
128
+ fallback.unref?.();
129
+ try {
130
+ watcher = watch(dirname(store.path), { persistent: false }, (_event, filename) => {
131
+ if (filename === null || filename.toString() === basename(store.path))
132
+ void checkChanged(false);
133
+ });
134
+ }
135
+ catch { /* Rationale: the bounded metadata fallback remains authoritative when watching is unavailable. */ }
136
+ options.signal?.addEventListener("abort", stop, { once: true });
137
+ void checkChanged(true);
138
+ return stop;
139
+ }
62
140
  /** Start or observe one silent update and resolve with the active newer version once restart is useful. */
63
141
  export async function requestAutomaticUpdate(options) {
64
142
  if (options.signal?.aborted)
@@ -292,9 +370,13 @@ async function spawnAutomaticUpdateWorker(entry, arguments_, environment) {
292
370
  }
293
371
  async function activeNewerVersion(dataDir, runningVersion, target) {
294
372
  const active = await activeRelease(dataDir);
295
- return active !== null && releaseChannelOf(active.packageVersion) === target.channel
296
- && isNewerRelease(active.packageVersion, runningVersion)
297
- && (active.packageVersion === target.version || isNewerRelease(active.packageVersion, target.version))
373
+ return activeNewerPackageVersion(active, runningVersion, target.channel, target.version);
374
+ }
375
+ function activeNewerPackageVersion(active, runningVersion, channel, minimum) {
376
+ if (active === null || releaseChannelOf(active.packageVersion) !== channel
377
+ || !isNewerRelease(active.packageVersion, runningVersion))
378
+ return null;
379
+ return minimum === undefined || active.packageVersion === minimum || isNewerRelease(active.packageVersion, minimum)
298
380
  ? active.packageVersion : null;
299
381
  }
300
382
  async function activeRelease(dataDir) {
@@ -22,11 +22,12 @@ export class EngineDiagnostics {
22
22
  if (validSemver(version) === null)
23
23
  return false;
24
24
  return this.#remove(diagnostic => {
25
- const target = diagnostic.code === "release-update-installed"
26
- ? /^Update (\S+) installed; restart to update\.$/u.exec(diagnostic.message)?.[1]
27
- : diagnostic.code === "release-update-blocked"
28
- ? /^Update (\S+) prepared; activation blocked\.$/u.exec(diagnostic.message)?.[1]
29
- : undefined;
25
+ // Invariant: restart readiness describes this long-lived process, not the outer client that
26
+ // attached to it. A newer client can clear activation blocking, but only process exit clears
27
+ // an installed-update notice.
28
+ const target = diagnostic.code === "release-update-blocked"
29
+ ? /^Update (\S+) prepared; activation blocked\.$/u.exec(diagnostic.message)?.[1]
30
+ : undefined;
30
31
  return target !== undefined && validSemver(target) !== null && gte(version, target);
31
32
  });
32
33
  }
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "arm64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-11-03T17:03:50.345Z",
10
+ "builtAt": "2026-11-03T20:15:18.883Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "a87eaaffcd986b624c8b044b756446cc1bdaf4334a1775b97820542fe34f9fb7",
@@ -9,11 +9,11 @@
9
9
  "architecture": "arm64",
10
10
  "target": "aarch64-apple-darwin",
11
11
  "capability": "supported",
12
- "builtAt": "2026-11-03T17:07:06.927Z",
12
+ "builtAt": "2026-11-03T20:18:38.194Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "1a0b3f286201df724ebed4c0596f0fe7d8ab5d27dea36c588dc4f4a60049995f",
16
- "size": 6585632
15
+ "sha256": "b14f69c00c7d472bdc4d742c7fc7d8a517d23aa1233a3e1e9e76b5f884999baa",
16
+ "size": 6585648
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-11-03T17:03:58.940Z",
10
+ "builtAt": "2026-11-03T20:15:26.152Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "783a65f0de287a1c02cd5429dbe97bc8633f4cc8a7b92e3e961180b9bdcff814",
@@ -9,11 +9,11 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-apple-darwin",
11
11
  "capability": "supported",
12
- "builtAt": "2026-11-03T17:09:41.020Z",
12
+ "builtAt": "2026-11-03T20:21:22.213Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "1d003bf193eb4c54439e711427faef94af2c395dd2a23cfb1497bb2c3ab4ad67",
16
- "size": 7178048
15
+ "sha256": "1c0471bb3f2f9520acd1808cce080600b7e3dd485c4d3a8bc6f8aa342768136b",
16
+ "size": 7182168
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -7,7 +7,7 @@
7
7
  "platform": "linux",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-11-03T17:04:12.745Z",
10
+ "builtAt": "2026-11-03T20:15:23.280Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
13
  "sha256": "118e5954e3864672bd8482ab7edf419cb27dcdf2533720316979b7f5cc5a53a5",
@@ -9,11 +9,11 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-unknown-linux-gnu",
11
11
  "capability": "supported",
12
- "builtAt": "2026-11-03T17:06:54.055Z",
12
+ "builtAt": "2026-11-03T20:18:59.521Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "fb9f0498986293e8dbed8a2b043313bf78a07777f6a1eb0a4b62e82365a71110",
16
- "size": 7959248
15
+ "sha256": "54eb39f88b6bfe88b6fd6815be1196d678450d06d1de1169a6de79bd4bf5ebec",
16
+ "size": 7961616
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -7,10 +7,10 @@
7
7
  "platform": "win32",
8
8
  "architecture": "x64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-11-03T17:04:32.725Z",
10
+ "builtAt": "2026-11-03T20:16:09.523Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
- "sha256": "3fb5f440a2febbb9c84e1d7761a785f0d275db0d689cecf175c6c3de47f6b50d",
13
+ "sha256": "9489c6ea57e8cf13e14afb173cef1b4bb87c2cadaf14e583f93f58e0b4dd57dd",
14
14
  "size": 402432
15
15
  },
16
16
  "provenance": {
@@ -9,11 +9,11 @@
9
9
  "architecture": "x64",
10
10
  "target": "x86_64-pc-windows-msvc",
11
11
  "capability": "supported",
12
- "builtAt": "2026-11-03T17:06:43.680Z",
12
+ "builtAt": "2026-11-03T20:20:00.737Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "5592caa99ee4c96544abaceeb025f833c1d8566529e3a7a8ecad51f8aa13e752",
16
- "size": 7077888
15
+ "sha256": "1b8c8a4dbdfad9873a196a5f2746fc86a92678876bfacdf4b28830f40e95f963",
16
+ "size": 7080960
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -1,4 +1,4 @@
1
- export declare const OWNED_UI_SETTINGS_VERSION = 19;
1
+ export declare const OWNED_UI_SETTINGS_VERSION = 20;
2
2
  export type OwnedUiSettingValue = string | number | boolean;
3
3
  export type OwnedUiSettingApplication = "live" | "restart";
4
4
  export interface OwnedUiSettingDeclaration {
@@ -42,7 +42,7 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
42
42
  id: "generic";
43
43
  title: "Generic";
44
44
  }>;
45
- description: "Format used by submitted-prompt timestamps and the resident tab clock.";
45
+ description: "Format used by submitted-prompt timestamps and the resident tab clock when Time is enabled.";
46
46
  application: "live";
47
47
  defaultValue: "8:00 PM";
48
48
  allowedValues: readonly ["20:00", "8:00 PM"];
@@ -228,6 +228,27 @@ export declare const OWNED_SETTING_DECLARATIONS: Readonly<{
228
228
  description: "Show the one Changelog tab.";
229
229
  }>)[];
230
230
  }>;
231
+ readonly tabBarRightItems: Readonly<{
232
+ id: "tabBarRightItems";
233
+ label: "Right side";
234
+ section: Readonly<{
235
+ id: "tabs";
236
+ title: "Tab Bar";
237
+ }>;
238
+ description: "Choose whether the tab bar's right side shows the executing Version, Time, both, or neither.";
239
+ application: "live";
240
+ defaultValue: "version,time";
241
+ allowedValues: readonly ["", "version", "time", "version,time"];
242
+ flags: readonly (Readonly<{
243
+ key: "version";
244
+ label: "Version";
245
+ description: "Show the executing A1 version.";
246
+ }> | Readonly<{
247
+ key: "time";
248
+ label: "Time";
249
+ description: "Show the current time.";
250
+ }>)[];
251
+ }>;
231
252
  readonly tabsAutoName: Readonly<{
232
253
  id: "tabsAutoName";
233
254
  label: "Auto name";
@@ -1,4 +1,4 @@
1
- export const OWNED_UI_SETTINGS_VERSION = 19;
1
+ export const OWNED_UI_SETTINGS_VERSION = 20;
2
2
  const UI_ACCENT_COLOR_CHOICES = Object.freeze([
3
3
  "blue", "purple", "cyan", "green", "orange", "pink",
4
4
  ]);
@@ -19,6 +19,12 @@ const PLUS_MENU_KINDS = Object.freeze([
19
19
  const PLUS_MENU_VALUES = Object.freeze([
20
20
  "", "agent", "settings", "changelog", "agent,settings", "agent,changelog", "settings,changelog", "agent,settings,changelog",
21
21
  ]);
22
+ /** The right-side strip items, in their fixed presentation order. */
23
+ const TAB_BAR_RIGHT_ITEMS = Object.freeze([
24
+ Object.freeze({ key: "version", label: "Version", description: "Show the executing A1 version." }),
25
+ Object.freeze({ key: "time", label: "Time", description: "Show the current time." }),
26
+ ]);
27
+ const TAB_BAR_RIGHT_ITEM_VALUES = Object.freeze(["", "version", "time", "version,time"]);
22
28
  export const OWNED_SETTING_DECLARATIONS = Object.freeze({
23
29
  updateCheck: Object.freeze({
24
30
  id: "updateCheck",
@@ -33,7 +39,7 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
33
39
  id: "timeFormat",
34
40
  label: "Time format",
35
41
  section: GENERIC_SECTION,
36
- description: "Format used by submitted-prompt timestamps and the resident tab clock.",
42
+ description: "Format used by submitted-prompt timestamps and the resident tab clock when Time is enabled.",
37
43
  application: "live",
38
44
  defaultValue: "8:00 PM",
39
45
  allowedValues: Object.freeze([
@@ -169,6 +175,16 @@ export const OWNED_SETTING_DECLARATIONS = Object.freeze({
169
175
  allowedValues: PLUS_MENU_VALUES,
170
176
  flags: PLUS_MENU_KINDS,
171
177
  }),
178
+ tabBarRightItems: Object.freeze({
179
+ id: "tabBarRightItems",
180
+ label: "Right side",
181
+ section: TABS_SECTION,
182
+ description: "Choose whether the tab bar's right side shows the executing Version, Time, both, or neither.",
183
+ application: "live",
184
+ defaultValue: "version,time",
185
+ allowedValues: TAB_BAR_RIGHT_ITEM_VALUES,
186
+ flags: TAB_BAR_RIGHT_ITEMS,
187
+ }),
172
188
  tabsAutoName: Object.freeze({
173
189
  id: "tabsAutoName",
174
190
  label: "Auto name",
@@ -155,6 +155,13 @@ export const OWNED_UI_SETTINGS_MIGRATIONS = Object.freeze([
155
155
  return { ...values };
156
156
  },
157
157
  }),
158
+ Object.freeze({
159
+ to: 20,
160
+ description: "Introduce the resident tab-bar Version and Time defaults.",
161
+ migrate(values) {
162
+ return { ...values };
163
+ },
164
+ }),
158
165
  ]);
159
166
  /** Shape-selected compatibility for the metadata-free product object. */
160
167
  export function normalizeOwnedUiSettingsValues(source) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.2.7-dev.1238",
3
+ "version": "0.2.7-dev.1239",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "privateLaunchContract": "neutral-launch-v1",
@@ -123,7 +123,7 @@
123
123
  "@mariozechner/clipboard": "^0.3.9"
124
124
  },
125
125
  "publication": {
126
- "source": "32afd4b3acaa0de3fddb429058084448ef5874fb",
127
- "pullRequest": 863
126
+ "source": "19a43afd07e6df67b85be09199298621b4ffc023",
127
+ "pullRequest": 864
128
128
  }
129
129
  }