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 {
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) {
return;
}

View file

@ -4987,22 +4987,8 @@ export class TeamProvisioningService {
);
}
const codexSelection = resolveCodexSelectionFromFacts({
selectedModel: params.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.'
}`
);
}
// Codex Fast is optional acceleration. If it is no longer eligible, the launch identity
// resolves it to normal Codex mode instead of blocking an otherwise launch-ready model.
if (!explicitModel || params.facts.modelIds.has(explicitModel)) {
return;

View file

@ -529,26 +529,32 @@ describe('ScheduledTaskExecutor', () => {
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();
await expect(
executor.execute(
makeRequest({
config: {
cwd: '/tmp/project',
prompt: 'do it',
providerId: 'codex',
providerBackendId: 'codex-native',
model: 'gpt-5.4-mini',
fastMode: 'on',
resolvedFastMode: false,
},
})
)
).rejects.toThrow('Codex Fast mode was requested');
void executor.execute(
makeRequest({
config: {
cwd: '/tmp/project',
prompt: 'do it',
providerId: 'codex',
providerBackendId: 'codex-native',
model: 'gpt-5.4-mini',
fastMode: 'on',
resolvedFastMode: false,
},
})
);
await flushAsync();
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 () => {

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 facts = {
defaultModel: 'gpt-5.4-mini',
@ -4624,7 +4624,27 @@ describe('TeamProvisioningService prepare/auth behavior', () => {
fastMode: 'on',
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', () => {