opencode-anthropic-fix 0.0.46 → 0.0.47

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 (3) hide show
  1. package/README.md +1 -1
  2. package/index.mjs +34 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -44,7 +44,7 @@ The [original plugin](https://github.com/anomalyco/opencode-anthropic-auth) prov
44
44
  - **Configurable strategies** — sticky, round-robin, or hybrid account selection
45
45
  - **Claude Code signature emulation** — full HTTP header, system prompt, beta flag, and metadata mimicry derived from Claude Code's open source code
46
46
  - **OAuth endpoint fingerprint parity** — matches the real CLI's bundled axios 1.13.6 HTTP client signature (`Accept`, `User-Agent`, `Content-Type`) on all OAuth token endpoint calls, required since 2026-03-21 server-side enforcement
47
- - **Dynamic billing cache hash** — computes the `cch` field in the billing header using the real CLI's `NP1()` algorithm (SHA-256 of salt + first user message chars + version), replacing the static `00000` placeholder
47
+ - **Billing header fingerprint parity** — `cc_version` suffix uses the real CLI's 3-char fingerprint hash (SHA-256 of salt + first user message chars + version), `cch` matches the Bun native client attestation placeholder, and `X-Claude-Code-Session-Id` header is sent on all requests
48
48
  - **Adaptive thinking for Opus/Sonnet 4.6** — automatically normalizes thinking to `{type: "adaptive"}` for supported models, with `effort-2025-11-24` beta
49
49
  - **Upstream-aligned auto betas** — 13+ always-on betas matching Claude Code 2.1.92 (`redact-thinking-2026-02-12` available as opt-in to preserve thinking block visibility)
50
50
  - **1M context limit override** — patches `model.limit.context` so OpenCode compacts at the right threshold while `models.dev` catches up
package/index.mjs CHANGED
@@ -2780,6 +2780,7 @@ export async function AnthropicAuthPlugin({ client, project, directory, worktree
2780
2780
  claudeCliVersion,
2781
2781
  customBetas: config.custom_betas,
2782
2782
  strategy: getEffectiveStrategy(),
2783
+ sessionId: signatureSessionId,
2783
2784
  },
2784
2785
  _adaptiveOverride,
2785
2786
  _tokenEconomy,
@@ -5553,19 +5554,34 @@ function buildRequestMetadata(input) {
5553
5554
 
5554
5555
  /**
5555
5556
  * Build the billing header block for Claude Code system prompt injection.
5556
- * Claude Code v2.1.84: cch computed via NP1() from first user message, cc_version includes model ID.
5557
+ * Claude Code v2.1.92: cc_version includes 3-char fingerprint hash (not model ID).
5558
+ * cch is a static "00000" placeholder for Bun native client attestation.
5559
+ *
5560
+ * Real CC (system.ts:78): version = `${MACRO.VERSION}.${fingerprint}`
5561
+ * Real CC (system.ts:82): cch = feature('NATIVE_CLIENT_ATTESTATION') ? ' cch=00000;' : ''
5557
5562
  *
5558
- * @param {string} version - CLI version (e.g., "2.1.84")
5559
- * @param {string} [modelId] - Model ID to append (e.g., "claude-opus-4-6")
5560
- * @param {string} [firstUserMessage] - First user message text for cch hash computation
5563
+ * @param {string} version - CLI version (e.g., "2.1.92")
5564
+ * @param {string} [firstUserMessage] - First user message text for fingerprint computation
5565
+ * @param {string} [provider] - API provider ("anthropic" | "bedrock" | "vertex" | "foundry")
5561
5566
  * @returns {string}
5562
5567
  */
5563
- function buildAnthropicBillingHeader(version, modelId, firstUserMessage) {
5568
+ function buildAnthropicBillingHeader(version, firstUserMessage, provider) {
5564
5569
  if (isFalsyEnv(process.env.CLAUDE_CODE_ATTRIBUTION_HEADER)) return "";
5565
5570
  const entrypoint = process.env.CLAUDE_CODE_ENTRYPOINT || "unknown";
5566
- const ccVersion = modelId ? `${version}.${modelId}` : version;
5567
- const cch = firstUserMessage ? computeBillingCacheHash(firstUserMessage, version) : "00000";
5568
- let header = `x-anthropic-billing-header: cc_version=${ccVersion}; cc_entrypoint=${entrypoint}; cch=${cch};`;
5571
+ // Fix #1: cc_version suffix is the 3-char fingerprint hash, NOT the model ID.
5572
+ // computeBillingCacheHash() computes SHA256(salt + msg[4]+msg[7]+msg[20] + version)[:3]
5573
+ // which matches computeFingerprint() in the real CC source (utils/fingerprint.ts).
5574
+ // Always call the hash function — even for empty messages the real CC computes
5575
+ // the hash from "000" chars (indices 4,7,20 all missing → fallback "0").
5576
+ const fingerprint = computeBillingCacheHash(firstUserMessage || "", version);
5577
+ const ccVersion = `${version}.${fingerprint}`;
5578
+ // Fix #4: cch is a static "00000" placeholder for Bun's native client attestation.
5579
+ // Real CC v92: cch is included for all providers EXCEPT bedrock/anthropicAws.
5580
+ // The real Bun binary overwrites these zeros in the serialized body bytes.
5581
+ // For non-Bun runtimes, the server sees "00000" and skips attestation verification.
5582
+ const isBedrock = provider === "bedrock" || provider === "anthropicAws";
5583
+ const cchPart = isBedrock ? "" : " cch=00000;";
5584
+ let header = `x-anthropic-billing-header: cc_version=${ccVersion}; cc_entrypoint=${entrypoint};${cchPart}`;
5569
5585
  const workload = process.env.CLAUDE_CODE_WORKLOAD;
5570
5586
  if (workload) {
5571
5587
  // QA fix M5: sanitize workload value to prevent header injection
@@ -5757,8 +5773,8 @@ function buildSystemPromptBlocks(system, signature) {
5757
5773
  const blocks = [];
5758
5774
  const billingHeader = buildAnthropicBillingHeader(
5759
5775
  signature.claudeCliVersion,
5760
- signature.modelId,
5761
5776
  signature.firstUserMessage,
5777
+ signature.provider,
5762
5778
  );
5763
5779
  if (billingHeader) {
5764
5780
  // Billing header: no cache_control (null scope — never cached)
@@ -6118,7 +6134,7 @@ async function fetchLatestClaudeCodeVersion(timeoutMs = 1200) {
6118
6134
  * @param {string} accessToken
6119
6135
  * @param {string | undefined} requestBody
6120
6136
  * @param {URL | null} requestUrl
6121
- * @param {{enabled: boolean, claudeCliVersion: string, strategy?: import('./lib/config.mjs').AccountSelectionStrategy, customBetas?: string[]}} signature
6137
+ * @param {{enabled: boolean, claudeCliVersion: string, strategy?: import('./lib/config.mjs').AccountSelectionStrategy, customBetas?: string[], sessionId?: string}} signature
6122
6138
  * @returns {Headers}
6123
6139
  */
6124
6140
  function buildRequestHeaders(
@@ -6182,7 +6198,14 @@ function buildRequestHeaders(
6182
6198
  requestHeaders.set("user-agent", buildExtendedUserAgent(signature.claudeCliVersion));
6183
6199
  if (signature.enabled) {
6184
6200
  requestHeaders.set("anthropic-version", "2023-06-01");
6185
- requestHeaders.set("x-app", "cli");
6201
+ // Fix #6: x-app is "cli" for interactive mode, "cli-bg" for background tasks.
6202
+ // Real CC (client.ts:106): 'x-app': 'cli' (foreground) or 'cli-bg' (background agent).
6203
+ requestHeaders.set("x-app", isTruthyEnv(process.env.CLAUDE_CODE_BACKGROUND) ? "cli-bg" : "cli");
6204
+ // Fix #3: X-Claude-Code-Session-Id — sent in ALL requests by real CC (client.ts:108).
6205
+ // Value matches metadata.user_id.session_id for server-side correlation.
6206
+ if (signature.sessionId) {
6207
+ requestHeaders.set("X-Claude-Code-Session-Id", signature.sessionId);
6208
+ }
6186
6209
  requestHeaders.set("x-stainless-arch", getStainlessArch(process.arch));
6187
6210
  requestHeaders.set("x-stainless-lang", "js");
6188
6211
  requestHeaders.set("x-stainless-os", getStainlessOs(process.platform));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-anthropic-fix",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "main": "./index.mjs",
6
6
  "files": [