@cortexkit/anthropic-auth-core 1.20.0 → 1.21.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.
@@ -3,9 +3,21 @@ export declare const STICKY_ROUTING_MAIN_ACCOUNT_ID = "main";
3
3
  export declare const STICKY_ROUTING_SHORT_RESET_GRACE_MS: number;
4
4
  export declare const STICKY_ROUTING_ASSIGNMENT_TTL_MS: number;
5
5
  export type StickyRouteFamily = 'fable' | 'opus' | 'general';
6
- export type StickyRouteCandidate = {
6
+ export type IdentityState = {
7
+ kind: 'known';
7
8
  accountId: string;
9
+ } | {
10
+ kind: 'unknown';
11
+ };
12
+ export type QuotaState = {
13
+ kind: 'known';
8
14
  quota: OAuthQuotaSnapshot;
15
+ } | {
16
+ kind: 'unknown';
17
+ };
18
+ export type StickyRouteCandidate = {
19
+ accountId: string;
20
+ quota: OAuthQuotaSnapshot | QuotaState;
9
21
  order: number;
10
22
  };
11
23
  export type StickyRouteAssignment = {
@@ -56,7 +68,7 @@ export declare function stickyRouteCandidateWeight(input: {
56
68
  }): number;
57
69
  export declare function stickyRouteKnownFableExhausted(candidate: StickyRouteCandidate, storage: AccountStorage | null, now?: number): boolean;
58
70
  export declare function decideStickyQuotaFailure(input: {
59
- quota: OAuthQuotaSnapshot | undefined;
71
+ quota: OAuthQuotaSnapshot | QuotaState | undefined;
60
72
  modelId?: string;
61
73
  now?: number;
62
74
  }): StickyQuotaFailureDecision;
@@ -136,6 +136,13 @@ export function stickyQuotaSnapshotIsFresh(quota, storage, now = Date.now(), mod
136
136
  function snapshotCheckedAt(quota) {
137
137
  return Math.max(quota.checkedAt ?? 0, quota.five_hour?.checkedAt ?? 0, quota.seven_day?.checkedAt ?? 0, ...(quota.scoped ?? []).map((window) => window.checkedAt ?? 0));
138
138
  }
139
+ function knownQuota(quota) {
140
+ if (!quota)
141
+ return undefined;
142
+ if ('kind' in quota)
143
+ return quota.kind === 'known' ? quota.quota : undefined;
144
+ return quota;
145
+ }
139
146
  function remainingThreshold(storage, accountId, key) {
140
147
  const quotaMinimum = getQuotaMinimumRemainingThresholds(storage);
141
148
  const quotaThreshold = key === 'five_hour'
@@ -170,18 +177,21 @@ export function stickyRouteFamilyForModel(model) {
170
177
  export function stickyRouteCandidateWeight(input) {
171
178
  const now = input.now ?? Date.now();
172
179
  const { candidate, storage } = input;
173
- if (!stickyQuotaSnapshotIsFresh(candidate.quota, storage, now, input.modelId)) {
180
+ const quota = knownQuota(candidate.quota);
181
+ if (!quota)
182
+ return 0;
183
+ if (!stickyQuotaSnapshotIsFresh(quota, storage, now, input.modelId)) {
174
184
  return 0;
175
185
  }
176
186
  const weights = [];
177
187
  for (const key of ['five_hour', 'seven_day']) {
178
- const window = candidate.quota[key];
188
+ const window = quota[key];
179
189
  if (!window || !Number.isFinite(window.remainingPercent))
180
190
  return 0;
181
191
  weights.push(sustainableWindowWeight(window.remainingPercent, remainingThreshold(storage, candidate.accountId, key), window.resetsAt, now));
182
192
  }
183
193
  if (input.family === 'fable') {
184
- const window = getScopedQuotaWindowForModel(candidate.quota, input.modelId);
194
+ const window = getScopedQuotaWindowForModel(quota, input.modelId);
185
195
  if (window && Number.isFinite(window.remainingPercent)) {
186
196
  weights.push(sustainableWindowWeight(window.remainingPercent, remainingThreshold(storage, candidate.accountId, 'scoped'), window.resetsAt, now));
187
197
  }
@@ -189,29 +199,31 @@ export function stickyRouteCandidateWeight(input) {
189
199
  return Math.min(...weights);
190
200
  }
191
201
  export function stickyRouteKnownFableExhausted(candidate, storage, now = Date.now()) {
192
- const window = getScopedQuotaWindowForModel(candidate.quota, 'claude-fable-5');
202
+ const quota = knownQuota(candidate.quota);
203
+ const window = getScopedQuotaWindowForModel(quota, 'claude-fable-5');
193
204
  return Boolean(window &&
194
- stickyQuotaSnapshotIsFresh(candidate.quota, storage, now, 'claude-fable-5') &&
205
+ stickyQuotaSnapshotIsFresh(quota, storage, now, 'claude-fable-5') &&
195
206
  Number.isFinite(window.remainingPercent) &&
196
207
  window.remainingPercent <= 0);
197
208
  }
198
209
  export function decideStickyQuotaFailure(input) {
199
210
  const now = input.now ?? Date.now();
200
- if (!input.quota)
211
+ const quota = knownQuota(input.quota);
212
+ if (!quota)
201
213
  return { action: 'retain', reason: 'unknown' };
202
- const scoped = getScopedQuotaWindowForModel(input.quota, input.modelId);
214
+ const scoped = getScopedQuotaWindowForModel(quota, input.modelId);
203
215
  if (scoped &&
204
216
  Number.isFinite(scoped.remainingPercent) &&
205
217
  scoped.remainingPercent <= 0) {
206
218
  return { action: 'migrate', reason: 'model-scoped' };
207
219
  }
208
- const sevenDay = input.quota.seven_day;
220
+ const sevenDay = quota.seven_day;
209
221
  if (sevenDay &&
210
222
  Number.isFinite(sevenDay.remainingPercent) &&
211
223
  sevenDay.remainingPercent <= 0) {
212
224
  return { action: 'migrate', reason: 'seven-day' };
213
225
  }
214
- const fiveHour = input.quota.five_hour;
226
+ const fiveHour = quota.five_hour;
215
227
  if (fiveHour &&
216
228
  Number.isFinite(fiveHour.remainingPercent) &&
217
229
  fiveHour.remainingPercent <= 0) {
@@ -386,14 +398,18 @@ export class StickySessionRouter {
386
398
  });
387
399
  return weight > 0 ? [{ candidate, weight }] : [];
388
400
  });
389
- if (weighted.length === 0)
390
- return undefined;
401
+ if (weighted.length === 0 &&
402
+ candidates.every((candidate) => !knownQuota(candidate.quota))) {
403
+ return [...candidates].sort((left, right) => left.order - right.order ||
404
+ left.accountId.localeCompare(right.accountId))[0];
405
+ }
391
406
  const pendingBytes = new Map();
392
407
  for (const assignment of Object.values(input.state.assignments)) {
393
408
  const candidate = weighted.find((entry) => entry.candidate.accountId === assignment.accountId)?.candidate;
394
409
  if (!candidate)
395
410
  continue;
396
- if (assignment.quotaCheckedAt !== snapshotCheckedAt(candidate.quota))
411
+ const quota = knownQuota(candidate.quota);
412
+ if (quota && assignment.quotaCheckedAt !== snapshotCheckedAt(quota))
397
413
  continue;
398
414
  pendingBytes.set(assignment.accountId, (pendingBytes.get(assignment.accountId) ?? 0) +
399
415
  assignment.initialInputBytes);
@@ -466,13 +482,14 @@ export class StickySessionRouter {
466
482
  return null;
467
483
  }
468
484
  const now = this.now();
485
+ const selectedQuota = knownQuota(selected.quota);
469
486
  const assignment = {
470
487
  accountId: selected.accountId,
471
488
  family: input.family,
472
489
  assignedAt: now,
473
490
  lastSeenAt: now,
474
491
  initialInputBytes: Math.max(1, input.inputBytes),
475
- quotaCheckedAt: snapshotCheckedAt(selected.quota),
492
+ quotaCheckedAt: selectedQuota ? snapshotCheckedAt(selectedQuota) : 0,
476
493
  };
477
494
  state.assignments[key] = assignment;
478
495
  await this.writeState(state);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "1.20.0",
3
+ "version": "1.21.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",