diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 0e753ced7..4ba1fae4a 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -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 diff --git a/packages/coding-agent/src/modes/controllers/command-controller.ts b/packages/coding-agent/src/modes/controllers/command-controller.ts index 5a8b7a715..6d570e963 100644 --- a/packages/coding-agent/src/modes/controllers/command-controller.ts +++ b/packages/coding-agent/src/modes/controllers/command-controller.ts @@ -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}`); } diff --git a/packages/coding-agent/src/slash-commands/helpers/active-oauth-account.ts b/packages/coding-agent/src/slash-commands/helpers/active-oauth-account.ts index 00e20c544..af98856d6 100644 --- a/packages/coding-agent/src/slash-commands/helpers/active-oauth-account.ts +++ b/packages/coding-agent/src/slash-commands/helpers/active-oauth-account.ts @@ -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. * diff --git a/packages/coding-agent/test/usage-report-tui-notes.test.ts b/packages/coding-agent/test/usage-report-tui-notes.test.ts index c107f7603..d4ce141a9 100644 --- a/packages/coding-agent/test/usage-report-tui-notes.test.ts +++ b/packages/coding-agent/test/usage-report-tui-notes.test.ts @@ -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("("); + }); +});