@cortexkit/anthropic-auth-core 1.21.0 → 1.23.0

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.
Files changed (59) hide show
  1. package/README.md +1 -1
  2. package/dist/accounts.d.ts +100 -3
  3. package/dist/accounts.js +1099 -169
  4. package/dist/auth.js +6 -10
  5. package/dist/cachekeep.d.ts +18 -1
  6. package/dist/cachekeep.js +108 -82
  7. package/dist/cch.d.ts +23 -9
  8. package/dist/cch.js +104 -21
  9. package/dist/claude-code.d.ts +8 -3
  10. package/dist/claude-code.js +24 -7
  11. package/dist/claustrum-enrollment-registry.d.ts +17 -0
  12. package/dist/claustrum-enrollment-registry.js +194 -0
  13. package/dist/claustrum-enrollment.d.ts +70 -0
  14. package/dist/claustrum-enrollment.js +485 -0
  15. package/dist/claustrum-scoped-roster.d.ts +23 -0
  16. package/dist/claustrum-scoped-roster.js +194 -0
  17. package/dist/claustrum-scoped-runtime.d.ts +28 -0
  18. package/dist/claustrum-scoped-runtime.js +230 -0
  19. package/dist/claustrum-scoped.d.ts +39 -0
  20. package/dist/claustrum-scoped.js +193 -0
  21. package/dist/claustrum.d.ts +252 -0
  22. package/dist/claustrum.js +1471 -0
  23. package/dist/commands/account.d.ts +43 -11
  24. package/dist/commands/account.js +147 -4
  25. package/dist/constants.d.ts +5 -5
  26. package/dist/constants.js +5 -5
  27. package/dist/custom-headers.d.ts +3 -0
  28. package/dist/custom-headers.js +102 -0
  29. package/dist/dump.js +2 -1
  30. package/dist/fast.js +1 -1
  31. package/dist/index.d.ts +11 -0
  32. package/dist/index.js +11 -0
  33. package/dist/json.d.ts +1 -0
  34. package/dist/json.js +10 -0
  35. package/dist/logger.js +3 -1
  36. package/dist/mid-conversation-output-config.d.ts +13 -0
  37. package/dist/mid-conversation-output-config.js +89 -0
  38. package/dist/model-remap.d.ts +27 -0
  39. package/dist/model-remap.js +72 -0
  40. package/dist/models.d.ts +46 -1
  41. package/dist/models.js +87 -8
  42. package/dist/network-errors.d.ts +1 -0
  43. package/dist/network-errors.js +27 -0
  44. package/dist/oauth-profile.d.ts +2 -1
  45. package/dist/oauth-profile.js +6 -0
  46. package/dist/prime.d.ts +11 -3
  47. package/dist/prime.js +25 -7
  48. package/dist/quota-header-feed.d.ts +32 -7
  49. package/dist/quota-header-feed.js +93 -10
  50. package/dist/quota-headers.js +59 -6
  51. package/dist/quota-manager.d.ts +20 -2
  52. package/dist/quota-manager.js +101 -24
  53. package/dist/relay.d.ts +8 -0
  54. package/dist/relay.js +72 -12
  55. package/dist/sticky-routing.d.ts +2 -0
  56. package/dist/sticky-routing.js +19 -1
  57. package/dist/thinking-binding.d.ts +7 -0
  58. package/dist/thinking-binding.js +54 -0
  59. package/package.json +4 -1
@@ -1,6 +1,13 @@
1
- import { type AccountStorage, type OAuthQuotaSnapshot } from './accounts.ts';
2
- export declare const QUOTA_HEADER_FEED_SCHEMA_VERSION = 2;
1
+ import { type AccountStorage, type OAuthQuotaSnapshot, type QuotaFieldName, type QuotaFieldSource, type QuotaFieldSources } from './accounts.ts';
2
+ export declare const QUOTA_HEADER_FEED_SCHEMA_VERSION = 3;
3
3
  export declare const QUOTA_HEADER_FEED_LEASE_MS = 180000;
4
+ /**
5
+ * Lease files are per process; each carries only the accounts whose response
6
+ * headers THAT process harvested. Consumers MUST union entries across all files
7
+ * inside `lease_horizon_ms`, deduplicating by account; "newest file wins" is wrong.
8
+ * `anthropic_account_uuid` is always present: null is unresolvable, while absence
9
+ * identifies an old producer. A fallback `account_ref` is store-local.
10
+ */
4
11
  export type QuotaHeaderFeedIdentity = {
5
12
  identity_source: 'credential_id';
6
13
  credential_id: string;
@@ -10,12 +17,28 @@ export type QuotaHeaderFeedIdentity = {
10
17
  } | {
11
18
  identity_source: 'none';
12
19
  };
13
- export type QuotaHeaderFeedEntry = QuotaHeaderFeedIdentity & {
20
+ export type QuotaHeaderFeedProvenance = Partial<Record<QuotaFieldName, QuotaFieldSource>>;
21
+ type QuotaHeaderFeedQuota = Pick<OAuthQuotaSnapshot, 'five_hour' | 'seven_day' | 'bindingWindow' | 'fallbackAdvised' | 'scoped' | 'extraUsage'> & {
22
+ provenance?: QuotaHeaderFeedProvenance;
23
+ };
24
+ type QuotaHeaderFeedMetadata = {
14
25
  schema_version: typeof QUOTA_HEADER_FEED_SCHEMA_VERSION;
15
26
  provider: 'anthropic';
16
27
  configured_account_count: number;
28
+ /** Header observation time; merged poll-owned fields retain their own checkedAt. */
17
29
  observed_at_ms: number;
18
- quota: Pick<OAuthQuotaSnapshot, 'five_hour' | 'seven_day' | 'bindingWindow' | 'fallbackAdvised' | 'scoped' | 'extraUsage'>;
30
+ };
31
+ export type QuotaHeaderFeedEntry = QuotaHeaderFeedIdentity & QuotaHeaderFeedMetadata & {
32
+ /** Always present: null means UUID resolution failed; absence denotes an old producer. */
33
+ anthropic_account_uuid: string | null;
34
+ quota: QuotaHeaderFeedQuota;
35
+ };
36
+ export type QuotaHeaderFeedPublishEntry = QuotaHeaderFeedIdentity & QuotaHeaderFeedMetadata & {
37
+ anthropic_account_uuid: string | null;
38
+ quota: Omit<QuotaHeaderFeedQuota, 'provenance'> & {
39
+ fieldSources?: QuotaFieldSources;
40
+ };
41
+ accountKey: string;
19
42
  };
20
43
  export declare function getDefaultQuotaHeaderFeedDirectory(): string;
21
44
  export declare function configuredAnthropicOAuthAccountCount(input: {
@@ -31,10 +54,12 @@ export declare class QuotaHeaderFeedRegistry {
31
54
  now?: () => number;
32
55
  leaseMs?: number;
33
56
  instanceId?: string;
57
+ removeFile?: (path: string) => Promise<void>;
58
+ beforeRemoveFile?: (path: string) => Promise<void>;
34
59
  });
35
- publish(entry: QuotaHeaderFeedEntry & {
36
- accountKey: string;
37
- }): Promise<void>;
60
+ publish(entry: QuotaHeaderFeedPublishEntry): Promise<void>;
61
+ private reapStaleSiblingLeases;
38
62
  list(): Promise<QuotaHeaderFeedEntry[]>;
39
63
  dispose(): Promise<void>;
40
64
  }
65
+ export {};
@@ -1,9 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { chmod, mkdir, readdir, readFile, rename, rm, writeFile, } from 'node:fs/promises';
2
+ import { chmod, lstat, mkdir, readdir, readFile, rename, rm, stat, writeFile, } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
- import { isOAuthAccount, } from './accounts.js';
6
- export const QUOTA_HEADER_FEED_SCHEMA_VERSION = 2;
5
+ import { isOAuthAccount, QUOTA_FIELD_NAMES, } from './accounts.js';
6
+ export const QUOTA_HEADER_FEED_SCHEMA_VERSION = 3;
7
7
  export const QUOTA_HEADER_FEED_LEASE_MS = 180_000;
8
8
  export function getDefaultQuotaHeaderFeedDirectory() {
9
9
  return (process.env.OPENCODE_ANTHROPIC_AUTH_QUOTA_FEED_DIR ??
@@ -22,6 +22,10 @@ function validIdentity(entry) {
22
22
  return false;
23
23
  if (!entry.quota || typeof entry.quota !== 'object')
24
24
  return false;
25
+ if (!Object.hasOwn(entry, 'anthropic_account_uuid') ||
26
+ (entry.anthropic_account_uuid !== null &&
27
+ typeof entry.anthropic_account_uuid !== 'string'))
28
+ return false;
25
29
  if (entry.identity_source === 'none') {
26
30
  return !('credential_id' in entry) && !('account_ref' in entry);
27
31
  }
@@ -129,6 +133,20 @@ function projectExtraUsage(value) {
129
133
  exhausted: candidate.exhausted,
130
134
  };
131
135
  }
136
+ function projectQuotaProvenance(value, presentFields) {
137
+ if (!value || typeof value !== 'object')
138
+ return undefined;
139
+ const candidate = value;
140
+ const provenance = {};
141
+ for (const field of QUOTA_FIELD_NAMES) {
142
+ if (!presentFields.has(field))
143
+ continue;
144
+ const source = candidate[field];
145
+ if (source === 'poll' || source === 'headers')
146
+ provenance[field] = source;
147
+ }
148
+ return Object.keys(provenance).length > 0 ? provenance : undefined;
149
+ }
132
150
  function projectQuota(quota) {
133
151
  const fiveHour = projectQuotaWindow(quota.five_hour);
134
152
  const sevenDay = projectQuotaWindow(quota.seven_day);
@@ -138,6 +156,22 @@ function projectQuota(quota) {
138
156
  .filter((entry) => entry != null)
139
157
  : undefined;
140
158
  const extraUsage = projectExtraUsage(quota.extraUsage);
159
+ const presentFields = new Set();
160
+ if (fiveHour)
161
+ presentFields.add('five_hour');
162
+ if (sevenDay)
163
+ presentFields.add('seven_day');
164
+ if (typeof quota.bindingWindow === 'string') {
165
+ presentFields.add('bindingWindow');
166
+ }
167
+ if (typeof quota.fallbackAdvised === 'boolean') {
168
+ presentFields.add('fallbackAdvised');
169
+ }
170
+ if (scoped)
171
+ presentFields.add('scoped');
172
+ if (extraUsage)
173
+ presentFields.add('extraUsage');
174
+ const provenance = projectQuotaProvenance(quota.fieldSources, presentFields);
141
175
  return {
142
176
  ...(fiveHour && { five_hour: fiveHour }),
143
177
  ...(sevenDay && { seven_day: sevenDay }),
@@ -149,6 +183,7 @@ function projectQuota(quota) {
149
183
  }),
150
184
  ...(scoped && { scoped }),
151
185
  ...(extraUsage && { extraUsage }),
186
+ ...(provenance && { provenance }),
152
187
  };
153
188
  }
154
189
  export class QuotaHeaderFeedRegistry {
@@ -161,17 +196,33 @@ export class QuotaHeaderFeedRegistry {
161
196
  this.filePath = join(options.directory ?? getDefaultQuotaHeaderFeedDirectory(), `${instanceId}.json`);
162
197
  }
163
198
  publish(entry) {
164
- const { accountKey, quota, ...entryWithoutQuota } = entry;
165
- const cleanEntry = {
166
- ...entryWithoutQuota,
167
- quota: projectQuota(quota),
168
- };
199
+ const { accountKey, quota } = entry;
169
200
  try {
170
- validatePublishEntry(cleanEntry);
201
+ validatePublishEntry(entry);
171
202
  }
172
203
  catch (error) {
173
204
  return Promise.reject(error);
174
205
  }
206
+ const identity = entry.identity_source === 'credential_id'
207
+ ? {
208
+ identity_source: 'credential_id',
209
+ credential_id: entry.credential_id,
210
+ }
211
+ : entry.identity_source === 'account_ref'
212
+ ? {
213
+ identity_source: 'account_ref',
214
+ account_ref: entry.account_ref,
215
+ }
216
+ : { identity_source: 'none' };
217
+ const cleanEntry = {
218
+ ...identity,
219
+ schema_version: entry.schema_version,
220
+ provider: entry.provider,
221
+ configured_account_count: entry.configured_account_count,
222
+ observed_at_ms: entry.observed_at_ms,
223
+ anthropic_account_uuid: entry.anthropic_account_uuid,
224
+ quota: projectQuota(quota),
225
+ };
175
226
  if (!accountKey)
176
227
  return Promise.reject(new Error('Invalid quota header feed account key'));
177
228
  this.writeChain = this.writeChain
@@ -180,6 +231,7 @@ export class QuotaHeaderFeedRegistry {
180
231
  const directory = this.options.directory ?? getDefaultQuotaHeaderFeedDirectory();
181
232
  await mkdir(directory, { recursive: true, mode: 0o700 });
182
233
  await chmod(directory, 0o700);
234
+ await this.reapStaleSiblingLeases(directory);
183
235
  let entries = {};
184
236
  try {
185
237
  const record = JSON.parse(await readFile(this.filePath, 'utf8'));
@@ -192,7 +244,7 @@ export class QuotaHeaderFeedRegistry {
192
244
  entries[accountKey] = cleanEntry;
193
245
  const tempPath = `${this.filePath}.${randomUUID()}.tmp`;
194
246
  try {
195
- await writeFile(tempPath, `${JSON.stringify({ version: QUOTA_HEADER_FEED_SCHEMA_VERSION, entries })}\n`, { mode: 0o600 });
247
+ await writeFile(tempPath, `${JSON.stringify({ version: QUOTA_HEADER_FEED_SCHEMA_VERSION, lease_horizon_ms: this.options.leaseMs ?? QUOTA_HEADER_FEED_LEASE_MS, entries })}\n`, { mode: 0o600 });
196
248
  await chmod(tempPath, 0o600);
197
249
  await rename(tempPath, this.filePath);
198
250
  }
@@ -202,6 +254,37 @@ export class QuotaHeaderFeedRegistry {
202
254
  });
203
255
  return this.writeChain;
204
256
  }
257
+ async reapStaleSiblingLeases(directory) {
258
+ const now = this.options.now?.() ?? Date.now();
259
+ const leaseMs = this.options.leaseMs ?? QUOTA_HEADER_FEED_LEASE_MS;
260
+ let names;
261
+ try {
262
+ names = await readdir(directory);
263
+ }
264
+ catch {
265
+ return;
266
+ }
267
+ await Promise.all(names
268
+ .filter((name) => /^\d+-[0-9a-f-]+\.json$/i.test(name))
269
+ .map(async (name) => {
270
+ const path = join(directory, name);
271
+ if (path === this.filePath)
272
+ return;
273
+ try {
274
+ const file = await stat(path);
275
+ if (file.mtimeMs > now || now - file.mtimeMs < leaseMs)
276
+ return;
277
+ await this.options.beforeRemoveFile?.(path);
278
+ const current = await lstat(path);
279
+ if (current.ino !== file.ino || current.mtimeMs !== file.mtimeMs)
280
+ return;
281
+ await (this.options.removeFile ?? ((target) => rm(target)))(path);
282
+ }
283
+ catch {
284
+ // A missed cleanup must not prevent this process from refreshing its lease.
285
+ }
286
+ }));
287
+ }
205
288
  async list() {
206
289
  await this.writeChain.catch(() => { });
207
290
  const directory = this.options.directory ?? getDefaultQuotaHeaderFeedDirectory();
@@ -1,3 +1,4 @@
1
+ import { QUOTA_FIELD_NAMES, quotaFieldSource } from './accounts.js';
1
2
  const PREFIX = 'anthropic-ratelimit-unified-';
2
3
  const WINDOW_KEYS = {
3
4
  '5h': 'five_hour',
@@ -31,38 +32,90 @@ function normalizeWindow(headers, suffix, checkedAt) {
31
32
  };
32
33
  }
33
34
  export function normalizeQuotaHeaders(headers, now = Date.now()) {
35
+ const fieldSources = {};
36
+ const fallbackHeader = headers.get(`${PREFIX}fallback`);
34
37
  const snapshot = {
35
- fallbackAdvised: headers.get(`${PREFIX}fallback`) === 'available',
38
+ fallbackAdvised: fallbackHeader === 'available',
36
39
  source: 'headers',
37
40
  checkedAt: now,
38
41
  };
39
42
  for (const [suffix, key] of Object.entries(WINDOW_KEYS)) {
40
43
  const window = normalizeWindow(headers, suffix, now);
41
- if (window)
44
+ if (window) {
42
45
  snapshot[key] = window;
46
+ fieldSources[key] = 'headers';
47
+ }
43
48
  }
44
49
  const representativeClaim = headers.get(`${PREFIX}representative-claim`);
45
50
  if (representativeClaim) {
46
51
  snapshot.bindingWindow = representativeClaim;
47
52
  snapshot.bindingWindowSource = 'headers';
53
+ fieldSources.bindingWindow = 'headers';
48
54
  }
49
- return snapshot;
55
+ if (fallbackHeader !== null)
56
+ fieldSources.fallbackAdvised = 'headers';
57
+ return {
58
+ ...snapshot,
59
+ fieldSources,
60
+ };
50
61
  }
51
62
  export function mergeHeaderQuotaSnapshot(existing, incoming) {
52
- const { scoped: existingScoped, extraUsage: existingExtraUsage, ...existingWithoutPollOwnedFields } = existing ?? {};
53
- const { scoped: _incomingScoped, extraUsage: _incomingExtraUsage, ...incomingWithoutPollOwnedFields } = incoming;
63
+ const { scoped: existingScoped, extraUsage: existingExtraUsage, fieldSources: existingFieldSources, ...existingWithoutPollOwnedFields } = existing ?? {};
64
+ const { scoped: _incomingScoped, extraUsage: _incomingExtraUsage, fieldSources: incomingFieldSources, ...incomingWithoutPollOwnedFields } = incoming;
65
+ const fieldSources = {};
66
+ for (const field of QUOTA_FIELD_NAMES) {
67
+ const value = field === 'scoped'
68
+ ? existingScoped
69
+ : field === 'extraUsage'
70
+ ? existingExtraUsage
71
+ : field === 'fallbackAdvised'
72
+ ? incomingFieldSources?.fallbackAdvised !== undefined
73
+ ? incoming.fallbackAdvised
74
+ : (existing?.fallbackAdvised ?? incoming.fallbackAdvised)
75
+ : field === 'bindingWindow'
76
+ ? existing?.bindingWindowSource === 'poll'
77
+ ? existing?.bindingWindow
78
+ : (incoming.bindingWindow ?? existing?.bindingWindow)
79
+ : (incoming[field] ?? existing?.[field]);
80
+ if (value === undefined)
81
+ continue;
82
+ const source = field === 'fallbackAdvised'
83
+ ? (incomingFieldSources?.fallbackAdvised ??
84
+ (existing?.fallbackAdvised !== undefined
85
+ ? existingFieldSources
86
+ ? existingFieldSources.fallbackAdvised
87
+ : quotaFieldSource(existing, field)
88
+ : undefined))
89
+ : (field === 'scoped' || field === 'extraUsage') && value !== undefined
90
+ ? 'poll'
91
+ : field === 'bindingWindow' &&
92
+ existing?.bindingWindowSource === 'poll' &&
93
+ existing.bindingWindow !== undefined
94
+ ? 'poll'
95
+ : incoming[field] !== undefined
96
+ ? (incomingFieldSources?.[field] ??
97
+ quotaFieldSource(incoming, field))
98
+ : (existingFieldSources?.[field] ??
99
+ quotaFieldSource(existing, field));
100
+ if (source)
101
+ fieldSources[field] = source;
102
+ }
54
103
  return {
55
104
  ...existingWithoutPollOwnedFields,
56
105
  ...incomingWithoutPollOwnedFields,
57
106
  ...(Array.isArray(existingScoped) && { scoped: existingScoped }),
58
107
  ...(existingExtraUsage != null && { extraUsage: existingExtraUsage }),
108
+ fallbackAdvised: incomingFieldSources?.fallbackAdvised !== undefined
109
+ ? incoming.fallbackAdvised
110
+ : (existing?.fallbackAdvised ?? incoming.fallbackAdvised),
59
111
  bindingWindow: existing?.bindingWindowSource === 'poll'
60
112
  ? existing.bindingWindow
61
113
  : (incoming.bindingWindow ?? existing?.bindingWindow),
62
114
  bindingWindowSource: existing?.bindingWindowSource === 'poll'
63
115
  ? 'poll'
64
116
  : (incoming.bindingWindowSource ?? existing?.bindingWindowSource),
65
- // Source tracks 5h/7d freshness; preserved scoped and credit fields remain poll-owned.
117
+ ...(Object.keys(fieldSources).length > 0 && { fieldSources }),
118
+ // Top-level source remains headers for compatibility; fieldSources records ownership after partial harvests.
66
119
  source: 'headers',
67
120
  };
68
121
  }
@@ -20,9 +20,17 @@ export type QuotaRefreshResult = {
20
20
  export type QuotaManagerOptions = {
21
21
  storage: AccountStorage | null;
22
22
  fetchImpl?: typeof fetch;
23
+ /** Resolve custody credentials at the actual HTTP boundary, after quota gates. */
24
+ fetchQuotaSnapshot?: (request: {
25
+ kind: 'main' | 'fallback';
26
+ accountId: string | undefined;
27
+ accessToken: string;
28
+ }) => Promise<OAuthQuotaSnapshot>;
23
29
  now?: () => number;
24
30
  onMainQuotaFetched?: (quota: OAuthQuotaSnapshot, checkedAt: number, tokenFingerprint: string, fetchStartedAt: number, quotaErrorGeneration: number, quotaErrorClearedAt: number | undefined) => void;
25
31
  onApiError?: (error: AccountOperationError, quotaErrorGeneration: number) => void;
32
+ onFallbackQuotaFetched?: (accountId: string, quota: OAuthQuotaSnapshot, checkedAt: number, fetchStartedAt: number) => void;
33
+ onFallbackApiError?: (accountId: string, error: AccountOperationError) => void;
26
34
  };
27
35
  export declare class QuotaManager {
28
36
  private main;
@@ -37,6 +45,8 @@ export declare class QuotaManager {
37
45
  private fallbackGenerations;
38
46
  private inflightMain;
39
47
  private inflightMainAccountId;
48
+ private inflightMainAccessToken;
49
+ private inflightMainToken;
40
50
  private inflightFallbacks;
41
51
  private mainLastApiError;
42
52
  private fallbackApiErrors;
@@ -44,9 +54,12 @@ export declare class QuotaManager {
44
54
  private lastApiCallAt;
45
55
  private storage;
46
56
  private readonly fetchImpl;
57
+ private readonly fetchQuotaSnapshot;
47
58
  private readonly now;
48
59
  private readonly onMainQuotaFetched;
49
60
  private readonly onApiError;
61
+ private readonly onFallbackQuotaFetched;
62
+ private readonly onFallbackApiError;
50
63
  constructor(opts: QuotaManagerOptions);
51
64
  /**
52
65
  * Cached main quota entry, scoped to the account behind the main credential.
@@ -57,10 +70,13 @@ export declare class QuotaManager {
57
70
  * Cached fallback quota entry, scoped to the configured account id.
58
71
  */
59
72
  getFallback(accountId: string, account?: Pick<OAuthAccount, 'authLineageId'>): QuotaEntry | null;
73
+ /** A host transport resolves scoped credentials at dispatch time. */
74
+ canFetchWithoutAccessToken(): boolean;
60
75
  getAllFallbacks(): Map<string, QuotaEntry>;
61
76
  setMain(mainAccountId: string | undefined, entry: QuotaEntry): void;
77
+ clearMain(): void;
62
78
  /** Bind quota state to the account behind the current main credential. */
63
- setMainQuotaAccountIdentity(accountIdentity: string | undefined, strict?: boolean): number | undefined;
79
+ setMainQuotaAccountIdentity(accountIdentity: string | undefined, strict?: boolean, force?: boolean): number | undefined;
64
80
  setFallback(accountId: string, entry: QuotaEntry, account: Pick<OAuthAccount, 'authLineageId'> | undefined): void;
65
81
  clearFallback(accountId: string): void;
66
82
  pushMainFromHeaders(mainAccountId: string | undefined, incoming: OAuthQuotaSnapshot): QuotaEntry;
@@ -69,7 +85,7 @@ export declare class QuotaManager {
69
85
  refreshMainWithMetadata(mainAccountIdOrAccessToken: string | undefined, accessToken?: string, expectedIdentityGeneration?: number): Promise<QuotaRefreshResult>;
70
86
  refreshFallback(accountId: string, accessToken: string, account: Pick<OAuthAccount, 'authLineageId'> | undefined): Promise<OAuthQuotaSnapshot>;
71
87
  refreshFallbackWithMetadata(accountId: string, accessToken: string, account: Pick<OAuthAccount, 'authLineageId'> | undefined): Promise<QuotaRefreshResult>;
72
- refreshAllFallbacks(accounts: OAuthAccount[]): Promise<void>;
88
+ refreshAllFallbacks(accounts: OAuthAccount[], resolveAccessToken?: (account: OAuthAccount) => string | undefined): Promise<void>;
73
89
  /**
74
90
  * Fire-and-forget refresh. Does not await, swallows errors.
75
91
  */
@@ -118,6 +134,8 @@ export declare class QuotaManager {
118
134
  private static quotaLockName;
119
135
  private bindFallbackLineage;
120
136
  private acceptFallbackQuotaObservation;
137
+ private isClosed;
138
+ close(): void;
121
139
  /**
122
140
  * Serialize API calls through a shared gate so only one
123
141
  * quota API request runs at a time, with a minimum gap