@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.
Files changed (107) hide show
  1. package/README.md +88 -88
  2. package/dist/opencode-anthropic-auth-cli.mjs +804 -507
  3. package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
  4. package/package.json +67 -59
  5. package/src/__tests__/billing-edge-cases.test.ts +59 -59
  6. package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
  7. package/src/__tests__/cc-comparison.test.ts +87 -87
  8. package/src/__tests__/cc-credentials.test.ts +254 -250
  9. package/src/__tests__/cch-drift-checker.test.ts +51 -51
  10. package/src/__tests__/cch-native-style.test.ts +56 -56
  11. package/src/__tests__/debug-gating.test.ts +42 -42
  12. package/src/__tests__/decomposition-smoke.test.ts +68 -68
  13. package/src/__tests__/fingerprint-regression.test.ts +575 -566
  14. package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
  15. package/src/__tests__/helpers/conversation-history.ts +119 -119
  16. package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
  17. package/src/__tests__/helpers/deferred.ts +69 -69
  18. package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
  19. package/src/__tests__/helpers/in-memory-storage.ts +88 -88
  20. package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
  21. package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
  22. package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
  23. package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
  24. package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
  25. package/src/__tests__/helpers/sse.ts +209 -209
  26. package/src/__tests__/index.parallel.test.ts +605 -595
  27. package/src/__tests__/sanitization-regex.test.ts +112 -112
  28. package/src/__tests__/state-bounds.test.ts +90 -90
  29. package/src/account-identity.test.ts +197 -192
  30. package/src/account-identity.ts +69 -67
  31. package/src/account-state.test.ts +86 -86
  32. package/src/account-state.ts +25 -25
  33. package/src/accounts/matching.test.ts +335 -0
  34. package/src/accounts/matching.ts +167 -0
  35. package/src/accounts/persistence.test.ts +345 -0
  36. package/src/accounts/persistence.ts +432 -0
  37. package/src/accounts/repair.test.ts +276 -0
  38. package/src/accounts/repair.ts +407 -0
  39. package/src/accounts.dedup.test.ts +621 -621
  40. package/src/accounts.test.ts +933 -929
  41. package/src/accounts.ts +633 -989
  42. package/src/backoff.test.ts +345 -345
  43. package/src/backoff.ts +219 -219
  44. package/src/betas.ts +124 -124
  45. package/src/bun-fetch.test.ts +345 -342
  46. package/src/bun-fetch.ts +424 -424
  47. package/src/bun-proxy.test.ts +25 -25
  48. package/src/bun-proxy.ts +209 -209
  49. package/src/cc-credentials.ts +111 -111
  50. package/src/circuit-breaker.test.ts +184 -184
  51. package/src/circuit-breaker.ts +169 -169
  52. package/src/cli/commands/auth.ts +963 -0
  53. package/src/cli/commands/config.ts +547 -0
  54. package/src/cli/formatting.test.ts +406 -0
  55. package/src/cli/formatting.ts +219 -0
  56. package/src/cli.ts +255 -2022
  57. package/src/commands/handlers/betas.ts +100 -0
  58. package/src/commands/handlers/config.ts +99 -0
  59. package/src/commands/handlers/files.ts +375 -0
  60. package/src/commands/oauth-flow.ts +181 -166
  61. package/src/commands/prompts.ts +61 -61
  62. package/src/commands/router.test.ts +421 -0
  63. package/src/commands/router.ts +143 -635
  64. package/src/config.test.ts +482 -482
  65. package/src/config.ts +412 -404
  66. package/src/constants.ts +48 -48
  67. package/src/drift/cch-constants.ts +95 -95
  68. package/src/env.ts +111 -105
  69. package/src/headers/billing.ts +33 -33
  70. package/src/headers/builder.ts +130 -130
  71. package/src/headers/cch.ts +75 -75
  72. package/src/headers/stainless.ts +25 -25
  73. package/src/headers/user-agent.ts +23 -23
  74. package/src/index.ts +436 -828
  75. package/src/models.ts +27 -27
  76. package/src/oauth.test.ts +102 -102
  77. package/src/oauth.ts +178 -178
  78. package/src/parent-pid-watcher.test.ts +148 -148
  79. package/src/parent-pid-watcher.ts +69 -69
  80. package/src/plugin-helpers.ts +82 -82
  81. package/src/refresh-helpers.ts +145 -139
  82. package/src/refresh-lock.test.ts +94 -94
  83. package/src/refresh-lock.ts +93 -93
  84. package/src/request/body.history.test.ts +579 -571
  85. package/src/request/body.ts +255 -255
  86. package/src/request/metadata.ts +65 -65
  87. package/src/request/retry.test.ts +156 -156
  88. package/src/request/retry.ts +67 -67
  89. package/src/request/url.ts +21 -21
  90. package/src/request-orchestration-helpers.ts +648 -0
  91. package/src/response/index.ts +5 -5
  92. package/src/response/mcp.ts +58 -58
  93. package/src/response/streaming.test.ts +313 -311
  94. package/src/response/streaming.ts +412 -410
  95. package/src/rotation.test.ts +304 -301
  96. package/src/rotation.ts +205 -205
  97. package/src/storage.test.ts +547 -547
  98. package/src/storage.ts +315 -291
  99. package/src/system-prompt/builder.ts +38 -38
  100. package/src/system-prompt/index.ts +5 -5
  101. package/src/system-prompt/normalize.ts +60 -60
  102. package/src/system-prompt/sanitize.ts +30 -30
  103. package/src/thinking.ts +21 -20
  104. package/src/token-refresh.test.ts +265 -265
  105. package/src/token-refresh.ts +219 -214
  106. package/src/types.ts +30 -30
  107. package/dist/bun-proxy.mjs +0 -291
package/src/betas.ts CHANGED
@@ -1,138 +1,138 @@
1
1
  import {
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,
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
- hasOneMillionContext,
12
- isAdaptiveThinkingModel,
13
- isHaikuModel,
14
- supportsContextManagement,
15
- supportsStructuredOutputs,
16
- supportsThinking,
17
- supportsWebSearch,
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
- 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,
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
- 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");
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
- betas.push(TOKEN_COUNTING_BETA_FLAG);
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
- mergedBetas = mergedBetas.filter((beta) => !EXPERIMENTAL_BETA_FLAGS.has(beta));
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
- if (!value) return "";
144
- const trimmed = value.trim();
145
- const mapped = BETA_SHORTCUTS.get(trimmed.toLowerCase());
146
- return mapped || trimmed;
143
+ if (!value) return "";
144
+ const trimmed = value.trim();
145
+ const mapped = BETA_SHORTCUTS.get(trimmed.toLowerCase());
146
+ return mapped || trimmed;
147
147
  }