@oh-my-pi/pi-ai 16.1.15 → 16.1.16

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,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.1.16] - 2026-06-23
6
+
7
+ ### Fixed
8
+
9
+ - Fixed Anthropic-compatible thinking requests sending replayed thinking blocks without `context_management.keep: "all"`, preserving multi-turn reasoning context for API-key providers. API-key requests now also advertise the required `context-management-2025-06-27` beta header so the field is honored instead of rejected. Injected SDK clients, GitHub Copilot's Anthropic proxy, and Vertex rawPredict are excluded because this code path cannot add the beta to caller-owned clients, Copilot strips Anthropic betas and demotes thinking blocks to text upstream, and Vertex expects betas in the JSON body rather than the Anthropic HTTP beta header. ([#3288](https://github.com/can1357/oh-my-pi/issues/3288))
10
+ - Fixed OpenRouter Responses native history replay leaking Gemini reasoning item `format` metadata back into follow-up requests, which caused HTTP 400 rejections while preserving encrypted reasoning replay.
11
+
5
12
  ## [16.1.15] - 2026-06-22
6
13
 
7
14
  ### Fixed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-ai",
4
- "version": "16.1.15",
4
+ "version": "16.1.16",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -38,9 +38,9 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.12.0",
41
- "@oh-my-pi/pi-catalog": "16.1.15",
42
- "@oh-my-pi/pi-utils": "16.1.15",
43
- "@oh-my-pi/pi-wire": "16.1.15",
41
+ "@oh-my-pi/pi-catalog": "16.1.16",
42
+ "@oh-my-pi/pi-utils": "16.1.16",
43
+ "@oh-my-pi/pi-wire": "16.1.16",
44
44
  "arktype": "^2.2.0",
45
45
  "zod": "^4"
46
46
  },
@@ -120,10 +120,11 @@ export function buildBetaHeader(baseBetas: readonly string[], extraBetas: readon
120
120
  }
121
121
 
122
122
  const midConversationSystemBeta = "mid-conversation-system-2026-04-07";
123
+ const contextManagementBeta = "context-management-2025-06-27";
123
124
  const claudeCodeUtilityBetaDefaults = [
124
125
  "oauth-2025-04-20",
125
126
  "interleaved-thinking-2025-05-14",
126
- "context-management-2025-06-27",
127
+ contextManagementBeta,
127
128
  "prompt-caching-scope-2026-01-05",
128
129
  "structured-outputs-2025-12-15",
129
130
  ] as const;
@@ -131,7 +132,7 @@ const claudeCodeAgentBetaDefaults = [
131
132
  "claude-code-20250219",
132
133
  "oauth-2025-04-20",
133
134
  "interleaved-thinking-2025-05-14",
134
- "context-management-2025-06-27",
135
+ contextManagementBeta,
135
136
  "prompt-caching-scope-2026-01-05",
136
137
  midConversationSystemBeta,
137
138
  "advanced-tool-use-2025-11-20",
@@ -1680,6 +1681,22 @@ const streamAnthropicOnce = (
1680
1681
  // carry it in the Claude Code list).
1681
1682
  extraBetas.push(midConversationSystemBeta);
1682
1683
  }
1684
+ // `context_management.clear_thinking_20251015` requires this beta. OAuth
1685
+ // requests carry it in `claudeCodeAgentBetaDefaults`; API-key requests
1686
+ // need it added explicitly so the field is honored instead of rejected
1687
+ // (#3288). Skip transports where this package cannot deliver the beta
1688
+ // in the form their adapter accepts: Copilot strips Anthropic betas,
1689
+ // and Vertex rawPredict needs betas in the body (`anthropic_beta`),
1690
+ // not as an `anthropic-beta` HTTP header.
1691
+ if (
1692
+ model.reasoning &&
1693
+ options?.thinkingEnabled &&
1694
+ model.provider !== "github-copilot" &&
1695
+ model.provider !== "google-vertex" &&
1696
+ !extraBetas.includes(contextManagementBeta)
1697
+ ) {
1698
+ extraBetas.push(contextManagementBeta);
1699
+ }
1683
1700
 
1684
1701
  const created = createClient(model, {
1685
1702
  model,
@@ -2944,11 +2961,28 @@ function buildParams(
2944
2961
  }
2945
2962
  }
2946
2963
 
2947
- // Pre-compute context_management (depends on thinking).
2948
- const contextManagement =
2949
- isOAuthToken && thinking?.type === "adaptive"
2950
- ? { edits: [{ type: "clear_thinking_20251015" as const, keep: "all" as const }] }
2951
- : undefined;
2964
+ // Pre-compute context_management. Send keep: "all" for every enabled or
2965
+ // adaptive thinking request (OAuth + API-key) — not just OAuth. Without
2966
+ // this directive Anthropic-compatible backends (Z.AI, Kimi, DeepSeek, …)
2967
+ // strip the replayed thinking blocks `replayUnsignedThinking` puts back
2968
+ // on the wire, so the model loses the prior reasoning chain across turns
2969
+ // and the KV cache misses every turn (#3288). Narrowing this guard back
2970
+ // to `isOAuthToken` regresses every API-key thinking provider. Skip
2971
+ // injected clients because this code cannot add the required
2972
+ // `context-management-2025-06-27` beta to caller-owned SDK clients. Skip
2973
+ // Copilot because its proxy strips Anthropic betas and demotes thinking
2974
+ // blocks to text upstream, so `keep: "all"` is a no-op that risks proxy
2975
+ // rejection of an unrecognized field. Skip Vertex rawPredict because that
2976
+ // adapter requires betas in the JSON body (`anthropic_beta`) instead of the
2977
+ // Anthropic HTTP beta header this code can add.
2978
+ const shouldKeepThinkingContext =
2979
+ !options?.client &&
2980
+ model.provider !== "github-copilot" &&
2981
+ model.provider !== "google-vertex" &&
2982
+ (thinking?.type === "adaptive" || thinking?.type === "enabled");
2983
+ const contextManagement = shouldKeepThinkingContext
2984
+ ? { edits: [{ type: "clear_thinking_20251015" as const, keep: "all" as const }] }
2985
+ : undefined;
2952
2986
 
2953
2987
  // Pre-compute output_config.
2954
2988
  const outputConfigEntries: AnthropicOutputConfig = {};
package/src/utils.ts CHANGED
@@ -78,6 +78,7 @@ function sanitizeOpenAIResponsesHistoryItemForReplay(
78
78
  ): OpenAIResponsesReplayItem | undefined {
79
79
  if (item.type === "item_reference") return undefined;
80
80
  if (item.type === "image_generation_call") return sanitizeOpenAIResponsesImageGenerationCallForReplay(item);
81
+ if (item.type === "reasoning") return sanitizeOpenAIResponsesReasoningItemForReplay(item);
81
82
 
82
83
  // providerPayload stores raw output items; replay strips item ids and keeps only normalized call_id.
83
84
  const { id: _id, ...sanitizedItem } = item;
@@ -88,6 +89,19 @@ function sanitizeOpenAIResponsesHistoryItemForReplay(
88
89
  return sanitizedItem as unknown as OpenAIResponsesReplayItem;
89
90
  }
90
91
 
92
+ function sanitizeOpenAIResponsesReasoningItemForReplay(item: Record<string, unknown>): OpenAIResponsesReplayItem {
93
+ const sanitizedItem: Record<string, unknown> = { type: "reasoning" };
94
+ if (Array.isArray(item.summary)) sanitizedItem.summary = item.summary;
95
+ if (Array.isArray(item.content)) sanitizedItem.content = item.content;
96
+ if (typeof item.encrypted_content === "string" || item.encrypted_content === null) {
97
+ sanitizedItem.encrypted_content = item.encrypted_content;
98
+ }
99
+ if (item.status === "in_progress" || item.status === "completed" || item.status === "incomplete") {
100
+ sanitizedItem.status = item.status;
101
+ }
102
+ return sanitizedItem as unknown as OpenAIResponsesReplayItem;
103
+ }
104
+
91
105
  function sanitizeOpenAIResponsesImageGenerationCallForReplay(
92
106
  item: Record<string, unknown>,
93
107
  ): ResponseInputItem.ImageGenerationCall | undefined {