@juspay/neurolink 13.8.17 → 13.8.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.
Files changed (192) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +1 -1
  3. package/dist/action/actionExecutor.d.ts +33 -3
  4. package/dist/action/actionExecutor.js +71 -15
  5. package/dist/action/actionInputs.js +4 -1
  6. package/dist/action/cliVersion.d.ts +25 -0
  7. package/dist/action/cliVersion.js +26 -0
  8. package/dist/action/index.d.ts +2 -1
  9. package/dist/action/index.js +2 -1
  10. package/dist/adapters/video/directorPipeline.js +46 -36
  11. package/dist/adapters/video/ffmpegAdapter.d.ts +17 -0
  12. package/dist/adapters/video/ffmpegAdapter.js +32 -5
  13. package/dist/adapters/video/frameExtractor.js +1 -1
  14. package/dist/adapters/video/videoMerger.js +5 -2
  15. package/dist/agent/arithmeticEvaluator.d.ts +32 -0
  16. package/dist/agent/arithmeticEvaluator.js +349 -0
  17. package/dist/agent/directTools.d.ts +6 -18
  18. package/dist/agent/directTools.js +17 -56
  19. package/dist/artifacts/artifactStore.d.ts +22 -1
  20. package/dist/artifacts/artifactStore.js +66 -6
  21. package/dist/auth/anthropicOAuth.d.ts +26 -1
  22. package/dist/auth/anthropicOAuth.js +81 -5
  23. package/dist/auth/secureFiles.d.ts +37 -0
  24. package/dist/auth/secureFiles.js +95 -0
  25. package/dist/auth/tokenStore.d.ts +43 -6
  26. package/dist/auth/tokenStore.js +260 -79
  27. package/dist/avatar/providers/DIDAvatar.js +49 -45
  28. package/dist/avatar/providers/ReplicateAvatar.js +33 -40
  29. package/dist/browser/neurolink.min.js +485 -558
  30. package/dist/cli/commands/auth.js +112 -70
  31. package/dist/cli/commands/proxy.d.ts +4 -0
  32. package/dist/cli/commands/proxy.js +128 -13
  33. package/dist/cli/commands/serve.d.ts +10 -1
  34. package/dist/cli/commands/serve.js +98 -17
  35. package/dist/cli/commands/server.js +17 -5
  36. package/dist/cli/index.js +146 -5
  37. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  38. package/dist/cli/utils/envManager.js +17 -3
  39. package/dist/cli/utils/serverUtils.d.ts +39 -3
  40. package/dist/cli/utils/serverUtils.js +63 -6
  41. package/dist/core/baseProvider.js +7 -1
  42. package/dist/core/modules/MessageBuilder.js +5 -0
  43. package/dist/core/modules/ToolsManager.d.ts +46 -1
  44. package/dist/core/modules/ToolsManager.js +190 -43
  45. package/dist/files/fileReferenceRegistry.d.ts +2 -23
  46. package/dist/files/fileReferenceRegistry.js +55 -5
  47. package/dist/hitl/hitlManager.js +1 -0
  48. package/dist/index.d.ts +2 -1
  49. package/dist/index.js +6 -1
  50. package/dist/mcp/auth/tokenStorage.d.ts +7 -0
  51. package/dist/mcp/auth/tokenStorage.js +23 -4
  52. package/dist/mcp/externalServerManager.d.ts +13 -3
  53. package/dist/mcp/externalServerManager.js +154 -136
  54. package/dist/mcp/index.d.ts +2 -0
  55. package/dist/mcp/index.js +3 -0
  56. package/dist/mcp/mcpClientFactory.d.ts +14 -0
  57. package/dist/mcp/mcpClientFactory.js +70 -7
  58. package/dist/mcp/projectConfigTrust.d.ts +72 -0
  59. package/dist/mcp/projectConfigTrust.js +237 -0
  60. package/dist/mcp/transportUrlPolicy.d.ts +39 -0
  61. package/dist/mcp/transportUrlPolicy.js +172 -0
  62. package/dist/music/providers/BeatovenMusic.js +35 -46
  63. package/dist/music/providers/ReplicateMusic.js +33 -40
  64. package/dist/neurolink.d.ts +16 -1
  65. package/dist/neurolink.js +64 -11
  66. package/dist/processors/archive/ArchiveProcessor.js +15 -1
  67. package/dist/processors/archive/zipEntryIndex.d.ts +22 -0
  68. package/dist/processors/archive/zipEntryIndex.js +33 -0
  69. package/dist/processors/base/BaseFileProcessor.d.ts +9 -21
  70. package/dist/processors/base/BaseFileProcessor.js +40 -111
  71. package/dist/processors/data/YamlProcessor.d.ts +13 -1
  72. package/dist/processors/data/YamlProcessor.js +93 -2
  73. package/dist/processors/document/OpenDocumentProcessor.js +7 -2
  74. package/dist/processors/document/PptxProcessor.js +12 -7
  75. package/dist/processors/media/VideoProcessor.js +93 -15
  76. package/dist/proxy/inboundAuth.d.ts +89 -0
  77. package/dist/proxy/inboundAuth.js +219 -0
  78. package/dist/proxy/tokenRefresh.d.ts +6 -0
  79. package/dist/proxy/tokenRefresh.js +13 -1
  80. package/dist/proxy/updateState.js +4 -4
  81. package/dist/rag/document/loaders.d.ts +0 -6
  82. package/dist/rag/document/loaders.js +16 -10
  83. package/dist/rag/ragIntegration.d.ts +2 -2
  84. package/dist/rag/ragIntegration.js +35 -10
  85. package/dist/rag/retrieval/vectorQueryTool.d.ts +7 -0
  86. package/dist/rag/retrieval/vectorQueryTool.js +54 -17
  87. package/dist/server/abstract/baseServerAdapter.d.ts +43 -0
  88. package/dist/server/abstract/baseServerAdapter.js +119 -6
  89. package/dist/server/adapters/expressAdapter.d.ts +3 -0
  90. package/dist/server/adapters/expressAdapter.js +78 -6
  91. package/dist/server/adapters/fastifyAdapter.d.ts +3 -0
  92. package/dist/server/adapters/fastifyAdapter.js +165 -70
  93. package/dist/server/adapters/honoAdapter.d.ts +9 -1
  94. package/dist/server/adapters/honoAdapter.js +99 -21
  95. package/dist/server/adapters/koaAdapter.d.ts +1 -0
  96. package/dist/server/adapters/koaAdapter.js +153 -74
  97. package/dist/server/index.d.ts +2 -0
  98. package/dist/server/index.js +4 -0
  99. package/dist/server/middleware/auth.js +6 -2
  100. package/dist/server/middleware/cache.js +6 -1
  101. package/dist/server/middleware/rateLimit.js +29 -4
  102. package/dist/server/routes/agentRoutes.js +24 -11
  103. package/dist/server/routes/claudeProxyRoutes.d.ts +1 -0
  104. package/dist/server/routes/claudeProxyRoutes.js +273 -84
  105. package/dist/server/routes/codexProxyRoutes.d.ts +2 -2
  106. package/dist/server/routes/codexProxyRoutes.js +125 -40
  107. package/dist/server/routes/index.d.ts +4 -0
  108. package/dist/server/routes/index.js +35 -4
  109. package/dist/server/routes/mcpRoutes.d.ts +6 -2
  110. package/dist/server/routes/mcpRoutes.js +21 -7
  111. package/dist/server/routes/memoryRoutes.js +7 -6
  112. package/dist/server/routes/toolRoutes.d.ts +12 -2
  113. package/dist/server/routes/toolRoutes.js +33 -3
  114. package/dist/server/routes/transcribeRoutes.js +60 -13
  115. package/dist/server/utils/clientAddress.d.ts +23 -0
  116. package/dist/server/utils/clientAddress.js +65 -0
  117. package/dist/server/utils/clientError.d.ts +35 -0
  118. package/dist/server/utils/clientError.js +129 -0
  119. package/dist/server/utils/pathMatch.d.ts +21 -0
  120. package/dist/server/utils/pathMatch.js +41 -0
  121. package/dist/server/utils/validation.d.ts +9 -0
  122. package/dist/server/utils/validation.js +9 -0
  123. package/dist/server/websocket/WebSocketHandler.js +22 -3
  124. package/dist/server/websocket/frameQueue.d.ts +19 -0
  125. package/dist/server/websocket/frameQueue.js +41 -3
  126. package/dist/server/websocket/transcribeWebSocket.d.ts +8 -1
  127. package/dist/server/websocket/transcribeWebSocket.js +223 -34
  128. package/dist/skills/skillStores.js +5 -0
  129. package/dist/tasks/store/fileTaskStore.d.ts +2 -0
  130. package/dist/tasks/store/fileTaskStore.js +11 -4
  131. package/dist/types/attachments.d.ts +117 -0
  132. package/dist/types/attachments.js +15 -0
  133. package/dist/types/auth.d.ts +6 -0
  134. package/dist/types/cli.d.ts +4 -0
  135. package/dist/types/config.d.ts +56 -0
  136. package/dist/types/file.d.ts +8 -0
  137. package/dist/types/hitl.d.ts +8 -0
  138. package/dist/types/index.d.ts +1 -0
  139. package/dist/types/index.js +1 -0
  140. package/dist/types/mcp.d.ts +98 -0
  141. package/dist/types/multimodal.js +0 -41
  142. package/dist/types/processor.d.ts +14 -1
  143. package/dist/types/proxy.d.ts +62 -3
  144. package/dist/types/rag.d.ts +9 -0
  145. package/dist/types/safeFetch.d.ts +40 -0
  146. package/dist/types/server.d.ts +206 -10
  147. package/dist/types/tools.d.ts +57 -0
  148. package/dist/types/transcribe.d.ts +6 -0
  149. package/dist/utils/attachmentPolicy.d.ts +89 -0
  150. package/dist/utils/attachmentPolicy.js +327 -0
  151. package/dist/utils/dotenvBootstrap.d.ts +48 -19
  152. package/dist/utils/dotenvBootstrap.js +172 -31
  153. package/dist/utils/fileDetector.js +115 -65
  154. package/dist/utils/json/coerce.js +11 -1
  155. package/dist/utils/logSanitize.d.ts +5 -0
  156. package/dist/utils/logSanitize.js +8 -0
  157. package/dist/utils/logger.js +73 -1
  158. package/dist/utils/messageBuilder.d.ts +2 -2
  159. package/dist/utils/messageBuilder.js +118 -105
  160. package/dist/utils/multimodalOptionsBuilder.d.ts +1 -0
  161. package/dist/utils/multimodalOptionsBuilder.js +2 -0
  162. package/dist/utils/redis.d.ts +12 -0
  163. package/dist/utils/redis.js +72 -0
  164. package/dist/utils/safeFetch.d.ts +22 -1
  165. package/dist/utils/safeFetch.js +132 -38
  166. package/dist/utils/safeStoreId.d.ts +19 -0
  167. package/dist/utils/safeStoreId.js +29 -0
  168. package/dist/utils/schemaConversion.d.ts +30 -3
  169. package/dist/utils/schemaConversion.js +457 -84
  170. package/dist/utils/sizeGuard.js +2 -2
  171. package/dist/utils/ssrfGuard.d.ts +3 -3
  172. package/dist/utils/ssrfGuard.js +71 -39
  173. package/dist/voice/eval/sttEvalStore.d.ts +26 -3
  174. package/dist/voice/eval/sttEvalStore.js +77 -12
  175. package/dist/voice/livekit/agentDefinition.d.ts +18 -0
  176. package/dist/voice/livekit/agentDefinition.js +22 -0
  177. package/dist/voice/livekit/realtimeMcpTools.d.ts +1 -1
  178. package/dist/voice/livekit/realtimeMcpTools.js +8 -7
  179. package/dist/voice/livekit/realtimeVoiceAgent.d.ts +2 -4
  180. package/dist/voice/livekit/realtimeVoiceAgent.js +18 -7
  181. package/dist/voice/livekit/voiceAgent.d.ts +2 -2
  182. package/dist/voice/livekit/voiceAgent.js +2 -1
  183. package/dist/voice/livekit/xaiSessionWatch.d.ts +0 -5
  184. package/dist/voice/livekit/xaiSessionWatch.js +30 -0
  185. package/dist/voice/providers/GeminiLive.js +21 -6
  186. package/dist/voice/providers/GoogleSTT.js +8 -5
  187. package/dist/voice/transcribe.d.ts +2 -2
  188. package/dist/voice/transcribe.js +34 -18
  189. package/dist/voice/whistle/audio.js +11 -5
  190. package/docs-site/static/search-index.json +71 -49
  191. package/package.json +171 -156
  192. package/scripts/observability/import-openobserve-dashboard.mjs +11 -3
@@ -45,6 +45,7 @@ import { evaluateResidentAccount, getResidentGrantForAccount, recordResidentSpen
45
45
  import { buildProvisionClaim, isLeaseRefusal, issueLease, } from "../../proxy/shareLease.js";
46
46
  import { forwardToPeer } from "../../proxy/peerTransport.js";
47
47
  import { getShareContext } from "../../proxy/shareContext.js";
48
+ import { authorizeInboundRequest, inboundAuthRefusalResponse, isInboundAuthExemptPath, resolveInboundToken, resolveMaxAccountsPerRequest, secretsMatch, warnIfProxyRoutesUnauthenticated, } from "../../proxy/inboundAuth.js";
48
49
  import { ProxyImageInputError } from "../../proxy/proxyImageInput.js";
49
50
  import { buildShareRefusal, extractShareToken } from "../../proxy/shareGate.js";
50
51
  import { debitShareGrantCoins, getNodePublicUrl, getNoteSecret, getShareGrant, resolveShareToken, setShareGrantState, } from "../../proxy/shareGrants.js";
@@ -69,15 +70,75 @@ import { handleCodexResponsesRequest } from "./codexProxyRoutes.js";
69
70
  // ---------------------------------------------------------------------------
70
71
  // Helpers
71
72
  // ---------------------------------------------------------------------------
72
- /** Headers that must never be forwarded upstream to Anthropic. */
73
- const BLOCKED_UPSTREAM_HEADERS = new Set([
74
- "cookie",
75
- "proxy-authorization",
76
- "host",
77
- "connection",
78
- "content-length",
79
- "transfer-encoding",
73
+ /**
74
+ * Protocol headers a client may influence upstream. Everything else is dropped
75
+ * rather than forwarded: proxy-internal `x-neurolink-*` (a borrower's share
76
+ * token among them), trace context, CDN and forwarding identity, cookies and
77
+ * hop-by-hop headers. An allow-list because the blocklist it replaced leaked
78
+ * every header nobody had thought to name.
79
+ */
80
+ const FORWARDABLE_UPSTREAM_HEADERS = new Set([
81
+ "accept",
82
+ "accept-encoding",
83
+ "accept-language",
84
+ "anthropic-beta",
85
+ "anthropic-dangerous-direct-browser-access",
86
+ "anthropic-version",
87
+ "content-type",
88
+ "sec-fetch-mode",
89
+ "user-agent",
90
+ "x-app",
91
+ "x-claude-code-session-id",
92
+ "x-subscription-tier",
80
93
  ]);
94
+ const FORWARDABLE_UPSTREAM_HEADER_PREFIXES = ["x-stainless-"];
95
+ /** Set by the proxy on the pool path, forwarded only in passthrough mode. */
96
+ const CLIENT_CREDENTIAL_HEADERS = new Set(["authorization", "x-api-key"]);
97
+ function isForwardableUpstreamHeader(lowerName) {
98
+ return (FORWARDABLE_UPSTREAM_HEADERS.has(lowerName) ||
99
+ FORWARDABLE_UPSTREAM_HEADER_PREFIXES.some((prefix) => lowerName.startsWith(prefix)));
100
+ }
101
+ /** The subset of client headers that may travel upstream, lower-cased. */
102
+ function filterForwardableUpstreamHeaders(headers, options) {
103
+ const forwarded = {};
104
+ for (const [name, value] of Object.entries(headers)) {
105
+ if (typeof value !== "string" || value === "") {
106
+ continue;
107
+ }
108
+ const lower = name.toLowerCase();
109
+ if (isForwardableUpstreamHeader(lower) ||
110
+ (options.includeClientCredentials && CLIENT_CREDENTIAL_HEADERS.has(lower))) {
111
+ forwarded[lower] = value;
112
+ }
113
+ }
114
+ return forwarded;
115
+ }
116
+ /**
117
+ * A request from Claude Code itself: both the session header and the CLI
118
+ * user-agent. The looser `isLikelyClaudeClient` still decides how a request's
119
+ * own `system` blocks are shaped, but nothing is persisted and no client-chosen
120
+ * identity is trusted on anything weaker than this.
121
+ */
122
+ function isGenuineClaudeCodeClient(headers) {
123
+ return (typeof headers["x-claude-code-session-id"] === "string" &&
124
+ headers["x-claude-code-session-id"] !== "" &&
125
+ (headers["user-agent"]?.startsWith("claude-cli/") ?? false));
126
+ }
127
+ /**
128
+ * The only headers the Codex fallback leg inherits from a `/v1/messages`
129
+ * client: the ones the token-budget session key reads. Nothing else crosses
130
+ * engines, so a Claude client's fingerprint never reaches chatgpt.com.
131
+ */
132
+ function pickClaudeFallbackHeaders(headers) {
133
+ const picked = {};
134
+ for (const name of ["x-neurolink-session-id", "x-claude-code-session-id"]) {
135
+ const value = headers[name];
136
+ if (typeof value === "string" && value !== "") {
137
+ picked[name] = value;
138
+ }
139
+ }
140
+ return picked;
141
+ }
81
142
  const PROXY_INTERNAL_ACCOUNT_LABEL = "proxy/internal";
82
143
  const PROXY_INTERNAL_ACCOUNT_TYPE = "internal";
83
144
  function resolveRequestLogAccountIdentity(accountLabel, accountType) {
@@ -2147,9 +2208,7 @@ function applySnapshotHeaders(headers, snapshot) {
2147
2208
  const lower = sk.toLowerCase();
2148
2209
  if (typeof sv === "string" &&
2149
2210
  !headers[lower] &&
2150
- !BLOCKED_UPSTREAM_HEADERS.has(lower) &&
2151
- lower !== "authorization" &&
2152
- lower !== "x-api-key" &&
2211
+ isForwardableUpstreamHeader(lower) &&
2153
2212
  lower !== "x-claude-code-session-id") {
2154
2213
  headers[lower] = sv;
2155
2214
  }
@@ -2173,6 +2232,18 @@ async function loadClaudeSnapshot(accountLabel) {
2173
2232
  if (!snapshot || typeof snapshot !== "object") {
2174
2233
  return null;
2175
2234
  }
2235
+ // Only a capture this code made — from a verified Claude Code client —
2236
+ // may contribute an identity tuple. Anything older was written by a
2237
+ // heuristic any caller could satisfy, so its body is dropped and the next
2238
+ // genuine Claude Code request recaptures it.
2239
+ const verifiedCapture = "capturedFrom" in snapshot &&
2240
+ snapshot.capturedFrom === "genuine-claude-code";
2241
+ const { body: sanitizedBody, changed: bodyChanged } = verifiedCapture
2242
+ ? sanitizeSnapshotBody(snapshot.body)
2243
+ : {
2244
+ body: undefined,
2245
+ changed: Object.keys(snapshot.body ?? {}).length > 0,
2246
+ };
2176
2247
  const normalized = {
2177
2248
  accountKey: "accountKey" in snapshot && typeof snapshot.accountKey === "string"
2178
2249
  ? snapshot.accountKey
@@ -2181,8 +2252,13 @@ async function loadClaudeSnapshot(accountLabel) {
2181
2252
  ? snapshot.capturedAt
2182
2253
  : new Date(0).toISOString(),
2183
2254
  source: "claude-code",
2184
- headers: "headers" in snapshot && snapshot.headers ? snapshot.headers : {},
2185
- ...(snapshot.body ? { body: snapshot.body } : {}),
2255
+ ...(verifiedCapture ? { capturedFrom: "genuine-claude-code" } : {}),
2256
+ headers: "headers" in snapshot && snapshot.headers
2257
+ ? filterForwardableUpstreamHeaders(snapshot.headers, {
2258
+ includeClientCredentials: false,
2259
+ })
2260
+ : {},
2261
+ ...(sanitizedBody ? { body: sanitizedBody } : {}),
2186
2262
  };
2187
2263
  if (Object.keys(normalized.headers).length === 0 &&
2188
2264
  Object.keys(normalized.body ?? {}).length === 0) {
@@ -2192,6 +2268,14 @@ async function loadClaudeSnapshot(accountLabel) {
2192
2268
  snapshot: normalized,
2193
2269
  loadedAt: Date.now(),
2194
2270
  });
2271
+ if (bodyChanged) {
2272
+ // A file written by an earlier release may carry prompt text captured
2273
+ // from an arbitrary client. Rewrite it without, so the poison is gone
2274
+ // for the next process as well as this one.
2275
+ void persistClaudeSnapshot(accountLabel, normalized).catch(() => {
2276
+ // Best effort; the in-memory copy is already clean.
2277
+ });
2278
+ }
2195
2279
  return normalized;
2196
2280
  }
2197
2281
  catch {
@@ -2204,9 +2288,7 @@ function buildSnapshotHeaders(headers, existingHeaders) {
2204
2288
  const lower = key.toLowerCase();
2205
2289
  if (typeof value === "string" &&
2206
2290
  SNAPSHOT_STABLE_HEADERS.has(lower) &&
2207
- !BLOCKED_UPSTREAM_HEADERS.has(lower) &&
2208
- lower !== "authorization" &&
2209
- lower !== "x-api-key" &&
2291
+ isForwardableUpstreamHeader(lower) &&
2210
2292
  lower !== "x-claude-code-session-id") {
2211
2293
  merged[lower] = value;
2212
2294
  }
@@ -2224,18 +2306,56 @@ function extractSnapshotBody(body) {
2224
2306
  : typeof parsed.system === "string"
2225
2307
  ? [{ type: "text", text: parsed.system }]
2226
2308
  : [];
2227
- const billingHeader = systemBlocks.find((block) => typeof block?.text === "string" &&
2309
+ const rawBillingHeader = systemBlocks.find((block) => typeof block?.text === "string" &&
2228
2310
  block.text.includes("x-anthropic-billing-header"))?.text;
2229
- const agentBlock = systemBlocks.find((block) => typeof block?.text === "string" &&
2230
- block.text.includes("Claude Agent SDK"))?.text;
2231
- if (!identity && !billingHeader && !agentBlock) {
2311
+ const hasAgentBlock = systemBlocks.some((block) => typeof block?.text === "string" &&
2312
+ block.text.includes("Claude Agent SDK"));
2313
+ if (!identity && !rawBillingHeader && !hasAgentBlock) {
2232
2314
  return undefined;
2233
2315
  }
2316
+ // Only the identity tuple, and the billing header reduced to the two
2317
+ // fields the proxy re-emits. No prompt text from the body is ever kept.
2234
2318
  return {
2235
2319
  ...(identity ? { metadataUserId: identity.metadataUserId } : {}),
2236
2320
  ...(identity ? { sessionId: identity.sessionId } : {}),
2237
- ...(billingHeader ? { billingHeader } : {}),
2238
- ...(agentBlock ? { agentBlock } : {}),
2321
+ ...(rawBillingHeader
2322
+ ? { billingHeader: buildStableClaudeCodeBillingHeader(rawBillingHeader) }
2323
+ : {}),
2324
+ ...(hasAgentBlock ? { hasAgentBlock: true } : {}),
2325
+ };
2326
+ }
2327
+ /**
2328
+ * Drop anything a persisted snapshot body must no longer carry (an earlier
2329
+ * release stored the client's agent-block prompt text) and normalise the rest.
2330
+ */
2331
+ function sanitizeSnapshotBody(body) {
2332
+ if (!body || typeof body !== "object") {
2333
+ return { body: undefined, changed: false };
2334
+ }
2335
+ const normalisedBilling = typeof body.billingHeader === "string"
2336
+ ? buildStableClaudeCodeBillingHeader(body.billingHeader)
2337
+ : undefined;
2338
+ const sanitized = {
2339
+ ...(typeof body.metadataUserId === "string"
2340
+ ? { metadataUserId: body.metadataUserId }
2341
+ : {}),
2342
+ ...(normalisedBilling ? { billingHeader: normalisedBilling } : {}),
2343
+ ...(typeof body.sessionId === "string"
2344
+ ? { sessionId: body.sessionId }
2345
+ : {}),
2346
+ };
2347
+ const changed = "agentBlock" in body ||
2348
+ (typeof body.billingHeader === "string" &&
2349
+ body.billingHeader !== normalisedBilling) ||
2350
+ Object.keys(body).some((key) => ![
2351
+ "metadataUserId",
2352
+ "billingHeader",
2353
+ "sessionId",
2354
+ "agentBlock",
2355
+ ].includes(key));
2356
+ return {
2357
+ body: Object.keys(sanitized).length > 0 ? sanitized : undefined,
2358
+ changed,
2239
2359
  };
2240
2360
  }
2241
2361
  function isLikelyClaudeClient(headers, snapshotBody) {
@@ -2243,7 +2363,7 @@ function isLikelyClaudeClient(headers, snapshotBody) {
2243
2363
  headers["user-agent"]?.startsWith("claude-cli/") ||
2244
2364
  !!snapshotBody?.metadataUserId ||
2245
2365
  !!snapshotBody?.billingHeader ||
2246
- !!snapshotBody?.agentBlock);
2366
+ !!snapshotBody?.hasAgentBlock);
2247
2367
  }
2248
2368
  function snapshotsMatch(existing, next) {
2249
2369
  if (!existing) {
@@ -2266,18 +2386,23 @@ async function maybeRefreshClaudeSnapshot(accountLabel, accountKey, headers, bod
2266
2386
  catch {
2267
2387
  return existing;
2268
2388
  }
2269
- const body = extractSnapshotBody(parsedBody);
2270
- if (!isLikelyClaudeClient(headers, body)) {
2389
+ // Capture only from Claude Code itself, and never from a borrowed request:
2390
+ // whatever lands here is replayed on every later request for this account,
2391
+ // so a body-shaped heuristic (a `system` block mentioning the Agent SDK, a
2392
+ // well-formed `metadata.user_id`) let any caller write it.
2393
+ if (getShareContext() || !isGenuineClaudeCodeClient(headers)) {
2271
2394
  return existing;
2272
2395
  }
2396
+ const { hasAgentBlock: _hasAgentBlock, ...body } = extractSnapshotBody(parsedBody) ?? {};
2273
2397
  const next = {
2274
2398
  accountKey,
2275
2399
  capturedAt: new Date().toISOString(),
2276
2400
  source: "claude-code",
2401
+ capturedFrom: "genuine-claude-code",
2277
2402
  headers: buildSnapshotHeaders(headers, existing?.headers),
2278
2403
  body: {
2279
2404
  ...(existing?.body ?? {}),
2280
- ...(body ?? {}),
2405
+ ...body,
2281
2406
  ...(typeof headers["x-claude-code-session-id"] === "string"
2282
2407
  ? { sessionId: headers["x-claude-code-session-id"] }
2283
2408
  : {}),
@@ -2370,15 +2495,15 @@ function responseInfoFromStream(data) {
2370
2495
  * into the body. Non-CC clients (Curator, custom apps) don't send these —
2371
2496
  * Anthropic rejects without them.
2372
2497
  */
2373
- function polyfillOAuthBody(bodyStr, accountToken, snapshot, isClaudeClientRequest, preferredSessionId) {
2498
+ /** The agent identity block the OAuth path sends for non-Claude-Code clients.
2499
+ * A compiled-in constant: it is never taken from a request or a snapshot. */
2500
+ const CLAUDE_AGENT_SDK_SYSTEM_TEXT = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
2501
+ function polyfillOAuthBody(bodyStr, accountToken, snapshot, isClaudeClientRequest, preferredSessionId, trustClientIdentity = isClaudeClientRequest) {
2374
2502
  try {
2375
2503
  const parsed = JSON.parse(bodyStr);
2376
- // Billing header block synthesized for clients that do not already provide
2377
- // the genuine Claude Code identity shape.
2378
2504
  const agentBlock = {
2379
2505
  type: "text",
2380
- text: snapshot?.body?.agentBlock ||
2381
- "You are a Claude agent, built on Anthropic's Claude Agent SDK.",
2506
+ text: CLAUDE_AGENT_SDK_SYSTEM_TEXT,
2382
2507
  };
2383
2508
  // Normalise system to an array, then route by client type.
2384
2509
  //
@@ -2436,8 +2561,14 @@ function polyfillOAuthBody(bodyStr, accountToken, snapshot, isClaudeClientReques
2436
2561
  }
2437
2562
  // Inject Claude-Code-shaped metadata.user_id (required for OAuth).
2438
2563
  const tokenPrefix = accountToken.substring(0, Math.min(20, accountToken.length));
2564
+ // A client-chosen `metadata.user_id` is the device identity this account
2565
+ // presents to Anthropic. Only Claude Code itself gets to pick it; anyone
2566
+ // else inherits the identity derived from the token (or captured from a
2567
+ // genuine Claude Code session on this account).
2439
2568
  const identity = getOrCreateClaudeCodeIdentity(tokenPrefix, {
2440
- existingUserId: parsed.metadata?.user_id ?? snapshot?.body?.metadataUserId,
2569
+ existingUserId: trustClientIdentity
2570
+ ? (parsed.metadata?.user_id ?? snapshot?.body?.metadataUserId)
2571
+ : snapshot?.body?.metadataUserId,
2441
2572
  preferredSessionId: preferredSessionId ?? snapshot?.body?.sessionId,
2442
2573
  });
2443
2574
  parsed.metadata = {
@@ -2585,12 +2716,11 @@ async function handleClaudePassthroughRequest(args) {
2585
2716
  tracer?.setMode("passthrough-cli");
2586
2717
  const { bodyStr, preparedContext } = prepareAnthropicWire(ctx, clientRequestBody);
2587
2718
  const toolCount = Array.isArray(body.tools) ? body.tools.length : 0;
2588
- const upstreamHeaders = {};
2589
- for (const [key, value] of Object.entries(ctx.headers)) {
2590
- if (!BLOCKED_UPSTREAM_HEADERS.has(key.toLowerCase()) && value) {
2591
- upstreamHeaders[key] = value;
2592
- }
2593
- }
2719
+ // Passthrough relays the caller's own credential, so authorization and
2720
+ // x-api-key travel here; the protocol allow-list bounds everything else.
2721
+ const upstreamHeaders = filterForwardableUpstreamHeaders(ctx.headers, {
2722
+ includeClientCredentials: true,
2723
+ });
2594
2724
  if (!upstreamHeaders["content-type"]) {
2595
2725
  upstreamHeaders["content-type"] = "application/json";
2596
2726
  }
@@ -3455,22 +3585,6 @@ async function auditCompleteShareHeartbeat(grant, reportedCoins) {
3455
3585
  async function recordReportedResidentSpend(grant, coins) {
3456
3586
  await debitShareGrantCoins(grant.id, coins);
3457
3587
  }
3458
- /**
3459
- * Compare a presented secret against the stored one without leaking where they
3460
- * diverge. Hand-rolled rather than `crypto.timingSafeEqual` for the same reason
3461
- * as `shareGrants.digestsMatch`: this module is reachable from a build whose
3462
- * `node:crypto` stub does not carry it.
3463
- */
3464
- function secretsMatch(expected, presented) {
3465
- if (expected.length !== presented.length || expected.length === 0) {
3466
- return false;
3467
- }
3468
- let difference = 0;
3469
- for (let index = 0; index < expected.length; index += 1) {
3470
- difference |= expected.charCodeAt(index) ^ presented.charCodeAt(index);
3471
- }
3472
- return difference === 0;
3473
- }
3474
3588
  /**
3475
3589
  * Peer wire protocol version. Bumped when a borrower would misread an older
3476
3590
  * node's answers — not when a field is added, which every version tolerates.
@@ -3999,7 +4113,7 @@ async function executeClaudeCodexFallback(args) {
3999
4113
  method: "POST",
4000
4114
  path: "/backend-api/codex/responses",
4001
4115
  headers: {
4002
- ...ctx.headers,
4116
+ ...pickClaudeFallbackHeaders(ctx.headers),
4003
4117
  "content-type": "application/json",
4004
4118
  accept: "text/event-stream",
4005
4119
  },
@@ -4742,8 +4856,14 @@ function joinAccountLabels(labels, max = 5) {
4742
4856
  function buildEntitlementErrorMessage(failure) {
4743
4857
  const count = new Set(failure.accounts).size;
4744
4858
  const code = failure.errorCode ? ` (${failure.errorCode})` : "";
4859
+ // A borrower is told how many accounts, never which: the labels are the
4860
+ // lender's email addresses.
4861
+ const accounts = getShareContext()
4862
+ ? `${count} pooled account${count === 1 ? "" : "s"} (identity withheld for borrowed requests)`
4863
+ : joinAccountLabels(failure.accounts);
4745
4864
  return (`All ${count} Anthropic account${count === 1 ? "" : "s"} are blocked by an ` +
4746
- `organization entitlement policy${code}. Accounts: ${joinAccountLabels(failure.accounts)}. Upstream: "${failure.message}". Ask an organization admin to re-enable ` +
4865
+ `organization entitlement policy${code}. Accounts: ${accounts}. ` +
4866
+ `Upstream: "${failure.message}". Ask an organization admin to re-enable ` +
4747
4867
  `Claude Code OAuth access, then run: neurolink auth enable anthropic:<account>.`);
4748
4868
  }
4749
4869
  /** Name the subscription window a set of cooldowns is waiting on. */
@@ -6113,7 +6233,7 @@ function finalizeAnthropicTerminalTransportError(args) {
6113
6233
  return clientError;
6114
6234
  }
6115
6235
  async function handleAnthropicNonOkResponse(args) {
6116
- const { response, account, accountState, enabledAccounts, orderedAccounts, tracer, requestStartTime, fetchStartMs, attemptNumber, logAttempt, logProxyBody, logFinalRequest, lastError, authFailureMessage, sawTransientFailure, invalidRequestFailure, entitlementFailure, allowConfiguredModelFallback = false, } = args;
6236
+ const { response, account, accountState, enabledAccounts, orderedAccounts, clientSuppliedBetaForwarded = false, tracer, requestStartTime, fetchStartMs, attemptNumber, logAttempt, logProxyBody, logFinalRequest, lastError, authFailureMessage, sawTransientFailure, invalidRequestFailure, entitlementFailure, allowConfiguredModelFallback = false, } = args;
6117
6237
  let currentLastError = lastError;
6118
6238
  let currentAuthFailureMessage = authFailureMessage;
6119
6239
  let currentSawTransientFailure = sawTransientFailure;
@@ -6159,8 +6279,12 @@ async function handleAnthropicNonOkResponse(args) {
6159
6279
  tracer?.recordRetry(account.label, "beta_unavailable");
6160
6280
  // A tier's lack of a beta is stable, not transient — advance the
6161
6281
  // fill-first primary pointer (like the auth/rate-limit rotation paths) so
6162
- // this account stops being retried first on every future request.
6163
- advancePrimaryIfCurrent(account.key, enabledAccounts.length, orderedAccounts[0]?.key);
6282
+ // this account stops being retried first on every future request. Not
6283
+ // when the rejected beta was the client's own addition: one caller must
6284
+ // not be able to re-rank the pool for everyone by naming a beta.
6285
+ if (!clientSuppliedBetaForwarded) {
6286
+ advancePrimaryIfCurrent(account.key, enabledAccounts.length, orderedAccounts[0]?.key);
6287
+ }
6164
6288
  currentLastError = summarizeErrorMessage(errBody);
6165
6289
  return {
6166
6290
  continueLoop: true,
@@ -6776,20 +6900,18 @@ async function prepareAnthropicAccountAttempt(args) {
6776
6900
  const filteredHeaders = {};
6777
6901
  for (const [k, v] of Object.entries(clientHeaders)) {
6778
6902
  if (typeof v === "string") {
6779
- filteredHeaders[k] = v;
6903
+ filteredHeaders[k.toLowerCase()] = v;
6780
6904
  }
6781
6905
  }
6906
+ const genuineClaudeCode = isGenuineClaudeCodeClient(filteredHeaders);
6782
6907
  const snapshot = isOAuth
6783
6908
  ? await maybeRefreshClaudeSnapshot(account.label, account.key, filteredHeaders, bodyStr)
6784
6909
  : null;
6785
- const headers = {};
6786
- for (const [headerKey, headerValue] of Object.entries(clientHeaders)) {
6787
- const lower = headerKey.toLowerCase();
6788
- if (typeof headerValue === "string" &&
6789
- !BLOCKED_UPSTREAM_HEADERS.has(lower)) {
6790
- headers[lower] = headerValue;
6791
- }
6792
- }
6910
+ // Pool credentials replace the client's, so its authorization/x-api-key
6911
+ // never travel; the allow-list bounds everything else.
6912
+ const headers = filterForwardableUpstreamHeaders(clientHeaders, {
6913
+ includeClientCredentials: false,
6914
+ });
6793
6915
  headers["content-type"] = "application/json";
6794
6916
  if (isOAuth) {
6795
6917
  headers.authorization = `Bearer ${account.token}`;
@@ -6815,17 +6937,27 @@ async function prepareAnthropicAccountAttempt(args) {
6815
6937
  if (!headers.accept) {
6816
6938
  headers.accept = "application/json";
6817
6939
  }
6940
+ let clientSuppliedBetaForwarded = false;
6818
6941
  if (isOAuth) {
6819
- const betaSeed = isClaudeClientRequest
6820
- ? (headers["anthropic-beta"] ?? "")
6821
- : (clientHeaders["anthropic-beta"] ?? "");
6822
- const existing = new Set(betaSeed
6942
+ const proxyBetas = isClaudeClientRequest
6943
+ ? CLAUDE_CODE_OAUTH_BETAS
6944
+ : NON_CLAUDE_OAUTH_BETAS;
6945
+ const knownBetas = new Set([
6946
+ ...CLAUDE_CODE_OAUTH_BETAS,
6947
+ ...NON_CLAUDE_OAUTH_BETAS,
6948
+ ]);
6949
+ const clientBetas = (headers["anthropic-beta"] ?? "")
6823
6950
  .split(",")
6824
6951
  .map((value) => value.trim())
6825
- .filter(Boolean));
6826
- for (const beta of isClaudeClientRequest
6827
- ? CLAUDE_CODE_OAUTH_BETAS
6828
- : NON_CLAUDE_OAUTH_BETAS) {
6952
+ .filter(Boolean);
6953
+ // A beta the pool's tier lacks turns one request into one upstream request
6954
+ // per account, so only Claude Code itself may add betas the proxy would
6955
+ // not send; everyone else is held to the proxy's own set.
6956
+ const existing = new Set(genuineClaudeCode
6957
+ ? clientBetas
6958
+ : clientBetas.filter((beta) => knownBetas.has(beta)));
6959
+ clientSuppliedBetaForwarded = [...existing].some((beta) => !proxyBetas.includes(beta));
6960
+ for (const beta of proxyBetas) {
6829
6961
  existing.add(beta);
6830
6962
  }
6831
6963
  headers["anthropic-beta"] = [...existing].join(",");
@@ -6845,7 +6977,7 @@ async function prepareAnthropicAccountAttempt(args) {
6845
6977
  }
6846
6978
  const buildUpstreamBody = (token) => {
6847
6979
  const built = isOAuth
6848
- ? polyfillOAuthBody(bodyStr, token, snapshot, isClaudeClientRequest, headers["x-claude-code-session-id"])
6980
+ ? polyfillOAuthBody(bodyStr, token, snapshot, isClaudeClientRequest, headers["x-claude-code-session-id"], genuineClaudeCode)
6849
6981
  : { bodyStr };
6850
6982
  return { ...built, ...prepareAnthropicWire(ctx, built.bodyStr) };
6851
6983
  };
@@ -6874,6 +7006,7 @@ async function prepareAnthropicAccountAttempt(args) {
6874
7006
  lastError,
6875
7007
  authFailureMessage,
6876
7008
  headers,
7009
+ clientSuppliedBetaForwarded,
6877
7010
  buildUpstreamBody,
6878
7011
  finalBodyStr,
6879
7012
  preparedContext: polyfilledBody.preparedContext,
@@ -7310,6 +7443,13 @@ async function handleAnthropicRoutedClaudeRequest(args) {
7310
7443
  effectiveAccounts.every((account) => !isAccountAdmissionAvailable(account.key, accountAdmissionCapacity))) {
7311
7444
  queuedAccountAdmission = await acquireFirstAvailableAccountAdmission(effectiveAccounts.map((account) => account.key), accountAdmissionCapacity, ctx.abortSignal, MAX_TRANSIENT_QUEUE_WAIT_MS, configGeneration);
7312
7445
  }
7446
+ // One inbound request may fan out to at most this many accounts. Without
7447
+ // the cap any rotation trigger multiplied one request by the pool size.
7448
+ const maxAccountsPerRequest = resolveMaxAccountsPerRequest();
7449
+ if (effectiveAccounts.length > maxAccountsPerRequest) {
7450
+ logger.debug(`[proxy] capping account fan-out at ${maxAccountsPerRequest} of ${effectiveAccounts.length} eligible accounts`);
7451
+ effectiveAccounts = effectiveAccounts.slice(0, maxAccountsPerRequest);
7452
+ }
7313
7453
  accountLoop: for (const [accountIndex, account,] of effectiveAccounts.entries()) {
7314
7454
  const hasNextAccount = accountIndex < effectiveAccounts.length - 1;
7315
7455
  const accountState = getOrCreateRuntimeState(account.key);
@@ -7616,6 +7756,7 @@ async function handleAnthropicRoutedClaudeRequest(args) {
7616
7756
  invalidRequestFailure: loopState.invalidRequestFailure,
7617
7757
  entitlementFailure: loopState.entitlementFailure,
7618
7758
  allowConfiguredModelFallback: hasConfiguredFallback,
7759
+ clientSuppliedBetaForwarded: preparedAttempt.clientSuppliedBetaForwarded ?? false,
7619
7760
  });
7620
7761
  loopState.lastError = nonOkResult.lastError;
7621
7762
  loopState.authFailureMessage = nonOkResult.authFailureMessage;
@@ -7848,9 +7989,37 @@ async function handleAnthropicRoutedClaudeRequest(args) {
7848
7989
  // Route factory
7849
7990
  // ---------------------------------------------------------------------------
7850
7991
  function isClaudeProxyRouteRuntimeOptions(value) {
7851
- return (value !== undefined &&
7852
- "runtimeConfigProvider" in value &&
7853
- typeof value.runtimeConfigProvider === "function");
7992
+ // The allowlist is a Set; the options bag is a plain object that may carry
7993
+ // only `inboundAuth`, so presence of the provider is no longer the tell.
7994
+ return (value !== undefined && typeof value === "object" && !(value instanceof Set));
7995
+ }
7996
+ /**
7997
+ * Require the inbound token on every route of a group that is not
7998
+ * self-authenticating. The refusal is a `Response` so every adapter — the CLI
7999
+ * listener and the SDK server adapters alike — passes it through unchanged.
8000
+ */
8001
+ function guardRouteGroupWithInboundAuth(group, basePath, token) {
8002
+ if (!token) {
8003
+ return group;
8004
+ }
8005
+ return {
8006
+ ...group,
8007
+ routes: group.routes.map((route) => {
8008
+ if (isInboundAuthExemptPath(route.path.slice(basePath.length))) {
8009
+ return route;
8010
+ }
8011
+ return {
8012
+ ...route,
8013
+ handler: async (ctx) => {
8014
+ const verdict = authorizeInboundRequest(ctx.headers, token);
8015
+ if (!verdict.ok) {
8016
+ return inboundAuthRefusalResponse();
8017
+ }
8018
+ return route.handler(ctx);
8019
+ },
8020
+ };
8021
+ }),
8022
+ };
7854
8023
  }
7855
8024
  function buildEarlyClaudeRequestError(args) {
7856
8025
  const { ctx, body, status, message, errorType } = args;
@@ -7945,7 +8114,12 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
7945
8114
  const runtimeConfigProvider = isClaudeProxyRouteRuntimeOptions(accountAllowlistOrRuntimeOptions)
7946
8115
  ? accountAllowlistOrRuntimeOptions.runtimeConfigProvider
7947
8116
  : undefined;
7948
- return {
8117
+ const inboundAuth = isClaudeProxyRouteRuntimeOptions(accountAllowlistOrRuntimeOptions)
8118
+ ? accountAllowlistOrRuntimeOptions.inboundAuth
8119
+ : undefined;
8120
+ const inboundToken = resolveInboundToken(inboundAuth?.token);
8121
+ warnIfProxyRoutesUnauthenticated("Claude", inboundAuth, inboundToken);
8122
+ const group = {
7949
8123
  prefix: `${basePath}/v1`,
7950
8124
  routes: [
7951
8125
  // =====================================================================
@@ -8612,6 +8786,16 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
8612
8786
  method: "GET",
8613
8787
  path: `${basePath}/accounts`,
8614
8788
  handler: async (ctx) => {
8789
+ // Operator-only, exactly as `/limits`: every row names an account —
8790
+ // an email for an OAuth account — with its quota and spend, and a
8791
+ // live refresh drives usage-API calls under the lender's tokens.
8792
+ if (getShareContext()) {
8793
+ return buildShareRefusal("no_capacity", {
8794
+ status: 403,
8795
+ message: "This proxy does not expose its account inventory to peers. " +
8796
+ "Use GET /peer/limits for what your grant may still do.",
8797
+ }).body;
8798
+ }
8615
8799
  const routing = runtimeConfigProvider?.();
8616
8800
  const effectiveAllowlist = routing?.accountAllowlist ?? accountAllowlist;
8617
8801
  // Hot-reloaded routing wins over the value baked in at construction,
@@ -8845,6 +9029,7 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
8845
9029
  },
8846
9030
  ],
8847
9031
  };
9032
+ return guardRouteGroupWithInboundAuth(group, basePath, inboundToken);
8848
9033
  }
8849
9034
  // ---------------------------------------------------------------------------
8850
9035
  // Helpers
@@ -8940,8 +9125,12 @@ async function clearAuthCooldownAfterRefresh(account, state) {
8940
9125
  });
8941
9126
  }
8942
9127
  function formatReauthMessage(labels) {
8943
- const value = Array.isArray(labels) ? labels.join(", ") : labels;
8944
- return `Account(s) require re-authentication: ${value}. Run: neurolink auth login anthropic --method oauth`;
9128
+ const list = Array.isArray(labels) ? labels : [labels];
9129
+ if (getShareContext()) {
9130
+ const count = new Set(list).size;
9131
+ return `Account(s) require re-authentication: ${count} pooled account${count === 1 ? "" : "s"} (identity withheld for borrowed requests). Ask the lender to run: neurolink auth login anthropic --method oauth`;
9132
+ }
9133
+ return `Account(s) require re-authentication: ${list.join(", ")}. Run: neurolink auth login anthropic --method oauth`;
8945
9134
  }
8946
9135
  function summarizeErrorMessage(message, maxLength = 180) {
8947
9136
  const compact = message.replace(/\s+/g, " ").trim();
@@ -9331,7 +9520,7 @@ export const __testHooks = {
9331
9520
  primaryAccountIndex = 0;
9332
9521
  lastKnownAccountCount = 0;
9333
9522
  },
9334
- polyfillOAuthBody: (bodyStr, isClaudeClientRequest) => polyfillOAuthBody(bodyStr, "test-account-token", null, isClaudeClientRequest),
9523
+ polyfillOAuthBody: (bodyStr, isClaudeClientRequest) => polyfillOAuthBody(bodyStr, "test-account-token", null, isClaudeClientRequest, undefined, isClaudeClientRequest),
9335
9524
  // Pins the sessionId path loadClaudeProxyAccounts resolves ahead of
9336
9525
  // account selection and feeds into selectClaudeProxyAccountOrder for
9337
9526
  // session affinity.
@@ -17,7 +17,7 @@
17
17
  * never collides with anthropic entries) and does pre-commit rotation only, not
18
18
  * the full transient-budget / admission machinery.
19
19
  */
20
- import type { AccountQuota, CodexRefreshTokenStore, CodexResponseEnvelope, CodexResponseStream, CodexRuntimeAccount, CodexTokenRefresher, RateLimitCoolingReason, RouteGroup, ServerContext, ProxyRuntimeConfigProvider } from "../../types/index.js";
20
+ import type { AccountQuota, CodexRefreshTokenStore, CodexResponseEnvelope, CodexResponseStream, CodexRuntimeAccount, CodexTokenRefresher, RateLimitCoolingReason, RouteGroup, ServerContext, ProxyInboundAuthOptions, ProxyRuntimeConfigProvider } from "../../types/index.js";
21
21
  declare function refreshCodexTokenOnceWithDependencies(key: string, refreshToken: string, store: CodexRefreshTokenStore, refresh: CodexTokenRefresher): Promise<{
22
22
  accessToken: string;
23
23
  refreshToken: string;
@@ -89,7 +89,7 @@ export declare function handleCodexResponsesRequest(ctx: ServerContext, runtimeC
89
89
  * `loopbackPort` when omitted.
90
90
  * @returns RouteGroup with the Codex backend Responses endpoint.
91
91
  */
92
- export declare function createCodexProxyRoutes(basePath?: string, runtimeConfigProvider?: ProxyRuntimeConfigProvider, loopbackPort?: number, internalDispatch?: (request: Request) => Response | Promise<Response>): RouteGroup;
92
+ export declare function createCodexProxyRoutes(basePath?: string, runtimeConfigProvider?: ProxyRuntimeConfigProvider, loopbackPort?: number, internalDispatch?: (request: Request) => Response | Promise<Response>, inboundAuth?: ProxyInboundAuthOptions): RouteGroup;
93
93
  export declare const __testHooks: {
94
94
  loadCodexProxyAccounts: typeof loadCodexProxyAccounts;
95
95
  orderCodexAccounts: typeof orderCodexAccounts;