@timurproko/a1 0.2.7-dev.1236 → 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.
Files changed (41) hide show
  1. package/dist/composition/owned-settings.js +1 -1
  2. package/dist/composition/release-update-observation.js +43 -14
  3. package/dist/contracts/owned-ui/session-workflows.d.ts +1 -1
  4. package/dist/features/owned-ui/plus-menu-dialog.d.ts +13 -7
  5. package/dist/features/owned-ui/plus-menu-dialog.js +17 -12
  6. package/dist/features/owned-ui/settings-app.js +94 -17
  7. package/dist/foundation/release/automatic-update.d.ts +12 -0
  8. package/dist/foundation/release/automatic-update.js +87 -5
  9. package/dist/integrations/pi/engine/default-model-setting.d.ts +12 -0
  10. package/dist/integrations/pi/engine/default-model-setting.js +70 -0
  11. package/dist/integrations/pi/engine/diagnostics-state.js +6 -5
  12. package/dist/integrations/pi/engine/index.d.ts +1 -1
  13. package/dist/integrations/pi/engine/index.js +1 -1
  14. package/dist/integrations/pi/engine/offline-settings-port.js +1 -0
  15. package/dist/integrations/pi/engine/runtime-integration.d.ts +1 -1
  16. package/dist/integrations/pi/engine/runtime-integration.js +12 -14
  17. package/dist/integrations/pi/engine/settings-bridge.d.ts +1 -2
  18. package/dist/integrations/pi/engine/settings-bridge.js +23 -29
  19. package/dist/integrations/pi/engine/settings-port.js +2 -1
  20. package/dist/integrations/pi/engine/workflow-runner.js +2 -2
  21. package/dist/native/darwin-arm64/manifest.json +1 -1
  22. package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
  23. package/dist/native/darwin-arm64/terminal-host.node +0 -0
  24. package/dist/native/darwin-x64/manifest.json +1 -1
  25. package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
  26. package/dist/native/darwin-x64/terminal-host.node +0 -0
  27. package/dist/native/linux-x64/manifest.json +1 -1
  28. package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
  29. package/dist/native/linux-x64/terminal-host.node +0 -0
  30. package/dist/native/win32-x64/manifest.json +2 -2
  31. package/dist/native/win32-x64/process-guardian.node +0 -0
  32. package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
  33. package/dist/native/win32-x64/terminal-host.node +0 -0
  34. package/dist/ui/components/dialog-panel.d.ts +6 -3
  35. package/dist/ui/components/dialog-panel.js +10 -7
  36. package/dist/ui/components/theme.d.ts +1 -1
  37. package/dist/ui/settings/declarations.d.ts +23 -2
  38. package/dist/ui/settings/declarations.js +18 -2
  39. package/dist/ui/settings/migrations.js +7 -0
  40. package/docs/architecture/ui-reference-provenance.md +3 -3
  41. package/package.json +3 -3
@@ -0,0 +1,70 @@
1
+ export const DEFAULT_MODEL_SETTING_KEY = "defaultModel";
2
+ export const SUPPLEMENTAL_SETTING_KEYS = [DEFAULT_MODEL_SETTING_KEY];
3
+ const UNSET = "not set";
4
+ function read(settings) {
5
+ const provider = settings.getDefaultProvider();
6
+ const model = settings.getDefaultModel();
7
+ return provider && model ? `${provider}/${model}` : UNSET;
8
+ }
9
+ export function defaultModelDescriptor(settings, models) {
10
+ const value = read(settings);
11
+ return {
12
+ key: DEFAULT_MODEL_SETTING_KEY,
13
+ valueType: "enum",
14
+ writable: true,
15
+ application: "next-session",
16
+ owner: "startup",
17
+ available: true,
18
+ limitationReason: null,
19
+ storedValue: value,
20
+ effectiveValue: value,
21
+ choices: models.map(model => model.key),
22
+ flags: models.map(model => ({
23
+ key: model.key, label: model.label, description: model.name, fallback: false,
24
+ })),
25
+ label: "Default model",
26
+ description: "Model used when a new session starts",
27
+ resolvedWhenRead: true,
28
+ };
29
+ }
30
+ export async function writeDefaultModel(settings, models, value) {
31
+ if (typeof value !== "string" || !models.some(model => model.key === value)) {
32
+ throw new TypeError("setting value is invalid: defaultModel");
33
+ }
34
+ const previous = read(settings);
35
+ const at = value.indexOf("/");
36
+ settings.setDefaultModelAndProvider(value.slice(0, at), value.slice(at + 1));
37
+ try {
38
+ await settings.flush();
39
+ const errors = settings.drainErrors();
40
+ if (errors.length > 0)
41
+ throw new Error(errors.map(item => item.error.message).join("; "));
42
+ return {
43
+ status: "deferred", application: "next-session", storedValue: value, effectiveValue: previous,
44
+ failure: null, limitationReason: null,
45
+ };
46
+ }
47
+ catch (error) {
48
+ let failure = error instanceof Error ? error.message : String(error);
49
+ try {
50
+ if (previous === UNSET)
51
+ await settings.reload();
52
+ else {
53
+ const split = previous.indexOf("/");
54
+ settings.setDefaultModelAndProvider(previous.slice(0, split), previous.slice(split + 1));
55
+ await settings.flush();
56
+ const errors = settings.drainErrors();
57
+ if (errors.length > 0)
58
+ throw new Error(errors.map(item => item.error.message).join("; "));
59
+ }
60
+ }
61
+ catch (rollbackError) {
62
+ failure += `; persistence rollback failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`;
63
+ }
64
+ const restored = read(settings);
65
+ return {
66
+ status: "failed", application: "next-session", storedValue: restored, effectiveValue: previous,
67
+ failure, limitationReason: null,
68
+ };
69
+ }
70
+ }
@@ -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
  }
@@ -10,7 +10,7 @@ export { PiSessionSelectionError, openSelectedPiSession, resolveSessionArgumentP
10
10
  export type { PiSessionForkPrompt, PiSessionSelection } from "./session-selection.js";
11
11
  export { PiSessionCommandIntegration } from "./session-integration.js";
12
12
  export type { PiDocumentedSessionCommands, PiSessionCommand, PiSessionCommandResult } from "./session-integration.js";
13
- export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift, settingsVisualInventoryViolations, } from "./settings-bridge.js";
13
+ export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift, } from "./settings-bridge.js";
14
14
  export { assertPiSettingsMetadata, loadPiSettingsMetadata, PI_SETTINGS_METADATA_FILE } from "./settings-metadata.js";
15
15
  export type { PiSettingBounds, PiSettingPresentation, PiSettingsMetadata } from "./settings-metadata.js";
16
16
  export type { PiSettingEffectDefinition, PiSettingEffectHandler, PiSettingKey, PiSettingOwnerHandlers, PiSettingStorageOperation, PiSettingVisualClass, PiSettingVisualEvidence, PiSettingsCoordinatorOptions, PiSettingsModelChoice, PiSettingsProviders, } from "./settings-bridge.js";
@@ -4,7 +4,7 @@ export { detachedEngineHostPorts } from "./host-ports.js";
4
4
  export { bindPiRuntimeSession, createPiRuntimeIntegration, createPiRuntimeServicesAfterTrust, disposePiRuntimeIntegration, replacePiRuntimeSession, resolveConfiguredModelScope, } from "./runtime-integration.js";
5
5
  export { PiSessionSelectionError, openSelectedPiSession, resolveSessionArgumentPath } from "./session-selection.js";
6
6
  export { PiSessionCommandIntegration } from "./session-integration.js";
7
- export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift, settingsVisualInventoryViolations, } from "./settings-bridge.js";
7
+ export { AUTOMATIC_THEME, EXPOSED_SETTING_KEYS, PI_SETTING_EFFECTS, PiSettingsBridge, PiSettingsCoordinator, parseAutomaticTheme, settingsEffectInventoryDrift, settingsInventoryDrift, } from "./settings-bridge.js";
8
8
  export { assertPiSettingsMetadata, loadPiSettingsMetadata, PI_SETTINGS_METADATA_FILE } from "./settings-metadata.js";
9
9
  export { resolvePiProjectTrustPreflight } from "./project-trust-preflight.js";
10
10
  export { configureOwnedHttpDispatcher, configuredOwnedHttpIdleTimeoutMs } from "./http-dispatcher.js";
@@ -40,6 +40,7 @@ function modelChoices(models, overrides) {
40
40
  return {
41
41
  key,
42
42
  label: `${model.id} [${model.provider}]`,
43
+ name: model.name ?? model.id,
43
44
  description: overrides[key] === undefined ? "global default" : `override: ${overrides[key]}`,
44
45
  levels: supportedThinkingLevels(model),
45
46
  };
@@ -41,7 +41,7 @@ interface ConfiguredModelScope {
41
41
  * Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
42
42
  * into a scoped model list, keep the resolver's warnings (e.g. "No models match
43
43
  * pattern ..."), and pick the same initial model pinned Pi would pick — the
44
- * saved default when it is in scope, otherwise the first scoped model.
44
+ * saved authenticated default, otherwise the first scoped model.
45
45
  */
46
46
  export declare function resolveConfiguredModelScope(services: Pick<AgentSessionServices, "settingsManager" | "modelRuntime">, signal?: AbortSignal): Promise<ConfiguredModelScope>;
47
47
  export declare function createPiRuntimeServicesAfterTrust(options: Pick<PiRuntimeIntegrationOptions, "agentDir" | "projectTrustPrompt" | "preflightDependencies" | "markStartupPhase"> & {
@@ -62,7 +62,7 @@ const runtimeSessionWriters = new WeakMap();
62
62
  * Mirrors pinned Pi's CLI startup: resolve the `models` patterns from settings
63
63
  * into a scoped model list, keep the resolver's warnings (e.g. "No models match
64
64
  * pattern ..."), and pick the same initial model pinned Pi would pick — the
65
- * saved default when it is in scope, otherwise the first scoped model.
65
+ * saved authenticated default, otherwise the first scoped model.
66
66
  */
67
67
  export async function resolveConfiguredModelScope(services, signal) {
68
68
  const patterns = services.settingsManager.getEnabledModels();
@@ -70,21 +70,19 @@ export async function resolveConfiguredModelScope(services, signal) {
70
70
  return { scopedModels: [], model: undefined, thinkingLevel: undefined, diagnostics: [] };
71
71
  }
72
72
  const { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics([...patterns], services.modelRuntime, { signal: signal === undefined ? AbortSignal.timeout(15_000) : AbortSignal.any([signal, AbortSignal.timeout(15_000)]) });
73
- let selected;
74
- if (scopedModels.length > 0) {
75
- const savedProvider = services.settingsManager.getDefaultProvider();
76
- const savedModelId = services.settingsManager.getDefaultModel();
77
- const savedModel = savedProvider && savedModelId
78
- ? services.modelRuntime.getModel(savedProvider, savedModelId)
79
- : undefined;
80
- selected = (savedModel
81
- ? scopedModels.find(scoped => scoped.model.provider === savedModel.provider && scoped.model.id === savedModel.id)
82
- : undefined) ?? scopedModels[0];
83
- }
73
+ const savedProvider = services.settingsManager.getDefaultProvider();
74
+ const savedModelId = services.settingsManager.getDefaultModel();
75
+ const savedModel = savedProvider && savedModelId
76
+ ? services.modelRuntime.getAvailableSnapshot().find(model => model.provider === savedProvider && model.id === savedModelId)
77
+ : undefined;
78
+ const fallback = scopedModels[0];
84
79
  return {
85
80
  scopedModels,
86
- model: selected?.model,
87
- thinkingLevel: selected?.thinkingLevel,
81
+ model: savedModel ?? fallback?.model,
82
+ thinkingLevel: savedModel
83
+ ? services.settingsManager.getModelThinkingLevel(savedModel.provider, savedModel.id)
84
+ ?? services.settingsManager.getDefaultThinkingLevel()
85
+ : fallback?.thinkingLevel,
88
86
  diagnostics: diagnostics.map(diagnostic => ({ type: diagnostic.type, message: diagnostic.message })),
89
87
  };
90
88
  }
@@ -65,11 +65,9 @@ export declare function settingsEffectInventoryDrift(presented: readonly string[
65
65
  readonly stale: readonly string[];
66
66
  readonly duplicated: readonly string[];
67
67
  };
68
- export declare function settingsVisualInventoryViolations(presented: readonly string[], reviewed?: Readonly<Partial<Record<PiSettingKey, PiSettingEffectDefinition>>>): readonly string[];
69
68
  /** Values only the running engine knows. */
70
69
  export interface PiSettingsProviders {
71
70
  readonly themes?: () => readonly string[];
72
- /** Models a per-model thinking override may name, with the levels each one supports. */
73
71
  readonly models?: () => readonly PiSettingsModelChoice[];
74
72
  readonly productMode?: "bare" | "comparison";
75
73
  }
@@ -77,6 +75,7 @@ export interface PiSettingsModelChoice {
77
75
  /** `provider/modelId`, the key the engine stores the override under. */
78
76
  readonly key: string;
79
77
  readonly label: string;
78
+ readonly name: string;
80
79
  readonly description: string;
81
80
  readonly levels: readonly string[];
82
81
  }
@@ -15,6 +15,7 @@ export const PI_SETTING_EFFECTS = Object.freeze({
15
15
  transport: effect("live", "agent", "status-error", "pinned provider request status and failure rows", "settings-effects-provider-parity"),
16
16
  httpIdleTimeoutMs: effect("live", "agent", "retry-error", "pinned timeout, retry, and terminal failure rows", "pinned-transcript-lifecycle-parity"),
17
17
  cacheWarmingMode: effect("live", "agent", "footer-transcript", "pinned cache warming notices and session usage rows", "pinned-transcript-lifecycle-parity"),
18
+ defaultModel: effect("next-session", "startup", "selector", "pinned default-model selector", "owned-default-model-setting"),
18
19
  modelThinkingLevels: effect("live", "agent", "footer-transcript", "pinned per-model thinking override, footer indicator, and thinking rows", "pinned-status-indicator-parity"),
19
20
  theme: hiddenEffect("live", "shell", "pinned theme selector and complete themed shell", "pinned-theme-parity"),
20
21
  hideThinkingBlock: effect("live", "shell", "transcript", "pinned thinking block presence and spacing", "pinned-transcript-lifecycle-parity"),
@@ -225,27 +226,6 @@ export function settingsEffectInventoryDrift(presented, reviewed = Object.keys(P
225
226
  duplicated: [...counts].filter(([, count]) => count > 1).map(([key]) => key),
226
227
  };
227
228
  }
228
- export function settingsVisualInventoryViolations(presented, reviewed = PI_SETTING_EFFECTS) {
229
- const violations = [];
230
- for (const candidate of presented) {
231
- const key = candidate;
232
- const definition = reviewed[key];
233
- if (definition === undefined) {
234
- violations.push(`${candidate}: missing visual classification`);
235
- continue;
236
- }
237
- const visual = definition.visual;
238
- if (!visual || visual.pinnedSurface.trim().length === 0)
239
- violations.push(`${candidate}: missing pinned visual source`);
240
- if (!visual || visual.evidence.trim().length === 0)
241
- violations.push(`${candidate}: missing independent visual evidence`);
242
- if (definition.hiddenInBare === true && visual?.class !== "hidden")
243
- violations.push(`${candidate}: hidden setting declares a visible frame`);
244
- if (definition.hiddenInBare !== true && visual?.class === "hidden")
245
- violations.push(`${candidate}: visible setting declares hidden evidence`);
246
- }
247
- return violations;
248
- }
249
229
  function outcome(status, application, storedValue, effectiveValue, failure, limitationReason) {
250
230
  return { status, application, storedValue, effectiveValue, failure, limitationReason };
251
231
  }
@@ -271,6 +251,7 @@ export const AUTOMATIC_THEME = "automatic";
271
251
  const LIGHT_APPEARANCE_THEME = "light";
272
252
  const DARK_APPEARANCE_THEME = "dark";
273
253
  const THEME_KEY = "theme";
254
+ const DEFAULT_MODEL_KEY = "defaultModel";
274
255
  const MODEL_THINKING_KEY = "modelThinkingLevels";
275
256
  export function parseAutomaticTheme(stored) {
276
257
  const at = stored.indexOf("/");
@@ -307,13 +288,16 @@ export class PiSettingsBridge {
307
288
  capabilities = { write: true, flush: true };
308
289
  #operations;
309
290
  #providers;
291
+ #settings;
310
292
  #coordinator;
311
293
  constructor(settings, providers = {}) {
312
294
  this.#providers = providers;
295
+ this.#settings = settings;
313
296
  const mapped = operations(settings, providers);
314
297
  this.#operations = new Map(mapped.map(operation => [operation.key, operation]));
315
298
  const presentationDrift = settingsInventoryDrift(EXPOSED_SETTING_KEYS, mapped.map(operation => operation.key));
316
- const effectDrift = settingsEffectInventoryDrift(EXPOSED_SETTING_KEYS);
299
+ const generatedEffects = Object.keys(PI_SETTING_EFFECTS).filter(key => key !== DEFAULT_MODEL_KEY);
300
+ const effectDrift = settingsEffectInventoryDrift(EXPOSED_SETTING_KEYS, generatedEffects);
317
301
  if (presentationDrift.unmapped.length > 0 || presentationDrift.stale.length > 0
318
302
  || effectDrift.unmapped.length > 0 || effectDrift.stale.length > 0 || effectDrift.duplicated.length > 0) {
319
303
  throw new Error([
@@ -331,7 +315,8 @@ export class PiSettingsBridge {
331
315
  });
332
316
  }
333
317
  bindOwner(owner, handlers) {
334
- return this.#coordinator.bindOwner(owner, handlers);
318
+ const { defaultModel: _defaultModel, ...generated } = handlers;
319
+ return this.#coordinator.bindOwner(owner, generated);
335
320
  }
336
321
  unbindOwner(owner) {
337
322
  this.#coordinator.unbindOwner(owner);
@@ -341,14 +326,14 @@ export class PiSettingsBridge {
341
326
  const at = PRESENTATION.order.indexOf(key);
342
327
  return at < 0 ? PRESENTATION.order.length : at;
343
328
  };
344
- return [...this.#operations.values()]
329
+ const descriptors = [...this.#operations.values()]
345
330
  .filter(operation => this.#coordinator.available(operation.key))
346
331
  .map(operation => {
347
332
  const key = operation.key;
348
333
  const wording = PRESENTATION.settings[key];
349
334
  const flags = PRESENTATION.dialogs[key];
350
335
  const bounds = PRESENTATION.bounds[key];
351
- const descriptor = {
336
+ return this.#resolved({
352
337
  ...operation.descriptor,
353
338
  application: PI_SETTING_EFFECTS[key].application,
354
339
  owner: PI_SETTING_EFFECTS[key].owner,
@@ -360,10 +345,11 @@ export class PiSettingsBridge {
360
345
  ...(bounds === undefined ? {} : bounds),
361
346
  ...(wording === undefined ? {} : { label: wording.label, description: wording.description }),
362
347
  ...(flags === undefined ? {} : { flags }),
363
- };
364
- return this.#resolved(descriptor);
365
- })
366
- .sort((left, right) => rank(left.key) - rank(right.key));
348
+ });
349
+ });
350
+ const { defaultModelDescriptor } = await import("./default-model-setting.js");
351
+ descriptors.push(defaultModelDescriptor(this.#settings, this.#providers.models?.() ?? []));
352
+ return descriptors.sort((left, right) => rank(left.key) - rank(right.key));
367
353
  }
368
354
  #resolved(descriptor) {
369
355
  if (descriptor.key === THEME_KEY) {
@@ -387,9 +373,17 @@ export class PiSettingsBridge {
387
373
  return this.#providers.themes?.() ?? [];
388
374
  }
389
375
  async readSetting(key) {
376
+ if (key === DEFAULT_MODEL_KEY) {
377
+ const { defaultModelDescriptor } = await import("./default-model-setting.js");
378
+ return defaultModelDescriptor(this.#settings, this.#providers.models?.() ?? []).storedValue;
379
+ }
390
380
  return isPiSettingKey(key) && this.#operations.has(key) ? this.#coordinator.storedValue(key) : undefined;
391
381
  }
392
382
  async writeSetting(key, value) {
383
+ if (key === DEFAULT_MODEL_KEY) {
384
+ const { writeDefaultModel } = await import("./default-model-setting.js");
385
+ return await writeDefaultModel(this.#settings, this.#providers.models?.() ?? [], value);
386
+ }
393
387
  if (!isPiSettingKey(key) || !this.#operations.has(key))
394
388
  throw new Error(`setting is unavailable: ${key}`);
395
389
  return await this.#coordinator.apply(key, value);
@@ -77,7 +77,7 @@ export class PiEngineSettings {
77
77
  });
78
78
  this.#settingsIntegration.bindOwner("startup", {
79
79
  // Invariant: deferred application is the owner operation: the next preflight reads
80
- // the persisted default before constructing project-backed services.
80
+ // the persisted defaults before constructing project-backed services.
81
81
  defaultProjectTrust: { apply() { } },
82
82
  collapseChangelog: { apply() { } },
83
83
  });
@@ -167,6 +167,7 @@ export class PiEngineSettings {
167
167
  return {
168
168
  key,
169
169
  label: `${model.id} [${model.provider}]`,
170
+ name: model.name,
170
171
  description: overrides[key] === undefined ? "global default" : `override: ${overrides[key]}`,
171
172
  levels: supportedThinkingLevels(model),
172
173
  };
@@ -333,13 +333,13 @@ export class PiWorkflowRunner {
333
333
  });
334
334
  }
335
335
  case "models": {
336
- // Invariant: the bare dialog seeds its query from the argument; only an explicit selection switches models.
336
+ // Invariant: the bare dialog seeds its query from the argument; only an explicit selection switches the current session.
337
337
  if (!selection)
338
338
  return workflowResult(request.command, "requires-selection", "Select a model", argument || undefined, "silent");
339
339
  return this.#selectModel(request, selection, {
340
340
  pool: [...(runtime.services.modelRuntime.getAvailableSnapshot?.() ?? [])],
341
341
  refreshOnMiss: true,
342
- persist: true,
342
+ persist: false,
343
343
  onMiss: "failed",
344
344
  });
345
345
  }
@@ -7,7 +7,7 @@
7
7
  "platform": "darwin",
8
8
  "architecture": "arm64",
9
9
  "capability": "supported",
10
- "builtAt": "2026-11-03T12:34:41.201Z",
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-03T12:38:29.199Z",
12
+ "builtAt": "2026-11-03T20:18:38.194Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "f340571d5eef45a551e7c53f8c31d91dd75c948fe11c97bda0c23a2d9bbfa2a4",
16
- "size": 6585248
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-03T12:34:49.251Z",
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-03T12:41:50.549Z",
12
+ "builtAt": "2026-11-03T20:21:22.213Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "92cceb0cf8a8b955b86762fc677396586f0e32569923827a96281fc5a29a3add",
16
- "size": 7173680
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-03T12:33:48.123Z",
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-03T12:37:02.302Z",
12
+ "builtAt": "2026-11-03T20:18:59.521Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "e5d97e8f613e7ca960f541861f9ae61435fb5e1cff1a5e80e34000665e5afecf",
16
- "size": 7954680
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-03T12:34:57.755Z",
10
+ "builtAt": "2026-11-03T20:16:09.523Z",
11
11
  "artifact": {
12
12
  "filename": "process-guardian.node",
13
- "sha256": "037cc7426ed420a010993dcd7fda7001707fc87d0c6104f93c01e32c789bd748",
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-03T12:36:35.360Z",
12
+ "builtAt": "2026-11-03T20:20:00.737Z",
13
13
  "artifact": {
14
14
  "filename": "terminal-host.node",
15
- "sha256": "2dd46801648d082efbf5c6122ed76a059a6fee052ca0364dea517ddb3f1f6ba9",
16
- "size": 7071232
15
+ "sha256": "1b8c8a4dbdfad9873a196a5f2746fc86a92678876bfacdf4b28830f40e95f963",
16
+ "size": 7080960
17
17
  },
18
18
  "entry": {
19
19
  "filename": "dist/foundation/resident-tabs/host-entry.js",
@@ -9,6 +9,8 @@ import type { UiTheme } from "./theme.js";
9
9
  export interface DialogRow {
10
10
  readonly label: string;
11
11
  readonly labelSuffix?: string;
12
+ /** Success-colored marker rendered after the complete label. */
13
+ readonly successSuffix?: string;
12
14
  readonly value: string;
13
15
  /** What this part does, shown while it is the one in hand. */
14
16
  readonly description?: string;
@@ -31,8 +33,9 @@ export interface DialogPanelFrame {
31
33
  }
32
34
  export interface SteppedDialogPanelState {
33
35
  readonly title: string;
34
- readonly step: number;
35
- readonly steps: number;
36
+ /** Omitted for a one-step searchable selector whose title needs no step marker. */
37
+ readonly step?: number;
38
+ readonly steps?: number;
36
39
  readonly description: string;
37
40
  readonly input?: LineInput;
38
41
  readonly rows: readonly DialogRow[];
@@ -55,7 +58,7 @@ export declare function renderCenteredDialogOverlay(frame: readonly string[], di
55
58
  export declare function dialogValueColumn(rows: readonly DialogRow[]): number;
56
59
  /** The semantic rows for one generic configure modal. */
57
60
  export declare function renderDialogPanel(state: DialogPanelState, width: number, theme: UiTheme): readonly string[];
58
- /** Owned stepped-selector content for structured settings with nested choices. */
61
+ /** Owned searchable selector content, with an optional step marker for nested choices. */
59
62
  export declare function renderSteppedDialogPanel(state: SteppedDialogPanelState, width: number, theme: UiTheme): readonly string[];
60
63
  /** The row a pointer report lands on, or null when it is not on one. */
61
64
  export declare function dialogRowAt(frame: DialogPanelFrame, row: number, column: number, valueWidth: number): number | null;
@@ -99,18 +99,19 @@ export function renderDialogPanel(state, width, theme) {
99
99
  layout.blank,
100
100
  ];
101
101
  }
102
- /** Owned stepped-selector content for structured settings with nested choices. */
102
+ /** Owned searchable selector content, with an optional step marker for nested choices. */
103
103
  export function renderSteppedDialogPanel(state, width, theme) {
104
104
  const layout = dialogPanelLayout(width, theme);
105
- const labelColumn = Math.min(STEPPED_LABEL_COLUMN_CAP, Math.max(0, ...state.rows.map(row => displayWidth(`${row.label}${row.labelSuffix ?? ""}`))));
105
+ const labelColumn = Math.min(STEPPED_LABEL_COLUMN_CAP, Math.max(0, ...state.rows.map(row => displayWidth(`${row.label}${row.labelSuffix ?? ""}${row.successSuffix === undefined ? "" : ` ${row.successSuffix}`}`))));
106
106
  const rows = state.rows.map((row, index) => {
107
107
  const selected = index === state.index;
108
- const completeLabel = `${row.label}${row.labelSuffix ?? ""}`;
108
+ const success = row.successSuffix === undefined ? "" : ` ${row.successSuffix}`;
109
+ const completeLabel = `${row.label}${row.labelSuffix ?? ""}${success}`;
109
110
  const padding = " ".repeat(Math.max(0, labelColumn - displayWidth(completeLabel)));
110
111
  const suffix = row.value.length === 0 ? "" : ` ${row.value}`;
111
112
  const cursor = selected ? "→ " : " ";
112
113
  const raw = `${cursor}${completeLabel}${padding}${suffix}`;
113
- const label = `${theme.fg("text", row.label)}${theme.fg("muted", row.labelSuffix ?? "")}${padding}`;
114
+ const label = `${theme.fg("text", row.label)}${theme.fg("muted", row.labelSuffix ?? "")}${theme.fg("success", success)}${padding}`;
114
115
  const content = selected
115
116
  ? `${theme.fg("accent", cursor)}${label}${theme.fg("muted", suffix)}`
116
117
  : `${cursor}${label}${theme.fg("muted", suffix)}`;
@@ -128,9 +129,11 @@ export function renderSteppedDialogPanel(state, width, theme) {
128
129
  layout.blank,
129
130
  ];
130
131
  const descriptionRows = wrapText(state.description, layout.contentWidth);
131
- const step = `(step ${state.step}/${state.steps})`;
132
- const title = `${state.title} ${step}`;
133
- const paintedTitle = `${theme.bold(theme.fg("accent", state.title))} ${theme.fg("dim", step)}`;
132
+ const step = state.step === undefined || state.steps === undefined ? "" : `(step ${state.step}/${state.steps})`;
133
+ const title = step.length === 0 ? state.title : `${state.title} ${step}`;
134
+ const paintedTitle = step.length === 0
135
+ ? theme.bold(theme.fg("accent", state.title))
136
+ : `${theme.bold(theme.fg("accent", state.title))} ${theme.fg("dim", step)}`;
134
137
  return [
135
138
  layout.blank,
136
139
  layout.contentRow(paintedTitle, title),
@@ -1,4 +1,4 @@
1
- export type UiThemeToken = "accent" | "mdHeading" | "text" | "muted" | "dim" | "border" | "error";
1
+ export type UiThemeToken = "accent" | "mdHeading" | "text" | "muted" | "dim" | "border" | "error" | "success";
2
2
  export interface UiTheme {
3
3
  fg(token: UiThemeToken, text: string): string;
4
4
  bold(text: string): string;
@@ -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";