@cortexkit/anthropic-auth-core 2.0.1 → 2.0.2

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/dist/index.js CHANGED
@@ -8042,7 +8042,8 @@ function parseEntry(raw, codec) {
8042
8042
  return;
8043
8043
  if ("disabledReason" in raw && typeof raw.disabledReason !== "string")
8044
8044
  return;
8045
- if ("quota" in raw && !codec.validate(raw.quota))
8045
+ const quota = "quota" in raw && codec.decode ? codec.decode(raw.quota) : raw.quota;
8046
+ if ("quota" in raw && !codec.validate(quota))
8046
8047
  return;
8047
8048
  if ("staged" in raw && !parseReservation(raw.staged))
8048
8049
  return;
@@ -8050,7 +8051,7 @@ function parseEntry(raw, codec) {
8050
8051
  credentialEpoch: epoch,
8051
8052
  needsFirstReading: raw.needsFirstReading === true,
8052
8053
  ...typeof raw.disabledReason === "string" ? { disabledReason: raw.disabledReason } : {},
8053
- ..."quota" in raw ? { quota: raw.quota } : {}
8054
+ ..."quota" in raw ? { quota } : {}
8054
8055
  };
8055
8056
  }
8056
8057
  function credentialFor(raw, stateEntry) {
@@ -10701,6 +10702,29 @@ function isCreditBudgetEntry(value) {
10701
10702
  return true;
10702
10703
  return value.kind === "reading" && typeof value.reached === "boolean" && optional(value, "remainingPercent", isFiniteNumber) && optional(value, "usedPercent", isFiniteNumber) && optional(value, "resetsAt", isString) && optional(value, "limit", isFiniteNumber) && optional(value, "used", isFiniteNumber) && optional(value, "remaining", isFiniteNumber) && optional(value, "unit", isString);
10703
10704
  }
10705
+ function isQuotaCounter(value) {
10706
+ return isRecord6(value) && isFiniteNumber(value.value) && Number.isInteger(value.value) && value.value >= 0 && isFiniteNumber(value.checkedAt);
10707
+ }
10708
+ function isQuotaCounters(value) {
10709
+ return isRecord6(value) && Object.values(value).every(isQuotaCounter);
10710
+ }
10711
+ function readQuotaCounters(value) {
10712
+ if (!isRecord6(value))
10713
+ return;
10714
+ const entries = Object.entries(value).filter(([, entry]) => isQuotaCounter(entry));
10715
+ return entries.length === 0 ? undefined : Object.fromEntries(entries);
10716
+ }
10717
+ function decodeQuotaMap(value) {
10718
+ if (isQuotaMap(value))
10719
+ return value;
10720
+ if (!isRecord6(value))
10721
+ return;
10722
+ const { counters: rawCounters, ...rest } = value;
10723
+ if (!isQuotaMap(rest))
10724
+ return;
10725
+ const counters = readQuotaCounters(rawCounters);
10726
+ return { ...rest, ...counters === undefined ? {} : { counters } };
10727
+ }
10704
10728
  function isQuotaMap(value) {
10705
10729
  if (!isRecord6(value) || !Array.isArray(value.limits))
10706
10730
  return false;
@@ -10713,7 +10737,7 @@ function isQuotaMap(value) {
10713
10737
  return false;
10714
10738
  seen.add(key);
10715
10739
  }
10716
- return optional(value, "budget", isCreditBudgetEntry);
10740
+ return optional(value, "budget", isCreditBudgetEntry) && optional(value, "counters", isQuotaCounters);
10717
10741
  }
10718
10742
 
10719
10743
  // ../../node_modules/@cortexkit/common-auth/dist/quota/merge.js
@@ -10772,7 +10796,7 @@ function isQuotaObservation(value) {
10772
10796
  }
10773
10797
  if (!coverage.every(isObservedPair))
10774
10798
  return false;
10775
- return value.budget === undefined || isObservedBudget(value.budget);
10799
+ return (value.budget === undefined || isObservedBudget(value.budget)) && (value.counters === undefined || isQuotaCounters(value.counters));
10776
10800
  }
10777
10801
  function compareEntries(left, right) {
10778
10802
  if (left.scope !== right.scope)
@@ -10788,14 +10812,25 @@ function mergeBudget(stored, observed, checkedAt) {
10788
10812
  return stored;
10789
10813
  return { ...observed, checkedAt };
10790
10814
  }
10815
+ function mergeCounters(stored, observed) {
10816
+ const counters = new Map(Object.entries(stored ?? {}));
10817
+ for (const [name, counter] of Object.entries(observed ?? {})) {
10818
+ const existing = counters.get(name);
10819
+ if (existing === undefined || counter.checkedAt >= existing.checkedAt) {
10820
+ counters.set(name, counter);
10821
+ }
10822
+ }
10823
+ return counters.size === 0 ? undefined : Object.fromEntries(counters);
10824
+ }
10791
10825
  function mergeQuotaObservation(stored, observation) {
10792
- if (stored !== undefined && !isQuotaMap(stored)) {
10826
+ const decoded = stored === undefined ? undefined : decodeQuotaMap(stored);
10827
+ if (stored !== undefined && decoded === undefined) {
10793
10828
  throw new QuotaCodecError("stored quota map is malformed");
10794
10829
  }
10795
10830
  if (!isQuotaObservation(observation)) {
10796
10831
  throw new QuotaCodecError("quota observation is malformed");
10797
10832
  }
10798
- const base = stored ?? { limits: [] };
10833
+ const base = decoded ?? { limits: [] };
10799
10834
  const at = observation.checkedAt;
10800
10835
  const entries = new Map;
10801
10836
  for (const entry of base.limits) {
@@ -10830,18 +10865,21 @@ function mergeQuotaObservation(stored, observation) {
10830
10865
  const existing = entries.get(key);
10831
10866
  entries.set(key, existing === undefined || existing.kind === "absent" ? { scope, label: pair.label, kind: "absent", checkedAt: at } : { scope, label: pair.label, kind: "retired", retiredAt: at });
10832
10867
  }
10833
- const { limits: _limits, budget: _budget, ...unknownKeys } = base;
10868
+ const { limits: _limits, budget: _budget, counters: _counters, ...unknownKeys } = base;
10834
10869
  const budget = mergeBudget(base.budget, observation.budget, at);
10870
+ const counters = mergeCounters(base.counters, observation.counters);
10835
10871
  return {
10836
10872
  ...unknownKeys,
10837
10873
  limits: [...entries.values()].sort(compareEntries),
10838
- ...budget === undefined ? {} : { budget }
10874
+ ...budget === undefined ? {} : { budget },
10875
+ ...counters === undefined ? {} : { counters }
10839
10876
  };
10840
10877
  }
10841
10878
 
10842
10879
  // ../../node_modules/@cortexkit/common-auth/dist/quota/codec.js
10843
10880
  var quotaCodec = Object.freeze({
10844
10881
  validate: isQuotaMap,
10882
+ decode: decodeQuotaMap,
10845
10883
  merge: mergeQuotaObservation
10846
10884
  });
10847
10885
  // ../../node_modules/@cortexkit/common-auth/dist/quota/format.js
@@ -13305,11 +13343,103 @@ function logCommittedNativeAccountAdded(account) {
13305
13343
 
13306
13344
  // ../../node_modules/@cortexkit/common-auth/dist/claustrum/index.js
13307
13345
  import { ClaustrumClient } from "@cortexkit/claustrum-client";
13346
+
13347
+ // ../../node_modules/@cortexkit/common-auth/dist/claustrum/insula.js
13348
+ import { resolve as resolve4 } from "node:path";
13349
+ import { storeIdentity } from "@cortexkit/claustrum-client";
13350
+ import { SubcClient } from "@cortexkit/subc-client";
13351
+ var MINUTE = 60000;
13352
+ var MAX_AGE = 4 * MINUTE;
13353
+ var sources = new Map;
13354
+ var attempts = new Map;
13355
+ function record7(value) {
13356
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13357
+ }
13358
+
13359
+ class InsulaQuotaSource {
13360
+ transport;
13361
+ key;
13362
+ #entries = [];
13363
+ #pending;
13364
+ constructor(transport, key) {
13365
+ this.transport = transport;
13366
+ this.key = key;
13367
+ }
13368
+ refresh(now) {
13369
+ if (this.#pending)
13370
+ return this.#pending;
13371
+ for (const [key, time4] of attempts) {
13372
+ if (now - time4 >= MINUTE)
13373
+ attempts.delete(key);
13374
+ }
13375
+ const lastAttempt = attempts.get(this.key);
13376
+ if (lastAttempt !== undefined && now - lastAttempt < MINUTE)
13377
+ return Promise.resolve();
13378
+ attempts.set(this.key, now);
13379
+ this.#pending = (async () => {
13380
+ try {
13381
+ const reply = await this.transport.getUsage();
13382
+ if (!record7(reply) || !Array.isArray(reply.result) || reply.error !== undefined)
13383
+ throw new Error("unavailable insula usage");
13384
+ this.#entries = reply.result.filter((entry2) => record7(entry2) && typeof entry2.provider === "string");
13385
+ } catch {
13386
+ this.#entries = [];
13387
+ }
13388
+ })().finally(() => {
13389
+ this.#pending = undefined;
13390
+ });
13391
+ return this.#pending;
13392
+ }
13393
+ current(provider, accountId, now) {
13394
+ for (const entry2 of this.#entries) {
13395
+ if (entry2.provider !== provider || typeof entry2.account !== "string" || entry2.account.trim() !== accountId)
13396
+ continue;
13397
+ if (entry2.stale !== undefined || entry2.errorClass !== undefined || !record7(entry2.usage))
13398
+ continue;
13399
+ const fetchedAt = typeof entry2.fetchedAt === "string" ? Date.parse(entry2.fetchedAt) : Number.NaN;
13400
+ if (!Number.isFinite(fetchedAt) || now - fetchedAt >= MAX_AGE)
13401
+ continue;
13402
+ return {
13403
+ entry: entry2,
13404
+ context: {
13405
+ fetchedAt,
13406
+ ...typeof entry2.source === "string" && { source: entry2.source }
13407
+ }
13408
+ };
13409
+ }
13410
+ return;
13411
+ }
13412
+ }
13413
+ function acquireInsulaQuotaSource(transport, tokenPath) {
13414
+ const key = JSON.stringify([
13415
+ resolve4(transport.connectionFile),
13416
+ resolve4(tokenPath)
13417
+ ]);
13418
+ let shared = sources.get(key);
13419
+ if (!shared) {
13420
+ shared = { source: new InsulaQuotaSource(transport, key), refs: 0 };
13421
+ sources.set(key, shared);
13422
+ }
13423
+ shared.refs++;
13424
+ let released = false;
13425
+ return {
13426
+ source: shared.source,
13427
+ release() {
13428
+ if (released)
13429
+ return;
13430
+ released = true;
13431
+ if (--shared.refs === 0)
13432
+ sources.delete(key);
13433
+ }
13434
+ };
13435
+ }
13436
+
13437
+ // ../../node_modules/@cortexkit/common-auth/dist/claustrum/index.js
13308
13438
  import { ClaustrumCredentialError as ClaustrumCredentialError4 } from "@cortexkit/claustrum-client";
13309
13439
  // ../../node_modules/@cortexkit/common-auth/dist/logger/engine.js
13310
13440
  import { appendFileSync as appendFileSync2, chmodSync, existsSync as existsSync2, renameSync as renameSync2, statSync as statSync3 } from "node:fs";
13311
13441
  import { open as open5 } from "node:fs/promises";
13312
- import { dirname as dirname7, resolve as resolve4 } from "node:path";
13442
+ import { dirname as dirname7, resolve as resolve5 } from "node:path";
13313
13443
 
13314
13444
  // ../../node_modules/@cortexkit/common-auth/dist/logger/redact.js
13315
13445
  var SECRET_KEY_EXACT2 = /^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i;
@@ -13432,7 +13562,7 @@ async function openWindowsFile(sink, file) {
13432
13562
  return sink.handle;
13433
13563
  }
13434
13564
  async function flushWindowsFile(sink, file, text4) {
13435
- file = resolve4(file);
13565
+ file = resolve5(file);
13436
13566
  try {
13437
13567
  if (sink.file !== file)
13438
13568
  await closeWindowsFile(sink);
@@ -13685,7 +13815,7 @@ import { ClaustrumCredentialError as ClaustrumCredentialError3 } from "@cortexki
13685
13815
  import { createHash as createHash11, randomBytes as randomBytes4, randomUUID as randomUUID11 } from "node:crypto";
13686
13816
  import { constants as fsConstants2 } from "node:fs";
13687
13817
  import { chmod as chmod2, mkdir as mkdir8, open as open6, readFile as readFile8, realpath as realpath2, rename as rename9, stat as stat4, unlink as unlink4 } from "node:fs/promises";
13688
- import { basename as basename3, dirname as dirname8, extname as extname2, isAbsolute as isAbsolute3, join as join9, resolve as resolve5 } from "node:path";
13818
+ import { basename as basename3, dirname as dirname8, extname as extname2, isAbsolute as isAbsolute3, join as join9, resolve as resolve6 } from "node:path";
13689
13819
  import { ClaustrumCredentialError as ClaustrumCredentialError2, ClaustrumClient as ClaustrumWireClient2, writeEnrollmentTokenFile as writeEnrollmentTokenFile2 } from "@cortexkit/claustrum-client";
13690
13820
 
13691
13821
  // ../../node_modules/@cortexkit/common-auth/dist/claustrum/errors.js
@@ -14361,8 +14491,8 @@ function accessTokenFromMaterial(material) {
14361
14491
  }
14362
14492
  if (!parsed || typeof parsed !== "object")
14363
14493
  throw invalidMaterial();
14364
- const record7 = parsed;
14365
- const value = record7.access_token ?? record7.access;
14494
+ const record8 = parsed;
14495
+ const value = record8.access_token ?? record8.access;
14366
14496
  access = typeof value === "string" ? value : "";
14367
14497
  }
14368
14498
  if (!/^[\x21-\x7e]+$/.test(access) || access.startsWith(CUSTODY_PLACEHOLDER_PREFIX)) {
@@ -14489,8 +14619,8 @@ class ClaustrumScopedCustody {
14489
14619
  ...row.orgName !== undefined && { orgName: row.orgName }
14490
14620
  }));
14491
14621
  }
14492
- for (const record7 of skipped)
14493
- this.#logger.warn("skipped malformed vault record", record7);
14622
+ for (const record8 of skipped)
14623
+ this.#logger.warn("skipped malformed vault record", record8);
14494
14624
  return {
14495
14625
  view: inventory.view,
14496
14626
  credentials: Object.freeze(credentials),
@@ -14640,8 +14770,8 @@ function projectVaultRoster(previous, inventory, options = {}) {
14640
14770
  const declined = [...previous?.declined ?? []];
14641
14771
  const complete = inventory.skipped.length === 0;
14642
14772
  const listed = new Set(inventory.credentials.map((credential2) => credential2.credentialId));
14643
- const rejectedIds = new Set(inventory.skipped.flatMap((record7) => record7.credentialId === undefined ? [] : [record7.credentialId]));
14644
- const unrecoverable = inventory.skipped.some((record7) => record7.credentialId === undefined);
14773
+ const rejectedIds = new Set(inventory.skipped.flatMap((record8) => record8.credentialId === undefined ? [] : [record8.credentialId]));
14774
+ const unrecoverable = inventory.skipped.some((record8) => record8.credentialId === undefined);
14645
14775
  const unaccounted = (id2) => !listed.has(id2) && (rejectedIds.has(id2) || unrecoverable);
14646
14776
  const lastKnown = new Map;
14647
14777
  for (const row of previousRows) {
@@ -14748,11 +14878,11 @@ function projectVaultRoster(previous, inventory, options = {}) {
14748
14878
  view: inventory.view,
14749
14879
  complete,
14750
14880
  ...!complete && {
14751
- rejected: inventory.skipped.map((record7) => ({
14752
- ...record7.credentialId !== undefined && {
14753
- credentialId: record7.credentialId
14881
+ rejected: inventory.skipped.map((record8) => ({
14882
+ ...record8.credentialId !== undefined && {
14883
+ credentialId: record8.credentialId
14754
14884
  },
14755
- reason: record7.reason
14885
+ reason: record8.reason
14756
14886
  }))
14757
14887
  },
14758
14888
  rows: projected.map((entry2) => entry2.row),
@@ -14773,9 +14903,13 @@ function optionalString2(value) {
14773
14903
  return value === undefined || typeof value === "string";
14774
14904
  }
14775
14905
  function decodeRow(value) {
14776
- if (!isRecord9(value) || typeof value.routeId !== "string" || typeof value.credentialId !== "string" || value.credentialType !== "oauth" && value.credentialType !== "api_key" || typeof value.state !== "string" || typeof value.label !== "string" || typeof value.enabled !== "boolean" || typeof value.addedAt !== "number" || !optionalString2(value.accountIdentity) || !optionalString2(value.email) || !optionalString2(value.orgName) || value.aliases !== undefined && (!Array.isArray(value.aliases) || !value.aliases.every((alias) => typeof alias === "string")) || value.quota !== undefined && !isQuotaMap(value.quota) || value.stale !== undefined && value.stale !== true || value.unclaimed !== undefined && value.unclaimed !== true)
14906
+ const quota2 = isRecord9(value) ? decodeQuotaMap(value.quota) : undefined;
14907
+ if (!isRecord9(value) || typeof value.routeId !== "string" || typeof value.credentialId !== "string" || value.credentialType !== "oauth" && value.credentialType !== "api_key" || typeof value.state !== "string" || typeof value.label !== "string" || typeof value.enabled !== "boolean" || typeof value.addedAt !== "number" || !optionalString2(value.accountIdentity) || !optionalString2(value.email) || !optionalString2(value.orgName) || value.aliases !== undefined && (!Array.isArray(value.aliases) || !value.aliases.every((alias) => typeof alias === "string")) || value.quota !== undefined && quota2 === undefined || value.stale !== undefined && value.stale !== true || value.unclaimed !== undefined && value.unclaimed !== true)
14777
14908
  return;
14778
- return value;
14909
+ return {
14910
+ ...value,
14911
+ ...quota2 === undefined ? {} : { quota: quota2 }
14912
+ };
14779
14913
  }
14780
14914
  var SKIPPED_REASONS = new Set([
14781
14915
  "empty credential id",
@@ -14920,6 +15054,21 @@ function recordVaultQuota(path2, input) {
14920
15054
  };
14921
15055
  });
14922
15056
  }
15057
+ function recordVaultAccountQuota(path2, input) {
15058
+ return mutateVaultRoster(path2, (current) => {
15059
+ const row = current?.rows.find((entry2) => entry2.routeId === input.routeId);
15060
+ if (!current || !row || row.accountIdentity === undefined || row.accountIdentity !== input.accountIdentity)
15061
+ return { result: false };
15062
+ const quota2 = mergeQuotaObservation(row.quota, input.observation);
15063
+ return {
15064
+ next: {
15065
+ ...current,
15066
+ rows: current.rows.map((entry2) => entry2 === row ? { ...entry2, quota: quota2 } : entry2)
15067
+ },
15068
+ result: true
15069
+ };
15070
+ });
15071
+ }
14923
15072
  async function refreshVaultRoster(options) {
14924
15073
  options.signal?.throwIfAborted();
14925
15074
  const active = async () => await options.isActive?.() ?? true;
@@ -14966,6 +15115,7 @@ class ClaustrumConsumer {
14966
15115
  #roster;
14967
15116
  #timer;
14968
15117
  #started = false;
15118
+ #insula;
14969
15119
  constructor(options) {
14970
15120
  this.#options = options;
14971
15121
  this.#logger = options.logger ?? createLogger("claustrum");
@@ -14982,7 +15132,7 @@ class ClaustrumConsumer {
14982
15132
  this.#shutdown.signal,
14983
15133
  ...signal ? [signal] : []
14984
15134
  ]);
14985
- return new Promise((resolve6, reject) => {
15135
+ return new Promise((resolve7, reject) => {
14986
15136
  const cleanup2 = () => {
14987
15137
  combined.signal.removeEventListener("abort", abort);
14988
15138
  combined.dispose();
@@ -14997,7 +15147,7 @@ class ClaustrumConsumer {
14997
15147
  if (combined.signal.aborted)
14998
15148
  reject(combined.signal.reason);
14999
15149
  else
15000
- resolve6(value);
15150
+ resolve7(value);
15001
15151
  }, (error) => {
15002
15152
  cleanup2();
15003
15153
  reject(error);
@@ -15025,6 +15175,9 @@ class ClaustrumConsumer {
15025
15175
  now: this.#options.now,
15026
15176
  logger: this.#logger
15027
15177
  });
15178
+ if (this.#options.insula && client.insula) {
15179
+ this.#insula = acquireInsulaQuotaSource(client.insula, this.#options.tokenPath);
15180
+ }
15028
15181
  return this.#custody;
15029
15182
  }).finally(() => {
15030
15183
  this.#connecting = undefined;
@@ -15047,6 +15200,8 @@ class ClaustrumConsumer {
15047
15200
  this.#roster = undefined;
15048
15201
  this.#custody?.close();
15049
15202
  this.#custody = undefined;
15203
+ this.#insula?.release();
15204
+ this.#insula = undefined;
15050
15205
  return;
15051
15206
  }
15052
15207
  const custody = await this.#getCustody();
@@ -15081,6 +15236,7 @@ class ClaustrumConsumer {
15081
15236
  const tick = async () => {
15082
15237
  try {
15083
15238
  await this.refresh();
15239
+ await this.refreshInsulaQuota();
15084
15240
  } catch (error) {
15085
15241
  if (!this.#shutdown.signal.aborted)
15086
15242
  this.#options.onError?.(error);
@@ -15098,6 +15254,43 @@ class ClaustrumConsumer {
15098
15254
  };
15099
15255
  tick();
15100
15256
  }
15257
+ async refreshInsulaQuota() {
15258
+ if (!this.#options.insula || this.#shutdown.signal.aborted || !await this.#active())
15259
+ return;
15260
+ try {
15261
+ await readClaustrumEnrollmentToken2(this.#options.tokenPath);
15262
+ await this.#getCustody();
15263
+ await this.#insula?.source.refresh((this.#options.now ?? Date.now)());
15264
+ } catch {}
15265
+ }
15266
+ async cachedInsulaQuota(receipt) {
15267
+ if (!this.#options.insula || this.#shutdown.signal.aborted || !await this.#active())
15268
+ return;
15269
+ try {
15270
+ await readClaustrumEnrollmentToken2(this.#options.tokenPath);
15271
+ return this.#normalizeInsulaQuota(receipt.assertedAccountIdentity);
15272
+ } catch {
15273
+ return;
15274
+ }
15275
+ }
15276
+ async cachedInsulaQuotaForRoute(routeId) {
15277
+ if (!this.#options.insula || this.#shutdown.signal.aborted || !await this.#active())
15278
+ return;
15279
+ try {
15280
+ await readClaustrumEnrollmentToken2(this.#options.tokenPath);
15281
+ const roster = await readVaultRoster(this.#options.rosterPath);
15282
+ const row = roster?.rows.find((entry2) => entry2.routeId === routeId);
15283
+ return this.#normalizeInsulaQuota(row?.accountIdentity);
15284
+ } catch {
15285
+ return;
15286
+ }
15287
+ }
15288
+ #normalizeInsulaQuota(accountIdentity) {
15289
+ if (!this.#options.insula || !accountIdentity)
15290
+ return;
15291
+ const current = this.#insula?.source.current(this.#options.insula.provider, accountIdentity, (this.#options.now ?? Date.now)());
15292
+ return current ? this.#options.insula.normalize(current.entry, current.context) : undefined;
15293
+ }
15101
15294
  async authorize(routeId, signal) {
15102
15295
  this.#assertOpen(signal);
15103
15296
  const roster = this.#roster ?? await this.#wait(this.refresh(), signal);
@@ -15186,6 +15379,17 @@ class ClaustrumConsumer {
15186
15379
  this.#roster = await readVaultRoster(this.#options.rosterPath);
15187
15380
  return kept;
15188
15381
  }
15382
+ async recordAccountQuota(routeId, accountIdentity, observation) {
15383
+ this.#assertOpen();
15384
+ const kept = await recordVaultAccountQuota(this.#options.rosterPath, {
15385
+ routeId,
15386
+ accountIdentity,
15387
+ observation
15388
+ });
15389
+ if (kept)
15390
+ this.#roster = await readVaultRoster(this.#options.rosterPath);
15391
+ return kept;
15392
+ }
15189
15393
  close() {
15190
15394
  if (this.#shutdown.signal.aborted)
15191
15395
  return;
@@ -15194,6 +15398,8 @@ class ClaustrumConsumer {
15194
15398
  (this.#options.clearTimeoutImpl ?? clearTimeout)(this.#timer);
15195
15399
  this.#timer = undefined;
15196
15400
  this.#custody?.close();
15401
+ this.#insula?.release();
15402
+ this.#insula = undefined;
15197
15403
  this.#roster = undefined;
15198
15404
  }
15199
15405
  }
@@ -15715,7 +15921,7 @@ function createNativeRefreshCoordinator(options) {
15715
15921
  name: spec.name,
15716
15922
  timeoutMs: POOL_LOCK_DEFAULTS.timeoutMs
15717
15923
  });
15718
- await new Promise((resolve6) => setTimeout(resolve6, Math.min(POOL_LOCK_DEFAULTS.retryMs, remaining)));
15924
+ await new Promise((resolve7) => setTimeout(resolve7, Math.min(POOL_LOCK_DEFAULTS.retryMs, remaining)));
15719
15925
  }
15720
15926
  }
15721
15927
  async function run(job, request) {
@@ -16579,6 +16785,7 @@ async function requireNativePoolAuthority(paths) {
16579
16785
  if (!nativeMigrationAuthorityPhase(journal)) {
16580
16786
  throw new NativeAuthorityError("migration-incomplete");
16581
16787
  }
16788
+ return journal;
16582
16789
  }
16583
16790
  function deepFreeze(value) {
16584
16791
  if (value === null || typeof value !== "object")
@@ -16929,7 +17136,7 @@ function createNativeLocalCredentialService(options) {
16929
17136
  }
16930
17137
 
16931
17138
  // src/native-runtime-registry.ts
16932
- import { resolve as resolve6 } from "node:path";
17139
+ import { resolve as resolve7 } from "node:path";
16933
17140
  var identities = new WeakMap;
16934
17141
  var nextIdentity = 0;
16935
17142
  function nativeRuntimeOptionIdentity(value) {
@@ -16958,14 +17165,14 @@ function nativeRuntimeStorageKey(options) {
16958
17165
  "runtime",
16959
17166
  "journal",
16960
17167
  "roster"
16961
- ].map((field) => resolve6(paths[field])),
16962
- resolve6(enrollment.tokenPath),
16963
- resolve6(enrollment.statePath),
16964
- resolve6(connection)
17168
+ ].map((field) => resolve7(paths[field])),
17169
+ resolve7(enrollment.tokenPath),
17170
+ resolve7(enrollment.statePath),
17171
+ resolve7(connection)
16965
17172
  ]);
16966
17173
  }
16967
17174
  function nativeRuntimeConnectionFile(host, env) {
16968
- return resolve6(resolveClaustrumConnectionPath(host === "pi" ? env.PI_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE : undefined, host === "pi" ? {
17175
+ return resolve7(resolveClaustrumConnectionPath(host === "pi" ? env.PI_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE : undefined, host === "pi" ? {
16969
17176
  ...env,
16970
17177
  OPENCODE_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE: undefined
16971
17178
  } : env));
@@ -17082,12 +17289,12 @@ async function joinNativePoolWrite(paths, signals, check, now = Date.now) {
17082
17289
  function pause(ms, signals) {
17083
17290
  if (signals.some((signal) => signal.aborted))
17084
17291
  return Promise.resolve();
17085
- return new Promise((resolve7) => {
17292
+ return new Promise((resolve8) => {
17086
17293
  const wake = () => {
17087
17294
  clearTimeout(timer);
17088
17295
  for (const signal of signals)
17089
17296
  signal.removeEventListener("abort", wake);
17090
- resolve7();
17297
+ resolve8();
17091
17298
  };
17092
17299
  const timer = setTimeout(wake, ms);
17093
17300
  for (const signal of signals)
@@ -17218,12 +17425,12 @@ function expectedSeed(input, boundary, refuse3) {
17218
17425
  const inventory = capture(top("inventory", "roster-incomplete"), ["view", "credentials", "skipped"], "roster-incomplete", refuse3);
17219
17426
  const view = inventory.view;
17220
17427
  const skipped = items(inventory.skipped, "roster-incomplete", refuse3);
17221
- const credentialRecords = items(inventory.credentials, "roster-incomplete", refuse3).map((record7) => capture(record7, ["credentialId", "credentialType", "accountIdentity", "state"], "roster-incomplete", refuse3));
17428
+ const credentialRecords = items(inventory.credentials, "roster-incomplete", refuse3).map((record8) => capture(record8, ["credentialId", "credentialType", "accountIdentity", "state"], "roster-incomplete", refuse3));
17222
17429
  if (!filled(view) || skipped.length > 0)
17223
17430
  refuse3("roster-incomplete");
17224
17431
  const listed = new Map;
17225
- for (const record7 of credentialRecords) {
17226
- const { credentialId, credentialType, accountIdentity, state } = record7;
17432
+ for (const record8 of credentialRecords) {
17433
+ const { credentialId, credentialType, accountIdentity, state } = record8;
17227
17434
  if (!filled(credentialId) || !filled(accountIdentity) || !filled(state) || listed.has(credentialId)) {
17228
17435
  refuse3("roster-incomplete");
17229
17436
  }
@@ -17234,7 +17441,7 @@ function expectedSeed(input, boundary, refuse3) {
17234
17441
  const primaryIdentity = primary.accountIdentity;
17235
17442
  boundary.fallback = "seed-conflict";
17236
17443
  const primaryRouteId = top("primaryRouteId", "seed-conflict");
17237
- const legacyRecords = items(top("legacyRows", "seed-conflict"), "seed-conflict", refuse3).map((record7) => capture(record7, [
17444
+ const legacyRecords = items(top("legacyRows", "seed-conflict"), "seed-conflict", refuse3).map((record8) => capture(record8, [
17238
17445
  "routeId",
17239
17446
  "scopedCredentialId",
17240
17447
  "anthropicAccountUuid",
@@ -18022,7 +18229,7 @@ function recencyOnly(patch) {
18022
18229
  return keys4.length === 1 && keys4[0] === "lastUsed" && typeof patch.lastUsed === "number";
18023
18230
  }
18024
18231
  function waitFor(pending, signal) {
18025
- return new Promise((resolve7, reject) => {
18232
+ return new Promise((resolve8, reject) => {
18026
18233
  const abort = () => {
18027
18234
  cleanup2();
18028
18235
  reject(signal.reason);
@@ -18031,7 +18238,7 @@ function waitFor(pending, signal) {
18031
18238
  signal.addEventListener("abort", abort, { once: true });
18032
18239
  pending.then((value) => {
18033
18240
  cleanup2();
18034
- signal.aborted ? reject(signal.reason) : resolve7(value);
18241
+ signal.aborted ? reject(signal.reason) : resolve8(value);
18035
18242
  }, (error) => {
18036
18243
  cleanup2();
18037
18244
  reject(error);
@@ -18348,6 +18555,7 @@ function stickyRetryAfterWithJitter(sessionId, retryAfterSeconds) {
18348
18555
  const hash2 = createHash16("sha256").update(sessionId).digest().readUInt32BE(0);
18349
18556
  return Math.max(1, Math.ceil(retryAfterSeconds)) + hash2 % 21;
18350
18557
  }
18558
+ var localRecoveries = new Map;
18351
18559
 
18352
18560
  class StickySessionRouter {
18353
18561
  options;
@@ -18408,7 +18616,7 @@ class StickySessionRouter {
18408
18616
  this.stateMtimeNs = await this.readStateMtimeNs();
18409
18617
  this.loaded = true;
18410
18618
  }
18411
- async acquireLock() {
18619
+ async acquireLock(wait = true) {
18412
18620
  await mkdir9(dirname10(this.options.path), { recursive: true, mode: 448 });
18413
18621
  const deadline = this.now() + STICKY_ROUTING_LOCK_WAIT_MS;
18414
18622
  while (true) {
@@ -18420,10 +18628,53 @@ class StickySessionRouter {
18420
18628
  });
18421
18629
  if (lock)
18422
18630
  return lock;
18423
- if (this.now() >= deadline) {
18631
+ if (!wait || this.now() >= deadline) {
18424
18632
  throw new Error("Timed out acquiring sticky routing state lock");
18425
18633
  }
18426
- await new Promise((resolve7) => setTimeout(resolve7, STICKY_ROUTING_LOCK_POLL_MS));
18634
+ await new Promise((resolve8) => setTimeout(resolve8, STICKY_ROUTING_LOCK_POLL_MS));
18635
+ }
18636
+ }
18637
+ async recoverLocalPins() {
18638
+ const source2 = this.options.localRecoveryPath;
18639
+ if (!source2 || source2 === this.options.path)
18640
+ return;
18641
+ const key = JSON.stringify([this.options.path, source2]);
18642
+ let recovery = localRecoveries.get(key);
18643
+ if (!recovery) {
18644
+ recovery = this.fillLocalPins(source2).catch(() => {
18645
+ logger.warn("sticky-routing", "local pin recovery unavailable", {
18646
+ reason: "recovery-failed"
18647
+ });
18648
+ });
18649
+ localRecoveries.set(key, recovery);
18650
+ }
18651
+ await recovery;
18652
+ }
18653
+ async fillLocalPins(source2) {
18654
+ const lock = await this.acquireLock(false);
18655
+ try {
18656
+ let legacy;
18657
+ try {
18658
+ legacy = normalizeState(JSON.parse(await readFile9(source2, "utf8")));
18659
+ } catch (error) {
18660
+ if (error.code === "ENOENT")
18661
+ return;
18662
+ throw error;
18663
+ }
18664
+ if (!legacy)
18665
+ return;
18666
+ const state = await this.readState();
18667
+ let changed = false;
18668
+ for (const [key, assignment] of Object.entries(legacy.assignments)) {
18669
+ if (!/^[0-9a-f]{64}$/.test(key) || Object.hasOwn(state.assignments, key))
18670
+ continue;
18671
+ state.assignments[key] = assignment;
18672
+ changed = true;
18673
+ }
18674
+ if (changed)
18675
+ await this.writeState(state, () => lock.assertOwned(), false);
18676
+ } finally {
18677
+ await lock.release();
18427
18678
  }
18428
18679
  }
18429
18680
  assignmentIsActive(assignment) {
@@ -18586,6 +18837,7 @@ class StickySessionRouter {
18586
18837
  async resolve(input) {
18587
18838
  if (!input.sessionId)
18588
18839
  return null;
18840
+ await this.recoverLocalPins();
18589
18841
  await this.prepareLegacySeed();
18590
18842
  await this.refreshStateIfChanged();
18591
18843
  const key = sessionKey(input.sessionId);
@@ -18677,6 +18929,7 @@ class StickySessionRouter {
18677
18929
  async clear(sessionId) {
18678
18930
  if (!sessionId)
18679
18931
  return;
18932
+ await this.recoverLocalPins();
18680
18933
  const key = sessionKey(sessionId);
18681
18934
  const lock = await this.acquireLock();
18682
18935
  try {
@@ -18794,11 +19047,12 @@ function createNativeAccountRuntime(options) {
18794
19047
  throw new NativeCustodyError("closed");
18795
19048
  signal?.throwIfAborted();
18796
19049
  }
19050
+ let localRouting;
18797
19051
  async function authority(signal) {
18798
19052
  check(signal);
18799
19053
  let mode = await readNativeEnrollmentMode(options.host, env);
18800
19054
  if (mode === "local") {
18801
- await requireNativePoolAuthority(paths);
19055
+ localRouting = (await requireNativePoolAuthority(paths)).routingPaths;
18802
19056
  mode = await readNativeEnrollmentMode(options.host, env);
18803
19057
  }
18804
19058
  check(signal);
@@ -18965,6 +19219,10 @@ function createNativeAccountRuntime(options) {
18965
19219
  await vaultRouter.prepareLegacySeed(roster ?? null);
18966
19220
  snapshot.routingPath = vaultPaths.routing;
18967
19221
  snapshot.routingSeed = { ...routingSeed };
19222
+ } else if (localRouting) {
19223
+ snapshot.routingPath = localRouting.destination;
19224
+ if (localRouting.source !== localRouting.destination)
19225
+ snapshot.routingRecoveryPath = localRouting.source;
18968
19226
  }
18969
19227
  return {
18970
19228
  snapshot,
@@ -19769,9 +20027,9 @@ import { dirname as dirname13, isAbsolute as isAbsolute6 } from "node:path";
19769
20027
  // src/pool-paths.ts
19770
20028
  import { createHash as createHash17 } from "node:crypto";
19771
20029
  import { realpath as realpath3 } from "node:fs/promises";
19772
- import { basename as basename6, dirname as dirname12, join as join13, resolve as resolve7 } from "node:path";
20030
+ import { basename as basename6, dirname as dirname12, join as join13, resolve as resolve8 } from "node:path";
19773
20031
  async function canonicalPath(path2) {
19774
- const absolute = resolve7(path2);
20032
+ const absolute = resolve8(path2);
19775
20033
  try {
19776
20034
  return await realpath3(absolute);
19777
20035
  } catch (error) {
@@ -20078,7 +20336,7 @@ async function checkPaths(input, captured) {
20078
20336
  refuse3();
20079
20337
  }
20080
20338
  async function retire(input, journal, assertOwned, hooks) {
20081
- const sources = [
20339
+ const sources2 = [
20082
20340
  {
20083
20341
  role: "config",
20084
20342
  path: input.legacyConfigPath ?? input.paths.legacyConfig,
@@ -20097,7 +20355,7 @@ async function retire(input, journal, assertOwned, hooks) {
20097
20355
  }
20098
20356
  ]
20099
20357
  ];
20100
- const snapshots = await Promise.all(sources.map(async (source2) => {
20358
+ const snapshots = await Promise.all(sources2.map(async (source2) => {
20101
20359
  const snapshot = await readNativeMigrationSource(source2.role, source2.path);
20102
20360
  owned(snapshot);
20103
20361
  if (snapshot.data && snapshot.digest !== source2.digest)
@@ -20116,7 +20374,7 @@ async function retire(input, journal, assertOwned, hooks) {
20116
20374
  }
20117
20375
  return advanceNativeMigration(input.paths, "committed", "retired", {
20118
20376
  assertOwned: async () => {
20119
- for (const source2 of sources)
20377
+ for (const source2 of sources2)
20120
20378
  if (!await absent(source2.path))
20121
20379
  refuse3("retirement-refused");
20122
20380
  await input.processFence();
@@ -21133,8 +21391,8 @@ async function runNativeCustodyActivation(options, hooks = {}) {
21133
21391
  }
21134
21392
  async function commitMode(paths, routingPath, plan, context) {
21135
21393
  let openGate = () => {};
21136
- const gate = new Promise((resolve8) => {
21137
- openGate = resolve8;
21394
+ const gate = new Promise((resolve9) => {
21395
+ openGate = resolve9;
21138
21396
  });
21139
21397
  let leases;
21140
21398
  let scope;
@@ -22800,7 +23058,7 @@ function createNativeUsagePublisher(channel) {
22800
23058
  return {
22801
23059
  observe,
22802
23060
  publish(write) {
22803
- observe(new Promise((resolve8) => setTimeout(resolve8, 0)).then(write));
23061
+ observe(new Promise((resolve9) => setTimeout(resolve9, 0)).then(write));
22804
23062
  },
22805
23063
  async drain() {
22806
23064
  while (pending.size)
@@ -22861,11 +23119,11 @@ async function refreshForRequest(runtime2, ready) {
22861
23119
  break;
22862
23120
  if (combined.aborted)
22863
23121
  break;
22864
- await new Promise((resolve8) => {
23122
+ await new Promise((resolve9) => {
22865
23123
  const finish = () => {
22866
23124
  clearTimeout(pause2);
22867
23125
  combined.removeEventListener("abort", finish);
22868
- resolve8();
23126
+ resolve9();
22869
23127
  };
22870
23128
  const pause2 = setTimeout(finish, 25);
22871
23129
  combined.addEventListener("abort", finish, { once: true });
@@ -22881,16 +23139,16 @@ async function refreshForRequest(runtime2, ready) {
22881
23139
  function waitForRequest(pending, signal) {
22882
23140
  if (!signal)
22883
23141
  return pending;
22884
- return new Promise((resolve8, reject) => {
23142
+ return new Promise((resolve9, reject) => {
22885
23143
  const abort = () => reject(signal.reason);
22886
23144
  signal.addEventListener("abort", abort, { once: true });
22887
- pending.then(resolve8, reject).finally(() => signal.removeEventListener("abort", abort));
23145
+ pending.then(resolve9, reject).finally(() => signal.removeEventListener("abort", abort));
22888
23146
  if (signal.aborted)
22889
23147
  abort();
22890
23148
  });
22891
23149
  }
22892
23150
  // src/native-vault-connection.ts
22893
- import { dirname as dirname15, join as join14, resolve as resolve9 } from "node:path";
23151
+ import { dirname as dirname15, join as join14, resolve as resolve10 } from "node:path";
22894
23152
  // ../../node_modules/@cortexkit/common-auth/dist/opencode2/integration.js
22895
23153
  var PLACEHOLDER_PREFIX = "common-auth-placeholder";
22896
23154
  var PLACEHOLDER_LIFETIME_MS = 365 * 24 * 60 * 60 * 1000;
@@ -22901,7 +23159,7 @@ function placeholderSecret(integrationID) {
22901
23159
  import { createHash as createHash19 } from "node:crypto";
22902
23160
  import { constants as constants3 } from "node:fs";
22903
23161
  import { lstat as lstat8, mkdir as mkdir12, open as open9, unlink as unlink7 } from "node:fs/promises";
22904
- import { dirname as dirname14, resolve as resolve8 } from "node:path";
23162
+ import { dirname as dirname14, resolve as resolve9 } from "node:path";
22905
23163
  import { setTimeout as sleep } from "node:timers/promises";
22906
23164
 
22907
23165
  // ../../node_modules/@cortexkit/common-auth/dist/pi-slot/lock.js
@@ -23288,8 +23546,8 @@ function createPiSlot(options, onStep, hooks) {
23288
23546
  if (!provider || !options.authPath || !options.stashPath || !placeholderKey || placeholderKey.startsWith("!") || placeholderKey.includes("$") || !Number.isFinite(timeout) || timeout < 0) {
23289
23547
  throw new PiSlotError("invalid-options");
23290
23548
  }
23291
- const authPath = resolve8(options.authPath);
23292
- const stashPath = resolve8(options.stashPath);
23549
+ const authPath = resolve9(options.authPath);
23550
+ const stashPath = resolve9(options.stashPath);
23293
23551
  if (authPath === stashPath || stashPath === `${authPath}.lock`) {
23294
23552
  throw new PiSlotError("invalid-options");
23295
23553
  }
@@ -23427,7 +23685,7 @@ function createPiSlot(options, onStep, hooks) {
23427
23685
  // src/native-vault-connection.ts
23428
23686
  function nativePiAuthSlotPaths(options) {
23429
23687
  return {
23430
- authPath: resolve9(options.authPath),
23688
+ authPath: resolve10(options.authPath),
23431
23689
  stashPath: join14(dirname15(getHostClaustrumEnrollmentPaths("pi", options.env).tokenPath), "pi-auth-slot", "anthropic.json"),
23432
23690
  provider: "anthropic",
23433
23691
  placeholderKey: placeholderSecret("anthropic")
@@ -23446,7 +23704,7 @@ import { createHash as createHash20 } from "node:crypto";
23446
23704
  import { realpathSync } from "node:fs";
23447
23705
  import { mkdir as mkdir13, readdir as readdir3, rm as rm7, stat as stat7, writeFile as writeFile6 } from "node:fs/promises";
23448
23706
  import { tmpdir as tmpdir5 } from "node:os";
23449
- import { join as join15, resolve as resolve10 } from "node:path";
23707
+ import { join as join15, resolve as resolve11 } from "node:path";
23450
23708
  var CLAUDE_PRIME_COMMAND_NAME = "claude-prime";
23451
23709
  var PRIME_TICK_MS = 60000;
23452
23710
  var PRIME_DUE_OFFSET_MS = 60000;
@@ -23637,7 +23895,7 @@ function defaultMarkerDir() {
23637
23895
  return join15(tmpdir5(), "opencode-anthropic-auth", "prime");
23638
23896
  }
23639
23897
  function primeStorageFingerprint(storagePath) {
23640
- const absolutePath = resolve10(storagePath);
23898
+ const absolutePath = resolve11(storagePath);
23641
23899
  let canonicalPath2;
23642
23900
  try {
23643
23901
  canonicalPath2 = realpathSync(absolutePath);
@@ -24317,9 +24575,9 @@ class QuotaHeaderFeedRegistry {
24317
24575
  await this.reapStaleSiblingLeases(directory);
24318
24576
  let entries = {};
24319
24577
  try {
24320
- const record7 = JSON.parse(await readFile10(this.filePath, "utf8"));
24321
- if (record7.version === QUOTA_HEADER_FEED_SCHEMA_VERSION && record7.entries && typeof record7.entries === "object")
24322
- entries = record7.entries;
24578
+ const record8 = JSON.parse(await readFile10(this.filePath, "utf8"));
24579
+ if (record8.version === QUOTA_HEADER_FEED_SCHEMA_VERSION && record8.entries && typeof record8.entries === "object")
24580
+ entries = record8.entries;
24323
24581
  } catch {}
24324
24582
  entries[accountKey] = cleanEntry;
24325
24583
  const tempPath = `${this.filePath}.${randomUUID16()}.tmp`;
@@ -24373,10 +24631,10 @@ class QuotaHeaderFeedRegistry {
24373
24631
  const newest = new Map;
24374
24632
  await Promise.all(names2.map(async (name) => {
24375
24633
  try {
24376
- const record7 = JSON.parse(await readFile10(join16(directory, name), "utf8"));
24377
- if (record7.version !== QUOTA_HEADER_FEED_SCHEMA_VERSION || !record7.entries || typeof record7.entries !== "object")
24634
+ const record8 = JSON.parse(await readFile10(join16(directory, name), "utf8"));
24635
+ if (record8.version !== QUOTA_HEADER_FEED_SCHEMA_VERSION || !record8.entries || typeof record8.entries !== "object")
24378
24636
  return;
24379
- for (const [accountKey, candidate] of Object.entries(record7.entries)) {
24637
+ for (const [accountKey, candidate] of Object.entries(record8.entries)) {
24380
24638
  if (!candidate || typeof candidate !== "object" || candidate.schema_version !== QUOTA_HEADER_FEED_SCHEMA_VERSION || candidate.provider !== "anthropic" || !validIdentity(candidate))
24381
24639
  continue;
24382
24640
  const observed = candidate.observed_at_ms;
@@ -24946,8 +25204,8 @@ class QuotaManager {
24946
25204
  if (this.lastApiCallAt !== undefined) {
24947
25205
  const remaining = QuotaManager.API_CALL_GAP_MS - (this.now() - this.lastApiCallAt);
24948
25206
  if (remaining > 0)
24949
- await new Promise((resolve11) => {
24950
- const id2 = nativeSetTimeout(resolve11, remaining);
25207
+ await new Promise((resolve12) => {
25208
+ const id2 = nativeSetTimeout(resolve12, remaining);
24951
25209
  if (typeof id2 === "object" && "unref" in id2)
24952
25210
  id2.unref();
24953
25211
  });
@@ -25850,7 +26108,7 @@ class PersistentRelaySession {
25850
26108
  return;
25851
26109
  if (this.connecting)
25852
26110
  return await this.connecting;
25853
- this.connecting = new Promise((resolve11, reject) => {
26111
+ this.connecting = new Promise((resolve12, reject) => {
25854
26112
  const url = toWebSocketUrl(this.config.url, this.config.token);
25855
26113
  const parsed = new URL(url);
25856
26114
  parsed.searchParams.set("affinity", this.affinity);
@@ -25872,7 +26130,7 @@ class PersistentRelaySession {
25872
26130
  if (message.type === "ready") {
25873
26131
  this.timers.clearTimeout(timeout);
25874
26132
  this.serverState = message.state;
25875
- resolve11();
26133
+ resolve12();
25876
26134
  return;
25877
26135
  }
25878
26136
  this.handleMessage(message);
@@ -25924,15 +26182,15 @@ class PersistentRelaySession {
25924
26182
  const sentAt = perfNowMs();
25925
26183
  let resolveDone;
25926
26184
  let rejectDone;
25927
- const done2 = new Promise((resolve11, reject) => {
25928
- resolveDone = resolve11;
26185
+ const done2 = new Promise((resolve12, reject) => {
26186
+ resolveDone = resolve12;
25929
26187
  rejectDone = reject;
25930
26188
  });
25931
- const response = new Promise((resolve11, reject) => {
26189
+ const response = new Promise((resolve12, reject) => {
25932
26190
  const pending = {
25933
26191
  payload,
25934
26192
  bodyText,
25935
- resolve: resolve11,
26193
+ resolve: resolve12,
25936
26194
  reject,
25937
26195
  streamDone: false,
25938
26196
  accepted: false,
@@ -1,5 +1,7 @@
1
1
  export interface QuotaCodec {
2
2
  readonly validate: (value: unknown) => boolean;
3
+ /** Normalize persisted data before validation; older codecs may omit this. */
4
+ readonly decode?: (value: unknown) => unknown;
3
5
  readonly merge: (stored: unknown, observation: unknown) => unknown;
4
6
  }
5
7
  /**
@@ -2,8 +2,8 @@ export type { QuotaCodec } from './codec.js';
2
2
  export { quotaCodec } from './codec.js';
3
3
  export type { QuotaTextForm, QuotaTextOptions } from './format.js';
4
4
  export { formatQuota, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, } from './format.js';
5
- export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
- export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, emptyQuotaMap, isQuotaMap, } from './map.js';
5
+ export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
+ export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
7
7
  export type { ObservedBudget, ObservedPair, ObservedReading, QuotaObservation, } from './merge.js';
8
8
  export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
9
9
  export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, } from './projection.js';
@@ -44,16 +44,27 @@ export interface CreditBudgetCleared {
44
44
  checkedAt: number;
45
45
  }
46
46
  export type CreditBudgetEntry = CreditBudgetReading | CreditBudgetCleared;
47
+ export interface QuotaCounter {
48
+ value: number;
49
+ checkedAt: number;
50
+ }
47
51
  export interface QuotaMap {
48
52
  limits: QuotaEntry[];
49
53
  /** Absent means no observation has reported on the budget yet. */
50
54
  budget?: CreditBudgetEntry;
55
+ /** Provider-named counts, each with its own observation time. */
56
+ counters?: Record<string, QuotaCounter>;
51
57
  }
52
58
  export declare function emptyQuotaMap(): QuotaMap;
53
59
  /** The time an entry speaks for: its reading, retirement or absence time. */
54
60
  export declare function entryTime(entry: QuotaEntry): number;
55
61
  export declare function limitKey(scope: string, label: string): string;
56
62
  export declare function isCreditBudgetEntry(value: unknown): value is CreditBudgetEntry;
63
+ export declare function isQuotaCounters(value: unknown): value is Record<string, QuotaCounter>;
64
+ /** Keep usable counters without letting one malformed count hide a quota map. */
65
+ export declare function readQuotaCounters(value: unknown): Record<string, QuotaCounter> | undefined;
66
+ /** Decode persisted quota, dropping malformed counters but not limit evidence. */
67
+ export declare function decodeQuotaMap(value: unknown): QuotaMap | undefined;
57
68
  /**
58
69
  * True when `value` is a well-formed quota map. Unrecognised top-level keys
59
70
  * are tolerated (and preserved by merge) so a newer writer's additions
@@ -1,4 +1,4 @@
1
- import { type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaMap } from './map.js';
2
2
  export interface ObservedReading {
3
3
  /** Defaults to `all`. */
4
4
  scope?: string;
@@ -32,6 +32,8 @@ export interface QuotaObservation {
32
32
  coverage?: ObservedPair[];
33
33
  /** Absent means the observation says nothing about the budget. */
34
34
  budget?: ObservedBudget;
35
+ /** Each named count uses its own time, independently of limits and budget. */
36
+ counters?: Record<string, QuotaCounter>;
35
37
  }
36
38
  /** Thrown by `mergeQuotaObservation` when either argument is malformed. */
37
39
  export declare class QuotaCodecError extends TypeError {
@@ -1,4 +1,4 @@
1
- import { type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaMap } from './map.js';
2
2
  export interface ProjectedLimit {
3
3
  scope: string;
4
4
  label: string;
@@ -30,6 +30,7 @@ export interface ProjectedQuota {
30
30
  checkedAt?: number;
31
31
  /** Present only when the stored budget is a reading, not cleared. */
32
32
  budget?: ProjectedBudget;
33
+ counters?: Record<string, QuotaCounter>;
33
34
  }
34
35
  /**
35
36
  * Resolves `map` for a request in `scope` (`all` or a model family). A family
@@ -49,6 +49,8 @@ export type StoredCredential = (OAuthCredential & {
49
49
  /** Quota codec supplied by `/quota`: the store never interprets the map itself. */
50
50
  export interface QuotaCodec {
51
51
  validate(value: unknown): boolean;
52
+ /** Normalize persisted quota before validation, when the codec supports it. */
53
+ decode?(value: unknown): unknown;
52
54
  merge(stored: unknown | undefined, observation: unknown): unknown;
53
55
  }
54
56
  /**
@@ -42,8 +42,10 @@ export interface NativeAccountSnapshot {
42
42
  mode: 'local' | 'claustrum';
43
43
  settings: Record<string, unknown>;
44
44
  accounts: NativeAccountView[];
45
- /** Vault session-to-account assignments use this separate file. Local mode keeps the assignment file at the destination recorded by migration. */
45
+ /** Vault storage, or the local destination recorded by the authorized migration journal. */
46
46
  routingPath?: string;
47
+ /** Distinct pre-migration routing file that OpenCode local serving may have recreated. */
48
+ routingRecoveryPath?: string;
47
49
  routingSeed?: NativeVaultRoutingSeed;
48
50
  /** AccountStorage-shaped input for pure policy predicates, never a credential or persistence authority. */
49
51
  policyStorage: AccountStorage;
@@ -126,7 +126,7 @@ export declare function readNativeMigrationJournal(paths: NativePoolPaths): Prom
126
126
  */
127
127
  export declare function nativeMigrationAuthorityPhase(journal: NativeMigrationJournal | undefined): 'committed' | 'retired' | undefined;
128
128
  /** An empty or readable store is not proof that the offline authority flip completed. */
129
- export declare function requireNativePoolAuthority(paths: NativePoolPaths): Promise<void>;
129
+ export declare function requireNativePoolAuthority(paths: NativePoolPaths): Promise<NativeMigrationJournal>;
130
130
  interface JournalHooks {
131
131
  /** Outer controller and pool leases remain held through the atomic journal rename. */
132
132
  assertOwned?: () => Promise<void>;
@@ -106,12 +106,15 @@ export declare class StickySessionRouter {
106
106
  now?: () => number;
107
107
  assignmentTtlMs?: number;
108
108
  vaultSeed?: NativeVaultRoutingSeed;
109
+ localRecoveryPath?: string;
109
110
  });
110
111
  private now;
111
112
  private readState;
112
113
  private readStateMtimeNs;
113
114
  private refreshStateIfChanged;
114
115
  private acquireLock;
116
+ private recoverLocalPins;
117
+ private fillLocalPins;
115
118
  private assignmentIsActive;
116
119
  private prune;
117
120
  private writeState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,11 +30,12 @@
30
30
  "prepublishOnly": "bun run build"
31
31
  },
32
32
  "devDependencies": {
33
- "@cortexkit/common-auth": "0.15.0"
33
+ "@cortexkit/common-auth": "0.17.0"
34
34
  },
35
35
  "dependencies": {
36
36
  "@cortexkit/claustrum-client": "0.9.0",
37
37
  "@cortexkit/private-files": "0.2.1",
38
+ "@cortexkit/subc-client": "^0.16.0",
38
39
  "jsonc-parser": "^3.3.1",
39
40
  "xxhash-wasm": "^1.1.0"
40
41
  }