@cortexkit/pi-anthropic-auth 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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Pi package for CortexKit Anthropic OAuth support. It overrides Pi's built-in `anthropic` provider with a CortexKit provider extension backed by the shared `@cortexkit/anthropic-auth-core` package.
4
4
 
5
- The Pi provider catalog includes Claude Fable 5 (`claude-fable-5`), limited-access Claude Mythos 5 (`claude-mythos-5`), Claude Opus 5 (`claude-opus-5`), Claude Opus 4.8, Claude Opus 4.5, Claude Sonnet 4.5, and Claude Sonnet 5 (`claude-sonnet-5`). Fable/Mythos reasoning uses Anthropic adaptive thinking with `thinking.display: "summarized"` and `output_config.effort`; the package does not send rejected manual `thinking.budget_tokens` for those models.
5
+ The Pi provider catalog includes Claude Fable 5 (`claude-fable-5`) and 5.1 (`claude-fable-5-1`), limited-access Claude Mythos 5 (`claude-mythos-5`) and 5.1 (`claude-mythos-5-1`), Claude Opus 5 (`claude-opus-5`), Claude Opus 4.8, Claude Opus 4.5, Claude Sonnet 4.5, and Claude Sonnet 5 (`claude-sonnet-5`). Fable/Mythos reasoning uses Anthropic adaptive thinking with `thinking.display: "summarized"` and `output_config.effort`; the package does not send rejected manual `thinking.budget_tokens` for those models. OAuth Fable 5.1 sessions preserve Pi thinking-level changes as mid-conversation effort markers, so switching effort does not rewrite the cached prefix. Replayed signed/redacted thinking follows the configured prefix-mismatch behavior.
6
6
 
7
7
  This package is part of the CortexKit Anthropic Auth monorepo, which supports both OpenCode (`@cortexkit/opencode-anthropic-auth`) and Pi (`@cortexkit/pi-anthropic-auth`) through the same shared core logic.
8
8
 
@@ -42,7 +42,7 @@ Pi state is stored separately from OpenCode at:
42
42
 
43
43
  Override the path with `PI_ANTHROPIC_AUTH_FILE`. The package also respects `PI_AGENT_DIR` when deriving the default sidecar path.
44
44
 
45
- The sidecar uses the same JSON shape as the OpenCode package, including `routing`, `claudeCache`, `cacheKeep`, `prime`, `claudeFast`, `dump`, `relay`, and fallback `accounts` blocks. Runtime OAuth/quota state is stored in `anthropic-auth-state.json`; sticky session assignments are stored separately in `anthropic-auth-routing-state.json` with SHA-256-hashed session IDs.
45
+ The sidecar uses the same JSON shape as the OpenCode package, including `routing`, `claudeCache`, `cacheKeep`, `prime`, `claudeFast`, `thinkingBinding`, `dump`, `relay`, and fallback `accounts` blocks. `thinkingBinding.prefixMismatchBehavior` accepts `account-default` (default), `error`, or `drop_block` for OAuth Fable 5.1 replay. Runtime OAuth/quota state is stored in `anthropic-auth-state.json`; sticky session assignments are stored separately in `anthropic-auth-routing-state.json` with SHA-256-hashed session IDs.
46
46
 
47
47
  ## Commands
48
48
 
@@ -77,10 +77,13 @@ The sidecar uses the same JSON shape as the OpenCode package, including `routing
77
77
  /claude-routing sticky-balanced
78
78
  /claude-routing reset
79
79
 
80
+ /claude-account
81
+ /claude-account reset-backoff
82
+
80
83
  /claude-quota
81
84
  ```
82
85
 
83
- `/claude-quota` reports sidecar OAuth fallback quota state from `~/.pi/agent/anthropic-auth.json`. `/claude-routing fallback-first` prefers usable OAuth fallback accounts before the main account; `/claude-routing main-first` restores the default. `/claude-routing sticky-balanced` assigns each Pi session to an OAuth account according to current 5-hour, 7-day, and matching model-scoped quota headroom, then persists that assignment across transient failures and process restarts. `/claude-routing reset` clears the current Pi session's assignment. Direct Opus sessions prefer usable accounts whose Fable quota is exhausted. API-key routes use the same sidecar shape as OpenCode and are sent directly to their configured Anthropic-compatible base URL, such as Kie's `https://api.kie.ai/claude`, but Pi only uses them after the main OAuth model response reports HTTP 429 or a streaming rate-limit error and a live quota check confirms 0% remaining. `/claude-cachekeep always` keeps active hybrid caches warm while Pi remains open; `/claude-cachekeep HH-HH` limits prewarms to a local time window. Both send `max_tokens: 0` pre-warm requests about five minutes before the 1-hour TTL expires. Running `/claude-cachekeep` without arguments lists live tracked sessions across Pi processes through a temporary lease registry that stores only session IDs and cache timing. `/claude-fast on` adds Anthropic `speed: "fast"` plus the `fast-mode-2026-02-01` beta header for supported Opus models (`claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, and `claude-opus-5`).
86
+ `/claude-account reset-backoff` clears the main OAuth refresh backoff and its matching quota backoff. Persisted refresh backoffs are bound to the refresh token that produced them, so replacing a rejected credential immediately escapes its stale latch. `/claude-quota` reports sidecar OAuth fallback quota state from `~/.pi/agent/anthropic-auth.json`. `/claude-routing fallback-first` prefers usable OAuth fallback accounts before the main account; `/claude-routing main-first` restores the default. `/claude-routing sticky-balanced` assigns each Pi session to an OAuth account according to current 5-hour, 7-day, and matching model-scoped quota headroom, then persists that assignment across transient failures and process restarts. `/claude-routing reset` clears the current Pi session's assignment. Direct Opus sessions prefer usable accounts whose Fable quota is exhausted. API-key routes use the same sidecar shape as OpenCode and are sent directly to their configured Anthropic-compatible base URL, such as Kie's `https://api.kie.ai/claude`, but Pi only uses them after the main OAuth model response reports HTTP 429 or a streaming rate-limit error and a live quota check confirms 0% remaining. `/claude-cachekeep always` keeps active hybrid caches warm while Pi remains open; `/claude-cachekeep HH-HH` limits prewarms to a local time window. Both send `max_tokens: 0` pre-warm requests about five minutes before the 1-hour TTL expires. Running `/claude-cachekeep` without arguments lists live tracked sessions across Pi processes through a temporary lease registry that stores only session IDs and cache timing. `/claude-fast on` adds Anthropic `speed: "fast"` plus the `fast-mode-2026-02-01` beta header for supported Opus models (`claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, and `claude-opus-5`).
84
87
 
85
88
  ### Claude quota window priming
86
89
 
package/dist/commands.js CHANGED
@@ -1,4 +1,4 @@
1
- import { buildClaudeQuotaSummary, buildFallbackQuotaSummaries, buildPrimeAccountStatuses, CLAUDE_ACCOUNT_COMMAND_NAME, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_PRIME_COMMAND_NAME, CLAUDE_ROUTING_COMMAND_NAME, createEmptyStorage, executeAccountCommand, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, executeLoggingCommand, executePrimeCommand, executeRoutingCommand, getCache1hPersistentMode, getCacheKeepWindow, getPersistedLogLevel, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepAlways, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isPrimePersistentlyEnabled, loadAccounts, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, parseLoggingCommandAction, parseRoutingCommandAction, removeAccountPersistent, reorderAccountsPersistent, setAccountEnabledPersistent, setCache1hPersistentEnabled, setCache1hPersistentMode, setCacheKeepPersistentAlways, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setCacheKeepSubagentsEnabled, setDumpEnabled, setDumpPersistentEnabled, setFastModePersistentEnabled, setLogLevelPersistent, setRoutingMode, } from '@cortexkit/anthropic-auth-core';
1
+ import { buildClaudeQuotaSummary, buildFallbackQuotaSummaries, buildPrimeAccountStatuses, CLAUDE_ACCOUNT_COMMAND_NAME, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_PRIME_COMMAND_NAME, CLAUDE_ROUTING_COMMAND_NAME, createEmptyStorage, detectClaustrumConnection, executeAccountCommand, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, executeLoggingCommand, executePrimeCommand, executeRoutingCommand, getCache1hPersistentMode, getCacheKeepWindow, getPersistedLogLevel, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepAlways, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isPrimePersistentlyEnabled, loadAccounts, mergeMainQuotaErrorClearedAt, mergeMainRefreshErrorClearedAt, parseAccountCommandAction, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, parseLoggingCommandAction, parseRoutingCommandAction, removeAccountPersistent, reorderAccountsPersistent, saveAccountState, setAccountEnabledPersistent, setCache1hPersistentEnabled, setCache1hPersistentMode, setCacheKeepPersistentAlways, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setCacheKeepSubagentsEnabled, setDumpEnabled, setDumpPersistentEnabled, setFastModePersistentEnabled, setLogLevelPersistent, setRoutingMode, } from '@cortexkit/anthropic-auth-core';
2
2
  import { getPiAccountStoragePath } from './paths.js';
3
3
  import { clearPiStickyRoutingSession, getPiTrackedCacheKeepSessions, } from './stream.js';
4
4
  function notify(ctx, message, kind = 'info') {
@@ -152,9 +152,13 @@ export function registerCommands(pi) {
152
152
  handler: async (args, ctx) => {
153
153
  const path = getPiAccountStoragePath();
154
154
  const storage = await loadAccounts(path);
155
+ const action = parseAccountCommandAction(args ?? '');
155
156
  const result = executeAccountCommand({
156
157
  argumentsText: args ?? '',
157
158
  storage: storage ?? createEmptyStorage(),
159
+ claustrum: action.type === 'status'
160
+ ? await detectClaustrumConnection()
161
+ : undefined,
158
162
  });
159
163
  if (!result.updated) {
160
164
  notify(ctx, result.text);
@@ -181,6 +185,29 @@ export function registerCommands(pi) {
181
185
  await reorderAccountsPersistent(newOrder, path);
182
186
  }
183
187
  }
188
+ else if (mutationAction === 'reset-backoff') {
189
+ const nextStorage = storage ?? createEmptyStorage();
190
+ nextStorage.refresh = nextStorage.refresh ?? {};
191
+ nextStorage.refresh.mainLastRefreshError = undefined;
192
+ nextStorage.refresh.mainRefreshErrorClearedAt =
193
+ mergeMainRefreshErrorClearedAt(nextStorage.refresh.mainRefreshErrorClearedAt, Date.now());
194
+ const mainIdentity = nextStorage.mainAccountId;
195
+ const quotaError = nextStorage.quota?.mainLastQuotaApiError;
196
+ if (!quotaError?.accountIdentity ||
197
+ !mainIdentity ||
198
+ quotaError.accountIdentity === mainIdentity) {
199
+ nextStorage.quota = nextStorage.quota ?? {};
200
+ nextStorage.quota.mainLastQuotaApiError = undefined;
201
+ const nextQuotaErrorGeneration = (nextStorage.quota.mainQuotaErrorGeneration ?? 0) + 1;
202
+ nextStorage.quota.mainQuotaErrorGeneration = Math.max(nextStorage.quota.mainQuotaErrorGeneration ?? 0, nextQuotaErrorGeneration);
203
+ nextStorage.quota.mainQuotaErrorClearedAt =
204
+ mergeMainQuotaErrorClearedAt(nextStorage.quota.mainQuotaErrorClearedAt, Date.now());
205
+ }
206
+ await saveAccountState(nextStorage, path, {
207
+ mainRefresh: true,
208
+ mainQuota: true,
209
+ });
210
+ }
184
211
  notify(ctx, result.text);
185
212
  },
186
213
  });
package/dist/convert.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Cache1hMode, type ClaudeCodeIdentity } from '@cortexkit/anthropic-auth-core';
1
+ import { type Cache1hMode, type ClaudeCodeIdentity, type MidConversationEffortTransition, type ThinkingPrefixMismatchBehavior } from '@cortexkit/anthropic-auth-core';
2
2
  import type { Context, SimpleStreamOptions, Tool } from '@earendil-works/pi-ai';
3
3
  export type AnthropicRequestBody = {
4
4
  model: string;
@@ -26,7 +26,10 @@ export declare function fromClaudeCodeToolName(name: string, tools?: Tool[]): st
26
26
  export declare function buildAnthropicRequest(modelId: string, context: Context, options: SimpleStreamOptions | undefined, cache: {
27
27
  enabled: boolean;
28
28
  mode: Cache1hMode;
29
- }, fastModeEnabled?: boolean, identity?: ClaudeCodeIdentity): Promise<{
29
+ }, fastModeEnabled?: boolean, identity?: ClaudeCodeIdentity, controls?: {
30
+ effortTransitions?: readonly MidConversationEffortTransition[];
31
+ thinkingPrefixMismatchBehavior?: ThinkingPrefixMismatchBehavior;
32
+ }): Promise<{
30
33
  body: AnthropicRequestBody;
31
34
  bodyText: string;
32
35
  }>;
package/dist/convert.js CHANGED
@@ -1,8 +1,9 @@
1
- import { applyClaudeCodeMetadata, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, CLAUDE_OPUS_5_ADAPTIVE_THINKING, CLAUDE_SONNET_5_ADAPTIVE_THINKING, isClaudeFableOrMythos5Model, isClaudeOpus5Model, isClaudeSonnet5Model, isFastModeSupportedModel, isOpenAIReasoningSignature, orderClaudeCodeBody, signRequestBody, } from '@cortexkit/anthropic-auth-core';
1
+ import { applyClaudeCodeMetadata, applyMidConversationOutputConfig, applyThinkingBindingControls, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, CLAUDE_OPUS_5_ADAPTIVE_THINKING, CLAUDE_SONNET_5_ADAPTIVE_THINKING, ClaudeCodeFirstUserTextTracker, isClaudeFableOrMythos5Model, isClaudeOpus5Model, isClaudeSonnet5Model, isFastModeSupportedModel, isOpenAIReasoningSignature, orderClaudeCodeBody, signRequestBody, } from '@cortexkit/anthropic-auth-core';
2
2
  // Anchor identifying Pi's documentation paragraph — the only part of the prompt
3
3
  // that Anthropic currently rejects in system[]. Unknown prompt shapes take the
4
4
  // service-preserving fallback below instead of returning the full prompt there.
5
5
  const PI_DOCS_ANCHOR = 'Pi documentation';
6
+ const firstUserTextTracker = new ClaudeCodeFirstUserTextTracker();
6
7
  const ANTHROPIC_REPLAY_APIS = new Set([
7
8
  'anthropic-messages',
8
9
  'cortexkit-anthropic-messages',
@@ -351,7 +352,7 @@ function applyCacheMode(body, enabled, mode) {
351
352
  body.cache_control = { type: 'ephemeral' };
352
353
  addTtl(body);
353
354
  }
354
- export async function buildAnthropicRequest(modelId, context, options, cache, fastModeEnabled = false, identity) {
355
+ export async function buildAnthropicRequest(modelId, context, options, cache, fastModeEnabled = false, identity, controls = {}) {
355
356
  const messages = convertMessages(context.messages, modelId);
356
357
  // Strip trailing assistant messages — Anthropic rejects prefill on some models
357
358
  while (messages.length &&
@@ -361,7 +362,9 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
361
362
  const system = [
362
363
  {
363
364
  type: 'text',
364
- text: buildBillingHeaderValue(messages, undefined, CLAUDE_CODE_ENTRYPOINT),
365
+ text: buildBillingHeaderValue(messages, undefined, CLAUDE_CODE_ENTRYPOINT, options?.sessionId
366
+ ? firstUserTextTracker.resolve(options.sessionId, messages)
367
+ : undefined),
365
368
  },
366
369
  { type: 'text', text: CLAUDE_CODE_IDENTITY },
367
370
  ];
@@ -448,10 +451,13 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
448
451
  };
449
452
  }
450
453
  }
454
+ applyMidConversationOutputConfig(body, controls.effortTransitions ?? []);
451
455
  addEphemeralCacheControl(body);
452
456
  applyCacheMode(body, cache.enabled, cache.mode);
453
- if (identity)
457
+ if (identity) {
458
+ applyThinkingBindingControls(body, controls.thinkingPrefixMismatchBehavior ?? 'account-default');
454
459
  applyClaudeCodeMetadata(body, identity);
460
+ }
455
461
  const unsigned = JSON.stringify(orderClaudeCodeBody(body));
456
462
  const bodyText = await signRequestBody(unsigned);
457
463
  return { body, bodyText };
@@ -0,0 +1,12 @@
1
+ import { type MidConversationEffortTransition } from '@cortexkit/anthropic-auth-core';
2
+ type SessionEntryLike = {
3
+ id?: unknown;
4
+ type?: unknown;
5
+ thinkingLevel?: unknown;
6
+ message?: {
7
+ role?: unknown;
8
+ };
9
+ };
10
+ /** Build Pi's active, compaction-aware Fable 5.1 effort timeline. */
11
+ export declare function collectPiEffortHistory(contextEntries: readonly SessionEntryLike[], fullBranch: readonly SessionEntryLike[]): MidConversationEffortTransition[];
12
+ export {};
@@ -0,0 +1,63 @@
1
+ import { normalizeAdaptiveEffort, } from '@cortexkit/anthropic-auth-core';
2
+ function entryRole(entry) {
3
+ if (entry.type === 'compaction' || entry.type === 'branch_summary') {
4
+ return 'user';
5
+ }
6
+ if (entry.type === 'custom_message')
7
+ return 'user';
8
+ return entry.type === 'message' ? entry.message?.role : undefined;
9
+ }
10
+ function appendTransition(transitions, afterAssistantMessages, effort) {
11
+ const previous = transitions.at(-1);
12
+ if (previous?.effort === effort)
13
+ return;
14
+ if (previous?.afterAssistantMessages === afterAssistantMessages) {
15
+ previous.effort = effort;
16
+ return;
17
+ }
18
+ transitions.push({ afterAssistantMessages, effort });
19
+ }
20
+ /** Build Pi's active, compaction-aware Fable 5.1 effort timeline. */
21
+ export function collectPiEffortHistory(contextEntries, fullBranch) {
22
+ let activeEffort = 'high';
23
+ let assistantMessages = 0;
24
+ const transitions = [];
25
+ const first = contextEntries[0];
26
+ const compactionIndex = first?.type === 'compaction'
27
+ ? fullBranch.findIndex((entry) => entry.id === first.id)
28
+ : -1;
29
+ if (compactionIndex >= 0) {
30
+ for (let index = 0; index <= compactionIndex; index++) {
31
+ const entry = fullBranch[index];
32
+ if (entry?.type !== 'thinking_level_change')
33
+ continue;
34
+ activeEffort = normalizeAdaptiveEffort(entry.thinkingLevel) ?? 'high';
35
+ }
36
+ appendTransition(transitions, 0, activeEffort);
37
+ }
38
+ const branchIndexById = new Map(fullBranch.map((entry, index) => [entry.id, index]));
39
+ for (const entry of contextEntries) {
40
+ if (entry === first && entry.type === 'compaction')
41
+ continue;
42
+ const branchIndex = branchIndexById.get(entry.id);
43
+ const retainedBeforeCompaction = compactionIndex >= 0 &&
44
+ typeof branchIndex === 'number' &&
45
+ branchIndex < compactionIndex;
46
+ if (entry.type === 'thinking_level_change') {
47
+ if (!retainedBeforeCompaction) {
48
+ activeEffort = normalizeAdaptiveEffort(entry.thinkingLevel) ?? 'high';
49
+ }
50
+ continue;
51
+ }
52
+ const role = entryRole(entry);
53
+ if (role === 'assistant') {
54
+ assistantMessages++;
55
+ }
56
+ else if (role === 'user' ||
57
+ role === 'toolResult' ||
58
+ role === 'tool_result') {
59
+ appendTransition(transitions, assistantMessages, activeEffort);
60
+ }
61
+ }
62
+ return transitions;
63
+ }
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { authorize, CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW, CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS, CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS, CLAUDE_FABLE_MYTHOS_5_PRICING, exchange, refreshClaudeOAuthToken, } from '@cortexkit/anthropic-auth-core';
1
+ import { authorize, CLAUDE_FABLE_MYTHOS_5_1_PRICING, CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW, CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS, CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS, CLAUDE_FABLE_MYTHOS_5_PRICING, exchange, isClaudeFableOrMythos51Model, refreshClaudeOAuthToken, } from '@cortexkit/anthropic-auth-core';
2
2
  import { registerCommands } from './commands.js';
3
+ import { collectPiEffortHistory } from './effort-history.js';
3
4
  import { streamCortexKitAnthropic } from './stream.js';
4
5
  async function loginAnthropic(callbacks) {
5
6
  const auth = await authorize('max');
@@ -32,25 +33,51 @@ async function refreshAnthropicToken(credentials) {
32
33
  }
33
34
  export default function cortexKitPiAnthropicAuth(pi) {
34
35
  registerCommands(pi);
36
+ const effortHistoryBySession = new Map();
37
+ pi.on('turn_start', async (_event, ctx) => {
38
+ const sessionId = ctx.sessionManager.getSessionId();
39
+ if (!sessionId)
40
+ return;
41
+ const transitions = collectPiEffortHistory(ctx.sessionManager.buildContextEntries(), ctx.sessionManager.getBranch());
42
+ effortHistoryBySession.delete(sessionId);
43
+ effortHistoryBySession.set(sessionId, transitions);
44
+ while (effortHistoryBySession.size > 128) {
45
+ const oldest = effortHistoryBySession.keys().next().value;
46
+ if (oldest)
47
+ effortHistoryBySession.delete(oldest);
48
+ else
49
+ break;
50
+ }
51
+ });
52
+ pi.on('session_shutdown', async (_event, ctx) => {
53
+ const sessionId = ctx.sessionManager.getSessionId();
54
+ if (sessionId)
55
+ effortHistoryBySession.delete(sessionId);
56
+ });
35
57
  pi.registerProvider('anthropic', {
36
58
  name: 'Anthropic (CortexKit OAuth)',
37
59
  baseUrl: 'https://api.anthropic.com',
38
60
  api: 'cortexkit-anthropic-messages',
39
61
  models: [
40
- ...Object.values(CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS).map((model) => ({
41
- id: model.id,
42
- name: model.name,
43
- reasoning: true,
44
- input: textImageInput(),
45
- cost: {
46
- input: CLAUDE_FABLE_MYTHOS_5_PRICING.input,
47
- output: CLAUDE_FABLE_MYTHOS_5_PRICING.output,
48
- cacheRead: CLAUDE_FABLE_MYTHOS_5_PRICING.cacheRead,
49
- cacheWrite: CLAUDE_FABLE_MYTHOS_5_PRICING.cacheWrite5m,
50
- },
51
- contextWindow: CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW,
52
- maxTokens: CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS,
53
- })),
62
+ ...Object.values(CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS).map((model) => {
63
+ const pricing = isClaudeFableOrMythos51Model(model.id)
64
+ ? CLAUDE_FABLE_MYTHOS_5_1_PRICING
65
+ : CLAUDE_FABLE_MYTHOS_5_PRICING;
66
+ return {
67
+ id: model.id,
68
+ name: model.name,
69
+ reasoning: true,
70
+ input: textImageInput(),
71
+ cost: {
72
+ input: pricing.input,
73
+ output: pricing.output,
74
+ cacheRead: pricing.cacheRead,
75
+ cacheWrite: pricing.cacheWrite5m,
76
+ },
77
+ contextWindow: CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW,
78
+ maxTokens: CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS,
79
+ };
80
+ }),
54
81
  {
55
82
  id: 'claude-opus-5',
56
83
  name: 'Claude Opus 5',
@@ -103,6 +130,8 @@ export default function cortexKitPiAnthropicAuth(pi) {
103
130
  refreshToken: refreshAnthropicToken,
104
131
  getApiKey: (credentials) => credentials.access,
105
132
  },
106
- streamSimple: streamCortexKitAnthropic,
133
+ streamSimple: (model, context, options) => streamCortexKitAnthropic(model, context, options, options?.sessionId
134
+ ? effortHistoryBySession.get(options.sessionId)
135
+ : undefined),
107
136
  });
108
137
  }
package/dist/stream.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type ApiKeyAccount } from '@cortexkit/anthropic-auth-core';
1
+ import { type ApiKeyAccount, type MidConversationEffortTransition } from '@cortexkit/anthropic-auth-core';
2
2
  import { type Api, type AssistantMessageEventStream, type Context, type Model, type SimpleStreamOptions } from '@earendil-works/pi-ai';
3
3
  export declare function clearPiStickyRoutingSession(storagePath: string, sessionId: string): Promise<void>;
4
4
  export declare function getPiTrackedCacheKeepSessions(): Promise<import("@cortexkit/anthropic-auth-core").CacheKeepTrackedSession[]>;
@@ -16,5 +16,5 @@ export declare function buildExplicitBaseMessagesUrl(baseURL: string): URL;
16
16
  export declare function configureApiRouteHeaders(account: ApiKeyAccount, fastMode: boolean): Headers;
17
17
  export declare function parseSse(response: Response): AsyncGenerator<AnthropicEvent>;
18
18
  export declare function primaryResponseAllowsApiFallback(preflight: Response | string): boolean;
19
- export declare function streamCortexKitAnthropic(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
19
+ export declare function streamCortexKitAnthropic(model: Model<Api>, context: Context, options?: SimpleStreamOptions, effortTransitions?: readonly MidConversationEffortTransition[]): AssistantMessageEventStream;
20
20
  export {};
package/dist/stream.js CHANGED
@@ -1,4 +1,4 @@
1
- import { applyClaudeCodeHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CacheKeepSessionRegistry, createStickyNoRouteResponse, decideStickyQuotaFailure, dumpDirectRequest, FAST_MODE_BETA, FallbackAccountManager, getCache1hPersistentMode, getDefaultCacheKeepRegistryDirectory, getFallbackReauthLabels, getOrCreateMainAccountId, getRelayConfig, getRoutingMode, getStickyRoutingStatePath, isApiKeyAccount, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isOAuthAccount, isPermanentRefreshError, isValidApiBaseURL, killswitchPassesPolicy, loadAccounts, mergeAnthropicBetas, QuotaManager, quotaSnapshotHasStandardWindows, quotaSnapshotModelScopeIsExhausted, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, refreshBackoffActive, resolveClaudeCodeIdentity, STICKY_ROUTING_MAIN_ACCOUNT_ID, StickySessionRouter, sendViaRelay, setDumpEnabled, shouldFallbackStatus, stickyQuotaSnapshotIsFresh, stickyRetryAfterWithJitter, stickyRouteFamilyForModel, } from '@cortexkit/anthropic-auth-core';
1
+ import { applyClaudeCodeHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CacheKeepSessionRegistry, createStickyNoRouteResponse, decideStickyQuotaFailure, dumpDirectRequest, FAST_MODE_BETA, FallbackAccountManager, getCache1hPersistentMode, getDefaultCacheKeepRegistryDirectory, getFallbackReauthLabels, getOrCreateMainAccountId, getRelayConfig, getRoutingMode, getStickyRoutingStatePath, getThinkingPrefixMismatchBehavior, hasThinkingBindingControls, isApiKeyAccount, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isOAuthAccount, isPermanentRefreshError, isValidApiBaseURL, killswitchPassesPolicy, loadAccounts, MID_CONVERSATION_OUTPUT_CONFIG_BETA, mergeAnthropicBetas, QuotaManager, quotaSnapshotHasStandardWindows, quotaSnapshotModelScopeIsExhausted, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, refreshBackoffActive, resolveClaudeCodeIdentity, STICKY_ROUTING_MAIN_ACCOUNT_ID, StickySessionRouter, sendViaRelay, setDumpEnabled, shouldFallbackStatus, stickyQuotaSnapshotIsFresh, stickyRetryAfterWithJitter, stickyRouteFamilyForModel, THINKING_BINDING_CONTROLS_BETA, tokenFingerprint, usesMidConversationOutputConfig, } from '@cortexkit/anthropic-auth-core';
2
2
  import { calculateCost, createAssistantMessageEventStream, } from '@earendil-works/pi-ai';
3
3
  import { buildAnthropicRequest, fromClaudeCodeToolName } from './convert.js';
4
4
  import { getPiAccountStoragePath } from './paths.js';
@@ -90,10 +90,16 @@ const cacheKeepManager = new CacheKeepManager({
90
90
  const storage = await loadAccounts(storagePath);
91
91
  const identity = await resolveClaudeCodeIdentity(accessToken, typeof body.model === 'string' ? body.model : undefined, target.oauthAccountId ?? storage?.mainAccountId);
92
92
  headers.delete('anthropic-beta');
93
- applyClaudeCodeHeaders(headers, accessToken, { body, identity });
94
- headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [
95
- CACHE_KEEP_EXTENDED_TTL_BETA,
96
- ]));
93
+ applyClaudeCodeHeaders(headers, accessToken, {
94
+ body,
95
+ identity,
96
+ extraBetas: [
97
+ CACHE_KEEP_EXTENDED_TTL_BETA,
98
+ ...(hasThinkingBindingControls(body)
99
+ ? [THINKING_BINDING_CONTROLS_BETA]
100
+ : []),
101
+ ],
102
+ });
97
103
  if (body.speed === 'fast') {
98
104
  headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [FAST_MODE_BETA]));
99
105
  }
@@ -230,13 +236,26 @@ async function sendAnthropicRequest(options) {
230
236
  const { body, bodyText } = await buildAnthropicRequest(options.model.id, options.context, options.streamOptions, {
231
237
  enabled: isCache1hPersistentlyEnabled(storage),
232
238
  mode: getCache1hPersistentMode(storage),
233
- }, isFastModePersistentlyEnabled(storage), identity);
239
+ }, isFastModePersistentlyEnabled(storage), identity, {
240
+ effortTransitions: options.apiAccount ? [] : options.effortTransitions,
241
+ thinkingPrefixMismatchBehavior: options.apiAccount
242
+ ? 'account-default'
243
+ : getThinkingPrefixMismatchBehavior(storage),
244
+ });
234
245
  const fastMode = body.speed === 'fast';
235
246
  const headers = options.apiAccount
236
247
  ? configureApiRouteHeaders(options.apiAccount, fastMode)
237
248
  : applyClaudeCodeHeaders(new Headers(), options.accessToken ?? '', {
238
249
  body,
239
250
  identity,
251
+ extraBetas: [
252
+ ...(hasThinkingBindingControls(body)
253
+ ? [THINKING_BINDING_CONTROLS_BETA]
254
+ : []),
255
+ ...(usesMidConversationOutputConfig(body)
256
+ ? [MID_CONVERSATION_OUTPUT_CONFIG_BETA]
257
+ : []),
258
+ ],
240
259
  });
241
260
  if (!options.apiAccount && fastMode) {
242
261
  headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [FAST_MODE_BETA]));
@@ -474,7 +493,7 @@ async function executeWithFallback(options) {
474
493
  (configured.expires !== undefined &&
475
494
  configured.expires <= Date.now()) ||
476
495
  isPermanentRefreshError(configured.lastRefreshError) ||
477
- refreshBackoffActive(configured.lastRefreshError, configured.id, Date.now()))
496
+ refreshBackoffActive(configured.lastRefreshError, configured.id, Date.now(), tokenFingerprint(configured.refresh)))
478
497
  continue;
479
498
  account = configured;
480
499
  const quota = quotaManager.getFallback(configured.id, configured)?.quota ??
@@ -786,7 +805,7 @@ async function executeWithFallback(options) {
786
805
  }
787
806
  return primaryPreflight instanceof Response ? primaryPreflight : primary;
788
807
  }
789
- export function streamCortexKitAnthropic(model, context, options) {
808
+ export function streamCortexKitAnthropic(model, context, options, effortTransitions) {
790
809
  const stream = createAssistantMessageEventStream();
791
810
  void (async () => {
792
811
  const output = createOutput(model);
@@ -802,6 +821,7 @@ export function streamCortexKitAnthropic(model, context, options) {
802
821
  streamOptions: options,
803
822
  primaryAccessToken: accessToken,
804
823
  storagePath,
824
+ effortTransitions,
805
825
  });
806
826
  if (!response.ok) {
807
827
  throw new Error(`Anthropic request failed: HTTP ${response.status} ${await response.text()}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/pi-anthropic-auth",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "prepublishOnly": "bun run build"
37
37
  },
38
38
  "dependencies": {
39
- "@cortexkit/anthropic-auth-core": "1.21.0"
39
+ "@cortexkit/anthropic-auth-core": "1.22.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@earendil-works/pi-ai": "*",