@cortexkit/pi-anthropic-auth 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands.js +6 -1
- package/dist/convert.js +75 -27
- package/dist/stream.d.ts +12 -0
- package/dist/stream.js +42 -10
- package/package.json +2 -2
package/dist/commands.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildClaudeQuotaSummary, buildFallbackQuotaSummaries, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_ROUTING_COMMAND_NAME, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, executeRoutingCommand, getCache1hPersistentMode, getCacheKeepWindow, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, loadAccounts, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, parseRoutingCommandAction, setCache1hPersistentEnabled, setCache1hPersistentMode, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setDumpPersistentEnabled, setFastModePersistentEnabled, setRoutingMode, } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { buildClaudeQuotaSummary, buildFallbackQuotaSummaries, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_ROUTING_COMMAND_NAME, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, executeRoutingCommand, getCache1hPersistentMode, getCacheKeepWindow, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, loadAccounts, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, parseRoutingCommandAction, setCache1hPersistentEnabled, setCache1hPersistentMode, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setDumpEnabled, setDumpPersistentEnabled, setFastModePersistentEnabled, setRoutingMode, } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
import { getPiAccountStoragePath } from "./paths.js";
|
|
3
3
|
function notify(ctx, message, kind = 'info') {
|
|
4
4
|
ctx.ui.notify(message, kind);
|
|
@@ -68,9 +68,14 @@ export function registerCommands(pi) {
|
|
|
68
68
|
: enabled;
|
|
69
69
|
if (action.type === 'enable') {
|
|
70
70
|
await setDumpPersistentEnabled(true, path);
|
|
71
|
+
setDumpEnabled(true);
|
|
71
72
|
}
|
|
72
73
|
else if (action.type === 'disable') {
|
|
73
74
|
await setDumpPersistentEnabled(false, path);
|
|
75
|
+
setDumpEnabled(false);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
setDumpEnabled(enabled);
|
|
74
79
|
}
|
|
75
80
|
notify(ctx, executeDumpCommand({ argumentsText: args ?? '', enabled: nextEnabled }), action.type === 'usage' ? 'warning' : 'info');
|
|
76
81
|
},
|
package/dist/convert.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyClaudeCodeMetadata, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, isClaudeFableOrMythos5Model, isFastModeSupportedModel, orderClaudeCodeBody, signRequestBody, } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { applyClaudeCodeMetadata, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, isClaudeFableOrMythos5Model, isFastModeSupportedModel, isOpenAIReasoningSignature, orderClaudeCodeBody, signRequestBody, } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
const CLAUDE_CODE_TOOLS = new Map([
|
|
3
3
|
'Read',
|
|
4
4
|
'Write',
|
|
@@ -41,6 +41,37 @@ export function fromClaudeCodeToolName(name, tools) {
|
|
|
41
41
|
const lower = name.toLowerCase();
|
|
42
42
|
return tools?.find((tool) => tool.name.toLowerCase() === lower)?.name ?? name;
|
|
43
43
|
}
|
|
44
|
+
function getAssistantToolCallIds(message) {
|
|
45
|
+
const ids = new Set();
|
|
46
|
+
if (message?.role !== 'assistant' || !Array.isArray(message.content)) {
|
|
47
|
+
return ids;
|
|
48
|
+
}
|
|
49
|
+
for (const block of message.content) {
|
|
50
|
+
if (block.type === 'toolCall')
|
|
51
|
+
ids.add(sanitizeToolId(block.id));
|
|
52
|
+
}
|
|
53
|
+
return ids;
|
|
54
|
+
}
|
|
55
|
+
function getImmediateToolResultIds(messages, assistantIndex) {
|
|
56
|
+
const ids = new Set();
|
|
57
|
+
let index = assistantIndex + 1;
|
|
58
|
+
while (messages[index]?.role === 'toolResult') {
|
|
59
|
+
ids.add(sanitizeToolId(messages[index].toolCallId));
|
|
60
|
+
index += 1;
|
|
61
|
+
}
|
|
62
|
+
return ids;
|
|
63
|
+
}
|
|
64
|
+
function assistantToolCallsHaveImmediateResults(messages, assistantIndex) {
|
|
65
|
+
const toolCallIds = getAssistantToolCallIds(messages[assistantIndex]);
|
|
66
|
+
if (toolCallIds.size === 0)
|
|
67
|
+
return true;
|
|
68
|
+
const resultIds = getImmediateToolResultIds(messages, assistantIndex);
|
|
69
|
+
for (const id of toolCallIds) {
|
|
70
|
+
if (!resultIds.has(id))
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
44
75
|
function convertTextAndImages(content) {
|
|
45
76
|
if (!content.some((item) => item.type === 'image')) {
|
|
46
77
|
return content
|
|
@@ -91,6 +122,15 @@ function convertMessages(messages) {
|
|
|
91
122
|
continue;
|
|
92
123
|
}
|
|
93
124
|
if (message.role === 'assistant') {
|
|
125
|
+
const toolCallIds = getAssistantToolCallIds(message);
|
|
126
|
+
if (toolCallIds.size > 0 &&
|
|
127
|
+
!assistantToolCallsHaveImmediateResults(messages, index)) {
|
|
128
|
+
// Anthropic requires every tool_use block to be followed immediately by
|
|
129
|
+
// a matching tool_result message. Interrupted Pi runs can leave an
|
|
130
|
+
// aborted assistant message with a partial toolCall and no result; drop
|
|
131
|
+
// that incomplete assistant turn from the replayed history.
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
94
134
|
const blocks = [];
|
|
95
135
|
for (const block of message.content) {
|
|
96
136
|
if (block.type === 'text' && block.text.trim()) {
|
|
@@ -98,6 +138,12 @@ function convertMessages(messages) {
|
|
|
98
138
|
}
|
|
99
139
|
else if (block.type === 'thinking' && block.thinking.trim()) {
|
|
100
140
|
const thinking = block;
|
|
141
|
+
if (isOpenAIReasoningSignature(thinking.thinkingSignature)) {
|
|
142
|
+
// OpenAI reasoningEncryptedContent is also opaque encrypted state,
|
|
143
|
+
// but it is not an Anthropic thinking signature. Sending it as
|
|
144
|
+
// `thinking.signature` makes Anthropic reject the request.
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
101
147
|
if (thinking.thinkingSignature &&
|
|
102
148
|
!hasLoneSurrogate(thinking.thinking)) {
|
|
103
149
|
// Signed thinking blocks must be sent back verbatim — the signature
|
|
@@ -132,41 +178,43 @@ function convertMessages(messages) {
|
|
|
132
178
|
continue;
|
|
133
179
|
}
|
|
134
180
|
if (message.role === 'toolResult') {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
content = [{ type: 'text', text: 'Error' }];
|
|
181
|
+
const previous = messages[index - 1];
|
|
182
|
+
if (previous?.role !== 'assistant' ||
|
|
183
|
+
!assistantToolCallsHaveImmediateResults(messages, index - 1)) {
|
|
184
|
+
while (messages[index + 1]?.role === 'toolResult')
|
|
185
|
+
index += 1;
|
|
186
|
+
continue;
|
|
142
187
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let nextIndex = index + 1;
|
|
150
|
-
while (nextIndex < messages.length &&
|
|
151
|
-
messages[nextIndex]?.role === 'toolResult') {
|
|
152
|
-
const next = messages[nextIndex];
|
|
153
|
-
let nextContent = convertTextAndImages(next.content);
|
|
188
|
+
const expectedToolCallIds = getAssistantToolCallIds(previous);
|
|
189
|
+
const appendToolResult = (toolResult) => {
|
|
190
|
+
const toolUseId = sanitizeToolId(toolResult.toolCallId);
|
|
191
|
+
if (!expectedToolCallIds.delete(toolUseId))
|
|
192
|
+
return;
|
|
193
|
+
let content = convertTextAndImages(toolResult.content);
|
|
154
194
|
// Anthropic rejects tool_result with is_error=true but empty content
|
|
155
|
-
if (
|
|
156
|
-
(!
|
|
157
|
-
|
|
158
|
-
nextContent = [{ type: 'text', text: 'Error' }];
|
|
195
|
+
if (toolResult.isError &&
|
|
196
|
+
(!content || (Array.isArray(content) && content.length === 0))) {
|
|
197
|
+
content = [{ type: 'text', text: 'Error' }];
|
|
159
198
|
}
|
|
160
199
|
toolResults.push({
|
|
161
200
|
type: 'tool_result',
|
|
162
|
-
tool_use_id:
|
|
163
|
-
content:
|
|
164
|
-
is_error:
|
|
201
|
+
tool_use_id: toolUseId,
|
|
202
|
+
content: content,
|
|
203
|
+
is_error: toolResult.isError,
|
|
165
204
|
});
|
|
205
|
+
};
|
|
206
|
+
const toolResult = message;
|
|
207
|
+
const toolResults = [];
|
|
208
|
+
appendToolResult(toolResult);
|
|
209
|
+
let nextIndex = index + 1;
|
|
210
|
+
while (nextIndex < messages.length &&
|
|
211
|
+
messages[nextIndex]?.role === 'toolResult') {
|
|
212
|
+
appendToolResult(messages[nextIndex]);
|
|
166
213
|
nextIndex += 1;
|
|
167
214
|
}
|
|
168
215
|
index = nextIndex - 1;
|
|
169
|
-
|
|
216
|
+
if (toolResults.length)
|
|
217
|
+
result.push({ role: 'user', content: toolResults });
|
|
170
218
|
}
|
|
171
219
|
}
|
|
172
220
|
return result;
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { type ApiKeyAccount } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
import { type Api, type AssistantMessageEventStream, type Context, type Model, type SimpleStreamOptions } from '@earendil-works/pi-ai';
|
|
3
|
+
type AnthropicEvent = {
|
|
4
|
+
type?: string;
|
|
5
|
+
index?: number;
|
|
6
|
+
content_block?: Record<string, unknown>;
|
|
7
|
+
delta?: Record<string, unknown>;
|
|
8
|
+
message?: {
|
|
9
|
+
usage?: Record<string, number>;
|
|
10
|
+
};
|
|
11
|
+
usage?: Record<string, number>;
|
|
12
|
+
};
|
|
3
13
|
export declare function buildExplicitBaseMessagesUrl(baseURL: string): URL;
|
|
4
14
|
export declare function configureApiRouteHeaders(account: ApiKeyAccount, fastMode: boolean): Headers;
|
|
15
|
+
export declare function parseSse(response: Response): AsyncGenerator<AnthropicEvent>;
|
|
5
16
|
export declare function primaryResponseAllowsApiFallback(preflight: Response | string): boolean;
|
|
6
17
|
export declare function streamCortexKitAnthropic(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
|
|
18
|
+
export {};
|
package/dist/stream.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { applyClaudeCodeHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, FAST_MODE_BETA, FallbackAccountManager, getCache1hPersistentMode, getRelayConfig, getRoutingMode, isApiKeyAccount, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isFastModePersistentlyEnabled, isOAuthAccount, isValidApiBaseURL, loadAccounts, mergeAnthropicBetas, QuotaManager, resolveClaudeCodeIdentity, sendViaRelay, shouldFallbackStatus, } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { applyClaudeCodeHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, dumpDirectRequest, FAST_MODE_BETA, FallbackAccountManager, getCache1hPersistentMode, getRelayConfig, getRoutingMode, isApiKeyAccount, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isOAuthAccount, isValidApiBaseURL, loadAccounts, mergeAnthropicBetas, QuotaManager, resolveClaudeCodeIdentity, sendViaRelay, setDumpEnabled, shouldFallbackStatus, } 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";
|
|
5
|
+
function errorText(error) {
|
|
6
|
+
return error instanceof Error ? error.message : String(error);
|
|
7
|
+
}
|
|
5
8
|
const cacheKeepManager = new CacheKeepManager({
|
|
6
9
|
loadStorage: () => loadAccounts(getPiAccountStoragePath()),
|
|
7
10
|
prepareHeaders: async (headers, target) => {
|
|
@@ -100,20 +103,17 @@ export function configureApiRouteHeaders(account, fastMode) {
|
|
|
100
103
|
}
|
|
101
104
|
return headers;
|
|
102
105
|
}
|
|
103
|
-
async function* parseSse(response) {
|
|
106
|
+
export async function* parseSse(response) {
|
|
104
107
|
if (!response.body)
|
|
105
108
|
return;
|
|
106
109
|
const reader = response.body.getReader();
|
|
107
110
|
const decoder = new TextDecoder();
|
|
108
111
|
let buffer = '';
|
|
109
|
-
let completed = false;
|
|
110
112
|
try {
|
|
111
113
|
while (true) {
|
|
112
114
|
const { done, value } = await reader.read();
|
|
113
|
-
if (done)
|
|
114
|
-
completed = true;
|
|
115
|
+
if (done)
|
|
115
116
|
break;
|
|
116
|
-
}
|
|
117
117
|
buffer += decoder.decode(value, { stream: true });
|
|
118
118
|
let boundary = buffer.indexOf('\n\n');
|
|
119
119
|
while (boundary !== -1) {
|
|
@@ -132,13 +132,18 @@ async function* parseSse(response) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
finally {
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
// Do not cancel the reader on early abandon. `firstStreamingError()` peeks
|
|
136
|
+
// the first SSE event from a `response.clone()` and then abandons this
|
|
137
|
+
// generator; cancelling the cloned (tee'd) reader tears down the shared
|
|
138
|
+
// underlying body, so the real `parseSse(response)` that streams the reply
|
|
139
|
+
// reads zero events and the assistant message comes back empty. Releasing
|
|
140
|
+
// the lock is enough — the abandoned clone branch is garbage-collected.
|
|
137
141
|
reader.releaseLock();
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
async function sendAnthropicRequest(options) {
|
|
141
145
|
const storage = await loadAccounts(options.storagePath);
|
|
146
|
+
setDumpEnabled(isDumpPersistentlyEnabled(storage));
|
|
142
147
|
const identity = options.accessToken
|
|
143
148
|
? await resolveClaudeCodeIdentity(options.accessToken, options.model.id)
|
|
144
149
|
: undefined;
|
|
@@ -174,15 +179,42 @@ async function sendAnthropicRequest(options) {
|
|
|
174
179
|
storage,
|
|
175
180
|
cacheMode: isCacheKeepHybridActive(storage) ? 'hybrid' : 'disabled',
|
|
176
181
|
});
|
|
182
|
+
const directFetch = async () => {
|
|
183
|
+
try {
|
|
184
|
+
const response = await fetch(input, init);
|
|
185
|
+
await dumpDirectRequest({
|
|
186
|
+
affinity: relayAffinity,
|
|
187
|
+
route: options.apiAccount ? `api:${options.apiAccount.id}` : 'oauth',
|
|
188
|
+
status: response.status,
|
|
189
|
+
bodyText,
|
|
190
|
+
url: input.toString(),
|
|
191
|
+
method: init.method,
|
|
192
|
+
headers,
|
|
193
|
+
});
|
|
194
|
+
return response;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
await dumpDirectRequest({
|
|
198
|
+
affinity: relayAffinity,
|
|
199
|
+
route: options.apiAccount ? `api:${options.apiAccount.id}` : 'oauth',
|
|
200
|
+
error: errorText(error),
|
|
201
|
+
bodyText,
|
|
202
|
+
url: input.toString(),
|
|
203
|
+
method: init.method,
|
|
204
|
+
headers,
|
|
205
|
+
});
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
177
209
|
if (options.apiAccount)
|
|
178
|
-
return
|
|
210
|
+
return directFetch();
|
|
179
211
|
return sendViaRelay({
|
|
180
212
|
config: getRelayConfig(storage),
|
|
181
213
|
input,
|
|
182
214
|
init,
|
|
183
215
|
headers,
|
|
184
216
|
body: bodyText,
|
|
185
|
-
fallback:
|
|
217
|
+
fallback: directFetch,
|
|
186
218
|
affinity: relayAffinity,
|
|
187
219
|
});
|
|
188
220
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/pi-anthropic-auth",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
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.9.
|
|
39
|
+
"@cortexkit/anthropic-auth-core": "1.9.4"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@earendil-works/pi-ai": "*",
|