diff --git a/src/server/infra/transport/handlers/provider-handler.ts b/src/server/infra/transport/handlers/provider-handler.ts index f1a2a59e5..92996e93d 100644 --- a/src/server/infra/transport/handlers/provider-handler.ts +++ b/src/server/infra/transport/handlers/provider-handler.ts @@ -311,7 +311,14 @@ export class ProviderHandler { // "needs setup" and unmounts any in-flight setup flow on the home // page. The model:setActive handler activates the provider when the // user picks a model, which is the right moment. - const willHaveActiveModel = Boolean(provider?.defaultModel) + // + // byterover bypasses this gate: it has no model fetcher and no + // `brv model switch` recovery path, so deferring would strand it as + // connected-but-never-active. Its model is resolved at runtime via + // DEFAULT_LLM_MODEL in agent-process.ts rather than persisted here, + // so future default changes roll out without a per-user migration. + const willHaveActiveModel = providerId === 'byterover' + || Boolean(provider?.defaultModel) || Boolean(await this.providerConfigStore.getActiveModel(providerId)) await this.providerConfigStore.connectProvider(providerId, { activeModel: provider?.defaultModel, diff --git a/src/tui/features/auth/api/get-auth-state.ts b/src/tui/features/auth/api/get-auth-state.ts index 5a021fc7c..a127cc0a1 100644 --- a/src/tui/features/auth/api/get-auth-state.ts +++ b/src/tui/features/auth/api/get-auth-state.ts @@ -9,9 +9,12 @@ export const getAuthState = (): Promise => { const {apiClient} = useTransportStore.getState() if (!apiClient) return Promise.reject(new Error('Not connected')) - // Use 500ms timeout to fail fast if handler not ready yet - // React Query will retry automatically with exponential backoff - return apiClient.request(AuthEvents.GET_STATE, undefined, {timeout: 500}) + // The daemon-side handler does a network round-trip to /user/me. Measured + // p99 across multiple networks ranges 1.2-3.1s with occasional outliers, + // so 4000ms gives ~1.3x headroom over the worst observed sample with + // margin left for slower connections (mobile, international, VPN). + // React Query retries once on failure for transient blips. + return apiClient.request(AuthEvents.GET_STATE, undefined, {timeout: 4000}) } export const getAuthStateQueryOptions = () => diff --git a/src/tui/features/auth/components/auth-initializer.tsx b/src/tui/features/auth/components/auth-initializer.tsx index 5c397bf58..ca4b81f83 100644 --- a/src/tui/features/auth/components/auth-initializer.tsx +++ b/src/tui/features/auth/components/auth-initializer.tsx @@ -30,8 +30,10 @@ export function AuthInitializer({children}: {children: React.ReactNode}): React. } = useGetAuthState({ queryConfig: { enabled: apiClient !== null, - retry: 5, - retryDelay: (attemptIndex) => Math.min(500 * 2 ** attemptIndex, 2000), + // One retry covers transient blips; the per-attempt timeout is now generous + // (3s) so we don't need 5+ retries that would block startup for ~17s when offline. + retry: 1, + retryDelay: 500, staleTime: 2 * 60 * 1000, }, }) diff --git a/test/unit/core/domain/entities/provider-registry.test.ts b/test/unit/core/domain/entities/provider-registry.test.ts index e3da74413..564442fca 100644 --- a/test/unit/core/domain/entities/provider-registry.test.ts +++ b/test/unit/core/domain/entities/provider-registry.test.ts @@ -107,6 +107,12 @@ describe('Provider Registry', () => { expect(provider?.oauth).to.be.undefined }) + it('should not have a defaultModel for byterover (model is resolved at runtime via DEFAULT_LLM_MODEL)', () => { + // intentional: model is runtime-resolved so default changes auto-roll without per-user migration. + const provider = getProviderById('byterover') + expect(provider?.defaultModel).to.be.undefined + }) + it('should not have oauth config for anthropic yet', () => { const provider = getProviderById('anthropic') expect(provider?.oauth).to.be.undefined diff --git a/test/unit/infra/transport/handlers/provider-handler.test.ts b/test/unit/infra/transport/handlers/provider-handler.test.ts index cac62be51..b9765ee21 100644 --- a/test/unit/infra/transport/handlers/provider-handler.test.ts +++ b/test/unit/infra/transport/handlers/provider-handler.test.ts @@ -367,6 +367,18 @@ describe('ProviderHandler', () => { const connectArgs = providerConfigStore.connectProvider.firstCall.args[1] as Record expect(connectArgs.setAsActive).to.equal(true) }) + + it('should activate byterover on connect without persisting an activeModel', async () => { + // byterover bypasses the gate (no model fetcher, no model-switch recovery path); runtime resolves via DEFAULT_LLM_MODEL. + createHandler() + + const handler = transport._handlers.get(ProviderEvents.CONNECT) + await handler!({providerId: 'byterover'}, 'client-1') + + const connectArgs = providerConfigStore.connectProvider.firstCall.args[1] as Record + expect(connectArgs.setAsActive).to.equal(true) + expect(connectArgs.activeModel).to.be.undefined + }) }) describe('provider:disconnect', () => {