@gajae-code/ai 0.13.1 → 0.13.2

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,8 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.13.2] - 2026-08-13
6
+
5
7
  ## [0.13.1] - 2026-08-11
6
8
 
7
9
  ### Fixed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/ai",
4
- "version": "0.13.1",
4
+ "version": "0.13.2",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -40,7 +40,7 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/sdk": "^0.94.0",
42
42
  "@bufbuild/protobuf": "^2.12.0",
43
- "@gajae-code/utils": "0.13.1",
43
+ "@gajae-code/utils": "0.13.2",
44
44
  "openai": "^6.36.0",
45
45
  "partial-json": "^0.1.7",
46
46
  "zod": "4.4.3"
@@ -2396,12 +2396,15 @@ function latestAssistantThinkingIsUnreplayable(messages: Message[], model: Model
2396
2396
  if (block.type !== "thinking") return false;
2397
2397
  // A block with empty text and no signature cannot go back on the wire:
2398
2398
  // `convertAnthropicMessages` drops it, and Anthropic rejects the turn for
2399
- // arriving without it. A block with a valid signature is replayable even
2400
- // when the text is empty, and non-signing endpoints replay unsigned blocks
2401
- // verbatim, so only signing endpoints treat a missing signature as
2402
- // unreplayable.
2399
+ // arriving without it. A block with a valid signature AND non-empty text is
2400
+ // replayable. But a signed block whose text was emptied — e.g. by
2401
+ // clear_thinking_20251015 — carries a stale signature that signing endpoints
2402
+ // reject on replay (issue #4247). Non-signing endpoints replay unsigned
2403
+ // blocks verbatim, so only they treat a missing signature as unreplayable.
2403
2404
  const hasSignature = !!block.thinkingSignature?.trim();
2405
+ const isEmpty = !block.thinking.trim();
2404
2406
  if (!hasSignature) return requiresSignature;
2407
+ if (isEmpty && requiresSignature) return true;
2405
2408
  return false;
2406
2409
  });
2407
2410
  }
@@ -98,8 +98,15 @@ export function transformMessages<TApi extends Api>(
98
98
  if (dropAssistantThinkingForRepair && replaysAsNativeThinking) return [];
99
99
  if (mustPreserveLatestAnthropicThinking) return sanitized;
100
100
  // For same model: keep thinking blocks with signatures (needed for replay)
101
- // even if the thinking text is empty (OpenAI encrypted reasoning)
102
- if (isSameModel && sanitized.thinkingSignature) return sanitized;
101
+ // even if the thinking text is empty — but only for non-Anthropic APIs where
102
+ // the signature represents OpenAI encrypted reasoning. For anthropic-messages,
103
+ // a signed block with empty text means clear_thinking_20251015 stripped the
104
+ // content server-side while the stale signature remained; replaying it
105
+ // produces `thinking ... cannot be modified` 400s on every turn (#4247).
106
+ if (isSameModel && sanitized.thinkingSignature) {
107
+ if (sanitized.thinking.trim() === "" && model.api === "anthropic-messages") return [];
108
+ return sanitized;
109
+ }
103
110
  // Skip empty thinking blocks, convert others to plain text
104
111
  if (!sanitized.thinking || sanitized.thinking.trim() === "") return [];
105
112
  if (isSameModel) return sanitized;