sidekick-shared 0.21.3 → 0.21.5

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,
@@ -44,6 +44,7 @@ const codexProfiles_1 = require("./codexProfiles");
44
44
  const quotaSnapshots_1 = require("./quotaSnapshots");
45
45
  const quota_1 = require("./quota");
46
46
  const codex_1 = require("./providers/codex");
47
+ const codex_2 = require("./types/codex");
47
48
  const DEFAULT_TAIL_BYTES = 2 * 1024 * 1024;
48
49
  const DEFAULT_MAX_SESSION_FILES = 50;
49
50
  const CHATGPT_USAGE_URL = 'https://chatgpt.com/backend-api/wham/usage';
@@ -54,11 +55,19 @@ function timestampMs(value, fallbackMs = 0) {
54
55
  return Number.isFinite(ms) ? ms : fallbackMs;
55
56
  }
56
57
  // Codex can report slightly different percentages for the same reset window
57
- // across recent sessions. Prefer newer reset windows, then the highest observed
58
- // same-window utilization so local fallbacks do not under-report quota usage.
58
+ // across recent sessions. Prefer the aggregate plan family ("codex") over
59
+ // model/feature-specific families (which can read 0% while the plan is busy),
60
+ // then newer reset windows, then the highest observed same-window utilization so
61
+ // local fallbacks do not under-report quota usage.
59
62
  function isPreferredQuotaHit(candidate, current) {
60
63
  if (!current)
61
64
  return true;
65
+ // Family rank first: an aggregate hit always outranks a model-specific one, so a
66
+ // freshly-used per-model family (e.g. codex_bengalfox at 0%) can never mask the plan quota.
67
+ const candidateAggregate = (0, codex_2.isAggregateCodexLimit)(candidate.quota.limitId);
68
+ const currentAggregate = (0, codex_2.isAggregateCodexLimit)(current.quota.limitId);
69
+ if (candidateAggregate !== currentAggregate)
70
+ return candidateAggregate;
62
71
  const candidatePrimaryReset = timestampMs(candidate.quota.fiveHour.resetsAt);
63
72
  const currentPrimaryReset = timestampMs(current.quota.fiveHour.resetsAt);
64
73
  if (candidatePrimaryReset !== currentPrimaryReset)
@@ -464,7 +473,11 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
464
473
  const firstNewline = text.indexOf('\n');
465
474
  text = firstNewline >= 0 ? text.slice(firstNewline + 1) : '';
466
475
  }
476
+ // Scan from the end for the latest aggregate ("codex") rate-limit sample, which
477
+ // is what the plan quota represents. Keep the latest sample of any family as a
478
+ // fallback so a model-only session still surfaces something.
467
479
  const lines = text.split('\n');
480
+ let fallback = null;
468
481
  for (let i = lines.length - 1; i >= 0; i--) {
469
482
  const line = lines[i].trim();
470
483
  if (!line || !line.includes('rate_limits'))
@@ -474,13 +487,20 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
474
487
  if (parsed.type !== 'event_msg' || parsed.payload?.type !== 'token_count')
475
488
  continue;
476
489
  const quota = quotaFromCodexRateLimits(parsed.payload.rate_limits, source, parsed.timestamp ?? new Date(stat.mtime).toISOString());
477
- if (quota)
478
- return { quota, filePath: sessionPath, mtimeMs: stat.mtime.getTime() };
490
+ if (!quota)
491
+ continue;
492
+ const hit = { quota, filePath: sessionPath, mtimeMs: stat.mtime.getTime() };
493
+ if ((0, codex_2.isAggregateCodexLimit)(quota.limitId))
494
+ return hit;
495
+ if (!fallback)
496
+ fallback = hit;
479
497
  }
480
498
  catch {
481
499
  // Ignore malformed or partial lines.
482
500
  }
483
501
  }
502
+ if (fallback)
503
+ return fallback;
484
504
  }
485
505
  catch {
486
506
  return null;
@@ -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
@@ -4,6 +4,7 @@
4
4
  export type { SessionEvent, ClaudeSessionEvent, MessageUsage, SessionMessage, TokenUsage, ToolCall, ToolAnalytics, TimelineEvent, PendingToolCall, TaskStatus as SessionTaskStatus, TrackedTask, TaskState, SubagentStats, PendingUserRequest, ResponseLatency, LatencyStats, PlanStep, PlanState, SessionStats, CompactionEvent, TruncationEvent, ContextAttribution, TurnAttribution, ContextSizePoint, PermissionMode, PermissionModeChange, } from './types/sessionEvent';
5
5
  export type { OpenCodeSession, OpenCodeMessage, OpenCodePart, OpenCodeTextPart, OpenCodeReasoningPart, OpenCodeToolInvocationPart, OpenCodeCompactionPart, OpenCodeDbToolPart, OpenCodeStepStartPart, OpenCodeStepFinishPart, OpenCodePatchPart, OpenCodeSubtaskPart, OpenCodeAgentPart, OpenCodeFilePart, OpenCodeRetryPart, OpenCodeSnapshotPart, OpenCodeProject, DbProject as OpenCodeDbProject, DbSession as OpenCodeDbSession, DbMessage as OpenCodeDbMessage, DbPart as OpenCodeDbPart, } from './types/opencode';
6
6
  export type { CodexRolloutLine, CodexSessionMeta, CodexResponseItem, CodexMessageItem, CodexContentPart, CodexReasoningItem, CodexFunctionCallItem, CodexFunctionCallOutputItem, CodexLocalShellCallItem, CodexWebSearchCallItem, CodexCustomToolCallItem, CodexCustomToolCallOutputItem, CodexCompacted, CodexTurnContext, CodexEventMsg, CodexEvent, CodexTurnStartedEvent, CodexTurnCompleteEvent, CodexTaskStartedEvent, CodexTaskCompleteEvent, CodexTurnAbortedEvent, CodexTokenCountEvent, CodexTokenUsage, CodexRateLimits, CodexAgentMessageEvent, CodexAgentReasoningEvent, CodexUserMessageEvent, CodexExecCommandBeginEvent, CodexExecCommandEndEvent, CodexMcpToolCallBeginEvent, CodexMcpToolCallEndEvent, CodexErrorEvent, CodexContextCompactedEvent, CodexPatchAppliedEvent, CodexBackgroundEvent, CodexGenericEvent, CodexDbThread, CodexReasoningSummary, } from './types/codex';
7
+ export { isAggregateCodexLimit } from './types/codex';
7
8
  export type { PersistedTask, TaskPersistenceStore, TaskStatus } from './types/taskPersistence';
8
9
  export { TASK_PERSISTENCE_SCHEMA_VERSION, normalizeTaskStatus } from './types/taskPersistence';
9
10
  export type { DecisionEntry, DecisionLogStore, DecisionSource } from './types/decisionLog';
@@ -93,17 +94,17 @@ export { readClaudeMaxCredentials, readClaudeMaxAccessTokenSync } from './creden
93
94
  export type { ClaudeMaxCredentials } from './credentials';
94
95
  export { ensureDefaultAccounts } from './ensureDefaultAccounts';
95
96
  export type { EnsureDefaultAccountStatus, EnsureDefaultAccountsOptions, EnsureDefaultAccountsResult, } from './ensureDefaultAccounts';
96
- export { readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, resolveActiveClaudeHome, applyActiveClaudeToLiveHome, reconcileClaudeAuthState, removeAccount, listAccounts, getActiveAccount, isMultiAccountEnabled, } from './accounts';
97
+ export { readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, resolveActiveClaudeHome, applyActiveClaudeToLiveHome, reconcileClaudeAuthState, removeAccount, listAccounts, getActiveAccount, resolveActiveClaudeAccount, isMultiAccountEnabled, } from './accounts';
97
98
  export type { AccountEntry, AccountRegistry, ActiveAccountInfo, AccountManagerResult, } from './accounts';
98
99
  export { beginAccountLogin, getAccountLoginStatus, finalizeAccountLogin, spawnAccountLogin, switchAccount, listAllAccounts, resolveClaudeLoginCommand, } from './accountManager';
99
100
  export type { AccountLoginCommand, AccountLoginState, AccountLoginStatus, BeginAccountLoginResult, FinalizeAccountLoginOptions, ListAllAccountsResult, SpawnAccountLoginOptions, } from './accountManager';
100
101
  export { getAccountsDir, readSavedAccountRegistry, writeSavedAccountRegistry, listSavedAccountProfiles, getActiveSavedAccount, upsertSavedAccountProfile, setActiveSavedAccount, replaceSavedAccountProfiles, removeSavedAccountProfile, } from './accountRegistry';
101
- export type { AccountProviderId, AccountIdentityMetadata, SavedAccountProfile, SavedAccountRegistry, } from './accountRegistry';
102
+ export type { AccountProviderId, AccountIdentityMetadata, ResolvedActiveAccount, SavedAccountProfile, SavedAccountRegistry, } from './accountRegistry';
102
103
  export { getActiveAccountStatus } from './accountStatus';
103
104
  export type { ActiveAccountStatus, ActiveProviderAccountStatus } from './accountStatus';
104
105
  export { getClaudeProfilesDir, getClaudeProfileHome, claudeKeychainSuffix, claudeKeychainService, isClaudeProfileAuthenticated, readClaudeProfileIdentity, } from './claudeProfiles';
105
106
  export type { ClaudeProfileIdentity } from './claudeProfiles';
106
- export { getCodexProfilesDir, getCodexProfileHome, getCodexMonitoringHomes, getSystemCodexHome, listCodexAccounts, getActiveCodexAccount, resolveSidekickCodexHome, getCodexExecutionEnv, prepareCodexAccount, finalizeCodexAccount, switchToCodexAccount, removeCodexAccount, } from './codexProfiles';
107
+ export { getCodexProfilesDir, getCodexProfileHome, getCodexMonitoringHomes, getSystemCodexHome, listCodexAccounts, getActiveCodexAccount, resolveActiveCodexAccount, resolveSidekickCodexHome, getCodexExecutionEnv, prepareCodexAccount, finalizeCodexAccount, switchToCodexAccount, removeCodexAccount, } from './codexProfiles';
107
108
  export type { CodexAccountManagerResult } from './codexProfiles';
108
109
  export { setTerminalActiveProfile, installShellHook, uninstallShellHook, isShellHookInstalled, writeLauncher, removeLauncher, } from './terminalSync';
109
110
  export { DEFAULT_AUTO_SWITCH_CONFIG, decideAutoSwitch, AutoSwitchController } from './autoSwitch';
package/dist/index.js CHANGED
@@ -3,12 +3,14 @@
3
3
  * Public API for sidekick-shared.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
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
- 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;
6
+ 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 = exports.isAggregateCodexLimit = void 0;
7
+ 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 = exports.discoverSessionDirectory = void 0;
8
+ 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 = exports.highlightEvent = void 0;
9
+ 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 = exports.upsertSavedAccountProfile = void 0;
10
+ 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 = exports.resolveCodexQuota = 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 = exports.sessionMessageSchema = void 0;
12
+ var codex_1 = require("./types/codex");
13
+ Object.defineProperty(exports, "isAggregateCodexLimit", { enumerable: true, get: function () { return codex_1.isAggregateCodexLimit; } });
12
14
  var taskPersistence_1 = require("./types/taskPersistence");
13
15
  Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
14
16
  Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
@@ -57,8 +59,8 @@ Object.defineProperty(exports, "ClaudeCodeProvider", { enumerable: true, get: fu
57
59
  var openCode_1 = require("./providers/openCode");
58
60
  Object.defineProperty(exports, "OpenCodeProvider", { enumerable: true, get: function () { return openCode_1.OpenCodeProvider; } });
59
61
  Object.defineProperty(exports, "getOpenCodeDataDir", { enumerable: true, get: function () { return openCode_1.getOpenCodeDataDir; } });
60
- var codex_1 = require("./providers/codex");
61
- Object.defineProperty(exports, "CodexProvider", { enumerable: true, get: function () { return codex_1.CodexProvider; } });
62
+ var codex_2 = require("./providers/codex");
63
+ Object.defineProperty(exports, "CodexProvider", { enumerable: true, get: function () { return codex_2.CodexProvider; } });
62
64
  // Parsers — JSONL
63
65
  var jsonl_1 = require("./parsers/jsonl");
64
66
  Object.defineProperty(exports, "JsonlParser", { enumerable: true, get: function () { return jsonl_1.JsonlParser; } });
@@ -220,6 +222,7 @@ Object.defineProperty(exports, "reconcileClaudeAuthState", { enumerable: true, g
220
222
  Object.defineProperty(exports, "removeAccount", { enumerable: true, get: function () { return accounts_1.removeAccount; } });
221
223
  Object.defineProperty(exports, "listAccounts", { enumerable: true, get: function () { return accounts_1.listAccounts; } });
222
224
  Object.defineProperty(exports, "getActiveAccount", { enumerable: true, get: function () { return accounts_1.getActiveAccount; } });
225
+ Object.defineProperty(exports, "resolveActiveClaudeAccount", { enumerable: true, get: function () { return accounts_1.resolveActiveClaudeAccount; } });
223
226
  Object.defineProperty(exports, "isMultiAccountEnabled", { enumerable: true, get: function () { return accounts_1.isMultiAccountEnabled; } });
224
227
  var accountManager_1 = require("./accountManager");
225
228
  Object.defineProperty(exports, "beginAccountLogin", { enumerable: true, get: function () { return accountManager_1.beginAccountLogin; } });
@@ -255,6 +258,7 @@ Object.defineProperty(exports, "getCodexMonitoringHomes", { enumerable: true, ge
255
258
  Object.defineProperty(exports, "getSystemCodexHome", { enumerable: true, get: function () { return codexProfiles_1.getSystemCodexHome; } });
256
259
  Object.defineProperty(exports, "listCodexAccounts", { enumerable: true, get: function () { return codexProfiles_1.listCodexAccounts; } });
257
260
  Object.defineProperty(exports, "getActiveCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.getActiveCodexAccount; } });
261
+ Object.defineProperty(exports, "resolveActiveCodexAccount", { enumerable: true, get: function () { return codexProfiles_1.resolveActiveCodexAccount; } });
258
262
  Object.defineProperty(exports, "resolveSidekickCodexHome", { enumerable: true, get: function () { return codexProfiles_1.resolveSidekickCodexHome; } });
259
263
  Object.defineProperty(exports, "getCodexExecutionEnv", { enumerable: true, get: function () { return codexProfiles_1.getCodexExecutionEnv; } });
260
264
  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
  }
@@ -43,6 +43,7 @@ export declare class CodexRolloutParser {
43
43
  private lastTokenUsage;
44
44
  private modelContextWindow;
45
45
  private lastRateLimits;
46
+ private hasAggregateRateLimits;
46
47
  private inPlanMode;
47
48
  private emittedToolUseIds;
48
49
  private patchExpandedToolUseIds;
@@ -16,6 +16,7 @@ exports.CodexRolloutParser = void 0;
16
16
  exports.normalizeCodexToolName = normalizeCodexToolName;
17
17
  exports.normalizeCodexToolInput = normalizeCodexToolInput;
18
18
  exports.extractPatchFilePaths = extractPatchFilePaths;
19
+ const codex_1 = require("../types/codex");
19
20
  const openCodeParser_1 = require("./openCodeParser");
20
21
  function normalizeRateLimits(rateLimits) {
21
22
  if (!rateLimits?.primary && !rateLimits?.secondary)
@@ -126,6 +127,7 @@ class CodexRolloutParser {
126
127
  lastTokenUsage = null;
127
128
  modelContextWindow = null;
128
129
  lastRateLimits = null;
130
+ hasAggregateRateLimits = false;
129
131
  inPlanMode = false;
130
132
  emittedToolUseIds = new Set();
131
133
  patchExpandedToolUseIds = new Map();
@@ -177,6 +179,7 @@ class CodexRolloutParser {
177
179
  this.lastTokenUsage = null;
178
180
  this.modelContextWindow = null;
179
181
  this.lastRateLimits = null;
182
+ this.hasAggregateRateLimits = false;
180
183
  this.inPlanMode = false;
181
184
  this.emittedToolUseIds.clear();
182
185
  this.patchExpandedToolUseIds.clear();
@@ -493,9 +496,19 @@ class CodexRolloutParser {
493
496
  if (e.info?.model_context_window) {
494
497
  this.modelContextWindow = e.info.model_context_window;
495
498
  }
496
- // Store rate_limits if provided (subscription quota data)
499
+ // Store rate_limits if provided (subscription quota data). Codex interleaves
500
+ // multiple families (aggregate "codex" plus per-model ones like codex_bengalfox);
501
+ // keep the latest aggregate sample so a trailing model-specific event at 0% does
502
+ // not mask the plan quota. Fall back to a non-aggregate sample only until an
503
+ // aggregate one has been seen.
497
504
  if (e.rate_limits) {
498
- this.lastRateLimits = e.rate_limits;
505
+ if ((0, codex_1.isAggregateCodexLimit)(e.rate_limits.limit_id)) {
506
+ this.lastRateLimits = e.rate_limits;
507
+ this.hasAggregateRateLimits = true;
508
+ }
509
+ else if (!this.hasAggregateRateLimits) {
510
+ this.lastRateLimits = e.rate_limits;
511
+ }
499
512
  }
500
513
  // Usage data is nested under info.last_token_usage (info can be null)
501
514
  const usage = e.info?.last_token_usage || e.info?.total_token_usage;
@@ -39,6 +39,7 @@ const crypto = __importStar(require("crypto"));
39
39
  const fs = __importStar(require("fs"));
40
40
  const path = __importStar(require("path"));
41
41
  const paths_1 = require("./paths");
42
+ const codex_1 = require("./types/codex");
42
43
  function getQuotaSnapshotPath() {
43
44
  return path.join((0, paths_1.getConfigDir)(), 'quota-snapshots.json');
44
45
  }
@@ -94,6 +95,15 @@ function windowResetMs(value) {
94
95
  // Preserve the best-known same-window snapshot while still allowing lower
95
96
  // utilization after Codex advances to a newer reset window.
96
97
  function shouldKeepExistingSnapshot(existing, next) {
98
+ // Family rank first (Codex): the aggregate plan-quota family ("codex") must never be
99
+ // replaced or blocked by a model/feature-specific family (e.g. codex_bengalfox), whose
100
+ // later-resetting 0% window would otherwise win the reset-window comparison below. This
101
+ // is a no-op for providers whose limitId is consistent across samples (Claude: absent;
102
+ // z.ai: zai-*), where both sides share the same aggregate-ness.
103
+ const existingAggregate = (0, codex_1.isAggregateCodexLimit)(existing.limitId);
104
+ const nextAggregate = (0, codex_1.isAggregateCodexLimit)(next.limitId);
105
+ if (existingAggregate !== nextAggregate)
106
+ return existingAggregate;
97
107
  const existingPrimaryReset = windowResetMs(existing.fiveHour.resetsAt);
98
108
  const nextPrimaryReset = windowResetMs(next.fiveHour.resetsAt);
99
109
  if (existingPrimaryReset !== nextPrimaryReset)
@@ -8,9 +8,9 @@ import { z } from 'zod';
8
8
  export declare const assistantTurnEventTypeSchema: z.ZodEnum<{
9
9
  status: "status";
10
10
  error: "error";
11
+ text: "text";
11
12
  tool_use: "tool_use";
12
13
  tool_result: "tool_result";
13
- text: "text";
14
14
  progress: "progress";
15
15
  thinking: "thinking";
16
16
  delta: "delta";
@@ -19,9 +19,9 @@ export declare const assistantTurnEventSchema: z.ZodObject<{
19
19
  eventType: z.ZodEnum<{
20
20
  status: "status";
21
21
  error: "error";
22
+ text: "text";
22
23
  tool_use: "tool_use";
23
24
  tool_result: "tool_result";
24
- text: "text";
25
25
  progress: "progress";
26
26
  thinking: "thinking";
27
27
  delta: "delta";
@@ -41,10 +41,10 @@ export declare const sessionEventSchema: z.ZodObject<{
41
41
  type: z.ZodEnum<{
42
42
  user: "user";
43
43
  assistant: "assistant";
44
+ system: "system";
44
45
  tool_use: "tool_use";
45
46
  tool_result: "tool_result";
46
47
  summary: "summary";
47
- system: "system";
48
48
  }>;
49
49
  message: z.ZodObject<{
50
50
  role: z.ZodString;
@@ -192,6 +192,15 @@ export interface CodexRateLimits {
192
192
  plan_type?: string;
193
193
  rate_limit_reached_type?: string;
194
194
  }
195
+ /**
196
+ * Codex reports several rate-limit families per session, keyed by `limit_id`.
197
+ * The aggregate plan quota — what the usage API surfaces — is `limit_id: "codex"`
198
+ * (or absent on older payloads). Model/feature-specific families (e.g.
199
+ * `codex_bengalfox`, `premium`) report their own windows and can read 0% while the
200
+ * plan is heavily used, so they must never mask the aggregate in the standard
201
+ * quota view. Returns true only for the aggregate family.
202
+ */
203
+ export declare function isAggregateCodexLimit(limitId?: string | null): boolean;
195
204
  export interface CodexTokenUsage {
196
205
  input_tokens: number;
197
206
  cached_input_tokens?: number;
@@ -12,3 +12,15 @@
12
12
  * @module types/codex
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.isAggregateCodexLimit = isAggregateCodexLimit;
16
+ /**
17
+ * Codex reports several rate-limit families per session, keyed by `limit_id`.
18
+ * The aggregate plan quota — what the usage API surfaces — is `limit_id: "codex"`
19
+ * (or absent on older payloads). Model/feature-specific families (e.g.
20
+ * `codex_bengalfox`, `premium`) report their own windows and can read 0% while the
21
+ * plan is heavily used, so they must never mask the aggregate in the standard
22
+ * quota view. Returns true only for the aggregate family.
23
+ */
24
+ function isAggregateCodexLimit(limitId) {
25
+ return !limitId || limitId === 'codex';
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-shared",
3
- "version": "0.21.3",
3
+ "version": "0.21.5",
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",