sidekick-shared 0.21.4 → 0.21.6

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
@@ -9,6 +9,7 @@ Types, parsers, providers, readers, formatters, aggregation, search, reporting,
9
9
 
10
10
  ## Recent additions
11
11
 
12
+ - **Codex reset credits** — `fetchCodexResetCreditsFromApi()` reads ChatGPT's reset-credit endpoint into a `CodexResetCreditsSnapshot`, and `fetchCodexQuotaFromApi()` attaches it to the returned quota state (preserved across session-sourced snapshot writes).
12
13
  - **Assistant-turn projection** — `segmentAssistantTurn()` / `assistantTurnEventsFromSessionEvents()` segment a provider-normalized turn into a browser-safe Timeline + Process + Answer shape (see [Assistant Turns](#api-overview)).
13
14
  - **Session-context snapshots** — `buildSessionContextSnapshot()` / `calculateSessionContextPressure()` expose layered evidence sources and low/medium/high context pressure.
14
15
  - **Zod boundary schemas** — runtime validation for data crossing process/IPC boundaries, importable fs-free from [`sidekick-shared/schemas`](#supported-import-paths).
@@ -25,34 +26,34 @@ npm install sidekick-shared
25
26
 
26
27
  ## API Overview
27
28
 
28
- | Module | Description |
29
- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
- | **Types** | Session events, OpenCode/Codex format types, persistence schemas (tasks, decisions, notes, plans, historical data) |
31
- | **Paths** | Config directory resolution, project data paths, workspace encoding |
32
- | **Readers** | Read tasks, decisions, notes, history, handoff, and plans from `~/.config/sidekick/` |
33
- | **Providers** | Session provider abstraction with Claude Code, OpenCode, and Codex implementations; auto-detection via filesystem |
34
- | **Parsers** | JSONL event parsing, OpenCode/Codex format normalization, subagent scanning, session path resolution, debug log parsing |
35
- | **Watchers** | Live session file watching with event bridging, plus `createJsonlTail()` for raw incremental JSONL consumers |
36
- | **Formatters** | Display helpers (`formatTokenCount()`, `formatDurationMs()`), tool summary, noise classification, session dump (text/markdown/JSON), event highlighting |
37
- | **Search** | Cross-session full-text search, advanced filtering (substring, fuzzy, regex, date) |
38
- | **Aggregation** | Event aggregation, frequency tracking, activity heatmaps, pattern extraction |
39
- | **Session Context** | Provider-neutral context evidence snapshots (`buildSessionContextSnapshot()`, `calculateSessionContextPressure()`, `createSessionContextProjector()`, `readSessionContextSnapshot()`): layered evidence sources, low/medium/high context pressure, and observed capabilities (tools, MCP servers, permission mode, rate limits) |
40
- | **Assistant Turns** | Browser-safe timeline/process/answer projection for provider-normalized assistant turns (`segmentAssistantTurn()`, `assistantTurnEventsFromSessionEvents()`), including interleaved reasoning, compact tool groups, and Claude `Task` subagent refs without prompt leakage |
41
- | **Report** | Self-contained HTML session report generation |
42
- | **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
43
- | **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows), Codex rate-limit extraction from event streams, and authoritative z.ai Coding Plan quota fetching via `resolveZaiQuota()` / `fetchZaiQuotaFromApi()` (reads z.ai's `api/monitor/usage/quota/limit` endpoint with cached-snapshot fallback). The deprecated observed-traffic z.ai estimator helpers remain exported for backward compatibility only |
44
- | **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
45
- | **Schemas** | Zod schemas for runtime validation of data crossing process/IPC boundaries — JSONL session events (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`), assistant turns, quota, account status, and quota history — plus `extractSessionEvents()` to unwrap `progress`-wrapped events. Also published fs-free via the [`sidekick-shared/schemas`](#supported-import-paths) subpath |
46
- | **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks), plus Node-only session asset extraction (`gatherAssetsForCwd()`, `readClaudeAssets()`, `readCodexAssets()`) for URLs, file paths, commands, and plans |
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
- | **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
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, `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
- | **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
- | **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
- | **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 |
54
- | **Quota Snapshots** | Persistent quota caching per provider/account for offline fallback |
55
- | **Phrases** | Curated humorous phrases for loading/idle states, available as a flat `ALL_PHRASES` array or grouped via `PHRASE_CATEGORIES` for category-aware UI |
29
+ | Module | Description |
30
+ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | **Types** | Session events, OpenCode/Codex format types, persistence schemas (tasks, decisions, notes, plans, historical data) |
32
+ | **Paths** | Config directory resolution, project data paths, workspace encoding |
33
+ | **Readers** | Read tasks, decisions, notes, history, handoff, and plans from `~/.config/sidekick/` |
34
+ | **Providers** | Session provider abstraction with Claude Code, OpenCode, and Codex implementations; auto-detection via filesystem |
35
+ | **Parsers** | JSONL event parsing, OpenCode/Codex format normalization, subagent scanning, session path resolution, debug log parsing |
36
+ | **Watchers** | Live session file watching with event bridging, plus `createJsonlTail()` for raw incremental JSONL consumers |
37
+ | **Formatters** | Display helpers (`formatTokenCount()`, `formatDurationMs()`), tool summary, noise classification, session dump (text/markdown/JSON), event highlighting |
38
+ | **Search** | Cross-session full-text search, advanced filtering (substring, fuzzy, regex, date) |
39
+ | **Aggregation** | Event aggregation, frequency tracking, activity heatmaps, pattern extraction |
40
+ | **Session Context** | Provider-neutral context evidence snapshots (`buildSessionContextSnapshot()`, `calculateSessionContextPressure()`, `createSessionContextProjector()`, `readSessionContextSnapshot()`): layered evidence sources, low/medium/high context pressure, and observed capabilities (tools, MCP servers, permission mode, rate limits) |
41
+ | **Assistant Turns** | Browser-safe timeline/process/answer projection for provider-normalized assistant turns (`segmentAssistantTurn()`, `assistantTurnEventsFromSessionEvents()`), including interleaved reasoning, compact tool groups, and Claude `Task` subagent refs without prompt leakage |
42
+ | **Report** | Self-contained HTML session report generation |
43
+ | **Credentials** | Claude Max OAuth credential reading from `~/.claude/.credentials.json` |
44
+ | **Quota** | Claude Max subscription quota fetching (5-hour and 7-day windows), Codex rate-limit extraction from event streams (plus Codex reset credits via `fetchCodexResetCreditsFromApi()`, attached to quota state by `fetchCodexQuotaFromApi()`), and authoritative z.ai Coding Plan quota fetching via `resolveZaiQuota()` / `fetchZaiQuotaFromApi()` (reads z.ai's `api/monitor/usage/quota/limit` endpoint with cached-snapshot fallback). The deprecated observed-traffic z.ai estimator helpers remain exported for backward compatibility only |
45
+ | **Provider Status** | API health checking via status.claude.com and status.openai.com (indicator, components, incidents) |
46
+ | **Schemas** | Zod schemas for runtime validation of data crossing process/IPC boundaries — JSONL session events (`sessionEventSchema`, `messageUsageSchema`, `sessionMessageSchema`), assistant turns, quota, account status, and quota history — plus `extractSessionEvents()` to unwrap `progress`-wrapped events. Also published fs-free via the [`sidekick-shared/schemas`](#supported-import-paths) subpath |
47
+ | **Extractors** | Pure functions for single-event processing: `extractTokenUsage()`, `extractToolCall()` (top-level `tool_use`), `extractToolCalls()` (assistant content blocks), plus Node-only session asset extraction (`gatherAssetsForCwd()`, `readClaudeAssets()`, `readCodexAssets()`) for URLs, file paths, commands, and plans |
48
+ | **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()`) |
49
+ | **Quota Polling** | `QuotaPoller` class with exponential backoff, active/idle intervals, and cached fallback |
50
+ | **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 |
51
+ | **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 |
52
+ | **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 |
53
+ | **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 |
54
+ | **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 |
55
+ | **Quota Snapshots** | Persistent quota caching per provider/account for offline fallback |
56
+ | **Phrases** | Curated humorous phrases for loading/idle states, available as a flat `ALL_PHRASES` array or grouped via `PHRASE_CATEGORIES` for category-aware UI |
56
57
 
57
58
  ## Supported import paths
58
59
 
@@ -1,4 +1,4 @@
1
- import type { QuotaState } from './quota';
1
+ import type { CodexResetCreditsSnapshot, QuotaState } from './quota';
2
2
  import { CodexProvider } from './providers/codex';
3
3
  import type { ProviderQuotaState } from './providerQuota';
4
4
  import type { SavedAccountProfile } from './accountRegistry';
@@ -27,8 +27,10 @@ export interface CodexQuotaResolveOptions {
27
27
  export interface CodexQuotaApiOptions {
28
28
  codexHome?: string;
29
29
  accessToken?: string;
30
+ accountId?: string;
30
31
  fetchImpl?: typeof fetch;
31
32
  usageUrl?: string;
33
+ resetCreditsUrl?: string;
32
34
  capturedAt?: string;
33
35
  }
34
36
  export declare function quotaFromCodexRateLimits(rateLimits: CodexRateLimits | null | undefined, source?: 'api' | 'session' | 'cache', capturedAt?: string): QuotaState | null;
@@ -40,4 +42,5 @@ export declare function readLatestCodexQuotaFromRollouts(sessionPaths: string[],
40
42
  export declare function resolveCodexQuotaFromLocalSources(options?: CodexQuotaResolveOptions): ProviderQuotaState<'codex'> | null;
41
43
  export declare function resolveCodexQuota(options?: CodexQuotaResolveOptions): Promise<ProviderQuotaState<'codex'>>;
42
44
  export declare function fetchCodexQuotaFromApi(options?: CodexQuotaApiOptions): Promise<QuotaState>;
45
+ export declare function fetchCodexResetCreditsFromApi(options?: CodexQuotaApiOptions): Promise<CodexResetCreditsSnapshot | undefined>;
43
46
  export {};
@@ -38,15 +38,18 @@ exports.readLatestCodexQuotaFromRollouts = readLatestCodexQuotaFromRollouts;
38
38
  exports.resolveCodexQuotaFromLocalSources = resolveCodexQuotaFromLocalSources;
39
39
  exports.resolveCodexQuota = resolveCodexQuota;
40
40
  exports.fetchCodexQuotaFromApi = fetchCodexQuotaFromApi;
41
+ exports.fetchCodexResetCreditsFromApi = fetchCodexResetCreditsFromApi;
41
42
  const fs = __importStar(require("fs"));
42
43
  const path = __importStar(require("path"));
43
44
  const codexProfiles_1 = require("./codexProfiles");
44
45
  const quotaSnapshots_1 = require("./quotaSnapshots");
45
46
  const quota_1 = require("./quota");
46
47
  const codex_1 = require("./providers/codex");
48
+ const codex_2 = require("./types/codex");
47
49
  const DEFAULT_TAIL_BYTES = 2 * 1024 * 1024;
48
50
  const DEFAULT_MAX_SESSION_FILES = 50;
49
51
  const CHATGPT_USAGE_URL = 'https://chatgpt.com/backend-api/wham/usage';
52
+ const CHATGPT_RESET_CREDITS_URL = 'https://chatgpt.com/backend-api/wham/rate-limit-reset-credits';
50
53
  function timestampMs(value, fallbackMs = 0) {
51
54
  if (!value)
52
55
  return fallbackMs;
@@ -54,11 +57,19 @@ function timestampMs(value, fallbackMs = 0) {
54
57
  return Number.isFinite(ms) ? ms : fallbackMs;
55
58
  }
56
59
  // 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.
60
+ // across recent sessions. Prefer the aggregate plan family ("codex") over
61
+ // model/feature-specific families (which can read 0% while the plan is busy),
62
+ // then newer reset windows, then the highest observed same-window utilization so
63
+ // local fallbacks do not under-report quota usage.
59
64
  function isPreferredQuotaHit(candidate, current) {
60
65
  if (!current)
61
66
  return true;
67
+ // Family rank first: an aggregate hit always outranks a model-specific one, so a
68
+ // freshly-used per-model family (e.g. codex_bengalfox at 0%) can never mask the plan quota.
69
+ const candidateAggregate = (0, codex_2.isAggregateCodexLimit)(candidate.quota.limitId);
70
+ const currentAggregate = (0, codex_2.isAggregateCodexLimit)(current.quota.limitId);
71
+ if (candidateAggregate !== currentAggregate)
72
+ return candidateAggregate;
62
73
  const candidatePrimaryReset = timestampMs(candidate.quota.fiveHour.resetsAt);
63
74
  const currentPrimaryReset = timestampMs(current.quota.fiveHour.resetsAt);
64
75
  if (candidatePrimaryReset !== currentPrimaryReset)
@@ -140,6 +151,37 @@ function normalizeCredits(credits) {
140
151
  balance: credits.balance ?? undefined,
141
152
  };
142
153
  }
154
+ function normalizeCount(value) {
155
+ return typeof value === 'number' && Number.isFinite(value) ? value : 0;
156
+ }
157
+ function normalizeResetCreditsPayload(payload, capturedAt) {
158
+ const credits = Array.isArray(payload.credits)
159
+ ? payload.credits.flatMap((credit) => {
160
+ const status = firstString(credit.status);
161
+ const expiresAt = firstString(credit.expires_at);
162
+ if (!status || !expiresAt)
163
+ return [];
164
+ return [
165
+ {
166
+ title: firstString(credit.title),
167
+ status,
168
+ resetType: firstString(credit.reset_type),
169
+ expiresAt,
170
+ grantedAt: firstString(credit.granted_at),
171
+ },
172
+ ];
173
+ })
174
+ : [];
175
+ return {
176
+ availableCount: normalizeCount(payload.available_count),
177
+ totalEarnedCount: typeof payload.total_earned_count === 'number' && Number.isFinite(payload.total_earned_count)
178
+ ? payload.total_earned_count
179
+ : undefined,
180
+ credits,
181
+ source: 'api',
182
+ capturedAt,
183
+ };
184
+ }
143
185
  function normalizeRateLimitReachedType(value) {
144
186
  if (typeof value === 'string')
145
187
  return value;
@@ -354,7 +396,10 @@ async function resolveCodexQuota(options = {}) {
354
396
  }
355
397
  async function fetchCodexQuotaFromApi(options = {}) {
356
398
  const capturedAt = options.capturedAt ?? new Date().toISOString();
357
- const accessToken = options.accessToken ?? readCodexAccessToken(options.codexHome ?? (0, codexProfiles_1.resolveSidekickCodexHome)());
399
+ const auth = options.accessToken != null
400
+ ? { accessToken: options.accessToken, accountId: options.accountId }
401
+ : readCodexAuth(options.codexHome ?? (0, codexProfiles_1.resolveSidekickCodexHome)());
402
+ const accessToken = auth?.accessToken;
358
403
  if (!accessToken) {
359
404
  return {
360
405
  fiveHour: { utilization: 0, resetsAt: '' },
@@ -418,6 +463,15 @@ async function fetchCodexQuotaFromApi(options = {}) {
418
463
  sevenDayLabel: 'Secondary',
419
464
  };
420
465
  }
466
+ const resetCredits = await fetchCodexResetCreditsFromApi({
467
+ ...options,
468
+ accessToken,
469
+ accountId: auth?.accountId,
470
+ capturedAt,
471
+ });
472
+ if (resetCredits) {
473
+ quota.resetCredits = resetCredits;
474
+ }
421
475
  return quota;
422
476
  }
423
477
  catch {
@@ -435,12 +489,49 @@ async function fetchCodexQuotaFromApi(options = {}) {
435
489
  };
436
490
  }
437
491
  }
438
- function readCodexAccessToken(codexHome) {
492
+ async function fetchCodexResetCreditsFromApi(options = {}) {
493
+ const capturedAt = options.capturedAt ?? new Date().toISOString();
494
+ const auth = options.accessToken != null
495
+ ? { accessToken: options.accessToken, accountId: options.accountId }
496
+ : readCodexAuth(options.codexHome ?? (0, codexProfiles_1.resolveSidekickCodexHome)());
497
+ const accessToken = auth?.accessToken;
498
+ if (!accessToken)
499
+ return undefined;
500
+ try {
501
+ const fetchImpl = options.fetchImpl ?? fetch;
502
+ const headers = {
503
+ Authorization: `Bearer ${accessToken}`,
504
+ originator: 'Codex Desktop',
505
+ 'OAI-Product-Sku': 'CODEX',
506
+ Accept: 'application/json',
507
+ };
508
+ if (auth?.accountId) {
509
+ headers['ChatGPT-Account-ID'] = auth.accountId;
510
+ }
511
+ const response = await fetchImpl(options.resetCreditsUrl ?? CHATGPT_RESET_CREDITS_URL, {
512
+ method: 'GET',
513
+ headers,
514
+ });
515
+ if (!response.ok)
516
+ return undefined;
517
+ return normalizeResetCreditsPayload((await response.json()), capturedAt);
518
+ }
519
+ catch {
520
+ return undefined;
521
+ }
522
+ }
523
+ function readCodexAuth(codexHome) {
439
524
  try {
440
525
  const parsed = JSON.parse(fs.readFileSync(path.join(codexHome, 'auth.json'), 'utf8'));
441
526
  if (parsed.OPENAI_API_KEY || parsed.auth_mode === 'api_key')
442
527
  return null;
443
- return parsed.tokens?.access_token || null;
528
+ const accessToken = parsed.tokens?.access_token;
529
+ if (!accessToken)
530
+ return null;
531
+ return {
532
+ accessToken,
533
+ accountId: parsed.tokens?.account_id,
534
+ };
444
535
  }
445
536
  catch {
446
537
  return null;
@@ -464,7 +555,11 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
464
555
  const firstNewline = text.indexOf('\n');
465
556
  text = firstNewline >= 0 ? text.slice(firstNewline + 1) : '';
466
557
  }
558
+ // Scan from the end for the latest aggregate ("codex") rate-limit sample, which
559
+ // is what the plan quota represents. Keep the latest sample of any family as a
560
+ // fallback so a model-only session still surfaces something.
467
561
  const lines = text.split('\n');
562
+ let fallback = null;
468
563
  for (let i = lines.length - 1; i >= 0; i--) {
469
564
  const line = lines[i].trim();
470
565
  if (!line || !line.includes('rate_limits'))
@@ -474,13 +569,20 @@ function readLatestQuotaFromRollout(sessionPath, maxTailBytes, source) {
474
569
  if (parsed.type !== 'event_msg' || parsed.payload?.type !== 'token_count')
475
570
  continue;
476
571
  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() };
572
+ if (!quota)
573
+ continue;
574
+ const hit = { quota, filePath: sessionPath, mtimeMs: stat.mtime.getTime() };
575
+ if ((0, codex_2.isAggregateCodexLimit)(quota.limitId))
576
+ return hit;
577
+ if (!fallback)
578
+ fallback = hit;
479
579
  }
480
580
  catch {
481
581
  // Ignore malformed or partial lines.
482
582
  }
483
583
  }
584
+ if (fallback)
585
+ return fallback;
484
586
  }
485
587
  catch {
486
588
  return null;
@@ -208,26 +208,31 @@ class CodexQuotaWatcher {
208
208
  return;
209
209
  }
210
210
  const account = this.getActiveAccount();
211
+ const cached = account ? this.readSnapshot('codex', account.id) : null;
212
+ const liveQuotaWithResetCredits = {
213
+ ...liveQuota,
214
+ resetCredits: liveQuota.resetCredits ?? cached?.resetCredits,
215
+ };
211
216
  if (account) {
212
- this.writeSnapshot('codex', account.id, liveQuota);
217
+ this.writeSnapshot('codex', account.id, liveQuotaWithResetCredits);
213
218
  if (this.workspaceId) {
214
219
  const sample = {
215
- timestamp: liveQuota.capturedAt ?? new Date().toISOString(),
220
+ timestamp: liveQuotaWithResetCredits.capturedAt ?? new Date().toISOString(),
216
221
  runtimeProvider: 'codex',
217
222
  providerId: account.id,
218
223
  workspaceId: this.workspaceId,
219
224
  fiveHour: {
220
- utilization: liveQuota.fiveHour.utilization,
221
- resetsAt: liveQuota.fiveHour.resetsAt,
225
+ utilization: liveQuotaWithResetCredits.fiveHour.utilization,
226
+ resetsAt: liveQuotaWithResetCredits.fiveHour.resetsAt,
222
227
  },
223
228
  sevenDay: {
224
- utilization: liveQuota.sevenDay.utilization,
225
- resetsAt: liveQuota.sevenDay.resetsAt,
229
+ utilization: liveQuotaWithResetCredits.sevenDay.utilization,
230
+ resetsAt: liveQuotaWithResetCredits.sevenDay.resetsAt,
226
231
  },
227
- available: liveQuota.available,
228
- error: liveQuota.error,
229
- source: liveQuota.source,
230
- stale: liveQuota.stale,
232
+ available: liveQuotaWithResetCredits.available,
233
+ error: liveQuotaWithResetCredits.error,
234
+ source: liveQuotaWithResetCredits.source,
235
+ stale: liveQuotaWithResetCredits.stale,
231
236
  };
232
237
  try {
233
238
  const result = this.appendHistorySample(sample);
@@ -243,7 +248,7 @@ class CodexQuotaWatcher {
243
248
  }
244
249
  }
245
250
  this.emitState(enrichQuotaState({
246
- ...liveQuota,
251
+ ...liveQuotaWithResetCredits,
247
252
  runtimeProvider: 'codex',
248
253
  providerId: 'codex',
249
254
  }, account));
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';
@@ -109,7 +110,7 @@ export { setTerminalActiveProfile, installShellHook, uninstallShellHook, isShell
109
110
  export { DEFAULT_AUTO_SWITCH_CONFIG, decideAutoSwitch, AutoSwitchController } from './autoSwitch';
110
111
  export type { AutoSwitchActiveAccount, AutoSwitchCandidate, AutoSwitchConfig, AutoSwitchControllerOptions, AutoSwitchDecision, AutoSwitchTransitionEvent, } from './autoSwitch';
111
112
  export { FIVE_HOUR_WINDOW_MS, SEVEN_DAY_WINDOW_MS, fetchQuota, projectQuotaWindow, withQuotaProjections, } from './quota';
112
- export type { QuotaProjectionInput, QuotaWindow, QuotaState } from './quota';
113
+ export type { CodexResetCredit, CodexResetCreditsSnapshot, QuotaProjectionInput, QuotaWindow, QuotaState, } from './quota';
113
114
  export { describeQuotaFailure } from './quotaPresentation';
114
115
  export type { QuotaFailureDescriptor } from './quotaPresentation';
115
116
  export { QuotaPoller } from './quotaPoller';
@@ -117,7 +118,7 @@ export type { QuotaPollerOptions } from './quotaPoller';
117
118
  export { readQuotaSnapshot, writeQuotaSnapshot } from './quotaSnapshots';
118
119
  export { appendQuotaHistorySample, readQuotaHistoryRange, readQuotaHistoryDailyBuckets, pruneQuotaHistory, getWorkspaceIdFromPath, } from './quotaHistory';
119
120
  export type { QuotaHistorySample, QuotaHistoryAppendOptions, QuotaHistoryRangeOptions, QuotaHistoryDailyBucket, QuotaHistoryRuntimeProvider, } from './quotaHistory';
120
- export { fetchCodexQuotaFromApi, quotaFromCodexRateLimits, readLatestCodexQuotaFromRollouts, resolveCodexQuota, resolveCodexQuotaFromLocalSources, } from './codexQuota';
121
+ export { fetchCodexResetCreditsFromApi, fetchCodexQuotaFromApi, quotaFromCodexRateLimits, readLatestCodexQuotaFromRollouts, resolveCodexQuota, resolveCodexQuotaFromLocalSources, } from './codexQuota';
121
122
  export type { CodexQuotaApiOptions, CodexQuotaCreditsSnapshot, CodexQuotaResolveOptions, CodexQuotaResolveSource, } from './codexQuota';
122
123
  export { CodexQuotaWatcher } from './codexQuotaWatcher';
123
124
  export type { CodexQuotaWatcherOptions } from './codexQuotaWatcher';
@@ -145,7 +146,7 @@ export { readCodexAssets, codexSessions } from './extractors/sources/codexAssets
145
146
  export { gatherAssetsForCwd } from './extractors/gatherAssets';
146
147
  export type { GatherAssetsOptions, GatherAssetsResult } from './extractors/gatherAssets';
147
148
  export { messageUsageSchema, sessionMessageSchema, sessionEventSchema, permissionModeSchema, extractSessionEvents, } from './schemas/sessionEvent';
148
- export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProviderIdSchema, quotaSourceSchema, peakHoursStateSchema, quotaFailureDescriptorSchema, runtimeQuotaProviderSchema, providerQuotaStateSchema, claudeProviderQuotaStateSchema, codexProviderQuotaStateSchema, zaiProviderQuotaStateSchema, providerQuotaMapSchema, } from './schemas/quota';
149
+ export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProviderIdSchema, quotaSourceSchema, codexResetCreditSchema, codexResetCreditsSnapshotSchema, peakHoursStateSchema, quotaFailureDescriptorSchema, runtimeQuotaProviderSchema, providerQuotaStateSchema, claudeProviderQuotaStateSchema, codexProviderQuotaStateSchema, zaiProviderQuotaStateSchema, providerQuotaMapSchema, } from './schemas/quota';
149
150
  export { quotaHistoryRuntimeProviderSchema, quotaHistorySampleSchema, quotaHistoryDailyBucketSchema, } from './schemas/quotaHistory';
150
151
  export { activeProviderAccountStatusSchema, activeAccountStatusSchema, } from './schemas/accountStatus';
151
152
  export { accountProviderIdSchema, accountManagerResultSchema, beginAccountLoginResultSchema, accountLoginStatusSchema, accountEntrySchema, savedAccountProfileSchema, listAllAccountsResultSchema, } from './schemas/accountManager';
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.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;
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.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.fetchCodexResetCreditsFromApi = 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.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 = exports.readLatestCodexQuotaFromRollouts = 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.codexResetCreditsSnapshotSchema = exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = 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; } });
@@ -295,6 +297,7 @@ Object.defineProperty(exports, "readQuotaHistoryDailyBuckets", { enumerable: tru
295
297
  Object.defineProperty(exports, "pruneQuotaHistory", { enumerable: true, get: function () { return quotaHistory_1.pruneQuotaHistory; } });
296
298
  Object.defineProperty(exports, "getWorkspaceIdFromPath", { enumerable: true, get: function () { return quotaHistory_1.getWorkspaceIdFromPath; } });
297
299
  var codexQuota_1 = require("./codexQuota");
300
+ Object.defineProperty(exports, "fetchCodexResetCreditsFromApi", { enumerable: true, get: function () { return codexQuota_1.fetchCodexResetCreditsFromApi; } });
298
301
  Object.defineProperty(exports, "fetchCodexQuotaFromApi", { enumerable: true, get: function () { return codexQuota_1.fetchCodexQuotaFromApi; } });
299
302
  Object.defineProperty(exports, "quotaFromCodexRateLimits", { enumerable: true, get: function () { return codexQuota_1.quotaFromCodexRateLimits; } });
300
303
  Object.defineProperty(exports, "readLatestCodexQuotaFromRollouts", { enumerable: true, get: function () { return codexQuota_1.readLatestCodexQuotaFromRollouts; } });
@@ -382,6 +385,8 @@ Object.defineProperty(exports, "quotaStateSchema", { enumerable: true, get: func
382
385
  Object.defineProperty(exports, "quotaFailureKindSchema", { enumerable: true, get: function () { return quota_2.quotaFailureKindSchema; } });
383
386
  Object.defineProperty(exports, "quotaProviderIdSchema", { enumerable: true, get: function () { return quota_2.quotaProviderIdSchema; } });
384
387
  Object.defineProperty(exports, "quotaSourceSchema", { enumerable: true, get: function () { return quota_2.quotaSourceSchema; } });
388
+ Object.defineProperty(exports, "codexResetCreditSchema", { enumerable: true, get: function () { return quota_2.codexResetCreditSchema; } });
389
+ Object.defineProperty(exports, "codexResetCreditsSnapshotSchema", { enumerable: true, get: function () { return quota_2.codexResetCreditsSnapshotSchema; } });
385
390
  Object.defineProperty(exports, "peakHoursStateSchema", { enumerable: true, get: function () { return quota_2.peakHoursStateSchema; } });
386
391
  Object.defineProperty(exports, "quotaFailureDescriptorSchema", { enumerable: true, get: function () { return quota_2.quotaFailureDescriptorSchema; } });
387
392
  Object.defineProperty(exports, "runtimeQuotaProviderSchema", { enumerable: true, get: function () { return quota_2.runtimeQuotaProviderSchema; } });
@@ -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;
package/dist/quota.d.ts CHANGED
@@ -10,6 +10,20 @@ export interface QuotaWindow {
10
10
  /** ISO timestamp when the window resets */
11
11
  resetsAt: string;
12
12
  }
13
+ export interface CodexResetCredit {
14
+ title?: string;
15
+ status: string;
16
+ resetType?: string;
17
+ expiresAt: string;
18
+ grantedAt?: string;
19
+ }
20
+ export interface CodexResetCreditsSnapshot {
21
+ availableCount: number;
22
+ totalEarnedCount?: number;
23
+ credits: CodexResetCredit[];
24
+ source?: 'api' | 'cache';
25
+ capturedAt?: string;
26
+ }
13
27
  export interface QuotaState {
14
28
  fiveHour: QuotaWindow;
15
29
  sevenDay: QuotaWindow;
@@ -45,6 +59,8 @@ export interface QuotaState {
45
59
  limitName?: string;
46
60
  /** Provider-specific credits snapshot */
47
61
  credits?: unknown;
62
+ /** Codex reset credits snapshot from the ChatGPT reset-credit endpoint */
63
+ resetCredits?: CodexResetCreditsSnapshot;
48
64
  /** Provider-specific plan type */
49
65
  planType?: string;
50
66
  /** Provider-specific rate-limit reached reason */
@@ -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)
@@ -110,14 +120,16 @@ function shouldKeepExistingSnapshot(existing, next) {
110
120
  }
111
121
  function writeQuotaSnapshot(providerId, accountId, quota) {
112
122
  const store = readStore();
123
+ const index = store.snapshots.findIndex((item) => item.providerId === providerId && item.accountId === accountId);
124
+ const existingQuota = index >= 0 ? store.snapshots[index].quota : undefined;
113
125
  const snapshot = {
114
126
  ...quota,
115
127
  providerId,
116
128
  capturedAt: quota.capturedAt ?? new Date().toISOString(),
117
129
  source: quota.source ?? 'session',
118
130
  stale: false,
131
+ resetCredits: quota.resetCredits ?? (providerId === 'codex' ? existingQuota?.resetCredits : undefined),
119
132
  };
120
- const index = store.snapshots.findIndex((item) => item.providerId === providerId && item.accountId === accountId);
121
133
  if (index >= 0 && shouldKeepExistingSnapshot(store.snapshots[index].quota, snapshot)) {
122
134
  return;
123
135
  }
@@ -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";
@@ -12,8 +12,8 @@
12
12
  */
13
13
  export { messageUsageSchema, sessionMessageSchema, sessionEventSchema, permissionModeSchema, extractSessionEvents, } from './sessionEvent';
14
14
  export type { MessageUsage, SessionMessage, SessionEvent, PermissionMode, } from '../types/sessionEvent';
15
- export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProviderIdSchema, quotaSourceSchema, peakHoursStateSchema, quotaFailureDescriptorSchema, runtimeQuotaProviderSchema, providerQuotaStateSchema, claudeProviderQuotaStateSchema, codexProviderQuotaStateSchema, zaiProviderQuotaStateSchema, providerQuotaMapSchema, } from './quota';
16
- export type { QuotaWindow, QuotaState } from '../quota';
15
+ export { quotaWindowSchema, quotaStateSchema, quotaFailureKindSchema, quotaProviderIdSchema, quotaSourceSchema, codexResetCreditSchema, codexResetCreditsSnapshotSchema, peakHoursStateSchema, quotaFailureDescriptorSchema, runtimeQuotaProviderSchema, providerQuotaStateSchema, claudeProviderQuotaStateSchema, codexProviderQuotaStateSchema, zaiProviderQuotaStateSchema, providerQuotaMapSchema, } from './quota';
16
+ export type { CodexResetCredit, CodexResetCreditsSnapshot, QuotaWindow, QuotaState, } from '../quota';
17
17
  export type { PeakHoursState } from '../peakHours';
18
18
  export type { QuotaFailureDescriptor } from '../quotaPresentation';
19
19
  export type { ProviderQuotaState, ProviderQuotaMap, RuntimeQuotaProvider } from '../providerQuota';
@@ -12,7 +12,7 @@
12
12
  * subpath is self-sufficient for boundary-validation modules.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- 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 = exports.messageUsageSchema = void 0;
15
+ 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.codexResetCreditsSnapshotSchema = exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = void 0;
16
16
  var sessionEvent_1 = require("./sessionEvent");
17
17
  Object.defineProperty(exports, "messageUsageSchema", { enumerable: true, get: function () { return sessionEvent_1.messageUsageSchema; } });
18
18
  Object.defineProperty(exports, "sessionMessageSchema", { enumerable: true, get: function () { return sessionEvent_1.sessionMessageSchema; } });
@@ -25,6 +25,8 @@ Object.defineProperty(exports, "quotaStateSchema", { enumerable: true, get: func
25
25
  Object.defineProperty(exports, "quotaFailureKindSchema", { enumerable: true, get: function () { return quota_1.quotaFailureKindSchema; } });
26
26
  Object.defineProperty(exports, "quotaProviderIdSchema", { enumerable: true, get: function () { return quota_1.quotaProviderIdSchema; } });
27
27
  Object.defineProperty(exports, "quotaSourceSchema", { enumerable: true, get: function () { return quota_1.quotaSourceSchema; } });
28
+ Object.defineProperty(exports, "codexResetCreditSchema", { enumerable: true, get: function () { return quota_1.codexResetCreditSchema; } });
29
+ Object.defineProperty(exports, "codexResetCreditsSnapshotSchema", { enumerable: true, get: function () { return quota_1.codexResetCreditsSnapshotSchema; } });
28
30
  Object.defineProperty(exports, "peakHoursStateSchema", { enumerable: true, get: function () { return quota_1.peakHoursStateSchema; } });
29
31
  Object.defineProperty(exports, "quotaFailureDescriptorSchema", { enumerable: true, get: function () { return quota_1.quotaFailureDescriptorSchema; } });
30
32
  Object.defineProperty(exports, "runtimeQuotaProviderSchema", { enumerable: true, get: function () { return quota_1.runtimeQuotaProviderSchema; } });
@@ -27,9 +27,32 @@ export declare const quotaProviderIdSchema: z.ZodEnum<{
27
27
  }>;
28
28
  export declare const quotaSourceSchema: z.ZodEnum<{
29
29
  api: "api";
30
- session: "session";
31
30
  cache: "cache";
31
+ session: "session";
32
32
  }>;
33
+ export declare const codexResetCreditSchema: z.ZodObject<{
34
+ title: z.ZodOptional<z.ZodString>;
35
+ status: z.ZodString;
36
+ resetType: z.ZodOptional<z.ZodString>;
37
+ expiresAt: z.ZodString;
38
+ grantedAt: z.ZodOptional<z.ZodString>;
39
+ }, z.core.$strip>;
40
+ export declare const codexResetCreditsSnapshotSchema: z.ZodObject<{
41
+ availableCount: z.ZodNumber;
42
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
43
+ credits: z.ZodArray<z.ZodObject<{
44
+ title: z.ZodOptional<z.ZodString>;
45
+ status: z.ZodString;
46
+ resetType: z.ZodOptional<z.ZodString>;
47
+ expiresAt: z.ZodString;
48
+ grantedAt: z.ZodOptional<z.ZodString>;
49
+ }, z.core.$strip>>;
50
+ source: z.ZodOptional<z.ZodEnum<{
51
+ api: "api";
52
+ cache: "cache";
53
+ }>>;
54
+ capturedAt: z.ZodOptional<z.ZodString>;
55
+ }, z.core.$strip>;
33
56
  export declare const quotaStateSchema: z.ZodObject<{
34
57
  fiveHour: z.ZodObject<{
35
58
  utilization: z.ZodNumber;
@@ -59,8 +82,8 @@ export declare const quotaStateSchema: z.ZodObject<{
59
82
  }>>;
60
83
  source: z.ZodOptional<z.ZodEnum<{
61
84
  api: "api";
62
- session: "session";
63
85
  cache: "cache";
86
+ session: "session";
64
87
  }>>;
65
88
  capturedAt: z.ZodOptional<z.ZodString>;
66
89
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -69,6 +92,22 @@ export declare const quotaStateSchema: z.ZodObject<{
69
92
  limitId: z.ZodOptional<z.ZodString>;
70
93
  limitName: z.ZodOptional<z.ZodString>;
71
94
  credits: z.ZodOptional<z.ZodUnknown>;
95
+ resetCredits: z.ZodOptional<z.ZodObject<{
96
+ availableCount: z.ZodNumber;
97
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
98
+ credits: z.ZodArray<z.ZodObject<{
99
+ title: z.ZodOptional<z.ZodString>;
100
+ status: z.ZodString;
101
+ resetType: z.ZodOptional<z.ZodString>;
102
+ expiresAt: z.ZodString;
103
+ grantedAt: z.ZodOptional<z.ZodString>;
104
+ }, z.core.$strip>>;
105
+ source: z.ZodOptional<z.ZodEnum<{
106
+ api: "api";
107
+ cache: "cache";
108
+ }>>;
109
+ capturedAt: z.ZodOptional<z.ZodString>;
110
+ }, z.core.$strip>>;
72
111
  planType: z.ZodOptional<z.ZodString>;
73
112
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
74
113
  }, z.core.$strip>;
@@ -139,8 +178,8 @@ export declare const providerQuotaStateSchema: z.ZodObject<{
139
178
  }>>;
140
179
  source: z.ZodOptional<z.ZodEnum<{
141
180
  api: "api";
142
- session: "session";
143
181
  cache: "cache";
182
+ session: "session";
144
183
  }>>;
145
184
  capturedAt: z.ZodOptional<z.ZodString>;
146
185
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -149,6 +188,22 @@ export declare const providerQuotaStateSchema: z.ZodObject<{
149
188
  limitId: z.ZodOptional<z.ZodString>;
150
189
  limitName: z.ZodOptional<z.ZodString>;
151
190
  credits: z.ZodOptional<z.ZodUnknown>;
191
+ resetCredits: z.ZodOptional<z.ZodObject<{
192
+ availableCount: z.ZodNumber;
193
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
194
+ credits: z.ZodArray<z.ZodObject<{
195
+ title: z.ZodOptional<z.ZodString>;
196
+ status: z.ZodString;
197
+ resetType: z.ZodOptional<z.ZodString>;
198
+ expiresAt: z.ZodString;
199
+ grantedAt: z.ZodOptional<z.ZodString>;
200
+ }, z.core.$strip>>;
201
+ source: z.ZodOptional<z.ZodEnum<{
202
+ api: "api";
203
+ cache: "cache";
204
+ }>>;
205
+ capturedAt: z.ZodOptional<z.ZodString>;
206
+ }, z.core.$strip>>;
152
207
  planType: z.ZodOptional<z.ZodString>;
153
208
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
154
209
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -221,8 +276,8 @@ export declare const claudeProviderQuotaStateSchema: z.ZodObject<{
221
276
  }>>;
222
277
  source: z.ZodOptional<z.ZodEnum<{
223
278
  api: "api";
224
- session: "session";
225
279
  cache: "cache";
280
+ session: "session";
226
281
  }>>;
227
282
  capturedAt: z.ZodOptional<z.ZodString>;
228
283
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -231,6 +286,22 @@ export declare const claudeProviderQuotaStateSchema: z.ZodObject<{
231
286
  limitId: z.ZodOptional<z.ZodString>;
232
287
  limitName: z.ZodOptional<z.ZodString>;
233
288
  credits: z.ZodOptional<z.ZodUnknown>;
289
+ resetCredits: z.ZodOptional<z.ZodObject<{
290
+ availableCount: z.ZodNumber;
291
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
292
+ credits: z.ZodArray<z.ZodObject<{
293
+ title: z.ZodOptional<z.ZodString>;
294
+ status: z.ZodString;
295
+ resetType: z.ZodOptional<z.ZodString>;
296
+ expiresAt: z.ZodString;
297
+ grantedAt: z.ZodOptional<z.ZodString>;
298
+ }, z.core.$strip>>;
299
+ source: z.ZodOptional<z.ZodEnum<{
300
+ api: "api";
301
+ cache: "cache";
302
+ }>>;
303
+ capturedAt: z.ZodOptional<z.ZodString>;
304
+ }, z.core.$strip>>;
234
305
  planType: z.ZodOptional<z.ZodString>;
235
306
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
236
307
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -299,8 +370,8 @@ export declare const codexProviderQuotaStateSchema: z.ZodObject<{
299
370
  }>>;
300
371
  source: z.ZodOptional<z.ZodEnum<{
301
372
  api: "api";
302
- session: "session";
303
373
  cache: "cache";
374
+ session: "session";
304
375
  }>>;
305
376
  capturedAt: z.ZodOptional<z.ZodString>;
306
377
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -309,6 +380,22 @@ export declare const codexProviderQuotaStateSchema: z.ZodObject<{
309
380
  limitId: z.ZodOptional<z.ZodString>;
310
381
  limitName: z.ZodOptional<z.ZodString>;
311
382
  credits: z.ZodOptional<z.ZodUnknown>;
383
+ resetCredits: z.ZodOptional<z.ZodObject<{
384
+ availableCount: z.ZodNumber;
385
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
386
+ credits: z.ZodArray<z.ZodObject<{
387
+ title: z.ZodOptional<z.ZodString>;
388
+ status: z.ZodString;
389
+ resetType: z.ZodOptional<z.ZodString>;
390
+ expiresAt: z.ZodString;
391
+ grantedAt: z.ZodOptional<z.ZodString>;
392
+ }, z.core.$strip>>;
393
+ source: z.ZodOptional<z.ZodEnum<{
394
+ api: "api";
395
+ cache: "cache";
396
+ }>>;
397
+ capturedAt: z.ZodOptional<z.ZodString>;
398
+ }, z.core.$strip>>;
312
399
  planType: z.ZodOptional<z.ZodString>;
313
400
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
314
401
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -377,8 +464,8 @@ export declare const zaiProviderQuotaStateSchema: z.ZodObject<{
377
464
  }>>;
378
465
  source: z.ZodOptional<z.ZodEnum<{
379
466
  api: "api";
380
- session: "session";
381
467
  cache: "cache";
468
+ session: "session";
382
469
  }>>;
383
470
  capturedAt: z.ZodOptional<z.ZodString>;
384
471
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -387,6 +474,22 @@ export declare const zaiProviderQuotaStateSchema: z.ZodObject<{
387
474
  limitId: z.ZodOptional<z.ZodString>;
388
475
  limitName: z.ZodOptional<z.ZodString>;
389
476
  credits: z.ZodOptional<z.ZodUnknown>;
477
+ resetCredits: z.ZodOptional<z.ZodObject<{
478
+ availableCount: z.ZodNumber;
479
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
480
+ credits: z.ZodArray<z.ZodObject<{
481
+ title: z.ZodOptional<z.ZodString>;
482
+ status: z.ZodString;
483
+ resetType: z.ZodOptional<z.ZodString>;
484
+ expiresAt: z.ZodString;
485
+ grantedAt: z.ZodOptional<z.ZodString>;
486
+ }, z.core.$strip>>;
487
+ source: z.ZodOptional<z.ZodEnum<{
488
+ api: "api";
489
+ cache: "cache";
490
+ }>>;
491
+ capturedAt: z.ZodOptional<z.ZodString>;
492
+ }, z.core.$strip>>;
390
493
  planType: z.ZodOptional<z.ZodString>;
391
494
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
392
495
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -456,8 +559,8 @@ export declare const providerQuotaMapSchema: z.ZodObject<{
456
559
  }>>;
457
560
  source: z.ZodOptional<z.ZodEnum<{
458
561
  api: "api";
459
- session: "session";
460
562
  cache: "cache";
563
+ session: "session";
461
564
  }>>;
462
565
  capturedAt: z.ZodOptional<z.ZodString>;
463
566
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -466,6 +569,22 @@ export declare const providerQuotaMapSchema: z.ZodObject<{
466
569
  limitId: z.ZodOptional<z.ZodString>;
467
570
  limitName: z.ZodOptional<z.ZodString>;
468
571
  credits: z.ZodOptional<z.ZodUnknown>;
572
+ resetCredits: z.ZodOptional<z.ZodObject<{
573
+ availableCount: z.ZodNumber;
574
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
575
+ credits: z.ZodArray<z.ZodObject<{
576
+ title: z.ZodOptional<z.ZodString>;
577
+ status: z.ZodString;
578
+ resetType: z.ZodOptional<z.ZodString>;
579
+ expiresAt: z.ZodString;
580
+ grantedAt: z.ZodOptional<z.ZodString>;
581
+ }, z.core.$strip>>;
582
+ source: z.ZodOptional<z.ZodEnum<{
583
+ api: "api";
584
+ cache: "cache";
585
+ }>>;
586
+ capturedAt: z.ZodOptional<z.ZodString>;
587
+ }, z.core.$strip>>;
469
588
  planType: z.ZodOptional<z.ZodString>;
470
589
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
471
590
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -534,8 +653,8 @@ export declare const providerQuotaMapSchema: z.ZodObject<{
534
653
  }>>;
535
654
  source: z.ZodOptional<z.ZodEnum<{
536
655
  api: "api";
537
- session: "session";
538
656
  cache: "cache";
657
+ session: "session";
539
658
  }>>;
540
659
  capturedAt: z.ZodOptional<z.ZodString>;
541
660
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -544,6 +663,22 @@ export declare const providerQuotaMapSchema: z.ZodObject<{
544
663
  limitId: z.ZodOptional<z.ZodString>;
545
664
  limitName: z.ZodOptional<z.ZodString>;
546
665
  credits: z.ZodOptional<z.ZodUnknown>;
666
+ resetCredits: z.ZodOptional<z.ZodObject<{
667
+ availableCount: z.ZodNumber;
668
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
669
+ credits: z.ZodArray<z.ZodObject<{
670
+ title: z.ZodOptional<z.ZodString>;
671
+ status: z.ZodString;
672
+ resetType: z.ZodOptional<z.ZodString>;
673
+ expiresAt: z.ZodString;
674
+ grantedAt: z.ZodOptional<z.ZodString>;
675
+ }, z.core.$strip>>;
676
+ source: z.ZodOptional<z.ZodEnum<{
677
+ api: "api";
678
+ cache: "cache";
679
+ }>>;
680
+ capturedAt: z.ZodOptional<z.ZodString>;
681
+ }, z.core.$strip>>;
547
682
  planType: z.ZodOptional<z.ZodString>;
548
683
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
549
684
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -612,8 +747,8 @@ export declare const providerQuotaMapSchema: z.ZodObject<{
612
747
  }>>;
613
748
  source: z.ZodOptional<z.ZodEnum<{
614
749
  api: "api";
615
- session: "session";
616
750
  cache: "cache";
751
+ session: "session";
617
752
  }>>;
618
753
  capturedAt: z.ZodOptional<z.ZodString>;
619
754
  stale: z.ZodOptional<z.ZodBoolean>;
@@ -622,6 +757,22 @@ export declare const providerQuotaMapSchema: z.ZodObject<{
622
757
  limitId: z.ZodOptional<z.ZodString>;
623
758
  limitName: z.ZodOptional<z.ZodString>;
624
759
  credits: z.ZodOptional<z.ZodUnknown>;
760
+ resetCredits: z.ZodOptional<z.ZodObject<{
761
+ availableCount: z.ZodNumber;
762
+ totalEarnedCount: z.ZodOptional<z.ZodNumber>;
763
+ credits: z.ZodArray<z.ZodObject<{
764
+ title: z.ZodOptional<z.ZodString>;
765
+ status: z.ZodString;
766
+ resetType: z.ZodOptional<z.ZodString>;
767
+ expiresAt: z.ZodString;
768
+ grantedAt: z.ZodOptional<z.ZodString>;
769
+ }, z.core.$strip>>;
770
+ source: z.ZodOptional<z.ZodEnum<{
771
+ api: "api";
772
+ cache: "cache";
773
+ }>>;
774
+ capturedAt: z.ZodOptional<z.ZodString>;
775
+ }, z.core.$strip>>;
625
776
  planType: z.ZodOptional<z.ZodString>;
626
777
  rateLimitReachedType: z.ZodOptional<z.ZodString>;
627
778
  accountLabel: z.ZodOptional<z.ZodString>;
@@ -10,7 +10,7 @@
10
10
  * @module schemas/quota
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaStateSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaWindowSchema = void 0;
13
+ exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.quotaStateSchema = exports.codexResetCreditsSnapshotSchema = exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaWindowSchema = void 0;
14
14
  const zod_1 = require("zod");
15
15
  // ── QuotaWindow ──
16
16
  exports.quotaWindowSchema = zod_1.z.object({
@@ -27,6 +27,20 @@ exports.quotaFailureKindSchema = zod_1.z.enum([
27
27
  ]);
28
28
  exports.quotaProviderIdSchema = zod_1.z.enum(['claude-code', 'codex', 'zai']);
29
29
  exports.quotaSourceSchema = zod_1.z.enum(['api', 'session', 'cache']);
30
+ exports.codexResetCreditSchema = zod_1.z.object({
31
+ title: zod_1.z.string().optional(),
32
+ status: zod_1.z.string(),
33
+ resetType: zod_1.z.string().optional(),
34
+ expiresAt: zod_1.z.string(),
35
+ grantedAt: zod_1.z.string().optional(),
36
+ });
37
+ exports.codexResetCreditsSnapshotSchema = zod_1.z.object({
38
+ availableCount: zod_1.z.number(),
39
+ totalEarnedCount: zod_1.z.number().optional(),
40
+ credits: zod_1.z.array(exports.codexResetCreditSchema),
41
+ source: zod_1.z.enum(['api', 'cache']).optional(),
42
+ capturedAt: zod_1.z.string().optional(),
43
+ });
30
44
  exports.quotaStateSchema = zod_1.z.object({
31
45
  fiveHour: exports.quotaWindowSchema,
32
46
  sevenDay: exports.quotaWindowSchema,
@@ -46,6 +60,7 @@ exports.quotaStateSchema = zod_1.z.object({
46
60
  limitId: zod_1.z.string().optional(),
47
61
  limitName: zod_1.z.string().optional(),
48
62
  credits: zod_1.z.unknown().optional(),
63
+ resetCredits: exports.codexResetCreditsSnapshotSchema.optional(),
49
64
  planType: zod_1.z.string().optional(),
50
65
  rateLimitReachedType: zod_1.z.string().optional(),
51
66
  });
@@ -35,8 +35,8 @@ export declare const quotaHistorySampleSchema: z.ZodObject<{
35
35
  error: z.ZodOptional<z.ZodString>;
36
36
  source: z.ZodOptional<z.ZodEnum<{
37
37
  api: "api";
38
- session: "session";
39
38
  cache: "cache";
39
+ session: "session";
40
40
  }>>;
41
41
  stale: z.ZodOptional<z.ZodBoolean>;
42
42
  }, z.core.$strip>;
@@ -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.4",
3
+ "version": "0.21.6",
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",