sidekick-shared 0.21.3 → 0.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,7 +47,7 @@ npm install sidekick-shared
47
47
  | **Model Info & Pricing** | Model family parsing (Anthropic / OpenAI / Google, including legacy `claude-3-opus-…` and `claude-3-5-sonnet-…` IDs), context-window lookup (including Fable 5 / Opus 4.8 / Opus 4.7 / Sonnet 4.7 1M and GPT-5.x variants), pricing tables with optional LiteLLM hydration, null-aware cost (`calculateCost()`), provenance-preserving cost (`calculateCostWithProvenance()`, `mergeCostSources()`), and display helpers (`shortModelName()`, `getModelDisplayInfo()`, `compareModelIds()`, `sortModelIds()`, `formatCost()`) |
48
48
  | **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
49
49
  | **Multi-Provider Quota** | `MultiProviderQuotaService` orchestrates Claude polling + peak-hours + account labels + Codex/z.ai quota updates behind one typed event stream. `CodexQuotaWatcher` watches the active Codex rollout for live rate limits with snapshot fallback; z.ai quota uses the API resolver with snapshot fallback |
50
- | **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account, and `getActiveAccountStatus()` for a single-pass active-account read across providers |
50
+ | **Accounts** | Multi-provider account registry (v2) with per-provider active account, save/switch/remove, v1 migration, `ensureDefaultAccounts()` for first-run bootstrap of the active system Claude/Codex credentials as a "Default" saved account, `getActiveAccountStatus()` for a single-pass active-account read across providers, and live-first `resolveActiveClaudeAccount()` / `resolveActiveCodexAccount()` that report the currently logged-in account (self-healing the saved pointer) for display |
51
51
  | **Account Management 2.0** | Provider-neutral acquisition + switching: `beginAccountLogin()` / `getAccountLoginStatus()` / `finalizeAccountLogin()` / `spawnAccountLogin()` drive a TTY-less, profile-isolated login that doesn't disturb the active account until finalization; `listAllAccounts()` and `switchAccount()` expose a shared switcher across Claude and Codex. Claude accounts get canonical profile homes with account-scoped macOS keychain suffixes and startup migration from legacy flat backups |
52
52
  | **Terminal sync & auto-switch** | Opt-in terminal profile pointers, shell-hook/launcher helpers, and a default-off `AutoSwitchController` (`decideAutoSwitch()`) that moves to a healthier saved account when quota crosses a configured threshold |
53
53
  | **Codex Profiles** | Codex account lifecycle — prepare, finalize, switch, remove — switching atomically swaps the profile's backed-up credentials into the system `~/.codex/auth.json`, with rotated-token staleness protection, one-time dual-home migration, and legacy multi-home session monitoring |
@@ -359,6 +359,21 @@ console.log(status.claude.present, status.claude.email);
359
359
  console.log(status.codex.present, status.codex.label);
360
360
  ```
361
361
 
362
+ For display surfaces that must reflect the **currently logged-in** account — even after a native `claude /login` or `codex login` outside Sidekick — use the live-first resolvers. They prefer the live provider auth (`~/.claude/.claude.json` oauthAccount; the `~/.codex/auth.json` id_token JWT) over the saved `activeByProvider` pointer, fall back to the registry, and self-heal the saved pointer on an unambiguous match (best-effort, never creating or deleting profiles):
363
+
364
+ ```typescript
365
+ import { resolveActiveClaudeAccount, resolveActiveCodexAccount } from 'sidekick-shared';
366
+ import type { ResolvedActiveAccount } from 'sidekick-shared';
367
+
368
+ const claude: ResolvedActiveAccount = resolveActiveClaudeAccount();
369
+ // source: 'live' → identity came from the live provider auth (label filled when it matches a saved profile)
370
+ // source: 'registry' → no usable live identity; fell back to the saved active pointer
371
+ // source: 'none' → neither a live identity nor a saved active account
372
+ console.log(claude.email, claude.label, claude.source);
373
+
374
+ const codex = resolveActiveCodexAccount();
375
+ ```
376
+
362
377
  ### Track cost provenance for honest UI rollups
363
378
 
364
379
  ```typescript
@@ -19,6 +19,23 @@ export interface SavedAccountRegistry {
19
19
  activeByProvider: Record<AccountProviderId, string | null>;
20
20
  accounts: SavedAccountProfile[];
21
21
  }
22
+ /**
23
+ * The currently active account resolved from the *live* provider auth, with the
24
+ * saved registry used only to enrich the display label.
25
+ *
26
+ * - `source: 'live'` — identity came from the provider's live auth file. This
27
+ * is the source of truth; `label` is filled in only when the live identity
28
+ * matches a saved profile.
29
+ * - `source: 'registry'` — no usable live identity (logged out, api-key auth,
30
+ * unparseable token); fell back to the saved active pointer.
31
+ * - `source: 'none'` — neither a live identity nor a saved active account.
32
+ */
33
+ export interface ResolvedActiveAccount {
34
+ email?: string;
35
+ label?: string;
36
+ providerAccountId?: string;
37
+ source: 'live' | 'registry' | 'none';
38
+ }
22
39
  export declare function getAccountsDir(): string;
23
40
  export declare function readSavedAccountRegistry(): SavedAccountRegistry | null;
24
41
  export declare function writeSavedAccountRegistry(registry: SavedAccountRegistry): void;
@@ -14,23 +14,20 @@ function errorMessage(error) {
14
14
  */
15
15
  function getActiveAccountStatus(error) {
16
16
  try {
17
- const claudeAccount = (0, accounts_1.readActiveClaudeAccount)();
18
- const codexAccount = (0, codexProfiles_1.getActiveCodexAccount)();
19
- const claude = claudeAccount
17
+ const claudeAccount = (0, accounts_1.resolveActiveClaudeAccount)();
18
+ const codexAccount = (0, codexProfiles_1.resolveActiveCodexAccount)();
19
+ const claude = claudeAccount.source !== 'none'
20
20
  ? {
21
21
  present: true,
22
22
  email: claudeAccount.email,
23
- label: claudeAccount.email,
23
+ label: claudeAccount.label ?? claudeAccount.email,
24
24
  }
25
25
  : { present: false };
26
- const codex = codexAccount
26
+ const codex = codexAccount.source !== 'none'
27
27
  ? {
28
28
  present: true,
29
- email: codexAccount.email ?? codexAccount.metadata?.email,
30
- label: codexAccount.label ??
31
- codexAccount.email ??
32
- codexAccount.metadata?.email ??
33
- codexAccount.id,
29
+ email: codexAccount.email,
30
+ label: codexAccount.label ?? codexAccount.email ?? codexAccount.providerAccountId,
34
31
  }
35
32
  : { present: false };
36
33
  return {
@@ -4,6 +4,7 @@
4
4
  * Handles saving, listing, switching, and removing Claude accounts.
5
5
  * Both the VS Code extension and CLI consume this module.
6
6
  */
7
+ import { type ResolvedActiveAccount } from './accountRegistry';
7
8
  export interface AccountEntry {
8
9
  uuid: string;
9
10
  email: string;
@@ -38,4 +39,16 @@ export declare function reconcileClaudeAuthState(): void;
38
39
  export declare function removeAccount(uuid: string): AccountManagerResult;
39
40
  export declare function listAccounts(): AccountEntry[];
40
41
  export declare function getActiveAccount(): AccountEntry | null;
42
+ /**
43
+ * Resolves the *currently logged-in* Claude account for display, preferring the
44
+ * live `~/.claude/.claude.json` identity over the saved registry pointer (which
45
+ * only sidekick's own switch flow updates and therefore goes stale after a native
46
+ * `claude /login`).
47
+ *
48
+ * Safe self-heal: when the live account matches a saved profile (by account UUID,
49
+ * email as fallback) that isn't the current active pointer, the pointer is
50
+ * re-pointed so registry-keyed data tracks reality too. Never creates or deletes
51
+ * profiles; an unknown live account is shown as-is with no label and no write.
52
+ */
53
+ export declare function resolveActiveClaudeAccount(): ResolvedActiveAccount;
41
54
  export declare function isMultiAccountEnabled(): boolean;
package/dist/accounts.js CHANGED
@@ -50,6 +50,7 @@ exports.reconcileClaudeAuthState = reconcileClaudeAuthState;
50
50
  exports.removeAccount = removeAccount;
51
51
  exports.listAccounts = listAccounts;
52
52
  exports.getActiveAccount = getActiveAccount;
53
+ exports.resolveActiveClaudeAccount = resolveActiveClaudeAccount;
53
54
  exports.isMultiAccountEnabled = isMultiAccountEnabled;
54
55
  const fs = __importStar(require("fs"));
55
56
  const path = __importStar(require("path"));
@@ -513,6 +514,55 @@ function getActiveAccount() {
513
514
  addedAt: active.addedAt,
514
515
  };
515
516
  }
517
+ /**
518
+ * Resolves the *currently logged-in* Claude account for display, preferring the
519
+ * live `~/.claude/.claude.json` identity over the saved registry pointer (which
520
+ * only sidekick's own switch flow updates and therefore goes stale after a native
521
+ * `claude /login`).
522
+ *
523
+ * Safe self-heal: when the live account matches a saved profile (by account UUID,
524
+ * email as fallback) that isn't the current active pointer, the pointer is
525
+ * re-pointed so registry-keyed data tracks reality too. Never creates or deletes
526
+ * profiles; an unknown live account is shown as-is with no label and no write.
527
+ */
528
+ function resolveActiveClaudeAccount() {
529
+ const live = readActiveClaudeAccount();
530
+ if (live) {
531
+ const profiles = (0, accountRegistry_1.listSavedAccountProfiles)('claude-code');
532
+ const match = profiles.find((p) => (p.providerAccountId ?? p.id) === live.uuid) ??
533
+ profiles.find((p) => (p.email ?? p.metadata?.email) === live.email);
534
+ if (match) {
535
+ const active = (0, accountRegistry_1.getActiveSavedAccount)('claude-code');
536
+ if (!active || active.id !== match.id) {
537
+ // Self-heal is best-effort: a registry write failure (read-only/full
538
+ // disk) must never break display or extension activation. We still
539
+ // return the correct live identity below.
540
+ try {
541
+ (0, accountRegistry_1.setActiveSavedAccount)('claude-code', match.id);
542
+ }
543
+ catch {
544
+ /* keep going with the live identity */
545
+ }
546
+ }
547
+ }
548
+ return {
549
+ email: live.email,
550
+ label: match?.label,
551
+ providerAccountId: live.uuid,
552
+ source: 'live',
553
+ };
554
+ }
555
+ const active = getActiveAccount();
556
+ if (active) {
557
+ return {
558
+ email: active.email,
559
+ label: active.label,
560
+ providerAccountId: active.uuid,
561
+ source: 'registry',
562
+ };
563
+ }
564
+ return { source: 'none' };
565
+ }
516
566
  function isMultiAccountEnabled() {
517
567
  return (0, accountRegistry_1.listSavedAccountProfiles)('claude-code').length >= 1;
518
568
  }
@@ -1,4 +1,4 @@
1
- import type { AccountIdentityMetadata, SavedAccountProfile } from './accountRegistry';
1
+ import type { AccountIdentityMetadata, ResolvedActiveAccount, SavedAccountProfile } from './accountRegistry';
2
2
  import type { AccountManagerResult } from './accounts';
3
3
  export interface CodexAccountManagerResult extends AccountManagerResult {
4
4
  needsLogin?: boolean;
@@ -14,6 +14,20 @@ export declare function isCodexProfileAuthenticated(codexHome: string): boolean;
14
14
  export declare function listCodexAccounts(): SavedAccountProfile[];
15
15
  export declare function getActiveCodexAccount(): SavedAccountProfile | null;
16
16
  export declare function resolveSidekickCodexHome(): string;
17
+ /**
18
+ * Resolves the *currently logged-in* Codex account for display, preferring the
19
+ * live `auth.json` identity over the saved registry pointer (which only sidekick's
20
+ * own switch flow updates and therefore goes stale after a native `codex login`).
21
+ *
22
+ * Uses the cheap JWT decode from `auth.json` directly — NOT `readCodexAccountMetadata`,
23
+ * whose fallback spawns `codex login status` (a multi-second subprocess) unsuitable
24
+ * for a render path.
25
+ *
26
+ * Safe self-heal: when the live identity unambiguously matches a saved profile that
27
+ * isn't the current active pointer, the pointer is re-pointed so registry-keyed data
28
+ * (quota history, auto-switch) tracks reality too. Never creates or deletes profiles.
29
+ */
30
+ export declare function resolveActiveCodexAccount(): ResolvedActiveAccount;
17
31
  export declare function getCodexExecutionEnv(baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
18
32
  export declare function prepareCodexAccount(label: string): CodexAccountManagerResult;
19
33
  export declare function finalizeCodexAccount(profileId: string, opts?: {
@@ -42,6 +42,7 @@ exports.isCodexProfileAuthenticated = isCodexProfileAuthenticated;
42
42
  exports.listCodexAccounts = listCodexAccounts;
43
43
  exports.getActiveCodexAccount = getActiveCodexAccount;
44
44
  exports.resolveSidekickCodexHome = resolveSidekickCodexHome;
45
+ exports.resolveActiveCodexAccount = resolveActiveCodexAccount;
45
46
  exports.getCodexExecutionEnv = getCodexExecutionEnv;
46
47
  exports.prepareCodexAccount = prepareCodexAccount;
47
48
  exports.finalizeCodexAccount = finalizeCodexAccount;
@@ -346,6 +347,56 @@ function resolveSidekickCodexHome() {
346
347
  // home (or an explicit CODEX_HOME) is always the single live home.
347
348
  return getSystemCodexHome();
348
349
  }
350
+ /**
351
+ * Resolves the *currently logged-in* Codex account for display, preferring the
352
+ * live `auth.json` identity over the saved registry pointer (which only sidekick's
353
+ * own switch flow updates and therefore goes stale after a native `codex login`).
354
+ *
355
+ * Uses the cheap JWT decode from `auth.json` directly — NOT `readCodexAccountMetadata`,
356
+ * whose fallback spawns `codex login status` (a multi-second subprocess) unsuitable
357
+ * for a render path.
358
+ *
359
+ * Safe self-heal: when the live identity unambiguously matches a saved profile that
360
+ * isn't the current active pointer, the pointer is re-pointed so registry-keyed data
361
+ * (quota history, auto-switch) tracks reality too. Never creates or deletes profiles.
362
+ */
363
+ function resolveActiveCodexAccount() {
364
+ const identity = readAuthIdentityFromRaw(readFileOrNull(path.join(resolveSidekickCodexHome(), 'auth.json')));
365
+ if (identity && (identity.email || identity.workspaceId)) {
366
+ const match = findProfileForIdentity(identity);
367
+ if (match) {
368
+ const active = (0, accountRegistry_1.getActiveSavedAccount)('codex');
369
+ if (!active || active.id !== match.id) {
370
+ // Self-heal is best-effort: a registry write failure (read-only/full
371
+ // disk) must never break display, extension activation, or the quota
372
+ // watcher's hot path. We still return the correct live identity below.
373
+ try {
374
+ (0, accountRegistry_1.setActiveSavedAccount)('codex', match.id);
375
+ }
376
+ catch {
377
+ /* keep going with the live identity */
378
+ }
379
+ }
380
+ }
381
+ return {
382
+ email: identity.email ?? match?.email ?? match?.metadata?.email,
383
+ label: match?.label,
384
+ providerAccountId: match?.providerAccountId ?? identity.workspaceId,
385
+ source: 'live',
386
+ };
387
+ }
388
+ // api-key auth, unparseable token, or logged out → fall back to the registry.
389
+ const active = getActiveCodexAccount();
390
+ if (active) {
391
+ return {
392
+ email: active.email ?? active.metadata?.email,
393
+ label: active.label,
394
+ providerAccountId: active.providerAccountId,
395
+ source: 'registry',
396
+ };
397
+ }
398
+ return { source: 'none' };
399
+ }
349
400
  function getCodexExecutionEnv(baseEnv = process.env) {
350
401
  return {
351
402
  ...baseEnv,
@@ -96,7 +96,15 @@ class CodexQuotaWatcher {
96
96
  this.discoveryPollIntervalMs =
97
97
  options.discoveryPollIntervalMs ?? DEFAULT_DISCOVERY_POLL_INTERVAL_MS;
98
98
  this.providerFactory = options.providerFactory ?? (() => new codex_1.CodexProvider());
99
- this.getActiveAccount = options.getActiveAccount ?? codexProfiles_1.getActiveCodexAccount;
99
+ // Self-heal the registry pointer to the live login before reading it, so the
100
+ // history key (account.id) and the display label track the currently
101
+ // logged-in account even after a native `codex login`. Overridable for tests.
102
+ this.getActiveAccount =
103
+ options.getActiveAccount ??
104
+ (() => {
105
+ (0, codexProfiles_1.resolveActiveCodexAccount)();
106
+ return (0, codexProfiles_1.getActiveCodexAccount)();
107
+ });
100
108
  this.readSnapshot = options.readSnapshot ?? quotaSnapshots_1.readQuotaSnapshot;
101
109
  this.writeSnapshot = options.writeSnapshot ?? quotaSnapshots_1.writeQuotaSnapshot;
102
110
  this.watchFile = options.watchFile ?? fs.watch;
package/dist/index.d.ts CHANGED
@@ -93,17 +93,17 @@ export { readClaudeMaxCredentials, readClaudeMaxAccessTokenSync } from './creden
93
93
  export type { ClaudeMaxCredentials } from './credentials';
94
94
  export { ensureDefaultAccounts } from './ensureDefaultAccounts';
95
95
  export type { EnsureDefaultAccountStatus, EnsureDefaultAccountsOptions, EnsureDefaultAccountsResult, } from './ensureDefaultAccounts';
96
- export { readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, resolveActiveClaudeHome, applyActiveClaudeToLiveHome, reconcileClaudeAuthState, removeAccount, listAccounts, getActiveAccount, isMultiAccountEnabled, } from './accounts';
96
+ export { readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, resolveActiveClaudeHome, applyActiveClaudeToLiveHome, reconcileClaudeAuthState, removeAccount, listAccounts, getActiveAccount, resolveActiveClaudeAccount, isMultiAccountEnabled, } from './accounts';
97
97
  export type { AccountEntry, AccountRegistry, ActiveAccountInfo, AccountManagerResult, } from './accounts';
98
98
  export { beginAccountLogin, getAccountLoginStatus, finalizeAccountLogin, spawnAccountLogin, switchAccount, listAllAccounts, resolveClaudeLoginCommand, } from './accountManager';
99
99
  export type { AccountLoginCommand, AccountLoginState, AccountLoginStatus, BeginAccountLoginResult, FinalizeAccountLoginOptions, ListAllAccountsResult, SpawnAccountLoginOptions, } from './accountManager';
100
100
  export { getAccountsDir, readSavedAccountRegistry, writeSavedAccountRegistry, listSavedAccountProfiles, getActiveSavedAccount, upsertSavedAccountProfile, setActiveSavedAccount, replaceSavedAccountProfiles, removeSavedAccountProfile, } from './accountRegistry';
101
- export type { AccountProviderId, AccountIdentityMetadata, SavedAccountProfile, SavedAccountRegistry, } from './accountRegistry';
101
+ export type { AccountProviderId, AccountIdentityMetadata, ResolvedActiveAccount, SavedAccountProfile, SavedAccountRegistry, } from './accountRegistry';
102
102
  export { getActiveAccountStatus } from './accountStatus';
103
103
  export type { ActiveAccountStatus, ActiveProviderAccountStatus } from './accountStatus';
104
104
  export { getClaudeProfilesDir, getClaudeProfileHome, claudeKeychainSuffix, claudeKeychainService, isClaudeProfileAuthenticated, readClaudeProfileIdentity, } from './claudeProfiles';
105
105
  export type { ClaudeProfileIdentity } from './claudeProfiles';
106
- export { getCodexProfilesDir, getCodexProfileHome, getCodexMonitoringHomes, getSystemCodexHome, listCodexAccounts, getActiveCodexAccount, resolveSidekickCodexHome, getCodexExecutionEnv, prepareCodexAccount, finalizeCodexAccount, switchToCodexAccount, removeCodexAccount, } from './codexProfiles';
106
+ export { getCodexProfilesDir, getCodexProfileHome, getCodexMonitoringHomes, getSystemCodexHome, listCodexAccounts, getActiveCodexAccount, resolveActiveCodexAccount, resolveSidekickCodexHome, getCodexExecutionEnv, prepareCodexAccount, finalizeCodexAccount, switchToCodexAccount, removeCodexAccount, } from './codexProfiles';
107
107
  export type { CodexAccountManagerResult } from './codexProfiles';
108
108
  export { setTerminalActiveProfile, installShellHook, uninstallShellHook, isShellHookInstalled, writeLauncher, removeLauncher, } from './terminalSync';
109
109
  export { DEFAULT_AUTO_SWITCH_CONFIG, decideAutoSwitch, AutoSwitchController } from './autoSwitch';
package/dist/index.js CHANGED
@@ -5,10 +5,10 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.discoverSessionDirectory = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.getOpenCodeDataDir = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = void 0;
7
7
  exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.formatTokenCount = exports.formatDurationMs = exports.createJsonlTail = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.readSessionContextSnapshot = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = exports.findActiveClaudeSession = void 0;
8
- exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = void 0;
9
- exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.withQuotaProjections = exports.projectQuotaWindow = exports.fetchQuota = exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = void 0;
10
- exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractToolCalls = exports.extractToolCall = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.MultiProviderQuotaService = exports.resolveZaiQuota = exports.readZaiCredentials = exports.quotaStateFromZaiQuotaLimitPayload = exports.fetchZaiQuotaFromApi = exports.ZaiQuotaWatcher = exports.ZAI_TIER_BUDGETS = exports.ZAI_PROVIDER_IDS = exports.ZAI_PROMPT_INVOCATIONS = exports.turnTokenWeight = exports.rowsToZaiTurnsAndErrors = exports.resolveZaiTier = exports.parseZaiQuotaError = exports.makeUnavailableZaiQuotaState = exports.isZaiProviderId = exports.inferZaiQuotaState = exports.extractNextFlushTime = exports.accumulateZaiUsage = void 0;
11
- exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = void 0;
8
+ exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.resolveActiveClaudeAccount = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = void 0;
9
+ exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.withQuotaProjections = exports.projectQuotaWindow = exports.fetchQuota = exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.resolveActiveCodexAccount = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = void 0;
10
+ exports.sessionMessageSchema = exports.messageUsageSchema = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractToolCalls = exports.extractToolCall = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.MultiProviderQuotaService = exports.resolveZaiQuota = exports.readZaiCredentials = exports.quotaStateFromZaiQuotaLimitPayload = exports.fetchZaiQuotaFromApi = exports.ZaiQuotaWatcher = exports.ZAI_TIER_BUDGETS = exports.ZAI_PROVIDER_IDS = exports.ZAI_PROMPT_INVOCATIONS = exports.turnTokenWeight = exports.rowsToZaiTurnsAndErrors = exports.resolveZaiTier = exports.parseZaiQuotaError = exports.makeUnavailableZaiQuotaState = exports.isZaiProviderId = exports.inferZaiQuotaState = exports.extractNextFlushTime = exports.accumulateZaiUsage = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = void 0;
11
+ exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = void 0;
12
12
  var taskPersistence_1 = require("./types/taskPersistence");
13
13
  Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
14
14
  Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
@@ -220,6 +220,7 @@ Object.defineProperty(exports, "reconcileClaudeAuthState", { enumerable: true, g
220
220
  Object.defineProperty(exports, "removeAccount", { enumerable: true, get: function () { return accounts_1.removeAccount; } });
221
221
  Object.defineProperty(exports, "listAccounts", { enumerable: true, get: function () { return accounts_1.listAccounts; } });
222
222
  Object.defineProperty(exports, "getActiveAccount", { enumerable: true, get: function () { return accounts_1.getActiveAccount; } });
223
+ Object.defineProperty(exports, "resolveActiveClaudeAccount", { enumerable: true, get: function () { return accounts_1.resolveActiveClaudeAccount; } });
223
224
  Object.defineProperty(exports, "isMultiAccountEnabled", { enumerable: true, get: function () { return accounts_1.isMultiAccountEnabled; } });
224
225
  var accountManager_1 = require("./accountManager");
225
226
  Object.defineProperty(exports, "beginAccountLogin", { enumerable: true, get: function () { return accountManager_1.beginAccountLogin; } });
@@ -255,6 +256,7 @@ Object.defineProperty(exports, "getCodexMonitoringHomes", { enumerable: true, ge
255
256
  Object.defineProperty(exports, "getSystemCodexHome", { enumerable: true, get: function () { return codexProfiles_1.getSystemCodexHome; } });
256
257
  Object.defineProperty(exports, "listCodexAccounts", { enumerable: true, get: function () { return codexProfiles_1.listCodexAccounts; } });
257
258
  Object.defineProperty(exports, "getActiveCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.getActiveCodexAccount; } });
259
+ Object.defineProperty(exports, "resolveActiveCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.resolveActiveCodexAccount; } });
258
260
  Object.defineProperty(exports, "resolveSidekickCodexHome", { enumerable: true, get: function () { return codexProfiles_1.resolveSidekickCodexHome; } });
259
261
  Object.defineProperty(exports, "getCodexExecutionEnv", { enumerable: true, get: function () { return codexProfiles_1.getCodexExecutionEnv; } });
260
262
  Object.defineProperty(exports, "prepareCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.prepareCodexAccount; } });
@@ -1,4 +1,4 @@
1
- import { readActiveClaudeAccount } from './accounts';
1
+ import { resolveActiveClaudeAccount } from './accounts';
2
2
  import type { ClaudeMaxCredentials } from './credentials';
3
3
  import type { PeakHoursState } from './peakHours';
4
4
  import type { QuotaState } from './quota';
@@ -25,7 +25,7 @@ export interface MultiProviderQuotaServiceOptions {
25
25
  codexWatcher?: CodexWatcherLike | null;
26
26
  zaiWatcher?: ZaiWatcherLike | null;
27
27
  readClaudeCredentials?: () => Promise<ClaudeMaxCredentials | null>;
28
- readClaudeAccount?: typeof readActiveClaudeAccount;
28
+ readClaudeAccount?: typeof resolveActiveClaudeAccount;
29
29
  fetchClaudeQuota?: (accessToken: string) => Promise<QuotaState>;
30
30
  fetchPeakHours?: () => Promise<PeakHoursState>;
31
31
  log?: Logger;
@@ -82,7 +82,7 @@ class MultiProviderQuotaService {
82
82
  this.peakHoursCacheMaxAgeMs = options.peakHoursCacheMaxAgeMs ?? PEAK_HOURS_CACHE_MAX_AGE_MS;
83
83
  this.includePeakHours = options.includePeakHours ?? true;
84
84
  this.readClaudeCredentials = options.readClaudeCredentials ?? credentials_1.readClaudeMaxCredentials;
85
- this.readClaudeAccount = options.readClaudeAccount ?? accounts_1.readActiveClaudeAccount;
85
+ this.readClaudeAccount = options.readClaudeAccount ?? accounts_1.resolveActiveClaudeAccount;
86
86
  this.fetchClaudeQuota = options.fetchClaudeQuota ?? quota_1.fetchQuota;
87
87
  this.fetchPeakHours = options.fetchPeakHours ?? peakHours_1.fetchPeakHoursStatus;
88
88
  this.log = options.log;
@@ -353,7 +353,8 @@ class MultiProviderQuotaService {
353
353
  this.timer = setTimeout(() => void this.poll(), delay);
354
354
  }
355
355
  withClaudeAccountDetails(state) {
356
- const accountEmail = this.readClaudeAccount()?.email;
356
+ const resolved = this.readClaudeAccount();
357
+ const accountEmail = resolved.email;
357
358
  if (!accountEmail) {
358
359
  const nextState = { ...state };
359
360
  delete nextState.accountLabel;
@@ -362,7 +363,7 @@ class MultiProviderQuotaService {
362
363
  }
363
364
  return {
364
365
  ...state,
365
- accountLabel: accountEmail,
366
+ accountLabel: resolved.label ?? accountEmail,
366
367
  accountDetail: accountEmail,
367
368
  };
368
369
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-shared",
3
- "version": "0.21.3",
3
+ "version": "0.21.4",
4
4
  "description": "Shared data access layer for Sidekick — readers, types, providers, credentials, quota",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",