opencode-anthropic-fix 0.0.19 → 0.0.20

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 CHANGED
@@ -43,8 +43,9 @@ The [original plugin](https://github.com/anomalyco/opencode-anthropic-auth) prov
43
43
  - **Standalone CLI** — manage accounts without opening OpenCode
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
- - **Adaptive thinking for Opus 4.6** — automatically uses `adaptive-thinking-2026-01-28` and maps `budgetTokens` to effort levels (`low`/`medium`/`high`/`max`)
46
+ - **Adaptive thinking for Opus 4.6** — automatically uses `adaptive-thinking-2026-01-28` and maps `budgetTokens` to effort levels (`low`/`medium`/`high`)
47
47
  - **1M context limit override** — patches `model.limit.context` so OpenCode compacts at the right threshold while `models.dev` catches up
48
+ - **Runtime config + custom betas** — `/anthropic set`, `/anthropic config`, and `/anthropic betas` slash commands for live feature toggling without restarting OpenCode
48
49
 
49
50
  ## Installation
50
51
 
@@ -231,20 +232,65 @@ Most commands have short aliases: `ln`, `lo`, `ra`, `rf`, `ls`, `st`, `sw`, `en`
231
232
 
232
233
  ## Slash Commands in OpenCode
233
234
 
234
- The plugin also registers a built-in `/anthropic` slash command so you can manage accounts without leaving OpenCode.
235
+ The plugin registers a built-in `/anthropic` slash command for account management, feature toggles, and custom beta headers — all without leaving OpenCode.
235
236
 
236
- ### Common examples
237
+ ### Account management
237
238
 
238
239
  ```text
239
- /anthropic # list (default)
240
+ /anthropic # list accounts (default)
240
241
  /anthropic usage # full account list + quota windows
241
242
  /anthropic switch 2
242
243
  /anthropic refresh 1
243
244
  /anthropic logout 2 # revoke tokens and remove account 2
244
245
  /anthropic logout --all # revoke all tokens and clear all accounts
245
- /anthropic strategy hybrid
246
246
  /anthropic stats
247
- /anthropic config
247
+ ```
248
+
249
+ ### Runtime configuration
250
+
251
+ Toggle features live without restarting OpenCode. Changes are persisted to `anthropic-auth.json`.
252
+
253
+ ```text
254
+ /anthropic config # show all current settings
255
+ /anthropic set emulation on # enable/disable Claude signature emulation
256
+ /anthropic set compaction minimal # set prompt compaction mode (minimal/off)
257
+ /anthropic set 1m-context on # enable/disable 1M context limit override
258
+ /anthropic set idle-refresh on # enable/disable idle account refresh
259
+ /anthropic set strategy round-robin # change account selection strategy
260
+ /anthropic set debug on # enable/disable debug logging
261
+ /anthropic set quiet on # suppress non-error toasts
262
+ ```
263
+
264
+ ### Custom beta headers
265
+
266
+ Add or remove beta flags that get included in every `anthropic-beta` header. Persisted across sessions.
267
+
268
+ ```text
269
+ /anthropic betas # show active betas (auto + custom) with presets
270
+ /anthropic betas add <beta-name> # add a custom beta
271
+ /anthropic betas remove <beta-name> # remove a custom beta
272
+ ```
273
+
274
+ **Available preset betas** (shown by `/anthropic betas`):
275
+
276
+ | Beta | Description |
277
+ | --------------------------------- | ------------------------------------------------- |
278
+ | `prompt-caching-scope-2026-01-05` | Cache control with `scope: "org"` — free perf win |
279
+ | `context-management-2025-06-27` | Server-side auto-summarization when context fills |
280
+ | `structured-outputs-2025-12-15` | Strict JSON schema output enforcement |
281
+ | `tool-examples-2025-10-29` | Input/output examples in tool definitions |
282
+ | `code-execution-2025-08-25` | Python sandbox execution by the model |
283
+ | `files-api-2025-04-14` | Upload once, reuse file ID across requests |
284
+ | `compact-2026-01-12` | Conversation compaction endpoint |
285
+ | `mcp-servers-2025-12-04` | MCP servers in API request payload |
286
+ | `web-search-2025-03-05` | Web search (Vertex/Foundry only) |
287
+
288
+ Example workflow:
289
+
290
+ ```text
291
+ /anthropic betas add prompt-caching-scope-2026-01-05
292
+ /anthropic betas add context-management-2025-06-27
293
+ /anthropic betas # verify
248
294
  ```
249
295
 
250
296
  ### OAuth flows from slash command
@@ -272,11 +318,11 @@ Pending slash OAuth flows expire after 10 minutes. If completion fails with an e
272
318
 
273
319
  Control how the plugin picks which account to use for each request.
274
320
 
275
- | Strategy | Behavior | Best For |
276
- | ---------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------- |
277
- | **`sticky`** (default) | Stay on one account until it fails or is rate-limited | Single account, or when you want predictable behavior |
278
- | **`round-robin`** | Rotate through accounts on every request | Spreading load evenly across accounts |
279
- | **`hybrid`** | Score-based selection with stickiness bias. Considers health, token budget, and freshness | Multiple accounts with varying rate limits |
321
+ | Strategy | Behavior | Best For |
322
+ | --------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------- |
323
+ | **`round-robin`** (default) | Rotate through accounts on every request | Spreading load evenly across accounts |
324
+ | **`sticky`** | Stay on one account until it fails or is rate-limited | Single account, or when you want predictable behavior |
325
+ | **`hybrid`** | Score-based selection with stickiness bias. Considers health, token budget, and freshness | Multiple accounts with varying rate limits |
280
326
 
281
327
  ### Change Strategy
282
328
 
@@ -297,8 +343,8 @@ Configuration is stored at `~/.config/opencode/anthropic-auth.json`. All setting
297
343
 
298
344
  ```jsonc
299
345
  {
300
- // Account selection strategy: "sticky" | "round-robin" | "hybrid"
301
- "account_selection_strategy": "sticky",
346
+ // Account selection strategy: "round-robin" | "sticky" | "hybrid"
347
+ "account_selection_strategy": "round-robin",
302
348
 
303
349
  // Seconds before consecutive failure count resets (60-7200)
304
350
  "failure_ttl_seconds": 3600,
@@ -325,8 +371,8 @@ Configuration is stored at `~/.config/opencode/anthropic-auth.json`. All setting
325
371
  // Only applied for OAuth (Max Plan) sessions — API key users use the
326
372
  // context-1m-2025-08-07 beta header instead.
327
373
  "override_model_limits": {
328
- // Enable/disable the override
329
- "enabled": true,
374
+ // Enable/disable the override (default: off — enable if you need 1M context)
375
+ "enabled": false,
330
376
  // Context window to inject (tokens). Default: 1_000_000.
331
377
  "context": 1000000,
332
378
  // Max output tokens to inject. 0 = leave the model's default unchanged.
@@ -351,6 +397,9 @@ Configuration is stored at `~/.config/opencode/anthropic-auth.json`. All setting
351
397
  "initial_tokens": 50,
352
398
  },
353
399
 
400
+ // Custom beta headers (added to every request via /anthropic betas add)
401
+ "custom_betas": [],
402
+
354
403
  // Toast notification settings
355
404
  "toasts": {
356
405
  // Suppress non-error toasts (account status, switching)
package/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { AccountManager } from "./lib/accounts.mjs";
7
7
  import { authorize, exchange, refreshToken } from "./lib/oauth.mjs";
8
- import { loadConfig, CLIENT_ID, getConfigDir } from "./lib/config.mjs";
8
+ import { loadConfig, loadConfigFresh, saveConfig, CLIENT_ID, getConfigDir } from "./lib/config.mjs";
9
9
  import { loadAccounts, saveAccounts, clearAccounts, createDefaultStats } from "./lib/storage.mjs";
10
10
  import { applyOAuthCredentials, resetAccountTracking } from "./lib/account-state.mjs";
11
11
  import { acquireRefreshLock, releaseRefreshLock } from "./lib/refresh-lock.mjs";
@@ -659,9 +659,10 @@ function buildSystemPromptBlocks(system, signature, messages) {
659
659
  * @param {boolean} signatureEnabled
660
660
  * @param {string} model
661
661
  * @param {"anthropic" | "bedrock" | "vertex" | "foundry"} provider
662
+ * @param {string[]} [customBetas]
662
663
  * @returns {string}
663
664
  */
664
- function buildAnthropicBetaHeader(incomingBeta, signatureEnabled, model, provider) {
665
+ function buildAnthropicBetaHeader(incomingBeta, signatureEnabled, model, provider, customBetas) {
665
666
  const incomingBetasList = incomingBeta
666
667
  .split(",")
667
668
  .map((b) => b.trim())
@@ -729,6 +730,10 @@ function buildAnthropicBetaHeader(incomingBeta, signatureEnabled, model, provide
729
730
 
730
731
  betas.push("fine-grained-tool-streaming-2025-05-14");
731
732
 
733
+ if (Array.isArray(customBetas)) {
734
+ betas.push(...customBetas.filter(Boolean));
735
+ }
736
+
732
737
  const mergedBetas = [...new Set([...betas, ...incomingBetasList])];
733
738
  if (provider === "bedrock") {
734
739
  return mergedBetas.filter((beta) => !BEDROCK_UNSUPPORTED_BETAS.has(beta)).join(",");
@@ -887,7 +892,7 @@ function buildRequestHeaders(input, requestInit, accessToken, requestBody, reque
887
892
  const incomingBeta = requestHeaders.get("anthropic-beta") || "";
888
893
  const { model, tools, messages } = parseRequestBodyMetadata(requestBody);
889
894
  const provider = detectProvider(requestUrl);
890
- const mergedBetas = buildAnthropicBetaHeader(incomingBeta, signature.enabled, model, provider);
895
+ const mergedBetas = buildAnthropicBetaHeader(incomingBeta, signature.enabled, model, provider, signature.customBetas);
891
896
 
892
897
  const authTokenOverride = process.env.ANTHROPIC_AUTH_TOKEN?.trim();
893
898
  const bearerToken = authTokenOverride || accessToken;
@@ -1900,6 +1905,140 @@ export async function AnthropicAuthPlugin({ client }) {
1900
1905
  return;
1901
1906
  }
1902
1907
 
1908
+ // /anthropic config — show effective config
1909
+ if (primary === "config") {
1910
+ const fresh = loadConfigFresh();
1911
+ const lines = [
1912
+ "▣ Anthropic Config",
1913
+ "",
1914
+ `strategy: ${fresh.account_selection_strategy}`,
1915
+ `emulation: ${fresh.signature_emulation.enabled ? "on" : "off"}`,
1916
+ `compaction: ${fresh.signature_emulation.prompt_compaction}`,
1917
+ `1m-context: ${fresh.override_model_limits.enabled ? "on" : "off"}`,
1918
+ `idle-refresh: ${fresh.idle_refresh.enabled ? "on" : "off"}`,
1919
+ `debug: ${fresh.debug ? "on" : "off"}`,
1920
+ `quiet: ${fresh.toasts.quiet ? "on" : "off"}`,
1921
+ `custom_betas: ${fresh.custom_betas.length ? fresh.custom_betas.join(", ") : "(none)"}`,
1922
+ ];
1923
+ await sendCommandMessage(input.sessionID, lines.join("\n"));
1924
+ return;
1925
+ }
1926
+
1927
+ // /anthropic set <key> <value> — toggle features at runtime
1928
+ if (primary === "set") {
1929
+ const key = (args[1] || "").toLowerCase();
1930
+ const value = (args[2] || "").toLowerCase();
1931
+ /** @type {Record<string, () => void>} */
1932
+ const setters = {
1933
+ emulation: () =>
1934
+ saveConfig({ signature_emulation: { enabled: value === "on" || value === "1" || value === "true" } }),
1935
+ compaction: () =>
1936
+ saveConfig({ signature_emulation: { prompt_compaction: value === "off" ? "off" : "minimal" } }),
1937
+ "1m-context": () =>
1938
+ saveConfig({ override_model_limits: { enabled: value === "on" || value === "1" || value === "true" } }),
1939
+ "idle-refresh": () =>
1940
+ saveConfig({ idle_refresh: { enabled: value === "on" || value === "1" || value === "true" } }),
1941
+ debug: () => saveConfig({ debug: value === "on" || value === "1" || value === "true" }),
1942
+ quiet: () => saveConfig({ toasts: { quiet: value === "on" || value === "1" || value === "true" } }),
1943
+ strategy: () => {
1944
+ const valid = ["sticky", "round-robin", "hybrid"];
1945
+ if (valid.includes(value)) saveConfig({ account_selection_strategy: value });
1946
+ else throw new Error(`Invalid strategy. Valid: ${valid.join(", ")}`);
1947
+ },
1948
+ };
1949
+
1950
+ if (!key || !setters[key]) {
1951
+ const keys = Object.keys(setters).join(", ");
1952
+ await sendCommandMessage(
1953
+ input.sessionID,
1954
+ `▣ Anthropic Set\n\nUsage: /anthropic set <key> <value>\nKeys: ${keys}\nValues: on/off (or specific values for strategy/compaction)`,
1955
+ );
1956
+ return;
1957
+ }
1958
+ if (!value) {
1959
+ await sendCommandMessage(input.sessionID, `▣ Anthropic Set\n\nMissing value for "${key}".`);
1960
+ return;
1961
+ }
1962
+ setters[key]();
1963
+ // Reload config into runtime
1964
+ Object.assign(config, loadConfigFresh());
1965
+ await sendCommandMessage(input.sessionID, `▣ Anthropic Set\n\n${key} = ${value}`);
1966
+ return;
1967
+ }
1968
+
1969
+ // /anthropic betas [add|remove <beta>] — show/manage custom betas
1970
+ if (primary === "betas") {
1971
+ const action = (args[1] || "").toLowerCase();
1972
+
1973
+ if (!action || action === "list") {
1974
+ const fresh = loadConfigFresh();
1975
+ const lines = [
1976
+ "▣ Anthropic Betas",
1977
+ "",
1978
+ "Preset betas (auto-computed per model/provider):",
1979
+ " oauth-2025-04-20, claude-code-20250219,",
1980
+ " interleaved-thinking-2025-05-14 OR adaptive-thinking-2026-01-28,",
1981
+ " fine-grained-tool-streaming-2025-05-14",
1982
+ "",
1983
+ `Custom betas: ${fresh.custom_betas.length ? fresh.custom_betas.join(", ") : "(none)"}`,
1984
+ "",
1985
+ "Toggleable presets:",
1986
+ " /anthropic betas add structured-outputs-2025-12-15",
1987
+ " /anthropic betas add context-management-2025-06-27",
1988
+ " /anthropic betas add prompt-caching-scope-2026-01-05",
1989
+ " /anthropic betas add tool-examples-2025-10-29",
1990
+ " /anthropic betas add web-search-2025-03-05",
1991
+ " /anthropic betas add code-execution-2025-08-25",
1992
+ " /anthropic betas add compact-2026-01-12",
1993
+ " /anthropic betas add mcp-servers-2025-12-04",
1994
+ " /anthropic betas add files-api-2025-04-14",
1995
+ "",
1996
+ "Remove: /anthropic betas remove <beta>",
1997
+ ];
1998
+ await sendCommandMessage(input.sessionID, lines.join("\n"));
1999
+ return;
2000
+ }
2001
+
2002
+ if (action === "add") {
2003
+ const beta = args[2]?.trim();
2004
+ if (!beta) {
2005
+ await sendCommandMessage(input.sessionID, "▣ Anthropic Betas\n\nUsage: /anthropic betas add <beta-name>");
2006
+ return;
2007
+ }
2008
+ const fresh = loadConfigFresh();
2009
+ const current = fresh.custom_betas || [];
2010
+ if (current.includes(beta)) {
2011
+ await sendCommandMessage(input.sessionID, `▣ Anthropic Betas\n\n"${beta}" already added.`);
2012
+ return;
2013
+ }
2014
+ saveConfig({ custom_betas: [...current, beta] });
2015
+ Object.assign(config, loadConfigFresh());
2016
+ await sendCommandMessage(input.sessionID, `▣ Anthropic Betas\n\nAdded: ${beta}`);
2017
+ return;
2018
+ }
2019
+
2020
+ if (action === "remove" || action === "rm") {
2021
+ const beta = args[2]?.trim();
2022
+ if (!beta) {
2023
+ await sendCommandMessage(input.sessionID, "▣ Anthropic Betas\n\nUsage: /anthropic betas remove <beta-name>");
2024
+ return;
2025
+ }
2026
+ const fresh = loadConfigFresh();
2027
+ const current = fresh.custom_betas || [];
2028
+ if (!current.includes(beta)) {
2029
+ await sendCommandMessage(input.sessionID, `▣ Anthropic Betas\n\n"${beta}" not in custom betas.`);
2030
+ return;
2031
+ }
2032
+ saveConfig({ custom_betas: current.filter((b) => b !== beta) });
2033
+ Object.assign(config, loadConfigFresh());
2034
+ await sendCommandMessage(input.sessionID, `▣ Anthropic Betas\n\nRemoved: ${beta}`);
2035
+ return;
2036
+ }
2037
+
2038
+ await sendCommandMessage(input.sessionID, "▣ Anthropic Betas\n\nUsage: /anthropic betas [add|remove <beta>]");
2039
+ return;
2040
+ }
2041
+
1903
2042
  // Interactive CLI command is not compatible with slash flow.
1904
2043
  if (primary === "manage" || primary === "mg") {
1905
2044
  await sendCommandMessage(
@@ -2160,7 +2299,7 @@ export async function AnthropicAuthPlugin({ client }) {
2160
2299
  input.command ??= {};
2161
2300
  input.command["anthropic"] = {
2162
2301
  template: "/anthropic",
2163
- description: "Manage Anthropic multi-account auth (status, usage, switch, login, reauth, logout)",
2302
+ description: "Manage Anthropic auth, config, and betas (usage, login, config, set, betas, switch)",
2164
2303
  };
2165
2304
  },
2166
2305
  "command.execute.before": async (input) => {
@@ -2388,6 +2527,7 @@ export async function AnthropicAuthPlugin({ client }) {
2388
2527
  const requestHeaders = buildRequestHeaders(input, requestInit, accessToken, body, requestUrl, {
2389
2528
  enabled: signatureEmulationEnabled,
2390
2529
  claudeCliVersion,
2530
+ customBetas: config.custom_betas,
2391
2531
  });
2392
2532
  // Execute the request
2393
2533
  let response;
package/lib/config.mjs CHANGED
@@ -51,6 +51,7 @@ import { homedir } from "node:os";
51
51
  * @property {boolean} debug
52
52
  * @property {{ enabled: boolean, fetch_claude_code_version_on_startup: boolean, prompt_compaction: 'minimal' | 'off' }} signature_emulation
53
53
  * @property {OverrideModelLimitsConfig} override_model_limits
54
+ * @property {string[]} custom_betas
54
55
  * @property {HealthScoreConfig} health_score
55
56
  * @property {TokenBucketConfig} token_bucket
56
57
  * @property {ToastConfig} toasts
@@ -60,7 +61,7 @@ import { homedir } from "node:os";
60
61
 
61
62
  /** @type {AnthropicAuthConfig} */
62
63
  export const DEFAULT_CONFIG = {
63
- account_selection_strategy: "sticky",
64
+ account_selection_strategy: "round-robin",
64
65
  failure_ttl_seconds: 3600,
65
66
  debug: false,
66
67
  signature_emulation: {
@@ -69,10 +70,11 @@ export const DEFAULT_CONFIG = {
69
70
  prompt_compaction: "minimal",
70
71
  },
71
72
  override_model_limits: {
72
- enabled: true,
73
+ enabled: false,
73
74
  context: 1_000_000,
74
75
  output: 0,
75
76
  },
77
+ custom_betas: [],
76
78
  health_score: {
77
79
  initial: 70,
78
80
  success_reward: 1,
@@ -112,6 +114,7 @@ function createDefaultConfig() {
112
114
  ...DEFAULT_CONFIG,
113
115
  signature_emulation: { ...DEFAULT_CONFIG.signature_emulation },
114
116
  override_model_limits: { ...DEFAULT_CONFIG.override_model_limits },
117
+ custom_betas: [...DEFAULT_CONFIG.custom_betas],
115
118
  health_score: { ...DEFAULT_CONFIG.health_score },
116
119
  token_bucket: { ...DEFAULT_CONFIG.token_bucket },
117
120
  toasts: { ...DEFAULT_CONFIG.toasts },
@@ -197,6 +200,11 @@ function validateConfig(raw) {
197
200
  };
198
201
  }
199
202
 
203
+ // Custom betas
204
+ if (Array.isArray(raw.custom_betas)) {
205
+ config.custom_betas = raw.custom_betas.filter((b) => typeof b === "string" && b.trim()).map((b) => b.trim());
206
+ }
207
+
200
208
  // Health score sub-config
201
209
  if (raw.health_score && typeof raw.health_score === "object") {
202
210
  const hs = /** @type {Record<string, unknown>} */ (raw.health_score);
@@ -408,19 +416,56 @@ export function loadRawConfig() {
408
416
  * Uses atomic write (temp + rename) for safety.
409
417
  * @param {Record<string, unknown>} updates - Keys to merge into the config file
410
418
  */
419
+ /**
420
+ * Deep merge source into target (one level deep for objects, replace for arrays/primitives).
421
+ * @param {Record<string, unknown>} target
422
+ * @param {Record<string, unknown>} source
423
+ * @returns {Record<string, unknown>}
424
+ */
425
+ function deepMergeConfig(target, source) {
426
+ const result = { ...target };
427
+ for (const [key, value] of Object.entries(source)) {
428
+ if (
429
+ value &&
430
+ typeof value === "object" &&
431
+ !Array.isArray(value) &&
432
+ typeof result[key] === "object" &&
433
+ result[key] &&
434
+ !Array.isArray(result[key])
435
+ ) {
436
+ result[key] = {
437
+ .../** @type {Record<string, unknown>} */ (result[key]),
438
+ .../** @type {Record<string, unknown>} */ (value),
439
+ };
440
+ } else {
441
+ result[key] = value;
442
+ }
443
+ }
444
+ return result;
445
+ }
446
+
411
447
  export function saveConfig(updates) {
412
448
  const configPath = getConfigPath();
413
449
  const dir = dirname(configPath);
414
450
  mkdirSync(dir, { recursive: true });
415
451
 
416
- // Read existing raw config
452
+ // Re-read from disk to avoid clobbering concurrent writes
417
453
  const existing = loadRawConfig();
418
454
 
419
- // Merge updates
420
- const merged = { ...existing, ...updates };
455
+ // Deep merge (one level) so sub-config objects are merged, not replaced
456
+ const merged = deepMergeConfig(existing, updates);
421
457
 
422
458
  // Atomic write: temp file + rename
423
459
  const tmpPath = configPath + `.tmp.${process.pid}`;
424
460
  writeFileSync(tmpPath, JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 0o600 });
425
461
  renameSync(tmpPath, configPath);
426
462
  }
463
+
464
+ /**
465
+ * Load config fresh from disk (bypassing any startup cache).
466
+ * Useful for slash commands that need current on-disk state.
467
+ * @returns {AnthropicAuthConfig}
468
+ */
469
+ export function loadConfigFresh() {
470
+ return loadConfig();
471
+ }
@@ -10,7 +10,7 @@ vi.mock("node:fs", () => ({
10
10
 
11
11
  describe("DEFAULT_CONFIG", () => {
12
12
  it("has expected default strategy", () => {
13
- expect(DEFAULT_CONFIG.account_selection_strategy).toBe("sticky");
13
+ expect(DEFAULT_CONFIG.account_selection_strategy).toBe("round-robin");
14
14
  });
15
15
 
16
16
  it("has expected health score defaults", () => {
@@ -114,7 +114,7 @@ describe("loadConfig", () => {
114
114
  existsSync.mockReturnValue(true);
115
115
  readFileSync.mockReturnValue(JSON.stringify({ account_selection_strategy: "invalid" }));
116
116
  const config = loadConfig();
117
- expect(config.account_selection_strategy).toBe("sticky");
117
+ expect(config.account_selection_strategy).toBe("round-robin");
118
118
  });
119
119
 
120
120
  it("accepts boolean debug", () => {
@@ -199,7 +199,7 @@ describe("loadConfig", () => {
199
199
  existsSync.mockReturnValue(false);
200
200
  process.env.OPENCODE_ANTHROPIC_STRATEGY = "invalid";
201
201
  const config = loadConfig();
202
- expect(config.account_selection_strategy).toBe("sticky");
202
+ expect(config.account_selection_strategy).toBe("round-robin");
203
203
  });
204
204
 
205
205
  it("enables debug from OPENCODE_ANTHROPIC_DEBUG=1", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-anthropic-fix",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "main": "./index.mjs",
5
5
  "files": [
6
6
  "index.mjs",