diff --git a/src/main/services/infrastructure/CliInstallerService.ts b/src/main/services/infrastructure/CliInstallerService.ts index 1bbf7d43..c377081a 100644 --- a/src/main/services/infrastructure/CliInstallerService.ts +++ b/src/main/services/infrastructure/CliInstallerService.ts @@ -1024,6 +1024,7 @@ export class CliInstallerService { * on timeout, getStatus() returns whatever fields were populated so far. * * Flow: binary resolve → --version (sequential) → Promise.all([auth, GCS]) (parallel) + * Lightweight multimodel startup status stops after binary resolution; full status hydrates health. */ private async gatherStatus( ref: { current: CliInstallationStatus }, @@ -1050,6 +1051,18 @@ export class CliInstallerService { diag.binaryResolveMs = Date.now() - binaryResolveStartedAt; if (binaryPath) { r.binaryPath = binaryPath; + if (r.flavor === 'agent_teams_orchestrator' && providerStatusMode === 'defer') { + const recoveredHealthyStatus = this.getRecoverableHealthyStatus(binaryPath); + diag.versionProbeMs = 0; + r.installed = true; + r.installedVersion = recoveredHealthyStatus?.installedVersion ?? null; + r.launchError = null; + r.authStatusChecking = false; + this.markProvidersDeferred(r); + this.publishStatusSnapshotIfCurrent(r, generation); + return; + } + const versionProbeStartedAt = Date.now(); const versionProbe = await this.probeCliVersion(binaryPath); diag.versionProbeMs = Date.now() - versionProbeStartedAt; @@ -1059,13 +1072,6 @@ export class CliInstallerService { r.launchError = null; r.authStatusChecking = true; - if (r.flavor === 'agent_teams_orchestrator' && providerStatusMode === 'defer') { - r.authStatusChecking = false; - this.markProvidersDeferred(r); - this.publishStatusSnapshotIfCurrent(r, generation); - return; - } - this.rememberHealthyStatus(r); this.publishStatusSnapshotIfCurrent(r, generation); diff --git a/src/shared/types/cliInstaller.ts b/src/shared/types/cliInstaller.ts index 391f51e7..5bc39708 100644 --- a/src/shared/types/cliInstaller.ts +++ b/src/shared/types/cliInstaller.ts @@ -302,7 +302,7 @@ export interface CliInstallationStatus { showVersionDetails: boolean; /** Whether binary path should be shown in the UI */ showBinaryPath: boolean; - /** Whether the CLI was found and passed the startup health check (`--version`) */ + /** Whether the CLI is available. Lightweight startup status may defer the health check. */ installed: boolean; /** Installed version string (e.g. "2.1.59"), null if unavailable or not installed */ installedVersion: string | null; diff --git a/test/main/services/infrastructure/CliInstallerService.test.ts b/test/main/services/infrastructure/CliInstallerService.test.ts index 4ba73b75..b1ac08be 100644 --- a/test/main/services/infrastructure/CliInstallerService.test.ts +++ b/test/main/services/infrastructure/CliInstallerService.test.ts @@ -356,7 +356,6 @@ describe('CliInstallerService', () => { showBinaryPath: false, }); vi.mocked(ClaudeBinaryResolver.resolve).mockResolvedValue('/mock/agent_teams_orchestrator'); - vi.mocked(execCli).mockResolvedValueOnce({ stdout: '0.0.46', stderr: '' }); const getProviderStatusesSpy = vi .spyOn(ClaudeMultimodelBridgeService.prototype, 'getProviderStatuses') .mockResolvedValue([ @@ -377,6 +376,8 @@ describe('CliInstallerService', () => { .filter((event) => event.type === 'status'); expect(status.installed).toBe(true); + expect(status.binaryPath).toBe('/mock/agent_teams_orchestrator'); + expect(status.installedVersion).toBeNull(); expect(resolveInteractiveShellEnvBestEffortMock).not.toHaveBeenCalled(); expect(status.authStatusChecking).toBe(false); expect(status.authLoggedIn).toBe(false); @@ -403,11 +404,14 @@ describe('CliInstallerService', () => { ) ).toBe(true); expect(getProviderStatusesSpy).not.toHaveBeenCalled(); - expect(execCli).toHaveBeenCalledTimes(1); - expect(execCli).toHaveBeenCalledWith( - '/mock/agent_teams_orchestrator', - ['--version'], - expect.objectContaining({ timeout: expect.any(Number) }) + expect(execCli).not.toHaveBeenCalled(); + await vi.waitFor(() => + expect(appendCliAuthDiag).toHaveBeenCalledWith( + expect.objectContaining({ + shellEnvMs: 0, + versionProbeMs: 0, + }) + ) ); });