@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 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.160";
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 Code, Anthropic's official CLI for 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, prefixOverride?: string) => string;
47
- export declare const stripClaudeToolPrefix: (name: string, prefixOverride?: string) => 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.2",
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.2",
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
- "Anthropic-Version": "2023-06-01",
200
- "Anthropic-Dangerous-Direct-Browser-Access": "true",
201
- "X-App": "cli",
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
- "Anthropic-Beta": betaHeader,
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, cli)`;
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
- "Anthropic-Beta": betaHeader,
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
- "Anthropic-Beta": betaHeader,
246
+ "anthropic-beta": betaHeader,
247
247
  };
248
248
  } else {
249
249
  return {
250
250
  ...modelHeaders,
251
251
  Accept: acceptHeader,
252
252
  ...sharedHeaders,
253
- "Anthropic-Beta": betaHeader,
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.160";
402
- export const claudeToolPrefix: string = "proxy_";
403
- export const claudeCodeSystemInstruction = "You are Claude Code, Anthropic's official CLI for Claude.";
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": "600",
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
- "Anthropic-Version",
457
- "Anthropic-Dangerous-Direct-Browser-Access",
458
- "Anthropic-Beta",
457
+ "anthropic-version",
458
+ "anthropic-dangerous-direct-browser-access",
459
+ "anthropic-beta",
459
460
  "User-Agent",
460
- "X-App",
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=cli; ${CCH_PLACEHOLDER_STR};`;
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, prefixOverride: string = claudeToolPrefix) => {
624
- if (!prefixOverride) return name;
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
- const prefix = prefixOverride.toLowerCase();
627
- if (name.toLowerCase().startsWith(prefix)) return name;
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, prefixOverride: string = claudeToolPrefix) => {
632
- if (!prefixOverride) return name;
633
- const prefix = prefixOverride.toLowerCase();
634
- if (!name.toLowerCase().startsWith(prefix)) return name;
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 !== "text") continue;
2297
- fallback ??= block.text;
2298
- if (!isClaudeSyntheticUserText(block.text)) return block.text;
2280
+ if (block.type === "text") return block.text;
2299
2281
  }
2300
- return fallback ?? "";
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
- if (options?.topP !== undefined) {
2332
- params.top_p = options.topP;
2333
- }
2334
- if (options?.topK !== undefined) {
2335
- params.top_k = options.topK;
2336
- }
2337
- if (options?.stopSequences?.length) {
2338
- const seqs = options.stopSequences;
2339
- if (seqs.length > ANTHROPIC_STOP_SEQUENCES_MAX && !warnedStopSequencesTrim) {
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
- params.tools = convertTools(
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
- params.tools = [];
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
- params.thinking = adaptive;
2383
- if (effort) {
2384
- getAnthropicOutputConfig(params).effort = effort;
2385
- }
2340
+ thinking = adaptive;
2341
+ if (effort) outputConfigEffort = effort;
2386
2342
  } else {
2387
- params.thinking = {
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
- params.thinking = { type: "disabled" };
2351
+ thinking = { type: "disabled" };
2398
2352
  }
2399
2353
  }
2400
2354
 
2401
- if (options?.taskBudget) {
2402
- getAnthropicOutputConfig(params).task_budget = options.taskBudget;
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
- const metadataUserId = resolveAnthropicMetadataUserId(options?.metadata?.user_id, isOAuthToken, options?.sessionId);
2405
- if (metadataUserId) {
2406
- params.metadata = { user_id: metadataUserId };
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 turnAbortedGuidance from "../prompts/turn-aborted-guidance.md" with { type: "text" };
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
 
@@ -1,4 +0,0 @@
1
- <turn-aborted>
2
- The previous turn was aborted. Any running tools/commands were terminated.
3
- If tools were aborted, they may have partially executed; verify current state before retrying.
4
- </turn-aborted>