fix(runtime): preserve codex-native lane state truth
This commit is contained in:
parent
30fce3c64d
commit
92a3124e3f
3 changed files with 215 additions and 0 deletions
|
|
@ -76,6 +76,24 @@ function isCodexNativeLane(provider: CliProviderStatus): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function getSelectedRuntimeBackendOption(
|
||||
provider: CliProviderStatus
|
||||
): NonNullable<CliProviderStatus['availableBackends']>[number] | null {
|
||||
const options = provider.availableBackends ?? [];
|
||||
if (options.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const selectedBackendId = provider.selectedBackendId ?? null;
|
||||
const resolvedBackendId = provider.resolvedBackendId ?? null;
|
||||
|
||||
return (
|
||||
options.find((option) => option.id === selectedBackendId) ??
|
||||
options.find((option) => option.id === resolvedBackendId) ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
export function isConnectionManagedRuntimeProvider(provider: CliProviderStatus): boolean {
|
||||
return provider.providerId === 'codex' && (provider.availableBackends?.length ?? 0) === 0;
|
||||
}
|
||||
|
|
@ -106,6 +124,27 @@ export function getProviderCurrentRuntimeSummary(provider: CliProviderStatus): s
|
|||
}
|
||||
|
||||
export function formatProviderStatusText(provider: CliProviderStatus): string {
|
||||
const selectedBackendOption = getSelectedRuntimeBackendOption(provider);
|
||||
|
||||
if (
|
||||
isCodexNativeLane(provider) &&
|
||||
selectedBackendOption &&
|
||||
selectedBackendOption.state &&
|
||||
selectedBackendOption.state !== 'ready'
|
||||
) {
|
||||
return (
|
||||
selectedBackendOption.statusMessage ?? provider.statusMessage ?? 'Codex native unavailable'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isCodexNativeLane(provider) &&
|
||||
selectedBackendOption?.audience === 'internal' &&
|
||||
selectedBackendOption.statusMessage
|
||||
) {
|
||||
return selectedBackendOption.statusMessage;
|
||||
}
|
||||
|
||||
if (!provider.supported) {
|
||||
return provider.statusMessage ?? 'Unavailable in current runtime';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -506,4 +506,65 @@ describe('ClaudeMultimodelBridgeService', () => {
|
|||
statusMessage: 'Ready for internal use',
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves codex-native runtime-missing rollout states from runtime status payloads', async () => {
|
||||
execCliMock.mockResolvedValue({
|
||||
stdout: JSON.stringify({
|
||||
providers: {
|
||||
codex: {
|
||||
supported: true,
|
||||
authenticated: false,
|
||||
authMethod: null,
|
||||
verificationState: 'unknown',
|
||||
canLoginFromUi: false,
|
||||
statusMessage: 'Codex native runtime not ready',
|
||||
detailMessage: 'Codex native runtime requires the codex CLI binary to be installed.',
|
||||
selectedBackendId: 'codex-native',
|
||||
resolvedBackendId: null,
|
||||
availableBackends: [
|
||||
{
|
||||
id: 'codex-native',
|
||||
label: 'Codex native',
|
||||
selectable: false,
|
||||
recommended: false,
|
||||
available: false,
|
||||
state: 'runtime-missing',
|
||||
audience: 'internal',
|
||||
statusMessage: 'Codex CLI not found',
|
||||
detailMessage: 'Install the codex CLI before enabling the lane.',
|
||||
},
|
||||
],
|
||||
capabilities: {
|
||||
teamLaunch: true,
|
||||
oneShot: true,
|
||||
extensions: {
|
||||
plugins: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
|
||||
mcp: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
|
||||
skills: { status: 'unsupported', ownership: 'shared', reason: 'Phase 1' },
|
||||
apiKeys: { status: 'supported', ownership: 'shared', reason: null },
|
||||
},
|
||||
},
|
||||
backend: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
stderr: '',
|
||||
exitCode: 0,
|
||||
});
|
||||
|
||||
const { ClaudeMultimodelBridgeService } =
|
||||
await import('@main/services/runtime/ClaudeMultimodelBridgeService');
|
||||
const service = new ClaudeMultimodelBridgeService();
|
||||
|
||||
const codex = await service.getProviderStatus('/mock/agent_teams_orchestrator', 'codex');
|
||||
|
||||
expect(codex.availableBackends?.find((backend) => backend.id === 'codex-native')).toMatchObject({
|
||||
id: 'codex-native',
|
||||
selectable: false,
|
||||
available: false,
|
||||
state: 'runtime-missing',
|
||||
audience: 'internal',
|
||||
statusMessage: 'Codex CLI not found',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
formatProviderStatusText,
|
||||
getProviderConnectionModeSummary,
|
||||
getProviderCredentialSummary,
|
||||
getProviderCurrentRuntimeSummary,
|
||||
|
|
@ -252,4 +253,118 @@ describe('providerConnectionUi', () => {
|
|||
|
||||
expect(getProviderCredentialSummary(provider)).toBe('Saved API key available in Manage');
|
||||
});
|
||||
|
||||
it('keeps locked codex-native lanes visible instead of flattening them to connected-via-api-key', () => {
|
||||
const provider = createCodexProvider({
|
||||
authenticated: true,
|
||||
authMethod: 'api_key',
|
||||
statusMessage: 'Codex native runtime ready',
|
||||
selectedBackendId: 'codex-native',
|
||||
resolvedBackendId: 'codex-native',
|
||||
availableBackends: [
|
||||
{
|
||||
id: 'codex-native',
|
||||
label: 'Codex native',
|
||||
description: 'Use codex exec JSON mode.',
|
||||
selectable: false,
|
||||
recommended: false,
|
||||
available: true,
|
||||
state: 'locked',
|
||||
audience: 'internal',
|
||||
statusMessage: 'Ready but locked',
|
||||
detailMessage: 'Internal rollout only.',
|
||||
},
|
||||
],
|
||||
backend: {
|
||||
kind: 'codex-native',
|
||||
label: 'Codex native',
|
||||
},
|
||||
});
|
||||
|
||||
expect(formatProviderStatusText(provider)).toBe('Ready but locked');
|
||||
});
|
||||
|
||||
it('keeps internal codex-native ready state explicit instead of showing a generic auth label', () => {
|
||||
const provider = createCodexProvider({
|
||||
authenticated: true,
|
||||
authMethod: 'api_key',
|
||||
statusMessage: 'Codex native runtime ready',
|
||||
selectedBackendId: 'codex-native',
|
||||
resolvedBackendId: 'codex-native',
|
||||
availableBackends: [
|
||||
{
|
||||
id: 'codex-native',
|
||||
label: 'Codex native',
|
||||
description: 'Use codex exec JSON mode.',
|
||||
selectable: true,
|
||||
recommended: false,
|
||||
available: true,
|
||||
state: 'ready',
|
||||
audience: 'internal',
|
||||
statusMessage: 'Ready for internal use',
|
||||
detailMessage: 'Internal rollout only.',
|
||||
},
|
||||
],
|
||||
backend: {
|
||||
kind: 'codex-native',
|
||||
label: 'Codex native',
|
||||
},
|
||||
});
|
||||
|
||||
expect(formatProviderStatusText(provider)).toBe('Ready for internal use');
|
||||
});
|
||||
|
||||
it('surfaces native auth-required state from the selected backend option', () => {
|
||||
const provider = createCodexProvider({
|
||||
authenticated: false,
|
||||
authMethod: null,
|
||||
statusMessage: 'Codex native runtime not ready',
|
||||
selectedBackendId: 'codex-native',
|
||||
resolvedBackendId: null,
|
||||
availableBackends: [
|
||||
{
|
||||
id: 'codex-native',
|
||||
label: 'Codex native',
|
||||
description: 'Use codex exec JSON mode.',
|
||||
selectable: false,
|
||||
recommended: false,
|
||||
available: false,
|
||||
state: 'authentication-required',
|
||||
audience: 'internal',
|
||||
statusMessage: 'Authentication required',
|
||||
detailMessage: 'Set CODEX_API_KEY.',
|
||||
},
|
||||
],
|
||||
backend: null,
|
||||
});
|
||||
|
||||
expect(formatProviderStatusText(provider)).toBe('Authentication required');
|
||||
});
|
||||
|
||||
it('surfaces native runtime-missing state from the selected backend option', () => {
|
||||
const provider = createCodexProvider({
|
||||
authenticated: false,
|
||||
authMethod: null,
|
||||
statusMessage: 'Codex native runtime not ready',
|
||||
selectedBackendId: 'codex-native',
|
||||
resolvedBackendId: null,
|
||||
availableBackends: [
|
||||
{
|
||||
id: 'codex-native',
|
||||
label: 'Codex native',
|
||||
description: 'Use codex exec JSON mode.',
|
||||
selectable: false,
|
||||
recommended: false,
|
||||
available: false,
|
||||
state: 'runtime-missing',
|
||||
audience: 'internal',
|
||||
statusMessage: 'Codex CLI not found',
|
||||
detailMessage: 'Install the codex CLI before enabling the lane.',
|
||||
},
|
||||
],
|
||||
backend: null,
|
||||
});
|
||||
|
||||
expect(formatProviderStatusText(provider)).toBe('Codex CLI not found');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue