opencode-anthropic-multi-account 0.2.16 → 0.2.18

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.js CHANGED
@@ -1658,6 +1658,9 @@ function hasMeaningfulContent(content) {
1658
1658
  if (!isRecord(block)) {
1659
1659
  return false;
1660
1660
  }
1661
+ if (block.type === "tool_use") {
1662
+ return true;
1663
+ }
1661
1664
  if (typeof block.text === "string" && block.text.trim().length > 0) {
1662
1665
  return true;
1663
1666
  }
@@ -1727,6 +1730,27 @@ function extractFirstUserMessage(messages) {
1727
1730
  function hasCompleteToolSchemas(tools) {
1728
1731
  return tools.length > 0 && tools.every((tool2) => typeof tool2 === "object" && tool2 !== null && "input_schema" in tool2);
1729
1732
  }
1733
+ function enrichIncomingToolsWithTemplateSchemas(incomingTools, templateTools) {
1734
+ if (!hasCompleteToolSchemas(templateTools) || incomingTools.length !== templateTools.length) {
1735
+ return incomingTools;
1736
+ }
1737
+ return incomingTools.map((tool2, index) => {
1738
+ if ("input_schema" in tool2) {
1739
+ return tool2;
1740
+ }
1741
+ const templateTool = templateTools[index];
1742
+ return templateTool && "input_schema" in templateTool ? { ...tool2, input_schema: templateTool.input_schema } : tool2;
1743
+ });
1744
+ }
1745
+ function buildOutboundTools(incomingTools, templateTools) {
1746
+ if (incomingTools.length > 0) {
1747
+ return enrichIncomingToolsWithTemplateSchemas(incomingTools, templateTools);
1748
+ }
1749
+ if (!hasCompleteToolSchemas(templateTools)) {
1750
+ return incomingTools;
1751
+ }
1752
+ return templateTools.map((tool2) => ({ ...tool2 }));
1753
+ }
1730
1754
  function getCcVersion(template) {
1731
1755
  return template.cc_version ?? DEFAULT_CC_VERSION;
1732
1756
  }
@@ -1860,7 +1884,7 @@ function buildUpstreamRequest(inputBody, identity, template, options) {
1860
1884
  const activeSessionId = options?.sessionId ?? getActiveSessionId();
1861
1885
  body.messages = messages;
1862
1886
  const incomingTools = Array.isArray(body.tools) ? body.tools : [];
1863
- body.tools = hasCompleteToolSchemas(template.tools) ? template.tools.map((tool2) => ({ ...tool2 })) : incomingTools;
1887
+ body.tools = buildOutboundTools(incomingTools, template.tools);
1864
1888
  body.system = [
1865
1889
  {
1866
1890
  type: "text",
@@ -1962,6 +1986,149 @@ function createStreamingReverseMapper(response, reverseLookup) {
1962
1986
  });
1963
1987
  }
1964
1988
 
1989
+ // src/tool-flow.ts
1990
+ import { createHash as createHash3 } from "crypto";
1991
+ var TOOL_MASK_PREFIX = "tool_";
1992
+ function isRecord2(value) {
1993
+ return typeof value === "object" && value !== null;
1994
+ }
1995
+ function shouldMaskToolName(name, claudeToolNames) {
1996
+ if (!name) {
1997
+ return false;
1998
+ }
1999
+ return !claudeToolNames.has(name) && !name.startsWith("mcp__") && !name.startsWith(TOOL_MASK_PREFIX);
2000
+ }
2001
+ function extractFirstUserText(parsed) {
2002
+ if (!Array.isArray(parsed.messages)) {
2003
+ return "";
2004
+ }
2005
+ const firstUser = parsed.messages.find((message) => message.role === "user");
2006
+ if (!isRecord2(firstUser)) {
2007
+ return "";
2008
+ }
2009
+ const content = firstUser.content;
2010
+ if (typeof content === "string") {
2011
+ return content.trim();
2012
+ }
2013
+ if (!Array.isArray(content)) {
2014
+ return "";
2015
+ }
2016
+ return content.filter((block) => isRecord2(block) && block.type === "text" && typeof block.text === "string").map((block) => String(block.text)).join("\n\n").trim();
2017
+ }
2018
+ function buildMaskedToolName(seed, toolName, length = 8) {
2019
+ const digest = createHash3("sha256").update(`tool-mask:${seed}:${toolName}`).digest("hex").slice(0, length);
2020
+ return `${TOOL_MASK_PREFIX}${digest}`;
2021
+ }
2022
+ function collectToolNames(parsed) {
2023
+ const names = /* @__PURE__ */ new Set();
2024
+ if (Array.isArray(parsed.tools)) {
2025
+ for (const tool2 of parsed.tools) {
2026
+ if (isRecord2(tool2) && typeof tool2.name === "string") {
2027
+ names.add(tool2.name);
2028
+ }
2029
+ }
2030
+ }
2031
+ if (Array.isArray(parsed.messages)) {
2032
+ for (const message of parsed.messages) {
2033
+ if (!isRecord2(message) || !Array.isArray(message.content)) {
2034
+ continue;
2035
+ }
2036
+ for (const block of message.content) {
2037
+ if (isRecord2(block) && block.type === "tool_use" && typeof block.name === "string") {
2038
+ names.add(block.name);
2039
+ }
2040
+ }
2041
+ }
2042
+ }
2043
+ if (isRecord2(parsed.tool_choice) && parsed.tool_choice.type === "tool" && typeof parsed.tool_choice.name === "string") {
2044
+ names.add(parsed.tool_choice.name);
2045
+ }
2046
+ return [...names];
2047
+ }
2048
+ function buildClaudeToolNameSet(claudeToolNames) {
2049
+ return new Set(claudeToolNames.filter((name) => typeof name === "string" && name.length > 0));
2050
+ }
2051
+ function buildRequestScopedToolLookup(parsed, claudeToolNames) {
2052
+ const lookup = /* @__PURE__ */ new Map();
2053
+ const usedOutgoing = /* @__PURE__ */ new Set();
2054
+ const seed = extractFirstUserText(parsed);
2055
+ const claudeToolSet = buildClaudeToolNameSet(claudeToolNames);
2056
+ for (const originalName of collectToolNames(parsed)) {
2057
+ if (!shouldMaskToolName(originalName, claudeToolSet)) {
2058
+ lookup.set(originalName, originalName);
2059
+ usedOutgoing.add(originalName);
2060
+ continue;
2061
+ }
2062
+ let length = 8;
2063
+ let masked = buildMaskedToolName(seed, originalName, length);
2064
+ while (usedOutgoing.has(masked)) {
2065
+ length += 2;
2066
+ masked = buildMaskedToolName(seed, originalName, length);
2067
+ }
2068
+ lookup.set(masked, originalName);
2069
+ usedOutgoing.add(masked);
2070
+ }
2071
+ return lookup;
2072
+ }
2073
+ function getOutgoingName(name, reverseLookup) {
2074
+ if (!name) {
2075
+ return name;
2076
+ }
2077
+ for (const [outgoing, original] of reverseLookup) {
2078
+ if (original === name) {
2079
+ return outgoing;
2080
+ }
2081
+ }
2082
+ return name;
2083
+ }
2084
+ function rewriteToolUseNames(value, reverseLookup) {
2085
+ if (Array.isArray(value)) {
2086
+ return value.map((item) => rewriteToolUseNames(item, reverseLookup));
2087
+ }
2088
+ if (!isRecord2(value)) {
2089
+ return value;
2090
+ }
2091
+ const cloned = {};
2092
+ for (const [key, nested] of Object.entries(value)) {
2093
+ cloned[key] = rewriteToolUseNames(nested, reverseLookup);
2094
+ }
2095
+ if (cloned.type === "tool_use" && typeof cloned.name === "string") {
2096
+ cloned.name = getOutgoingName(cloned.name, reverseLookup);
2097
+ }
2098
+ return cloned;
2099
+ }
2100
+ function applyOutboundToolFlow(parsed, claudeToolNames) {
2101
+ const reverseLookup = buildRequestScopedToolLookup(parsed, claudeToolNames);
2102
+ const next = { ...parsed };
2103
+ if (Array.isArray(parsed.tools)) {
2104
+ next.tools = parsed.tools.map((tool2) => ({
2105
+ ...tool2,
2106
+ name: getOutgoingName(tool2.name, reverseLookup)
2107
+ }));
2108
+ }
2109
+ if (Array.isArray(parsed.messages)) {
2110
+ next.messages = parsed.messages.map((message) => {
2111
+ if (!isRecord2(message) || !Array.isArray(message.content)) {
2112
+ return message;
2113
+ }
2114
+ return {
2115
+ ...message,
2116
+ content: rewriteToolUseNames(message.content, reverseLookup)
2117
+ };
2118
+ });
2119
+ }
2120
+ if (isRecord2(parsed.tool_choice) && parsed.tool_choice.type === "tool") {
2121
+ next.tool_choice = {
2122
+ ...parsed.tool_choice,
2123
+ name: getOutgoingName(parsed.tool_choice.name, reverseLookup)
2124
+ };
2125
+ }
2126
+ return {
2127
+ body: JSON.stringify(next),
2128
+ reverseLookup
2129
+ };
2130
+ }
2131
+
1965
2132
  // src/upstream-headers.ts
1966
2133
  import { randomUUID as randomUUID3 } from "crypto";
1967
2134
  var UPSTREAM_TIMEOUT_MS = 3e5;
@@ -2048,6 +2215,9 @@ function filterBillableBetas(betas) {
2048
2215
  }
2049
2216
 
2050
2217
  // src/request-transform.ts
2218
+ function applyRequestToolMasking(parsed, claudeToolNames) {
2219
+ return applyOutboundToolFlow(parsed, claudeToolNames);
2220
+ }
2051
2221
  function extractModelIdFromBody(body) {
2052
2222
  if (typeof body !== "string") {
2053
2223
  return "unknown";
@@ -2258,18 +2428,38 @@ function transformBodyToUpstream(body, identity, sessionId2) {
2258
2428
  try {
2259
2429
  const parsed = JSON.parse(body);
2260
2430
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2261
- return body;
2262
- }
2263
- return JSON.stringify(
2264
- buildUpstreamRequest(
2265
- parsed,
2266
- identity,
2267
- loadTemplate(),
2268
- { sessionId: sessionId2 }
2269
- )
2431
+ return { body, reverseLookup: /* @__PURE__ */ new Map() };
2432
+ }
2433
+ const template = loadTemplate();
2434
+ const upstreamRequest = buildUpstreamRequest(
2435
+ parsed,
2436
+ identity,
2437
+ template,
2438
+ { sessionId: sessionId2 }
2270
2439
  );
2440
+ return applyRequestToolMasking(upstreamRequest, template.tool_names);
2271
2441
  } catch {
2272
- return body;
2442
+ return { body, reverseLookup: /* @__PURE__ */ new Map() };
2443
+ }
2444
+ }
2445
+ async function applyResponseReverseLookup(response, reverseLookup) {
2446
+ const contentType = response.headers.get("content-type") ?? "";
2447
+ if (contentType.includes("text/event-stream")) {
2448
+ return createStreamingReverseMapper(response, reverseLookup);
2449
+ }
2450
+ if (!contentType.includes("application/json") || reverseLookup.size === 0) {
2451
+ return response;
2452
+ }
2453
+ try {
2454
+ const payload = await response.clone().json();
2455
+ const remapped = reverseMapResponse(payload, reverseLookup);
2456
+ return new Response(JSON.stringify(remapped), {
2457
+ status: response.status,
2458
+ statusText: response.statusText,
2459
+ headers: new Headers(response.headers)
2460
+ });
2461
+ } catch {
2462
+ return response;
2273
2463
  }
2274
2464
  }
2275
2465
  async function enrichRateLimitResponse(response) {
@@ -2393,7 +2583,7 @@ var AccountRuntimeFactory = class {
2393
2583
  void recordObservedToolNames(extractToolNamesFromRequestBody(init.body)).catch(() => {
2394
2584
  });
2395
2585
  }
2396
- const transformedBody = typeof init?.body === "string" ? transformBodyToUpstream(init.body, this.identity, sessionId2) : init?.body;
2586
+ const transformedRequest = typeof init?.body === "string" ? transformBodyToUpstream(init.body, this.identity, sessionId2) : { body: init?.body, reverseLookup: /* @__PURE__ */ new Map() };
2397
2587
  const pacingCfg = resolvePacingConfig();
2398
2588
  const getNow = this.pacingTestOverrides.now ?? Date.now;
2399
2589
  const sleepFn = this.pacingTestOverrides.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
@@ -2420,7 +2610,7 @@ var AccountRuntimeFactory = class {
2420
2610
  const response2 = await fetch(transformedInput, {
2421
2611
  ...init,
2422
2612
  headers: requestHeaders,
2423
- body: transformedBody
2613
+ body: transformedRequest.body
2424
2614
  });
2425
2615
  return await enrichRateLimitResponse(response2);
2426
2616
  } catch (error) {
@@ -2453,7 +2643,7 @@ var AccountRuntimeFactory = class {
2453
2643
  );
2454
2644
  response = await performFetch(retryHeaders);
2455
2645
  }
2456
- return createStreamingReverseMapper(response);
2646
+ return applyResponseReverseLookup(response, transformedRequest.reverseLookup);
2457
2647
  }
2458
2648
  async createRuntime(uuid) {
2459
2649
  const fetchWithAccount = async (input, init) => {
@@ -2463,9 +2653,8 @@ var AccountRuntimeFactory = class {
2463
2653
  throw new Error(`No credentials found for account ${uuid}`);
2464
2654
  }
2465
2655
  let accessToken = storedAccount.accessToken;
2466
- let expiresAt = storedAccount.expiresAt;
2467
- if (!accessToken || !expiresAt || isTokenExpired({ accessToken, expiresAt })) {
2468
- ({ accessToken, expiresAt } = await this.ensureFreshToken(storedAccount, uuid));
2656
+ if (!accessToken || !storedAccount.expiresAt || isTokenExpired({ accessToken, expiresAt: storedAccount.expiresAt })) {
2657
+ ({ accessToken } = await this.ensureFreshToken(storedAccount, uuid));
2469
2658
  }
2470
2659
  if (!accessToken) {
2471
2660
  throw new Error(`No access token available for account ${uuid}`);
@@ -2626,7 +2815,7 @@ var EMPTY_OAUTH_CREDENTIALS = {
2626
2815
  access: "",
2627
2816
  expires: 0
2628
2817
  };
2629
- function extractFirstUserText(input) {
2818
+ function extractFirstUserText2(input) {
2630
2819
  try {
2631
2820
  const raw = input;
2632
2821
  const messages = raw.messages ?? raw.request?.messages;
@@ -2819,7 +3008,7 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
2819
3008
  });
2820
3009
  return {
2821
3010
  "experimental.chat.system.transform": (input, output) => {
2822
- const billingHeader = composeBillingSystemEntry(extractFirstUserText(input), claudeCodeVersion);
3011
+ const billingHeader = composeBillingSystemEntry(extractFirstUserText2(input), claudeCodeVersion);
2823
3012
  prependMissingSystemEntries(output, [
2824
3013
  billingHeader,
2825
3014
  upstreamAgentIdentity,