@timurproko/a1 0.1.1-dev.5 → 0.1.1-dev.7

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 (151) hide show
  1. package/README.md +32 -9
  2. package/bin/a1-guardian.js +22 -0
  3. package/bin/a1-ui.js +7 -4
  4. package/dist/native/darwin-arm64/a1-process-guardian +0 -0
  5. package/dist/native/darwin-arm64/manifest.json +20 -0
  6. package/dist/native/linux-x64/a1-process-guardian +0 -0
  7. package/dist/native/linux-x64/manifest.json +20 -0
  8. package/dist/native/win32-x64/a1-process-guardian.exe +0 -0
  9. package/dist/native/win32-x64/manifest.json +20 -0
  10. package/dist/src/composition/index.d.ts +26 -0
  11. package/dist/src/composition/index.js +88 -2
  12. package/dist/src/features/launch/runtime-selection.d.ts +2 -1
  13. package/dist/src/features/launch/runtime-selection.js +2 -2
  14. package/dist/src/features/owned-ui/index.d.ts +1 -0
  15. package/dist/src/features/owned-ui/index.js +1 -0
  16. package/dist/src/features/owned-ui/run.d.ts +6 -0
  17. package/dist/src/features/owned-ui/run.js +3 -1
  18. package/dist/src/features/owned-ui/settings-app.d.ts +22 -0
  19. package/dist/src/features/owned-ui/settings-app.js +631 -0
  20. package/dist/src/foundation/agent-engine-contracts/domain.d.ts +26 -0
  21. package/dist/src/foundation/launch-guardian/index.d.ts +1 -0
  22. package/dist/src/foundation/launch-guardian/index.js +1 -0
  23. package/dist/src/foundation/launch-guardian/main.d.ts +31 -0
  24. package/dist/src/foundation/launch-guardian/main.js +165 -0
  25. package/dist/src/foundation/lifecycle/commands.d.ts +40 -0
  26. package/dist/src/foundation/lifecycle/commands.js +1 -0
  27. package/dist/src/foundation/lifecycle/index.d.ts +2 -0
  28. package/dist/src/foundation/lifecycle/index.js +2 -0
  29. package/dist/src/foundation/lifecycle/launch-instance.d.ts +46 -0
  30. package/dist/src/foundation/lifecycle/launch-instance.js +134 -0
  31. package/dist/src/foundation/lifecycle/model.d.ts +6 -42
  32. package/dist/src/foundation/owned-ui-settings/declarations.d.ts +13 -0
  33. package/dist/src/foundation/owned-ui-settings/declarations.js +39 -0
  34. package/dist/src/foundation/owned-ui-settings/index.d.ts +6 -0
  35. package/dist/src/foundation/owned-ui-settings/index.js +6 -0
  36. package/dist/src/foundation/owned-ui-settings/migrations.d.ts +13 -0
  37. package/dist/src/foundation/owned-ui-settings/migrations.js +28 -0
  38. package/dist/src/foundation/owned-ui-settings/resolution.d.ts +45 -0
  39. package/dist/src/foundation/owned-ui-settings/resolution.js +147 -0
  40. package/dist/src/foundation/owned-ui-settings/sections.d.ts +70 -0
  41. package/dist/src/foundation/owned-ui-settings/sections.js +93 -0
  42. package/dist/src/foundation/owned-ui-settings/session.d.ts +44 -0
  43. package/dist/src/foundation/owned-ui-settings/session.js +145 -0
  44. package/dist/src/foundation/owned-ui-settings/store.d.ts +28 -0
  45. package/dist/src/foundation/owned-ui-settings/store.js +90 -0
  46. package/dist/src/foundation/pi-component-adapter/index.d.ts +1 -0
  47. package/dist/src/foundation/pi-component-adapter/index.js +1 -0
  48. package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.d.ts +9 -1
  49. package/dist/src/foundation/pi-component-adapter/shell-selectors-dialogs.js +10 -8
  50. package/dist/src/foundation/pi-engine-adapter/adapter.d.ts +12 -3
  51. package/dist/src/foundation/pi-engine-adapter/adapter.js +73 -7
  52. package/dist/src/foundation/pi-engine-adapter/pi-settings-metadata.json +374 -0
  53. package/dist/src/foundation/pi-engine-adapter/public-main-entry.js +10 -1
  54. package/dist/src/foundation/pi-engine-adapter/settings-integration.d.ts +42 -2
  55. package/dist/src/foundation/pi-engine-adapter/settings-integration.js +185 -24
  56. package/dist/src/foundation/pi-engine-adapter/workflows.d.ts +9 -0
  57. package/dist/src/foundation/pi-owned-ui-integration/index.d.ts +1 -0
  58. package/dist/src/foundation/pi-owned-ui-integration/index.js +1 -0
  59. package/dist/src/foundation/pi-owned-ui-integration/route-host.d.ts +29 -0
  60. package/dist/src/foundation/pi-owned-ui-integration/route-host.js +1 -0
  61. package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +6 -0
  62. package/dist/src/foundation/pi-owned-ui-integration/session-shell.js +71 -1
  63. package/dist/src/foundation/pi-tui-runtime-adapter/adapter.d.ts +6 -0
  64. package/dist/src/foundation/pi-tui-runtime-adapter/adapter.js +9 -0
  65. package/dist/src/foundation/process-containment/artifact.d.ts +1 -0
  66. package/dist/src/foundation/process-containment/artifact.js +34 -0
  67. package/dist/src/foundation/process-containment/contracts.d.ts +31 -0
  68. package/dist/src/foundation/process-containment/contracts.js +35 -0
  69. package/dist/src/foundation/process-containment/index.d.ts +5 -0
  70. package/dist/src/foundation/process-containment/index.js +5 -0
  71. package/dist/src/foundation/process-containment/linux-process-inspector.d.ts +6 -0
  72. package/dist/src/foundation/process-containment/linux-process-inspector.js +26 -0
  73. package/dist/src/foundation/process-containment/native-guardian-containment.d.ts +16 -0
  74. package/dist/src/foundation/process-containment/native-guardian-containment.js +139 -0
  75. package/dist/src/foundation/process-containment/windows-process-inspector.d.ts +17 -0
  76. package/dist/src/foundation/process-containment/windows-process-inspector.js +53 -0
  77. package/dist/src/foundation/protocol/client.d.ts +2 -1
  78. package/dist/src/foundation/protocol/client.js +3 -0
  79. package/dist/src/foundation/protocol/messages.d.ts +5 -5
  80. package/dist/src/foundation/protocol/messages.js +1 -1
  81. package/dist/src/foundation/release/bootstrap.js +29 -10
  82. package/dist/src/foundation/release/cohort-selection.js +3 -3
  83. package/dist/src/foundation/release/cohort-state.d.ts +3 -2
  84. package/dist/src/foundation/release/process-cleanup.d.ts +1 -1
  85. package/dist/src/foundation/release/process-cleanup.js +2 -2
  86. package/dist/src/foundation/release/update.js +2 -2
  87. package/dist/src/foundation/storage/control-store.d.ts +8 -7
  88. package/dist/src/foundation/storage/control-store.js +105 -55
  89. package/dist/src/foundation/supervision/server.d.ts +4 -2
  90. package/dist/src/foundation/supervision/server.js +254 -87
  91. package/dist/src/foundation/transparent-terminal/command-resolution.js +6 -3
  92. package/dist/src/foundation/transparent-terminal/foreground-broker.d.ts +5 -13
  93. package/dist/src/foundation/transparent-terminal/foreground-broker.js +11 -68
  94. package/dist/src/foundation/transparent-terminal/main.js +1 -8
  95. package/dist/src/foundation/ui-apps/contracts.d.ts +42 -0
  96. package/dist/src/foundation/ui-apps/contracts.js +12 -0
  97. package/dist/src/foundation/ui-apps/host.d.ts +42 -0
  98. package/dist/src/foundation/ui-apps/host.js +152 -0
  99. package/dist/src/foundation/ui-apps/index.d.ts +3 -0
  100. package/dist/src/foundation/ui-apps/index.js +3 -0
  101. package/dist/src/foundation/ui-apps/registry.d.ts +11 -0
  102. package/dist/src/foundation/ui-apps/registry.js +32 -0
  103. package/dist/src/foundation/ui-components/dialog-panel.d.ts +36 -0
  104. package/dist/src/foundation/ui-components/dialog-panel.js +47 -0
  105. package/dist/src/foundation/ui-components/frame.d.ts +21 -0
  106. package/dist/src/foundation/ui-components/frame.js +54 -0
  107. package/dist/src/foundation/ui-components/index.d.ts +18 -0
  108. package/dist/src/foundation/ui-components/index.js +18 -0
  109. package/dist/src/foundation/ui-components/label.d.ts +8 -0
  110. package/dist/src/foundation/ui-components/label.js +36 -0
  111. package/dist/src/foundation/ui-components/line-input.d.ts +74 -0
  112. package/dist/src/foundation/ui-components/line-input.js +245 -0
  113. package/dist/src/foundation/ui-components/list-block.d.ts +66 -0
  114. package/dist/src/foundation/ui-components/list-block.js +176 -0
  115. package/dist/src/foundation/ui-components/list-view.d.ts +66 -0
  116. package/dist/src/foundation/ui-components/list-view.js +76 -0
  117. package/dist/src/foundation/ui-components/mouse.d.ts +15 -0
  118. package/dist/src/foundation/ui-components/mouse.js +46 -0
  119. package/dist/src/foundation/ui-components/pane.d.ts +33 -0
  120. package/dist/src/foundation/ui-components/pane.js +7 -0
  121. package/dist/src/foundation/ui-components/revision.d.ts +35 -0
  122. package/dist/src/foundation/ui-components/revision.js +79 -0
  123. package/dist/src/foundation/ui-components/scrollbar.d.ts +62 -0
  124. package/dist/src/foundation/ui-components/scrollbar.js +102 -0
  125. package/dist/src/foundation/ui-components/shortcuts.d.ts +57 -0
  126. package/dist/src/foundation/ui-components/shortcuts.js +81 -0
  127. package/dist/src/foundation/ui-components/spans.d.ts +7 -0
  128. package/dist/src/foundation/ui-components/spans.js +59 -0
  129. package/dist/src/foundation/ui-components/status-line.d.ts +16 -0
  130. package/dist/src/foundation/ui-components/status-line.js +14 -0
  131. package/dist/src/foundation/ui-components/stepper.d.ts +24 -0
  132. package/dist/src/foundation/ui-components/stepper.js +33 -0
  133. package/dist/src/foundation/ui-components/surface.d.ts +20 -0
  134. package/dist/src/foundation/ui-components/surface.js +38 -0
  135. package/dist/src/foundation/ui-components/text.d.ts +24 -0
  136. package/dist/src/foundation/ui-components/text.js +123 -0
  137. package/dist/src/foundation/ui-components/theme.d.ts +17 -0
  138. package/dist/src/foundation/ui-components/theme.js +12 -0
  139. package/dist/src/foundation/ui-components/value-menu.d.ts +41 -0
  140. package/dist/src/foundation/ui-components/value-menu.js +33 -0
  141. package/dist/src/product-identity.d.ts +2 -2
  142. package/dist/src/product-identity.js +2 -1
  143. package/dist/src/product-identity.json +3 -0
  144. package/docs/architecture/boundaries.md +31 -13
  145. package/docs/architecture/process-guardian-provenance.md +26 -0
  146. package/docs/architecture/toolchain.md +6 -0
  147. package/docs/architecture/ui-reference-provenance.md +50 -0
  148. package/docs/ci-release-runbook.md +38 -42
  149. package/docs/features/launch-profiles.md +7 -1
  150. package/docs/manual-launch-instance-acceptance.md +66 -0
  151. package/package.json +11 -12
@@ -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) {
@@ -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,8 @@ export declare class PiEngineAdapter {
106
113
  unbindExtensionUi(): Promise<void>;
107
114
  workflowAutocompleteCommands(): readonly PiWorkflowAutocompleteCommand[];
108
115
  cycleModelWorkflow(direction: "forward" | "backward"): Promise<PiWorkflowResult>;
116
+ /** Settings port for the live runtime, or null before the runtime is available. */
117
+ settingsPort(): AgentSettingsPort | null;
109
118
  pinnedModelSelectorContext(): {
110
119
  readonly currentModel: unknown;
111
120
  readonly settingsManager: unknown;
@@ -119,12 +128,12 @@ export declare class PiEngineAdapter {
119
128
  updateScopedModels(enabledModelIds: readonly string[] | null): void;
120
129
  persistScopedModels(enabledModelIds: readonly string[] | null): void;
121
130
  refreshScopedModels(signal: AbortSignal): Promise<PiScopedModelsRefreshResult>;
122
- pinnedLoginOptions(authType?: "oauth" | "api_key"): readonly PiWorkflowOption[];
131
+ pinnedLoginOptions(authType?: "oauth" | "api_key"): readonly PiAuthenticationProviderOption[];
123
132
  pinnedLoginMethodOptions(providerReference: string): {
124
133
  readonly title: string;
125
134
  readonly options: readonly PiWorkflowOption[];
126
135
  };
127
- pinnedLogoutOptions(): Promise<readonly PiWorkflowOption[]>;
136
+ pinnedLogoutOptions(): Promise<readonly PiAuthenticationProviderOption[]>;
128
137
  pinnedForkOptions(): readonly PiWorkflowOption[];
129
138
  pinnedTreeSelectorContext(): PiTreeSelectorContext;
130
139
  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,20 @@ export class PiEngineAdapter {
377
380
  return workflowResult("model", "failed", error instanceof Error ? error.message : String(error));
378
381
  }
379
382
  }
383
+ /** Settings port for the live runtime, or null before the runtime is available. */
384
+ settingsPort() {
385
+ const settings = this.#runtime?.services.settingsManager;
386
+ if (!settings)
387
+ return null;
388
+ const session = this.#runtime?.session;
389
+ return new PiSettingsIntegration(settings, {
390
+ ...(this.#availableThemes === null ? {} : { themes: this.#availableThemes }),
391
+ thinkingLevels: () => {
392
+ const levels = session?.getAvailableThinkingLevels?.();
393
+ return Array.isArray(levels) ? levels.map(level => String(level)) : [];
394
+ },
395
+ });
396
+ }
380
397
  pinnedModelSelectorContext() {
381
398
  const session = this.#requireWorkflowSession();
382
399
  const runtime = this.#runtime;
@@ -397,7 +414,7 @@ export class PiEngineAdapter {
397
414
  ? settingsManager
398
415
  : { setDefaultModelAndProvider() { } };
399
416
  return {
400
- currentModel: session.model,
417
+ currentModel: this.#activeModel === null ? undefined : session.model,
401
418
  settingsManager: selectorSettings,
402
419
  modelRuntime: selectorRuntime,
403
420
  scopedModels: Array.isArray(scoped) ? scoped : [],
@@ -539,8 +556,9 @@ export class PiEngineAdapter {
539
556
  const oauth = dynamicObject(dynamicObject(provider, "auth"), "oauth");
540
557
  const loginLabel = stringProperty(oauth, "loginLabel") ?? "Sign in with an account";
541
558
  const options = this.#loginOptions().filter(option => option.id.endsWith(`:${providerId}`)).map(option => ({
542
- ...option,
559
+ id: option.id,
543
560
  label: option.id.startsWith("api_key:") ? "Sign in with an API key" : loginLabel,
561
+ ...(option.description === undefined ? {} : { description: option.description }),
544
562
  }));
545
563
  return { title: `Select authentication method for ${providerName}:`, options };
546
564
  }
@@ -687,7 +705,7 @@ export class PiEngineAdapter {
687
705
  footer: {
688
706
  branch: this.#gitBranch,
689
707
  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,
708
+ availableProviderCount: new Set(this.#runtime?.services.modelRuntime.getAvailableSnapshot?.().map(model => model.provider).filter(provider => provider !== undefined) ?? []).size,
691
709
  extensionStatuses: [],
692
710
  },
693
711
  },
@@ -987,6 +1005,8 @@ export class PiEngineAdapter {
987
1005
  finally {
988
1006
  this.#workflowInteraction.finishLogin?.();
989
1007
  }
1008
+ this.#reconcileActiveModelAvailability();
1009
+ this.#emitView();
990
1010
  return workflowResult(request.command, "completed", `Logged in to ${providerName}. Credentials saved to ${join(this.#agentDir, "auth.json")}`);
991
1011
  }
992
1012
  case "logout": {
@@ -997,6 +1017,8 @@ export class PiEngineAdapter {
997
1017
  await requireCapability(modelRuntime.logout, "logout").call(modelRuntime, providerId, { signal: AbortSignal.timeout(15_000) });
998
1018
  const provider = modelRuntime.getProvider?.(providerId);
999
1019
  const providerName = stringProperty(provider, "name") ?? providerId;
1020
+ this.#reconcileActiveModelAvailability();
1021
+ this.#emitView();
1000
1022
  return workflowResult(request.command, "completed", credentialType === "api_key"
1001
1023
  ? `Removed stored API key for ${providerName}. Environment variables and models.json config are unchanged.`
1002
1024
  : `Logged out of ${providerName}`);
@@ -1087,7 +1109,10 @@ export class PiEngineAdapter {
1087
1109
  }
1088
1110
  #loginOptions(authType) {
1089
1111
  const runtime = this.#runtime;
1090
- const providers = runtime?.services.modelRuntime.getProviders?.();
1112
+ const modelRuntime = runtime?.services.modelRuntime;
1113
+ if (!modelRuntime)
1114
+ return [];
1115
+ const providers = modelRuntime.getProviders?.();
1091
1116
  if (!Array.isArray(providers))
1092
1117
  return [];
1093
1118
  return providers.filter(isRecord).flatMap(provider => {
@@ -1096,11 +1121,33 @@ export class PiEngineAdapter {
1096
1121
  return [];
1097
1122
  const name = stringProperty(provider, "name") ?? id;
1098
1123
  const auth = isRecord(provider.auth) ? provider.auth : {};
1124
+ const authStatus = modelRuntime.getProviderAuthStatus?.(id);
1125
+ const source = authStatus?.label ?? authStatus?.source;
1126
+ const status = authStatus?.configured === true
1127
+ ? {
1128
+ type: modelRuntime.isUsingOAuth?.(id) === true ? "oauth" : "api_key",
1129
+ ...(source === undefined ? {} : { source }),
1130
+ }
1131
+ : undefined;
1099
1132
  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" }] : []),
1133
+ ...(authType !== "api_key" && auth.oauth ? [{
1134
+ id: `oauth:${id}`,
1135
+ providerId: id,
1136
+ label: name,
1137
+ description: "Account / OAuth",
1138
+ authType: "oauth",
1139
+ ...(status === undefined ? {} : { status }),
1140
+ }] : []),
1141
+ ...(authType !== "oauth" && auth.apiKey ? [{
1142
+ id: `api_key:${id}`,
1143
+ providerId: id,
1144
+ label: name,
1145
+ description: "API key",
1146
+ authType: "api_key",
1147
+ ...(status === undefined ? {} : { status }),
1148
+ }] : []),
1102
1149
  ];
1103
- });
1150
+ }).sort((left, right) => left.label.localeCompare(right.label));
1104
1151
  }
1105
1152
  async #logoutOptions() {
1106
1153
  const runtime = this.#runtime;
@@ -1118,8 +1165,11 @@ export class PiEngineAdapter {
1118
1165
  const provider = modelRuntime.getProvider?.(providerId);
1119
1166
  return [{
1120
1167
  id: `${credentialType}:${providerId}`,
1168
+ providerId,
1121
1169
  label: stringProperty(provider, "name") ?? providerId,
1122
1170
  description: credentialType,
1171
+ authType: credentialType,
1172
+ status: { type: credentialType, source: "stored credential" },
1123
1173
  }];
1124
1174
  });
1125
1175
  }
@@ -1272,12 +1322,28 @@ export class PiEngineAdapter {
1272
1322
  };
1273
1323
  this.#status = { ...this.#status, workingMessage: null, badges: [] };
1274
1324
  this.#activeModel = readModel(session.model);
1325
+ this.#reconcileActiveModelAvailability();
1275
1326
  this.#thinkingLevel = readThinkingLevel(session.thinkingLevel);
1276
1327
  this.#rebuildTranscript(session.messages, "finalized");
1277
1328
  this.#unsubscribe = session.subscribe(event => this.#handlePiEvent(event));
1278
1329
  if (this.#extensionUi !== undefined)
1279
1330
  void this.#bindExtensionUiToSession();
1280
1331
  }
1332
+ #reconcileActiveModelAvailability() {
1333
+ const modelRuntime = this.#runtime?.services.modelRuntime;
1334
+ if (!modelRuntime) {
1335
+ this.#activeModel = null;
1336
+ return;
1337
+ }
1338
+ const available = modelRuntime.getAvailableSnapshot?.() ?? [];
1339
+ const sessionModel = readModel(this.#session?.model);
1340
+ const authoritative = this.#activeModel ?? sessionModel;
1341
+ if (authoritative === null)
1342
+ return;
1343
+ const remainsAvailable = available.some(model => stringProperty(model, "provider") === authoritative.providerId
1344
+ && stringProperty(model, "id") === authoritative.modelId);
1345
+ this.#activeModel = remainsAvailable ? authoritative : null;
1346
+ }
1281
1347
  async #bindExtensionUiToSession() {
1282
1348
  const session = this.#session;
1283
1349
  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
+ }