@cortexkit/pi-anthropic-auth 1.21.0 → 1.23.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 +20 -3
- package/dist/commands.d.ts +2 -1
- package/dist/commands.js +45 -4
- package/dist/convert.d.ts +6 -2
- package/dist/convert.js +132 -29
- package/dist/custody.d.ts +17 -0
- package/dist/custody.js +66 -0
- package/dist/effort-history.d.ts +27 -0
- package/dist/effort-history.js +103 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +143 -19
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +42 -2
- package/dist/stream.d.ts +4 -2
- package/dist/stream.js +316 -47
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
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, getClaustrumMode, isClaudeFableOrMythos51Model, loadAccounts, refreshClaudeOAuthToken, } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
import { registerCommands } from './commands.js';
|
|
3
|
-
import {
|
|
3
|
+
import { createPiCustodyCommands, requirePiEnrollment } from './custody.js';
|
|
4
|
+
import { collectPiEffortHistory, deriveContextEntries, } from './effort-history.js';
|
|
5
|
+
import { getPiAccountStoragePath } from './paths.js';
|
|
6
|
+
import { closePiScopedRuntime, getPiScopedRuntime, streamCortexKitAnthropic, } from './stream.js';
|
|
7
|
+
async function assertLocalAuthentication() {
|
|
8
|
+
if (getClaustrumMode(await loadAccounts(getPiAccountStoragePath())) ===
|
|
9
|
+
'claustrum') {
|
|
10
|
+
throw new Error('Local Anthropic login and refresh are disabled while Claustrum custody is active');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
4
13
|
async function loginAnthropic(callbacks) {
|
|
14
|
+
await assertLocalAuthentication();
|
|
5
15
|
const auth = await authorize('max');
|
|
6
16
|
callbacks.onAuth({ url: auth.url });
|
|
7
17
|
const callback = await callbacks.onPrompt({
|
|
@@ -11,6 +21,7 @@ async function loginAnthropic(callbacks) {
|
|
|
11
21
|
if (result.type !== 'success') {
|
|
12
22
|
throw new Error('Anthropic OAuth exchange failed');
|
|
13
23
|
}
|
|
24
|
+
await assertLocalAuthentication();
|
|
14
25
|
return {
|
|
15
26
|
refresh: result.refresh,
|
|
16
27
|
access: result.access,
|
|
@@ -21,6 +32,7 @@ function textImageInput() {
|
|
|
21
32
|
return ['text', 'image'];
|
|
22
33
|
}
|
|
23
34
|
async function refreshAnthropicToken(credentials) {
|
|
35
|
+
await assertLocalAuthentication();
|
|
24
36
|
const refreshed = await refreshClaudeOAuthToken({
|
|
25
37
|
refreshToken: credentials.refresh,
|
|
26
38
|
});
|
|
@@ -30,27 +42,85 @@ async function refreshAnthropicToken(credentials) {
|
|
|
30
42
|
expires: refreshed.expires,
|
|
31
43
|
};
|
|
32
44
|
}
|
|
33
|
-
export default function cortexKitPiAnthropicAuth(pi) {
|
|
34
|
-
|
|
35
|
-
pi
|
|
45
|
+
export default async function cortexKitPiAnthropicAuth(pi, options = {}) {
|
|
46
|
+
const storagePath = getPiAccountStoragePath();
|
|
47
|
+
registerCommands(pi, createPiCustodyCommands({
|
|
48
|
+
storagePath,
|
|
49
|
+
reconfigure: configureProvider,
|
|
50
|
+
connect: options.connectScoped,
|
|
51
|
+
}));
|
|
52
|
+
const effortHistoryBySession = new Map();
|
|
53
|
+
pi.on('turn_start', async (_event, ctx) => {
|
|
54
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
55
|
+
if (!sessionId)
|
|
56
|
+
return;
|
|
57
|
+
// This hook only adds mid-conversation effort markers to Fable/Mythos 5.1
|
|
58
|
+
// requests. A host whose session entries do not match what this reads must
|
|
59
|
+
// cost the session its transitions and nothing else: the handler runs
|
|
60
|
+
// before every turn, and an exception here surfaced as a per-turn extension
|
|
61
|
+
// error while collecting no effort history at all (issue #200).
|
|
62
|
+
//
|
|
63
|
+
// The catch stays quiet: `ExtensionAPI` carries no log surface on either
|
|
64
|
+
// host, and writing to stdout from a per-turn hook corrupts the host's
|
|
65
|
+
// rendering — which is the same per-turn noise this fix removes. The
|
|
66
|
+
// degraded state is observable in the request: no effort markers.
|
|
67
|
+
let transitions;
|
|
68
|
+
try {
|
|
69
|
+
const branch = ctx.sessionManager.getBranch();
|
|
70
|
+
transitions = collectPiEffortHistory(deriveContextEntries(branch), branch);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
transitions = [];
|
|
74
|
+
}
|
|
75
|
+
effortHistoryBySession.delete(sessionId);
|
|
76
|
+
effortHistoryBySession.set(sessionId, transitions);
|
|
77
|
+
while (effortHistoryBySession.size > 128) {
|
|
78
|
+
const oldest = effortHistoryBySession.keys().next().value;
|
|
79
|
+
if (oldest)
|
|
80
|
+
effortHistoryBySession.delete(oldest);
|
|
81
|
+
else
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
pi.on('session_shutdown', async (_event, ctx) => {
|
|
86
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
87
|
+
if (sessionId)
|
|
88
|
+
effortHistoryBySession.delete(sessionId);
|
|
89
|
+
closePiScopedRuntime(storagePath);
|
|
90
|
+
});
|
|
91
|
+
const configuration = {
|
|
36
92
|
name: 'Anthropic (CortexKit OAuth)',
|
|
37
93
|
baseUrl: 'https://api.anthropic.com',
|
|
38
94
|
api: 'cortexkit-anthropic-messages',
|
|
39
95
|
models: [
|
|
40
|
-
...Object.values(CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS).map((model) =>
|
|
41
|
-
|
|
42
|
-
|
|
96
|
+
...Object.values(CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS).map((model) => {
|
|
97
|
+
const pricing = isClaudeFableOrMythos51Model(model.id)
|
|
98
|
+
? CLAUDE_FABLE_MYTHOS_5_1_PRICING
|
|
99
|
+
: CLAUDE_FABLE_MYTHOS_5_PRICING;
|
|
100
|
+
return {
|
|
101
|
+
id: model.id,
|
|
102
|
+
name: model.name,
|
|
103
|
+
reasoning: true,
|
|
104
|
+
input: textImageInput(),
|
|
105
|
+
cost: {
|
|
106
|
+
input: pricing.input,
|
|
107
|
+
output: pricing.output,
|
|
108
|
+
cacheRead: pricing.cacheRead,
|
|
109
|
+
cacheWrite: pricing.cacheWrite5m,
|
|
110
|
+
},
|
|
111
|
+
contextWindow: CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW,
|
|
112
|
+
maxTokens: CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS,
|
|
113
|
+
};
|
|
114
|
+
}),
|
|
115
|
+
{
|
|
116
|
+
id: 'claude-opus-5-5',
|
|
117
|
+
name: 'Claude Opus 5.5',
|
|
43
118
|
reasoning: true,
|
|
44
119
|
input: textImageInput(),
|
|
45
|
-
cost: {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
})),
|
|
120
|
+
cost: { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 8 },
|
|
121
|
+
contextWindow: 1_000_000,
|
|
122
|
+
maxTokens: 128_000,
|
|
123
|
+
},
|
|
54
124
|
{
|
|
55
125
|
id: 'claude-opus-5',
|
|
56
126
|
name: 'Claude Opus 5',
|
|
@@ -103,6 +173,60 @@ export default function cortexKitPiAnthropicAuth(pi) {
|
|
|
103
173
|
refreshToken: refreshAnthropicToken,
|
|
104
174
|
getApiKey: (credentials) => credentials.access,
|
|
105
175
|
},
|
|
106
|
-
streamSimple: streamCortexKitAnthropic,
|
|
107
|
-
|
|
176
|
+
streamSimple: (model, context, options) => streamCortexKitAnthropic(model, context, options, options?.sessionId
|
|
177
|
+
? effortHistoryBySession.get(options.sessionId)
|
|
178
|
+
: undefined),
|
|
179
|
+
};
|
|
180
|
+
async function configureProvider() {
|
|
181
|
+
if (getClaustrumMode(await loadAccounts(storagePath)) !== 'claustrum') {
|
|
182
|
+
closePiScopedRuntime(storagePath);
|
|
183
|
+
pi.registerProvider('anthropic', configuration);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const streamSimple = configuration.streamSimple;
|
|
187
|
+
if (!streamSimple)
|
|
188
|
+
throw new Error('Anthropic stream implementation is unavailable');
|
|
189
|
+
const configured = async () => {
|
|
190
|
+
if (getClaustrumMode(await loadAccounts(storagePath)) !== 'claustrum')
|
|
191
|
+
return false;
|
|
192
|
+
await requirePiEnrollment();
|
|
193
|
+
return true;
|
|
194
|
+
};
|
|
195
|
+
const provider = {
|
|
196
|
+
id: 'anthropic',
|
|
197
|
+
name: 'Anthropic (Claustrum)',
|
|
198
|
+
baseUrl: 'https://api.anthropic.com',
|
|
199
|
+
auth: {
|
|
200
|
+
// Native ambient auth avoids fake keys and local OAuth refresh. Pi refuses
|
|
201
|
+
// a leftover stored OAuth credential because this provider has no OAuth
|
|
202
|
+
// handler; setup must obtain consent before removing that local entry.
|
|
203
|
+
apiKey: {
|
|
204
|
+
name: 'Claustrum',
|
|
205
|
+
check: async () => (await configured())
|
|
206
|
+
? { type: 'api_key', source: 'Claustrum' }
|
|
207
|
+
: undefined,
|
|
208
|
+
resolve: async () => (await configured())
|
|
209
|
+
? { auth: {}, source: 'Claustrum' }
|
|
210
|
+
: undefined,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
getModels: () => (configuration.models ?? []).map((model) => ({
|
|
214
|
+
...model,
|
|
215
|
+
provider: 'anthropic',
|
|
216
|
+
api: model.api ?? 'cortexkit-anthropic-messages',
|
|
217
|
+
baseUrl: model.baseUrl ?? 'https://api.anthropic.com',
|
|
218
|
+
})),
|
|
219
|
+
// Preserve the legacy provider's simplified option surface for raw calls.
|
|
220
|
+
stream: (model, context, options) => streamSimple(model, context, options),
|
|
221
|
+
streamSimple,
|
|
222
|
+
};
|
|
223
|
+
pi.registerProvider(provider);
|
|
224
|
+
getPiScopedRuntime(storagePath, {
|
|
225
|
+
...(options.connectScoped && { connect: options.connectScoped }),
|
|
226
|
+
...(options.pollIntervalMs !== undefined && {
|
|
227
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
228
|
+
}),
|
|
229
|
+
}).start();
|
|
230
|
+
}
|
|
231
|
+
await configureProvider();
|
|
108
232
|
}
|
package/dist/paths.d.ts
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
export declare function getPiConfigDir(): string;
|
|
2
2
|
export declare function getPiAccountStoragePath(): string;
|
|
3
|
+
export declare function getPiClaustrumConnectionOptions(storagePath?: string): {
|
|
4
|
+
storagePath: string;
|
|
5
|
+
identity: {
|
|
6
|
+
project_root: string;
|
|
7
|
+
harness: string;
|
|
8
|
+
session: string;
|
|
9
|
+
};
|
|
10
|
+
connectionFile: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
/** Read-only metadata inspection; never create or rewrite Pi's credential store. */
|
|
13
|
+
export declare function hasPiLocalAnthropicOAuth(): Promise<boolean>;
|
package/dist/paths.js
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
1
2
|
import { homedir } from 'node:os';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
|
-
import { ACCOUNT_FILE_NAME } from '@cortexkit/anthropic-auth-core';
|
|
4
|
+
import { ACCOUNT_FILE_NAME, parseJsonRedacted, primeStorageFingerprint, } from '@cortexkit/anthropic-auth-core';
|
|
4
5
|
export function getPiConfigDir() {
|
|
5
|
-
return process.env.
|
|
6
|
+
return (process.env.PI_CODING_AGENT_DIR?.trim() ||
|
|
7
|
+
process.env.PI_AGENT_DIR?.trim() ||
|
|
8
|
+
join(homedir(), '.pi', 'agent'));
|
|
6
9
|
}
|
|
7
10
|
export function getPiAccountStoragePath() {
|
|
8
11
|
return (process.env.PI_ANTHROPIC_AUTH_FILE?.trim() ||
|
|
9
12
|
join(getPiConfigDir(), ACCOUNT_FILE_NAME));
|
|
10
13
|
}
|
|
14
|
+
export function getPiClaustrumConnectionOptions(storagePath = getPiAccountStoragePath()) {
|
|
15
|
+
return {
|
|
16
|
+
storagePath,
|
|
17
|
+
identity: {
|
|
18
|
+
project_root: process.cwd(),
|
|
19
|
+
harness: 'pi',
|
|
20
|
+
session: `store-${primeStorageFingerprint(storagePath)}`,
|
|
21
|
+
},
|
|
22
|
+
connectionFile: process.env.PI_ANTHROPIC_AUTH_CLAUSTRUM_CONNECTION_FILE?.trim() ||
|
|
23
|
+
process.env.CLAUSTRUM_SUBC_CONNECTION?.trim() ||
|
|
24
|
+
undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Read-only metadata inspection; never create or rewrite Pi's credential store. */
|
|
28
|
+
export async function hasPiLocalAnthropicOAuth() {
|
|
29
|
+
try {
|
|
30
|
+
const value = parseJsonRedacted(await readFile(join(getPiConfigDir(), 'auth.json'), 'utf8'));
|
|
31
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
32
|
+
throw new Error('Invalid authentication store');
|
|
33
|
+
const credential = value.anthropic;
|
|
34
|
+
if (credential === undefined)
|
|
35
|
+
return false;
|
|
36
|
+
if (!credential ||
|
|
37
|
+
typeof credential !== 'object' ||
|
|
38
|
+
Array.isArray(credential))
|
|
39
|
+
throw new Error('Invalid Anthropic authentication entry');
|
|
40
|
+
const type = credential.type;
|
|
41
|
+
if (type !== 'oauth' && type !== 'api_key')
|
|
42
|
+
throw new Error('Unknown Anthropic authentication entry');
|
|
43
|
+
return type === 'oauth';
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error.code === 'ENOENT')
|
|
47
|
+
return false;
|
|
48
|
+
throw new Error('Cannot safely inspect Pi authentication metadata');
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { type ApiKeyAccount } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { type ApiKeyAccount, ClaustrumScopedRuntime, type ClaustrumScopedRuntimeOptions, 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
|
+
export declare function getPiScopedRuntime(storagePath?: string, overrides?: Partial<Pick<ClaustrumScopedRuntimeOptions, 'connect' | 'pollIntervalMs' | 'setTimeoutImpl' | 'clearTimeoutImpl'>>): ClaustrumScopedRuntime;
|
|
4
|
+
export declare function closePiScopedRuntime(storagePath?: string): void;
|
|
3
5
|
export declare function clearPiStickyRoutingSession(storagePath: string, sessionId: string): Promise<void>;
|
|
4
6
|
export declare function getPiTrackedCacheKeepSessions(): Promise<import("@cortexkit/anthropic-auth-core").CacheKeepTrackedSession[]>;
|
|
5
7
|
type AnthropicEvent = {
|
|
@@ -16,5 +18,5 @@ export declare function buildExplicitBaseMessagesUrl(baseURL: string): URL;
|
|
|
16
18
|
export declare function configureApiRouteHeaders(account: ApiKeyAccount, fastMode: boolean): Headers;
|
|
17
19
|
export declare function parseSse(response: Response): AsyncGenerator<AnthropicEvent>;
|
|
18
20
|
export declare function primaryResponseAllowsApiFallback(preflight: Response | string): boolean;
|
|
19
|
-
export declare function streamCortexKitAnthropic(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
|
|
21
|
+
export declare function streamCortexKitAnthropic(model: Model<Api>, context: Context, options?: SimpleStreamOptions, effortTransitions?: readonly MidConversationEffortTransition[]): AssistantMessageEventStream;
|
|
20
22
|
export {};
|