opencode-anthropic-multi-account 0.2.18 → 0.2.19

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/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Plugin } from '@opencode-ai/plugin';
2
2
 
3
3
  declare const ClaudeMultiAuthPlugin: Plugin;
4
+ declare function setProviderModelsObserverForTest(observer: ((models: Record<string, unknown>) => void) | null): void;
5
+ declare function resetProviderModelsObserverForTest(): void;
4
6
 
5
- export { ClaudeMultiAuthPlugin };
7
+ export { ClaudeMultiAuthPlugin, resetProviderModelsObserverForTest, setProviderModelsObserverForTest };
package/dist/index.js CHANGED
@@ -1529,8 +1529,52 @@ function loadClaudeIdentity() {
1529
1529
 
1530
1530
  // src/upstream-request.ts
1531
1531
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
1532
+
1533
+ // src/model-capabilities.ts
1534
+ var runtimeModelCapabilities = /* @__PURE__ */ new Map();
1535
+ function isRecord(value) {
1536
+ return typeof value === "object" && value !== null;
1537
+ }
1538
+ function readNumber(value) {
1539
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1540
+ }
1541
+ function readBoolean(value) {
1542
+ return typeof value === "boolean" ? value : void 0;
1543
+ }
1544
+ function normalizeModelId(modelId) {
1545
+ const trimmed = modelId.trim().toLowerCase();
1546
+ const slashIndex = trimmed.indexOf("/");
1547
+ return slashIndex >= 0 ? trimmed.slice(slashIndex + 1) : trimmed;
1548
+ }
1549
+ function readLimitOutput(raw) {
1550
+ const limit = isRecord(raw.limit) ? raw.limit : void 0;
1551
+ const capabilityLimit = isRecord(raw.capabilities) && isRecord(raw.capabilities.limit) ? raw.capabilities.limit : void 0;
1552
+ return readNumber(limit?.output) ?? readNumber(capabilityLimit?.output);
1553
+ }
1554
+ function readThinkingSupport(raw) {
1555
+ return readBoolean(raw.reasoning) ?? readBoolean(raw.thinking) ?? readBoolean(raw.supportsThinking) ?? (isRecord(raw.capabilities) ? readBoolean(raw.capabilities.reasoning) ?? readBoolean(raw.capabilities.thinking) ?? readBoolean(raw.capabilities.supportsThinking) : void 0);
1556
+ }
1557
+ function ingestProviderModelsCapabilities(models) {
1558
+ runtimeModelCapabilities.clear();
1559
+ for (const [key, value] of Object.entries(models)) {
1560
+ if (!isRecord(value)) {
1561
+ continue;
1562
+ }
1563
+ const resolvedId = typeof value.id === "string" ? value.id : key;
1564
+ const capability = {
1565
+ maxOutputTokens: readLimitOutput(value),
1566
+ supportsThinking: readThinkingSupport(value)
1567
+ };
1568
+ runtimeModelCapabilities.set(normalizeModelId(resolvedId), capability);
1569
+ runtimeModelCapabilities.set(normalizeModelId(key), capability);
1570
+ }
1571
+ }
1572
+ function getRuntimeModelCapability(modelId) {
1573
+ return runtimeModelCapabilities.get(normalizeModelId(modelId));
1574
+ }
1575
+
1576
+ // src/upstream-request.ts
1532
1577
  var BILLING_SEED = "59cf53e54c78";
1533
- var DEFAULT_CC_VERSION = "2.1.100";
1534
1578
  var SESSION_IDLE_ROTATE_MS = 15 * 60 * 1e3;
1535
1579
  var MAX_TOOL_RESULT_TEXT_LENGTH = 30 * 1024;
1536
1580
  var TRUNCATION_SUFFIX = "[...truncated]";
@@ -1587,7 +1631,7 @@ function getActiveSessionId() {
1587
1631
  function getUpstreamSessionId() {
1588
1632
  return getActiveSessionId();
1589
1633
  }
1590
- function isRecord(value) {
1634
+ function isRecord2(value) {
1591
1635
  return typeof value === "object" && value !== null;
1592
1636
  }
1593
1637
  function cloneBody(value) {
@@ -1611,7 +1655,7 @@ function stripCacheControl(value) {
1611
1655
  }
1612
1656
  return;
1613
1657
  }
1614
- if (!isRecord(value)) {
1658
+ if (!isRecord2(value)) {
1615
1659
  return;
1616
1660
  }
1617
1661
  delete value.cache_control;
@@ -1633,11 +1677,15 @@ function sanitizeMessageBlock(block) {
1633
1677
  if (!Array.isArray(block.content)) {
1634
1678
  return;
1635
1679
  }
1636
- for (const item of block.content) {
1637
- if (isRecord(item) && typeof item.text === "string") {
1638
- item.text = truncateToolResultText(sanitizeAndScrubText(item.text));
1680
+ block.content = block.content.map((item) => {
1681
+ if (isRecord2(item) && typeof item.text === "string") {
1682
+ return {
1683
+ ...item,
1684
+ text: truncateToolResultText(sanitizeAndScrubText(item.text))
1685
+ };
1639
1686
  }
1640
- }
1687
+ return item;
1688
+ }).filter((item) => !isRecord2(item) || typeof item.text !== "string" || item.text.trim().length > 0);
1641
1689
  }
1642
1690
  function stripAssistantThinkingBlocks(messages) {
1643
1691
  for (const message of messages) {
@@ -1655,7 +1703,7 @@ function hasMeaningfulContent(content) {
1655
1703
  return false;
1656
1704
  }
1657
1705
  return content.some((block) => {
1658
- if (!isRecord(block)) {
1706
+ if (!isRecord2(block)) {
1659
1707
  return false;
1660
1708
  }
1661
1709
  if (block.type === "tool_use") {
@@ -1679,6 +1727,79 @@ function trimTrailingEmptyTurns(messages) {
1679
1727
  messages.pop();
1680
1728
  }
1681
1729
  }
1730
+ function compactMessageContent(messages) {
1731
+ for (const message of messages) {
1732
+ if (!Array.isArray(message.content)) {
1733
+ continue;
1734
+ }
1735
+ message.content = message.content.filter((block) => {
1736
+ if (!isRecord2(block)) {
1737
+ return false;
1738
+ }
1739
+ if (block.type === "text") {
1740
+ return typeof block.text !== "string" || block.text.trim().length > 0;
1741
+ }
1742
+ if (block.type === "tool_result" && Array.isArray(block.content)) {
1743
+ return block.content.length > 0;
1744
+ }
1745
+ return true;
1746
+ });
1747
+ }
1748
+ }
1749
+ function stripUnsupportedSamplingFields(body) {
1750
+ delete body.temperature;
1751
+ delete body.top_p;
1752
+ delete body.top_k;
1753
+ }
1754
+ function stripThinkingControlFields(body) {
1755
+ delete body.thinking;
1756
+ delete body.context_management;
1757
+ delete body.output_config;
1758
+ }
1759
+ var ADAPTIVE_THINKING_MODEL_MATCHERS = [
1760
+ (modelId) => modelId.includes("claude-sonnet-4-6") || modelId.includes("claude-sonnet-4.6"),
1761
+ (modelId) => modelId.includes("claude-opus-4-6") || modelId.includes("claude-opus-4.6"),
1762
+ (modelId) => /claude-opus-4[-._]([7-9]|\d{2,})/.test(modelId)
1763
+ ];
1764
+ var LARGE_OUTPUT_MODEL_MATCHERS = [
1765
+ (modelId) => modelId.includes("claude-opus-4-6") || modelId.includes("claude-opus-4.6"),
1766
+ (modelId) => /claude-opus-4[-._]([7-9]|\d{2,})/.test(modelId)
1767
+ ];
1768
+ var DEFAULT_MAX_OUTPUT_TOKENS = 64e3;
1769
+ var LARGE_MODEL_MAX_OUTPUT_TOKENS = 128e3;
1770
+ function normalizeModelId2(modelId) {
1771
+ return modelId.trim().toLowerCase();
1772
+ }
1773
+ function supportsAdaptiveThinking(modelId) {
1774
+ const runtimeCapability = getRuntimeModelCapability(modelId);
1775
+ if (typeof runtimeCapability?.supportsThinking === "boolean") {
1776
+ return runtimeCapability.supportsThinking;
1777
+ }
1778
+ const normalized = normalizeModelId2(modelId);
1779
+ if (normalized.includes("haiku")) {
1780
+ return false;
1781
+ }
1782
+ return ADAPTIVE_THINKING_MODEL_MATCHERS.some((matches) => matches(normalized));
1783
+ }
1784
+ function getModelMaxOutputTokens(modelId) {
1785
+ const runtimeCapability = getRuntimeModelCapability(modelId);
1786
+ if (typeof runtimeCapability?.maxOutputTokens === "number") {
1787
+ return runtimeCapability.maxOutputTokens;
1788
+ }
1789
+ const normalized = normalizeModelId2(modelId);
1790
+ return LARGE_OUTPUT_MODEL_MATCHERS.some((matches) => matches(normalized)) ? LARGE_MODEL_MAX_OUTPUT_TOKENS : DEFAULT_MAX_OUTPUT_TOKENS;
1791
+ }
1792
+ function resolveMaxTokens(modelId, requestedMaxTokens) {
1793
+ const modelCap = getModelMaxOutputTokens(modelId);
1794
+ if (typeof requestedMaxTokens !== "number" || !Number.isFinite(requestedMaxTokens)) {
1795
+ return modelCap;
1796
+ }
1797
+ const normalized = Math.floor(requestedMaxTokens);
1798
+ if (normalized <= 0) {
1799
+ return modelCap;
1800
+ }
1801
+ return Math.min(normalized, modelCap);
1802
+ }
1682
1803
  function normalizeSystemTexts(system) {
1683
1804
  if (typeof system === "string") {
1684
1805
  const next = sanitizeAndScrubText(system);
@@ -1696,7 +1817,7 @@ function normalizeSystemTexts(system) {
1696
1817
  }
1697
1818
  continue;
1698
1819
  }
1699
- if (isRecord(entry) && typeof entry.text === "string") {
1820
+ if (isRecord2(entry) && typeof entry.text === "string") {
1700
1821
  const next = sanitizeAndScrubText(entry.text);
1701
1822
  if (next) {
1702
1823
  texts.push(next);
@@ -1752,7 +1873,7 @@ function buildOutboundTools(incomingTools, templateTools) {
1752
1873
  return templateTools.map((tool2) => ({ ...tool2 }));
1753
1874
  }
1754
1875
  function getCcVersion(template) {
1755
- return template.cc_version ?? DEFAULT_CC_VERSION;
1876
+ return template.cc_version ?? detectCliVersion();
1756
1877
  }
1757
1878
  function buildBillingHeader(firstUserMessage, template) {
1758
1879
  const version = getCcVersion(template);
@@ -1778,7 +1899,7 @@ function reverseMapToolUseNames(value, reverseLookup) {
1778
1899
  if (Array.isArray(value)) {
1779
1900
  return value.map((item) => reverseMapToolUseNames(item, reverseLookup));
1780
1901
  }
1781
- if (!isRecord(value)) {
1902
+ if (!isRecord2(value)) {
1782
1903
  return value;
1783
1904
  }
1784
1905
  const cloned = {};
@@ -1811,7 +1932,7 @@ function sanitizeMessages(body) {
1811
1932
  return;
1812
1933
  }
1813
1934
  for (const message of messages) {
1814
- if (!isRecord(message)) {
1935
+ if (!isRecord2(message)) {
1815
1936
  continue;
1816
1937
  }
1817
1938
  if (typeof message.content === "string") {
@@ -1822,7 +1943,7 @@ function sanitizeMessages(body) {
1822
1943
  continue;
1823
1944
  }
1824
1945
  for (const block of message.content) {
1825
- if (isRecord(block) && typeof block.text === "string") {
1946
+ if (isRecord2(block) && typeof block.text === "string") {
1826
1947
  block.text = sanitizeContent(block.text);
1827
1948
  }
1828
1949
  }
@@ -1874,6 +1995,7 @@ function buildUpstreamRequest(inputBody, identity, template, options) {
1874
1995
  sanitizeMessageBlock(block);
1875
1996
  }
1876
1997
  }
1998
+ compactMessageContent(messages);
1877
1999
  trimTrailingEmptyTurns(messages);
1878
2000
  const firstUserMessage = extractFirstUserMessage(messages);
1879
2001
  const billingHeader = buildBillingHeader(firstUserMessage, template);
@@ -1883,6 +2005,8 @@ function buildUpstreamRequest(inputBody, identity, template, options) {
1883
2005
  ].map((entry) => sanitizeAndScrubText(entry)).filter(Boolean).join("\n\n");
1884
2006
  const activeSessionId = options?.sessionId ?? getActiveSessionId();
1885
2007
  body.messages = messages;
2008
+ stripUnsupportedSamplingFields(body);
2009
+ stripThinkingControlFields(body);
1886
2010
  const incomingTools = Array.isArray(body.tools) ? body.tools : [];
1887
2011
  body.tools = buildOutboundTools(incomingTools, template.tools);
1888
2012
  body.system = [
@@ -1902,17 +2026,20 @@ function buildUpstreamRequest(inputBody, identity, template, options) {
1902
2026
  }
1903
2027
  ];
1904
2028
  body.metadata = {
1905
- ...isRecord(body.metadata) ? body.metadata : {},
2029
+ ...isRecord2(body.metadata) ? body.metadata : {},
1906
2030
  user_id: JSON.stringify({
1907
2031
  device_id: identity.deviceId,
1908
2032
  account_uuid: identity.accountUuid,
1909
2033
  session_id: activeSessionId
1910
2034
  })
1911
2035
  };
1912
- body.thinking = { type: "adaptive" };
1913
- body.context_management = DEFAULT_CONTEXT_MANAGEMENT;
1914
- body.output_config = DEFAULT_OUTPUT_CONFIG;
1915
- body.max_tokens = 64e3;
2036
+ const modelId = typeof body.model === "string" ? body.model : "";
2037
+ if (supportsAdaptiveThinking(modelId)) {
2038
+ body.thinking = { type: "adaptive" };
2039
+ body.context_management = DEFAULT_CONTEXT_MANAGEMENT;
2040
+ body.output_config = DEFAULT_OUTPUT_CONFIG;
2041
+ }
2042
+ body.max_tokens = resolveMaxTokens(modelId, body.max_tokens);
1916
2043
  return orderBodyForOutbound(body, template.body_field_order);
1917
2044
  }
1918
2045
  function orderBodyForOutbound(body, overrideOrder) {
@@ -1989,7 +2116,7 @@ function createStreamingReverseMapper(response, reverseLookup) {
1989
2116
  // src/tool-flow.ts
1990
2117
  import { createHash as createHash3 } from "crypto";
1991
2118
  var TOOL_MASK_PREFIX = "tool_";
1992
- function isRecord2(value) {
2119
+ function isRecord3(value) {
1993
2120
  return typeof value === "object" && value !== null;
1994
2121
  }
1995
2122
  function shouldMaskToolName(name, claudeToolNames) {
@@ -2003,7 +2130,7 @@ function extractFirstUserText(parsed) {
2003
2130
  return "";
2004
2131
  }
2005
2132
  const firstUser = parsed.messages.find((message) => message.role === "user");
2006
- if (!isRecord2(firstUser)) {
2133
+ if (!isRecord3(firstUser)) {
2007
2134
  return "";
2008
2135
  }
2009
2136
  const content = firstUser.content;
@@ -2013,7 +2140,7 @@ function extractFirstUserText(parsed) {
2013
2140
  if (!Array.isArray(content)) {
2014
2141
  return "";
2015
2142
  }
2016
- return content.filter((block) => isRecord2(block) && block.type === "text" && typeof block.text === "string").map((block) => String(block.text)).join("\n\n").trim();
2143
+ return content.filter((block) => isRecord3(block) && block.type === "text" && typeof block.text === "string").map((block) => String(block.text)).join("\n\n").trim();
2017
2144
  }
2018
2145
  function buildMaskedToolName(seed, toolName, length = 8) {
2019
2146
  const digest = createHash3("sha256").update(`tool-mask:${seed}:${toolName}`).digest("hex").slice(0, length);
@@ -2023,24 +2150,24 @@ function collectToolNames(parsed) {
2023
2150
  const names = /* @__PURE__ */ new Set();
2024
2151
  if (Array.isArray(parsed.tools)) {
2025
2152
  for (const tool2 of parsed.tools) {
2026
- if (isRecord2(tool2) && typeof tool2.name === "string") {
2153
+ if (isRecord3(tool2) && typeof tool2.name === "string") {
2027
2154
  names.add(tool2.name);
2028
2155
  }
2029
2156
  }
2030
2157
  }
2031
2158
  if (Array.isArray(parsed.messages)) {
2032
2159
  for (const message of parsed.messages) {
2033
- if (!isRecord2(message) || !Array.isArray(message.content)) {
2160
+ if (!isRecord3(message) || !Array.isArray(message.content)) {
2034
2161
  continue;
2035
2162
  }
2036
2163
  for (const block of message.content) {
2037
- if (isRecord2(block) && block.type === "tool_use" && typeof block.name === "string") {
2164
+ if (isRecord3(block) && block.type === "tool_use" && typeof block.name === "string") {
2038
2165
  names.add(block.name);
2039
2166
  }
2040
2167
  }
2041
2168
  }
2042
2169
  }
2043
- if (isRecord2(parsed.tool_choice) && parsed.tool_choice.type === "tool" && typeof parsed.tool_choice.name === "string") {
2170
+ if (isRecord3(parsed.tool_choice) && parsed.tool_choice.type === "tool" && typeof parsed.tool_choice.name === "string") {
2044
2171
  names.add(parsed.tool_choice.name);
2045
2172
  }
2046
2173
  return [...names];
@@ -2085,7 +2212,7 @@ function rewriteToolUseNames(value, reverseLookup) {
2085
2212
  if (Array.isArray(value)) {
2086
2213
  return value.map((item) => rewriteToolUseNames(item, reverseLookup));
2087
2214
  }
2088
- if (!isRecord2(value)) {
2215
+ if (!isRecord3(value)) {
2089
2216
  return value;
2090
2217
  }
2091
2218
  const cloned = {};
@@ -2108,7 +2235,7 @@ function applyOutboundToolFlow(parsed, claudeToolNames) {
2108
2235
  }
2109
2236
  if (Array.isArray(parsed.messages)) {
2110
2237
  next.messages = parsed.messages.map((message) => {
2111
- if (!isRecord2(message) || !Array.isArray(message.content)) {
2238
+ if (!isRecord3(message) || !Array.isArray(message.content)) {
2112
2239
  return message;
2113
2240
  }
2114
2241
  return {
@@ -2117,7 +2244,7 @@ function applyOutboundToolFlow(parsed, claudeToolNames) {
2117
2244
  };
2118
2245
  });
2119
2246
  }
2120
- if (isRecord2(parsed.tool_choice) && parsed.tool_choice.type === "tool") {
2247
+ if (isRecord3(parsed.tool_choice) && parsed.tool_choice.type === "tool") {
2121
2248
  next.tool_choice = {
2122
2249
  ...parsed.tool_choice,
2123
2250
  name: getOutgoingName(parsed.tool_choice.name, reverseLookup)
@@ -2815,6 +2942,7 @@ var EMPTY_OAUTH_CREDENTIALS = {
2815
2942
  access: "",
2816
2943
  expires: 0
2817
2944
  };
2945
+ var providerModelsObserverForTest = null;
2818
2946
  function extractFirstUserText2(input) {
2819
2947
  try {
2820
2948
  const raw = input;
@@ -3083,6 +3211,8 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
3083
3211
  { type: "api", label: "Manually enter API Key" }
3084
3212
  ],
3085
3213
  async loader(getAuth, provider) {
3214
+ providerModelsObserverForTest?.(provider.models ?? {});
3215
+ ingestProviderModelsCapabilities(provider.models ?? {});
3086
3216
  const auth = await getAuth();
3087
3217
  if (auth.type !== "oauth") {
3088
3218
  stopHeartbeat();
@@ -3168,7 +3298,15 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
3168
3298
  }
3169
3299
  };
3170
3300
  };
3301
+ function setProviderModelsObserverForTest(observer) {
3302
+ providerModelsObserverForTest = observer;
3303
+ }
3304
+ function resetProviderModelsObserverForTest() {
3305
+ providerModelsObserverForTest = null;
3306
+ }
3171
3307
  export {
3172
- ClaudeMultiAuthPlugin
3308
+ ClaudeMultiAuthPlugin,
3309
+ resetProviderModelsObserverForTest,
3310
+ setProviderModelsObserverForTest
3173
3311
  };
3174
3312
  //# sourceMappingURL=index.js.map