@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.9
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/README.md +88 -88
- package/dist/opencode-anthropic-auth-cli.mjs +804 -507
- package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
- package/package.json +67 -59
- package/src/__tests__/billing-edge-cases.test.ts +59 -59
- package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
- package/src/__tests__/cc-comparison.test.ts +87 -87
- package/src/__tests__/cc-credentials.test.ts +254 -250
- package/src/__tests__/cch-drift-checker.test.ts +51 -51
- package/src/__tests__/cch-native-style.test.ts +56 -56
- package/src/__tests__/debug-gating.test.ts +42 -42
- package/src/__tests__/decomposition-smoke.test.ts +68 -68
- package/src/__tests__/fingerprint-regression.test.ts +575 -566
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
- package/src/__tests__/helpers/conversation-history.ts +119 -119
- package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
- package/src/__tests__/helpers/deferred.ts +69 -69
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
- package/src/__tests__/helpers/in-memory-storage.ts +88 -88
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
- package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
- package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
- package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
- package/src/__tests__/helpers/sse.ts +209 -209
- package/src/__tests__/index.parallel.test.ts +605 -595
- package/src/__tests__/sanitization-regex.test.ts +112 -112
- package/src/__tests__/state-bounds.test.ts +90 -90
- package/src/account-identity.test.ts +197 -192
- package/src/account-identity.ts +69 -67
- package/src/account-state.test.ts +86 -86
- package/src/account-state.ts +25 -25
- package/src/accounts/matching.test.ts +335 -0
- package/src/accounts/matching.ts +167 -0
- package/src/accounts/persistence.test.ts +345 -0
- package/src/accounts/persistence.ts +432 -0
- package/src/accounts/repair.test.ts +276 -0
- package/src/accounts/repair.ts +407 -0
- package/src/accounts.dedup.test.ts +621 -621
- package/src/accounts.test.ts +933 -929
- package/src/accounts.ts +633 -989
- package/src/backoff.test.ts +345 -345
- package/src/backoff.ts +219 -219
- package/src/betas.ts +124 -124
- package/src/bun-fetch.test.ts +345 -342
- package/src/bun-fetch.ts +424 -424
- package/src/bun-proxy.test.ts +25 -25
- package/src/bun-proxy.ts +209 -209
- package/src/cc-credentials.ts +111 -111
- package/src/circuit-breaker.test.ts +184 -184
- package/src/circuit-breaker.ts +169 -169
- package/src/cli/commands/auth.ts +963 -0
- package/src/cli/commands/config.ts +547 -0
- package/src/cli/formatting.test.ts +406 -0
- package/src/cli/formatting.ts +219 -0
- package/src/cli.ts +255 -2022
- package/src/commands/handlers/betas.ts +100 -0
- package/src/commands/handlers/config.ts +99 -0
- package/src/commands/handlers/files.ts +375 -0
- package/src/commands/oauth-flow.ts +181 -166
- package/src/commands/prompts.ts +61 -61
- package/src/commands/router.test.ts +421 -0
- package/src/commands/router.ts +143 -635
- package/src/config.test.ts +482 -482
- package/src/config.ts +412 -404
- package/src/constants.ts +48 -48
- package/src/drift/cch-constants.ts +95 -95
- package/src/env.ts +111 -105
- package/src/headers/billing.ts +33 -33
- package/src/headers/builder.ts +130 -130
- package/src/headers/cch.ts +75 -75
- package/src/headers/stainless.ts +25 -25
- package/src/headers/user-agent.ts +23 -23
- package/src/index.ts +436 -828
- package/src/models.ts +27 -27
- package/src/oauth.test.ts +102 -102
- package/src/oauth.ts +178 -178
- package/src/parent-pid-watcher.test.ts +148 -148
- package/src/parent-pid-watcher.ts +69 -69
- package/src/plugin-helpers.ts +82 -82
- package/src/refresh-helpers.ts +145 -139
- package/src/refresh-lock.test.ts +94 -94
- package/src/refresh-lock.ts +93 -93
- package/src/request/body.history.test.ts +579 -571
- package/src/request/body.ts +255 -255
- package/src/request/metadata.ts +65 -65
- package/src/request/retry.test.ts +156 -156
- package/src/request/retry.ts +67 -67
- package/src/request/url.ts +21 -21
- package/src/request-orchestration-helpers.ts +648 -0
- package/src/response/index.ts +5 -5
- package/src/response/mcp.ts +58 -58
- package/src/response/streaming.test.ts +313 -311
- package/src/response/streaming.ts +412 -410
- package/src/rotation.test.ts +304 -301
- package/src/rotation.ts +205 -205
- package/src/storage.test.ts +547 -547
- package/src/storage.ts +315 -291
- package/src/system-prompt/builder.ts +38 -38
- package/src/system-prompt/index.ts +5 -5
- package/src/system-prompt/normalize.ts +60 -60
- package/src/system-prompt/sanitize.ts +30 -30
- package/src/thinking.ts +21 -20
- package/src/token-refresh.test.ts +265 -265
- package/src/token-refresh.ts +219 -214
- package/src/types.ts +30 -30
- package/dist/bun-proxy.mjs +0 -291
package/src/betas.ts
CHANGED
|
@@ -1,138 +1,138 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
BEDROCK_UNSUPPORTED_BETAS,
|
|
3
|
+
BETA_SHORTCUTS,
|
|
4
|
+
CLAUDE_CODE_BETA_FLAG,
|
|
5
|
+
EFFORT_BETA_FLAG,
|
|
6
|
+
EXPERIMENTAL_BETA_FLAGS,
|
|
7
|
+
TOKEN_COUNTING_BETA_FLAG,
|
|
8
8
|
} from "./constants.js";
|
|
9
9
|
import { isTruthyEnv } from "./env.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
hasOneMillionContext,
|
|
12
|
+
isAdaptiveThinkingModel,
|
|
13
|
+
isHaikuModel,
|
|
14
|
+
supportsContextManagement,
|
|
15
|
+
supportsStructuredOutputs,
|
|
16
|
+
supportsThinking,
|
|
17
|
+
supportsWebSearch,
|
|
18
18
|
} from "./models.js";
|
|
19
19
|
import type { AccountSelectionStrategy, Provider } from "./types.js";
|
|
20
20
|
|
|
21
21
|
export function buildAnthropicBetaHeader(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
incomingBeta: string,
|
|
23
|
+
signatureEnabled: boolean,
|
|
24
|
+
model: string,
|
|
25
|
+
provider: Provider,
|
|
26
|
+
customBetas: string[] | undefined,
|
|
27
|
+
strategy: AccountSelectionStrategy | undefined,
|
|
28
|
+
requestPath: string | undefined,
|
|
29
|
+
hasFileReferences: boolean,
|
|
30
30
|
): string {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
const incomingBetasList = incomingBeta
|
|
32
|
+
.split(",")
|
|
33
|
+
.map((b) => b.trim())
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
|
|
36
|
+
const betas: string[] = ["oauth-2025-04-20"];
|
|
37
|
+
const disableExperimentalBetas = isTruthyEnv(process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS);
|
|
38
|
+
const isMessagesCountTokensPath = requestPath === "/v1/messages/count_tokens";
|
|
39
|
+
const isFilesEndpoint = requestPath?.startsWith("/v1/files") ?? false;
|
|
40
|
+
|
|
41
|
+
if (!signatureEnabled) {
|
|
42
|
+
betas.push("interleaved-thinking-2025-05-14");
|
|
43
|
+
if (isMessagesCountTokensPath) {
|
|
44
|
+
betas.push(TOKEN_COUNTING_BETA_FLAG);
|
|
45
|
+
}
|
|
46
|
+
let mergedBetas = [...new Set([...betas, ...incomingBetasList])];
|
|
47
|
+
if (disableExperimentalBetas) {
|
|
48
|
+
mergedBetas = mergedBetas.filter((beta) => !EXPERIMENTAL_BETA_FLAGS.has(beta));
|
|
49
|
+
}
|
|
50
|
+
return mergedBetas.join(",");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const haiku = isHaikuModel(model);
|
|
54
|
+
const isRoundRobin = strategy === "round-robin";
|
|
55
|
+
|
|
56
|
+
if (!haiku) {
|
|
57
|
+
betas.push(CLAUDE_CODE_BETA_FLAG);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Files API beta is endpoint/content-scoped instead of globally applied.
|
|
61
|
+
if ((isFilesEndpoint || hasFileReferences) && !disableExperimentalBetas) {
|
|
62
|
+
betas.push("files-api-2025-04-14");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// NOTE: redact-thinking-2026-02-12 is in upstream 2.1.79+ base profile but
|
|
66
|
+
// intentionally NOT auto-included here — OpenCode users benefit from seeing
|
|
67
|
+
// thinking blocks. Available via /anthropic betas add redact-thinking-2026-02-12.
|
|
68
|
+
|
|
69
|
+
// CC 2.1.98 Proxyman capture shows these are NOT in the actual request headers
|
|
70
|
+
// even though they're in the source. Only include when explicitly requested.
|
|
71
|
+
// advanced-tool-use and fast-mode were causing fingerprint mismatch.
|
|
72
|
+
|
|
73
|
+
// Advisor tool — CC 2.1.98 always includes this.
|
|
74
|
+
betas.push("advisor-tool-2026-03-01");
|
|
75
|
+
|
|
76
|
+
if (isAdaptiveThinkingModel(model)) {
|
|
77
|
+
// Adaptive thinking models (Opus 4.6, Sonnet 4.6) use effort-based thinking controls.
|
|
78
|
+
betas.push(EFFORT_BETA_FLAG);
|
|
79
|
+
} else if (
|
|
80
|
+
!disableExperimentalBetas &&
|
|
81
|
+
!isTruthyEnv(process.env.DISABLE_INTERLEAVED_THINKING) &&
|
|
82
|
+
supportsThinking(model)
|
|
83
|
+
) {
|
|
84
|
+
betas.push("interleaved-thinking-2025-05-14");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// context-1m-2025-08-07 is only supported for API key users; OAuth provider does not support it.
|
|
88
|
+
// For OAuth (this plugin's only auth mode), compaction is gated by model.limit.input instead.
|
|
89
|
+
if (!disableExperimentalBetas && hasOneMillionContext(model) && provider !== "anthropic") {
|
|
90
|
+
betas.push("context-1m-2025-08-07");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Context management: upstream CC adds this for Claude 4+ models (thinking
|
|
94
|
+
// preservation) or when ant users opt in via USE_API_CONTEXT_MANAGEMENT.
|
|
95
|
+
if (!disableExperimentalBetas && supportsContextManagement(model)) {
|
|
96
|
+
betas.push("context-management-2025-06-27");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!disableExperimentalBetas && supportsStructuredOutputs(model) && isTruthyEnv(process.env.TENGU_TOOL_PEAR)) {
|
|
100
|
+
betas.push("structured-outputs-2025-12-15");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!disableExperimentalBetas && (provider === "vertex" || provider === "foundry") && supportsWebSearch(model)) {
|
|
104
|
+
betas.push("web-search-2025-03-05");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Upstream CC always sends prompt-caching-scope for firstParty providers.
|
|
108
|
+
// Skip in round-robin (zero cache hits, doubled costs).
|
|
109
|
+
if (!disableExperimentalBetas && !isRoundRobin) {
|
|
110
|
+
betas.push("prompt-caching-scope-2026-01-05");
|
|
111
|
+
}
|
|
112
|
+
|
|
43
113
|
if (isMessagesCountTokensPath) {
|
|
44
|
-
|
|
114
|
+
betas.push(TOKEN_COUNTING_BETA_FLAG);
|
|
45
115
|
}
|
|
116
|
+
|
|
117
|
+
if (process.env.ANTHROPIC_BETAS) {
|
|
118
|
+
const envBetas = process.env.ANTHROPIC_BETAS.split(",")
|
|
119
|
+
.map((b) => b.trim())
|
|
120
|
+
.filter(Boolean);
|
|
121
|
+
betas.push(...envBetas);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (Array.isArray(customBetas)) {
|
|
125
|
+
betas.push(...customBetas.filter(Boolean));
|
|
126
|
+
}
|
|
127
|
+
|
|
46
128
|
let mergedBetas = [...new Set([...betas, ...incomingBetasList])];
|
|
47
129
|
if (disableExperimentalBetas) {
|
|
48
|
-
|
|
130
|
+
mergedBetas = mergedBetas.filter((beta) => !EXPERIMENTAL_BETA_FLAGS.has(beta));
|
|
131
|
+
}
|
|
132
|
+
if (provider === "bedrock") {
|
|
133
|
+
return mergedBetas.filter((beta) => !BEDROCK_UNSUPPORTED_BETAS.has(beta)).join(",");
|
|
49
134
|
}
|
|
50
135
|
return mergedBetas.join(",");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const haiku = isHaikuModel(model);
|
|
54
|
-
const isRoundRobin = strategy === "round-robin";
|
|
55
|
-
|
|
56
|
-
if (!haiku) {
|
|
57
|
-
betas.push(CLAUDE_CODE_BETA_FLAG);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Files API beta is endpoint/content-scoped instead of globally applied.
|
|
61
|
-
if ((isFilesEndpoint || hasFileReferences) && !disableExperimentalBetas) {
|
|
62
|
-
betas.push("files-api-2025-04-14");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// NOTE: redact-thinking-2026-02-12 is in upstream 2.1.79+ base profile but
|
|
66
|
-
// intentionally NOT auto-included here — OpenCode users benefit from seeing
|
|
67
|
-
// thinking blocks. Available via /anthropic betas add redact-thinking-2026-02-12.
|
|
68
|
-
|
|
69
|
-
// CC 2.1.98 Proxyman capture shows these are NOT in the actual request headers
|
|
70
|
-
// even though they're in the source. Only include when explicitly requested.
|
|
71
|
-
// advanced-tool-use and fast-mode were causing fingerprint mismatch.
|
|
72
|
-
|
|
73
|
-
// Advisor tool — CC 2.1.98 always includes this.
|
|
74
|
-
betas.push("advisor-tool-2026-03-01");
|
|
75
|
-
|
|
76
|
-
if (isAdaptiveThinkingModel(model)) {
|
|
77
|
-
// Adaptive thinking models (Opus 4.6, Sonnet 4.6) use effort-based thinking controls.
|
|
78
|
-
betas.push(EFFORT_BETA_FLAG);
|
|
79
|
-
} else if (
|
|
80
|
-
!disableExperimentalBetas &&
|
|
81
|
-
!isTruthyEnv(process.env.DISABLE_INTERLEAVED_THINKING) &&
|
|
82
|
-
supportsThinking(model)
|
|
83
|
-
) {
|
|
84
|
-
betas.push("interleaved-thinking-2025-05-14");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// context-1m-2025-08-07 is only supported for API key users; OAuth provider does not support it.
|
|
88
|
-
// For OAuth (this plugin's only auth mode), compaction is gated by model.limit.input instead.
|
|
89
|
-
if (!disableExperimentalBetas && hasOneMillionContext(model) && provider !== "anthropic") {
|
|
90
|
-
betas.push("context-1m-2025-08-07");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Context management: upstream CC adds this for Claude 4+ models (thinking
|
|
94
|
-
// preservation) or when ant users opt in via USE_API_CONTEXT_MANAGEMENT.
|
|
95
|
-
if (!disableExperimentalBetas && supportsContextManagement(model)) {
|
|
96
|
-
betas.push("context-management-2025-06-27");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!disableExperimentalBetas && supportsStructuredOutputs(model) && isTruthyEnv(process.env.TENGU_TOOL_PEAR)) {
|
|
100
|
-
betas.push("structured-outputs-2025-12-15");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!disableExperimentalBetas && (provider === "vertex" || provider === "foundry") && supportsWebSearch(model)) {
|
|
104
|
-
betas.push("web-search-2025-03-05");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Upstream CC always sends prompt-caching-scope for firstParty providers.
|
|
108
|
-
// Skip in round-robin (zero cache hits, doubled costs).
|
|
109
|
-
if (!disableExperimentalBetas && !isRoundRobin) {
|
|
110
|
-
betas.push("prompt-caching-scope-2026-01-05");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (isMessagesCountTokensPath) {
|
|
114
|
-
betas.push(TOKEN_COUNTING_BETA_FLAG);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (process.env.ANTHROPIC_BETAS) {
|
|
118
|
-
const envBetas = process.env.ANTHROPIC_BETAS.split(",")
|
|
119
|
-
.map((b) => b.trim())
|
|
120
|
-
.filter(Boolean);
|
|
121
|
-
betas.push(...envBetas);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (Array.isArray(customBetas)) {
|
|
125
|
-
betas.push(...customBetas.filter(Boolean));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let mergedBetas = [...new Set([...betas, ...incomingBetasList])];
|
|
129
|
-
if (disableExperimentalBetas) {
|
|
130
|
-
mergedBetas = mergedBetas.filter((beta) => !EXPERIMENTAL_BETA_FLAGS.has(beta));
|
|
131
|
-
}
|
|
132
|
-
if (provider === "bedrock") {
|
|
133
|
-
return mergedBetas.filter((beta) => !BEDROCK_UNSUPPORTED_BETAS.has(beta)).join(",");
|
|
134
|
-
}
|
|
135
|
-
return mergedBetas.join(",");
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
@@ -140,8 +140,8 @@ export function buildAnthropicBetaHeader(
|
|
|
140
140
|
* Falls back to the original value if no shortcut is found.
|
|
141
141
|
*/
|
|
142
142
|
export function resolveBetaShortcut(value: string | undefined): string {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
if (!value) return "";
|
|
144
|
+
const trimmed = value.trim();
|
|
145
|
+
const mapped = BETA_SHORTCUTS.get(trimmed.toLowerCase());
|
|
146
|
+
return mapped || trimmed;
|
|
147
147
|
}
|