fix(team): downgrade unavailable codex fast launches

This commit is contained in:
777genius 2026-06-06 20:17:17 +03:00
parent 0a3876e141
commit 08b1de7fa2
4 changed files with 47 additions and 44 deletions

View file

@ -55,15 +55,6 @@ function buildProviderFastModeArgs(config: ScheduleLaunchConfig): string[] {
} }
function validateFastModeLaunchConfig(config: ScheduleLaunchConfig): void { function validateFastModeLaunchConfig(config: ScheduleLaunchConfig): void {
if (
config.providerId === 'codex' &&
config.fastMode === 'on' &&
config.resolvedFastMode !== true
) {
throw new Error(
'Codex Fast mode was requested for this schedule, but the saved launch profile is not Fast-eligible. Reopen the schedule and save it again with a supported ChatGPT account configuration.'
);
}
if (config.providerId !== 'codex' || config.resolvedFastMode !== true) { if (config.providerId !== 'codex' || config.resolvedFastMode !== true) {
return; return;
} }

View file

@ -4987,22 +4987,8 @@ export class TeamProvisioningService {
); );
} }
const codexSelection = resolveCodexSelectionFromFacts({ // Codex Fast is optional acceleration. If it is no longer eligible, the launch identity
selectedModel: params.model, // resolves it to normal Codex mode instead of blocking an otherwise launch-ready model.
facts: params.facts,
});
const codexFastResolution = resolveCodexFastMode({
selection: codexSelection,
selectedFastMode: params.fastMode,
});
if ((params.fastMode ?? 'inherit') === 'on' && !codexFastResolution.selectable) {
throw new Error(
`${params.actorLabel} enables Codex Fast mode, but ${
codexFastResolution.disabledReason ??
'it is unavailable for the selected runtime, model, or auth mode.'
}`
);
}
if (!explicitModel || params.facts.modelIds.has(explicitModel)) { if (!explicitModel || params.facts.modelIds.has(explicitModel)) {
return; return;

View file

@ -529,26 +529,32 @@ describe('ScheduledTaskExecutor', () => {
proc.emit('close', 0); proc.emit('close', 0);
}); });
it('rejects explicit Codex schedule Fast before spawn when saved eligibility is false', async () => { it('runs a standard Codex schedule when saved Fast eligibility is false', async () => {
const proc = createMockProcess();
mockSpawnCli.mockReturnValue(proc);
const executor = new ScheduledTaskExecutor(); const executor = new ScheduledTaskExecutor();
await expect( void executor.execute(
executor.execute( makeRequest({
makeRequest({ config: {
config: { cwd: '/tmp/project',
cwd: '/tmp/project', prompt: 'do it',
prompt: 'do it', providerId: 'codex',
providerId: 'codex', providerBackendId: 'codex-native',
providerBackendId: 'codex-native', model: 'gpt-5.4-mini',
model: 'gpt-5.4-mini', fastMode: 'on',
fastMode: 'on', resolvedFastMode: false,
resolvedFastMode: false, },
}, })
}) );
) await flushAsync();
).rejects.toThrow('Codex Fast mode was requested');
expect(mockSpawnCli).not.toHaveBeenCalled(); const args = mockSpawnCli.mock.calls[0][1] as string[];
expect(args).not.toContain('service_tier="fast"');
expect(args).not.toContain('features.fast_mode=true');
proc.emit('close', 0);
}); });
it('does not hard-code Codex Fast schedules to GPT-5.4 when saved eligibility is true', async () => { it('does not hard-code Codex Fast schedules to GPT-5.4 when saved eligibility is true', async () => {

View file

@ -4556,7 +4556,7 @@ describe('TeamProvisioningService prepare/auth behavior', () => {
}); });
}); });
it('rejects explicit Codex Fast before launch when auth or model eligibility is invalid', () => { it('allows explicit Codex Fast to downgrade before launch when auth or model eligibility is invalid', () => {
const svc = new TeamProvisioningService(); const svc = new TeamProvisioningService();
const facts = { const facts = {
defaultModel: 'gpt-5.4-mini', defaultModel: 'gpt-5.4-mini',
@ -4624,7 +4624,27 @@ describe('TeamProvisioningService prepare/auth behavior', () => {
fastMode: 'on', fastMode: 'on',
facts, facts,
}) })
).toThrow('enables Codex Fast mode'); ).not.toThrow();
expect(
(svc as any).buildProviderModelLaunchIdentity({
request: {
providerId: 'codex',
providerBackendId: 'codex-native',
model: 'gpt-5.4-mini',
fastMode: 'on',
},
facts,
})
).toMatchObject({
providerId: 'codex',
providerBackendId: 'codex-native',
selectedModel: 'gpt-5.4-mini',
resolvedLaunchModel: 'gpt-5.4-mini',
selectedFastMode: 'on',
resolvedFastMode: false,
fastResolutionReason: expect.stringContaining('API key mode uses standard API pricing'),
});
}); });
it('rejects Anthropic max and fast when the exact resolved launch model does not support them', () => { it('rejects Anthropic max and fast when the exact resolved launch model does not support them', () => {