opencode-anthropic-fix 0.0.47 → 0.0.48

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.
Files changed (2) hide show
  1. package/index.mjs +28 -24
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -5801,16 +5801,20 @@ function buildSystemPromptBlocks(system, signature) {
5801
5801
  // we always include it for better cache hit rates on the proxy side.
5802
5802
  blocks.push({ type: "text", text: CLAUDE_CODE_IDENTITY_STRING, cache_control: baseCacheControl });
5803
5803
 
5804
- // Filtered blocks: keep as-is, with optional static/dynamic boundary marker
5804
+ // Real CC (utils/api.ts splitSysPromptPrefix): ALL user system blocks are joined
5805
+ // with '\n\n' into a SINGLE text block. This is true in ALL modes (default, MCP, boundary).
5806
+ // Wire format: [billing (no cache), identity (ephemeral), ONE_JOINED_BLOCK (ephemeral)]
5807
+ // Sending separate blocks is a detectable fingerprinting signal.
5805
5808
  if (filtered.length > 0) {
5806
5809
  const useBoundary =
5807
5810
  signature.cachePolicy?.boundary_marker || isTruthyEnv(process.env.CLAUDE_CODE_FORCE_GLOBAL_CACHE);
5808
5811
 
5809
5812
  if (useBoundary) {
5810
- // Heuristic: treat first half as "static" (tool defs, instructions)
5811
- // and second half as "dynamic" (env info, memory, etc.)
5812
- // Find a split point: look for blocks containing environment/date/CWD info
5813
- // as the boundary between static and dynamic.
5813
+ // Global cache mode: split blocks into static (pre-boundary) and dynamic (post-boundary).
5814
+ // Real CC (splitSysPromptPrefix boundary path):
5815
+ // static blocks → joined + cacheScope:'global' → cache_control:{type:'ephemeral',scope:'global',ttl:'1h'}
5816
+ // dynamic blocks → joined + cacheScope:null → NO cache_control
5817
+ // We use a heuristic to find the split point (environment/date/CWD info = dynamic).
5814
5818
  const splitIndex = filtered.findIndex((block) => {
5815
5819
  const text = block.text.toLowerCase();
5816
5820
  return (
@@ -5824,29 +5828,29 @@ function buildSystemPromptBlocks(system, signature) {
5824
5828
 
5825
5829
  const effectiveSplit = splitIndex > 0 ? splitIndex : Math.ceil(filtered.length / 2);
5826
5830
 
5827
- // Static blocks (before boundary) get global-scope cache_control
5828
- for (let i = 0; i < effectiveSplit; i++) {
5829
- blocks.push({ ...filtered[i], cache_control: globalCacheControl });
5831
+ // Static blocks (before boundary): joined into ONE block with global cache
5832
+ const staticText = filtered
5833
+ .slice(0, effectiveSplit)
5834
+ .map((b) => b.text)
5835
+ .join("\n\n");
5836
+ if (staticText) {
5837
+ blocks.push({ type: "text", text: staticText, cache_control: globalCacheControl });
5830
5838
  }
5831
5839
 
5832
- // Boundary marker
5833
- blocks.push({ type: "text", text: SYSTEM_PROMPT_DYNAMIC_BOUNDARY });
5834
-
5835
- // Dynamic blocks (after boundary) get NO cache_control
5836
- for (let i = effectiveSplit; i < filtered.length; i++) {
5837
- const { cache_control: _cc, ...rest } = filtered[i];
5838
- blocks.push(rest);
5840
+ // Dynamic blocks (after boundary): joined into ONE block with NO cache
5841
+ const dynamicText = filtered
5842
+ .slice(effectiveSplit)
5843
+ .map((b) => b.text)
5844
+ .join("\n\n");
5845
+ if (dynamicText) {
5846
+ blocks.push({ type: "text", text: dynamicText });
5839
5847
  }
5840
5848
  } else {
5841
- // Original behavior: only last block gets cache_control.
5842
- // Strip any upstream cache_control from intermediate blocks to prevent
5843
- // TTL ordering violations (e.g., upstream 5m followed by our 1h).
5844
- for (let i = 0; i < filtered.length - 1; i++) {
5845
- const { cache_control: _cc, ...rest } = filtered[i];
5846
- blocks.push(rest);
5847
- }
5848
- const lastFiltered = filtered[filtered.length - 1];
5849
- blocks.push({ ...lastFiltered, cache_control: baseCacheControl });
5849
+ // Default mode (and MCP tools mode): ALL filtered blocks joined into ONE block.
5850
+ // Real CC (splitSysPromptPrefix default/MCP path):
5851
+ // rest.join('\n\n') → cacheScope:'org' → cache_control:{type:'ephemeral',ttl:'1h'}
5852
+ const joinedText = filtered.map((b) => b.text).join("\n\n");
5853
+ blocks.push({ type: "text", text: joinedText, cache_control: baseCacheControl });
5850
5854
  }
5851
5855
  }
5852
5856
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-anthropic-fix",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "main": "./index.mjs",
6
6
  "files": [