@timurproko/a1 0.1.1-dev.1 → 0.1.1-dev.10

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 (176) hide show
  1. package/README.md +8 -43
  2. package/bin/guardian.js +22 -0
  3. package/bin/{a1-ui.js → ui.js} +4 -8
  4. package/dist/native/darwin-arm64/manifest.json +20 -0
  5. package/dist/native/darwin-arm64/process-guardian +0 -0
  6. package/dist/native/linux-x64/manifest.json +20 -0
  7. package/dist/native/linux-x64/process-guardian +0 -0
  8. package/dist/native/win32-x64/manifest.json +20 -0
  9. package/dist/native/win32-x64/process-guardian.exe +0 -0
  10. package/dist/src/composition/index.d.ts +26 -0
  11. package/dist/src/composition/index.js +94 -2
  12. package/dist/src/features/launch/initialize-profile.js +23 -2
  13. package/dist/src/features/launch/profiles.d.ts +1 -1
  14. package/dist/src/features/launch/profiles.js +2 -2
  15. package/dist/src/features/launch/runtime-selection.d.ts +13 -8
  16. package/dist/src/features/launch/runtime-selection.js +6 -6
  17. package/dist/src/features/owned-ui/index.d.ts +1 -0
  18. package/dist/src/features/owned-ui/index.js +1 -0
  19. package/dist/src/features/owned-ui/run.d.ts +6 -0
  20. package/dist/src/features/owned-ui/run.js +3 -1
  21. package/dist/src/features/owned-ui/settings-app.d.ts +22 -0
  22. package/dist/src/features/owned-ui/settings-app.js +633 -0
  23. package/dist/src/foundation/agent-engine-contracts/domain.d.ts +26 -0
  24. package/dist/src/foundation/launch-guardian/index.d.ts +1 -0
  25. package/dist/src/foundation/launch-guardian/index.js +1 -0
  26. package/dist/src/foundation/launch-guardian/main.d.ts +31 -0
  27. package/dist/src/foundation/launch-guardian/main.js +165 -0
  28. package/dist/src/foundation/lifecycle/commands.d.ts +40 -0
  29. package/dist/src/foundation/lifecycle/commands.js +1 -0
  30. package/dist/src/foundation/lifecycle/index.d.ts +2 -0
  31. package/dist/src/foundation/lifecycle/index.js +2 -0
  32. package/dist/src/foundation/lifecycle/launch-instance.d.ts +46 -0
  33. package/dist/src/foundation/lifecycle/launch-instance.js +134 -0
  34. package/dist/src/foundation/lifecycle/model.d.ts +6 -42
  35. package/dist/src/foundation/owned-ui-settings/declarations.d.ts +13 -0
  36. package/dist/src/foundation/owned-ui-settings/declarations.js +39 -0
  37. package/dist/src/foundation/owned-ui-settings/index.d.ts +6 -0
  38. package/dist/src/foundation/owned-ui-settings/index.js +6 -0
  39. package/dist/src/foundation/owned-ui-settings/migrations.d.ts +13 -0
  40. package/dist/src/foundation/owned-ui-settings/migrations.js +28 -0
  41. package/dist/src/foundation/owned-ui-settings/resolution.d.ts +45 -0
  42. package/dist/src/foundation/owned-ui-settings/resolution.js +147 -0
  43. package/dist/src/foundation/owned-ui-settings/sections.d.ts +70 -0
  44. package/dist/src/foundation/owned-ui-settings/sections.js +93 -0
  45. package/dist/src/foundation/owned-ui-settings/session.d.ts +44 -0
  46. package/dist/src/foundation/owned-ui-settings/session.js +145 -0
  47. package/dist/src/foundation/owned-ui-settings/store.d.ts +28 -0
  48. package/dist/src/foundation/owned-ui-settings/store.js +90 -0
  49. package/dist/src/foundation/pi-component-adapter/index.d.ts +1 -0
  50. package/dist/src/foundation/pi-component-adapter/index.js +1 -0
  51. package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.d.ts +9 -1
  52. package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.js +10 -8
  53. package/dist/src/foundation/pi-component-adapter/upstream/theme/theme.d.ts +8 -0
  54. package/dist/src/foundation/pi-component-adapter/upstream/theme/theme.js +12 -0
  55. package/dist/src/foundation/pi-engine-adapter/adapter.d.ts +17 -3
  56. package/dist/src/foundation/pi-engine-adapter/adapter.js +80 -7
  57. package/dist/src/foundation/pi-engine-adapter/pi-settings-metadata.json +374 -0
  58. package/dist/src/foundation/pi-engine-adapter/public-main-entry.js +10 -1
  59. package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +40 -2
  60. package/dist/src/foundation/pi-engine-adapter/settings-integration.js +172 -24
  61. package/dist/src/foundation/pi-engine-adapter/workflows.d.ts +9 -0
  62. package/dist/src/foundation/pi-owned-ui-integration/index.d.ts +1 -0
  63. package/dist/src/foundation/pi-owned-ui-integration/index.js +1 -0
  64. package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +29 -0
  65. package/dist/src/foundation/pi-owned-ui-integration/route-host.js +1 -0
  66. package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +6 -0
  67. package/dist/src/foundation/pi-owned-ui-integration/session-shell.js +71 -1
  68. package/dist/src/foundation/pi-tui-runtime-adapter/adapter.d.ts +6 -0
  69. package/dist/src/foundation/pi-tui-runtime-adapter/adapter.js +9 -0
  70. package/dist/src/foundation/process-containment/artifact.d.ts +1 -0
  71. package/dist/src/foundation/process-containment/artifact.js +34 -0
  72. package/dist/src/foundation/process-containment/contracts.d.ts +31 -0
  73. package/dist/src/foundation/process-containment/contracts.js +35 -0
  74. package/dist/src/foundation/process-containment/index.d.ts +5 -0
  75. package/dist/src/foundation/process-containment/index.js +5 -0
  76. package/dist/src/foundation/process-containment/linux-process-inspector.d.ts +6 -0
  77. package/dist/src/foundation/process-containment/linux-process-inspector.js +26 -0
  78. package/dist/src/foundation/process-containment/native-guardian-containment.d.ts +16 -0
  79. package/dist/src/foundation/process-containment/native-guardian-containment.js +139 -0
  80. package/dist/src/foundation/process-containment/windows-process-inspector.d.ts +17 -0
  81. package/dist/src/foundation/process-containment/windows-process-inspector.js +53 -0
  82. package/dist/src/foundation/protocol/client.d.ts +2 -1
  83. package/dist/src/foundation/protocol/client.js +3 -0
  84. package/dist/src/foundation/protocol/messages.d.ts +5 -5
  85. package/dist/src/foundation/protocol/messages.js +1 -1
  86. package/dist/src/foundation/release/bootstrap.d.ts +2 -2
  87. package/dist/src/foundation/release/bootstrap.js +69 -18
  88. package/dist/src/foundation/release/cohort-selection.js +3 -3
  89. package/dist/src/foundation/release/cohort-state.d.ts +3 -2
  90. package/dist/src/foundation/release/concurrency.d.ts +1 -0
  91. package/dist/src/foundation/release/concurrency.js +17 -0
  92. package/dist/src/foundation/release/process-cleanup.d.ts +1 -1
  93. package/dist/src/foundation/release/process-cleanup.js +2 -2
  94. package/dist/src/foundation/release/release-store.d.ts +35 -2
  95. package/dist/src/foundation/release/release-store.js +90 -31
  96. package/dist/src/foundation/release/release.d.ts +15 -0
  97. package/dist/src/foundation/release/release.js +65 -36
  98. package/dist/src/foundation/release/update.d.ts +19 -2
  99. package/dist/src/foundation/release/update.js +132 -16
  100. package/dist/src/foundation/storage/control-store.d.ts +8 -7
  101. package/dist/src/foundation/storage/control-store.js +105 -55
  102. package/dist/src/foundation/supervision/main.js +6 -5
  103. package/dist/src/foundation/supervision/server.d.ts +4 -2
  104. package/dist/src/foundation/supervision/server.js +254 -87
  105. package/dist/src/foundation/ui-apps/contracts.d.ts +42 -0
  106. package/dist/src/foundation/ui-apps/contracts.js +12 -0
  107. package/dist/src/foundation/ui-apps/host.d.ts +42 -0
  108. package/dist/src/foundation/ui-apps/host.js +152 -0
  109. package/dist/src/foundation/ui-apps/index.d.ts +3 -0
  110. package/dist/src/foundation/ui-apps/index.js +3 -0
  111. package/dist/src/foundation/ui-apps/registry.d.ts +11 -0
  112. package/dist/src/foundation/ui-apps/registry.js +32 -0
  113. package/dist/src/foundation/ui-components/dialog-panel.d.ts +36 -0
  114. package/dist/src/foundation/ui-components/dialog-panel.js +47 -0
  115. package/dist/src/foundation/ui-components/frame.d.ts +21 -0
  116. package/dist/src/foundation/ui-components/frame.js +54 -0
  117. package/dist/src/foundation/ui-components/index.d.ts +18 -0
  118. package/dist/src/foundation/ui-components/index.js +18 -0
  119. package/dist/src/foundation/ui-components/label.d.ts +8 -0
  120. package/dist/src/foundation/ui-components/label.js +36 -0
  121. package/dist/src/foundation/ui-components/line-input.d.ts +74 -0
  122. package/dist/src/foundation/ui-components/line-input.js +245 -0
  123. package/dist/src/foundation/ui-components/list-block.d.ts +66 -0
  124. package/dist/src/foundation/ui-components/list-block.js +176 -0
  125. package/dist/src/foundation/ui-components/list-view.d.ts +66 -0
  126. package/dist/src/foundation/ui-components/list-view.js +76 -0
  127. package/dist/src/foundation/ui-components/mouse.d.ts +15 -0
  128. package/dist/src/foundation/ui-components/mouse.js +46 -0
  129. package/dist/src/foundation/ui-components/pane.d.ts +33 -0
  130. package/dist/src/foundation/ui-components/pane.js +7 -0
  131. package/dist/src/foundation/ui-components/revision.d.ts +35 -0
  132. package/dist/src/foundation/ui-components/revision.js +79 -0
  133. package/dist/src/foundation/ui-components/scrollbar.d.ts +62 -0
  134. package/dist/src/foundation/ui-components/scrollbar.js +102 -0
  135. package/dist/src/foundation/ui-components/shortcuts.d.ts +57 -0
  136. package/dist/src/foundation/ui-components/shortcuts.js +81 -0
  137. package/dist/src/foundation/ui-components/spans.d.ts +7 -0
  138. package/dist/src/foundation/ui-components/spans.js +59 -0
  139. package/dist/src/foundation/ui-components/status-line.d.ts +16 -0
  140. package/dist/src/foundation/ui-components/status-line.js +14 -0
  141. package/dist/src/foundation/ui-components/stepper.d.ts +24 -0
  142. package/dist/src/foundation/ui-components/stepper.js +33 -0
  143. package/dist/src/foundation/ui-components/surface.d.ts +20 -0
  144. package/dist/src/foundation/ui-components/surface.js +38 -0
  145. package/dist/src/foundation/ui-components/text.d.ts +24 -0
  146. package/dist/src/foundation/ui-components/text.js +123 -0
  147. package/dist/src/foundation/ui-components/theme.d.ts +23 -0
  148. package/dist/src/foundation/ui-components/theme.js +13 -0
  149. package/dist/src/foundation/ui-components/value-menu.d.ts +41 -0
  150. package/dist/src/foundation/ui-components/value-menu.js +33 -0
  151. package/dist/src/product-identity.d.ts +2 -2
  152. package/dist/src/product-identity.js +2 -1
  153. package/dist/src/product-identity.json +8 -5
  154. package/docs/architecture/boundaries.md +31 -13
  155. package/docs/architecture/process-guardian-provenance.md +26 -0
  156. package/docs/architecture/toolchain.md +9 -3
  157. package/docs/architecture/ui-reference-provenance.md +50 -0
  158. package/docs/ci-release-runbook.md +67 -0
  159. package/docs/features/launch-profiles.md +7 -1
  160. package/docs/manual-launch-instance-acceptance.md +66 -0
  161. package/docs/manual-terminal-colour-check.md +55 -0
  162. package/package.json +20 -14
  163. package/dist/src/composition/transparent-runtime.d.ts +0 -6
  164. package/dist/src/composition/transparent-runtime.js +0 -19
  165. package/dist/src/foundation/transparent-terminal/command-resolution.d.ts +0 -13
  166. package/dist/src/foundation/transparent-terminal/command-resolution.js +0 -69
  167. package/dist/src/foundation/transparent-terminal/foreground-broker.d.ts +0 -32
  168. package/dist/src/foundation/transparent-terminal/foreground-broker.js +0 -87
  169. package/dist/src/foundation/transparent-terminal/index.d.ts +0 -4
  170. package/dist/src/foundation/transparent-terminal/index.js +0 -4
  171. package/dist/src/foundation/transparent-terminal/main.d.ts +0 -9
  172. package/dist/src/foundation/transparent-terminal/main.js +0 -94
  173. package/dist/src/foundation/transparent-terminal/native-launcher.d.ts +0 -25
  174. package/dist/src/foundation/transparent-terminal/native-launcher.js +0 -146
  175. /package/bin/{a1.js → cli.js} +0 -0
  176. /package/bin/{a1-supervisor.js → supervisor.js} +0 -0
@@ -163,14 +163,16 @@ export function createPiShellReloadBox() {
163
163
  }
164
164
  export function createPiShellAuthProviderSelector(mode, providers, onSelect, onCancel) {
165
165
  ensureTheme();
166
- const selector = new OAuthSelectorComponent(mode, providers.map(provider => {
167
- const [authType, providerId] = provider.id.includes(":") ? provider.id.split(":", 2) : ["oauth", provider.id];
168
- return {
169
- id: providerId,
170
- name: provider.label,
171
- authType: authType === "api_key" ? "api_key" : "oauth",
172
- };
173
- }), (providerId, authType) => onSelect(`${authType}:${providerId}`), onCancel);
166
+ const selector = new OAuthSelectorComponent(mode, providers.map(provider => ({
167
+ id: provider.providerId,
168
+ name: provider.label,
169
+ authType: provider.authType,
170
+ ...(provider.status === undefined ? {} : { status: provider.status }),
171
+ })), (providerId, authType) => {
172
+ const selected = providers.find(provider => provider.providerId === providerId && provider.authType === authType);
173
+ if (selected)
174
+ onSelect(selected.id);
175
+ }, onCancel);
174
176
  return componentPort(selector);
175
177
  }
176
178
  export function createPiShellExtensionSelector(title, options, onSelect, onCancel) {
@@ -51,3 +51,11 @@ export declare function resolvePiThemeSetting(setting: string | undefined, termi
51
51
  export declare function detectPiTerminalBackgroundFromEnv(environment?: NodeJS.ProcessEnv): PiTerminalThemeDetection;
52
52
  export declare function detectPiTerminalBackgroundTheme(ui: PiTerminalThemeDetector, timeoutMs: number, environment?: NodeJS.ProcessEnv): Promise<PiTerminalThemeDetection>;
53
53
  export declare function detectPiTerminalThemeForAuto(ui: PiTerminalThemeDetector, timeoutMs: number, environment?: NodeJS.ProcessEnv): Promise<PiTerminalTheme>;
54
+ /**
55
+ * Applies the theme the reader configured, as the engine does at startup. The
56
+ * setting is either a theme's name or the `light/dark` pair meaning "follow the
57
+ * terminal", and only the pair consults the terminal's own background. An
58
+ * unreadable or absent setting falls back to that detection, which is what the
59
+ * engine does with an unset theme.
60
+ */
61
+ export declare function applyConfiguredPiTheme(setting: string | undefined): PiThemeResult;
@@ -312,3 +312,15 @@ function ansi256ToHex(index) {
312
312
  function isRecord(value) {
313
313
  return typeof value === "object" && value !== null && !Array.isArray(value);
314
314
  }
315
+ /**
316
+ * Applies the theme the reader configured, as the engine does at startup. The
317
+ * setting is either a theme's name or the `light/dark` pair meaning "follow the
318
+ * terminal", and only the pair consults the terminal's own background. An
319
+ * unreadable or absent setting falls back to that detection, which is what the
320
+ * engine does with an unset theme.
321
+ */
322
+ export function applyConfiguredPiTheme(setting) {
323
+ const detected = detectPiTerminalBackgroundFromEnv();
324
+ const resolved = resolvePiThemeSetting(setting, detected.theme);
325
+ return applyPiTheme(resolved ?? detected.theme);
326
+ }
@@ -1,6 +1,7 @@
1
1
  import { type AgentSessionRuntime, type SessionInfo } from "@earendil-works/pi-coding-agent";
2
2
  import { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, type OwnedUiCommand, type OwnedUiCommandOutcome, type OwnedUiEvent, type OwnedUiSessionViewModel, type OwnedUiSnapshot } from "../owned-ui-contracts/index.js";
3
- import { type PiBashWorkflowResult, type PiPinnedSettingsCallback, type PiPinnedSettingsSnapshot, type PiWorkflowAutocompleteCommand, type PiWorkflowHost, type PiWorkflowInteractionHost, type PiWorkflowOption, type PiWorkflowRequest, type PiWorkflowResult } from "./workflows.js";
3
+ import { type PiAuthenticationProviderOption, type PiBashWorkflowResult, type PiPinnedSettingsCallback, type PiPinnedSettingsSnapshot, type PiWorkflowAutocompleteCommand, type PiWorkflowHost, type PiWorkflowInteractionHost, type PiWorkflowOption, type PiWorkflowRequest, type PiWorkflowResult } from "./workflows.js";
4
+ import type { AgentSettingsPort } from "../agent-engine-contracts/index.js";
4
5
  export interface PiEngineRuntimeFactoryInput {
5
6
  readonly cwd: string;
6
7
  readonly agentDir: string;
@@ -83,6 +84,12 @@ export interface PiEngineAdapterOptions {
83
84
  readonly sessionId?: string;
84
85
  readonly createRuntime?: PiEngineRuntimeFactory;
85
86
  readonly workflowHost?: PiWorkflowHost;
87
+ /**
88
+ * Themes installed on this machine. Supplied because listing them belongs to
89
+ * the component adapter that owns the engine's theme unit; the grammar and the
90
+ * offering stay here.
91
+ */
92
+ readonly availableThemes?: () => readonly string[];
86
93
  }
87
94
  export interface AdapterCommandResult {
88
95
  readonly outcome: OwnedUiCommandOutcome;
@@ -106,6 +113,13 @@ export declare class PiEngineAdapter {
106
113
  unbindExtensionUi(): Promise<void>;
107
114
  workflowAutocompleteCommands(): readonly PiWorkflowAutocompleteCommand[];
108
115
  cycleModelWorkflow(direction: "forward" | "backward"): Promise<PiWorkflowResult>;
116
+ /**
117
+ * The theme the engine is configured with, in the engine's own grammar: a
118
+ * theme's name, or a `light/dark` pair meaning "follow the terminal".
119
+ */
120
+ configuredTheme(): string | undefined;
121
+ /** Settings port for the live runtime, or null before the runtime is available. */
122
+ settingsPort(): AgentSettingsPort | null;
109
123
  pinnedModelSelectorContext(): {
110
124
  readonly currentModel: unknown;
111
125
  readonly settingsManager: unknown;
@@ -119,12 +133,12 @@ export declare class PiEngineAdapter {
119
133
  updateScopedModels(enabledModelIds: readonly string[] | null): void;
120
134
  persistScopedModels(enabledModelIds: readonly string[] | null): void;
121
135
  refreshScopedModels(signal: AbortSignal): Promise<PiScopedModelsRefreshResult>;
122
- pinnedLoginOptions(authType?: "oauth" | "api_key"): readonly PiWorkflowOption[];
136
+ pinnedLoginOptions(authType?: "oauth" | "api_key"): readonly PiAuthenticationProviderOption[];
123
137
  pinnedLoginMethodOptions(providerReference: string): {
124
138
  readonly title: string;
125
139
  readonly options: readonly PiWorkflowOption[];
126
140
  };
127
- pinnedLogoutOptions(): Promise<readonly PiWorkflowOption[]>;
141
+ pinnedLogoutOptions(): Promise<readonly PiAuthenticationProviderOption[]>;
128
142
  pinnedForkOptions(): readonly PiWorkflowOption[];
129
143
  pinnedTreeSelectorContext(): PiTreeSelectorContext;
130
144
  pinnedSettingsSnapshot(): PiPinnedSettingsSnapshot;
@@ -66,12 +66,15 @@ export class PiEngineAdapter {
66
66
  #extensionShutdown;
67
67
  #extensionBound = false;
68
68
  #disposed = false;
69
+ /** Lists the themes installed on this machine, when the caller supplies one. */
70
+ #availableThemes;
69
71
  constructor(options = {}) {
70
72
  this.#cwd = options.cwd ?? process.cwd();
71
73
  this.#agentDir = options.agentDir ?? getAgentDir();
72
74
  this.#sessionId = options.sessionId ?? "owned-session-1";
73
75
  this.#runtimeFactory = options.createRuntime ?? createDefaultPiRuntime;
74
76
  this.#workflowHost = options.workflowHost ?? defaultWorkflowHost();
77
+ this.#availableThemes = options.availableThemes ?? null;
75
78
  this.#workflowInteraction = { prompt: async () => null, notify() { } };
76
79
  }
77
80
  setWorkflowInteractionHost(interaction) {
@@ -377,6 +380,27 @@ export class PiEngineAdapter {
377
380
  return workflowResult("model", "failed", error instanceof Error ? error.message : String(error));
378
381
  }
379
382
  }
383
+ /**
384
+ * The theme the engine is configured with, in the engine's own grammar: a
385
+ * theme's name, or a `light/dark` pair meaning "follow the terminal".
386
+ */
387
+ configuredTheme() {
388
+ return this.#runtime?.services.settingsManager.getThemeSetting();
389
+ }
390
+ /** Settings port for the live runtime, or null before the runtime is available. */
391
+ settingsPort() {
392
+ const settings = this.#runtime?.services.settingsManager;
393
+ if (!settings)
394
+ return null;
395
+ const session = this.#runtime?.session;
396
+ return new PiSettingsIntegration(settings, {
397
+ ...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
398
+ thinkingLevels: () => {
399
+ const levels = session?.getAvailableThinkingLevels?.();
400
+ return Array.isArray(levels) ? levels.map(level => String(level)) : [];
401
+ },
402
+ });
403
+ }
380
404
  pinnedModelSelectorContext() {
381
405
  const session = this.#requireWorkflowSession();
382
406
  const runtime = this.#runtime;
@@ -397,7 +421,7 @@ export class PiEngineAdapter {
397
421
  ? settingsManager
398
422
  : { setDefaultModelAndProvider() { } };
399
423
  return {
400
- currentModel: session.model,
424
+ currentModel: this.#activeModel === null ? undefined : session.model,
401
425
  settingsManager: selectorSettings,
402
426
  modelRuntime: selectorRuntime,
403
427
  scopedModels: Array.isArray(scoped) ? scoped : [],
@@ -539,8 +563,9 @@ export class PiEngineAdapter {
539
563
  const oauth = dynamicObject(dynamicObject(provider, "auth"), "oauth");
540
564
  const loginLabel = stringProperty(oauth, "loginLabel") ?? "Sign in with an account";
541
565
  const options = this.#loginOptions().filter(option => option.id.endsWith(`:${providerId}`)).map(option => ({
542
- ...option,
566
+ id: option.id,
543
567
  label: option.id.startsWith("api_key:") ? "Sign in with an API key" : loginLabel,
568
+ ...(option.description === undefined ? {} : { description: option.description }),
544
569
  }));
545
570
  return { title: `Select authentication method for ${providerName}:`, options };
546
571
  }
@@ -687,7 +712,7 @@ export class PiEngineAdapter {
687
712
  footer: {
688
713
  branch: this.#gitBranch,
689
714
  sessionName: this.#session?.sessionManager?.getSessionName() ?? null,
690
- availableProviderCount: new Set(this.#runtime?.services.modelRuntime.getAvailableSnapshot?.().map(model => model.provider).filter(provider => provider !== undefined) ?? []).size || 1,
715
+ availableProviderCount: new Set(this.#runtime?.services.modelRuntime.getAvailableSnapshot?.().map(model => model.provider).filter(provider => provider !== undefined) ?? []).size,
691
716
  extensionStatuses: [],
692
717
  },
693
718
  },
@@ -987,6 +1012,8 @@ export class PiEngineAdapter {
987
1012
  finally {
988
1013
  this.#workflowInteraction.finishLogin?.();
989
1014
  }
1015
+ this.#reconcileActiveModelAvailability();
1016
+ this.#emitView();
990
1017
  return workflowResult(request.command, "completed", `Logged in to ${providerName}. Credentials saved to ${join(this.#agentDir, "auth.json")}`);
991
1018
  }
992
1019
  case "logout": {
@@ -997,6 +1024,8 @@ export class PiEngineAdapter {
997
1024
  await requireCapability(modelRuntime.logout, "logout").call(modelRuntime, providerId, { signal: AbortSignal.timeout(15_000) });
998
1025
  const provider = modelRuntime.getProvider?.(providerId);
999
1026
  const providerName = stringProperty(provider, "name") ?? providerId;
1027
+ this.#reconcileActiveModelAvailability();
1028
+ this.#emitView();
1000
1029
  return workflowResult(request.command, "completed", credentialType === "api_key"
1001
1030
  ? `Removed stored API key for ${providerName}. Environment variables and models.json config are unchanged.`
1002
1031
  : `Logged out of ${providerName}`);
@@ -1087,7 +1116,10 @@ export class PiEngineAdapter {
1087
1116
  }
1088
1117
  #loginOptions(authType) {
1089
1118
  const runtime = this.#runtime;
1090
- const providers = runtime?.services.modelRuntime.getProviders?.();
1119
+ const modelRuntime = runtime?.services.modelRuntime;
1120
+ if (!modelRuntime)
1121
+ return [];
1122
+ const providers = modelRuntime.getProviders?.();
1091
1123
  if (!Array.isArray(providers))
1092
1124
  return [];
1093
1125
  return providers.filter(isRecord).flatMap(provider => {
@@ -1096,11 +1128,33 @@ export class PiEngineAdapter {
1096
1128
  return [];
1097
1129
  const name = stringProperty(provider, "name") ?? id;
1098
1130
  const auth = isRecord(provider.auth) ? provider.auth : {};
1131
+ const authStatus = modelRuntime.getProviderAuthStatus?.(id);
1132
+ const source = authStatus?.label ?? authStatus?.source;
1133
+ const status = authStatus?.configured === true
1134
+ ? {
1135
+ type: modelRuntime.isUsingOAuth?.(id) === true ? "oauth" : "api_key",
1136
+ ...(source === undefined ? {} : { source }),
1137
+ }
1138
+ : undefined;
1099
1139
  return [
1100
- ...(authType !== "api_key" && auth.oauth ? [{ id: `oauth:${id}`, label: name, description: "Account / OAuth" }] : []),
1101
- ...(authType !== "oauth" && auth.apiKey ? [{ id: `api_key:${id}`, label: name, description: "API key" }] : []),
1140
+ ...(authType !== "api_key" && auth.oauth ? [{
1141
+ id: `oauth:${id}`,
1142
+ providerId: id,
1143
+ label: name,
1144
+ description: "Account / OAuth",
1145
+ authType: "oauth",
1146
+ ...(status === undefined ? {} : { status }),
1147
+ }] : []),
1148
+ ...(authType !== "oauth" && auth.apiKey ? [{
1149
+ id: `api_key:${id}`,
1150
+ providerId: id,
1151
+ label: name,
1152
+ description: "API key",
1153
+ authType: "api_key",
1154
+ ...(status === undefined ? {} : { status }),
1155
+ }] : []),
1102
1156
  ];
1103
- });
1157
+ }).sort((left, right) => left.label.localeCompare(right.label));
1104
1158
  }
1105
1159
  async #logoutOptions() {
1106
1160
  const runtime = this.#runtime;
@@ -1118,8 +1172,11 @@ export class PiEngineAdapter {
1118
1172
  const provider = modelRuntime.getProvider?.(providerId);
1119
1173
  return [{
1120
1174
  id: `${credentialType}:${providerId}`,
1175
+ providerId,
1121
1176
  label: stringProperty(provider, "name") ?? providerId,
1122
1177
  description: credentialType,
1178
+ authType: credentialType,
1179
+ status: { type: credentialType, source: "stored credential" },
1123
1180
  }];
1124
1181
  });
1125
1182
  }
@@ -1272,12 +1329,28 @@ export class PiEngineAdapter {
1272
1329
  };
1273
1330
  this.#status = { ...this.#status, workingMessage: null, badges: [] };
1274
1331
  this.#activeModel = readModel(session.model);
1332
+ this.#reconcileActiveModelAvailability();
1275
1333
  this.#thinkingLevel = readThinkingLevel(session.thinkingLevel);
1276
1334
  this.#rebuildTranscript(session.messages, "finalized");
1277
1335
  this.#unsubscribe = session.subscribe(event => this.#handlePiEvent(event));
1278
1336
  if (this.#extensionUi !== undefined)
1279
1337
  void this.#bindExtensionUiToSession();
1280
1338
  }
1339
+ #reconcileActiveModelAvailability() {
1340
+ const modelRuntime = this.#runtime?.services.modelRuntime;
1341
+ if (!modelRuntime) {
1342
+ this.#activeModel = null;
1343
+ return;
1344
+ }
1345
+ const available = modelRuntime.getAvailableSnapshot?.() ?? [];
1346
+ const sessionModel = readModel(this.#session?.model);
1347
+ const authoritative = this.#activeModel ?? sessionModel;
1348
+ if (authoritative === null)
1349
+ return;
1350
+ const remainsAvailable = available.some(model => stringProperty(model, "provider") === authoritative.providerId
1351
+ && stringProperty(model, "id") === authoritative.modelId);
1352
+ this.#activeModel = remainsAvailable ? authoritative : null;
1353
+ }
1281
1354
  async #bindExtensionUiToSession() {
1282
1355
  const session = this.#session;
1283
1356
  const ui = this.#extensionUi;
@@ -0,0 +1,374 @@
1
+ {
2
+ "presented": [
3
+ "autoCompact",
4
+ "showImages",
5
+ "imageWidthCells",
6
+ "autoResizeImages",
7
+ "blockImages",
8
+ "enableSkillCommands",
9
+ "showHardwareCursor",
10
+ "editorPaddingX",
11
+ "outputPad",
12
+ "autocompleteMaxVisible",
13
+ "clearOnShrink",
14
+ "showTerminalProgress",
15
+ "steeringMode",
16
+ "followUpMode",
17
+ "transport",
18
+ "httpIdleTimeoutMs",
19
+ "hideThinkingBlock",
20
+ "mermaidRenderingMode",
21
+ "showCacheMissNotices",
22
+ "collapseChangelog",
23
+ "quietStartup",
24
+ "enableInstallTelemetry",
25
+ "defaultProjectTrust",
26
+ "doubleEscapeAction",
27
+ "treeFilterMode",
28
+ "warnings",
29
+ "thinkingLevel",
30
+ "tuiMode",
31
+ "fullscreenExitOutput",
32
+ "fullscreenScrollbar",
33
+ "theme"
34
+ ],
35
+ "order": [
36
+ "autoCompact",
37
+ "showImages",
38
+ "imageWidthCells",
39
+ "autoResizeImages",
40
+ "blockImages",
41
+ "enableSkillCommands",
42
+ "showHardwareCursor",
43
+ "editorPaddingX",
44
+ "outputPad",
45
+ "autocompleteMaxVisible",
46
+ "clearOnShrink",
47
+ "showTerminalProgress",
48
+ "steeringMode",
49
+ "followUpMode",
50
+ "transport",
51
+ "httpIdleTimeoutMs",
52
+ "hideThinkingBlock",
53
+ "mermaidRenderingMode",
54
+ "showCacheMissNotices",
55
+ "collapseChangelog",
56
+ "quietStartup",
57
+ "enableInstallTelemetry",
58
+ "defaultProjectTrust",
59
+ "doubleEscapeAction",
60
+ "treeFilterMode",
61
+ "warnings",
62
+ "thinkingLevel",
63
+ "tuiMode",
64
+ "fullscreenExitOutput",
65
+ "fullscreenScrollbar",
66
+ "theme"
67
+ ],
68
+ "settings": {
69
+ "autoCompact": {
70
+ "label": "Auto-compact",
71
+ "description": "Automatically compact context when it gets too large",
72
+ "opensDialog": false,
73
+ "values": [
74
+ "true",
75
+ "false"
76
+ ]
77
+ },
78
+ "showImages": {
79
+ "label": "Show images",
80
+ "description": "Render images inline in terminal",
81
+ "opensDialog": false,
82
+ "values": [
83
+ "true",
84
+ "false"
85
+ ]
86
+ },
87
+ "imageWidthCells": {
88
+ "label": "Image width",
89
+ "description": "Preferred inline image width in terminal cells",
90
+ "opensDialog": false,
91
+ "values": [
92
+ "60",
93
+ "80",
94
+ "120"
95
+ ]
96
+ },
97
+ "autoResizeImages": {
98
+ "label": "Auto-resize images",
99
+ "description": "Resize large images to 2000x2000 max for better model compatibility",
100
+ "opensDialog": false,
101
+ "values": [
102
+ "true",
103
+ "false"
104
+ ]
105
+ },
106
+ "blockImages": {
107
+ "label": "Block images",
108
+ "description": "Prevent images from being sent to LLM providers",
109
+ "opensDialog": false,
110
+ "values": [
111
+ "true",
112
+ "false"
113
+ ]
114
+ },
115
+ "enableSkillCommands": {
116
+ "label": "Skill commands",
117
+ "description": "Register skills as /skill:name commands",
118
+ "opensDialog": false,
119
+ "values": [
120
+ "true",
121
+ "false"
122
+ ]
123
+ },
124
+ "showHardwareCursor": {
125
+ "label": "Show hardware cursor",
126
+ "description": "Show the terminal cursor while still positioning it for IME support",
127
+ "opensDialog": false,
128
+ "values": [
129
+ "true",
130
+ "false"
131
+ ]
132
+ },
133
+ "editorPaddingX": {
134
+ "label": "Editor padding",
135
+ "description": "Horizontal padding for input editor (0-3)",
136
+ "opensDialog": false,
137
+ "values": [
138
+ "0",
139
+ "1",
140
+ "2",
141
+ "3"
142
+ ]
143
+ },
144
+ "outputPad": {
145
+ "label": "Output padding",
146
+ "description": "Horizontal padding for user messages, assistant messages, and thinking",
147
+ "opensDialog": false,
148
+ "values": [
149
+ "0",
150
+ "1"
151
+ ]
152
+ },
153
+ "autocompleteMaxVisible": {
154
+ "label": "Autocomplete max items",
155
+ "description": "Max visible items in autocomplete dropdown (3-20)",
156
+ "opensDialog": false,
157
+ "values": [
158
+ "3",
159
+ "5",
160
+ "7",
161
+ "10",
162
+ "15",
163
+ "20"
164
+ ]
165
+ },
166
+ "clearOnShrink": {
167
+ "label": "Clear on shrink",
168
+ "description": "Clear empty rows when content shrinks (may cause flicker)",
169
+ "opensDialog": false,
170
+ "values": [
171
+ "true",
172
+ "false"
173
+ ]
174
+ },
175
+ "showTerminalProgress": {
176
+ "label": "Terminal progress",
177
+ "description": "Show OSC 9;4 progress indicators in the terminal tab bar",
178
+ "opensDialog": false,
179
+ "values": [
180
+ "true",
181
+ "false"
182
+ ]
183
+ },
184
+ "steeringMode": {
185
+ "label": "Steering mode",
186
+ "description": "Enter while streaming queues steering messages. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.",
187
+ "opensDialog": false,
188
+ "values": [
189
+ "one-at-a-time",
190
+ "all"
191
+ ]
192
+ },
193
+ "followUpMode": {
194
+ "label": "Follow-up mode",
195
+ "description": "",
196
+ "opensDialog": false,
197
+ "values": [
198
+ "one-at-a-time",
199
+ "all"
200
+ ]
201
+ },
202
+ "transport": {
203
+ "label": "Transport",
204
+ "description": "Preferred transport for providers that support multiple transports",
205
+ "opensDialog": false,
206
+ "values": [
207
+ "sse",
208
+ "websocket",
209
+ "websocket-cached",
210
+ "auto"
211
+ ]
212
+ },
213
+ "httpIdleTimeoutMs": {
214
+ "label": "HTTP idle timeout",
215
+ "description": "Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.",
216
+ "opensDialog": false
217
+ },
218
+ "hideThinkingBlock": {
219
+ "label": "Hide thinking",
220
+ "description": "Hide thinking blocks in assistant responses",
221
+ "opensDialog": false,
222
+ "values": [
223
+ "true",
224
+ "false"
225
+ ]
226
+ },
227
+ "mermaidRenderingMode": {
228
+ "label": "Mermaid diagrams",
229
+ "description": "Render Mermaid code blocks as Unicode diagrams",
230
+ "opensDialog": false,
231
+ "values": [
232
+ "off",
233
+ "final",
234
+ "streaming"
235
+ ]
236
+ },
237
+ "showCacheMissNotices": {
238
+ "label": "Cache miss notices",
239
+ "description": "Show transcript notices for significant prompt-cache misses",
240
+ "opensDialog": false,
241
+ "values": [
242
+ "true",
243
+ "false"
244
+ ]
245
+ },
246
+ "collapseChangelog": {
247
+ "label": "Collapse changelog",
248
+ "description": "Show condensed changelog after updates",
249
+ "opensDialog": false,
250
+ "values": [
251
+ "true",
252
+ "false"
253
+ ]
254
+ },
255
+ "quietStartup": {
256
+ "label": "Quiet startup",
257
+ "description": "Disable verbose printing at startup",
258
+ "opensDialog": false,
259
+ "values": [
260
+ "true",
261
+ "false"
262
+ ]
263
+ },
264
+ "enableInstallTelemetry": {
265
+ "label": "Install telemetry",
266
+ "description": "Send an anonymous version/update ping after changelog-detected updates",
267
+ "opensDialog": false,
268
+ "values": [
269
+ "true",
270
+ "false"
271
+ ]
272
+ },
273
+ "defaultProjectTrust": {
274
+ "label": "Default project trust",
275
+ "description": "Fallback behavior when no extension or saved trust decision decides project trust",
276
+ "opensDialog": false,
277
+ "values": [
278
+ "ask",
279
+ "always",
280
+ "never"
281
+ ]
282
+ },
283
+ "doubleEscapeAction": {
284
+ "label": "Double-escape action",
285
+ "description": "Action when pressing Escape twice with empty editor",
286
+ "opensDialog": false,
287
+ "values": [
288
+ "tree",
289
+ "fork",
290
+ "none"
291
+ ]
292
+ },
293
+ "treeFilterMode": {
294
+ "label": "Tree filter mode",
295
+ "description": "Default filter when opening /tree",
296
+ "opensDialog": false,
297
+ "values": [
298
+ "default",
299
+ "no-tools",
300
+ "user-only",
301
+ "labeled-only",
302
+ "all"
303
+ ]
304
+ },
305
+ "warnings": {
306
+ "label": "Warnings",
307
+ "description": "Enable or disable individual warnings",
308
+ "opensDialog": true,
309
+ "literalValue": "configure"
310
+ },
311
+ "thinkingLevel": {
312
+ "label": "Thinking level",
313
+ "description": "Reasoning depth for thinking-capable models",
314
+ "opensDialog": true
315
+ },
316
+ "tuiMode": {
317
+ "label": "TUI mode",
318
+ "description": "Interface layout; fullscreen mode is experimental",
319
+ "opensDialog": false,
320
+ "values": [
321
+ "regular",
322
+ "fullscreen"
323
+ ]
324
+ },
325
+ "fullscreenExitOutput": {
326
+ "label": "Fullscreen exit output",
327
+ "description": "Print the transcript or only a session resume hint when exiting fullscreen mode",
328
+ "opensDialog": false,
329
+ "values": [
330
+ "transcript",
331
+ "resume-hint"
332
+ ]
333
+ },
334
+ "fullscreenScrollbar": {
335
+ "label": "Fullscreen scrollbar",
336
+ "description": "Scrollbar behavior in fullscreen mode; has no effect in regular mode",
337
+ "opensDialog": false,
338
+ "values": [
339
+ "auto",
340
+ "always",
341
+ "hidden"
342
+ ]
343
+ },
344
+ "theme": {
345
+ "label": "Theme",
346
+ "description": "Color theme for the interface",
347
+ "opensDialog": true
348
+ }
349
+ },
350
+ "dialogs": {
351
+ "warnings": [
352
+ {
353
+ "id": "anthropic-extra-usage",
354
+ "label": "Anthropic extra usage",
355
+ "description": "Warn when Anthropic subscription auth may use paid extra usage",
356
+ "key": "anthropicExtraUsage",
357
+ "fallback": true
358
+ }
359
+ ]
360
+ },
361
+ "bounds": {
362
+ "imageWidthCells": {
363
+ "minimum": 1
364
+ },
365
+ "editorPaddingX": {
366
+ "minimum": 0,
367
+ "maximum": 3
368
+ },
369
+ "autocompleteMaxVisible": {
370
+ "minimum": 3,
371
+ "maximum": 20
372
+ }
373
+ }
374
+ }
@@ -1,3 +1,4 @@
1
+ import { realpathSync } from "node:fs";
1
2
  import { resolve } from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { main as runPublicMain } from "@earendil-works/pi-coding-agent";
@@ -13,6 +14,14 @@ export async function runPublicMainEntry(arguments_, dependencies = {}) {
13
14
  return 1;
14
15
  }
15
16
  }
16
- const isMain = process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
17
+ const isMain = process.argv[1] !== undefined && canonicalPath(process.argv[1]) === canonicalPath(fileURLToPath(import.meta.url));
17
18
  if (isMain)
18
19
  process.exitCode = await runPublicMainEntry(process.argv.slice(2));
20
+ function canonicalPath(path) {
21
+ try {
22
+ return realpathSync.native(path);
23
+ }
24
+ catch {
25
+ return resolve(path);
26
+ }
27
+ }