@oh-my-pi/pi-ai 15.11.8 → 15.12.0

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.12.0] - 2026-06-12
6
+
7
+ ### Fixed
8
+
9
+ - Fixed Anthropic requests bypassing lone-surrogate sanitization after payload hooks or Anthropic-origin tool-call replay: the model itself can emit unpaired surrogate escapes in its own tool-argument JSON (streamed out fine, then rejected with `400 The request body is not valid JSON` on every subsequent request, bricking the session). The final Anthropic payload is now deep-sanitized with `toWellFormed()` immediately before SDK serialization; the pass is identity-preserving, so well-formed arguments stay byte-identical and prompt-cache prefixes are unaffected.
10
+
5
11
  ## [15.11.8] - 2026-06-12
6
12
 
7
13
  ### Breaking Changes
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.11.8",
4
+ "version": "15.12.0",
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,8 +38,8 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.12.0",
41
- "@oh-my-pi/pi-catalog": "15.11.8",
42
- "@oh-my-pi/pi-utils": "15.11.8",
41
+ "@oh-my-pi/pi-catalog": "15.12.0",
42
+ "@oh-my-pi/pi-utils": "15.12.0",
43
43
  "openai": "^6.39.0",
44
44
  "partial-json": "^0.1.7",
45
45
  "zod": "4.4.3"
@@ -1639,6 +1639,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
1639
1639
  if (replacementPayload !== undefined) {
1640
1640
  nextParams = replacementPayload as typeof nextParams;
1641
1641
  }
1642
+ nextParams = toWellFormedDeep(nextParams) as typeof nextParams;
1642
1643
  rawRequestDump = {
1643
1644
  provider: model.provider,
1644
1645
  api: output.api,
@@ -3075,13 +3076,12 @@ export function convertAnthropicMessages(
3075
3076
  type: "tool_use",
3076
3077
  id: block.id,
3077
3078
  name: isOAuthToken ? applyClaudeToolPrefix(block.name) : block.name,
3078
- // Anthropic-origin arguments are guaranteed well-formed (they came
3079
- // from the API's own JSON); cross-API replays can carry lone
3080
- // surrogates that Anthropic's strict UTF-8 validation rejects.
3081
- input:
3082
- msg.api === "anthropic-messages"
3083
- ? (block.arguments ?? {})
3084
- : toWellFormedDeep(block.arguments ?? {}),
3079
+ // Always sanitize: the model itself can emit lone-surrogate escapes
3080
+ // in tool-argument JSON (streamed out fine, rejected with a 400 on
3081
+ // replay by Anthropic's strict UTF-8 validation). toWellFormedDeep
3082
+ // is identity-preserving, so well-formed arguments stay
3083
+ // byte-identical and prompt-cache prefixes are unaffected.
3084
+ input: toWellFormedDeep(block.arguments ?? {}),
3085
3085
  });
3086
3086
  }
3087
3087
  }