@cortexkit/pi-anthropic-auth 1.5.0 → 1.6.1
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/convert.js +46 -21
- package/package.json +3 -2
package/dist/convert.js
CHANGED
|
@@ -12,15 +12,27 @@ const CLAUDE_CODE_TOOLS = new Map([
|
|
|
12
12
|
'WebSearch',
|
|
13
13
|
].map((name) => [name.toLowerCase(), name]));
|
|
14
14
|
function sanitize(text) {
|
|
15
|
-
return text.replace(/[\uD800-\uDFFF]/
|
|
15
|
+
return text.replace(/[\uD800-\uDFFF]/gu, '\uFFFD');
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Detect lone (unpaired) UTF-16 surrogates. With the `u` flag the character
|
|
19
|
+
* class only matches surrogates that are NOT part of a valid pair, since valid
|
|
20
|
+
* pairs are folded into a single astral code point. Anthropic rejects payloads
|
|
21
|
+
* containing lone surrogates (invalid UTF-8).
|
|
22
|
+
*/
|
|
23
|
+
function hasLoneSurrogate(text) {
|
|
24
|
+
return /[\uD800-\uDFFF]/u.test(text);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sanitize a tool-call ID to match Anthropic's `^[a-zA-Z0-9_-]+$` pattern.
|
|
28
|
+
* Cross-provider IDs (e.g. OpenAI Codex `call_xxx|fc_xxx`) contain characters
|
|
29
|
+
* Anthropic rejects. Deterministic — same input always yields the same output.
|
|
30
|
+
*/
|
|
17
31
|
function sanitizeToolId(id) {
|
|
18
32
|
if (!id)
|
|
19
33
|
return 'tool_call_unknown';
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
return 'tool_call_unknown';
|
|
23
|
-
return sanitized.slice(0, 256);
|
|
34
|
+
const cleaned = id.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
35
|
+
return cleaned.length > 256 ? cleaned.slice(0, 256) : cleaned;
|
|
24
36
|
}
|
|
25
37
|
function toClaudeCodeToolName(name) {
|
|
26
38
|
return CLAUDE_CODE_TOOLS.get(name.toLowerCase()) ?? name;
|
|
@@ -86,14 +98,23 @@ function convertMessages(messages) {
|
|
|
86
98
|
}
|
|
87
99
|
else if (block.type === 'thinking' && block.thinking.trim()) {
|
|
88
100
|
const thinking = block;
|
|
89
|
-
if (thinking.thinkingSignature
|
|
101
|
+
if (thinking.thinkingSignature &&
|
|
102
|
+
!hasLoneSurrogate(thinking.thinking)) {
|
|
103
|
+
// Signed thinking blocks must be sent back verbatim — the signature
|
|
104
|
+
// is computed over the original text. Sanitizing would alter it and
|
|
105
|
+
// Anthropic rejects the block as "modified". Anthropic-origin
|
|
106
|
+
// thinking is valid UTF-8, so this is the normal path.
|
|
90
107
|
blocks.push({
|
|
91
108
|
type: 'thinking',
|
|
92
|
-
thinking:
|
|
109
|
+
thinking: thinking.thinking,
|
|
93
110
|
signature: thinking.thinkingSignature,
|
|
94
111
|
});
|
|
95
112
|
}
|
|
96
113
|
else {
|
|
114
|
+
// Either unsigned, or signed-but-contains a lone surrogate. In the
|
|
115
|
+
// latter case we cannot keep the signature: sanitizing breaks it and
|
|
116
|
+
// sending the raw lone surrogate is an invalid-UTF8 400. Drop the
|
|
117
|
+
// signature and downgrade to sanitized text.
|
|
97
118
|
blocks.push({ type: 'text', text: sanitize(thinking.thinking) });
|
|
98
119
|
}
|
|
99
120
|
}
|
|
@@ -113,34 +134,33 @@ function convertMessages(messages) {
|
|
|
113
134
|
if (message.role === 'toolResult') {
|
|
114
135
|
const toolResult = message;
|
|
115
136
|
const toolResults = [];
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
firstContentArr.push({ type: 'text', text: 'Error' });
|
|
137
|
+
let content = convertTextAndImages(toolResult.content);
|
|
138
|
+
// Anthropic rejects tool_result with is_error=true but empty content
|
|
139
|
+
if (toolResult.isError &&
|
|
140
|
+
(!content || (Array.isArray(content) && content.length === 0))) {
|
|
141
|
+
content = [{ type: 'text', text: 'Error' }];
|
|
122
142
|
}
|
|
123
143
|
toolResults.push({
|
|
124
144
|
type: 'tool_result',
|
|
125
145
|
tool_use_id: sanitizeToolId(toolResult.toolCallId),
|
|
126
|
-
content:
|
|
146
|
+
content: content,
|
|
127
147
|
is_error: toolResult.isError,
|
|
128
148
|
});
|
|
129
149
|
let nextIndex = index + 1;
|
|
130
150
|
while (nextIndex < messages.length &&
|
|
131
151
|
messages[nextIndex]?.role === 'toolResult') {
|
|
132
152
|
const next = messages[nextIndex];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
153
|
+
let nextContent = convertTextAndImages(next.content);
|
|
154
|
+
// Anthropic rejects tool_result with is_error=true but empty content
|
|
155
|
+
if (next.isError &&
|
|
156
|
+
(!nextContent ||
|
|
157
|
+
(Array.isArray(nextContent) && nextContent.length === 0))) {
|
|
158
|
+
nextContent = [{ type: 'text', text: 'Error' }];
|
|
139
159
|
}
|
|
140
160
|
toolResults.push({
|
|
141
161
|
type: 'tool_result',
|
|
142
162
|
tool_use_id: sanitizeToolId(next.toolCallId),
|
|
143
|
-
content:
|
|
163
|
+
content: nextContent,
|
|
144
164
|
is_error: next.isError,
|
|
145
165
|
});
|
|
146
166
|
nextIndex += 1;
|
|
@@ -215,6 +235,11 @@ function applyCacheMode(body, enabled, mode) {
|
|
|
215
235
|
}
|
|
216
236
|
export async function buildAnthropicRequest(modelId, context, options, cache, fastModeEnabled = false, identity) {
|
|
217
237
|
const messages = convertMessages(context.messages);
|
|
238
|
+
// Strip trailing assistant messages — Anthropic rejects prefill on some models
|
|
239
|
+
while (messages.length &&
|
|
240
|
+
messages[messages.length - 1]?.role === 'assistant') {
|
|
241
|
+
messages.pop();
|
|
242
|
+
}
|
|
218
243
|
const system = [
|
|
219
244
|
{
|
|
220
245
|
type: 'text',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/pi-anthropic-auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,10 +32,11 @@
|
|
|
32
32
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
33
33
|
"typecheck": "tsc",
|
|
34
34
|
"types": "tsc",
|
|
35
|
+
"test": "bun test src/tests",
|
|
35
36
|
"prepublishOnly": "bun run build"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"@cortexkit/anthropic-auth-core": "1.
|
|
39
|
+
"@cortexkit/anthropic-auth-core": "1.6.1"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"@earendil-works/pi-ai": "*",
|