@cortexkit/pi-anthropic-auth 2.0.2 → 2.0.3
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 +34 -96
- package/dist/convert.js +5 -2
- package/dist/effort-history.d.ts +8 -3
- package/dist/effort-history.js +12 -5
- package/dist/index.js +9 -10
- package/dist/native-commands.js +18 -6
- package/dist/native.d.ts +1 -0
- package/dist/native.js +16 -8
- package/dist/stream.js +63 -33
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,116 +1,54 @@
|
|
|
1
1
|
# @cortexkit/pi-anthropic-auth
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use Claude Pro/Max subscriptions in [Pi](https://pi.dev) with `@cortexkit/pi-anthropic-auth`. The companion `@cortexkit/opencode-anthropic-auth` package supports [OpenCode](https://opencode.ai) 1 and 2; both integrations use the shared `@cortexkit/anthropic-auth-core` package.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Why use it
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Multiple Claude accounts with ordered fallbacks, quota-aware routing and per-session account affinity.
|
|
8
|
+
- Prompt-cache controls and optional cache keepalive for active sessions.
|
|
9
|
+
- Optional Claustrum integration: Claustrum is a local credential-vault service that supplies Claude accounts instead of each application's local account pool.
|
|
10
|
+
- An optional user-owned Cloudflare relay to reduce repeated request uploads.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Disclaimer
|
|
10
13
|
|
|
11
|
-
This
|
|
12
|
-
|
|
13
|
-
> [!IMPORTANT]
|
|
14
|
-
> These instructions describe the upcoming native release, which is not yet published.
|
|
15
|
-
|
|
16
|
-
## Setup (recommended)
|
|
17
|
-
|
|
18
|
-
Setup is an *offline* step: every OpenCode, LiteCode, and Pi process must be closed while it runs. Offline does not mean without network; setup still uses the network, for example to install packages and to sign in to Claude. Quit those processes, then run the setup command from the OpenCode package:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
bunx @cortexkit/opencode-anthropic-auth setup
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
What setup does: it installs this extension with `pi install` and migrates Pi to its local account pool, or, if you choose it, enrolls Pi in the Claustrum vault (Claustrum is a local credential-vault service; `ck` is its command-line tool). Useful flags: `--yes` (no prompts), `--dry-run`, `--login` (sign in to Claude during setup), `--claustrum` / `--no-claustrum`, and `--remove-pi-auth` (consent, with `--yes`, to moving Pi's stored Anthropic login). `--login` and `--claustrum` cannot be combined.
|
|
25
|
-
|
|
26
|
-
---
|
|
14
|
+
This project is not affiliated with or endorsed by Anthropic. Using Claude subscriptions outside Anthropic's official clients may violate Anthropic's terms and can lead to account restrictions or suspension. Use it at your own risk.
|
|
27
15
|
|
|
28
16
|
## Install
|
|
29
17
|
|
|
30
|
-
Install with Pi's package manager:
|
|
31
|
-
|
|
32
18
|
```bash
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
To pin a release, replace `VERSION` with its version number and keep the quotes: `pi install "npm:@cortexkit/pi-anthropic-auth@VERSION"`.
|
|
37
|
-
|
|
38
|
-
To try it for one run without changing Pi settings:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
pi -e npm:@cortexkit/pi-anthropic-auth
|
|
19
|
+
bunx @cortexkit/opencode-anthropic-auth setup
|
|
42
20
|
```
|
|
43
21
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Accounts and custody
|
|
47
|
-
|
|
48
|
-
Pi takes Claude credentials from one source, selected by Pi's own Claustrum enrollment record (`~/.local/state/cortexkit/anthropic-auth/pi-enrollment.json`, or `PI_ANTHROPIC_AUTH_CLAUSTRUM_ENROLLMENT_FILE`):
|
|
49
|
-
|
|
50
|
-
- **Local custody** (default): the local account pool at `~/.pi/agent/anthropic-auth-pool.json`, with private credential state in `anthropic-auth-pool-state.json`. The pool holds Pi's Claude OAuth accounts (one main account, tried first by default, plus fallbacks) and API-key fallback routes. Its files are created next to the old sidecar path, so `PI_ANTHROPIC_AUTH_FILE` moves them too. Every Pi process shares this pool; OpenCode keeps a separate one.
|
|
51
|
-
- **Vault custody**: the Claustrum vault, through the `anthropic-auth-pi` enrollment and read access to the `anthropic-native` credential category. Setup grants this with `ck auth grant --principal enrolled:anthropic-auth-pi --selector-kind category --selector anthropic-native --operation read`.
|
|
52
|
-
|
|
53
|
-
If the enrollment record cannot be read, Pi refuses the requests this extension would serve until the record is repaired or Pi is disconnected. An approved enrollment keeps Pi on the vault even if its token file is missing, so Pi refuses rather than falling back to local accounts. See [the custody contract](https://github.com/cortexkit/anthropic-auth/blob/main/docs/custody-state-machine.md) for details.
|
|
54
|
-
|
|
55
|
-
### Local custody
|
|
22
|
+
After setup:
|
|
56
23
|
|
|
57
|
-
|
|
24
|
+
OpenCode 1: open `/claude` → **Accounts** to sign in.
|
|
58
25
|
|
|
59
|
-
|
|
26
|
+
OpenCode 2: In OpenCode, sign in to Anthropic with "Claude Pro / Max (native account pool)".
|
|
60
27
|
|
|
61
|
-
|
|
28
|
+
Pi: open `/claude` and use **Start OAuth sign-in**, then **Finish OAuth sign-in**.
|
|
62
29
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
While enrolled, Pi serves only vault accounts; vault accounts added with `ck auth login --provider anthropic` are discovered without a restart. Local accounts are not used, refreshed, quota-polled, kept warm by cache keepalive, or shown, and they return unchanged after disconnect. If the vault has no usable Claude account, requests refuse: add accounts with `ck auth login --provider anthropic`, or disconnect.
|
|
66
|
-
|
|
67
|
-
**Disconnect from the vault** in `/claude` asks for confirmation, restores the stashed login byte-for-byte, then ends Pi's enrollment. If Pi's auth file already holds a different login, disconnect stops and keeps both logins and the enrollment so you can resolve them with setup. **Disconnect from the vault** is also offered when the enrollment record cannot be read. If the local pool has never been migrated, stop the hosts and run `setup --no-claustrum` before serving local accounts; disconnect does not perform migration.
|
|
68
|
-
|
|
69
|
-
## Settings
|
|
70
|
-
|
|
71
|
-
The pool stores the same settings as the OpenCode package: `routing`, `claudeCache`, `cacheKeep`, `prime`, `claudeFast`, `thinkingBinding`, `dump`, `relay`, and the fallback accounts. While Pi is enrolled in the vault, settings are kept separately in `anthropic-auth-vault-settings.json`. `thinkingBinding.prefixMismatchBehavior` accepts `account-default` (default), `error`, or `drop_block` when a Fable 5.1 OAuth request reuses reasoning with an Anthropic signature from an earlier reply after the preceding messages, instructions, or tools change. Sticky session assignments are stored in `anthropic-auth-pool-routing-state.json` (`anthropic-auth-vault-routing-state.json` while enrolled) with SHA-256-hashed session IDs.
|
|
72
|
-
|
|
73
|
-
## The /claude menu
|
|
74
|
-
|
|
75
|
-
Pi registers one command, `/claude`. It opens a menu; each action collects its own parameters, and typed arguments are rejected with a pointer to the menu. Without a UI, `/claude` prints the status sections only.
|
|
76
|
-
|
|
77
|
-
| Group | Status | Actions |
|
|
78
|
-
| --- | --- | --- |
|
|
79
|
-
| Accounts | Account status and usage | Enable account, Disable account, Remove account, Move account up, Move account down, Reset account backoff, Reset host enrollment, Add API-key fallback, Start OAuth sign-in, Finish OAuth sign-in, Custody setup guidance, Disconnect from the vault |
|
|
80
|
-
| Quota | Quota summary | — |
|
|
81
|
-
| Routing | Routing status | Routing mode (`main-first`, `fallback-first`, `sticky-balanced`), Reset routing session |
|
|
82
|
-
| Cache | Cache status, Cache keep status | Enable cache, Disable cache, Cache mode (`explicit`, `automatic`, `hybrid`), Keep cache warm always, Disable cache keep, Cache keep window, Cache keep for subagents (`on`/`off`) |
|
|
83
|
-
| Diagnostics | Dump status, Logging status | Enable request dumps, Disable request dumps, Logging level (`error`, `warn`, `info`, `debug`, `trace`) |
|
|
84
|
-
| Extras | Fast mode status, Prime quota window status | Enable fast mode, Disable fast mode |
|
|
85
|
-
|
|
86
|
-
In local custody, every Accounts action except **Disconnect from the vault** is offered. While Pi is enrolled, or when its enrollment cannot be read, the Accounts group shows only enable, disable, **Reset account backoff**, and **Disconnect from the vault**. **Remove account**, **Reset host enrollment**, and **Disconnect from the vault** ask for confirmation. Killswitch, quota refresh, Prime on/off, and lane start are OpenCode-only.
|
|
87
|
-
|
|
88
|
-
**Reset account 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 clears the backoff left by the old refresh token. **Quota summary** reports quota for the accounts of the active custody source. **Routing mode** → `fallback-first` prefers usable OAuth fallback accounts before the main account; `main-first` restores the default. `sticky-balanced` assigns each Pi session to an OAuth account according to current 5-hour, 7-day, and model-specific quota headroom, then keeps the session on that account (session affinity), persisting the assignment across transient failures and process restarts. Changing the session model discards the old assignment before quota-based reselection. **Reset routing session** clears the current Pi session's assignment. Direct Opus sessions prefer usable accounts whose Fable quota (the separate weekly limit for Claude Fable models) is exhausted, saving the other accounts for Fable sessions. API-key routes 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. **Keep cache warm always** keeps active hybrid caches warm while Pi remains open; **Cache keep window** limits prewarms to a local time window. Both send `max_tokens: 0` pre-warm requests about five minutes before the 1-hour TTL expires. **Cache keep status** lists live tracked sessions across Pi processes through a temporary lease registry that stores only session IDs and cache timing. **Enable fast mode** adds Anthropic `speed: "fast"` plus the `fast-mode-2026-02-01` beta header for supported Opus models (`claude-opus-4-8`, `claude-opus-5`, and `claude-opus-5-5`).
|
|
89
|
-
|
|
90
|
-
### Claude quota window priming
|
|
91
|
-
|
|
92
|
-
Priming is off by default. When enabled from OpenCode's `/claude` menu with **Enable Prime**, it watches each OAuth account's 5-hour quota reset and sends one minimal `claude-haiku-4-5` request about one minute after a confirmed reset. This starts the next window without waiting for a normal prompt. Usage is measured from response accounting.
|
|
93
|
-
|
|
94
|
-
For an idle account with no cached reset time, one minimal request starts a usage window; the follow-up quota check learns its reset time. When several processes share an account store, each tries to create a claim file for that account and reset; creating the file is atomic, so exactly one process succeeds and sends the request.
|
|
95
|
-
|
|
96
|
-
Claim files live in a temporary directory, separated by account store and credential lineage. Cleanup removes files older than six hours. Ordinary token refresh keeps the lineage; a new sign-in starts a new one. Private runtime state stores cumulative priming usage counters, not a history of primed windows. Legacy claim files are not migrated. Without the old claim, the new scheduler can treat an already primed window as unclaimed and send one extra request.
|
|
97
|
-
|
|
98
|
-
Pi's menu shows **Prime quota window status** only; turn priming on or off with **Enable Prime** or **Disable Prime** in OpenCode's `/claude` menu.
|
|
99
|
-
|
|
100
|
-
## Anthropic-compatible proxy overrides
|
|
101
|
-
|
|
102
|
-
API-key fallback routes honor `ANTHROPIC_CUSTOM_HEADERS`, `ANTHROPIC_MODEL`, and `ANTHROPIC_DEFAULT_{SONNET,OPUS,HAIKU,FABLE}_MODEL`. These variables never alter OAuth requests. Custom headers may add proxy metadata but cannot replace route authentication, Anthropic protocol headers, body framing, or internal correlation headers; a configuration containing an invalid or protected header is ignored as a whole. Versioned provider base paths are preserved without duplicating `/v1`.
|
|
103
|
-
|
|
104
|
-
## Relay
|
|
105
|
-
|
|
106
|
-
The Pi package can use a user-owned Cloudflare relay over HTTP or WebSocket transport. When setup migrates Pi, it imports an existing `relay` block from Pi's old sidecar file (`~/.pi/agent/anthropic-auth.json`, or `PI_ANTHROPIC_AUTH_FILE`). The relay setup helper in the OpenCode package CLI provisions a Worker and saves its settings in OpenCode's local account pool; it does not configure Pi:
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=... bunx @cortexkit/opencode-anthropic-auth relay setup
|
|
110
|
-
```
|
|
30
|
+
## Documentation
|
|
111
31
|
|
|
112
|
-
|
|
32
|
+
- [Installation, configuration and manual registration](../../docs/installation.md#installation-and-configuration)
|
|
33
|
+
- [Features and host support](../../docs/features.md#features-and-host-support)
|
|
34
|
+
- [Models, thinking and fast mode](../../docs/models.md#models-thinking-and-fast-mode)
|
|
35
|
+
- [Accounts, routing and quota](../../docs/accounts-and-routing.md#accounts-routing-and-quota)
|
|
36
|
+
- [Settings and routing reference](../../docs/configuration.md#settings-and-routing-reference)
|
|
37
|
+
- [Vault custody](../../docs/vault.md#vault-custody)
|
|
38
|
+
- [Credential custody contract](../../docs/custody-state-machine.md#anthropic-credential-custody)
|
|
39
|
+
- [Commands and menus](../../docs/commands.md#commands-and-menus)
|
|
40
|
+
- [Cache, keepalive and quota priming](../../docs/cache.md#cache-keepalive-and-quota-priming)
|
|
41
|
+
- [Cache diagnostics](../../docs/cache-diagnostics.md#cache-diagnostics)
|
|
42
|
+
- [OpenCode 2](../../docs/opencode-2.md#opencode-2)
|
|
43
|
+
- [OpenCode sidebar and TUI preferences](../../docs/sidebar.md#opencode-sidebar-and-tui-preferences)
|
|
44
|
+
- [Environment variables](../../docs/environment.md#environment-variables)
|
|
45
|
+
- [Request rewriting and safety fallback](../../docs/request-rewriting.md#request-rewriting-and-safety-fallback)
|
|
46
|
+
- [Transports and request diagnostics](../../docs/transports.md#transports-and-request-diagnostics)
|
|
47
|
+
- [Troubleshooting](../../docs/troubleshooting.md#troubleshooting)
|
|
48
|
+
- [Development](../../docs/development.md#development)
|
|
49
|
+
- [Build modes](../../docs/build-modes.md#build-modes)
|
|
50
|
+
- [Upgrades, recovery and releases](../../docs/releases.md#upgrades-recovery-and-releases)
|
|
113
51
|
|
|
114
52
|
## License
|
|
115
53
|
|
|
116
|
-
MIT
|
|
54
|
+
[MIT](../../LICENSE)
|
package/dist/convert.js
CHANGED
|
@@ -284,8 +284,11 @@ function addEphemeralCacheControl(body) {
|
|
|
284
284
|
if (message?.role !== 'user')
|
|
285
285
|
continue;
|
|
286
286
|
const content = message.content;
|
|
287
|
-
if (
|
|
288
|
-
|
|
287
|
+
if (typeof content === 'string') {
|
|
288
|
+
message.content = [{ type: 'text', text: content }];
|
|
289
|
+
}
|
|
290
|
+
if (Array.isArray(message.content)) {
|
|
291
|
+
const lastBlock = message.content.at(-1);
|
|
289
292
|
if (lastBlock && typeof lastBlock === 'object') {
|
|
290
293
|
lastBlock.cache_control = { type: 'ephemeral' };
|
|
291
294
|
}
|
package/dist/effort-history.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type MidConversationEffortTransition } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { type AdaptiveEffort, type MidConversationEffortTransition } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
type SessionEntryLike = {
|
|
3
3
|
id?: unknown;
|
|
4
4
|
type?: unknown;
|
|
@@ -22,6 +22,11 @@ type SessionEntryLike = {
|
|
|
22
22
|
* `firstKeptEntryId`, then everything appended after the compaction.
|
|
23
23
|
*/
|
|
24
24
|
export declare function deriveContextEntries<Entry extends SessionEntryLike>(branch: readonly Entry[]): Entry[];
|
|
25
|
-
/**
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Build Pi's active, compaction-aware effort timeline for per-message effort.
|
|
27
|
+
* A thinking level without an adaptive effort (such as `off`) and a branch
|
|
28
|
+
* without any level change both count as `defaultEffort`, the effort the
|
|
29
|
+
* request model applies when no top-level effort is sent.
|
|
30
|
+
*/
|
|
31
|
+
export declare function collectPiEffortHistory(contextEntries: readonly SessionEntryLike[], fullBranch: readonly SessionEntryLike[], defaultEffort?: AdaptiveEffort): MidConversationEffortTransition[];
|
|
27
32
|
export {};
|
package/dist/effort-history.js
CHANGED
|
@@ -57,9 +57,14 @@ function appendTransition(transitions, afterAssistantMessages, effort) {
|
|
|
57
57
|
}
|
|
58
58
|
transitions.push({ afterAssistantMessages, effort });
|
|
59
59
|
}
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Build Pi's active, compaction-aware effort timeline for per-message effort.
|
|
62
|
+
* A thinking level without an adaptive effort (such as `off`) and a branch
|
|
63
|
+
* without any level change both count as `defaultEffort`, the effort the
|
|
64
|
+
* request model applies when no top-level effort is sent.
|
|
65
|
+
*/
|
|
66
|
+
export function collectPiEffortHistory(contextEntries, fullBranch, defaultEffort = 'high') {
|
|
67
|
+
let activeEffort = defaultEffort;
|
|
63
68
|
let assistantMessages = 0;
|
|
64
69
|
const transitions = [];
|
|
65
70
|
const first = contextEntries[0];
|
|
@@ -71,7 +76,8 @@ export function collectPiEffortHistory(contextEntries, fullBranch) {
|
|
|
71
76
|
const entry = fullBranch[index];
|
|
72
77
|
if (entry?.type !== 'thinking_level_change')
|
|
73
78
|
continue;
|
|
74
|
-
activeEffort =
|
|
79
|
+
activeEffort =
|
|
80
|
+
normalizeAdaptiveEffort(entry.thinkingLevel) ?? defaultEffort;
|
|
75
81
|
}
|
|
76
82
|
appendTransition(transitions, 0, activeEffort);
|
|
77
83
|
}
|
|
@@ -85,7 +91,8 @@ export function collectPiEffortHistory(contextEntries, fullBranch) {
|
|
|
85
91
|
branchIndex < compactionIndex;
|
|
86
92
|
if (entry.type === 'thinking_level_change') {
|
|
87
93
|
if (!retainedBeforeCompaction) {
|
|
88
|
-
activeEffort =
|
|
94
|
+
activeEffort =
|
|
95
|
+
normalizeAdaptiveEffort(entry.thinkingLevel) ?? defaultEffort;
|
|
89
96
|
}
|
|
90
97
|
continue;
|
|
91
98
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { 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, CLAUDE_HAIKU_5_5_CONTEXT_WINDOW, CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING, CLAUDE_HAIKU_5_5_LONG_CONTEXT_THRESHOLD, CLAUDE_HAIKU_5_5_MAX_OUTPUT_TOKENS, CLAUDE_HAIKU_5_5_MODEL_ID, CLAUDE_HAIKU_5_5_PRICING, CLAUDE_SONNET_5_5_CONTEXT_WINDOW, CLAUDE_SONNET_5_5_MAX_OUTPUT_TOKENS, CLAUDE_SONNET_5_5_MODEL_ID, CLAUDE_SONNET_5_5_PRICING, isClaudeFableOrMythos51Model, readNativeEnrollmentMode, } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { 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, CLAUDE_HAIKU_5_5_CONTEXT_WINDOW, CLAUDE_HAIKU_5_5_LONG_CONTEXT_PRICING, CLAUDE_HAIKU_5_5_LONG_CONTEXT_THRESHOLD, CLAUDE_HAIKU_5_5_MAX_OUTPUT_TOKENS, CLAUDE_HAIKU_5_5_MODEL_ID, CLAUDE_HAIKU_5_5_PRICING, CLAUDE_SONNET_5_5_CONTEXT_WINDOW, CLAUDE_SONNET_5_5_MAX_OUTPUT_TOKENS, CLAUDE_SONNET_5_5_MODEL_ID, CLAUDE_SONNET_5_5_PRICING, defaultAdaptiveEffort, isClaudeFableOrMythos51Model, nativeAccountCanServe, readNativeEnrollmentMode, } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
import { registerCommands } from './commands.js';
|
|
3
3
|
import { collectPiEffortHistory, deriveContextEntries, } from './effort-history.js';
|
|
4
4
|
import { closePiNativeRuntime, getPiNativeRuntime, isNativeMigrationRequired, } from './native.js';
|
|
@@ -43,11 +43,12 @@ export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
|
|
|
43
43
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
44
44
|
if (!sessionId)
|
|
45
45
|
return;
|
|
46
|
-
// This hook
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
46
|
+
// This hook collects effort history for the per-message effort markers
|
|
47
|
+
// that core adds to Fable 5.1, Opus 5.5 and Sonnet 5.5 requests. A host
|
|
48
|
+
// whose session entries do not match what this reads must cost the
|
|
49
|
+
// session its transitions and nothing else: the handler runs before every
|
|
50
|
+
// turn, and an exception here surfaced as a per-turn extension error while
|
|
51
|
+
// collecting no effort history at all (issue #200).
|
|
51
52
|
//
|
|
52
53
|
// The catch stays quiet: `ExtensionAPI` carries no log surface on either
|
|
53
54
|
// host, and writing to stdout from a per-turn hook corrupts the host's
|
|
@@ -56,7 +57,7 @@ export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
|
|
|
56
57
|
let transitions;
|
|
57
58
|
try {
|
|
58
59
|
const branch = ctx.sessionManager.getBranch();
|
|
59
|
-
transitions = collectPiEffortHistory(deriveContextEntries(branch), branch);
|
|
60
|
+
transitions = collectPiEffortHistory(deriveContextEntries(branch), branch, defaultAdaptiveEffort(ctx.model?.id));
|
|
60
61
|
}
|
|
61
62
|
catch {
|
|
62
63
|
transitions = [];
|
|
@@ -251,9 +252,7 @@ export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
|
|
|
251
252
|
return true;
|
|
252
253
|
throw error;
|
|
253
254
|
}
|
|
254
|
-
return snapshot.accounts.some((account) => account.type === 'oauth' &&
|
|
255
|
-
account.enabled &&
|
|
256
|
-
(account.source !== 'vault' || account.state === 'active'));
|
|
255
|
+
return snapshot.accounts.some((account) => account.type === 'oauth' && nativeAccountCanServe(account));
|
|
257
256
|
};
|
|
258
257
|
const provider = {
|
|
259
258
|
id: 'anthropic',
|
package/dist/native-commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { authorize, buildClaudeQuotaSummary, buildPrimeAccountStatuses, DEFAULT_KILLSWITCH_THRESHOLDS, exchange, executeCacheKeepCommand, executePrimeCommand, formatEnrollmentStatus, formatNativeAccountState, formatOAuthAccountTier, getCache1hPersistentMode, getCacheKeepWindow, getPersistedLogLevel, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepAlways, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isPrimePersistentlyEnabled, setDumpEnabled, } from '@cortexkit/anthropic-auth-core';
|
|
2
|
+
import { authorize, buildClaudeQuotaSummary, buildPrimeAccountStatuses, DEFAULT_KILLSWITCH_THRESHOLDS, exchange, executeCacheKeepCommand, executePrimeCommand, formatEnrollmentStatus, formatNativeAccountState, formatOAuthAccountTier, getCache1hPersistentMode, getCacheKeepWindow, getPersistedLogLevel, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepAlways, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isPrimePersistentlyEnabled, nativeAccountCanServe, setDumpEnabled, } from '@cortexkit/anthropic-auth-core';
|
|
3
3
|
import { createPiCustodyCommands } from './custody.js';
|
|
4
4
|
import { getPiNativeRuntime } from './native.js';
|
|
5
5
|
import { getPiAccountStoragePath } from './paths.js';
|
|
@@ -174,7 +174,7 @@ export function createPiNativeCommands(storagePath = getPiAccountStoragePath())
|
|
|
174
174
|
case 'quota-refresh': {
|
|
175
175
|
const snapshot = await native.view();
|
|
176
176
|
for (const account of snapshot.accounts) {
|
|
177
|
-
if (!account
|
|
177
|
+
if (!nativeAccountCanServe(account) || account.type !== 'oauth')
|
|
178
178
|
continue;
|
|
179
179
|
await runtime.fetchQuota(account.id, fetchPiOAuth, options.signal);
|
|
180
180
|
await runtime.fetchProfile(account.id, fetchPiOAuth, options.signal);
|
|
@@ -299,7 +299,10 @@ export function createPiNativeCommands(storagePath = getPiAccountStoragePath())
|
|
|
299
299
|
case 'killswitch':
|
|
300
300
|
return `Killswitch: ${isKillswitchEnabled(storage) ? 'enabled' : 'disabled'}\n${JSON.stringify(storage.killswitch ?? {})}`;
|
|
301
301
|
case 'cache':
|
|
302
|
-
return
|
|
302
|
+
return [
|
|
303
|
+
`1-hour cache: ${isCache1hPersistentlyEnabled(storage) ? 'enabled' : 'disabled'}; mode: ${getCache1hPersistentMode(storage)}`,
|
|
304
|
+
`- Persisted: ${await runtime.getSettingsPath()}`,
|
|
305
|
+
].join('\n');
|
|
303
306
|
case 'cachekeep': {
|
|
304
307
|
const sessions = await getPiTrackedCacheKeepSessions();
|
|
305
308
|
return executeCacheKeepCommand({
|
|
@@ -316,11 +319,20 @@ export function createPiNativeCommands(storagePath = getPiAccountStoragePath())
|
|
|
316
319
|
});
|
|
317
320
|
}
|
|
318
321
|
case 'dump':
|
|
319
|
-
return
|
|
322
|
+
return [
|
|
323
|
+
`Request dumping: ${isDumpPersistentlyEnabled(storage) ? 'enabled' : 'disabled'}`,
|
|
324
|
+
`- Persisted: ${await runtime.getSettingsPath()}`,
|
|
325
|
+
].join('\n');
|
|
320
326
|
case 'logging':
|
|
321
|
-
return
|
|
327
|
+
return [
|
|
328
|
+
`Logging: ${getPersistedLogLevel(storage) ?? 'info'}`,
|
|
329
|
+
`- Persisted: ${await runtime.getSettingsPath()}`,
|
|
330
|
+
].join('\n');
|
|
322
331
|
case 'fast':
|
|
323
|
-
return
|
|
332
|
+
return [
|
|
333
|
+
`Fast mode: ${isFastModePersistentlyEnabled(storage) ? 'enabled' : 'disabled'}`,
|
|
334
|
+
`- Persisted: ${await runtime.getSettingsPath()}`,
|
|
335
|
+
].join('\n');
|
|
324
336
|
case 'prime':
|
|
325
337
|
return executePrimeCommand({
|
|
326
338
|
argumentsText: 'status',
|
package/dist/native.d.ts
CHANGED
package/dist/native.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
-
import { acquireNativeAccountRuntime, createNativeUsagePublisher, getAccountStatePath, getHostClaustrumEnrollmentPaths, isKillswitchEnabled, killswitchPassesPolicy, logger, nativeMigrationAuthorityPhase, nativeRuntimeOptionIdentity, quotaSnapshotHasStandardWindows, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, readNativeAccountsForRequest, readNativeMigrationJournal, resolveClaustrumConnectionPath, resolveNativePoolPaths, } from '@cortexkit/anthropic-auth-core';
|
|
2
|
+
import { acquireNativeAccountRuntime, createNativeUsagePublisher, getAccountStatePath, getHostClaustrumEnrollmentPaths, isKillswitchEnabled, killswitchPassesPolicy, logger, nativeAccountCanServe, nativeMigrationAuthorityPhase, nativeRuntimeOptionIdentity, quotaSnapshotHasStandardWindows, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, readNativeAccountsForRequest, readNativeMigrationJournal, resolveClaustrumConnectionPath, resolveLocalRoutingDestination, resolveNativePoolPaths, } from '@cortexkit/anthropic-auth-core';
|
|
3
3
|
import { getPiAccountStoragePath, getPiClaustrumConnectionOptions, getPiConfigDir, requirePiNativeHostAuth, } from './paths.js';
|
|
4
4
|
export function piAttemptAccessToken(attempt) {
|
|
5
5
|
return attempt.source === 'vault'
|
|
@@ -8,7 +8,7 @@ export function piAttemptAccessToken(attempt) {
|
|
|
8
8
|
}
|
|
9
9
|
function requestPolicy(routeId, modelId) {
|
|
10
10
|
return (account, current) => {
|
|
11
|
-
if (!account
|
|
11
|
+
if (!nativeAccountCanServe(account))
|
|
12
12
|
return { status: 'blocked', reason: 'account-disabled' };
|
|
13
13
|
if (account.source === 'local' && current.mode !== 'local')
|
|
14
14
|
return { status: 'blocked', reason: 'local-mode-unavailable' };
|
|
@@ -61,12 +61,14 @@ export const PI_VAULT_POLL_INTERVAL_MS = 2000;
|
|
|
61
61
|
export class PiNativeRuntime {
|
|
62
62
|
paths;
|
|
63
63
|
initialization;
|
|
64
|
+
env;
|
|
64
65
|
vaultWatch;
|
|
65
66
|
closed = false;
|
|
66
67
|
closing;
|
|
67
68
|
usagePublications = createNativeUsagePublisher('native-auth');
|
|
68
69
|
constructor(storagePath, options = {}) {
|
|
69
70
|
const env = { ...process.env };
|
|
71
|
+
this.env = env;
|
|
70
72
|
const poll = options.vaultPoll ?? { intervalMs: PI_VAULT_POLL_INTERVAL_MS };
|
|
71
73
|
const identity = getPiClaustrumConnectionOptions(storagePath).identity;
|
|
72
74
|
this.paths = resolveNativePoolPaths(storagePath);
|
|
@@ -124,10 +126,13 @@ export class PiNativeRuntime {
|
|
|
124
126
|
}
|
|
125
127
|
// Keep local session-to-account assignments at the exact file destination
|
|
126
128
|
// recorded by migration; guessing a new path would lose those assignments.
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
const paths = await this.paths;
|
|
130
|
+
const journal = await readNativeMigrationJournal(paths);
|
|
131
|
+
if (!journal || !nativeMigrationAuthorityPhase(journal))
|
|
132
|
+
throw new Error('Pi native routing requires committed offline migration');
|
|
133
|
+
if (journal.kind === 'empty-init')
|
|
134
|
+
return { path: resolveLocalRoutingDestination('pi', paths, this.env) };
|
|
135
|
+
if (!journal.routingPaths.destination)
|
|
131
136
|
throw new Error('Pi native routing requires committed offline migration');
|
|
132
137
|
return { path: journal.routingPaths.destination };
|
|
133
138
|
}
|
|
@@ -164,7 +169,7 @@ export class PiNativeRuntime {
|
|
|
164
169
|
const runtime = await this.service();
|
|
165
170
|
const snapshot = await runtime.read();
|
|
166
171
|
const account = snapshot.accounts.find((candidate) => candidate.id === routeId);
|
|
167
|
-
if (!account
|
|
172
|
+
if (!nativeAccountCanServe(account) || account?.type !== 'oauth')
|
|
168
173
|
throw new Error('Pi native OAuth route is unavailable');
|
|
169
174
|
const policy = requestPolicy(routeId, modelId);
|
|
170
175
|
if (policy(account, snapshot).status === 'blocked')
|
|
@@ -264,7 +269,10 @@ export class PiNativeRuntime {
|
|
|
264
269
|
this.vaultWatch?.close();
|
|
265
270
|
this.closing ??= (async () => {
|
|
266
271
|
await this.usagePublications.drain();
|
|
267
|
-
await this.initialization.then((runtime) =>
|
|
272
|
+
await this.initialization.then(async (runtime) => {
|
|
273
|
+
await runtime.drainQuotaPublications();
|
|
274
|
+
runtime.close();
|
|
275
|
+
}, () => { });
|
|
268
276
|
})();
|
|
269
277
|
return this.closing;
|
|
270
278
|
}
|
package/dist/stream.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyClaudeCodeHeaders, applyCustomHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CacheKeepSessionRegistry, createProviderErrorResponseScope, createSseFramer, createStickyNoRouteResponse, decideStickyQuotaFailure, decodeSseFrame, dumpDirectRequest, FAST_MODE_BETA, getCache1hPersistentMode, getClaudeCodeIdentityForVerifiedAccount, getDefaultCacheKeepRegistryDirectory, getFallbackReauthLabels, getQuotaCheckIntervalMs, getRoutingMode, getScopedQuotaWindowForModel, getThinkingPrefixMismatchBehavior, hasThinkingBindingControls, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isNativeTransientAccountError, isOAuthAccount, isPermanentRefreshError, isValidApiBaseURL, killswitchPassesPolicy, logger, MID_CONVERSATION_OUTPUT_CONFIG_BETA, mergeAnthropicBetas, NativeVaultNoAccountError, parseSseFrame, QuotaManager, quotaBackoffActive, quotaSnapshotHasStandardWindows, quotaSnapshotModelScopeIsExhausted, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, STICKY_ROUTING_MAIN_ACCOUNT_ID, StickySessionRouter, sendViaRelay, setDumpEnabled, shouldFallbackStatus, stickyQuotaSnapshotIsFresh, stickyRetryAfterWithJitter, stickyRouteFamilyForModel, THINKING_BINDING_CONTROLS_BETA, usesMidConversationOutputConfig, } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { applyClaudeCodeHeaders, applyCustomHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CacheKeepSessionRegistry, createProviderErrorResponseScope, createSseFramer, createStickyNoRouteResponse, decideStickyQuotaFailure, decodeSseFrame, dumpDirectRequest, FAST_MODE_BETA, getCache1hPersistentMode, getClaudeCodeIdentityForVerifiedAccount, getDefaultCacheKeepRegistryDirectory, getFallbackReauthLabels, getQuotaCheckIntervalMs, getRoutingMode, getScopedQuotaWindowForModel, getThinkingPrefixMismatchBehavior, hasThinkingBindingControls, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isNativeTransientAccountError, isOAuthAccount, isPermanentRefreshError, isValidApiBaseURL, killswitchPassesPolicy, logger, MID_CONVERSATION_OUTPUT_CONFIG_BETA, mergeAnthropicBetas, NativeVaultNoAccountError, nativeAccountCanServe, parseSseFrame, QuotaManager, quotaBackoffActive, quotaSnapshotHasStandardWindows, quotaSnapshotModelScopeIsExhausted, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, STICKY_ROUTING_MAIN_ACCOUNT_ID, StickySessionRouter, sendViaRelay, setDumpEnabled, shouldFallbackStatus, stickyQuotaSnapshotIsFresh, stickyRetryAfterWithJitter, stickyRouteFamilyForModel, THINKING_BINDING_CONTROLS_BETA, 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 { getPiNativeRuntime, piAttemptAccessToken, } from './native.js';
|
|
@@ -59,9 +59,9 @@ function getPiRoutingServices(storagePath, snapshot) {
|
|
|
59
59
|
const usable = [];
|
|
60
60
|
for (const account of current.accounts) {
|
|
61
61
|
const view = accounts.get(account.id);
|
|
62
|
-
if (!isOAuthAccount(account) ||
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
if (!isOAuthAccount(account) ||
|
|
63
|
+
!nativeAccountCanServe(view) ||
|
|
64
|
+
view?.type !== 'oauth')
|
|
65
65
|
continue;
|
|
66
66
|
if (isPermanentRefreshError(view.lastRefreshError) ||
|
|
67
67
|
(view.source === 'local' &&
|
|
@@ -117,6 +117,33 @@ function getPiCacheKeepRegistry() {
|
|
|
117
117
|
}
|
|
118
118
|
return cacheKeepRegistry;
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Sets the Claude Code headers for a Pi OAuth request to Anthropic, with the
|
|
122
|
+
* betas this body needs. Model requests and CacheKeep prewarms both call it:
|
|
123
|
+
* a prewarm replays a model request's body, and Anthropic needs the same
|
|
124
|
+
* betas to accept it (for example, the effort beta for the empty
|
|
125
|
+
* `role: "system"` effort markers in a Fable 5.1 body). `leadingBetas` are
|
|
126
|
+
* placed before the body's betas.
|
|
127
|
+
*/
|
|
128
|
+
function applyPiOAuthRequestHeaders(headers, accessToken, options) {
|
|
129
|
+
const { body } = options;
|
|
130
|
+
applyClaudeCodeHeaders(headers, accessToken, {
|
|
131
|
+
body,
|
|
132
|
+
identity: options.identity,
|
|
133
|
+
extraBetas: [
|
|
134
|
+
...(options.leadingBetas ?? []),
|
|
135
|
+
...(hasThinkingBindingControls(body)
|
|
136
|
+
? [THINKING_BINDING_CONTROLS_BETA]
|
|
137
|
+
: []),
|
|
138
|
+
...(usesMidConversationOutputConfig(body)
|
|
139
|
+
? [MID_CONVERSATION_OUTPUT_CONFIG_BETA]
|
|
140
|
+
: []),
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
if (body.speed === 'fast')
|
|
144
|
+
headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [FAST_MODE_BETA]));
|
|
145
|
+
return headers;
|
|
146
|
+
}
|
|
120
147
|
const nativePrewarmAttempts = new Map();
|
|
121
148
|
const cacheKeepManager = new CacheKeepManager({
|
|
122
149
|
fetchImpl: fetchPiOAuth,
|
|
@@ -136,18 +163,11 @@ const cacheKeepManager = new CacheKeepManager({
|
|
|
136
163
|
? (current.policyStorage.mainAccountId ?? id)
|
|
137
164
|
: id, served.accountIdentity);
|
|
138
165
|
headers.delete('anthropic-beta');
|
|
139
|
-
|
|
166
|
+
applyPiOAuthRequestHeaders(headers, piAttemptAccessToken(served), {
|
|
140
167
|
body,
|
|
141
168
|
identity,
|
|
142
|
-
|
|
143
|
-
CACHE_KEEP_EXTENDED_TTL_BETA,
|
|
144
|
-
...(hasThinkingBindingControls(body)
|
|
145
|
-
? [THINKING_BINDING_CONTROLS_BETA]
|
|
146
|
-
: []),
|
|
147
|
-
],
|
|
169
|
+
leadingBetas: [CACHE_KEEP_EXTENDED_TTL_BETA],
|
|
148
170
|
});
|
|
149
|
-
if (body.speed === 'fast')
|
|
150
|
-
headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [FAST_MODE_BETA]));
|
|
151
171
|
nativePrewarmAttempts.set(attempt.id, { runtime, served, reported: false });
|
|
152
172
|
return headers;
|
|
153
173
|
},
|
|
@@ -343,20 +363,7 @@ async function sendAnthropicRequest(options) {
|
|
|
343
363
|
const fastMode = body.speed === 'fast';
|
|
344
364
|
const headers = options.apiAccount
|
|
345
365
|
? new Headers()
|
|
346
|
-
:
|
|
347
|
-
body,
|
|
348
|
-
identity,
|
|
349
|
-
extraBetas: [
|
|
350
|
-
...(hasThinkingBindingControls(body)
|
|
351
|
-
? [THINKING_BINDING_CONTROLS_BETA]
|
|
352
|
-
: []),
|
|
353
|
-
...(usesMidConversationOutputConfig(body)
|
|
354
|
-
? [MID_CONVERSATION_OUTPUT_CONFIG_BETA]
|
|
355
|
-
: []),
|
|
356
|
-
],
|
|
357
|
-
});
|
|
358
|
-
if (!options.apiAccount && fastMode)
|
|
359
|
-
headers.set('anthropic-beta', mergeAnthropicBetas(headers.get('anthropic-beta'), [FAST_MODE_BETA]));
|
|
366
|
+
: applyPiOAuthRequestHeaders(new Headers(), '', { body, identity });
|
|
360
367
|
const relayAffinity = options.streamOptions?.sessionId ?? null;
|
|
361
368
|
let input = options.apiAccount
|
|
362
369
|
? new URL('https://api.anthropic.com/v1/messages')
|
|
@@ -471,6 +478,11 @@ async function sendAnthropicRequest(options) {
|
|
|
471
478
|
}
|
|
472
479
|
if (attempt)
|
|
473
480
|
await report(attempt, response.status, 'direct');
|
|
481
|
+
// The vault has been told about the 401 above. If the request was
|
|
482
|
+
// cancelled after the 401 arrived (before or while the vault was asked
|
|
483
|
+
// for a newer credential), end it as cancelled here.
|
|
484
|
+
if (attempt && response.status === 401)
|
|
485
|
+
await endIfCancelled(response, init.signal);
|
|
474
486
|
await dumpDirectRequest({
|
|
475
487
|
affinity: relayAffinity,
|
|
476
488
|
route: options.route ??
|
|
@@ -565,10 +577,27 @@ async function sendAnthropicRequest(options) {
|
|
|
565
577
|
await Promise.all(relayReports);
|
|
566
578
|
}
|
|
567
579
|
}
|
|
580
|
+
// A relayed 401 is reported to the vault as soon as the relay returns it.
|
|
581
|
+
// If the request was cancelled since, end it as cancelled here.
|
|
582
|
+
if (relay401Attempt && response.status === 401)
|
|
583
|
+
await endIfCancelled(response, init.signal);
|
|
568
584
|
if (response.ok && lastTransport === 'relay' && relayAttempt)
|
|
569
585
|
markServed(relayAttempt);
|
|
570
586
|
return response;
|
|
571
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* If the request's signal is aborted, cancels the response body and throws the
|
|
590
|
+
* signal's abort error; otherwise does nothing. Call it before a rejected
|
|
591
|
+
* response is handed to account routing. Routing treats a returned 401 as a
|
|
592
|
+
* failure of the account and may move the session to another account (sticky
|
|
593
|
+
* migration); a request the user cancelled must end as cancelled instead.
|
|
594
|
+
*/
|
|
595
|
+
async function endIfCancelled(response, signal) {
|
|
596
|
+
if (!signal?.aborted)
|
|
597
|
+
return;
|
|
598
|
+
await response.body?.cancel().catch(() => { });
|
|
599
|
+
signal.throwIfAborted();
|
|
600
|
+
}
|
|
572
601
|
function quotaSnapshotIsExhausted(quota) {
|
|
573
602
|
return ['five_hour', 'seven_day'].some((key) => (quota?.[key]?.remainingPercent ?? 1) <= 0);
|
|
574
603
|
}
|
|
@@ -708,8 +737,7 @@ async function executeWithFallback(options) {
|
|
|
708
737
|
const nativeReady = (id) => {
|
|
709
738
|
const account = accountView(id);
|
|
710
739
|
return Boolean(account?.type === 'oauth' &&
|
|
711
|
-
account
|
|
712
|
-
(account.source !== 'vault' || account.state === 'active') &&
|
|
740
|
+
nativeAccountCanServe(account) &&
|
|
713
741
|
!isPermanentRefreshError(account.lastRefreshError) &&
|
|
714
742
|
(account.source !== 'local' ||
|
|
715
743
|
(account.lastRefreshError?.nextRetryAt ?? 0) <= Date.now()));
|
|
@@ -767,7 +795,7 @@ async function executeWithFallback(options) {
|
|
|
767
795
|
});
|
|
768
796
|
}
|
|
769
797
|
for (const [index, configured] of (storage?.accounts ?? []).entries()) {
|
|
770
|
-
if (configured
|
|
798
|
+
if (!nativeAccountCanServe(configured) || !isOAuthAccount(configured))
|
|
771
799
|
continue;
|
|
772
800
|
const account = usableById.get(configured.id) ?? configured;
|
|
773
801
|
if (!nativeReady(account.id))
|
|
@@ -883,7 +911,7 @@ async function executeWithFallback(options) {
|
|
|
883
911
|
});
|
|
884
912
|
const usableOAuthById = new Map(usableOAuth.map((account) => [account.id, account]));
|
|
885
913
|
for (const configured of storage.accounts) {
|
|
886
|
-
if (configured
|
|
914
|
+
if (!nativeAccountCanServe(configured))
|
|
887
915
|
continue;
|
|
888
916
|
let response;
|
|
889
917
|
if (isOAuthAccount(configured)) {
|
|
@@ -904,8 +932,8 @@ async function executeWithFallback(options) {
|
|
|
904
932
|
}
|
|
905
933
|
else if (routeOptions.includeApiRoutes) {
|
|
906
934
|
const account = accountView(configured.id);
|
|
907
|
-
if (!account
|
|
908
|
-
account
|
|
935
|
+
if (!nativeAccountCanServe(account) ||
|
|
936
|
+
account?.type !== 'api' ||
|
|
909
937
|
!isValidApiBaseURL(account.baseURL))
|
|
910
938
|
continue;
|
|
911
939
|
response = await sendAnthropicRequest({
|
|
@@ -1043,6 +1071,8 @@ async function executeWithFallback(options) {
|
|
|
1043
1071
|
migrate = decision.action === 'migrate';
|
|
1044
1072
|
}
|
|
1045
1073
|
if (migrate) {
|
|
1074
|
+
// A cancelled request must not move the session's account.
|
|
1075
|
+
await endIfCancelled(preflight instanceof Response ? preflight : response, options.streamOptions?.signal);
|
|
1046
1076
|
const failedRouteId = route.id;
|
|
1047
1077
|
routes = await buildStickyRoutes(options.model.id);
|
|
1048
1078
|
if (routes.candidates.some((candidate) => candidate.accountId !== failedRouteId)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/pi-anthropic-auth",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
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": "2.0.
|
|
39
|
+
"@cortexkit/anthropic-auth-core": "2.0.3"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@earendil-works/pi-ai": ">=0.86.1 <2",
|