feat(timeline): enhance lane selection logic for Codex provider and add related tests

This commit is contained in:
Supra4E8C
2026-08-07 01:09:37 +08:00
parent 0eeb747cdc
commit 1ad9cac75c
2 changed files with 53 additions and 1 deletions
+20 -1
View File
@@ -268,6 +268,7 @@ export function laneHasWindow(lane: TimelineLane): boolean {
/** Shape the lane builder reads. Deliberately structural — see the note below. */
interface WindowLike {
id?: string;
label?: string;
usedPercent?: number | null;
resetAtMs?: number | null;
@@ -352,7 +353,25 @@ export function buildTimelineLane(input: TimelineLaneInput): TimelineLane {
const windows = ((quota as { windows?: WindowLike[] }).windows ?? []).filter(
(window) => typeof window.resetAtMs === 'number'
);
const chosen = pickLaneWindow(windows, maxPeriodHours);
const preferredCodexId =
maxPeriodHours !== undefined && maxPeriodHours <= SESSION_PERIOD_HOURS
? 'five-hour'
: 'weekly';
// Codex can report model-scoped windows with the same period as the account
// window (for example GPT-5.3-Codex-Spark weekly). A reset-time tie-break
// would make the lane silently switch to that model's quota. Keep the lane
// anchored to the standard account window whenever it fits this view.
const preferredCodexWindow =
provider === 'codex'
? windows.find(
(window) =>
window.id === preferredCodexId &&
typeof window.periodHours === 'number' &&
window.periodHours > 0 &&
(maxPeriodHours === undefined || window.periodHours <= maxPeriodHours)
)
: undefined;
const chosen = preferredCodexWindow ?? pickLaneWindow(windows, maxPeriodHours);
if (!chosen) return empty;
const resetCredits =
+33
View File
@@ -281,6 +281,39 @@ describe('buildTimelineLane', () => {
]);
});
test('codex: keeps the weekly lane on the account quota instead of Spark quota', () => {
const accountReset = at(2026, 7, 1, 20);
const sparkReset = at(2026, 6, 29, 20);
const lane = buildTimelineLane({
...base,
provider: 'codex',
quota: {
status: 'success',
windows: [
{
id: 'weekly',
label: 'Weekly limit',
usedPercent: 70,
resetAtMs: accountReset,
periodHours: 168,
},
{
id: 'gpt-5-3-codex-spark-weekly-0',
label: 'GPT-5.3-Codex-Spark weekly limit',
usedPercent: 2,
resetAtMs: sparkReset,
periodHours: 168,
},
],
},
maxPeriodHours: 14 * 24,
});
expect(lane.anchorMs).toBe(accountReset);
expect(lane.periodHours).toBe(168);
expect(lane.remaining).toBe(30);
});
test('codex: includes available reset credits with parseable expiry dates', () => {
const expiresAt = '2026-08-02T12:00:00Z';
const lane = buildTimelineLane({