@oh-my-pi/pi-ai 15.10.2 → 15.10.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/CHANGELOG.md +19 -0
- package/dist/types/providers/anthropic.d.ts +9 -4
- package/dist/types/providers/transform-messages.d.ts +0 -1
- package/package.json +2 -2
- package/src/providers/anthropic.ts +107 -122
- package/src/providers/transform-messages.ts +1 -22
- package/src/prompts/turn-aborted-guidance.md +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.10.4] - 2026-06-08
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added `anthropic-client-platform` (`desktop_app`) and `anthropic-client-version` (`1.11187.4`) headers to the Anthropic request fingerprint for OAuth sessions
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Changed non-built-in tool names sent to Anthropic from `proxy_` prefixing to `_` prefixing (for example `bash` to `_bash`) while built-in tool names remain unchanged
|
|
13
|
+
- Updated the Anthropic OAuth stealth fingerprint to track Claude Code 2.1.165: `claudeCodeVersion` bumped to `2.1.165` (flows into both the `cc_version` billing header and the `claude-cli/<version>` user-agent), `claudeCodeSystemInstruction` changed to `"You are a Claude agent, built on Anthropic's Claude Agent SDK."`, and the billing-header `cc_entrypoint` changed from `cli` to `local-agent`.
|
|
14
|
+
- Clamped the Anthropic request `max_tokens` to `Math.min(CLAUDE_CODE_MAX_OUTPUT_TOKENS, options.maxTokens || model.maxTokens)` (64k) so OAuth requests match Claude Code's requested output cap instead of sending the model's full ceiling (e.g. 128k for Opus 4.8).
|
|
15
|
+
|
|
16
|
+
## [15.10.3] - 2026-06-08
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
|
|
20
|
+
- Removed the synthetic `<turn-aborted>` developer guidance note that `transformMessages` injected after an aborted/errored assistant turn (and its `turn-aborted-guidance.md` prompt). The per-call synthetic `"aborted"` tool results already tell the model the turn's tools were terminated, so the extra "verify current state before retrying" note was redundant — and it biased the model toward second-guessing a deliberate user interrupt when the turn was resumed.
|
|
21
|
+
- Removed the legacy Anthropic first-user-message skip for `<system-reminder>` blocks now that synthetic reminders no longer travel as user messages.
|
|
22
|
+
|
|
23
|
+
|
|
5
24
|
## [15.10.2] - 2026-06-08
|
|
6
25
|
### Added
|
|
7
26
|
|
|
@@ -26,9 +26,12 @@ type AnthropicCacheControl = NonNullable<TextBlockParam["cache_control"]>;
|
|
|
26
26
|
*/
|
|
27
27
|
export declare function clearAnthropicFastModeFallback(providerSessionState: Map<string, ProviderSessionState> | undefined): void;
|
|
28
28
|
export declare function isAnthropicFastModeUnsupportedError(error: unknown): boolean;
|
|
29
|
-
export declare const claudeCodeVersion = "2.1.
|
|
29
|
+
export declare const claudeCodeVersion = "2.1.165";
|
|
30
|
+
export declare const claudeAgentSdkVersion = "0.3.165";
|
|
31
|
+
export declare const claudeClientVersion = "1.11187.4";
|
|
30
32
|
export declare const claudeToolPrefix: string;
|
|
31
|
-
export declare const claudeCodeSystemInstruction = "You are Claude
|
|
33
|
+
export declare const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
|
|
34
|
+
export declare const CLAUDE_CODE_MAX_OUTPUT_TOKENS = 64000;
|
|
32
35
|
export declare function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}`;
|
|
33
36
|
export declare function mapStainlessArch(arch: string): "x64" | "arm64" | "x86" | `other::${string}`;
|
|
34
37
|
export declare const claudeCodeHeaders: {
|
|
@@ -40,11 +43,13 @@ export declare const claudeCodeHeaders: {
|
|
|
40
43
|
"X-Stainless-Arch": "arm64" | "x64" | "x86" | `other::${string}`;
|
|
41
44
|
"X-Stainless-OS": "FreeBSD" | "Linux" | "MacOS" | "Windows" | `Other::${string}`;
|
|
42
45
|
"X-Stainless-Timeout": string;
|
|
46
|
+
"anthropic-client-platform": string;
|
|
47
|
+
"anthropic-client-version": string;
|
|
43
48
|
};
|
|
44
49
|
export declare function isClaudeCloakingUserId(userId: string): boolean;
|
|
45
50
|
export declare function generateClaudeCloakingUserId(): string;
|
|
46
|
-
export declare const applyClaudeToolPrefix: (name: string
|
|
47
|
-
export declare const stripClaudeToolPrefix: (name: string
|
|
51
|
+
export declare const applyClaudeToolPrefix: (name: string) => string;
|
|
52
|
+
export declare const stripClaudeToolPrefix: (name: string) => string;
|
|
48
53
|
export type AnthropicEffort = "low" | "medium" | "high" | "xhigh" | "max";
|
|
49
54
|
export type AnthropicThinkingDisplay = "summarized" | "omitted";
|
|
50
55
|
export interface AnthropicOptions extends StreamOptions {
|
|
@@ -7,6 +7,5 @@ import type { Api, AssistantMessage, Message, Model } from "../types";
|
|
|
7
7
|
* For aborted/errored turns, this function:
|
|
8
8
|
* - Preserves tool call structure (unlike converting to text summaries)
|
|
9
9
|
* - Injects synthetic "aborted" tool results
|
|
10
|
-
* - Adds a <turn-aborted> guidance marker for the model
|
|
11
10
|
*/
|
|
12
11
|
export declare function transformMessages<TApi extends Api>(messages: Message[], model: Model<TApi>, normalizeToolCallId?: (id: string, model: Model<TApi>, source: AssistantMessage) => string, maxNormalizedToolCallIdLength?: number, duplicateToolCallIdSuffixPrefix?: string): Message[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "15.10.
|
|
4
|
+
"version": "15.10.4",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@bufbuild/protobuf": "^2.12.0",
|
|
42
|
-
"@oh-my-pi/pi-utils": "15.10.
|
|
42
|
+
"@oh-my-pi/pi-utils": "15.10.4",
|
|
43
43
|
"openai": "^6.39.0",
|
|
44
44
|
"partial-json": "^0.1.7",
|
|
45
45
|
"zod": "4.4.3"
|
|
@@ -196,9 +196,9 @@ const sharedHeaders = {
|
|
|
196
196
|
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
197
197
|
Connection: "keep-alive",
|
|
198
198
|
"Content-Type": "application/json",
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
199
|
+
"anthropic-version": "2023-06-01",
|
|
200
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
201
|
+
"x-app": "cli",
|
|
202
202
|
};
|
|
203
203
|
|
|
204
204
|
export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<string, string> {
|
|
@@ -216,7 +216,7 @@ export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<s
|
|
|
216
216
|
...modelHeaders,
|
|
217
217
|
Accept: acceptHeader,
|
|
218
218
|
...sharedHeaders,
|
|
219
|
-
"
|
|
219
|
+
"anthropic-beta": betaHeader,
|
|
220
220
|
"cf-aig-authorization": `Bearer ${options.apiKey}`,
|
|
221
221
|
};
|
|
222
222
|
}
|
|
@@ -225,14 +225,14 @@ export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<s
|
|
|
225
225
|
const incomingUserAgent = getHeaderCaseInsensitive(options.modelHeaders, "User-Agent");
|
|
226
226
|
const userAgent = isClaudeCodeClientUserAgent(incomingUserAgent)
|
|
227
227
|
? incomingUserAgent
|
|
228
|
-
: `claude-cli/${claudeCodeVersion} (external,
|
|
228
|
+
: `claude-cli/${claudeCodeVersion} (external, local-agent, agent-sdk/${claudeAgentSdkVersion})`;
|
|
229
229
|
return {
|
|
230
230
|
...modelHeaders,
|
|
231
231
|
...claudeCodeHeaders,
|
|
232
232
|
Accept: acceptHeader,
|
|
233
233
|
Authorization: `Bearer ${options.apiKey}`,
|
|
234
234
|
...sharedHeaders,
|
|
235
|
-
"
|
|
235
|
+
"anthropic-beta": betaHeader,
|
|
236
236
|
...(options.claudeCodeSessionId ? { "X-Claude-Code-Session-Id": options.claudeCodeSessionId } : {}),
|
|
237
237
|
"x-client-request-id": nodeCrypto.randomUUID(),
|
|
238
238
|
"User-Agent": userAgent,
|
|
@@ -243,14 +243,14 @@ export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<s
|
|
|
243
243
|
Accept: acceptHeader,
|
|
244
244
|
Authorization: `Bearer ${options.apiKey}`,
|
|
245
245
|
...sharedHeaders,
|
|
246
|
-
"
|
|
246
|
+
"anthropic-beta": betaHeader,
|
|
247
247
|
};
|
|
248
248
|
} else {
|
|
249
249
|
return {
|
|
250
250
|
...modelHeaders,
|
|
251
251
|
Accept: acceptHeader,
|
|
252
252
|
...sharedHeaders,
|
|
253
|
-
"
|
|
253
|
+
"anthropic-beta": betaHeader,
|
|
254
254
|
"X-Api-Key": options.apiKey,
|
|
255
255
|
};
|
|
256
256
|
}
|
|
@@ -260,12 +260,6 @@ type AnthropicCacheControl = NonNullable<TextBlockParam["cache_control"]>;
|
|
|
260
260
|
|
|
261
261
|
type AnthropicOutputConfig = NonNullable<MessageCreateParamsStreaming["output_config"]>;
|
|
262
262
|
|
|
263
|
-
function getAnthropicOutputConfig(params: MessageCreateParamsStreaming): AnthropicOutputConfig {
|
|
264
|
-
const outputConfig = params.output_config ?? {};
|
|
265
|
-
params.output_config = outputConfig;
|
|
266
|
-
return outputConfig;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
263
|
const ANTHROPIC_STOP_SEQUENCES_MAX = 4;
|
|
270
264
|
let warnedStopSequencesTrim = false;
|
|
271
265
|
|
|
@@ -398,9 +392,14 @@ function getCacheControl(
|
|
|
398
392
|
}
|
|
399
393
|
|
|
400
394
|
// Stealth mode: mimic Claude Code's request fingerprint.
|
|
401
|
-
export const claudeCodeVersion = "2.1.
|
|
402
|
-
export const
|
|
403
|
-
export const
|
|
395
|
+
export const claudeCodeVersion = "2.1.165";
|
|
396
|
+
export const claudeAgentSdkVersion = "0.3.165";
|
|
397
|
+
export const claudeClientVersion = "1.11187.4";
|
|
398
|
+
export const claudeToolPrefix: string = "_";
|
|
399
|
+
export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
|
|
400
|
+
// Claude Code caps requested output at 64k tokens even when the model ceiling is
|
|
401
|
+
// higher (e.g. Opus 4.8 supports 128k); clamp to match the wire fingerprint.
|
|
402
|
+
export const CLAUDE_CODE_MAX_OUTPUT_TOKENS = 64000;
|
|
404
403
|
|
|
405
404
|
export function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}` {
|
|
406
405
|
switch (platform.toLowerCase()) {
|
|
@@ -443,7 +442,9 @@ export const claudeCodeHeaders = {
|
|
|
443
442
|
"X-Stainless-Lang": "js",
|
|
444
443
|
"X-Stainless-Arch": mapStainlessArch(process.arch),
|
|
445
444
|
"X-Stainless-OS": mapStainlessOs(process.platform),
|
|
446
|
-
"X-Stainless-Timeout": "
|
|
445
|
+
"X-Stainless-Timeout": "900",
|
|
446
|
+
"anthropic-client-platform": "desktop_app",
|
|
447
|
+
"anthropic-client-version": claudeClientVersion,
|
|
447
448
|
};
|
|
448
449
|
|
|
449
450
|
const enforcedHeaderKeys = new Set(
|
|
@@ -453,11 +454,11 @@ const enforcedHeaderKeys = new Set(
|
|
|
453
454
|
"Accept-Encoding",
|
|
454
455
|
"Connection",
|
|
455
456
|
"Content-Type",
|
|
456
|
-
"
|
|
457
|
-
"
|
|
458
|
-
"
|
|
457
|
+
"anthropic-version",
|
|
458
|
+
"anthropic-dangerous-direct-browser-access",
|
|
459
|
+
"anthropic-beta",
|
|
459
460
|
"User-Agent",
|
|
460
|
-
"
|
|
461
|
+
"x-app",
|
|
461
462
|
"Authorization",
|
|
462
463
|
"X-Api-Key",
|
|
463
464
|
"X-Claude-Code-Session-Id",
|
|
@@ -480,7 +481,7 @@ function createClaudeBillingHeader(firstUserMessageText: string): string {
|
|
|
480
481
|
.slice(0, 3);
|
|
481
482
|
// cch=00000: placeholder replaced with the real attestation hash by wrapFetchForCch
|
|
482
483
|
// before the request hits the wire (see below).
|
|
483
|
-
return `${CLAUDE_BILLING_HEADER_PREFIX} cc_version=${claudeCodeVersion}.${versionSuffix}; cc_entrypoint=
|
|
484
|
+
return `${CLAUDE_BILLING_HEADER_PREFIX} cc_version=${claudeCodeVersion}.${versionSuffix}; cc_entrypoint=local-agent; ${CCH_PLACEHOLDER_STR};`;
|
|
484
485
|
}
|
|
485
486
|
|
|
486
487
|
// cch attestation: XXHash64(body_with_placeholder, seed) low-20-bits, 5 hex chars.
|
|
@@ -620,19 +621,17 @@ function resolveAnthropicMetadataUserId(
|
|
|
620
621
|
return generateClaudeJsonUserId(sessionId);
|
|
621
622
|
}
|
|
622
623
|
const ANTHROPIC_BUILTIN_TOOL_NAMES = new Set(["web_search", "code_execution", "text_editor", "computer"]);
|
|
623
|
-
export const applyClaudeToolPrefix = (name: string
|
|
624
|
-
if (!
|
|
624
|
+
export const applyClaudeToolPrefix = (name: string): string => {
|
|
625
|
+
if (!claudeToolPrefix) return name;
|
|
625
626
|
if (ANTHROPIC_BUILTIN_TOOL_NAMES.has(name.toLowerCase())) return name;
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
return `${prefixOverride}${name}`;
|
|
627
|
+
if (name.toLowerCase().startsWith(claudeToolPrefix.toLowerCase())) return name;
|
|
628
|
+
return `${claudeToolPrefix}${name}`;
|
|
629
629
|
};
|
|
630
630
|
|
|
631
|
-
export const stripClaudeToolPrefix = (name: string
|
|
632
|
-
if (!
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return name.slice(prefixOverride.length);
|
|
631
|
+
export const stripClaudeToolPrefix = (name: string): string => {
|
|
632
|
+
if (!claudeToolPrefix) return name;
|
|
633
|
+
if (!name.toLowerCase().startsWith(claudeToolPrefix.toLowerCase())) return name;
|
|
634
|
+
return name.slice(claudeToolPrefix.length);
|
|
636
635
|
};
|
|
637
636
|
|
|
638
637
|
const ANTHROPIC_MANY_IMAGE_THRESHOLD = 20;
|
|
@@ -2271,44 +2270,20 @@ function resolveAnthropicAdaptiveEffort(
|
|
|
2271
2270
|
return mapEffortToAnthropicAdaptiveEffort(model, requestedEffort);
|
|
2272
2271
|
}
|
|
2273
2272
|
|
|
2274
|
-
function startsWithAfterAsciiWhitespace(value: string, prefix: string): boolean {
|
|
2275
|
-
let index = 0;
|
|
2276
|
-
while (index < value.length) {
|
|
2277
|
-
const code = value.charCodeAt(index);
|
|
2278
|
-
if (code !== 9 && code !== 10 && code !== 13 && code !== 32) break;
|
|
2279
|
-
index++;
|
|
2280
|
-
}
|
|
2281
|
-
return value.startsWith(prefix, index);
|
|
2282
|
-
}
|
|
2283
|
-
|
|
2284
|
-
function isClaudeSyntheticUserText(value: string): boolean {
|
|
2285
|
-
return startsWithAfterAsciiWhitespace(value, "<system-reminder>");
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
2273
|
function extractClaudeCodeFirstUserMessageText(messages: readonly Message[]): string {
|
|
2289
2274
|
for (const message of messages) {
|
|
2290
2275
|
if (message.role !== "user") continue;
|
|
2291
2276
|
const { content } = message;
|
|
2292
2277
|
if (typeof content === "string") return content;
|
|
2293
2278
|
if (!Array.isArray(content)) return "";
|
|
2294
|
-
let fallback: string | undefined;
|
|
2295
2279
|
for (const block of content) {
|
|
2296
|
-
if (block.type
|
|
2297
|
-
fallback ??= block.text;
|
|
2298
|
-
if (!isClaudeSyntheticUserText(block.text)) return block.text;
|
|
2280
|
+
if (block.type === "text") return block.text;
|
|
2299
2281
|
}
|
|
2300
|
-
return
|
|
2282
|
+
return "";
|
|
2301
2283
|
}
|
|
2302
2284
|
return "";
|
|
2303
2285
|
}
|
|
2304
2286
|
|
|
2305
|
-
function applyClaudeCodeContextManagement(params: MessageCreateParamsStreaming, isOAuthToken: boolean): void {
|
|
2306
|
-
if (!isOAuthToken || params.thinking?.type !== "adaptive") return;
|
|
2307
|
-
params.context_management = {
|
|
2308
|
-
edits: [{ type: "clear_thinking_20251015", keep: "all" }],
|
|
2309
|
-
};
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
2287
|
function buildParams(
|
|
2313
2288
|
model: Model<"anthropic-messages">,
|
|
2314
2289
|
baseUrl: string,
|
|
@@ -2318,58 +2293,41 @@ function buildParams(
|
|
|
2318
2293
|
disableStrictTools = false,
|
|
2319
2294
|
): MessageCreateParamsStreaming {
|
|
2320
2295
|
const { cacheControl } = getCacheControl(model, baseUrl, options?.cacheRetention, isOAuthToken);
|
|
2321
|
-
const params: MessageCreateParamsStreaming = {
|
|
2322
|
-
model: model.id,
|
|
2323
|
-
messages: convertAnthropicMessages(context.messages, model, isOAuthToken),
|
|
2324
|
-
max_tokens: options?.maxTokens || model.maxTokens,
|
|
2325
|
-
stream: true,
|
|
2326
|
-
};
|
|
2327
|
-
if (options?.temperature !== undefined && !options?.thinkingEnabled) {
|
|
2328
|
-
params.temperature = options.temperature;
|
|
2329
|
-
}
|
|
2330
2296
|
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
warnedStopSequencesTrim = true;
|
|
2341
|
-
logger.warn("anthropic: stop_sequences exceeds 4; extra entries dropped", {
|
|
2342
|
-
received: seqs.length,
|
|
2343
|
-
kept: ANTHROPIC_STOP_SEQUENCES_MAX,
|
|
2344
|
-
});
|
|
2345
|
-
}
|
|
2346
|
-
params.stop_sequences =
|
|
2347
|
-
seqs.length > ANTHROPIC_STOP_SEQUENCES_MAX ? seqs.slice(0, ANTHROPIC_STOP_SEQUENCES_MAX) : seqs;
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
// Opus 4.7+ rejects non-default sampling parameters with 400 error.
|
|
2351
|
-
if (hasOpus47ApiRestrictions(model.id)) {
|
|
2352
|
-
delete params.top_p;
|
|
2353
|
-
delete params.top_k;
|
|
2354
|
-
delete params.temperature;
|
|
2355
|
-
}
|
|
2297
|
+
// Pre-compute system blocks so they occupy the right slot in the serialized body.
|
|
2298
|
+
const shouldInjectClaudeCodeInstruction = isOAuthToken && !model.id.startsWith("claude-3-5-haiku");
|
|
2299
|
+
const firstUserMessageText = shouldInjectClaudeCodeInstruction
|
|
2300
|
+
? extractClaudeCodeFirstUserMessageText(context.messages)
|
|
2301
|
+
: "";
|
|
2302
|
+
const systemBlocks = buildAnthropicSystemBlocks(context.systemPrompt, {
|
|
2303
|
+
includeClaudeCodeInstruction: shouldInjectClaudeCodeInstruction,
|
|
2304
|
+
firstUserMessageText,
|
|
2305
|
+
});
|
|
2356
2306
|
|
|
2307
|
+
// Pre-compute tools.
|
|
2308
|
+
let tools: ReturnType<typeof convertTools> | undefined;
|
|
2357
2309
|
if (context.tools) {
|
|
2358
|
-
|
|
2310
|
+
tools = convertTools(
|
|
2359
2311
|
context.tools,
|
|
2360
2312
|
isOAuthToken,
|
|
2361
2313
|
disableStrictTools || model.provider === "github-copilot",
|
|
2362
2314
|
getAnthropicCompat(model).supportsEagerToolInputStreaming,
|
|
2363
2315
|
);
|
|
2364
2316
|
} else if (isOAuthToken) {
|
|
2365
|
-
|
|
2317
|
+
tools = [];
|
|
2366
2318
|
}
|
|
2367
2319
|
|
|
2320
|
+
// Pre-compute metadata.
|
|
2321
|
+
const metadataUserId = resolveAnthropicMetadataUserId(options?.metadata?.user_id, isOAuthToken, options?.sessionId);
|
|
2322
|
+
const metadata = metadataUserId ? { user_id: metadataUserId } : undefined;
|
|
2323
|
+
|
|
2324
|
+
// Pre-compute thinking + output_config effort.
|
|
2325
|
+
let thinking: MessageCreateParamsStreaming["thinking"] | undefined;
|
|
2326
|
+
let outputConfigEffort: AnthropicEffort | undefined;
|
|
2368
2327
|
if (model.reasoning) {
|
|
2369
2328
|
if (options?.thinkingEnabled) {
|
|
2370
2329
|
const mode = model.thinking?.mode;
|
|
2371
2330
|
const effort = resolveAnthropicAdaptiveEffort(model, options);
|
|
2372
|
-
|
|
2373
2331
|
const compat = getAnthropicCompat(model);
|
|
2374
2332
|
if (mode === "anthropic-adaptive" && !compat.disableAdaptiveThinking) {
|
|
2375
2333
|
const adaptive: { type: "adaptive"; display?: AnthropicThinkingDisplay } = { type: "adaptive" };
|
|
@@ -2379,31 +2337,70 @@ function buildParams(
|
|
|
2379
2337
|
if (options.thinkingDisplay !== undefined || supportsAdaptiveThinkingDisplay(model.id)) {
|
|
2380
2338
|
adaptive.display = options.thinkingDisplay ?? "summarized";
|
|
2381
2339
|
}
|
|
2382
|
-
|
|
2383
|
-
if (effort)
|
|
2384
|
-
getAnthropicOutputConfig(params).effort = effort;
|
|
2385
|
-
}
|
|
2340
|
+
thinking = adaptive;
|
|
2341
|
+
if (effort) outputConfigEffort = effort;
|
|
2386
2342
|
} else {
|
|
2387
|
-
|
|
2343
|
+
thinking = {
|
|
2388
2344
|
type: "enabled",
|
|
2389
2345
|
budget_tokens: options.thinkingBudgetTokens || 1024,
|
|
2390
2346
|
display: options.thinkingDisplay ?? "summarized",
|
|
2391
2347
|
};
|
|
2392
|
-
if (mode === "anthropic-budget-effort" && effort)
|
|
2393
|
-
getAnthropicOutputConfig(params).effort = effort;
|
|
2394
|
-
}
|
|
2348
|
+
if (mode === "anthropic-budget-effort" && effort) outputConfigEffort = effort;
|
|
2395
2349
|
}
|
|
2396
2350
|
} else if (options?.thinkingEnabled === false) {
|
|
2397
|
-
|
|
2351
|
+
thinking = { type: "disabled" };
|
|
2398
2352
|
}
|
|
2399
2353
|
}
|
|
2400
2354
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2355
|
+
// Pre-compute context_management (depends on thinking).
|
|
2356
|
+
const contextManagement =
|
|
2357
|
+
isOAuthToken && thinking?.type === "adaptive"
|
|
2358
|
+
? { edits: [{ type: "clear_thinking_20251015" as const, keep: "all" as const }] }
|
|
2359
|
+
: undefined;
|
|
2360
|
+
|
|
2361
|
+
// Pre-compute output_config.
|
|
2362
|
+
const outputConfigEntries: AnthropicOutputConfig = {};
|
|
2363
|
+
if (outputConfigEffort) outputConfigEntries.effort = outputConfigEffort;
|
|
2364
|
+
if (options?.taskBudget) outputConfigEntries.task_budget = options.taskBudget;
|
|
2365
|
+
const outputConfig = Object.keys(outputConfigEntries).length ? outputConfigEntries : undefined;
|
|
2366
|
+
|
|
2367
|
+
// Build params in the canonical field order: model → messages → system → tools →
|
|
2368
|
+
// metadata → max_tokens → thinking → context_management → output_config → stream.
|
|
2369
|
+
const params: MessageCreateParamsStreaming = {
|
|
2370
|
+
model: model.id,
|
|
2371
|
+
messages: convertAnthropicMessages(context.messages, model, isOAuthToken),
|
|
2372
|
+
...(systemBlocks && { system: systemBlocks }),
|
|
2373
|
+
...(tools !== undefined && { tools }),
|
|
2374
|
+
...(metadata && { metadata }),
|
|
2375
|
+
max_tokens: Math.min(CLAUDE_CODE_MAX_OUTPUT_TOKENS, options?.maxTokens || model.maxTokens),
|
|
2376
|
+
...(thinking && { thinking }),
|
|
2377
|
+
...(contextManagement && { context_management: contextManagement }),
|
|
2378
|
+
...(outputConfig && { output_config: outputConfig }),
|
|
2379
|
+
stream: true,
|
|
2380
|
+
};
|
|
2381
|
+
|
|
2382
|
+
// Opus 4.7+ rejects non-default sampling parameters with 400 error.
|
|
2383
|
+
const allowSamplingParams = !hasOpus47ApiRestrictions(model.id);
|
|
2384
|
+
if (allowSamplingParams && options?.temperature !== undefined && !options?.thinkingEnabled) {
|
|
2385
|
+
params.temperature = options.temperature;
|
|
2403
2386
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2387
|
+
if (allowSamplingParams && options?.topP !== undefined) {
|
|
2388
|
+
params.top_p = options.topP;
|
|
2389
|
+
}
|
|
2390
|
+
if (allowSamplingParams && options?.topK !== undefined) {
|
|
2391
|
+
params.top_k = options.topK;
|
|
2392
|
+
}
|
|
2393
|
+
if (options?.stopSequences?.length) {
|
|
2394
|
+
const seqs = options.stopSequences;
|
|
2395
|
+
if (seqs.length > ANTHROPIC_STOP_SEQUENCES_MAX && !warnedStopSequencesTrim) {
|
|
2396
|
+
warnedStopSequencesTrim = true;
|
|
2397
|
+
logger.warn("anthropic: stop_sequences exceeds 4; extra entries dropped", {
|
|
2398
|
+
received: seqs.length,
|
|
2399
|
+
kept: ANTHROPIC_STOP_SEQUENCES_MAX,
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
params.stop_sequences =
|
|
2403
|
+
seqs.length > ANTHROPIC_STOP_SEQUENCES_MAX ? seqs.slice(0, ANTHROPIC_STOP_SEQUENCES_MAX) : seqs;
|
|
2407
2404
|
}
|
|
2408
2405
|
|
|
2409
2406
|
if (resolveServiceTier(options?.serviceTier, model.provider) === "priority") {
|
|
@@ -2420,19 +2417,7 @@ function buildParams(
|
|
|
2420
2417
|
}
|
|
2421
2418
|
}
|
|
2422
2419
|
|
|
2423
|
-
const shouldInjectClaudeCodeInstruction = isOAuthToken && !model.id.startsWith("claude-3-5-haiku");
|
|
2424
|
-
const firstUserMessageText = shouldInjectClaudeCodeInstruction
|
|
2425
|
-
? extractClaudeCodeFirstUserMessageText(context.messages)
|
|
2426
|
-
: "";
|
|
2427
|
-
const systemBlocks = buildAnthropicSystemBlocks(context.systemPrompt, {
|
|
2428
|
-
includeClaudeCodeInstruction: shouldInjectClaudeCodeInstruction,
|
|
2429
|
-
firstUserMessageText,
|
|
2430
|
-
});
|
|
2431
|
-
if (systemBlocks) {
|
|
2432
|
-
params.system = systemBlocks;
|
|
2433
|
-
}
|
|
2434
2420
|
disableThinkingIfToolChoiceForced(params);
|
|
2435
|
-
applyClaudeCodeContextManagement(params, isOAuthToken);
|
|
2436
2421
|
ensureMaxTokensForThinking(params, model);
|
|
2437
2422
|
applyPromptCaching(params, cacheControl);
|
|
2438
2423
|
enforceCacheControlLimit(params, 4);
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
Api,
|
|
4
|
-
AssistantMessage,
|
|
5
|
-
DeveloperMessage,
|
|
6
|
-
Message,
|
|
7
|
-
Model,
|
|
8
|
-
ToolCall,
|
|
9
|
-
ToolResultMessage,
|
|
10
|
-
UserMessage,
|
|
11
|
-
} from "../types";
|
|
1
|
+
import type { Api, AssistantMessage, Message, Model, ToolCall, ToolResultMessage, UserMessage } from "../types";
|
|
12
2
|
|
|
13
3
|
const enum ToolCallStatus {
|
|
14
4
|
/** A tool result has already been emitted for this tool call; later duplicates must be skipped. */
|
|
@@ -142,7 +132,6 @@ function getLatestSurvivingAssistantIndex(messages: readonly Message[]): number
|
|
|
142
132
|
* For aborted/errored turns, this function:
|
|
143
133
|
* - Preserves tool call structure (unlike converting to text summaries)
|
|
144
134
|
* - Injects synthetic "aborted" tool results
|
|
145
|
-
* - Adds a <turn-aborted> guidance marker for the model
|
|
146
135
|
*/
|
|
147
136
|
export function transformMessages<TApi extends Api>(
|
|
148
137
|
messages: Message[],
|
|
@@ -345,11 +334,6 @@ export function transformMessages<TApi extends Api>(
|
|
|
345
334
|
} as ToolResultMessage);
|
|
346
335
|
toolCallStatus.set(tc.id, ToolCallStatus.Aborted);
|
|
347
336
|
}
|
|
348
|
-
result.push({
|
|
349
|
-
role: "developer",
|
|
350
|
-
content: turnAbortedGuidance,
|
|
351
|
-
timestamp: pendingAbortedTimestamp + 1,
|
|
352
|
-
} as DeveloperMessage);
|
|
353
337
|
pendingAbortedToolCalls = new Map();
|
|
354
338
|
pendingAbortedTimestamp = undefined;
|
|
355
339
|
};
|
|
@@ -378,11 +362,6 @@ export function transformMessages<TApi extends Api>(
|
|
|
378
362
|
// (OpenAI completions `reasoning_text`, Google signed thought parts).
|
|
379
363
|
const originalMsg = messages[i]!;
|
|
380
364
|
if (originalMsg.role === "assistant" && shouldDropTruncatedThinkingOnlyAssistant(originalMsg)) {
|
|
381
|
-
if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
|
|
382
|
-
// Still arm the aborted-turn note so downstream guidance fires.
|
|
383
|
-
pendingAbortedToolCalls = new Map();
|
|
384
|
-
pendingAbortedTimestamp = assistantMsg.timestamp;
|
|
385
|
-
}
|
|
386
365
|
continue;
|
|
387
366
|
}
|
|
388
367
|
|