@cortexkit/anthropic-auth-core 2.4.4 → 2.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -171,6 +171,9 @@ export declare class CacheKeepManager {
171
171
  attempt: CacheKeepPrewarmAttempt;
172
172
  }) => Promise<Headers | undefined> | Headers | undefined;
173
173
  onTrackedSessionsChanged?: (sessions: readonly CacheKeepTrackedSession[]) => Promise<void> | void;
174
+ /** Hosts may mirror targets without delaying a live request on storage I/O. */
175
+ onTargetChanged?: (target: Readonly<CacheKeepTarget>) => void;
176
+ onTargetRemoved?: (id: string) => void;
174
177
  prepareBody?: (bodyText: string, target: CacheKeepTarget) => string | Promise<string>;
175
178
  /**
176
179
  * Veto a prewarm after its body is built but before anything is
@@ -248,6 +251,8 @@ export declare class CacheKeepManager {
248
251
  accountStoragePath?: string;
249
252
  isSubagent?: boolean;
250
253
  sourceWarm?: boolean;
254
+ /** Restore only the remaining lifetime, never grant a restarted cache a new hour. */
255
+ retained?: Pick<CacheKeepTarget, 'cacheExpiresAt' | 'nextPrewarmAt'>;
251
256
  }): {
252
257
  tracked: true;
253
258
  } | {
package/dist/index.d.ts CHANGED
@@ -66,7 +66,7 @@ export type { NativeVaultQuotaPurpose, NativeVaultRuntime, NativeVaultRuntimeOpt
66
66
  export { acquireNativeVaultRuntime, createNativeVaultRuntime, discoverNativeVaultInventory, publishNativeVaultRosterSeed, publishNativeVaultRuntimeSeed, } from './native-vault-runtime.ts';
67
67
  export * from './oauth-origin.ts';
68
68
  export * from './oauth-profile.ts';
69
- export { createWindowsOwnerOnly, ensureWindowsOwnerOnlyDirectory, isOwnerOnly, type OwnerOnlyOptions, } from './owner-only.ts';
69
+ export { createWindowsOwnerOnly, ensureWindowsOwnerOnlyDirectory, isOwnerOnly, type OwnerOnlyOptions, readWindowsOwnerOnly, } from './owner-only.ts';
70
70
  export * from './pkce.ts';
71
71
  export type { NativeEmptyInitJournal, NativeLegacyMigrationJournal, NativeMigrationJournal, } from './pool-authority.ts';
72
72
  export { nativeJournalMayAdoptLaterHostLogin, nativeMigrationAuthorityPhase, readNativeMigrationJournal, } from './pool-authority.ts';
@@ -78,6 +78,7 @@ export * from './prime.ts';
78
78
  export * from './provider.ts';
79
79
  export type { ProviderErrorRedactor, ProviderErrorResponseScope, ProviderErrorSummary, ProviderResponseMode, ProviderResponseSettlement, } from './provider-error.ts';
80
80
  export { createProviderErrorRedactor, createProviderErrorResponseScope, summarizePrewarmProviderError, } from './provider-error.ts';
81
+ export { renameWithWindowsSharingRetry } from './publication-rename.ts';
81
82
  export * from './quota-header-feed.ts';
82
83
  export * from './quota-headers.ts';
83
84
  export * from './quota-manager.ts';
package/dist/index.js CHANGED
@@ -729,7 +729,7 @@ async function withLock(target, options, fn) {
729
729
  options.signal?.throwIfAborted();
730
730
  return await fn(lock);
731
731
  } finally {
732
- await lock.release();
732
+ await lock.release(options.signal);
733
733
  }
734
734
  }
735
735
  const remaining = options.timeoutMs - (performance.now() - started);
@@ -1387,7 +1387,7 @@ async function acquireRefreshFileLockWithClock(options) {
1387
1387
  ...typeof observed?.expiresAt === "number" ? { observedExpiresAt: observed.expiresAt } : {}
1388
1388
  });
1389
1389
  },
1390
- release: async () => {
1390
+ release: async (signal) => {
1391
1391
  if (windowsPrivateFiles && releaseConfirmed)
1392
1392
  return;
1393
1393
  released = true;
@@ -1428,6 +1428,8 @@ async function acquireRefreshFileLockWithClock(options) {
1428
1428
  releaseConfirmed = removalConfirmed;
1429
1429
  return;
1430
1430
  }
1431
+ if (signal?.aborted)
1432
+ return;
1431
1433
  await recoverStaleEvictionMarker();
1432
1434
  } catch (error) {
1433
1435
  if (windowsPrivateFiles)
@@ -7982,15 +7984,27 @@ function credentialTypeOf(row, family) {
7982
7984
  return notMine;
7983
7985
  if (!row.categories.includes(family.category))
7984
7986
  return notMine;
7985
- if (row.credentialType !== "oauth" && row.credentialType !== "api_key")
7986
- return notMine;
7987
7987
  if ("classify" in family) {
7988
+ const metadata = rowFactsOf(row);
7989
+ let type;
7990
+ switch (metadata.authMethod) {
7991
+ case "apikey":
7992
+ type = "api_key";
7993
+ break;
7994
+ case "chatgpt":
7995
+ case "antigravity":
7996
+ case "oauth":
7997
+ type = "oauth";
7998
+ break;
7999
+ default:
8000
+ return { kind: "refused", code: "unknown-auth-method" };
8001
+ }
7988
8002
  const facts = {
7989
8003
  credentialId: row.id,
7990
- credentialType: row.credentialType,
8004
+ credentialType: type,
7991
8005
  categories: row.categories,
7992
8006
  refreshAdapter: row.refreshAdapter,
7993
- ...rowFactsOf(row)
8007
+ ...metadata
7994
8008
  };
7995
8009
  if (facts.providerIds === undefined)
7996
8010
  return { kind: "refused", code: "unmapped" };
@@ -7999,13 +8013,15 @@ function credentialTypeOf(row, family) {
7999
8013
  if (result.kind === "not-mine")
8000
8014
  return notMine;
8001
8015
  if (result.kind === "mine" && typeof result.member === "string")
8002
- return { kind: "mine", type: row.credentialType, member: result.member };
8016
+ return { kind: "mine", type, member: result.member };
8003
8017
  if (result.kind === "refused" && typeof result.code === "string" && /^[a-z][a-z0-9-]{0,31}$/.test(result.code) && !result.code.includes(`
8004
8018
  `) && family.refusalCodes.includes(result.code))
8005
8019
  return { kind: "refused", code: result.code };
8006
8020
  } catch {}
8007
8021
  return { kind: "refused", code: "unclassified" };
8008
8022
  }
8023
+ if (row.credentialType !== "oauth" && row.credentialType !== "api_key")
8024
+ return notMine;
8009
8025
  if (row.credentialType === "oauth")
8010
8026
  return row.refreshAdapter === family.refreshAdapter ? { kind: "mine", type: "oauth" } : notMine;
8011
8027
  if (row.credentialType === "api_key" && family.apiKeys)
@@ -9586,7 +9602,7 @@ async function refreshVaultRoster(options) {
9586
9602
  pendingDiscoveries.set(key, discovered);
9587
9603
  throw error;
9588
9604
  } finally {
9589
- await lease.release();
9605
+ await lease.release(options.signal);
9590
9606
  }
9591
9607
  }
9592
9608
 
@@ -10015,8 +10031,16 @@ class ClaustrumConsumer {
10015
10031
  this.#refreshing = undefined;
10016
10032
  });
10017
10033
  this.#track(this.#refreshing);
10034
+ this.#notifyDiscovery(this.#refreshing);
10018
10035
  return this.#refreshing;
10019
10036
  }
10037
+ #notifyDiscovery(promise) {
10038
+ try {
10039
+ this.#options.onDiscovery?.(promise);
10040
+ } catch (error) {
10041
+ this.#options.onError?.(error);
10042
+ }
10043
+ }
10020
10044
  start() {
10021
10045
  if (this.#started)
10022
10046
  return;
@@ -10199,6 +10223,13 @@ class ClaustrumConsumer {
10199
10223
  }
10200
10224
  #hubHooks() {
10201
10225
  return {
10226
+ discovery: (promise) => {
10227
+ const hub = this.#hub;
10228
+ if (!hub || !this.#options.onDiscovery || this.#shutdown.signal.aborted)
10229
+ return;
10230
+ const discovery = this.#track(promise.then((outcome) => this.#hubOutcome(hub, outcome)));
10231
+ this.#notifyDiscovery(discovery);
10232
+ },
10202
10233
  install: (roster) => {
10203
10234
  if (this.#shutdown.signal.aborted)
10204
10235
  return;
@@ -10229,6 +10260,9 @@ class ClaustrumConsumer {
10229
10260
  }
10230
10261
  async#hubRefresh(hub) {
10231
10262
  const outcome = await hub.refresh();
10263
+ return this.#hubOutcome(hub, outcome);
10264
+ }
10265
+ #hubOutcome(hub, outcome) {
10232
10266
  this.#assertOpen();
10233
10267
  if (!outcome)
10234
10268
  return;
@@ -10365,6 +10399,8 @@ class ClaustrumConsumer {
10365
10399
  this.#assertOpen();
10366
10400
  await declineVaultRoute(this.#options.rosterPath, routeId);
10367
10401
  this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
10402
+ if (this.#roster)
10403
+ this.#hub?.publishRoster(this.#roster);
10368
10404
  if (this.#hub)
10369
10405
  this.#hub.retainUnion();
10370
10406
  else
@@ -10374,11 +10410,15 @@ class ClaustrumConsumer {
10374
10410
  this.#assertOpen();
10375
10411
  await acceptVaultRoute(this.#options.rosterPath, routeId);
10376
10412
  this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
10413
+ if (this.#roster)
10414
+ this.#hub?.publishRoster(this.#roster);
10377
10415
  }
10378
10416
  async reorder(routeIds) {
10379
10417
  this.#assertOpen();
10380
10418
  const result = await reorderVaultRoster(this.#options.rosterPath, routeIds);
10381
10419
  this.#roster = pendingVaultQuota(this.#options.rosterPath, await readVaultRoster(this.#options.rosterPath));
10420
+ if (this.#roster)
10421
+ this.#hub?.publishRoster(this.#roster);
10382
10422
  return result;
10383
10423
  }
10384
10424
  async recordQuota(routeId, observation, attempt) {
@@ -10438,7 +10478,8 @@ class ClaustrumConsumer {
10438
10478
  // ../../node_modules/@cortexkit/common-auth/dist/claustrum/hub.js
10439
10479
  import { createHash as createHash6 } from "node:crypto";
10440
10480
  import { readdir as readdir2, stat as stat7 } from "node:fs/promises";
10441
- import { dirname as dirname8, join as join10 } from "node:path";
10481
+ import { dirname as dirname8, join as join10, resolve as resolve9 } from "node:path";
10482
+ var sharedRosters = new Map;
10442
10483
  var FILE_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
10443
10484
  var WINDOWS_DEVICE = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\.|$)/;
10444
10485
  var FINGERPRINT = /^[0-9a-f]{16}$/;
@@ -10501,12 +10542,12 @@ function unreachableConnect2() {
10501
10542
  return new ClaustrumUnreachableError;
10502
10543
  }
10503
10544
  function untilAborted(pending, signal) {
10504
- return new Promise((resolve9, reject) => {
10545
+ return new Promise((resolve10, reject) => {
10505
10546
  const abort = () => reject(signal.reason);
10506
10547
  signal.addEventListener("abort", abort, { once: true });
10507
10548
  pending.then((value) => {
10508
10549
  signal.removeEventListener("abort", abort);
10509
- resolve9(value);
10550
+ resolve10(value);
10510
10551
  }, (error) => {
10511
10552
  signal.removeEventListener("abort", abort);
10512
10553
  reject(error);
@@ -10609,9 +10650,11 @@ class ClaustrumDiscoveryHub {
10609
10650
  watchMode: () => this.watchState(),
10610
10651
  keptReceiptState: () => this.keptReceiptState(),
10611
10652
  retainUnion: () => this.#retainUnion(),
10653
+ publishRoster: (roster) => this.#publishRoster(entry, roster),
10612
10654
  forget: (credentialId) => this.#current?.custody?.forget(credentialId),
10613
10655
  detach: () => {
10614
10656
  entry.closed = true;
10657
+ entry.releaseRoster?.();
10615
10658
  }
10616
10659
  };
10617
10660
  const consumerOptions = {
@@ -10624,14 +10667,51 @@ class ClaustrumDiscoveryHub {
10624
10667
  },
10625
10668
  ...this.#options.mapAccount && { mapAccount: this.#options.mapAccount },
10626
10669
  ...options.onRoster && { onRoster: options.onRoster },
10670
+ ...options.onDiscovery && { onDiscovery: options.onDiscovery },
10627
10671
  ...options.onError && { onError: options.onError },
10628
10672
  logger: options.logger ?? this.#logger,
10629
10673
  ...this.#options.now && { now: this.#options.now }
10630
10674
  };
10631
10675
  const { consumer, hooks } = createHubMember(port, consumerOptions);
10632
10676
  entry.hooks = hooks;
10677
+ const path2 = resolve9(entry.path);
10678
+ const subscribers = sharedRosters.get(path2) ?? new Set;
10679
+ const install = (roster) => {
10680
+ if (entry.closed || this.#active !== true || !this.#current || !this.#live(this.#current) || !hooks.roster() || this.#collides(hooks, roster))
10681
+ return;
10682
+ entry.seen = JSON.stringify(decodedVaultRoster(roster));
10683
+ hooks.install(roster);
10684
+ if (roster.view !== this.#committed?.view)
10685
+ entry.pending ??= { failures: 0, wait: 0 };
10686
+ this.#retainUnion();
10687
+ };
10688
+ subscribers.add(install);
10689
+ entry.receiveRoster = install;
10690
+ sharedRosters.set(path2, subscribers);
10691
+ let subscribed = true;
10692
+ entry.releaseRoster = () => {
10693
+ if (!subscribed)
10694
+ return;
10695
+ subscribed = false;
10696
+ subscribers.delete(install);
10697
+ if (!subscribers.size)
10698
+ sharedRosters.delete(path2);
10699
+ };
10633
10700
  return consumer;
10634
10701
  }
10702
+ #publishRoster(entry, roster) {
10703
+ const generation = this.#current;
10704
+ if (entry.closed || !generation || !this.#live(generation))
10705
+ return;
10706
+ entry.seen = JSON.stringify(decodedVaultRoster(roster));
10707
+ entry.hooks?.install(roster);
10708
+ for (const install of sharedRosters.get(resolve9(entry.path)) ?? []) {
10709
+ if (!this.#live(generation))
10710
+ return;
10711
+ if (install !== entry.receiveRoster)
10712
+ install(roster);
10713
+ }
10714
+ }
10635
10715
  #startGeneration() {
10636
10716
  const generation = {
10637
10717
  id: ++this.#generations,
@@ -10896,6 +10976,8 @@ class ClaustrumDiscoveryHub {
10896
10976
  }));
10897
10977
  run.promise = promise;
10898
10978
  this.#run = run;
10979
+ for (const entry of this.#open())
10980
+ entry.hooks?.discovery(promise);
10899
10981
  return promise;
10900
10982
  }
10901
10983
  async#discover(run) {
@@ -10966,9 +11048,7 @@ class ClaustrumDiscoveryHub {
10966
11048
  this.#markPending(entry);
10967
11049
  continue;
10968
11050
  }
10969
- entry.seen = JSON.stringify(decodedVaultRoster(roster));
10970
- if (!entry.closed)
10971
- hooks.install(roster);
11051
+ this.#publishRoster(entry, roster);
10972
11052
  }
10973
11053
  if (!this.#live(generation))
10974
11054
  return;
@@ -11077,7 +11157,7 @@ class ClaustrumDiscoveryHub {
11077
11157
  }
11078
11158
  const before = enabledRows(memory);
11079
11159
  entry.seen = content;
11080
- hooks.install(file);
11160
+ this.#publishRoster(entry, file);
11081
11161
  if (enabledRows(hooks.roster()) !== before)
11082
11162
  this.#retainUnion();
11083
11163
  }
@@ -11198,6 +11278,8 @@ class ClaustrumDiscoveryHub {
11198
11278
  if (this.#shutdown.signal.aborted)
11199
11279
  return;
11200
11280
  this.#shutdown.abort(closed("Claustrum discovery hub is closed"));
11281
+ for (const entry of this.#members.values())
11282
+ entry.releaseRoster?.();
11201
11283
  if (this.#timer)
11202
11284
  (this.#options.clearTimeoutImpl ?? clearTimeout)(this.#timer);
11203
11285
  this.#timer = undefined;
@@ -11261,7 +11343,7 @@ import {
11261
11343
  extname as extname2,
11262
11344
  isAbsolute as isAbsolute3,
11263
11345
  join as join12,
11264
- resolve as resolve9
11346
+ resolve as resolve10
11265
11347
  } from "node:path";
11266
11348
  import {
11267
11349
  ClaustrumCredentialError as ClaustrumCredentialError5,
@@ -11664,7 +11746,7 @@ function getClaustrumEnrollmentPaths2(tokenPath) {
11664
11746
  }
11665
11747
  function getHostClaustrumEnrollmentPaths(host, env = process.env, cwd = process.cwd()) {
11666
11748
  const configured = env[`${host.toUpperCase()}_ANTHROPIC_AUTH_CLAUSTRUM_ENROLLMENT_FILE`]?.trim();
11667
- const tokenPath = configured ? isAbsolute3(configured) ? configured : resolve9(cwd, configured) : join12(anthropicAuthStateRoot({
11749
+ const tokenPath = configured ? isAbsolute3(configured) ? configured : resolve10(cwd, configured) : join12(anthropicAuthStateRoot({
11668
11750
  xdgStateHome: env.XDG_STATE_HOME,
11669
11751
  home: homedir4(),
11670
11752
  acceptRelative: true
@@ -12216,8 +12298,8 @@ function createNativeCustody(options) {
12216
12298
  let stopped = false;
12217
12299
  let inner;
12218
12300
  let finish = () => {};
12219
- const ended = new Promise((resolve10) => {
12220
- finish = resolve10;
12301
+ const ended = new Promise((resolve11) => {
12302
+ finish = resolve11;
12221
12303
  });
12222
12304
  const failed = (error) => stopped || closed2 || error instanceof NativeCustodyError ? { kind: "stopped" } : classifyVaultFailure(error) === "unreachable" ? { kind: "transport_lost", error: new ClaustrumUnreachableError } : { kind: "ended" };
12223
12305
  Promise.resolve().then(connection).then((owned) => {
@@ -13877,8 +13959,8 @@ function createProviderErrorResponseScope(secrets, mode) {
13877
13959
  wrappedBodies: 0
13878
13960
  };
13879
13961
  let resolveSettled = () => {};
13880
- const settled = new Promise((resolve10) => {
13881
- resolveSettled = resolve10;
13962
+ const settled = new Promise((resolve11) => {
13963
+ resolveSettled = resolve11;
13882
13964
  });
13883
13965
  let retired = false;
13884
13966
  let used = false;
@@ -14287,6 +14369,7 @@ class CacheKeepManager {
14287
14369
  if (!this.targets.has(id))
14288
14370
  return false;
14289
14371
  this.targets.delete(id);
14372
+ this.options.onTargetRemoved?.(id);
14290
14373
  this.publishTrackedSessions();
14291
14374
  return true;
14292
14375
  }
@@ -14332,6 +14415,7 @@ class CacheKeepManager {
14332
14415
  ...details
14333
14416
  });
14334
14417
  this.targets.delete(target.id);
14418
+ this.options.onTargetRemoved?.(target.id);
14335
14419
  }
14336
14420
  evictOldestTarget(now, reason) {
14337
14421
  const oldest = this.targets.values().next().value;
@@ -14349,8 +14433,10 @@ class CacheKeepManager {
14349
14433
  this.removeTarget(target, "schedule-period-ended", now);
14350
14434
  continue;
14351
14435
  }
14352
- if (now >= target.cacheExpiresAt)
14436
+ if (now >= target.cacheExpiresAt) {
14353
14437
  this.targets.delete(id);
14438
+ this.options.onTargetRemoved?.(id);
14439
+ }
14354
14440
  }
14355
14441
  while (this.targets.size > CACHE_KEEP_MAX_TARGETS)
14356
14442
  this.evictOldestTarget(now, "count");
@@ -14363,6 +14449,8 @@ class CacheKeepManager {
14363
14449
  }
14364
14450
  track(input) {
14365
14451
  const now = this.options.now?.() ?? Date.now();
14452
+ if (input.retained && input.retained.cacheExpiresAt <= now)
14453
+ return { tracked: false, reason: "cache expired" };
14366
14454
  let bodyBytes;
14367
14455
  const refuse = (reason, reasonCode) => {
14368
14456
  if (reasonCode === "body-budget")
@@ -14414,8 +14502,8 @@ class CacheKeepManager {
14414
14502
  headers,
14415
14503
  bodyText: input.bodyText,
14416
14504
  bodyBytes,
14417
- cacheExpiresAt: now + CACHE_KEEP_TTL_MS,
14418
- nextPrewarmAt: now + CACHE_KEEP_TTL_MS - CACHE_KEEP_PREWARM_LEAD_MS,
14505
+ cacheExpiresAt: input.retained?.cacheExpiresAt ?? now + CACHE_KEEP_TTL_MS,
14506
+ nextPrewarmAt: input.retained?.nextPrewarmAt ?? now + CACHE_KEEP_TTL_MS - CACHE_KEEP_PREWARM_LEAD_MS,
14419
14507
  consecutiveFailures: 0,
14420
14508
  dayKey: today,
14421
14509
  oauthAccountId: input.oauthAccountId,
@@ -14426,8 +14514,10 @@ class CacheKeepManager {
14426
14514
  });
14427
14515
  this.pruneTargets(now, today);
14428
14516
  this.publishTrackedSessions();
14429
- if (!this.targets.has(input.sessionId))
14517
+ const target = this.targets.get(input.sessionId);
14518
+ if (!target)
14430
14519
  return { tracked: false, reason: "evicted by cachekeep limits" };
14520
+ this.options.onTargetChanged?.(target);
14431
14521
  this.start();
14432
14522
  return { tracked: true };
14433
14523
  }
@@ -14504,6 +14594,9 @@ class CacheKeepManager {
14504
14594
  });
14505
14595
  target.consecutiveFailures += 1;
14506
14596
  target.nextPrewarmAt = now + cacheKeepRetryDelayMs(target.id, target.consecutiveFailures);
14597
+ } finally {
14598
+ if (this.targets.get(target.id) === target)
14599
+ this.options.onTargetChanged?.(target);
14507
14600
  }
14508
14601
  }
14509
14602
  this.publishTrackedSessions();
@@ -14780,7 +14873,7 @@ import { basename as basename8, dirname as dirname15, join as join16 } from "nod
14780
14873
  // src/pool-paths.ts
14781
14874
  import { createHash as createHash17 } from "node:crypto";
14782
14875
  import { realpath as realpath3 } from "node:fs/promises";
14783
- import { basename as basename7, dirname as dirname14, join as join15, resolve as resolve10 } from "node:path";
14876
+ import { basename as basename7, dirname as dirname14, join as join15, resolve as resolve11 } from "node:path";
14784
14877
 
14785
14878
  // src/sticky-routing.ts
14786
14879
  import { createHash as createHash16, randomUUID as randomUUID12 } from "node:crypto";
@@ -14956,7 +15049,7 @@ async function acquirePoolLock(spec, defaults, env) {
14956
15049
  });
14957
15050
  }
14958
15051
  const jitter = Math.floor(Math.random() * (options.retryMs + 1));
14959
- await new Promise((resolve10) => setTimeout(resolve10, Math.min(options.retryMs + jitter, remaining)));
15052
+ await new Promise((resolve11) => setTimeout(resolve11, Math.min(options.retryMs + jitter, remaining)));
14960
15053
  }
14961
15054
  }
14962
15055
  function definedOnly(spec) {
@@ -17598,11 +17691,11 @@ class PoolQuotaWriter {
17598
17691
  const left = deadline - performance.now();
17599
17692
  if (left <= 0)
17600
17693
  throw refusal("pull", id, "lock-contention", `pool quota queue for ${id} is full (${POOL_QUOTA_QUEUE_LIMIT} readings) and the store locks stayed held`, true);
17601
- await new Promise((resolve10) => {
17694
+ await new Promise((resolve11) => {
17602
17695
  const done = () => {
17603
17696
  clearTimeout(timer);
17604
17697
  this.wake.delete(done);
17605
- resolve10();
17698
+ resolve11();
17606
17699
  };
17607
17700
  const timer = setTimeout(done, left);
17608
17701
  this.wake.add(done);
@@ -18911,7 +19004,7 @@ async function renameWithWindowsSharingRetry(from, to, options = {}, beforeRetry
18911
19004
  return "published";
18912
19005
  }
18913
19006
  const now = options.now ?? (() => performance.now());
18914
- const sleep = options.sleep ?? ((ms) => new Promise((resolve10) => setTimeout(resolve10, ms)));
19007
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve11) => setTimeout(resolve11, ms)));
18915
19008
  const start = now();
18916
19009
  let original;
18917
19010
  try {
@@ -20310,6 +20403,141 @@ function nativeVaultLegacyRoutes(source2, roster) {
20310
20403
  return routes;
20311
20404
  }
20312
20405
 
20406
+ // src/quota-headers.ts
20407
+ var PREFIX = "anthropic-ratelimit-unified-";
20408
+ var WINDOW_KEYS = {
20409
+ "5h": "five_hour",
20410
+ "7d": "seven_day"
20411
+ };
20412
+ function isQuotaBearingHeaderFrame(headers) {
20413
+ return Object.keys(WINDOW_KEYS).some((suffix) => Number.isFinite(finiteHeaderNumber(headers, `${PREFIX}${suffix}-utilization`)));
20414
+ }
20415
+ function finiteHeaderNumber(headers, name) {
20416
+ const value = headers.get(name);
20417
+ if (value == null || value.trim() === "")
20418
+ return;
20419
+ const parsed = Number(value);
20420
+ return Number.isFinite(parsed) ? parsed : undefined;
20421
+ }
20422
+ function headerResetAt(headers, suffix) {
20423
+ const seconds = finiteHeaderNumber(headers, `${PREFIX}${suffix}-reset`);
20424
+ if (seconds == null)
20425
+ return;
20426
+ const reset = new Date(seconds * 1000);
20427
+ return Number.isFinite(reset.getTime()) ? reset.toISOString() : undefined;
20428
+ }
20429
+ function normalizeWindow(headers, suffix, checkedAt) {
20430
+ const utilization = finiteHeaderNumber(headers, `${PREFIX}${suffix}-utilization`);
20431
+ if (utilization == null)
20432
+ return;
20433
+ const usedPercent = Math.min(100, Math.max(0, Math.round(utilization * 100)));
20434
+ const resetsAt = headerResetAt(headers, suffix);
20435
+ return {
20436
+ usedPercent,
20437
+ remainingPercent: 100 - usedPercent,
20438
+ ...resetsAt && { resetsAt },
20439
+ checkedAt
20440
+ };
20441
+ }
20442
+ function quotaExhaustionFromHeaders(headers, modelId, now = Date.now()) {
20443
+ const snapshot = {};
20444
+ const exhausted = (suffix) => {
20445
+ const utilization = finiteHeaderNumber(headers, `${PREFIX}${suffix}-utilization`);
20446
+ if (headers.get(`${PREFIX}${suffix}-status`) !== "rejected" && (utilization == null || utilization < 1))
20447
+ return;
20448
+ return {
20449
+ usedPercent: 100,
20450
+ remainingPercent: 0,
20451
+ checkedAt: now,
20452
+ resetsAt: headerResetAt(headers, suffix)
20453
+ };
20454
+ };
20455
+ for (const [suffix, key] of Object.entries(WINDOW_KEYS)) {
20456
+ const window2 = exhausted(suffix);
20457
+ if (window2)
20458
+ snapshot[key] = window2;
20459
+ }
20460
+ for (const name of headers.keys()) {
20461
+ const match = /^anthropic-ratelimit-unified-(7d[_-]([a-z0-9]+))-(?:status|utilization)$/.exec(name);
20462
+ if (!match || !modelId?.toLowerCase().split(/[-_.]/).includes(match[2]))
20463
+ continue;
20464
+ const window2 = exhausted(match[1]);
20465
+ if (window2)
20466
+ snapshot.scoped = [
20467
+ {
20468
+ ...window2,
20469
+ id: match[1],
20470
+ title: match[2],
20471
+ modelName: match[2],
20472
+ modelId
20473
+ }
20474
+ ];
20475
+ }
20476
+ return snapshot;
20477
+ }
20478
+ function normalizeQuotaHeaders(headers, now = Date.now()) {
20479
+ const fieldSources2 = {};
20480
+ const fallbackHeader = headers.get(`${PREFIX}fallback`);
20481
+ const snapshot = {
20482
+ fallbackAdvised: fallbackHeader === "available",
20483
+ source: "headers",
20484
+ checkedAt: now
20485
+ };
20486
+ for (const [suffix, key] of Object.entries(WINDOW_KEYS)) {
20487
+ const window2 = normalizeWindow(headers, suffix, now);
20488
+ if (window2) {
20489
+ snapshot[key] = window2;
20490
+ fieldSources2[key] = "headers";
20491
+ }
20492
+ }
20493
+ const representativeClaim = headers.get(`${PREFIX}representative-claim`);
20494
+ if (representativeClaim) {
20495
+ snapshot.bindingWindow = representativeClaim;
20496
+ snapshot.bindingWindowSource = "headers";
20497
+ fieldSources2.bindingWindow = "headers";
20498
+ }
20499
+ if (fallbackHeader !== null)
20500
+ fieldSources2.fallbackAdvised = "headers";
20501
+ return {
20502
+ ...snapshot,
20503
+ fieldSources: fieldSources2
20504
+ };
20505
+ }
20506
+ function mergeHeaderQuotaSnapshot(existing, incoming) {
20507
+ const {
20508
+ scoped: existingScoped,
20509
+ extraUsage: existingExtraUsage,
20510
+ fieldSources: existingFieldSources,
20511
+ ...existingWithoutPollOwnedFields
20512
+ } = existing ?? {};
20513
+ const {
20514
+ scoped: _incomingScoped,
20515
+ extraUsage: _incomingExtraUsage,
20516
+ fieldSources: incomingFieldSources,
20517
+ ...incomingWithoutPollOwnedFields
20518
+ } = incoming;
20519
+ const fieldSources2 = {};
20520
+ for (const field of QUOTA_FIELD_NAMES) {
20521
+ const value = field === "scoped" ? existingScoped : field === "extraUsage" ? existingExtraUsage : field === "fallbackAdvised" ? incomingFieldSources?.fallbackAdvised !== undefined ? incoming.fallbackAdvised : existing?.fallbackAdvised ?? incoming.fallbackAdvised : field === "bindingWindow" ? existing?.bindingWindowSource === "poll" ? existing?.bindingWindow : incoming.bindingWindow ?? existing?.bindingWindow : incoming[field] ?? existing?.[field];
20522
+ if (value === undefined)
20523
+ continue;
20524
+ const source2 = field === "fallbackAdvised" ? incomingFieldSources?.fallbackAdvised ?? (existing?.fallbackAdvised !== undefined ? existingFieldSources ? existingFieldSources.fallbackAdvised : quotaFieldSource(existing, field) : undefined) : (field === "scoped" || field === "extraUsage") && value !== undefined ? "poll" : field === "bindingWindow" && existing?.bindingWindowSource === "poll" && existing.bindingWindow !== undefined ? "poll" : incoming[field] !== undefined ? incomingFieldSources?.[field] ?? quotaFieldSource(incoming, field) : existingFieldSources?.[field] ?? quotaFieldSource(existing, field);
20525
+ if (source2)
20526
+ fieldSources2[field] = source2;
20527
+ }
20528
+ return {
20529
+ ...existingWithoutPollOwnedFields,
20530
+ ...incomingWithoutPollOwnedFields,
20531
+ ...Array.isArray(existingScoped) && { scoped: existingScoped },
20532
+ ...existingExtraUsage != null && { extraUsage: existingExtraUsage },
20533
+ fallbackAdvised: incomingFieldSources?.fallbackAdvised !== undefined ? incoming.fallbackAdvised : existing?.fallbackAdvised ?? incoming.fallbackAdvised,
20534
+ bindingWindow: existing?.bindingWindowSource === "poll" ? existing.bindingWindow : incoming.bindingWindow ?? existing?.bindingWindow,
20535
+ bindingWindowSource: existing?.bindingWindowSource === "poll" ? "poll" : incoming.bindingWindowSource ?? existing?.bindingWindowSource,
20536
+ ...Object.keys(fieldSources2).length > 0 && { fieldSources: fieldSources2 },
20537
+ source: "headers"
20538
+ };
20539
+ }
20540
+
20313
20541
  // src/sticky-routing.ts
20314
20542
  var STICKY_ROUTING_MAIN_ACCOUNT_ID = "main";
20315
20543
  var STICKY_ROUTING_SHORT_RESET_GRACE_MS = 15 * 60000;
@@ -20532,7 +20760,16 @@ function stickyRouteKnownFableExhausted(candidate, storage, now = Date.now()) {
20532
20760
  }
20533
20761
  function decideStickyQuotaFailure(input) {
20534
20762
  const now = input.now ?? Date.now();
20535
- const quota2 = knownQuota(input.quota);
20763
+ let reading2 = knownQuota(input.quota);
20764
+ const confirmed = input.responseHeaders ? quotaExhaustionFromHeaders(input.responseHeaders, input.modelId, now) : undefined;
20765
+ if (reading2 && input.responseHeaders) {
20766
+ reading2 = { ...reading2 };
20767
+ for (const key of ["five_hour", "seven_day", "scoped"]) {
20768
+ if (quotaFieldSource(reading2, key) === "headers")
20769
+ delete reading2[key];
20770
+ }
20771
+ }
20772
+ const quota2 = reading2 || confirmed && Object.keys(confirmed).length ? { ...reading2, ...confirmed } : undefined;
20536
20773
  if (!quota2)
20537
20774
  return { action: "retain", reason: "unknown" };
20538
20775
  let blocking;
@@ -20560,6 +20797,31 @@ function decideStickyQuotaFailure(input) {
20560
20797
  }
20561
20798
  return { action: "migrate", reason: blocking.reason };
20562
20799
  }
20800
+ function stickyRetainRetryHeaders(upstream, now = Date.now()) {
20801
+ const headers = new Headers(upstream);
20802
+ let seconds = 60;
20803
+ const shorten = (value) => {
20804
+ if (Number.isFinite(value))
20805
+ seconds = Math.min(seconds, Math.max(0, value));
20806
+ };
20807
+ const after = headers.get("retry-after");
20808
+ if (after?.trim()) {
20809
+ shorten(Number.isFinite(Number(after)) ? Number(after) : (Date.parse(after) - now) / 1000);
20810
+ }
20811
+ const ms = headers.get("retry-after-ms");
20812
+ if (ms?.trim())
20813
+ shorten(Number(ms) / 1000);
20814
+ const resets = [...headers.keys()].filter((name) => /^anthropic-ratelimit-.*-reset$/.test(name));
20815
+ for (const name of resets) {
20816
+ const value = headers.get(name);
20817
+ if (!value.trim())
20818
+ continue;
20819
+ shorten((Number.isFinite(Number(value)) ? Number(value) * 1000 : Date.parse(value)) / 1000 - now / 1000);
20820
+ }
20821
+ headers.set("retry-after", String(seconds));
20822
+ headers.set("retry-after-ms", String(seconds * 1000));
20823
+ return headers;
20824
+ }
20563
20825
  function getStickyRoutingStatePath(accountStoragePath) {
20564
20826
  const name = basename6(accountStoragePath);
20565
20827
  const stem = name.endsWith(".json") ? name.slice(0, -5) : name;
@@ -20664,7 +20926,7 @@ class StickySessionRouter {
20664
20926
  if (!wait || performance.now() >= deadline) {
20665
20927
  throw new Error("Timed out acquiring sticky routing state lock");
20666
20928
  }
20667
- await new Promise((resolve10) => setTimeout(resolve10, STICKY_ROUTING_LOCK_POLL_MS));
20929
+ await new Promise((resolve11) => setTimeout(resolve11, STICKY_ROUTING_LOCK_POLL_MS));
20668
20930
  }
20669
20931
  }
20670
20932
  recoverLocalPins() {
@@ -21035,7 +21297,7 @@ function resolveLocalRoutingDestination(host, paths, env) {
21035
21297
  return (host === "opencode" ? env.OPENCODE_ANTHROPIC_AUTH_ROUTING_STATE_FILE : env.PI_ANTHROPIC_AUTH_ROUTING_STATE_FILE) || getStickyRoutingStatePath(paths.config);
21036
21298
  }
21037
21299
  async function canonicalPath(path2) {
21038
- const absolute = resolve10(path2);
21300
+ const absolute = resolve11(path2);
21039
21301
  try {
21040
21302
  return await realpath3(absolute);
21041
21303
  } catch (error) {
@@ -21336,7 +21598,7 @@ class StateLayoutLockStack {
21336
21598
  name: spec.name,
21337
21599
  timeoutMs
21338
21600
  });
21339
- await new Promise((resolve11) => setTimeout(resolve11, Math.min(remaining, POOL_LOCK_DEFAULTS.retryMs + Math.floor(Math.random() * 25))));
21601
+ await new Promise((resolve12) => setTimeout(resolve12, Math.min(remaining, POOL_LOCK_DEFAULTS.retryMs + Math.floor(Math.random() * 25))));
21340
21602
  }
21341
21603
  }
21342
21604
  async assertAll() {
@@ -22004,10 +22266,10 @@ async function linuxIncarnation(pid) {
22004
22266
  }
22005
22267
  }
22006
22268
  function macosIncarnation(pid) {
22007
- return new Promise((resolve11) => {
22269
+ return new Promise((resolve12) => {
22008
22270
  execFile("ps", ["-o", "lstart=", "-p", String(pid)], { env: { ...process.env, TZ: "UTC0" }, timeout: 5000 }, (error, stdout) => {
22009
22271
  const text5 = String(stdout ?? "").trim();
22010
- resolve11(error || !text5 ? null : text5);
22272
+ resolve12(error || !text5 ? null : text5);
22011
22273
  });
22012
22274
  });
22013
22275
  }
@@ -22277,8 +22539,8 @@ function retryInBackground(attempt, options) {
22277
22539
  let timer;
22278
22540
  let closed2 = false;
22279
22541
  let settle = () => {};
22280
- const settled = new Promise((resolve11) => {
22281
- settle = resolve11;
22542
+ const settled = new Promise((resolve12) => {
22543
+ settle = resolve12;
22282
22544
  });
22283
22545
  const schedule2 = () => {
22284
22546
  if (closed2)
@@ -23921,13 +24183,13 @@ function logCommittedNativeAccountAdded(account) {
23921
24183
  }
23922
24184
 
23923
24185
  // src/native-vault-invalidation.ts
23924
- import { resolve as resolve11 } from "node:path";
24186
+ import { resolve as resolve12 } from "node:path";
23925
24187
  var holders = new Set;
23926
24188
  function watchNativeVaultAuthority(holder) {
23927
24189
  const held = {
23928
24190
  ...holder,
23929
- roster: resolve11(holder.roster),
23930
- tokenPath: resolve11(holder.tokenPath)
24191
+ roster: resolve12(holder.roster),
24192
+ tokenPath: resolve12(holder.tokenPath)
23931
24193
  };
23932
24194
  holders.add(held);
23933
24195
  return () => {
@@ -23936,12 +24198,12 @@ function watchNativeVaultAuthority(holder) {
23936
24198
  }
23937
24199
  function retainNativeVaultRoster(path2, roster) {
23938
24200
  for (const holder of holders)
23939
- if (holder.roster === resolve11(path2))
24201
+ if (holder.roster === resolve12(path2))
23940
24202
  holder.retain(roster);
23941
24203
  }
23942
24204
  function closeNativeVaultAuthority(tokenPath) {
23943
24205
  for (const holder of holders)
23944
- if (holder.tokenPath === resolve11(tokenPath))
24206
+ if (holder.tokenPath === resolve12(tokenPath))
23945
24207
  holder.close();
23946
24208
  }
23947
24209
 
@@ -24250,12 +24512,12 @@ async function joinNativePoolWrite(paths, signals, check, onLockEvent) {
24250
24512
  function pause(ms, signals) {
24251
24513
  if (signals.some((signal) => signal.aborted))
24252
24514
  return Promise.resolve();
24253
- return new Promise((resolve12) => {
24515
+ return new Promise((resolve13) => {
24254
24516
  const wake = () => {
24255
24517
  clearTimeout(timer);
24256
24518
  for (const signal of signals)
24257
24519
  signal.removeEventListener("abort", wake);
24258
- resolve12();
24520
+ resolve13();
24259
24521
  };
24260
24522
  const timer = setTimeout(wake, ms);
24261
24523
  for (const signal of signals)
@@ -24511,7 +24773,7 @@ function createNativeRefreshCoordinator(options) {
24511
24773
  name: spec.name,
24512
24774
  timeoutMs: POOL_LOCK_DEFAULTS.timeoutMs
24513
24775
  });
24514
- await new Promise((resolve12) => setTimeout(resolve12, Math.min(POOL_LOCK_DEFAULTS.retryMs, remaining)));
24776
+ await new Promise((resolve13) => setTimeout(resolve13, Math.min(POOL_LOCK_DEFAULTS.retryMs, remaining)));
24515
24777
  }
24516
24778
  }
24517
24779
  async function run(job, request) {
@@ -25906,7 +26168,7 @@ function createPublisher() {
25906
26168
  const batch = { patch, result: Promise.resolve(false) };
25907
26169
  pending.set(key, batch);
25908
26170
  batch.result = run(async () => {
25909
- await new Promise((resolve12) => setTimeout(resolve12, 0));
26171
+ await new Promise((resolve13) => setTimeout(resolve13, 0));
25910
26172
  pending.delete(key);
25911
26173
  return write(batch.patch);
25912
26174
  });
@@ -26028,7 +26290,7 @@ function createNativeUsagePublisher(channel) {
26028
26290
  return {
26029
26291
  observe: observe2,
26030
26292
  publish(write) {
26031
- observe2(new Promise((resolve12) => setTimeout(resolve12, 0)).then(write));
26293
+ observe2(new Promise((resolve13) => setTimeout(resolve13, 0)).then(write));
26032
26294
  },
26033
26295
  async drain() {
26034
26296
  while (pending.size)
@@ -26115,7 +26377,7 @@ class NativeQuotaObservations extends Map {
26115
26377
  }
26116
26378
 
26117
26379
  // src/native-runtime-registry.ts
26118
- import { resolve as resolve12 } from "node:path";
26380
+ import { resolve as resolve13 } from "node:path";
26119
26381
  var identities = new WeakMap;
26120
26382
  var nextIdentity = 0;
26121
26383
  function nativeRuntimeOptionIdentity(value) {
@@ -26144,14 +26406,14 @@ function nativeRuntimeStorageKey(options) {
26144
26406
  "runtime",
26145
26407
  "journal",
26146
26408
  "roster"
26147
- ].map((field) => resolve12(paths[field])),
26148
- resolve12(enrollment.tokenPath),
26149
- resolve12(enrollment.statePath),
26150
- resolve12(connection)
26409
+ ].map((field) => resolve13(paths[field])),
26410
+ resolve13(enrollment.tokenPath),
26411
+ resolve13(enrollment.statePath),
26412
+ resolve13(connection)
26151
26413
  ]);
26152
26414
  }
26153
26415
  function nativeRuntimeConnectionFile(host, env) {
26154
- return resolve12(resolveClaustrumConnectionPath(host === "pi" ? env.PI_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE : undefined, host === "pi" ? {
26416
+ return resolve13(resolveClaustrumConnectionPath(host === "pi" ? env.PI_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE : undefined, host === "pi" ? {
26155
26417
  ...env,
26156
26418
  OPENCODE_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE: undefined
26157
26419
  } : env));
@@ -26623,105 +26885,11 @@ function validateNativeVaultRosterSeed(existing, input) {
26623
26885
  }
26624
26886
  });
26625
26887
  }
26626
-
26627
- // src/quota-headers.ts
26628
- var PREFIX = "anthropic-ratelimit-unified-";
26629
- var WINDOW_KEYS = {
26630
- "5h": "five_hour",
26631
- "7d": "seven_day"
26632
- };
26633
- function isQuotaBearingHeaderFrame(headers) {
26634
- return Object.keys(WINDOW_KEYS).some((suffix) => Number.isFinite(finiteHeaderNumber(headers, `${PREFIX}${suffix}-utilization`)));
26635
- }
26636
- function finiteHeaderNumber(headers, name) {
26637
- const value = headers.get(name);
26638
- if (value == null || value.trim() === "")
26639
- return;
26640
- const parsed = Number(value);
26641
- return Number.isFinite(parsed) ? parsed : undefined;
26642
- }
26643
- function normalizeWindow(headers, suffix, checkedAt) {
26644
- const utilization = finiteHeaderNumber(headers, `${PREFIX}${suffix}-utilization`);
26645
- if (utilization == null)
26646
- return;
26647
- const usedPercent = Math.min(100, Math.max(0, Math.round(utilization * 100)));
26648
- const resetSeconds = finiteHeaderNumber(headers, `${PREFIX}${suffix}-reset`);
26649
- const resetDate = resetSeconds == null ? undefined : new Date(resetSeconds * 1000);
26650
- const resetsAt = resetDate && Number.isFinite(resetDate.getTime()) ? resetDate.toISOString() : undefined;
26651
- return {
26652
- usedPercent,
26653
- remainingPercent: 100 - usedPercent,
26654
- ...resetsAt && { resetsAt },
26655
- checkedAt
26656
- };
26657
- }
26658
- function normalizeQuotaHeaders(headers, now = Date.now()) {
26659
- const fieldSources2 = {};
26660
- const fallbackHeader = headers.get(`${PREFIX}fallback`);
26661
- const snapshot = {
26662
- fallbackAdvised: fallbackHeader === "available",
26663
- source: "headers",
26664
- checkedAt: now
26665
- };
26666
- for (const [suffix, key] of Object.entries(WINDOW_KEYS)) {
26667
- const window2 = normalizeWindow(headers, suffix, now);
26668
- if (window2) {
26669
- snapshot[key] = window2;
26670
- fieldSources2[key] = "headers";
26671
- }
26672
- }
26673
- const representativeClaim = headers.get(`${PREFIX}representative-claim`);
26674
- if (representativeClaim) {
26675
- snapshot.bindingWindow = representativeClaim;
26676
- snapshot.bindingWindowSource = "headers";
26677
- fieldSources2.bindingWindow = "headers";
26678
- }
26679
- if (fallbackHeader !== null)
26680
- fieldSources2.fallbackAdvised = "headers";
26681
- return {
26682
- ...snapshot,
26683
- fieldSources: fieldSources2
26684
- };
26685
- }
26686
- function mergeHeaderQuotaSnapshot(existing, incoming) {
26687
- const {
26688
- scoped: existingScoped,
26689
- extraUsage: existingExtraUsage,
26690
- fieldSources: existingFieldSources,
26691
- ...existingWithoutPollOwnedFields
26692
- } = existing ?? {};
26693
- const {
26694
- scoped: _incomingScoped,
26695
- extraUsage: _incomingExtraUsage,
26696
- fieldSources: incomingFieldSources,
26697
- ...incomingWithoutPollOwnedFields
26698
- } = incoming;
26699
- const fieldSources2 = {};
26700
- for (const field of QUOTA_FIELD_NAMES) {
26701
- const value = field === "scoped" ? existingScoped : field === "extraUsage" ? existingExtraUsage : field === "fallbackAdvised" ? incomingFieldSources?.fallbackAdvised !== undefined ? incoming.fallbackAdvised : existing?.fallbackAdvised ?? incoming.fallbackAdvised : field === "bindingWindow" ? existing?.bindingWindowSource === "poll" ? existing?.bindingWindow : incoming.bindingWindow ?? existing?.bindingWindow : incoming[field] ?? existing?.[field];
26702
- if (value === undefined)
26703
- continue;
26704
- const source2 = field === "fallbackAdvised" ? incomingFieldSources?.fallbackAdvised ?? (existing?.fallbackAdvised !== undefined ? existingFieldSources ? existingFieldSources.fallbackAdvised : quotaFieldSource(existing, field) : undefined) : (field === "scoped" || field === "extraUsage") && value !== undefined ? "poll" : field === "bindingWindow" && existing?.bindingWindowSource === "poll" && existing.bindingWindow !== undefined ? "poll" : incoming[field] !== undefined ? incomingFieldSources?.[field] ?? quotaFieldSource(incoming, field) : existingFieldSources?.[field] ?? quotaFieldSource(existing, field);
26705
- if (source2)
26706
- fieldSources2[field] = source2;
26707
- }
26708
- return {
26709
- ...existingWithoutPollOwnedFields,
26710
- ...incomingWithoutPollOwnedFields,
26711
- ...Array.isArray(existingScoped) && { scoped: existingScoped },
26712
- ...existingExtraUsage != null && { extraUsage: existingExtraUsage },
26713
- fallbackAdvised: incomingFieldSources?.fallbackAdvised !== undefined ? incoming.fallbackAdvised : existing?.fallbackAdvised ?? incoming.fallbackAdvised,
26714
- bindingWindow: existing?.bindingWindowSource === "poll" ? existing.bindingWindow : incoming.bindingWindow ?? existing?.bindingWindow,
26715
- bindingWindowSource: existing?.bindingWindowSource === "poll" ? "poll" : incoming.bindingWindowSource ?? existing?.bindingWindowSource,
26716
- ...Object.keys(fieldSources2).length > 0 && { fieldSources: fieldSources2 },
26717
- source: "headers"
26718
- };
26719
- }
26720
26888
  // src/quota-manager.ts
26721
26889
  var nativeSetTimeout = globalThis.setTimeout;
26722
26890
  function pauseOnRealTimer(ms) {
26723
- return new Promise((resolve13) => {
26724
- const id2 = nativeSetTimeout(resolve13, ms);
26891
+ return new Promise((resolve14) => {
26892
+ const id2 = nativeSetTimeout(resolve14, ms);
26725
26893
  if (typeof id2 === "object" && "unref" in id2)
26726
26894
  id2.unref();
26727
26895
  });
@@ -28323,7 +28491,7 @@ function recencyOnly(patch) {
28323
28491
  return keys4.length === 1 && keys4[0] === "lastUsed" && typeof patch.lastUsed === "number";
28324
28492
  }
28325
28493
  function waitFor(pending, signal) {
28326
- return new Promise((resolve13, reject) => {
28494
+ return new Promise((resolve14, reject) => {
28327
28495
  const abort = () => {
28328
28496
  cleanup2();
28329
28497
  reject(signal.reason);
@@ -28332,7 +28500,7 @@ function waitFor(pending, signal) {
28332
28500
  signal.addEventListener("abort", abort, { once: true });
28333
28501
  pending.then((value) => {
28334
28502
  cleanup2();
28335
- signal.aborted ? reject(signal.reason) : resolve13(value);
28503
+ signal.aborted ? reject(signal.reason) : resolve14(value);
28336
28504
  }, (error) => {
28337
28505
  cleanup2();
28338
28506
  reject(error);
@@ -28943,7 +29111,7 @@ class PersistentRelaySession {
28943
29111
  return;
28944
29112
  if (this.connecting)
28945
29113
  return await this.connecting;
28946
- this.connecting = new Promise((resolve13, reject) => {
29114
+ this.connecting = new Promise((resolve14, reject) => {
28947
29115
  const url = toWebSocketUrl(this.config.url, this.config.token);
28948
29116
  const parsed = new URL(url);
28949
29117
  parsed.searchParams.set("affinity", this.affinity);
@@ -28965,7 +29133,7 @@ class PersistentRelaySession {
28965
29133
  if (message.type === "ready") {
28966
29134
  this.timers.clearTimeout(timeout);
28967
29135
  this.serverState = message.state;
28968
- resolve13();
29136
+ resolve14();
28969
29137
  return;
28970
29138
  }
28971
29139
  this.handleMessage(message);
@@ -29017,15 +29185,15 @@ class PersistentRelaySession {
29017
29185
  const sentAt = perfNowMs();
29018
29186
  let resolveDone;
29019
29187
  let rejectDone;
29020
- const done = new Promise((resolve13, reject) => {
29021
- resolveDone = resolve13;
29188
+ const done = new Promise((resolve14, reject) => {
29189
+ resolveDone = resolve14;
29022
29190
  rejectDone = reject;
29023
29191
  });
29024
- const response = new Promise((resolve13, reject) => {
29192
+ const response = new Promise((resolve14, reject) => {
29025
29193
  const pending = {
29026
29194
  payload,
29027
29195
  bodyText,
29028
- resolve: resolve13,
29196
+ resolve: resolve14,
29029
29197
  reject,
29030
29198
  streamDone: false,
29031
29199
  accepted: false,
@@ -30431,7 +30599,7 @@ function createNativeAccountRuntime(options) {
30431
30599
  const { mode, paths: paths2, store: store2 } = await selectedStorage();
30432
30600
  for (let attempt = 0;attempt < 4; attempt++) {
30433
30601
  if (attempt)
30434
- await new Promise((resolve13) => setTimeout(resolve13, 10));
30602
+ await new Promise((resolve14) => setTimeout(resolve14, 10));
30435
30603
  const settings = await store2.readSettings();
30436
30604
  if (settings.status !== "ready")
30437
30605
  throw new NativeRuntimeError("publication-refused");
@@ -32764,8 +32932,8 @@ async function runNativeCustodyActivation(options, hooks = {}) {
32764
32932
  }
32765
32933
  async function commitMode(paths, routingPath, plan, context) {
32766
32934
  let openGate = () => {};
32767
- const gate = new Promise((resolve13) => {
32768
- openGate = resolve13;
32935
+ const gate = new Promise((resolve14) => {
32936
+ openGate = resolve14;
32769
32937
  });
32770
32938
  let leases;
32771
32939
  let scope;
@@ -34467,11 +34635,11 @@ async function refreshForRequest(runtime2, ready) {
34467
34635
  break;
34468
34636
  if (combined.aborted)
34469
34637
  break;
34470
- await new Promise((resolve13) => {
34638
+ await new Promise((resolve14) => {
34471
34639
  const finish = () => {
34472
34640
  clearTimeout(pause2);
34473
34641
  combined.removeEventListener("abort", finish);
34474
- resolve13();
34642
+ resolve14();
34475
34643
  };
34476
34644
  const pause2 = setTimeout(finish, 25);
34477
34645
  combined.addEventListener("abort", finish, { once: true });
@@ -34487,16 +34655,16 @@ async function refreshForRequest(runtime2, ready) {
34487
34655
  function waitForRequest(pending, signal) {
34488
34656
  if (!signal)
34489
34657
  return pending;
34490
- return new Promise((resolve13, reject) => {
34658
+ return new Promise((resolve14, reject) => {
34491
34659
  const abort = () => reject(signal.reason);
34492
34660
  signal.addEventListener("abort", abort, { once: true });
34493
- pending.then(resolve13, reject).finally(() => signal.removeEventListener("abort", abort));
34661
+ pending.then(resolve14, reject).finally(() => signal.removeEventListener("abort", abort));
34494
34662
  if (signal.aborted)
34495
34663
  abort();
34496
34664
  });
34497
34665
  }
34498
34666
  // src/native-vault-connection.ts
34499
- import { dirname as dirname21, join as join22, resolve as resolve14 } from "node:path";
34667
+ import { dirname as dirname21, join as join22, resolve as resolve15 } from "node:path";
34500
34668
  // ../../node_modules/@cortexkit/common-auth/dist/opencode2/integration.js
34501
34669
  var PLACEHOLDER_PREFIX = "common-auth-placeholder";
34502
34670
  var PLACEHOLDER_LIFETIME_MS = 365 * 24 * 60 * 60 * 1000;
@@ -34510,7 +34678,7 @@ var MAX_RETRY_HOLD_MS = 20 * 60 * 1000;
34510
34678
  import { createHash as createHash24 } from "node:crypto";
34511
34679
  import { constants as constants4 } from "node:fs";
34512
34680
  import { lstat as lstat16, mkdir as mkdir14, open as open12, unlink as unlink9 } from "node:fs/promises";
34513
- import { dirname as dirname20, resolve as resolve13 } from "node:path";
34681
+ import { dirname as dirname20, resolve as resolve14 } from "node:path";
34514
34682
  import { setTimeout as sleep } from "node:timers/promises";
34515
34683
 
34516
34684
  // ../../node_modules/@cortexkit/common-auth/dist/pi-slot/lock.js
@@ -34913,8 +35081,8 @@ function createPiSlot(options, onStep, hooks) {
34913
35081
  if (!provider || !options.authPath || !options.stashPath || !placeholderKey || placeholderKey.startsWith("!") || placeholderKey.includes("$") || !Number.isFinite(timeout) || timeout < 0) {
34914
35082
  throw new PiSlotError("invalid-options");
34915
35083
  }
34916
- const authPath = resolve13(options.authPath);
34917
- const stashPath = resolve13(options.stashPath);
35084
+ const authPath = resolve14(options.authPath);
35085
+ const stashPath = resolve14(options.stashPath);
34918
35086
  if (authPath === stashPath || stashPath === `${authPath}.lock`) {
34919
35087
  throw new PiSlotError("invalid-options");
34920
35088
  }
@@ -35052,7 +35220,7 @@ function createPiSlot(options, onStep, hooks) {
35052
35220
  // src/native-vault-connection.ts
35053
35221
  function nativePiAuthSlotPaths(options) {
35054
35222
  return {
35055
- authPath: resolve14(options.authPath),
35223
+ authPath: resolve15(options.authPath),
35056
35224
  stashPath: join22(dirname21(getHostClaustrumEnrollmentPaths("pi", options.env).tokenPath), "pi-auth-slot", "anthropic.json"),
35057
35225
  provider: "anthropic",
35058
35226
  placeholderKey: placeholderSecret("anthropic")
@@ -35126,7 +35294,7 @@ import { createHash as createHash25 } from "node:crypto";
35126
35294
  import { realpathSync as realpathSync3 } from "node:fs";
35127
35295
  import { mkdir as mkdir15, readdir as readdir4, rm as rm8, stat as stat11, writeFile as writeFile5 } from "node:fs/promises";
35128
35296
  import { tmpdir as tmpdir7 } from "node:os";
35129
- import { join as join23, resolve as resolve15 } from "node:path";
35297
+ import { join as join23, resolve as resolve16 } from "node:path";
35130
35298
  var CLAUDE_PRIME_COMMAND_NAME = "claude-prime";
35131
35299
  var PRIME_TICK_MS = 60000;
35132
35300
  var PRIME_DUE_OFFSET_MS = 60000;
@@ -35317,7 +35485,7 @@ function defaultMarkerDir() {
35317
35485
  return join23(tmpdir7(), "opencode-anthropic-auth", "prime");
35318
35486
  }
35319
35487
  function primeStorageFingerprint(storagePath) {
35320
- const absolutePath = resolve15(storagePath);
35488
+ const absolutePath = resolve16(storagePath);
35321
35489
  let canonicalPath2;
35322
35490
  try {
35323
35491
  canonicalPath2 = realpathSync3(absolutePath);
@@ -36708,7 +36876,7 @@ import { basename as basename13, dirname as dirname24, join as join29 } from "no
36708
36876
  // src/layout-fence.ts
36709
36877
  import { readdir as readdir6, readFile as readFile15 } from "node:fs/promises";
36710
36878
  import { homedir as homedir5 } from "node:os";
36711
- import { basename as basename11, join as join26, resolve as resolve16 } from "node:path";
36879
+ import { basename as basename11, join as join26, resolve as resolve17 } from "node:path";
36712
36880
  var STATE_PATH_OVERRIDE_VARIABLES = [
36713
36881
  "OPENCODE_ANTHROPIC_AUTH_FILE",
36714
36882
  "OPENCODE_ANTHROPIC_AUTH_STATE_FILE",
@@ -36795,7 +36963,7 @@ function defaultPreNArtifactLocations(env = process.env, home = homedir5()) {
36795
36963
  const rpcOverride = env.OPENCODE_ANTHROPIC_AUTH_RPC_DIR?.trim();
36796
36964
  return {
36797
36965
  rpcRoots: [join26(root, "rpc")],
36798
- rpcDirs: rpcOverride ? [resolve16(rpcOverride)] : [],
36966
+ rpcDirs: rpcOverride ? [resolve17(rpcOverride)] : [],
36799
36967
  cacheKeepDirs: [
36800
36968
  ...new Set([
36801
36969
  getDefaultCacheKeepRegistryDirectory("opencode"),
@@ -38904,6 +39072,7 @@ export {
38904
39072
  stickyRouteFamilyForModel,
38905
39073
  stickyRouteCandidateWeight,
38906
39074
  stickyRetryAfterWithJitter,
39075
+ stickyRetainRetryHeaders,
38907
39076
  stickyQuotaSnapshotIsFresh,
38908
39077
  stateSubfolderName,
38909
39078
  stateRefusalPublicMessage,
@@ -38958,6 +39127,7 @@ export {
38958
39127
  requireNoSupervisedAuthContentSnapshot,
38959
39128
  requireAnthropicOAuthOrigin,
38960
39129
  requestGzipPadding,
39130
+ renameWithWindowsSharingRetry,
38961
39131
  remapRequestBodyModel,
38962
39132
  remapModelId,
38963
39133
  releaseNativeMigrationPreflight,
@@ -38968,6 +39138,7 @@ export {
38968
39138
  refreshBackoffActive,
38969
39139
  redactPayload,
38970
39140
  recordProcessStateLayout,
39141
+ readWindowsOwnerOnly,
38971
39142
  readStateLayoutMarker,
38972
39143
  readRelocationPointer,
38973
39144
  readNativeMigrationJournal,
@@ -38986,6 +39157,7 @@ export {
38986
39157
  quotaSnapshotHasStandardWindows,
38987
39158
  quotaSnapshotCheckedAt,
38988
39159
  quotaFieldSource,
39160
+ quotaExhaustionFromHeaders,
38989
39161
  quotaDisplayProjection,
38990
39162
  quotaBackoffActive,
38991
39163
  publishNativeVaultRuntimeSeed,
@@ -50,6 +50,12 @@ export interface ClaustrumConsumerOptions<Q = QuotaObservation> {
50
50
  * routine token refresh, which never moves the view.
51
51
  */
52
52
  onRoster?: (roster: VaultRosterFile) => void;
53
+ /**
54
+ * Called once when a coalesced discovery starts, with its completion promise.
55
+ * Covers explicit refresh, poll, watch and send-triggered discovery, even when
56
+ * the view does not change. Observe rejection when attaching async work.
57
+ */
58
+ onDiscovery?: (promise: Promise<VaultRosterFile | undefined>) => void;
53
59
  onError?: (error: unknown) => void;
54
60
  /**
55
61
  * How often `start()` ticks. A tick lists the vault when no vault watch is
@@ -104,6 +110,8 @@ export interface HubMemberPort {
104
110
  keptReceiptState(): KeptReceiptState;
105
111
  /** Apply the union of every member's roster in memory to kept receipts. */
106
112
  retainUnion(): void;
113
+ /** Share a committed preference change with live members using this roster file. */
114
+ publishRoster(roster: VaultRosterFile): void;
107
115
  forget(credentialId: string): void;
108
116
  /** The member closed: the hub stops committing and pushing to it. */
109
117
  detach(): void;
@@ -113,6 +121,8 @@ export interface HubRefreshOutcome {
113
121
  }
114
122
  /** What the hub may do to one member's memory. Package-internal. */
115
123
  export interface HubMemberHooks {
124
+ /** Observe a hub discovery without relying on member refresh overrides. */
125
+ discovery(promise: Promise<HubRefreshOutcome | undefined>): void;
116
126
  /**
117
127
  * Set the roster in memory as a consumer's own refresh does (pending quota
118
128
  * readings included) and notify `onRoster` when its view changed.
@@ -25,7 +25,8 @@ export interface ClaustrumFamily {
25
25
  /** Non-secret classification facts, including rows decoded by older clients. */
26
26
  export interface VaultRowFacts {
27
27
  readonly credentialId: string;
28
- readonly credentialType: string;
28
+ /** Internal serving type derived from `authMethod`, not the id's type hint. */
29
+ readonly credentialType: VaultCredentialType;
29
30
  readonly authMethod?: string;
30
31
  readonly refreshAdapter?: string;
31
32
  readonly providerIds?: readonly string[];
@@ -108,7 +109,10 @@ export interface VaultInventory {
108
109
  readonly view: string;
109
110
  readonly credentials: readonly VaultCredential[];
110
111
  readonly skipped: readonly SkippedVaultRecord[];
111
- /** Classifying families only: fixed refusal codes, never classifier errors. */
112
+ /**
113
+ * Classifying families only: declared policy codes or the fixed library codes
114
+ * `unknown-auth-method`, `unmapped` and `unclassified`, never classifier errors.
115
+ */
112
116
  readonly refused?: readonly {
113
117
  credentialId?: string;
114
118
  code: string;
@@ -1,5 +1,5 @@
1
1
  import type { QuotaObservation } from '../quota/index.js';
2
- import { type ClaustrumConsumer } from './consumer.js';
2
+ import { type ClaustrumConsumer, type ClaustrumConsumerOptions } from './consumer.js';
3
3
  import { type ClassifyingClaustrumFamily, type ClaustrumScopedClient, ClaustrumScopedCustody, type IdentityParser, type KeptReceiptState, type VaultGrantReach } from './custody.js';
4
4
  import { type ClaustrumLogger } from './errors.js';
5
5
  import { type AccountMapper, type VaultRosterFile } from './roster.js';
@@ -100,6 +100,8 @@ export interface ClaustrumHubMemberOptions<_Q = QuotaObservation> {
100
100
  reservedRouteIds?: () => Iterable<string>;
101
101
  /** As `ClaustrumConsumerOptions.onRoster`: once per change of view. */
102
102
  onRoster?: (roster: VaultRosterFile) => void;
103
+ /** Observe every hub discovery's member result, including background work. */
104
+ onDiscovery?: ClaustrumConsumerOptions<_Q>['onDiscovery'];
103
105
  /** This member's own failures, such as a roster commit that failed. */
104
106
  onError?: (error: unknown) => void;
105
107
  logger?: ClaustrumLogger;
@@ -21,7 +21,8 @@ export interface LockLoss {
21
21
  export interface RefreshFileLock {
22
22
  readonly ownerId: string;
23
23
  assertOwned(): Promise<void>;
24
- release(): Promise<void>;
24
+ /** Abort stops release retries; the unrenewed lease is left to expire by TTL. */
25
+ release(signal?: AbortSignal): Promise<void>;
25
26
  /** Resolves once on detected loss; remains pending after an owner's release. */
26
27
  whenLost(): Promise<LockLoss>;
27
28
  hasLost(): boolean;
@@ -1,4 +1,6 @@
1
1
  import type { OAuthQuotaSnapshot } from './accounts.ts';
2
2
  export declare function isQuotaBearingHeaderFrame(headers: Headers): boolean;
3
+ /** Only a rejected status or raw utilization at least one confirms a 429's limit. */
4
+ export declare function quotaExhaustionFromHeaders(headers: Headers, modelId?: string, now?: number): OAuthQuotaSnapshot;
3
5
  export declare function normalizeQuotaHeaders(headers: Headers, now?: number): OAuthQuotaSnapshot;
4
6
  export declare function mergeHeaderQuotaSnapshot(existing: OAuthQuotaSnapshot | undefined, incoming: OAuthQuotaSnapshot): OAuthQuotaSnapshot;
@@ -90,9 +90,12 @@ export declare function stickyRouteCandidateWeight(input: {
90
90
  export declare function stickyRouteKnownFableExhausted(candidate: StickyRouteCandidate, storage: AccountStorage | null, now?: number): boolean;
91
91
  export declare function decideStickyQuotaFailure(input: {
92
92
  quota: OAuthQuotaSnapshot | QuotaState | undefined;
93
+ responseHeaders?: Headers;
93
94
  modelId?: string;
94
95
  now?: number;
95
96
  }): StickyQuotaFailureDecision;
97
+ /** Bound an unconfirmed 429's host retry while preserving any shorter upstream wait. */
98
+ export declare function stickyRetainRetryHeaders(upstream: Headers, now?: number): Headers;
96
99
  export declare function getStickyRoutingStatePath(accountStoragePath: string): string;
97
100
  export declare function stickyRetryAfterWithJitter(sessionId: string, retryAfterSeconds: number): number;
98
101
  export declare class StickySessionRouter {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,7 +30,7 @@
30
30
  "prepublishOnly": "bun run build"
31
31
  },
32
32
  "devDependencies": {
33
- "@cortexkit/common-auth": "0.29.0"
33
+ "@cortexkit/common-auth": "0.31.0"
34
34
  },
35
35
  "dependencies": {
36
36
  "@cortexkit/claustrum-client": "0.13.0",