@cortexkit/anthropic-auth-core 2.4.6 → 2.4.8
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/accounts.d.ts +6 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +191 -24
- package/dist/insula-quota.d.ts +2 -3
- package/dist/native-account-runtime.d.ts +5 -1
- package/dist/native-vault-refusal.d.ts +28 -0
- package/dist/sticky-routing.d.ts +3 -0
- package/package.json +2 -2
package/dist/accounts.d.ts
CHANGED
|
@@ -400,6 +400,12 @@ export declare function getScopedQuotaWindowForModel(quota: OAuthQuotaSnapshot |
|
|
|
400
400
|
export declare function quotaSnapshotModelScopeIsExhausted(quota: OAuthQuotaSnapshot | undefined, model: unknown): boolean;
|
|
401
401
|
export declare function quotaSnapshotPassesModelScope(quota: OAuthQuotaSnapshot | undefined, model: unknown): boolean;
|
|
402
402
|
export declare function quotaSnapshotPassesPolicy(quota: OAuthQuotaSnapshot | undefined, storage: AccountStorage | null): boolean;
|
|
403
|
+
/** A poll's omitted session window is evidence, unlike a partial header harvest. */
|
|
404
|
+
export declare function quotaSnapshotOmitsFiveHour(quota: OAuthQuotaSnapshot | undefined): quota is OAuthQuotaSnapshot & {
|
|
405
|
+
checkedAt: number;
|
|
406
|
+
five_hour?: undefined;
|
|
407
|
+
seven_day: AccountQuotaWindow;
|
|
408
|
+
};
|
|
403
409
|
export declare function quotaSnapshotHasStandardWindows(quota: OAuthQuotaSnapshot | undefined): quota is OAuthQuotaSnapshot;
|
|
404
410
|
export declare const DEFAULT_KILLSWITCH_THRESHOLDS: Record<QuotaWindowName | 'scoped', number>;
|
|
405
411
|
export declare function normalizeKillswitchThresholds(thresholds: KillswitchThresholds | undefined): Record<QuotaWindowName | 'scoped', number>;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export { CLAUDE_CODE_DEFAULT_BETAS, CLAUDE_MODEL_CAPABILITIES, getClaudeModelCap
|
|
|
25
25
|
export * from './model-remap.ts';
|
|
26
26
|
export * from './models.ts';
|
|
27
27
|
export * from './native-account-refusal.ts';
|
|
28
|
-
export type { NativeAccountRuntime, NativeAccountRuntimeOptions, NativeAccountWriteInput, NativeApiSubject, NativeLocalAuthorizeOptions, NativeRelayUpdate, } from './native-account-runtime.ts';
|
|
28
|
+
export type { NativeAccountPolicy, NativeAccountRuntime, NativeAccountRuntimeOptions, NativeAccountWriteInput, NativeApiSubject, NativeLocalAuthorizeOptions, NativeQuotaPolicyLimit, NativeRelayUpdate, } from './native-account-runtime.ts';
|
|
29
29
|
export { acquireNativeAccountRuntime, createNativeAccountRuntime, nativeAccountPolicy, } from './native-account-runtime.ts';
|
|
30
30
|
export type { NativeAccountMetadataPatch, NativeAccountSnapshot, NativeAccountView, } from './native-account-view.ts';
|
|
31
31
|
export { formatNativeAccountState, nativeAccountCanServe, } from './native-account-view.ts';
|
|
@@ -62,6 +62,7 @@ export * from './native-usage-publication.ts';
|
|
|
62
62
|
export { NATIVE_VAULT_NO_ACCOUNT_MESSAGE, NativeVaultNoAccountError, readNativeAccountsForRequest, } from './native-vault-admission.ts';
|
|
63
63
|
export type { NativePiAuthSlot, NativePiAuthSlotErrorCode, NativePiAuthSlotInspection, NativePiAuthSlotOptions, NativeVaultDisconnectOptions, NativeVaultDisconnectResult, } from './native-vault-connection.ts';
|
|
64
64
|
export { disconnectNativeVaultForHost, NativePiAuthSlotError, nativePiAuthSlotPaths, openNativePiAuthSlot, } from './native-vault-connection.ts';
|
|
65
|
+
export { createNativeVaultNoRouteResponse, NativeVaultQuotaPolicyError, } from './native-vault-refusal.ts';
|
|
65
66
|
export type { NativeVaultQuotaPurpose, NativeVaultRuntime, NativeVaultRuntimeOptions, NativeVaultWatch, NativeVaultWatchOptions, } from './native-vault-runtime.ts';
|
|
66
67
|
export { acquireNativeVaultRuntime, createNativeVaultRuntime, discoverNativeVaultInventory, publishNativeVaultRosterSeed, publishNativeVaultRuntimeSeed, } from './native-vault-runtime.ts';
|
|
67
68
|
export * from './oauth-origin.ts';
|
package/dist/index.js
CHANGED
|
@@ -4509,6 +4509,8 @@ function mergeHeaderOwnedWindow(existingSnapshot, incomingSnapshot, key) {
|
|
|
4509
4509
|
const incoming = incomingSnapshot[key];
|
|
4510
4510
|
if (!incoming)
|
|
4511
4511
|
return existing;
|
|
4512
|
+
if (key === "five_hour" && quotaSnapshotOmitsFiveHour(existingSnapshot) && incoming.checkedAt <= (existingSnapshot.checkedAt ?? 0))
|
|
4513
|
+
return;
|
|
4512
4514
|
if (!existing)
|
|
4513
4515
|
return incoming;
|
|
4514
4516
|
if (incoming.checkedAt > existing.checkedAt)
|
|
@@ -4545,6 +4547,8 @@ function fieldSourcesForMergedQuota(existing, incoming, merged) {
|
|
|
4545
4547
|
if (source)
|
|
4546
4548
|
fieldSources[field] = source;
|
|
4547
4549
|
}
|
|
4550
|
+
if (!merged.five_hour && quotaSnapshotOmitsFiveHour(existing))
|
|
4551
|
+
fieldSources.five_hour = "poll";
|
|
4548
4552
|
return Object.keys(fieldSources).length > 0 ? fieldSources : undefined;
|
|
4549
4553
|
}
|
|
4550
4554
|
function mergeHeaderQuotaForPersistence(existing, incoming) {
|
|
@@ -4560,7 +4564,10 @@ function mergeHeaderQuotaForPersistence(existing, incoming) {
|
|
|
4560
4564
|
extraUsage: existing.extraUsage ?? incoming.extraUsage,
|
|
4561
4565
|
fallbackAdvised: incoming.fieldSources?.fallbackAdvised !== undefined ? incoming.fallbackAdvised : existing.fallbackAdvised ?? incoming.fallbackAdvised,
|
|
4562
4566
|
bindingWindow: preservePollBinding ? existing.bindingWindow : incoming.bindingWindow ?? existing.bindingWindow,
|
|
4563
|
-
bindingWindowSource: preservePollBinding ? "poll" : incoming.bindingWindowSource ?? existing.bindingWindowSource
|
|
4567
|
+
bindingWindowSource: preservePollBinding ? "poll" : incoming.bindingWindowSource ?? existing.bindingWindowSource,
|
|
4568
|
+
...quotaSnapshotOmitsFiveHour(existing) && {
|
|
4569
|
+
checkedAt: existing.checkedAt
|
|
4570
|
+
}
|
|
4564
4571
|
};
|
|
4565
4572
|
const fieldSources = fieldSourcesForMergedQuota(existing, incoming, merged);
|
|
4566
4573
|
return {
|
|
@@ -4993,20 +5000,25 @@ function quotaSnapshotPassesPolicy(quota, storage) {
|
|
|
4993
5000
|
if (!quotaEnabled(storage))
|
|
4994
5001
|
return true;
|
|
4995
5002
|
const thresholds = getQuotaMinimumRemainingThresholds(storage);
|
|
5003
|
+
let sawUnknownWindow = false;
|
|
4996
5004
|
for (const key of ["five_hour", "seven_day"]) {
|
|
5005
|
+
if (key === "five_hour" && quotaSnapshotOmitsFiveHour(quota))
|
|
5006
|
+
continue;
|
|
4997
5007
|
const window = quota?.[key];
|
|
4998
|
-
if (!window)
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
return !failClosedOnUnknownQuota(storage);
|
|
5008
|
+
if (!window || !Number.isFinite(window.remainingPercent)) {
|
|
5009
|
+
sawUnknownWindow = true;
|
|
5010
|
+
continue;
|
|
5002
5011
|
}
|
|
5003
5012
|
if (window.remainingPercent < thresholds[key])
|
|
5004
5013
|
return false;
|
|
5005
5014
|
}
|
|
5006
|
-
return
|
|
5015
|
+
return !sawUnknownWindow || !failClosedOnUnknownQuota(storage);
|
|
5016
|
+
}
|
|
5017
|
+
function quotaSnapshotOmitsFiveHour(quota) {
|
|
5018
|
+
return Boolean((quota?.source === "poll" || quota?.fieldSources?.five_hour === "poll") && quota && !quota.five_hour && quota.seven_day && Number.isFinite(quota.checkedAt) && (quota.checkedAt ?? 0) > 0);
|
|
5007
5019
|
}
|
|
5008
5020
|
function quotaSnapshotHasStandardWindows(quota) {
|
|
5009
|
-
return Boolean(quota?.
|
|
5021
|
+
return Boolean(quota?.seven_day && (quota.five_hour || quotaSnapshotOmitsFiveHour(quota)));
|
|
5010
5022
|
}
|
|
5011
5023
|
var DEFAULT_KILLSWITCH_THRESHOLDS = {
|
|
5012
5024
|
five_hour: 5,
|
|
@@ -5040,6 +5052,8 @@ function killswitchPassesPolicy(quota, storage, accountId, modelId) {
|
|
|
5040
5052
|
const thresholds = getKillswitchThresholdsForAccount(storage, accountId);
|
|
5041
5053
|
let sawUnknownWindow = false;
|
|
5042
5054
|
for (const key of ["five_hour", "seven_day"]) {
|
|
5055
|
+
if (key === "five_hour" && quotaSnapshotOmitsFiveHour(quota))
|
|
5056
|
+
continue;
|
|
5043
5057
|
const window = quota?.[key];
|
|
5044
5058
|
if (!window || !Number.isFinite(window.remainingPercent)) {
|
|
5045
5059
|
if (thresholds[key] > 0)
|
|
@@ -5106,6 +5120,8 @@ function getQuotaNextRefreshAt(quota, storage, now) {
|
|
|
5106
5120
|
const thresholds = getQuotaMinimumRemainingThresholds(storage);
|
|
5107
5121
|
const blockedResetTimes = [];
|
|
5108
5122
|
for (const key of ["five_hour", "seven_day"]) {
|
|
5123
|
+
if (key === "five_hour" && quotaSnapshotOmitsFiveHour(quota))
|
|
5124
|
+
continue;
|
|
5109
5125
|
const window = quota?.[key];
|
|
5110
5126
|
if (!window)
|
|
5111
5127
|
return capAtOldestWindow(now + intervalMs);
|
|
@@ -7539,6 +7555,8 @@ class InsulaQuotaSource {
|
|
|
7539
7555
|
...typeof entry.source === "string" && { source: entry.source }
|
|
7540
7556
|
}
|
|
7541
7557
|
};
|
|
7558
|
+
if (entry.stale !== undefined && (!record(entry.stale) || entry.stale.class !== "local_source_unavailable"))
|
|
7559
|
+
return { miss: "upstream-failed", reading };
|
|
7542
7560
|
return now >= fetchedAt + MAX_AGE || entry.stale !== undefined ? { miss: "stale", reading } : { reading };
|
|
7543
7561
|
}
|
|
7544
7562
|
if (this.#completeProviders.has(provider))
|
|
@@ -7882,6 +7900,7 @@ function combineAbortSignals(signals) {
|
|
|
7882
7900
|
}
|
|
7883
7901
|
|
|
7884
7902
|
// ../../node_modules/@cortexkit/common-auth/dist/claustrum/custody.js
|
|
7903
|
+
import { channel } from "node:diagnostics_channel";
|
|
7885
7904
|
import { ClaustrumCredentialError as ClaustrumCredentialError3 } from "@cortexkit/claustrum-client";
|
|
7886
7905
|
|
|
7887
7906
|
// ../../node_modules/@cortexkit/common-auth/dist/claustrum/host-slot.js
|
|
@@ -7941,6 +7960,7 @@ function scopedRetryReason(served, current) {
|
|
|
7941
7960
|
return "rotated";
|
|
7942
7961
|
}
|
|
7943
7962
|
var defaultLogger = createLogger("claustrum");
|
|
7963
|
+
var phaseDiagnostics = channel("common-auth.claustrum.phases");
|
|
7944
7964
|
function decideScopedRetryAfter401(site, served, current, logger2 = defaultLogger) {
|
|
7945
7965
|
const retry = isScopedCredentialRotation(served, current);
|
|
7946
7966
|
logger2.debug("scoped 401 re-authorized", {
|
|
@@ -8140,6 +8160,11 @@ class ClaustrumScopedCustody {
|
|
|
8140
8160
|
}
|
|
8141
8161
|
async#token(signal) {
|
|
8142
8162
|
this.#check(signal);
|
|
8163
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
8164
|
+
phaseDiagnostics.publish({
|
|
8165
|
+
logger: this.#logger,
|
|
8166
|
+
phase: "token read begin"
|
|
8167
|
+
});
|
|
8143
8168
|
const pending = this.#readToken();
|
|
8144
8169
|
this.#tokenReads.add(pending);
|
|
8145
8170
|
let value;
|
|
@@ -8147,6 +8172,11 @@ class ClaustrumScopedCustody {
|
|
|
8147
8172
|
value = await pending;
|
|
8148
8173
|
} finally {
|
|
8149
8174
|
this.#tokenReads.delete(pending);
|
|
8175
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
8176
|
+
phaseDiagnostics.publish({
|
|
8177
|
+
logger: this.#logger,
|
|
8178
|
+
phase: "token read end"
|
|
8179
|
+
});
|
|
8150
8180
|
}
|
|
8151
8181
|
this.#check(signal);
|
|
8152
8182
|
if (!/^[0-9a-f]{64}$/.test(value.token) || !Number.isSafeInteger(value.token_generation) || value.token_generation < 1) {
|
|
@@ -8202,11 +8232,22 @@ class ClaustrumScopedCustody {
|
|
|
8202
8232
|
this.#kept.clear();
|
|
8203
8233
|
throw error;
|
|
8204
8234
|
}
|
|
8235
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
8236
|
+
phaseDiagnostics.publish({
|
|
8237
|
+
logger: this.#logger,
|
|
8238
|
+
phase: "list scoped begin"
|
|
8239
|
+
});
|
|
8205
8240
|
try {
|
|
8206
8241
|
inventory = await this.#call(() => this.#client.listScoped(token), signal);
|
|
8207
8242
|
} catch (error) {
|
|
8208
8243
|
this.listFailed(error);
|
|
8209
8244
|
throw error;
|
|
8245
|
+
} finally {
|
|
8246
|
+
if (phaseDiagnostics.hasSubscribers)
|
|
8247
|
+
phaseDiagnostics.publish({
|
|
8248
|
+
logger: this.#logger,
|
|
8249
|
+
phase: "list scoped end"
|
|
8250
|
+
});
|
|
8210
8251
|
}
|
|
8211
8252
|
const skipped = [];
|
|
8212
8253
|
const refused = [];
|
|
@@ -10477,6 +10518,7 @@ class ClaustrumConsumer {
|
|
|
10477
10518
|
}
|
|
10478
10519
|
// ../../node_modules/@cortexkit/common-auth/dist/claustrum/hub.js
|
|
10479
10520
|
import { createHash as createHash6 } from "node:crypto";
|
|
10521
|
+
import { channel as channel2 } from "node:diagnostics_channel";
|
|
10480
10522
|
import { readdir as readdir2, stat as stat7 } from "node:fs/promises";
|
|
10481
10523
|
import { dirname as dirname8, join as join10, resolve as resolve9 } from "node:path";
|
|
10482
10524
|
var sharedRosters = new Map;
|
|
@@ -11074,7 +11116,24 @@ class ClaustrumDiscoveryHub {
|
|
|
11074
11116
|
throw closed("Claustrum hub member has no base view");
|
|
11075
11117
|
if (base instanceof Error)
|
|
11076
11118
|
throw base;
|
|
11077
|
-
|
|
11119
|
+
let seed;
|
|
11120
|
+
if (!base.file) {
|
|
11121
|
+
const phaseDiagnostics2 = channel2("common-auth.claustrum.phases");
|
|
11122
|
+
if (phaseDiagnostics2.hasSubscribers)
|
|
11123
|
+
phaseDiagnostics2.publish({
|
|
11124
|
+
logger: this.#logger,
|
|
11125
|
+
phase: `seed read ${entry.provider} begin`
|
|
11126
|
+
});
|
|
11127
|
+
try {
|
|
11128
|
+
seed = await this.#seed(entry);
|
|
11129
|
+
} finally {
|
|
11130
|
+
if (phaseDiagnostics2.hasSubscribers)
|
|
11131
|
+
phaseDiagnostics2.publish({
|
|
11132
|
+
logger: this.#logger,
|
|
11133
|
+
phase: `seed read ${entry.provider} end`
|
|
11134
|
+
});
|
|
11135
|
+
}
|
|
11136
|
+
}
|
|
11078
11137
|
const roster = await refreshVaultRoster({
|
|
11079
11138
|
path: entry.path,
|
|
11080
11139
|
custody: { discover: async () => share },
|
|
@@ -18792,6 +18851,13 @@ var nativeQuotaCodec = {
|
|
|
18792
18851
|
return toNativeQuotaMap(mergeHeaderQuotaForPersistence(previous, incoming));
|
|
18793
18852
|
const previousTime = quotaSnapshotCheckedAt(previous);
|
|
18794
18853
|
const incomingTime = quotaSnapshotCheckedAt(incoming);
|
|
18854
|
+
if (incoming.source === "poll" && incoming.checkedAt !== undefined && previous.checkedAt !== undefined && (previous.source === "poll" || quotaSnapshotOmitsFiveHour(previous)) && incoming.checkedAt < previous.checkedAt)
|
|
18855
|
+
return toNativeQuotaMap(previous);
|
|
18856
|
+
if (incoming.source === "poll" && incoming.seven_day && !incoming.five_hour && incoming.checkedAt !== undefined && incoming.checkedAt > (previous.five_hour?.checkedAt ?? 0) && (previous.source !== "poll" || incoming.checkedAt >= (previous.checkedAt ?? 0)))
|
|
18857
|
+
return toNativeQuotaMap(previous.source === "headers" ? mergeHeaderQuotaForPersistence(incoming, {
|
|
18858
|
+
...previous,
|
|
18859
|
+
five_hour: undefined
|
|
18860
|
+
}) : incoming);
|
|
18795
18861
|
return toNativeQuotaMap(previousTime > incomingTime || previousTime === incomingTime && previous.source === "poll" && incoming.source !== "poll" ? previous : incoming);
|
|
18796
18862
|
}
|
|
18797
18863
|
};
|
|
@@ -20504,6 +20570,12 @@ function normalizeQuotaHeaders(headers, now = Date.now()) {
|
|
|
20504
20570
|
};
|
|
20505
20571
|
}
|
|
20506
20572
|
function mergeHeaderQuotaSnapshot(existing, incoming) {
|
|
20573
|
+
if (quotaSnapshotOmitsFiveHour(existing) && incoming.five_hour && incoming.five_hour.checkedAt <= existing.checkedAt) {
|
|
20574
|
+
return mergeHeaderQuotaSnapshot(existing, {
|
|
20575
|
+
...incoming,
|
|
20576
|
+
five_hour: undefined
|
|
20577
|
+
});
|
|
20578
|
+
}
|
|
20507
20579
|
const {
|
|
20508
20580
|
scoped: existingScoped,
|
|
20509
20581
|
extraUsage: existingExtraUsage,
|
|
@@ -20525,6 +20597,8 @@ function mergeHeaderQuotaSnapshot(existing, incoming) {
|
|
|
20525
20597
|
if (source2)
|
|
20526
20598
|
fieldSources2[field] = source2;
|
|
20527
20599
|
}
|
|
20600
|
+
if (quotaSnapshotOmitsFiveHour(existing) && !incoming.five_hour)
|
|
20601
|
+
fieldSources2.five_hour = "poll";
|
|
20528
20602
|
return {
|
|
20529
20603
|
...existingWithoutPollOwnedFields,
|
|
20530
20604
|
...incomingWithoutPollOwnedFields,
|
|
@@ -20534,7 +20608,10 @@ function mergeHeaderQuotaSnapshot(existing, incoming) {
|
|
|
20534
20608
|
bindingWindow: existing?.bindingWindowSource === "poll" ? existing.bindingWindow : incoming.bindingWindow ?? existing?.bindingWindow,
|
|
20535
20609
|
bindingWindowSource: existing?.bindingWindowSource === "poll" ? "poll" : incoming.bindingWindowSource ?? existing?.bindingWindowSource,
|
|
20536
20610
|
...Object.keys(fieldSources2).length > 0 && { fieldSources: fieldSources2 },
|
|
20537
|
-
source: "headers"
|
|
20611
|
+
source: "headers",
|
|
20612
|
+
...quotaSnapshotOmitsFiveHour(existing) && {
|
|
20613
|
+
checkedAt: existing.checkedAt
|
|
20614
|
+
}
|
|
20538
20615
|
};
|
|
20539
20616
|
}
|
|
20540
20617
|
|
|
@@ -20575,7 +20652,8 @@ function createStickyNoRouteResponse(input) {
|
|
|
20575
20652
|
const retryAfter = killswitchRetryAfterSeconds(firstQuota, remainingQuotas.map((quota2) => ({ quota: quota2 })), input.now ?? Date.now(), scopedBlocked ? modelId : undefined);
|
|
20576
20653
|
const minutes = Math.floor(retryAfter / 60);
|
|
20577
20654
|
const seconds = retryAfter % 60;
|
|
20578
|
-
const
|
|
20655
|
+
const limitNames = input.vaultPolicyLimits?.map((limit) => limit === "model-scoped" ? `${modelName ?? "Model"} scoped limit` : limit === "killswitch" ? "killswitch limit" : "quota floor");
|
|
20656
|
+
const reason = limitNames ? `No vault account currently satisfies quota policy: ${limitNames.join(", ")}. Open /claude → Killswitch / Quota to change these limits.` : scopedBlocked && modelName ? `${modelName} weekly limit reached, no routable OAuth accounts.` : "No OAuth account currently satisfies sticky-balanced quota policy.";
|
|
20579
20657
|
const message = `${reason} Retry in ${minutes}m ${seconds}s.`;
|
|
20580
20658
|
return new Response(JSON.stringify({
|
|
20581
20659
|
type: "error",
|
|
@@ -20686,6 +20764,8 @@ function sessionKey(sessionId) {
|
|
|
20686
20764
|
function stickyQuotaSnapshotIsFresh(quota2, storage, now = Date.now(), modelId) {
|
|
20687
20765
|
const maxAge = getQuotaCheckIntervalMs(storage);
|
|
20688
20766
|
const standardFresh = ["five_hour", "seven_day"].every((key) => {
|
|
20767
|
+
if (key === "five_hour" && quotaSnapshotOmitsFiveHour(quota2))
|
|
20768
|
+
return true;
|
|
20689
20769
|
const window2 = quota2?.[key];
|
|
20690
20770
|
return Boolean(window2 && now - window2.checkedAt < maxAge);
|
|
20691
20771
|
});
|
|
@@ -20740,14 +20820,23 @@ function stickyRouteCandidateWeight(input) {
|
|
|
20740
20820
|
}
|
|
20741
20821
|
const weights = [];
|
|
20742
20822
|
for (const key of ["five_hour", "seven_day"]) {
|
|
20823
|
+
if (key === "five_hour" && quotaSnapshotOmitsFiveHour(quota2))
|
|
20824
|
+
continue;
|
|
20743
20825
|
const window2 = quota2[key];
|
|
20744
20826
|
if (!window2 || !Number.isFinite(window2.remainingPercent))
|
|
20745
20827
|
return 0;
|
|
20746
|
-
|
|
20828
|
+
const threshold = remainingThreshold(storage, candidate.accountId, key);
|
|
20829
|
+
if (window2.remainingPercent <= threshold) {
|
|
20830
|
+
const quotaThreshold = getQuotaMinimumRemainingThresholds(storage)[key];
|
|
20831
|
+
input.onPolicyBlock?.(isKillswitchEnabled(storage) && threshold > quotaThreshold ? "killswitch" : "quota-floor");
|
|
20832
|
+
}
|
|
20833
|
+
weights.push(sustainableWindowWeight(window2.remainingPercent, threshold, window2.resetsAt, now));
|
|
20747
20834
|
}
|
|
20748
20835
|
if (input.family === "fable") {
|
|
20749
20836
|
const window2 = getScopedQuotaWindowForModel(quota2, input.modelId);
|
|
20750
20837
|
if (window2 && Number.isFinite(window2.remainingPercent)) {
|
|
20838
|
+
if (window2.remainingPercent <= remainingThreshold(storage, candidate.accountId, "scoped"))
|
|
20839
|
+
input.onPolicyBlock?.("model-scoped");
|
|
20751
20840
|
weights.push(sustainableWindowWeight(window2.remainingPercent, remainingThreshold(storage, candidate.accountId, "scoped"), window2.resetsAt, now));
|
|
20752
20841
|
}
|
|
20753
20842
|
}
|
|
@@ -26271,18 +26360,18 @@ function classifySafeErrorForLog(error) {
|
|
|
26271
26360
|
} catch {}
|
|
26272
26361
|
return { name, code };
|
|
26273
26362
|
}
|
|
26274
|
-
function logPublicationFailure(
|
|
26363
|
+
function logPublicationFailure(channel3, error) {
|
|
26275
26364
|
let level = "warn";
|
|
26276
26365
|
try {
|
|
26277
26366
|
if (usagePublicationRefusalIsFatal(error))
|
|
26278
26367
|
level = "error";
|
|
26279
26368
|
} catch {}
|
|
26280
|
-
logger[level](
|
|
26369
|
+
logger[level](channel3, "Account usage publication unavailable", classifySafeErrorForLog(error));
|
|
26281
26370
|
}
|
|
26282
|
-
function createNativeUsagePublisher(
|
|
26371
|
+
function createNativeUsagePublisher(channel3) {
|
|
26283
26372
|
const pending = new Set;
|
|
26284
26373
|
const observe2 = (write) => {
|
|
26285
|
-
const work = write.then(() => {}, (error) => logPublicationFailure(
|
|
26374
|
+
const work = write.then(() => {}, (error) => logPublicationFailure(channel3, error));
|
|
26286
26375
|
pending.add(work);
|
|
26287
26376
|
work.then(() => pending.delete(work), () => pending.delete(work));
|
|
26288
26377
|
return work;
|
|
@@ -26565,19 +26654,19 @@ function normalizeInsulaClaudeQuota(entry2, context) {
|
|
|
26565
26654
|
kind: "weekly",
|
|
26566
26655
|
minutes: 10080
|
|
26567
26656
|
});
|
|
26568
|
-
if (!
|
|
26657
|
+
if (!seven_day || usage?.primary != null && !five_hour)
|
|
26569
26658
|
return;
|
|
26570
26659
|
const scoped = scopedReadings(usage?.extraRateWindows, checkedAt);
|
|
26571
26660
|
const extraUsage2 = extraUsageReading(entry2.pools ?? usage?.pools);
|
|
26572
26661
|
return {
|
|
26573
|
-
five_hour,
|
|
26662
|
+
...five_hour && { five_hour },
|
|
26574
26663
|
seven_day,
|
|
26575
26664
|
scoped,
|
|
26576
26665
|
...extraUsage2 && { extraUsage: extraUsage2 },
|
|
26577
26666
|
source: "poll",
|
|
26578
26667
|
checkedAt,
|
|
26579
26668
|
fieldSources: {
|
|
26580
|
-
five_hour: "poll",
|
|
26669
|
+
...five_hour && { five_hour: "poll" },
|
|
26581
26670
|
seven_day: "poll",
|
|
26582
26671
|
scoped: "poll",
|
|
26583
26672
|
...extraUsage2 && { extraUsage: "poll" }
|
|
@@ -26594,7 +26683,7 @@ function newer(reading2, stored) {
|
|
|
26594
26683
|
function mergeInsulaQuota(reading2, stored) {
|
|
26595
26684
|
if (!stored || quotaSnapshotCheckedAt(stored) <= reading2.checkedAt)
|
|
26596
26685
|
return reading2;
|
|
26597
|
-
const five_hour = newer(reading2.five_hour, stored.five_hour);
|
|
26686
|
+
const five_hour = quotaSnapshotOmitsFiveHour(stored) ? undefined : !reading2.five_hour && (stored.five_hour?.checkedAt ?? 0) <= reading2.checkedAt ? undefined : newer(reading2.five_hour, stored.five_hour);
|
|
26598
26687
|
const seven_day = newer(reading2.seven_day, stored.seven_day);
|
|
26599
26688
|
const scoped = new Map(reading2.scoped.map((window2) => [window2.id, window2]));
|
|
26600
26689
|
for (const window2 of stored.scoped ?? []) {
|
|
@@ -26609,10 +26698,10 @@ function mergeInsulaQuota(reading2, stored) {
|
|
|
26609
26698
|
fieldSources2.seven_day = stored.fieldSources.seven_day;
|
|
26610
26699
|
return {
|
|
26611
26700
|
...reading2,
|
|
26612
|
-
|
|
26701
|
+
five_hour,
|
|
26613
26702
|
...seven_day && { seven_day },
|
|
26614
26703
|
scoped: [...scoped.values()],
|
|
26615
|
-
checkedAt: Math.max(reading2.checkedAt, quotaSnapshotCheckedAt(stored)),
|
|
26704
|
+
checkedAt: quotaSnapshotOmitsFiveHour(stored) ? stored.checkedAt : quotaSnapshotOmitsFiveHour(reading2) ? reading2.checkedAt : Math.max(reading2.checkedAt, quotaSnapshotCheckedAt(stored)),
|
|
26616
26705
|
fieldSources: fieldSources2
|
|
26617
26706
|
};
|
|
26618
26707
|
}
|
|
@@ -26898,9 +26987,11 @@ function pauseOnRealTimer(ms) {
|
|
|
26898
26987
|
class QuotaRefreshDeferred extends Error {
|
|
26899
26988
|
}
|
|
26900
26989
|
function mergePollCompletionWithNewerHeaders(current, polled) {
|
|
26990
|
+
if (quotaSnapshotOmitsFiveHour(current) && (polled.checkedAt ?? quotaSnapshotCheckedAt(polled)) < current.checkedAt)
|
|
26991
|
+
return current;
|
|
26901
26992
|
if (current?.source !== "headers")
|
|
26902
26993
|
return polled;
|
|
26903
|
-
const fiveHourIsNewer = Boolean(current.five_hour && current.five_hour.checkedAt > (polled.five_hour?.checkedAt ?? 0));
|
|
26994
|
+
const fiveHourIsNewer = Boolean(current.five_hour && current.five_hour.checkedAt > (polled.five_hour?.checkedAt ?? polled.checkedAt ?? 0));
|
|
26904
26995
|
const sevenDayIsNewer = Boolean(current.seven_day && current.seven_day.checkedAt > (polled.seven_day?.checkedAt ?? 0));
|
|
26905
26996
|
if (!fiveHourIsNewer && !sevenDayIsNewer)
|
|
26906
26997
|
return polled;
|
|
@@ -30239,8 +30330,15 @@ function acquireNativeAccountRuntime(options) {
|
|
|
30239
30330
|
function nativeAccountPolicy(account, snapshot, modelId) {
|
|
30240
30331
|
if (!nativeAccountCanServe(account))
|
|
30241
30332
|
return { status: "blocked", reason: "account-disabled" };
|
|
30242
|
-
|
|
30243
|
-
|
|
30333
|
+
const quotaPolicyLimits = [];
|
|
30334
|
+
if (!quotaSnapshotPassesPolicy(account.quota, snapshot.policyStorage))
|
|
30335
|
+
quotaPolicyLimits.push("quota-floor");
|
|
30336
|
+
if (!quotaSnapshotPassesModelScope(account.quota, modelId))
|
|
30337
|
+
quotaPolicyLimits.push("model-scoped");
|
|
30338
|
+
if (!killswitchPassesPolicy(account.quota, snapshot.policyStorage, account.id === "main" ? undefined : account.id, modelId))
|
|
30339
|
+
quotaPolicyLimits.push("killswitch");
|
|
30340
|
+
if (quotaPolicyLimits.length)
|
|
30341
|
+
return { status: "blocked", reason: "quota-ineligible", quotaPolicyLimits };
|
|
30244
30342
|
return { status: "allowed" };
|
|
30245
30343
|
}
|
|
30246
30344
|
function createNativeAccountRuntime(options) {
|
|
@@ -35238,6 +35336,60 @@ async function disconnectNativeVaultForHost(options) {
|
|
|
35238
35336
|
closeNativeVaultAuthority(paths.tokenPath);
|
|
35239
35337
|
return result;
|
|
35240
35338
|
}
|
|
35339
|
+
// src/native-vault-refusal.ts
|
|
35340
|
+
function createNativeVaultNoRouteResponse(input) {
|
|
35341
|
+
const { snapshot, modelId } = input;
|
|
35342
|
+
const accounts = snapshot.accounts.filter((account) => account.type === "oauth");
|
|
35343
|
+
if (accounts.some((account) => account.enabled && account.state === "needs_reauth"))
|
|
35344
|
+
throw new NativeVaultNoAccountError;
|
|
35345
|
+
const active2 = accounts.filter(nativeAccountCanServe);
|
|
35346
|
+
const limits = new Set;
|
|
35347
|
+
for (const account of active2) {
|
|
35348
|
+
const policy = input.policies?.get(account.id) ?? nativeAccountPolicy(account, snapshot, modelId);
|
|
35349
|
+
for (const limit of policy.quotaPolicyLimits ?? [])
|
|
35350
|
+
limits.add(limit);
|
|
35351
|
+
if (input.sticky) {
|
|
35352
|
+
stickyRouteCandidateWeight({
|
|
35353
|
+
candidate: {
|
|
35354
|
+
accountId: account.id,
|
|
35355
|
+
order: 0,
|
|
35356
|
+
quota: account.quota ?? { kind: "unknown" }
|
|
35357
|
+
},
|
|
35358
|
+
family: stickyRouteFamilyForModel(modelId),
|
|
35359
|
+
modelId,
|
|
35360
|
+
storage: snapshot.policyStorage,
|
|
35361
|
+
onPolicyBlock: (limit) => limits.add(limit)
|
|
35362
|
+
});
|
|
35363
|
+
}
|
|
35364
|
+
}
|
|
35365
|
+
if (!limits.size)
|
|
35366
|
+
throw new NativeVaultNoAccountError;
|
|
35367
|
+
return createStickyNoRouteResponse({
|
|
35368
|
+
routeQuotas: active2.flatMap((account) => account.quota ? [account.quota] : []),
|
|
35369
|
+
modelId,
|
|
35370
|
+
vaultPolicyLimits: [...limits]
|
|
35371
|
+
});
|
|
35372
|
+
}
|
|
35373
|
+
|
|
35374
|
+
class NativeVaultQuotaPolicyError extends Error {
|
|
35375
|
+
response;
|
|
35376
|
+
body;
|
|
35377
|
+
kind = "quota-policy";
|
|
35378
|
+
status = 429;
|
|
35379
|
+
headers;
|
|
35380
|
+
retryAfter;
|
|
35381
|
+
constructor(response, body) {
|
|
35382
|
+
super(body.error.message);
|
|
35383
|
+
this.response = response;
|
|
35384
|
+
this.body = body;
|
|
35385
|
+
this.name = "NativeVaultQuotaPolicyError";
|
|
35386
|
+
this.headers = new Headers(response.headers);
|
|
35387
|
+
this.retryAfter = Number(this.headers.get("retry-after"));
|
|
35388
|
+
}
|
|
35389
|
+
static async fromResponse(response) {
|
|
35390
|
+
return new NativeVaultQuotaPolicyError(response, await response.clone().json());
|
|
35391
|
+
}
|
|
35392
|
+
}
|
|
35241
35393
|
// src/oauth-origin.ts
|
|
35242
35394
|
var ANTHROPIC_OAUTH_ORIGIN = "https://api.anthropic.com";
|
|
35243
35395
|
var ANTHROPIC_OAUTH_ORIGIN_REFUSAL = "Claude OAuth requests can only go to https://api.anthropic.com. A provider baseURL that points elsewhere is not used for Claude sign-in accounts.";
|
|
@@ -35687,6 +35839,12 @@ class PrimeManager {
|
|
|
35687
35839
|
});
|
|
35688
35840
|
return;
|
|
35689
35841
|
}
|
|
35842
|
+
if (quotaSnapshotOmitsFiveHour(evaluation.storedQuota)) {
|
|
35843
|
+
logger.debug("prime", "no five-hour limit", {
|
|
35844
|
+
account: evaluation.label
|
|
35845
|
+
});
|
|
35846
|
+
return;
|
|
35847
|
+
}
|
|
35690
35848
|
const storedResetEpoch = storedResetMs(evaluation.storedFiveHour);
|
|
35691
35849
|
if (storedResetEpoch !== undefined && now < storedResetEpoch + PRIME_DUE_OFFSET_MS) {
|
|
35692
35850
|
logger.trace("prime", "not due", { account: evaluation.label });
|
|
@@ -35744,6 +35902,12 @@ class PrimeManager {
|
|
|
35744
35902
|
});
|
|
35745
35903
|
return;
|
|
35746
35904
|
}
|
|
35905
|
+
if (quotaSnapshotOmitsFiveHour(fresh)) {
|
|
35906
|
+
logger.debug("prime", "no five-hour limit", {
|
|
35907
|
+
account: evaluation.label
|
|
35908
|
+
});
|
|
35909
|
+
return;
|
|
35910
|
+
}
|
|
35747
35911
|
if (!killswitchPassesPolicy(fresh, reloaded, evaluation.id, CLAUDE_HAIKU_4_5_MODEL_ID)) {
|
|
35748
35912
|
logger.debug("prime", "ineligible", {
|
|
35749
35913
|
account: evaluation.label,
|
|
@@ -39153,6 +39317,7 @@ export {
|
|
|
39153
39317
|
quotaTextParts,
|
|
39154
39318
|
quotaSnapshotPassesPolicy,
|
|
39155
39319
|
quotaSnapshotPassesModelScope,
|
|
39320
|
+
quotaSnapshotOmitsFiveHour,
|
|
39156
39321
|
quotaSnapshotModelScopeIsExhausted,
|
|
39157
39322
|
quotaSnapshotHasStandardWindows,
|
|
39158
39323
|
quotaSnapshotCheckedAt,
|
|
@@ -39389,6 +39554,7 @@ export {
|
|
|
39389
39554
|
createProviderErrorRedactor,
|
|
39390
39555
|
createProcessFence,
|
|
39391
39556
|
createNativeVaultRuntime,
|
|
39557
|
+
createNativeVaultNoRouteResponse,
|
|
39392
39558
|
createNativeUsagePublisher,
|
|
39393
39559
|
createNativeUi,
|
|
39394
39560
|
createNativePoolStore,
|
|
@@ -39501,6 +39667,7 @@ export {
|
|
|
39501
39667
|
PARAGRAPH_REMOVAL_ANCHORS,
|
|
39502
39668
|
OPENCODE_IDENTITY_PREFIX,
|
|
39503
39669
|
OAUTH_SCOPES,
|
|
39670
|
+
NativeVaultQuotaPolicyError,
|
|
39504
39671
|
NativeVaultNoAccountError,
|
|
39505
39672
|
NativeTransientAccountError,
|
|
39506
39673
|
NativeRuntimeError,
|
package/dist/insula-quota.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { InsulaQuotaContext, InsulaUsageEntry } from "./internal-types/clau
|
|
|
2
2
|
import { type AccountQuotaWindow, type AccountScopedQuotaWindow, type OAuthQuotaSnapshot } from './accounts.ts';
|
|
3
3
|
/** An insula reading in the shape our usage poll produces, before the account identity is attached. */
|
|
4
4
|
export type InsulaClaudeQuota = OAuthQuotaSnapshot & {
|
|
5
|
-
five_hour: AccountQuotaWindow;
|
|
6
5
|
seven_day: AccountQuotaWindow;
|
|
7
6
|
scoped: AccountScopedQuotaWindow[];
|
|
8
7
|
checkedAt: number;
|
|
@@ -10,8 +9,8 @@ export type InsulaClaudeQuota = OAuthQuotaSnapshot & {
|
|
|
10
9
|
/**
|
|
11
10
|
* Convert insula's Claude entry into our usage-poll snapshot. Every window's
|
|
12
11
|
* checkedAt is when insula fetched the reading, never when we read its cache,
|
|
13
|
-
* so a newer header or poll reading still wins.
|
|
14
|
-
*
|
|
12
|
+
* so a newer header or poll reading still wins. The weekly window is required;
|
|
13
|
+
* an absent primary slot means the five-hour limit does not apply.
|
|
15
14
|
* The tertiary slot (legacy seven_day_opus/sonnet) is not a window we model.
|
|
16
15
|
*/
|
|
17
16
|
export declare function normalizeInsulaClaudeQuota(entry: InsulaUsageEntry, context: InsulaQuotaContext): InsulaClaudeQuota | undefined;
|
|
@@ -127,8 +127,12 @@ export interface NativeAccountRuntime {
|
|
|
127
127
|
}
|
|
128
128
|
/** Host instances own leases, while stores and the vault connection live until the last release. */
|
|
129
129
|
export declare function acquireNativeAccountRuntime(options: NativeAccountRuntimeOptions): NativeAccountRuntime;
|
|
130
|
+
export type NativeQuotaPolicyLimit = 'quota-floor' | 'killswitch' | 'model-scoped';
|
|
131
|
+
export type NativeAccountPolicy = NativeLocalExternalPolicy & {
|
|
132
|
+
quotaPolicyLimits?: readonly NativeQuotaPolicyLimit[];
|
|
133
|
+
};
|
|
130
134
|
/** Evaluate quota and killswitch policy for this caller's model, outside shared credential refresh work. */
|
|
131
|
-
export declare function nativeAccountPolicy(account: NativeAccountView, snapshot: NativeAccountSnapshot, modelId?: string):
|
|
135
|
+
export declare function nativeAccountPolicy(account: NativeAccountView, snapshot: NativeAccountSnapshot, modelId?: string): NativeAccountPolicy;
|
|
132
136
|
/** The local migration journal records completed transfer from host credentials and sidecars to the credential pool. Local use refuses without that record. Host vault enrollment instead selects independent settings and metadata, leaving local credentials, journal and metadata untouched. */
|
|
133
137
|
export declare function createNativeAccountRuntime(options: NativeAccountRuntimeOptions): NativeAccountRuntime;
|
|
134
138
|
export declare function primeDelta(delta: PrimeUsageDelta): {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type NativeAccountPolicy } from './native-account-runtime.ts';
|
|
2
|
+
import { type NativeAccountSnapshot } from './native-account-view.ts';
|
|
3
|
+
/** Preserve authentication precedence, then explain the shared policy's exclusions. */
|
|
4
|
+
export declare function createNativeVaultNoRouteResponse(input: {
|
|
5
|
+
snapshot: NativeAccountSnapshot;
|
|
6
|
+
modelId?: string;
|
|
7
|
+
policies?: ReadonlyMap<string, NativeAccountPolicy>;
|
|
8
|
+
sticky?: boolean;
|
|
9
|
+
}): Response;
|
|
10
|
+
type QuotaPolicyBody = {
|
|
11
|
+
type: 'error';
|
|
12
|
+
error: {
|
|
13
|
+
type: 'rate_limit_error';
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/** OpenCode 2 selection refuses before sending; its chooser cannot return a Response. */
|
|
18
|
+
export declare class NativeVaultQuotaPolicyError extends Error {
|
|
19
|
+
readonly response: Response;
|
|
20
|
+
readonly body: QuotaPolicyBody;
|
|
21
|
+
readonly kind = "quota-policy";
|
|
22
|
+
readonly status = 429;
|
|
23
|
+
readonly headers: Headers;
|
|
24
|
+
readonly retryAfter: number;
|
|
25
|
+
private constructor();
|
|
26
|
+
static fromResponse(response: Response): Promise<NativeVaultQuotaPolicyError>;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
package/dist/sticky-routing.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AccountOperationError, type AccountStorage, type OAuthQuotaSnapshot } from './accounts.ts';
|
|
2
|
+
import type { NativeQuotaPolicyLimit } from './native-account-runtime.ts';
|
|
2
3
|
import { type NativeLockEvent } from './native-lock-observer.ts';
|
|
3
4
|
import type { NativeVaultRosterDocument } from './native-roster-seed.ts';
|
|
4
5
|
import { type NativeVaultRoutingSeed } from './native-vault-seed.ts';
|
|
@@ -60,6 +61,7 @@ export declare function createStickyNoRouteResponse(input: {
|
|
|
60
61
|
mainRefreshError?: AccountOperationError;
|
|
61
62
|
fallbackReauthLabels?: readonly string[];
|
|
62
63
|
routeQuotas: readonly OAuthQuotaSnapshot[];
|
|
64
|
+
vaultPolicyLimits?: readonly NativeQuotaPolicyLimit[];
|
|
63
65
|
modelId?: string;
|
|
64
66
|
now?: number;
|
|
65
67
|
}): Response;
|
|
@@ -86,6 +88,7 @@ export declare function stickyRouteCandidateWeight(input: {
|
|
|
86
88
|
modelId?: string;
|
|
87
89
|
storage: AccountStorage | null;
|
|
88
90
|
now?: number;
|
|
91
|
+
onPolicyBlock?: (limit: NativeQuotaPolicyLimit) => void;
|
|
89
92
|
}): number;
|
|
90
93
|
export declare function stickyRouteKnownFableExhausted(candidate: StickyRouteCandidate, storage: AccountStorage | null, now?: number): boolean;
|
|
91
94
|
export declare function decideStickyQuotaFailure(input: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/anthropic-auth-core",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
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.
|
|
33
|
+
"@cortexkit/common-auth": "0.32.1"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@cortexkit/claustrum-client": "0.13.0",
|