fix: bind Codex reset snapshots to accounts
This commit is contained in:
@@ -962,10 +962,8 @@ export class StatusLineComponent implements Component {
|
||||
return this.#vibeWorkerTokenRate?.() ?? null;
|
||||
}
|
||||
|
||||
#getUsageContextKey(session: AgentSession): string {
|
||||
const activeProvider = session.state.model?.provider ?? session.model?.provider ?? "";
|
||||
#formatUsageContextKey(activeProvider: string | undefined, identity: OAuthAccountIdentity | undefined): string {
|
||||
if (!activeProvider) return "";
|
||||
const identity = session.modelRegistry?.authStorage?.getOAuthAccountIdentity(activeProvider, session.sessionId);
|
||||
// orgId is part of the key: rotating between two same-email Anthropic
|
||||
// subscriptions must invalidate the cached usage immediately instead of
|
||||
// showing the previous org's quota for the rest of the cache TTL.
|
||||
@@ -978,6 +976,14 @@ export class StatusLineComponent implements Component {
|
||||
].join("\0");
|
||||
}
|
||||
|
||||
#getUsageContextKey(session: AgentSession): string {
|
||||
const activeProvider = session.state.model?.provider ?? session.model?.provider;
|
||||
const identity = activeProvider
|
||||
? session.modelRegistry?.authStorage?.getOAuthAccountIdentity(activeProvider, session.sessionId)
|
||||
: undefined;
|
||||
return this.#formatUsageContextKey(activeProvider, identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Startup redraws only arm a short-delayed task; timeout releases the render
|
||||
* cadence while a late successful fetch can still refresh the cached segment.
|
||||
@@ -1042,7 +1048,7 @@ export class StatusLineComponent implements Component {
|
||||
this.#cachedUsage = normalized;
|
||||
this.#usageFetchedAt = Date.now();
|
||||
if (activeProvider !== "openai-codex" || !normalized) return;
|
||||
const contextKey = this.#getUsageContextKey(session);
|
||||
const contextKey = this.#formatUsageContextKey(activeProvider, activeIdentity);
|
||||
const previous = this.#codexResetSnapshots.get(contextKey);
|
||||
this.#codexResetSnapshots.set(contextKey, normalized);
|
||||
if (!previous || !settings.get("tui.codexResetFireworks")) return;
|
||||
|
||||
@@ -65,8 +65,7 @@ interface CodexUsageState {
|
||||
omitFetchedAt?: boolean;
|
||||
}
|
||||
|
||||
function codexUsageReport(state: CodexUsageState): unknown[] {
|
||||
const accountId = "account-1";
|
||||
function codexUsageReport(state: CodexUsageState, accountId = "account-1"): unknown[] {
|
||||
return [
|
||||
{
|
||||
provider: "openai-codex",
|
||||
@@ -90,7 +89,13 @@ function codexUsageReport(state: CodexUsageState): unknown[] {
|
||||
];
|
||||
}
|
||||
|
||||
function makeCodexSession(fetchUsageReports: (signal?: AbortSignal) => Promise<unknown>): AgentSession {
|
||||
function makeCodexSession(
|
||||
fetchUsageReports: (signal?: AbortSignal) => Promise<unknown>,
|
||||
resolveActiveIdentity: () => { accountId: string; email?: string } = () => ({
|
||||
accountId: "account-1",
|
||||
email: "codex@example.com",
|
||||
}),
|
||||
): AgentSession {
|
||||
const session = makeSession(fetchUsageReports) as unknown as Record<string, unknown>;
|
||||
session.sessionId = "session-1";
|
||||
session.state = {
|
||||
@@ -100,10 +105,7 @@ function makeCodexSession(fetchUsageReports: (signal?: AbortSignal) => Promise<u
|
||||
session.model = { contextWindow: 200_000, provider: "openai-codex" };
|
||||
session.modelRegistry = {
|
||||
authStorage: {
|
||||
getOAuthAccountIdentity: () => ({
|
||||
accountId: "account-1",
|
||||
email: "codex@example.com",
|
||||
}),
|
||||
getOAuthAccountIdentity: resolveActiveIdentity,
|
||||
},
|
||||
};
|
||||
return session as unknown as AgentSession;
|
||||
@@ -323,6 +325,47 @@ describe("StatusLineComponent usage refresh", () => {
|
||||
component.dispose();
|
||||
});
|
||||
|
||||
it("binds each reset snapshot to the account identity used to normalize it", async () => {
|
||||
Settings.instance.set("tui.codexResetFireworks", true);
|
||||
const sevenDayResetAt = Date.now() + 80 * 3_600_000;
|
||||
const reports = [
|
||||
...codexUsageReport(
|
||||
{
|
||||
sevenDayPercent: 18,
|
||||
sevenDayResetAt,
|
||||
savedResets: 0,
|
||||
},
|
||||
"account-a",
|
||||
),
|
||||
...codexUsageReport(
|
||||
{
|
||||
sevenDayPercent: 22,
|
||||
sevenDayResetAt,
|
||||
savedResets: 1,
|
||||
},
|
||||
"account-b",
|
||||
),
|
||||
];
|
||||
const identityLookups: string[] = [];
|
||||
const component = new StatusLineComponent(
|
||||
makeCodexSession(
|
||||
async () => reports,
|
||||
() => ({ accountId: identityLookups.shift() ?? "account-a" }),
|
||||
),
|
||||
);
|
||||
const events: CodexResetFireworksEvent[] = [];
|
||||
component.setCodexResetFireworksHandler(event => events.push(event));
|
||||
|
||||
await refreshUsage(component);
|
||||
// The refresh starts under A, but B is active when its report is normalized.
|
||||
// A later identity lookup must not attribute B's saved reset to A.
|
||||
identityLookups.push("account-a", "account-b", "account-a");
|
||||
await refreshUsage(component, 5 * 60_000);
|
||||
|
||||
expect(events).toEqual([]);
|
||||
component.dispose();
|
||||
});
|
||||
|
||||
it("keeps an unavailable saved-reset count unknown across refreshes", async () => {
|
||||
Settings.instance.set("tui.codexResetFireworks", true);
|
||||
const sevenDayResetAt = Date.now() + 80 * 3_600_000;
|
||||
|
||||
Reference in New Issue
Block a user