fix(usage): org-qualified session marker for same-email accounts
The /usage show "in use by this session:" marker took only the bare email from OAuthAccountIdentity, so two same-email Anthropic credentials in different orgs were indistinguishable. Route the label through a shared formatActiveAccountLabel that suffixes the active org, matching the account list and login-success surfaces. Fixes #5691
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the `/usage show` `in use by this session:` marker showing only the login email, so two same-email Anthropic credentials in different orgs (a Team seat and a personal Max plan) were indistinguishable. The marker now suffixes the active organization (`email (OrgName)`) via a shared `formatActiveAccountLabel`, matching the account list and login-success surfaces ([#5691](https://github.com/can1357/oh-my-pi/issues/5691)).
|
||||
|
||||
## [17.0.1] - 2026-07-16
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -43,7 +43,7 @@ import type { AuthStorage, OAuthAccountIdentity } from "../../session/auth-stora
|
||||
import type { CompactMode } from "../../session/compact-modes";
|
||||
import type { NewSessionOptions } from "../../session/session-entries";
|
||||
import { formatShakeSummary, type ShakeMode, type ShakeResult } from "../../session/shake-types";
|
||||
import { limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
||||
import { formatActiveAccountLabel, limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
||||
import { outputMeta } from "../../tools/output-meta";
|
||||
import { resolveToCwd, stripOuterDoubleQuotes } from "../../tools/path-utils";
|
||||
import { replaceTabs, truncateToWidth } from "../../tools/render-utils";
|
||||
@@ -1642,7 +1642,7 @@ export function renderUsageReports(
|
||||
}
|
||||
|
||||
lines.push(uiTheme.bold(uiTheme.fg("accent", providerName)));
|
||||
const activeAccountLabel = activeAccount?.email ?? activeAccount?.accountId ?? activeAccount?.projectId;
|
||||
const activeAccountLabel = formatActiveAccountLabel(activeAccount);
|
||||
if (activeAccountLabel) {
|
||||
lines.push(` ${uiTheme.fg("accent", "in use by this session:")} ${activeAccountLabel}`);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,22 @@ function normalizeIdentityValue(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim() ? value.trim().toLowerCase() : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Session marker label for an active OAuth identity: the base identifier
|
||||
* (email → accountId → projectId) suffixed with the organization when present
|
||||
* and distinct. Same-email Anthropic multi-org accounts share the base, so the
|
||||
* org suffix is the only field that tells the session's quota pool apart —
|
||||
* mirrors the account-list rows (`formatUsageReportAccount`) and login success.
|
||||
* Returns `undefined` when no identifier is recoverable.
|
||||
*/
|
||||
export function formatActiveAccountLabel(identity: OAuthAccountIdentity | undefined): string | undefined {
|
||||
if (!identity) return undefined;
|
||||
const base = identity.email || identity.accountId || identity.projectId;
|
||||
if (!base) return undefined;
|
||||
const org = identity.orgName || identity.orgId;
|
||||
return org && org !== base ? `${base} (${org})` : base;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a single usage-limit column belongs to the given OAuth identity.
|
||||
*
|
||||
|
||||
@@ -82,3 +82,34 @@ describe("renderUsageReports (#3268 TUI aggregate)", () => {
|
||||
expect(occurrences).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderUsageReports session marker (#5691 org-qualified identity)", () => {
|
||||
it("suffixes the active org so same-email multi-org accounts are tellable apart", () => {
|
||||
const email = "dev@example.test";
|
||||
const reports: UsageReport[] = [
|
||||
report("anthropic", email, [limit("Claude 7 Day", "weekly", 7 * 24 * HOUR, 0.4)]),
|
||||
];
|
||||
const text = stripVTControlCharacters(
|
||||
renderUsageReports(reports, theme, Date.now(), 120, provider =>
|
||||
provider === "anthropic" ? { email, orgId: "uuid-A", orgName: "Team Org" } : undefined,
|
||||
),
|
||||
);
|
||||
const marker = text.split("\n").find(line => line.includes("in use by this session"));
|
||||
expect(marker).toContain(`${email} (Team Org)`);
|
||||
});
|
||||
|
||||
it("falls back to the bare base when the active identity carries no org", () => {
|
||||
const email = "solo@example.test";
|
||||
const reports: UsageReport[] = [
|
||||
report("anthropic", email, [limit("Claude 7 Day", "weekly", 7 * 24 * HOUR, 0.4)]),
|
||||
];
|
||||
const text = stripVTControlCharacters(
|
||||
renderUsageReports(reports, theme, Date.now(), 120, provider =>
|
||||
provider === "anthropic" ? { email } : undefined,
|
||||
),
|
||||
);
|
||||
const marker = text.split("\n").find(line => line.includes("in use by this session"));
|
||||
expect(marker).toContain(email);
|
||||
expect(marker).not.toContain("(");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user