@cortexkit/anthropic-auth-core 1.21.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/accounts.d.ts +43 -3
- package/dist/accounts.js +469 -90
- package/dist/auth.js +3 -10
- package/dist/cch.d.ts +13 -9
- package/dist/cch.js +57 -19
- package/dist/claude-code.d.ts +1 -2
- package/dist/claude-code.js +6 -4
- package/dist/claustrum.d.ts +109 -0
- package/dist/claustrum.js +529 -0
- package/dist/commands/account.d.ts +5 -1
- package/dist/commands/account.js +16 -2
- package/dist/constants.d.ts +4 -5
- package/dist/constants.js +4 -5
- package/dist/dump.js +2 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/json.d.ts +1 -0
- package/dist/json.js +10 -0
- package/dist/mid-conversation-output-config.d.ts +13 -0
- package/dist/mid-conversation-output-config.js +89 -0
- package/dist/models.d.ts +27 -1
- package/dist/models.js +43 -0
- package/dist/network-errors.d.ts +1 -0
- package/dist/network-errors.js +27 -0
- package/dist/quota-header-feed.d.ts +19 -7
- package/dist/quota-header-feed.js +33 -2
- package/dist/quota-headers.js +59 -6
- package/dist/quota-manager.d.ts +5 -1
- package/dist/quota-manager.js +31 -5
- package/dist/thinking-binding.d.ts +7 -0
- package/dist/thinking-binding.js +54 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -5,13 +5,16 @@ export * from './cachekeep.js';
|
|
|
5
5
|
export * from './cachekeep-registry.js';
|
|
6
6
|
export * from './cch.js';
|
|
7
7
|
export * from './claude-code.js';
|
|
8
|
+
export * from './claustrum.js';
|
|
8
9
|
export * from './commands/account.js';
|
|
9
10
|
export * from './constants.js';
|
|
10
11
|
export * from './dump.js';
|
|
11
12
|
export * from './fast.js';
|
|
13
|
+
export * from './json.js';
|
|
12
14
|
export * from './killswitch.js';
|
|
13
15
|
export * from './logger.js';
|
|
14
16
|
export * from './logging.js';
|
|
17
|
+
export * from './mid-conversation-output-config.js';
|
|
15
18
|
export * from './models.js';
|
|
16
19
|
export * from './oauth-profile.js';
|
|
17
20
|
export * from './pkce.js';
|
|
@@ -25,3 +28,4 @@ export * from './relay.js';
|
|
|
25
28
|
export * from './routing.js';
|
|
26
29
|
export * from './start.js';
|
|
27
30
|
export * from './sticky-routing.js';
|
|
31
|
+
export * from './thinking-binding.js';
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseJsonRedacted(text: string): unknown;
|
package/dist/json.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const MID_CONVERSATION_OUTPUT_CONFIG_BETA = "mid-conversation-output-config-2026-07-01";
|
|
2
|
+
export type AdaptiveEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
3
|
+
export type MidConversationEffortTransition = {
|
|
4
|
+
/** Number of assistant messages preceding the user boundary for this effort. */
|
|
5
|
+
afterAssistantMessages: number;
|
|
6
|
+
effort: AdaptiveEffort;
|
|
7
|
+
};
|
|
8
|
+
export declare function normalizeAdaptiveEffort(value: unknown): AdaptiveEffort | undefined;
|
|
9
|
+
export declare function usesMidConversationOutputConfig(body: Record<string, unknown>): boolean;
|
|
10
|
+
export declare function applyMidConversationOutputConfig(body: Record<string, unknown>, transitions: readonly MidConversationEffortTransition[]): {
|
|
11
|
+
applied: boolean;
|
|
12
|
+
inserted: number;
|
|
13
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { isClaudeFable51Model } from './models.js';
|
|
2
|
+
export const MID_CONVERSATION_OUTPUT_CONFIG_BETA = 'mid-conversation-output-config-2026-07-01';
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
export function normalizeAdaptiveEffort(value) {
|
|
7
|
+
if (value === 'minimal')
|
|
8
|
+
return 'low';
|
|
9
|
+
if (value === 'low' ||
|
|
10
|
+
value === 'medium' ||
|
|
11
|
+
value === 'high' ||
|
|
12
|
+
value === 'xhigh' ||
|
|
13
|
+
value === 'max') {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
export function usesMidConversationOutputConfig(body) {
|
|
19
|
+
return isClaudeFable51Model(body.model);
|
|
20
|
+
}
|
|
21
|
+
function compactTransitions(transitions) {
|
|
22
|
+
const compacted = [];
|
|
23
|
+
for (const transition of transitions) {
|
|
24
|
+
if (!Number.isSafeInteger(transition.afterAssistantMessages) ||
|
|
25
|
+
transition.afterAssistantMessages < 0 ||
|
|
26
|
+
!normalizeAdaptiveEffort(transition.effort)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const normalized = {
|
|
30
|
+
afterAssistantMessages: transition.afterAssistantMessages,
|
|
31
|
+
effort: transition.effort,
|
|
32
|
+
};
|
|
33
|
+
const previous = compacted.at(-1);
|
|
34
|
+
if (previous &&
|
|
35
|
+
previous.afterAssistantMessages === normalized.afterAssistantMessages) {
|
|
36
|
+
previous.effort = normalized.effort;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (previous?.effort === normalized.effort)
|
|
40
|
+
continue;
|
|
41
|
+
compacted.push(normalized);
|
|
42
|
+
}
|
|
43
|
+
return compacted;
|
|
44
|
+
}
|
|
45
|
+
export function applyMidConversationOutputConfig(body, transitions) {
|
|
46
|
+
if (!usesMidConversationOutputConfig(body) || !Array.isArray(body.messages)) {
|
|
47
|
+
return { applied: false, inserted: 0 };
|
|
48
|
+
}
|
|
49
|
+
const compacted = compactTransitions(transitions);
|
|
50
|
+
const currentOutputConfig = isRecord(body.output_config)
|
|
51
|
+
? body.output_config
|
|
52
|
+
: {};
|
|
53
|
+
const baseline = compacted[0]?.effort ??
|
|
54
|
+
normalizeAdaptiveEffort(currentOutputConfig.effort) ??
|
|
55
|
+
'high';
|
|
56
|
+
body.output_config = { ...currentOutputConfig, effort: baseline };
|
|
57
|
+
if (compacted.length < 2)
|
|
58
|
+
return { applied: true, inserted: 0 };
|
|
59
|
+
const messages = body.messages;
|
|
60
|
+
const transitionsByBoundary = new Map();
|
|
61
|
+
for (const transition of compacted.slice(1)) {
|
|
62
|
+
transitionsByBoundary.set(transition.afterAssistantMessages, transition.effort);
|
|
63
|
+
}
|
|
64
|
+
let assistantMessages = 0;
|
|
65
|
+
let activeEffort = baseline;
|
|
66
|
+
let inserted = 0;
|
|
67
|
+
const rewritten = [];
|
|
68
|
+
for (const message of messages) {
|
|
69
|
+
if (isRecord(message) &&
|
|
70
|
+
message.role === 'user' &&
|
|
71
|
+
transitionsByBoundary.has(assistantMessages)) {
|
|
72
|
+
const effort = transitionsByBoundary.get(assistantMessages);
|
|
73
|
+
if (effort && effort !== activeEffort) {
|
|
74
|
+
rewritten.push({
|
|
75
|
+
role: 'system',
|
|
76
|
+
content: [],
|
|
77
|
+
output_config: { effort },
|
|
78
|
+
});
|
|
79
|
+
activeEffort = effort;
|
|
80
|
+
inserted++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
rewritten.push(message);
|
|
84
|
+
if (isRecord(message) && message.role === 'assistant')
|
|
85
|
+
assistantMessages++;
|
|
86
|
+
}
|
|
87
|
+
body.messages = rewritten;
|
|
88
|
+
return { applied: true, inserted };
|
|
89
|
+
}
|
package/dist/models.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const CLAUDE_FABLE_5_MODEL_ID = "claude-fable-5";
|
|
2
2
|
export declare const CLAUDE_MYTHOS_5_MODEL_ID = "claude-mythos-5";
|
|
3
|
+
export declare const CLAUDE_FABLE_5_1_MODEL_ID = "claude-fable-5-1";
|
|
4
|
+
export declare const CLAUDE_MYTHOS_5_1_MODEL_ID = "claude-mythos-5-1";
|
|
3
5
|
/**
|
|
4
6
|
* Haiku 4.5 model identifier used by `/claude-prime` to start each OAuth
|
|
5
7
|
* account's five-hour quota window with a minimal request. Usage is measured
|
|
@@ -16,7 +18,8 @@ export declare const CLAUDE_HAIKU_4_5_PRICING: {
|
|
|
16
18
|
readonly input: 1;
|
|
17
19
|
readonly output: 5;
|
|
18
20
|
};
|
|
19
|
-
export declare const
|
|
21
|
+
export declare const CLAUDE_FABLE_MYTHOS_5_1_MODEL_IDS: readonly ["claude-fable-5-1", "claude-mythos-5-1"];
|
|
22
|
+
export declare const CLAUDE_FABLE_MYTHOS_5_MODEL_IDS: readonly ["claude-fable-5", "claude-mythos-5", "claude-fable-5-1", "claude-mythos-5-1"];
|
|
20
23
|
export type ClaudeFableMythos5ModelId = (typeof CLAUDE_FABLE_MYTHOS_5_MODEL_IDS)[number];
|
|
21
24
|
export declare const CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING: {
|
|
22
25
|
readonly type: 'adaptive';
|
|
@@ -29,15 +32,38 @@ export declare const CLAUDE_FABLE_MYTHOS_5_PRICING: {
|
|
|
29
32
|
readonly cacheWrite5m: 12.5;
|
|
30
33
|
readonly cacheWrite1h: 20;
|
|
31
34
|
};
|
|
35
|
+
export declare const CLAUDE_FABLE_MYTHOS_5_1_PRICING: {
|
|
36
|
+
readonly input: 10;
|
|
37
|
+
readonly output: 50;
|
|
38
|
+
readonly cacheRead: 0.25;
|
|
39
|
+
readonly cacheWrite5m: 12.5;
|
|
40
|
+
readonly cacheWrite1h: 20;
|
|
41
|
+
};
|
|
32
42
|
export declare const CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW = 1000000;
|
|
33
43
|
export declare const CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS = 128000;
|
|
34
44
|
export declare const CLAUDE_FABLE_MYTHOS_5_RELEASE_DATE = "2026-06-09";
|
|
45
|
+
export declare const CLAUDE_FABLE_MYTHOS_5_1_RELEASE_DATE = "2026-09-01";
|
|
35
46
|
export declare const CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS: Record<ClaudeFableMythos5ModelId, {
|
|
36
47
|
id: ClaudeFableMythos5ModelId;
|
|
37
48
|
name: string;
|
|
38
49
|
limited?: boolean;
|
|
39
50
|
}>;
|
|
51
|
+
export declare const CLAUDE_FABLE_MYTHOS_5_1_MODEL_SPECS: {
|
|
52
|
+
"claude-fable-5-1": {
|
|
53
|
+
id: ClaudeFableMythos5ModelId;
|
|
54
|
+
name: string;
|
|
55
|
+
limited?: boolean;
|
|
56
|
+
};
|
|
57
|
+
"claude-mythos-5-1": {
|
|
58
|
+
id: ClaudeFableMythos5ModelId;
|
|
59
|
+
name: string;
|
|
60
|
+
limited?: boolean;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
40
63
|
export declare function isClaudeFableOrMythos5Model(model: unknown): boolean;
|
|
64
|
+
export declare function isClaudeFableOrMythos51Model(model: unknown): boolean;
|
|
65
|
+
export declare function isClaudeFable51Model(model: unknown): boolean;
|
|
66
|
+
export declare function getClaudeFableMythos5ReleaseDate(model: unknown): "2026-06-09" | "2026-09-01";
|
|
41
67
|
export declare const CLAUDE_SONNET_5_MODEL_ID = "claude-sonnet-5";
|
|
42
68
|
/**
|
|
43
69
|
* Sonnet 5 enables adaptive thinking by default but ships `display: "omitted"`,
|
package/dist/models.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const CLAUDE_FABLE_5_MODEL_ID = 'claude-fable-5';
|
|
2
2
|
export const CLAUDE_MYTHOS_5_MODEL_ID = 'claude-mythos-5';
|
|
3
|
+
export const CLAUDE_FABLE_5_1_MODEL_ID = 'claude-fable-5-1';
|
|
4
|
+
export const CLAUDE_MYTHOS_5_1_MODEL_ID = 'claude-mythos-5-1';
|
|
3
5
|
/**
|
|
4
6
|
* Haiku 4.5 model identifier used by `/claude-prime` to start each OAuth
|
|
5
7
|
* account's five-hour quota window with a minimal request. Usage is measured
|
|
@@ -16,9 +18,14 @@ export const CLAUDE_HAIKU_4_5_PRICING = {
|
|
|
16
18
|
input: 1,
|
|
17
19
|
output: 5,
|
|
18
20
|
};
|
|
21
|
+
export const CLAUDE_FABLE_MYTHOS_5_1_MODEL_IDS = [
|
|
22
|
+
CLAUDE_FABLE_5_1_MODEL_ID,
|
|
23
|
+
CLAUDE_MYTHOS_5_1_MODEL_ID,
|
|
24
|
+
];
|
|
19
25
|
export const CLAUDE_FABLE_MYTHOS_5_MODEL_IDS = [
|
|
20
26
|
CLAUDE_FABLE_5_MODEL_ID,
|
|
21
27
|
CLAUDE_MYTHOS_5_MODEL_ID,
|
|
28
|
+
...CLAUDE_FABLE_MYTHOS_5_1_MODEL_IDS,
|
|
22
29
|
];
|
|
23
30
|
export const CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING = {
|
|
24
31
|
type: 'adaptive',
|
|
@@ -31,9 +38,17 @@ export const CLAUDE_FABLE_MYTHOS_5_PRICING = {
|
|
|
31
38
|
cacheWrite5m: 12.5,
|
|
32
39
|
cacheWrite1h: 20,
|
|
33
40
|
};
|
|
41
|
+
export const CLAUDE_FABLE_MYTHOS_5_1_PRICING = {
|
|
42
|
+
input: 10,
|
|
43
|
+
output: 50,
|
|
44
|
+
cacheRead: 0.25,
|
|
45
|
+
cacheWrite5m: 12.5,
|
|
46
|
+
cacheWrite1h: 20,
|
|
47
|
+
};
|
|
34
48
|
export const CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW = 1_000_000;
|
|
35
49
|
export const CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS = 128_000;
|
|
36
50
|
export const CLAUDE_FABLE_MYTHOS_5_RELEASE_DATE = '2026-06-09';
|
|
51
|
+
export const CLAUDE_FABLE_MYTHOS_5_1_RELEASE_DATE = '2026-09-01';
|
|
37
52
|
export const CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS = {
|
|
38
53
|
[CLAUDE_FABLE_5_MODEL_ID]: {
|
|
39
54
|
id: CLAUDE_FABLE_5_MODEL_ID,
|
|
@@ -44,11 +59,39 @@ export const CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS = {
|
|
|
44
59
|
name: 'Claude Mythos 5',
|
|
45
60
|
limited: true,
|
|
46
61
|
},
|
|
62
|
+
[CLAUDE_FABLE_5_1_MODEL_ID]: {
|
|
63
|
+
id: CLAUDE_FABLE_5_1_MODEL_ID,
|
|
64
|
+
name: 'Claude Fable 5.1',
|
|
65
|
+
},
|
|
66
|
+
[CLAUDE_MYTHOS_5_1_MODEL_ID]: {
|
|
67
|
+
id: CLAUDE_MYTHOS_5_1_MODEL_ID,
|
|
68
|
+
name: 'Claude Mythos 5.1',
|
|
69
|
+
limited: true,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
export const CLAUDE_FABLE_MYTHOS_5_1_MODEL_SPECS = {
|
|
73
|
+
[CLAUDE_FABLE_5_1_MODEL_ID]: CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS[CLAUDE_FABLE_5_1_MODEL_ID],
|
|
74
|
+
[CLAUDE_MYTHOS_5_1_MODEL_ID]: CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS[CLAUDE_MYTHOS_5_1_MODEL_ID],
|
|
47
75
|
};
|
|
48
76
|
export function isClaudeFableOrMythos5Model(model) {
|
|
49
77
|
return (typeof model === 'string' &&
|
|
50
78
|
CLAUDE_FABLE_MYTHOS_5_MODEL_IDS.some((id) => model === id || model.startsWith(`${id}-`)));
|
|
51
79
|
}
|
|
80
|
+
export function isClaudeFableOrMythos51Model(model) {
|
|
81
|
+
return (typeof model === 'string' &&
|
|
82
|
+
CLAUDE_FABLE_MYTHOS_5_1_MODEL_IDS.some((id) => model === id || model.startsWith(`${id}-`)));
|
|
83
|
+
}
|
|
84
|
+
export function isClaudeFable51Model(model) {
|
|
85
|
+
if (typeof model !== 'string')
|
|
86
|
+
return false;
|
|
87
|
+
return (model === CLAUDE_FABLE_5_1_MODEL_ID ||
|
|
88
|
+
model.startsWith(`${CLAUDE_FABLE_5_1_MODEL_ID}-`));
|
|
89
|
+
}
|
|
90
|
+
export function getClaudeFableMythos5ReleaseDate(model) {
|
|
91
|
+
return isClaudeFableOrMythos51Model(model)
|
|
92
|
+
? CLAUDE_FABLE_MYTHOS_5_1_RELEASE_DATE
|
|
93
|
+
: CLAUDE_FABLE_MYTHOS_5_RELEASE_DATE;
|
|
94
|
+
}
|
|
52
95
|
export const CLAUDE_SONNET_5_MODEL_ID = 'claude-sonnet-5';
|
|
53
96
|
/**
|
|
54
97
|
* Sonnet 5 enables adaptive thinking by default but ships `display: "omitted"`,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isTransientNetworkError(error: unknown): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const TRANSIENT_NETWORK_ERROR_CODES = new Set([
|
|
2
|
+
'EAI_AGAIN',
|
|
3
|
+
'ECONNREFUSED',
|
|
4
|
+
'ECONNRESET',
|
|
5
|
+
'EHOSTUNREACH',
|
|
6
|
+
'ENETUNREACH',
|
|
7
|
+
'ENOTFOUND',
|
|
8
|
+
'ETIMEDOUT',
|
|
9
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
10
|
+
]);
|
|
11
|
+
function errorField(error, field) {
|
|
12
|
+
if (!error || typeof error !== 'object')
|
|
13
|
+
return undefined;
|
|
14
|
+
const value = error[field];
|
|
15
|
+
return typeof value === 'string' ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
export function isTransientNetworkError(error) {
|
|
18
|
+
const code = errorField(error, 'code');
|
|
19
|
+
if (code && TRANSIENT_NETWORK_ERROR_CODES.has(code))
|
|
20
|
+
return true;
|
|
21
|
+
const message = error instanceof Error
|
|
22
|
+
? error.message
|
|
23
|
+
: (errorField(error, 'message') ?? String(error));
|
|
24
|
+
if (message.includes('fetch failed'))
|
|
25
|
+
return true;
|
|
26
|
+
return [...TRANSIENT_NETWORK_ERROR_CODES].some((candidate) => message.includes(candidate));
|
|
27
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AccountStorage, type OAuthQuotaSnapshot } from './accounts.ts';
|
|
2
|
-
export declare const QUOTA_HEADER_FEED_SCHEMA_VERSION =
|
|
1
|
+
import { type AccountStorage, type OAuthQuotaSnapshot, type QuotaFieldName, type QuotaFieldSource, type QuotaFieldSources } from './accounts.ts';
|
|
2
|
+
export declare const QUOTA_HEADER_FEED_SCHEMA_VERSION = 3;
|
|
3
3
|
export declare const QUOTA_HEADER_FEED_LEASE_MS = 180000;
|
|
4
4
|
export type QuotaHeaderFeedIdentity = {
|
|
5
5
|
identity_source: 'credential_id';
|
|
@@ -10,12 +10,25 @@ export type QuotaHeaderFeedIdentity = {
|
|
|
10
10
|
} | {
|
|
11
11
|
identity_source: 'none';
|
|
12
12
|
};
|
|
13
|
-
export type
|
|
13
|
+
export type QuotaHeaderFeedProvenance = Partial<Record<QuotaFieldName, QuotaFieldSource>>;
|
|
14
|
+
type QuotaHeaderFeedQuota = Pick<OAuthQuotaSnapshot, 'five_hour' | 'seven_day' | 'bindingWindow' | 'fallbackAdvised' | 'scoped' | 'extraUsage'> & {
|
|
15
|
+
provenance?: QuotaHeaderFeedProvenance;
|
|
16
|
+
};
|
|
17
|
+
type QuotaHeaderFeedMetadata = {
|
|
14
18
|
schema_version: typeof QUOTA_HEADER_FEED_SCHEMA_VERSION;
|
|
15
19
|
provider: 'anthropic';
|
|
16
20
|
configured_account_count: number;
|
|
21
|
+
/** Header observation time; merged poll-owned fields retain their own checkedAt. */
|
|
17
22
|
observed_at_ms: number;
|
|
18
|
-
|
|
23
|
+
};
|
|
24
|
+
export type QuotaHeaderFeedEntry = QuotaHeaderFeedIdentity & QuotaHeaderFeedMetadata & {
|
|
25
|
+
quota: QuotaHeaderFeedQuota;
|
|
26
|
+
};
|
|
27
|
+
export type QuotaHeaderFeedPublishEntry = QuotaHeaderFeedIdentity & QuotaHeaderFeedMetadata & {
|
|
28
|
+
quota: Omit<QuotaHeaderFeedQuota, 'provenance'> & {
|
|
29
|
+
fieldSources?: QuotaFieldSources;
|
|
30
|
+
};
|
|
31
|
+
accountKey: string;
|
|
19
32
|
};
|
|
20
33
|
export declare function getDefaultQuotaHeaderFeedDirectory(): string;
|
|
21
34
|
export declare function configuredAnthropicOAuthAccountCount(input: {
|
|
@@ -32,9 +45,8 @@ export declare class QuotaHeaderFeedRegistry {
|
|
|
32
45
|
leaseMs?: number;
|
|
33
46
|
instanceId?: string;
|
|
34
47
|
});
|
|
35
|
-
publish(entry:
|
|
36
|
-
accountKey: string;
|
|
37
|
-
}): Promise<void>;
|
|
48
|
+
publish(entry: QuotaHeaderFeedPublishEntry): Promise<void>;
|
|
38
49
|
list(): Promise<QuotaHeaderFeedEntry[]>;
|
|
39
50
|
dispose(): Promise<void>;
|
|
40
51
|
}
|
|
52
|
+
export {};
|
|
@@ -2,8 +2,8 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { chmod, mkdir, readdir, readFile, rename, rm, writeFile, } from 'node:fs/promises';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { isOAuthAccount, } from './accounts.js';
|
|
6
|
-
export const QUOTA_HEADER_FEED_SCHEMA_VERSION =
|
|
5
|
+
import { isOAuthAccount, QUOTA_FIELD_NAMES, } from './accounts.js';
|
|
6
|
+
export const QUOTA_HEADER_FEED_SCHEMA_VERSION = 3;
|
|
7
7
|
export const QUOTA_HEADER_FEED_LEASE_MS = 180_000;
|
|
8
8
|
export function getDefaultQuotaHeaderFeedDirectory() {
|
|
9
9
|
return (process.env.OPENCODE_ANTHROPIC_AUTH_QUOTA_FEED_DIR ??
|
|
@@ -129,6 +129,20 @@ function projectExtraUsage(value) {
|
|
|
129
129
|
exhausted: candidate.exhausted,
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
+
function projectQuotaProvenance(value, presentFields) {
|
|
133
|
+
if (!value || typeof value !== 'object')
|
|
134
|
+
return undefined;
|
|
135
|
+
const candidate = value;
|
|
136
|
+
const provenance = {};
|
|
137
|
+
for (const field of QUOTA_FIELD_NAMES) {
|
|
138
|
+
if (!presentFields.has(field))
|
|
139
|
+
continue;
|
|
140
|
+
const source = candidate[field];
|
|
141
|
+
if (source === 'poll' || source === 'headers')
|
|
142
|
+
provenance[field] = source;
|
|
143
|
+
}
|
|
144
|
+
return Object.keys(provenance).length > 0 ? provenance : undefined;
|
|
145
|
+
}
|
|
132
146
|
function projectQuota(quota) {
|
|
133
147
|
const fiveHour = projectQuotaWindow(quota.five_hour);
|
|
134
148
|
const sevenDay = projectQuotaWindow(quota.seven_day);
|
|
@@ -138,6 +152,22 @@ function projectQuota(quota) {
|
|
|
138
152
|
.filter((entry) => entry != null)
|
|
139
153
|
: undefined;
|
|
140
154
|
const extraUsage = projectExtraUsage(quota.extraUsage);
|
|
155
|
+
const presentFields = new Set();
|
|
156
|
+
if (fiveHour)
|
|
157
|
+
presentFields.add('five_hour');
|
|
158
|
+
if (sevenDay)
|
|
159
|
+
presentFields.add('seven_day');
|
|
160
|
+
if (typeof quota.bindingWindow === 'string') {
|
|
161
|
+
presentFields.add('bindingWindow');
|
|
162
|
+
}
|
|
163
|
+
if (typeof quota.fallbackAdvised === 'boolean') {
|
|
164
|
+
presentFields.add('fallbackAdvised');
|
|
165
|
+
}
|
|
166
|
+
if (scoped)
|
|
167
|
+
presentFields.add('scoped');
|
|
168
|
+
if (extraUsage)
|
|
169
|
+
presentFields.add('extraUsage');
|
|
170
|
+
const provenance = projectQuotaProvenance(quota.fieldSources, presentFields);
|
|
141
171
|
return {
|
|
142
172
|
...(fiveHour && { five_hour: fiveHour }),
|
|
143
173
|
...(sevenDay && { seven_day: sevenDay }),
|
|
@@ -149,6 +179,7 @@ function projectQuota(quota) {
|
|
|
149
179
|
}),
|
|
150
180
|
...(scoped && { scoped }),
|
|
151
181
|
...(extraUsage && { extraUsage }),
|
|
182
|
+
...(provenance && { provenance }),
|
|
152
183
|
};
|
|
153
184
|
}
|
|
154
185
|
export class QuotaHeaderFeedRegistry {
|
package/dist/quota-headers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { QUOTA_FIELD_NAMES, quotaFieldSource } from './accounts.js';
|
|
1
2
|
const PREFIX = 'anthropic-ratelimit-unified-';
|
|
2
3
|
const WINDOW_KEYS = {
|
|
3
4
|
'5h': 'five_hour',
|
|
@@ -31,38 +32,90 @@ function normalizeWindow(headers, suffix, checkedAt) {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
export function normalizeQuotaHeaders(headers, now = Date.now()) {
|
|
35
|
+
const fieldSources = {};
|
|
36
|
+
const fallbackHeader = headers.get(`${PREFIX}fallback`);
|
|
34
37
|
const snapshot = {
|
|
35
|
-
fallbackAdvised:
|
|
38
|
+
fallbackAdvised: fallbackHeader === 'available',
|
|
36
39
|
source: 'headers',
|
|
37
40
|
checkedAt: now,
|
|
38
41
|
};
|
|
39
42
|
for (const [suffix, key] of Object.entries(WINDOW_KEYS)) {
|
|
40
43
|
const window = normalizeWindow(headers, suffix, now);
|
|
41
|
-
if (window)
|
|
44
|
+
if (window) {
|
|
42
45
|
snapshot[key] = window;
|
|
46
|
+
fieldSources[key] = 'headers';
|
|
47
|
+
}
|
|
43
48
|
}
|
|
44
49
|
const representativeClaim = headers.get(`${PREFIX}representative-claim`);
|
|
45
50
|
if (representativeClaim) {
|
|
46
51
|
snapshot.bindingWindow = representativeClaim;
|
|
47
52
|
snapshot.bindingWindowSource = 'headers';
|
|
53
|
+
fieldSources.bindingWindow = 'headers';
|
|
48
54
|
}
|
|
49
|
-
|
|
55
|
+
if (fallbackHeader !== null)
|
|
56
|
+
fieldSources.fallbackAdvised = 'headers';
|
|
57
|
+
return {
|
|
58
|
+
...snapshot,
|
|
59
|
+
fieldSources,
|
|
60
|
+
};
|
|
50
61
|
}
|
|
51
62
|
export function mergeHeaderQuotaSnapshot(existing, incoming) {
|
|
52
|
-
const { scoped: existingScoped, extraUsage: existingExtraUsage, ...existingWithoutPollOwnedFields } = existing ?? {};
|
|
53
|
-
const { scoped: _incomingScoped, extraUsage: _incomingExtraUsage, ...incomingWithoutPollOwnedFields } = incoming;
|
|
63
|
+
const { scoped: existingScoped, extraUsage: existingExtraUsage, fieldSources: existingFieldSources, ...existingWithoutPollOwnedFields } = existing ?? {};
|
|
64
|
+
const { scoped: _incomingScoped, extraUsage: _incomingExtraUsage, fieldSources: incomingFieldSources, ...incomingWithoutPollOwnedFields } = incoming;
|
|
65
|
+
const fieldSources = {};
|
|
66
|
+
for (const field of QUOTA_FIELD_NAMES) {
|
|
67
|
+
const value = field === 'scoped'
|
|
68
|
+
? existingScoped
|
|
69
|
+
: field === 'extraUsage'
|
|
70
|
+
? existingExtraUsage
|
|
71
|
+
: field === 'fallbackAdvised'
|
|
72
|
+
? incomingFieldSources?.fallbackAdvised !== undefined
|
|
73
|
+
? incoming.fallbackAdvised
|
|
74
|
+
: (existing?.fallbackAdvised ?? incoming.fallbackAdvised)
|
|
75
|
+
: field === 'bindingWindow'
|
|
76
|
+
? existing?.bindingWindowSource === 'poll'
|
|
77
|
+
? existing?.bindingWindow
|
|
78
|
+
: (incoming.bindingWindow ?? existing?.bindingWindow)
|
|
79
|
+
: (incoming[field] ?? existing?.[field]);
|
|
80
|
+
if (value === undefined)
|
|
81
|
+
continue;
|
|
82
|
+
const source = field === 'fallbackAdvised'
|
|
83
|
+
? (incomingFieldSources?.fallbackAdvised ??
|
|
84
|
+
(existing?.fallbackAdvised !== undefined
|
|
85
|
+
? existingFieldSources
|
|
86
|
+
? existingFieldSources.fallbackAdvised
|
|
87
|
+
: quotaFieldSource(existing, field)
|
|
88
|
+
: undefined))
|
|
89
|
+
: (field === 'scoped' || field === 'extraUsage') && value !== undefined
|
|
90
|
+
? 'poll'
|
|
91
|
+
: field === 'bindingWindow' &&
|
|
92
|
+
existing?.bindingWindowSource === 'poll' &&
|
|
93
|
+
existing.bindingWindow !== undefined
|
|
94
|
+
? 'poll'
|
|
95
|
+
: incoming[field] !== undefined
|
|
96
|
+
? (incomingFieldSources?.[field] ??
|
|
97
|
+
quotaFieldSource(incoming, field))
|
|
98
|
+
: (existingFieldSources?.[field] ??
|
|
99
|
+
quotaFieldSource(existing, field));
|
|
100
|
+
if (source)
|
|
101
|
+
fieldSources[field] = source;
|
|
102
|
+
}
|
|
54
103
|
return {
|
|
55
104
|
...existingWithoutPollOwnedFields,
|
|
56
105
|
...incomingWithoutPollOwnedFields,
|
|
57
106
|
...(Array.isArray(existingScoped) && { scoped: existingScoped }),
|
|
58
107
|
...(existingExtraUsage != null && { extraUsage: existingExtraUsage }),
|
|
108
|
+
fallbackAdvised: incomingFieldSources?.fallbackAdvised !== undefined
|
|
109
|
+
? incoming.fallbackAdvised
|
|
110
|
+
: (existing?.fallbackAdvised ?? incoming.fallbackAdvised),
|
|
59
111
|
bindingWindow: existing?.bindingWindowSource === 'poll'
|
|
60
112
|
? existing.bindingWindow
|
|
61
113
|
: (incoming.bindingWindow ?? existing?.bindingWindow),
|
|
62
114
|
bindingWindowSource: existing?.bindingWindowSource === 'poll'
|
|
63
115
|
? 'poll'
|
|
64
116
|
: (incoming.bindingWindowSource ?? existing?.bindingWindowSource),
|
|
65
|
-
|
|
117
|
+
...(Object.keys(fieldSources).length > 0 && { fieldSources }),
|
|
118
|
+
// Top-level source remains headers for compatibility; fieldSources records ownership after partial harvests.
|
|
66
119
|
source: 'headers',
|
|
67
120
|
};
|
|
68
121
|
}
|
package/dist/quota-manager.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type QuotaManagerOptions = {
|
|
|
23
23
|
now?: () => number;
|
|
24
24
|
onMainQuotaFetched?: (quota: OAuthQuotaSnapshot, checkedAt: number, tokenFingerprint: string, fetchStartedAt: number, quotaErrorGeneration: number, quotaErrorClearedAt: number | undefined) => void;
|
|
25
25
|
onApiError?: (error: AccountOperationError, quotaErrorGeneration: number) => void;
|
|
26
|
+
onFallbackQuotaFetched?: (accountId: string, quota: OAuthQuotaSnapshot, checkedAt: number, fetchStartedAt: number) => void;
|
|
27
|
+
onFallbackApiError?: (accountId: string, error: AccountOperationError) => void;
|
|
26
28
|
};
|
|
27
29
|
export declare class QuotaManager {
|
|
28
30
|
private main;
|
|
@@ -47,6 +49,8 @@ export declare class QuotaManager {
|
|
|
47
49
|
private readonly now;
|
|
48
50
|
private readonly onMainQuotaFetched;
|
|
49
51
|
private readonly onApiError;
|
|
52
|
+
private readonly onFallbackQuotaFetched;
|
|
53
|
+
private readonly onFallbackApiError;
|
|
50
54
|
constructor(opts: QuotaManagerOptions);
|
|
51
55
|
/**
|
|
52
56
|
* Cached main quota entry, scoped to the account behind the main credential.
|
|
@@ -69,7 +73,7 @@ export declare class QuotaManager {
|
|
|
69
73
|
refreshMainWithMetadata(mainAccountIdOrAccessToken: string | undefined, accessToken?: string, expectedIdentityGeneration?: number): Promise<QuotaRefreshResult>;
|
|
70
74
|
refreshFallback(accountId: string, accessToken: string, account: Pick<OAuthAccount, 'authLineageId'> | undefined): Promise<OAuthQuotaSnapshot>;
|
|
71
75
|
refreshFallbackWithMetadata(accountId: string, accessToken: string, account: Pick<OAuthAccount, 'authLineageId'> | undefined): Promise<QuotaRefreshResult>;
|
|
72
|
-
refreshAllFallbacks(accounts: OAuthAccount[]): Promise<void>;
|
|
76
|
+
refreshAllFallbacks(accounts: OAuthAccount[], resolveAccessToken?: (account: OAuthAccount) => string | undefined): Promise<void>;
|
|
73
77
|
/**
|
|
74
78
|
* Fire-and-forget refresh. Does not await, swallows errors.
|
|
75
79
|
*/
|
package/dist/quota-manager.js
CHANGED
|
@@ -65,12 +65,16 @@ export class QuotaManager {
|
|
|
65
65
|
now;
|
|
66
66
|
onMainQuotaFetched;
|
|
67
67
|
onApiError;
|
|
68
|
+
onFallbackQuotaFetched;
|
|
69
|
+
onFallbackApiError;
|
|
68
70
|
constructor(opts) {
|
|
69
71
|
this.storage = opts.storage;
|
|
70
72
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
71
73
|
this.now = opts.now ?? Date.now;
|
|
72
74
|
this.onMainQuotaFetched = opts.onMainQuotaFetched;
|
|
73
75
|
this.onApiError = opts.onApiError;
|
|
76
|
+
this.onFallbackQuotaFetched = opts.onFallbackQuotaFetched;
|
|
77
|
+
this.onFallbackApiError = opts.onFallbackApiError;
|
|
74
78
|
this.seedMainFromStorage(opts.storage, opts.storage?.mainAccountId);
|
|
75
79
|
this.seedMainBackoffFromStorage(opts.storage);
|
|
76
80
|
}
|
|
@@ -255,19 +259,25 @@ export class QuotaManager {
|
|
|
255
259
|
});
|
|
256
260
|
return promise;
|
|
257
261
|
}
|
|
258
|
-
async refreshAllFallbacks(accounts) {
|
|
262
|
+
async refreshAllFallbacks(accounts, resolveAccessToken) {
|
|
259
263
|
const now = this.now();
|
|
260
264
|
for (const account of accounts) {
|
|
261
265
|
if (account.enabled === false)
|
|
262
266
|
continue;
|
|
263
|
-
|
|
267
|
+
const accessToken = resolveAccessToken?.(account) ??
|
|
268
|
+
(account.access &&
|
|
269
|
+
account.expires !== undefined &&
|
|
270
|
+
account.expires > now
|
|
271
|
+
? account.access
|
|
272
|
+
: undefined);
|
|
273
|
+
if (!accessToken)
|
|
264
274
|
continue;
|
|
265
275
|
this.bindFallbackLineage(account.id, account);
|
|
266
276
|
const cached = this.getFallback(account.id);
|
|
267
277
|
if (cached && now < cached.refreshAfter)
|
|
268
278
|
continue;
|
|
269
279
|
try {
|
|
270
|
-
await this.refreshFallback(account.id,
|
|
280
|
+
await this.refreshFallback(account.id, accessToken, account);
|
|
271
281
|
}
|
|
272
282
|
catch {
|
|
273
283
|
// Best-effort — keep stale cache entry if fetch fails
|
|
@@ -462,6 +472,18 @@ export class QuotaManager {
|
|
|
462
472
|
continue;
|
|
463
473
|
if (account.enabled === false)
|
|
464
474
|
continue;
|
|
475
|
+
const persistedError = account.lastQuotaRefreshError;
|
|
476
|
+
const persistedClearAt = quotaSnapshotCheckedAt(account.quota);
|
|
477
|
+
const currentError = this.fallbackApiErrors.get(account.id);
|
|
478
|
+
if (persistedError &&
|
|
479
|
+
quotaBackoffActive(persistedError, this.now()) &&
|
|
480
|
+
persistedClearAt <= (persistedError.checkedAt ?? 0) &&
|
|
481
|
+
(!currentError || persistedError.checkedAt >= currentError.checkedAt)) {
|
|
482
|
+
this.fallbackApiErrors.set(account.id, persistedError);
|
|
483
|
+
}
|
|
484
|
+
else if (currentError && persistedClearAt >= currentError.checkedAt) {
|
|
485
|
+
this.fallbackApiErrors.delete(account.id);
|
|
486
|
+
}
|
|
465
487
|
if (!account.quota)
|
|
466
488
|
continue;
|
|
467
489
|
const checkedAt = quotaSnapshotCheckedAt(account.quota);
|
|
@@ -696,6 +718,7 @@ export class QuotaManager {
|
|
|
696
718
|
throw new Error('Quota refresh is already in progress');
|
|
697
719
|
}
|
|
698
720
|
try {
|
|
721
|
+
const fetchStartedAt = this.now();
|
|
699
722
|
const quota = await fetchOAuthQuotaSnapshot({
|
|
700
723
|
accessToken,
|
|
701
724
|
fetchImpl: this.fetchImpl,
|
|
@@ -715,6 +738,7 @@ export class QuotaManager {
|
|
|
715
738
|
checkedAt: completedQuota.checkedAt ?? now,
|
|
716
739
|
}, account);
|
|
717
740
|
this.fallbackApiErrors.delete(accountId);
|
|
741
|
+
this.onFallbackQuotaFetched?.(accountId, completedQuota, now, fetchStartedAt);
|
|
718
742
|
return { quota: completedQuota, fetched: true };
|
|
719
743
|
}
|
|
720
744
|
finally {
|
|
@@ -769,11 +793,13 @@ export class QuotaManager {
|
|
|
769
793
|
if (QuotaManager.isAuthError(error))
|
|
770
794
|
return;
|
|
771
795
|
const previous = this.fallbackApiErrors.get(accountId);
|
|
772
|
-
|
|
796
|
+
const quotaError = buildQuotaOperationError({
|
|
773
797
|
error,
|
|
774
798
|
now: this.now(),
|
|
775
799
|
previous,
|
|
776
800
|
accountIdentity: accountId,
|
|
777
|
-
})
|
|
801
|
+
});
|
|
802
|
+
this.fallbackApiErrors.set(accountId, quotaError);
|
|
803
|
+
this.onFallbackApiError?.(accountId, quotaError);
|
|
778
804
|
}
|
|
779
805
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AccountStorage } from './accounts.ts';
|
|
2
|
+
export declare const THINKING_BINDING_CONTROLS_BETA = "thinking-binding-controls-2026-08-01";
|
|
3
|
+
export type ThinkingPrefixMismatchBehavior = 'account-default' | 'error' | 'drop_block';
|
|
4
|
+
export declare function getThinkingPrefixMismatchBehavior(storage: Pick<AccountStorage, 'thinkingBinding'> | null | undefined): ThinkingPrefixMismatchBehavior;
|
|
5
|
+
export declare function hasReplayableThinkingBlocks(body: Record<string, unknown>): boolean;
|
|
6
|
+
export declare function applyThinkingBindingControls(body: Record<string, unknown>, behavior?: ThinkingPrefixMismatchBehavior): boolean;
|
|
7
|
+
export declare function hasThinkingBindingControls(body: Record<string, unknown>): boolean;
|