@caeliq/llms 1.0.58 → 1.0.59

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 (50) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +82 -686
  3. package/dist/api/routes.d.ts +2 -0
  4. package/dist/cjs/server.cjs +286 -236
  5. package/dist/cjs/server.cjs.map +4 -4
  6. package/dist/cursor-sdk/session.d.ts +2 -1
  7. package/dist/esm/server.mjs +286 -236
  8. package/dist/esm/server.mjs.map +4 -4
  9. package/dist/routing/inbound-pipeline.d.ts +31 -0
  10. package/dist/routing/protocol-adapter.d.ts +37 -0
  11. package/dist/routing/protocol-endpoints.d.ts +77 -0
  12. package/dist/routing/protocol-errors.d.ts +10 -0
  13. package/dist/server.d.ts +3 -1
  14. package/dist/services/provider.d.ts +1 -1
  15. package/dist/tests/anthropic.provider-wire.d.ts +1 -0
  16. package/dist/tests/claude-auth.identity.d.ts +1 -0
  17. package/dist/tests/client-abort-classification.test.d.ts +1 -0
  18. package/dist/tests/inbound-protocol-routes.d.ts +1 -0
  19. package/dist/tests/inbound-routing-pipeline.d.ts +1 -0
  20. package/dist/tests/openai.inbound-chat.d.ts +1 -0
  21. package/dist/tests/openai.inbound-responses.d.ts +1 -0
  22. package/dist/tests/protocol-endpoints.d.ts +1 -0
  23. package/dist/tests/redact.body-for-log.d.ts +1 -0
  24. package/dist/tests/responses.call-id-sanitize.d.ts +1 -0
  25. package/dist/tests/responses.parallel-and-failure.d.ts +1 -0
  26. package/dist/tests/router-scenario-precedence.d.ts +1 -0
  27. package/dist/tests/system-instructions-fold.d.ts +1 -0
  28. package/dist/tests/upstream-error-semantics.d.ts +1 -0
  29. package/dist/transformer/anthropic.transformer.d.ts +15 -2
  30. package/dist/transformer/claude-auth.transformer.d.ts +36 -4
  31. package/dist/transformer/openai.responses.transformer.d.ts +17 -1
  32. package/dist/transformer/openai.transformer.d.ts +26 -32
  33. package/dist/transformer/tooluse.transformer.d.ts +1 -1
  34. package/dist/transformer/vercel.transformer.d.ts +1 -1
  35. package/dist/transformer/vertex-claude.transformer.d.ts +1 -1
  36. package/dist/transformer/vertex-gemini.transformer.d.ts +1 -1
  37. package/dist/types/transformer.d.ts +9 -0
  38. package/dist/utils/claude-auth.d.ts +9 -2
  39. package/dist/utils/claude-billing.d.ts +58 -0
  40. package/dist/utils/claude-model-catalog.d.ts +49 -0
  41. package/dist/utils/headers.d.ts +10 -0
  42. package/dist/utils/mistral.util.d.ts +1 -1
  43. package/dist/utils/openai.responses.util.d.ts +53 -0
  44. package/dist/utils/redact.d.ts +18 -0
  45. package/dist/utils/request.d.ts +1 -1
  46. package/dist/utils/retry.d.ts +7 -0
  47. package/dist/utils/router.d.ts +7 -0
  48. package/dist/utils/stream.d.ts +2 -0
  49. package/dist/utils/toolCallId.d.ts +8 -0
  50. package/package.json +25 -21
@@ -0,0 +1,31 @@
1
+ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
2
+ import { Transformer } from "../types/transformer";
3
+ import { UnifiedChatRequest } from "../types/llm";
4
+ import { ClientProtocolContext, ProtocolRouteMatch } from "./protocol-endpoints";
5
+ export interface PreparedInboundRequest {
6
+ match: ProtocolRouteMatch;
7
+ protocolContext: ClientProtocolContext;
8
+ /** Original client wire body (preserved for Anthropic custom routers). */
9
+ originalBody: any;
10
+ /**
11
+ * Client wire body after protocol adaptation and CCR-only cleanup
12
+ * (subagent tag stripped, REWRITE_SYSTEM_PROMPT applied). This — not
13
+ * originalBody — is what an exact-wire passthrough must send upstream.
14
+ */
15
+ clientWireBody: any;
16
+ /** Normalized Unified body used for routing and provider conversion. */
17
+ unifiedBody: UnifiedChatRequest;
18
+ providerName: string;
19
+ modelName: string;
20
+ }
21
+ /**
22
+ * Stages 1–7 of the canonical inbound lifecycle:
23
+ * detect → adapt → normalize → route → validate destination.
24
+ */
25
+ export declare function prepareInboundRequest(req: FastifyRequest, reply: FastifyReply, fastify: FastifyInstance, endpointTransformer: Transformer, routePath: string): Promise<PreparedInboundRequest>;
26
+ export declare function resolveDestination(model: string | undefined, protocol?: ClientProtocolContext["protocol"]): {
27
+ providerName: string;
28
+ modelName: string;
29
+ };
30
+ export declare function throwProtocolError(protocol: ClientProtocolContext["protocol"] | undefined, message: string, statusCode: number, code: string, type?: string): never;
31
+ export declare function protocolAwareBypass(provider: any, transformer: Transformer, protocolContext: ClientProtocolContext | undefined, modelName: string | undefined): boolean;
@@ -0,0 +1,37 @@
1
+ import type { UnifiedChatRequest } from "../types/llm";
2
+ import type { Transformer } from "../types/transformer";
3
+ import { ClientProtocolContext, ProtocolRouteMatch } from "./protocol-endpoints";
4
+ export interface ProtocolAdaptResult {
5
+ /** Cloned client body used as normalization input (never the live req.body). */
6
+ normalizationInput: any;
7
+ context: ClientProtocolContext;
8
+ }
9
+ /**
10
+ * Adapt path/query fields into a cloned normalization input and build the
11
+ * initial ClientProtocolContext. Does not mutate the caller's body object.
12
+ */
13
+ export declare function adaptClientRequest(match: ProtocolRouteMatch, rawBody: any, _query?: Record<string, unknown>): ProtocolAdaptResult;
14
+ /**
15
+ * Normalize client wire → Unified once via the endpoint transformer's
16
+ * transformRequestOut when present. Chat Completions bodies are already
17
+ * Unified-shaped; until Phase 2/3 add full converters, fall back to a
18
+ * lightweight projection sufficient for routing.
19
+ */
20
+ export declare function normalizeClientToUnified(protocol: ClientProtocolContext["protocol"], normalizationInput: any, endpointTransformer: Transformer, context: any): Promise<UnifiedChatRequest>;
21
+ /**
22
+ * Provider transformers are allowed to mutate their input. Every primary and
23
+ * fallback attempt therefore needs an independent copy of the normalized body.
24
+ */
25
+ export declare function cloneProtocolBody<T>(value: T): T;
26
+ /**
27
+ * Preserve only known protocol/version and SDK metadata headers for exact-wire
28
+ * passthrough. An allowlist prevents an arbitrary client header from becoming
29
+ * an upstream credential or tenant-routing secret. Provider authentication is
30
+ * generated independently.
31
+ */
32
+ export declare function sanitizePassthroughHeaders(headers: Headers | Record<string, unknown> | undefined): Record<string, string>;
33
+ /**
34
+ * Protocol-aware passthrough: only bypass when the provider speaks the same
35
+ * client protocol (matching transformer name) and the request is same-protocol.
36
+ */
37
+ export declare function shouldBypassTransformersProtocolAware(provider: any, endpointTransformer: Transformer, protocol: ClientProtocolContext["protocol"], bodyModel: string | undefined): boolean;
@@ -0,0 +1,77 @@
1
+ import type { RouterScenarioType } from "../utils/router";
2
+ /**
3
+ * Inbound client protocols supported by CCR's gateway lifecycle.
4
+ */
5
+ export type ClientProtocol = "anthropic_messages" | "openai_chat_completions" | "openai_responses";
6
+ export interface AnthropicSourceRequestFields {
7
+ metadata?: Record<string, unknown>;
8
+ thinking?: Record<string, unknown>;
9
+ outputConfig?: Record<string, unknown>;
10
+ stopSequences?: string[];
11
+ }
12
+ export interface ClientProtocolContext {
13
+ protocol: ClientProtocol;
14
+ pathname: string;
15
+ /** Canonical Fastify route path (without preset prefix), e.g. /v1/responses */
16
+ canonicalPath: string;
17
+ /** Alias path that matched, if different from canonical */
18
+ matchedPath: string;
19
+ originalModel?: string;
20
+ stream: boolean;
21
+ scenarioType?: RouterScenarioType;
22
+ /** Source-only Anthropic semantics retained before destination routing. */
23
+ anthropicSource?: AnthropicSourceRequestFields;
24
+ /** Keep explicit source cache directives or add provider-native automatic caching. */
25
+ anthropicCacheMode?: "preserve" | "automatic";
26
+ /** Claude Code routing metadata extracted without mutating the source billing block. */
27
+ claudeCodeSubagent?: boolean;
28
+ taggedSubagentModel?: string;
29
+ /** Transformer that owns this client protocol */
30
+ ownerTransformerName: string;
31
+ }
32
+ export interface ProtocolRouteMatch {
33
+ protocol: ClientProtocol;
34
+ canonicalPath: string;
35
+ matchedPath: string;
36
+ ownerTransformerName: string;
37
+ /** True when matchedPath is an alias of canonicalPath */
38
+ isAlias: boolean;
39
+ stream: boolean;
40
+ /** Preset namespace prefix without trailing slash, e.g. /preset/foo */
41
+ presetPrefix?: string;
42
+ }
43
+ interface ProtocolRouteSpec {
44
+ protocol: ClientProtocol;
45
+ canonicalPath: string;
46
+ aliases: string[];
47
+ ownerTransformerName: string;
48
+ /** Default stream intent when not encoded in the path */
49
+ defaultStream?: boolean;
50
+ }
51
+ /**
52
+ * Client-facing route table. Aliases are first-class; registration must not
53
+ * rely on transformer endPoint first-wins.
54
+ */
55
+ export declare const PROTOCOL_ROUTE_SPECS: ProtocolRouteSpec[];
56
+ /**
57
+ * Match a client LLM POST path to a protocol. Works with preset prefixes,
58
+ * trailing-slash normalization, and query stripping.
59
+ */
60
+ export declare function matchClientProtocol(method: string, pathnameOrUrl: string): ProtocolRouteMatch | null;
61
+ /** True when method+path is an in-scope routed LLM POST. */
62
+ export declare function isRoutedLlmPost(method: string, pathnameOrUrl: string): boolean;
63
+ /** All Fastify paths that should be registered for a protocol (canonical + aliases). */
64
+ export declare function getRegisteredPathsForProtocol(protocol: ClientProtocol): string[];
65
+ /** Flat list of registered client routes and their owner transformers. */
66
+ export declare function listClientRouteRegistrations(): Array<{
67
+ path: string;
68
+ protocol: ClientProtocol;
69
+ ownerTransformerName: string;
70
+ isCanonical: boolean;
71
+ }>;
72
+ export declare function createClientProtocolContext(match: ProtocolRouteMatch, options?: {
73
+ originalModel?: string;
74
+ stream?: boolean;
75
+ scenarioType?: RouterScenarioType;
76
+ }): ClientProtocolContext;
77
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { ClientProtocol } from "./protocol-endpoints";
2
+ export interface ProtocolErrorBody {
3
+ statusCode: number;
4
+ body: Record<string, unknown>;
5
+ }
6
+ /**
7
+ * Build a client-protocol-shaped error envelope for pre-provider failures
8
+ * (auth, validation, missing Router.default, etc.).
9
+ */
10
+ export declare function protocolErrorBody(protocol: ClientProtocol | undefined, message: string, statusCode: number, code: string, type?: string): ProtocolErrorBody;
package/dist/server.d.ts CHANGED
@@ -56,5 +56,7 @@ export { TokenizerService } from "./services/tokenizer";
56
56
  export { pluginManager, tokenSpeedPlugin, getTokenSpeedStats, getGlobalTokenSpeedStats, CCRPlugin, CCRPluginOptions, PluginMetadata } from "./plugins";
57
57
  export { SSEParserTransform, SSESerializerTransform, rewriteStream } from "./utils/sse";
58
58
  export { isClientAbortError } from "./utils/retry";
59
- export { sanitizeHeadersForLog, diffHeadersForLog, } from "./utils/redact";
59
+ export { sanitizeHeadersForLog, diffHeadersForLog, sanitizeBodyForLog, DEFAULT_LOG_BODY_MAX_BYTES, } from "./utils/redact";
60
60
  export { exchangeAuthorizationCode, fetchUserEmail, resolveProjectId, saveTokens, loadTokens, getValidAccessToken, ANTIGRAVITY_CLIENT_ID, ANTIGRAVITY_CLIENT_SECRET, ANTIGRAVITY_REDIRECT_URI, ANTIGRAVITY_SCOPES, type AntigravityTokens, } from "./utils/antigravity-auth";
61
+ export { matchClientProtocol, isRoutedLlmPost, listClientRouteRegistrations, type ClientProtocol, type ClientProtocolContext, type ProtocolRouteMatch, } from "./routing/protocol-endpoints";
62
+ export { protocolErrorBody } from "./routing/protocol-errors";
@@ -17,7 +17,7 @@ export declare class ProviderService {
17
17
  getProvider(name: string): LLMProvider | undefined;
18
18
  updateProvider(id: string, updates: Partial<LLMProvider>): LLMProvider | null;
19
19
  deleteProvider(id: string): boolean;
20
- toggleProvider(name: string, enabled: boolean): boolean;
20
+ toggleProvider(name: string, _enabled: boolean): boolean;
21
21
  resolveModelRoute(modelName: string): RequestRouteInfo | null;
22
22
  getAvailableModelNames(): string[];
23
23
  getModelRoutes(): ModelRoute[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -7,7 +7,20 @@ export declare class AnthropicTransformer implements Transformer {
7
7
  private useBearer;
8
8
  logger?: any;
9
9
  constructor(options?: TransformerOptions | undefined);
10
- auth(request: any, provider: LLMProvider, _context?: any): Promise<any>;
10
+ auth(request: any, provider: LLMProvider, context?: TransformerContext): Promise<any>;
11
+ /**
12
+ * Provider-side: Unified → Anthropic Messages wire.
13
+ * Enables cross-protocol clients (Chat/Responses) to reach Anthropic-shaped
14
+ * upstreams. Same-protocol Anthropic clients still use transformRequestOut
15
+ * for client→Unified and may bypass via protocol-aware passthrough.
16
+ */
17
+ transformRequestIn(request: UnifiedChatRequest, provider: LLMProvider, _context?: TransformerContext): Promise<Record<string, any>>;
18
+ /**
19
+ * Provider-side: Anthropic Messages wire → Unified (Chat Completions shape).
20
+ * Reuses the Vertex Claude Anthropic→Unified converter so streaming and JSON
21
+ * envelopes match existing Anthropic upstream behavior.
22
+ */
23
+ transformResponseOut(response: Response, _context?: TransformerContext): Promise<Response>;
11
24
  transformRequestOut(request: Record<string, any>, context?: TransformerContext): Promise<UnifiedChatRequest>;
12
25
  transformResponseIn(response: Response, context?: TransformerContext): Promise<Response>;
13
26
  /**
@@ -15,7 +28,7 @@ export declare class AnthropicTransformer implements Transformer {
15
28
  * Used by claude-auth.transformRequestIn() to reconstruct the body before
16
29
  * sending to Anthropic, preserving all original parameters.
17
30
  */
18
- static buildAnthropicBody(request: UnifiedChatRequest, logger?: any): Record<string, any>;
31
+ static buildAnthropicBody(request: UnifiedChatRequest, logger?: any, context?: TransformerContext): Record<string, any>;
19
32
  private convertAnthropicToolsToUnified;
20
33
  private convertOpenAIStreamToAnthropic;
21
34
  private convertOpenAIResponseToAnthropic;
@@ -1,25 +1,57 @@
1
1
  import { UnifiedChatRequest } from "../types/llm";
2
2
  import { Transformer, TransformerContext } from "../types/transformer";
3
+ import { ClaudeModelCatalogEntry } from "../utils/claude-model-catalog";
3
4
  /** Anthropic beta required for Claude subscription / Claude Code OAuth Bearer auth. */
4
5
  export declare const CLAUDE_OAUTH_REQUIRED_BETA = "oauth-2025-04-20";
5
6
  export declare function mergeAnthropicBetaValues(...values: Array<string | undefined | null>): string;
6
7
  /** Read a named header value from a Fastify/Node headers object (case-insensitive). */
7
8
  export declare function readHeaderValue(headers: Record<string, unknown> | undefined, name: string): string | undefined;
9
+ /** True when the client's User-Agent identifies it as the genuine Claude Code CLI. */
10
+ export declare function isClaudeCodeClient(userAgent: string | undefined): boolean;
8
11
  /**
9
12
  * Build outbound anthropic-beta for Claude subscription OAuth.
10
13
  *
11
14
  * - If the client sent anthropic-beta (e.g. Claude Code), merge with
12
15
  * oauth-2025-04-20 (deduped, case-insensitive).
13
- * - Otherwise, send only oauth-2025-04-20. Do not synthesise Claude Code
14
- * betas — Anthropic validates the attestation on claude-code-20250219
15
- * and subscription OAuth works without it for non-Claude-Code clients.
16
+ * - Otherwise, send only oauth-2025-04-20.
16
17
  */
17
18
  export declare function resolveClaudeAuthAnthropicBeta(input: {
18
19
  clientBeta?: string;
19
20
  }): string;
21
+ /**
22
+ * Build outbound anthropic-beta for the non-Claude-Code (full synthesis)
23
+ * branch, mirroring Claude Code's own model-driven beta selection. Never
24
+ * derived from model-name prefix matching — always from the capability
25
+ * catalog. `ANTHROPIC_BETA_FLAGS` replaces the list wholesale.
26
+ */
27
+ export declare function resolveClaudeAuthBetas(modelId: string | undefined, opts?: {
28
+ envBeta?: string;
29
+ }): string;
30
+ /**
31
+ * Reshape a built Anthropic body's `thinking`/`output_config`/`max_tokens`
32
+ * to what the resolved model actually supports, replacing a hand-rolled
33
+ * per-model denylist with a single catalog-driven pass. Operates on the
34
+ * post-build Anthropic body (not the Unified request) because
35
+ * `buildAnthropicBody` may synthesize `thinking`/`output_config` itself.
36
+ */
37
+ export declare function applyClaudeModelCapabilityAdjustments(anthropicBody: Record<string, any>, entry: ClaudeModelCatalogEntry | undefined): void;
38
+ /** Test-only reset hook so session-id state doesn't leak across test cases. */
39
+ export declare function __resetClaudeAuthTransformerStateForTests(): void;
20
40
  export declare class ClaudeAuthTransformer implements Transformer {
21
41
  name: string;
22
42
  logger?: any;
23
43
  transformRequestIn(request: UnifiedChatRequest, provider: any, context?: TransformerContext): Promise<Record<string, any>>;
24
- transformResponseOut(response: Response): Promise<Response>;
44
+ /**
45
+ * Body/URL/wire-format conversion belong to AnthropicTransformer's
46
+ * provider pair, which already ran (response-side order is reversed, so
47
+ * it runs before this stage). This stage only inspects the resulting
48
+ * response for subscription-specific overage observability.
49
+ */
50
+ transformResponseOut(response: Response, context?: TransformerContext): Promise<Response>;
51
+ /**
52
+ * 401 recovery: reload the token file in case another process (e.g. a
53
+ * concurrent `ccr claude-auth` re-login) rotated it externally, otherwise
54
+ * refresh and persist. Never falls through to an unauthenticated request.
55
+ */
56
+ private recoverUnauthorizedAuth;
25
57
  }
@@ -1,11 +1,27 @@
1
1
  import { UnifiedChatRequest } from "../types/llm";
2
- import { Transformer } from "../types/transformer";
2
+ import { Transformer, TransformerContext } from "../types/transformer";
3
3
  export declare class OpenAIResponsesTransformer implements Transformer {
4
4
  logger?: any;
5
5
  name: string;
6
6
  endPoint: string;
7
+ /**
8
+ * Client → Unified: validate Responses MVP and project to Chat Completions shape.
9
+ * Call-id mapping is stored on the transformer context for the response path.
10
+ */
11
+ transformRequestOut(request: any, context?: TransformerContext): Promise<UnifiedChatRequest>;
12
+ /**
13
+ * Unified → client Responses: JSON object or SSE lifecycle with mandatory
14
+ * content_part events (Codex requires these or streamed text is discarded).
15
+ */
16
+ transformResponseIn(response: Response, context?: TransformerContext): Promise<Response>;
17
+ private convertUnifiedStreamToResponses;
7
18
  transformRequestIn(request: UnifiedChatRequest, provider?: any, context?: any): Promise<UnifiedChatRequest>;
8
19
  transformResponseOut(response: Response): Promise<Response>;
20
+ /**
21
+ * Convert one Responses stream event to a Chat chunk. `choices[0].index` is
22
+ * always 0 — parallel-call identity lives in `delta.tool_calls[n].index`,
23
+ * allocated per Responses item by `toolIndexFor`.
24
+ */
9
25
  private convertStreamEvent;
10
26
  private normalizeRequestContent;
11
27
  private convertResponseToChat;
@@ -1,39 +1,12 @@
1
- import { Transformer } from "../types/transformer";
1
+ import { Transformer, TransformerContext } from "../types/transformer";
2
2
  import { UnifiedChatRequest } from "../types/llm";
3
3
  /**
4
4
  * Server-side route handler for the OpenAI Chat Completions API.
5
5
  *
6
- * ## How endPoint works
7
- *
8
- * At startup, `registerApiRoutes` (see `api/routes.ts`) scans all registered
9
- * transformers for ones that define `endPoint`. For each, it registers a
10
- * `POST` route at that path. When a request hits the route,
11
- * `handleTransformerEndpoint` is invoked with the matching transformer as
12
- * the "endpoint transformer" — the one responsible for converting between
13
- * the external wire format and the internal Unified format.
14
- *
15
- * ## Request handling
16
- *
17
- * The Unified format IS the OpenAI Chat Completions format. The conversion
18
- * from Anthropic → Unified already happened in
19
- * `AnthropicTransformer.transformRequestOut()` (which runs first in the
20
- * pipeline). So by the time the provider chain executes, the body is already
21
- * in the right shape — no further conversion is needed.
22
- *
23
- * `transformRequestIn` also translates Claude Code cache intent to the
24
- * selected provider's native Chat Completions behavior. The provider identity
25
- * is checked before adding request-level fields so OpenAI-compatible services
26
- * do not receive OpenAI-only parameters.
27
- *
28
- * ## Relationship to OpenAIResponsesTransformer
29
- *
30
- * `OpenAIResponsesTransformer` (in `openai.responses.transformer.ts`) is the
31
- * counterpart for the Responses API (`/v1/responses`). Unlike this
32
- * transformer, it defines `transformRequestIn` / `transformResponseOut`
33
- * because the Responses API uses a different wire format (e.g. `messages`
34
- * → `input`, function tools → flat tool definitions). It also uses the
35
- * shared utilities in `openai.util.ts` (`validateOpenAIToolCalls`,
36
- * `injectPromptCaching`) to sanitize the Unified body before converting it.
6
+ * The Unified format IS the OpenAI Chat Completions format. Client inbound
7
+ * normalization validates the MVP subset and passes through already-correct
8
+ * Chat bodies. Provider-side transformRequestIn applies cache policy for
9
+ * OpenAI-compatible upstreams.
37
10
  *
38
11
  * ## Full request pipeline (for context)
39
12
  *
@@ -44,9 +17,30 @@ import { UnifiedChatRequest } from "../types/llm";
44
17
  * → provider.transformer.use[].transformResponseOut() // provider middleware (reversed)
45
18
  * → AnthropicTransformer.transformResponseIn() // Unified (OpenAI) → Anthropic
46
19
  * → Client
20
+ *
21
+ * For inbound Chat Completions clients:
22
+ *
23
+ * Client → POST /v1/chat/completions
24
+ * → OpenAITransformer.transformRequestOut() // validate → Unified
25
+ * → provider.transformer.use[].transformRequestIn()
26
+ * → … → OpenAITransformer.transformResponseIn() // identity / light normalize
47
27
  */
48
28
  export declare class OpenAITransformer implements Transformer {
49
29
  name: string;
50
30
  endPoint: string;
31
+ /**
32
+ * Client → Unified: validate the Chat Completions MVP subset.
33
+ * Unified is already Chat-shaped, so this is validation + light normalization.
34
+ */
35
+ transformRequestOut(request: any, _context?: TransformerContext): Promise<UnifiedChatRequest>;
36
+ /**
37
+ * Provider-side: apply OpenAI-native cache policy to a Unified Chat body.
38
+ */
51
39
  transformRequestIn(request: UnifiedChatRequest, provider: any, context: any): Promise<UnifiedChatRequest>;
40
+ /**
41
+ * Unified → client Chat Completions: pass through already-correct Chat JSON/SSE.
42
+ * Ensures Content-Type and that streaming responses terminate with [DONE] when
43
+ * the upstream already speaks Chat Completions.
44
+ */
45
+ transformResponseIn(response: Response, _context?: TransformerContext): Promise<Response>;
52
46
  }
@@ -2,6 +2,6 @@ import { UnifiedChatRequest } from "../types/llm";
2
2
  import { Transformer } from "../types/transformer";
3
3
  export declare class TooluseTransformer implements Transformer {
4
4
  name: string;
5
- transformRequestIn(request: UnifiedChatRequest, provider?: any, context?: any): Promise<any>;
5
+ transformRequestIn(request: UnifiedChatRequest, _provider?: any, _context?: any): Promise<any>;
6
6
  transformResponseOut(response: Response): Promise<Response>;
7
7
  }
@@ -6,6 +6,6 @@ export declare class VercelTransformer implements Transformer {
6
6
  logger?: any;
7
7
  endPoint: string;
8
8
  constructor(options?: TransformerOptions | undefined);
9
- transformRequestIn(request: UnifiedChatRequest, provider?: any, context?: any): Promise<UnifiedChatRequest>;
9
+ transformRequestIn(request: UnifiedChatRequest, _provider?: any, _context?: any): Promise<UnifiedChatRequest>;
10
10
  transformResponseOut(response: Response): Promise<Response>;
11
11
  }
@@ -3,7 +3,7 @@ import { Transformer } from "../types/transformer";
3
3
  export declare class VertexClaudeTransformer implements Transformer {
4
4
  logger?: any;
5
5
  name: string;
6
- transformRequestIn(request: UnifiedChatRequest, provider: LLMProvider): Promise<Record<string, any>>;
6
+ transformRequestIn(request: UnifiedChatRequest, _provider: LLMProvider): Promise<Record<string, any>>;
7
7
  transformRequestOut(request: Record<string, any>): Promise<UnifiedChatRequest>;
8
8
  transformResponseOut(response: Response): Promise<Response>;
9
9
  }
@@ -6,7 +6,7 @@ export declare class VertexGeminiTransformer implements Transformer {
6
6
  name: string;
7
7
  private readonly thoughtSignatureFallback;
8
8
  constructor(options?: TransformerOptions);
9
- transformRequestIn(request: UnifiedChatRequest, provider: LLMProvider, context?: any): Promise<Record<string, any>>;
9
+ transformRequestIn(request: UnifiedChatRequest, provider: LLMProvider, _context?: any): Promise<Record<string, any>>;
10
10
  transformRequestOut(request: Record<string, any>): Promise<UnifiedChatRequest>;
11
11
  transformResponseOut(response: Response, context?: TransformerContext): Promise<Response>;
12
12
  }
@@ -14,6 +14,15 @@ export interface TransformerContext {
14
14
  signal?: AbortSignal;
15
15
  /** Protocol semantics that must not be serialized into the provider body. */
16
16
  unifiedRequest?: UnifiedRequestRuntime;
17
+ /**
18
+ * Set by claude-auth's transformRequestIn (non-Claude-Code branch) so that
19
+ * AnthropicTransformer.transformRequestIn — which owns building the wire
20
+ * body — can apply claude-auth's catalog-driven capability clamping and
21
+ * synthesized user_id metadata immediately after building it. Keeps model
22
+ * capability/identity-synthesis policy owned by claude-auth while
23
+ * AnthropicTransformer remains the sole body-shape/timing owner.
24
+ */
25
+ claudeAuthPostBuildHook?: (anthropicBody: Record<string, any>) => void;
17
26
  [key: string]: any;
18
27
  }
19
28
  export type Transformer = {
@@ -1,4 +1,5 @@
1
- declare const CLAUDE_AUTH_FILE: string;
1
+ declare function getClaudeAuthFilePath(): string;
2
+ declare function getClaudeDeviceFilePath(): string;
2
3
  declare const OAUTH_CONFIG: {
3
4
  client_id: string;
4
5
  authorization_endpoint: string;
@@ -19,4 +20,10 @@ export declare function saveTokens(tokens: ClaudeTokens): void;
19
20
  export declare function isTokenExpired(tokens: ClaudeTokens, leewaySeconds?: number): boolean;
20
21
  export declare function refreshTokens(refreshToken: string): Promise<ClaudeTokens>;
21
22
  export declare function getValidAccessToken(): Promise<ClaudeTokens>;
22
- export { OAUTH_CONFIG, CLAUDE_AUTH_FILE };
23
+ /**
24
+ * Load the persisted synthesized-client device id, minting and persisting a
25
+ * fresh 64-hex value on first use. Stored alongside the OAuth token file
26
+ * (mode 0600) but kept separate from it — it is not a credential.
27
+ */
28
+ export declare function loadOrCreateDeviceId(): string;
29
+ export { OAUTH_CONFIG, getClaudeAuthFilePath, getClaudeDeviceFilePath };
@@ -0,0 +1,58 @@
1
+ import { TextContent, UnifiedChatRequest, UnifiedMessage } from "../types/llm";
2
+ /** Salt used in Claude Code's `cc_version` suffix hash (`RYn()`). */
3
+ export declare const BILLING_SALT = "59cf53e54c78";
4
+ export declare const CC_VERSION: string;
5
+ export declare const CC_ENTRYPOINT: string;
6
+ export declare const SYSTEM_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
7
+ export declare function extractFirstUserMessageText(messages: UnifiedMessage[] | undefined): string;
8
+ export declare function computeVersionSuffix(text: string, version: string): string;
9
+ /** 5 lowercase hex chars, cached per process — not a content hash. */
10
+ export declare function sessionCch(): string;
11
+ export declare function __resetClaudeBillingStateForTests(): void;
12
+ export declare function buildClaudeBillingHeaderValue(messages: UnifiedMessage[] | undefined, version?: string, entrypoint?: string): string;
13
+ /**
14
+ * Normalize `request.system` to an array as a complete ordered fold:
15
+ * top-level `request.system` blocks first, then every `role:"system"` /
16
+ * `role:"developer"` message's text blocks in source order. Consumed messages
17
+ * are removed from `request.messages` so no downstream builder can drop or
18
+ * double-emit them. Block-level `cache_control` is preserved.
19
+ */
20
+ export declare function normalizeSystemToArray(request: UnifiedChatRequest): TextContent[];
21
+ /** Drop any existing billing entry (dedupe) and prepend a fresh one at system[0]. */
22
+ export declare function applyClaudeBillingSystemBlock(system: TextContent[], messages: UnifiedMessage[] | undefined): void;
23
+ /**
24
+ * Insert SYSTEM_IDENTITY as its own entry at system[1]. Any remaining
25
+ * caller system content is left in place here — relocateForeignSystemContent
26
+ * (called separately, afterwards) is what moves it out of system[].
27
+ */
28
+ export declare function applyClaudeSystemIdentity(system: TextContent[]): void;
29
+ /**
30
+ * Move everything in `system[]` past the identity block (index 1) into the
31
+ * first user message instead. Anthropic's OAuth billing validator appears to
32
+ * inspect `system[]` content beyond the identity prefix and reject requests
33
+ * whose system array carries a foreign harness prompt with an "out of extra
34
+ * usage" 400 — the same approach used by third-party Claude Code OAuth
35
+ * clients (e.g. opencode-claude-auth). The relocated text still reaches the
36
+ * model, just as part of the first user turn rather than
37
+ * `system[]`.
38
+ *
39
+ * A no-op when there is no user message to attach the content to, so nothing
40
+ * is silently dropped — the caller's system content stays in `system[]`
41
+ * instead.
42
+ */
43
+ export declare function relocateForeignSystemContent(system: TextContent[], messages: UnifiedMessage[] | undefined): void;
44
+ /**
45
+ * Claude Code's OAuth validator expects tool names in the mcp_PascalCase
46
+ * spelling used by the official CLI. Non-Claude-Code clients commonly send
47
+ * ordinary names such as `bash` or `read`, so normalize them before the
48
+ * Anthropic body is built. The prefix check keeps this operation idempotent.
49
+ */
50
+ export declare function prefixClaudeToolName(name: string): string;
51
+ /** Restore a Claude Code tool name to the caller's original spelling. */
52
+ export declare function unprefixClaudeToolName(name: string): string;
53
+ /** Rewrite tool names in a Unified request in place for the OAuth wire path. */
54
+ export declare function prefixClaudeToolNames(request: UnifiedChatRequest, nameMap?: Map<string, string>): void;
55
+ /** Rewrite tool names in a Unified/OpenAI-shaped response in place. */
56
+ export declare function unprefixClaudeToolNames(value: any, nameMap?: Map<string, string>): void;
57
+ /** Rewrite one OpenAI SSE data payload's tool names. */
58
+ export declare function unprefixClaudeToolNamesInSseData(value: any, nameMap?: Map<string, string>): void;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Claude Code's bundled model capability catalog (extracted from v2.1.220).
3
+ *
4
+ * Every model-dependent decision in the claude-auth impersonation path (beta
5
+ * flags, effort support, thinking shape, max_tokens ceiling) is driven by
6
+ * `capabilities` membership here, never by matching on the model name.
7
+ */
8
+ export interface ClaudeModelCatalogEntry {
9
+ /** Context window in tokens, or undefined when the model predates 1M-context routing. */
10
+ window?: number;
11
+ /** Router carries a native (unconditional) 1M-token context window. */
12
+ nativeOneMillion: boolean;
13
+ /** Model can opt into a 1M-token window via the context-1m-2025-08-07 beta. */
14
+ supportsOneMillionBeta: boolean;
15
+ /** Model id accepts an explicit "[1m]" wire suffix. */
16
+ supportsOneMillionSuffix: boolean;
17
+ maxOutputTokens: {
18
+ default: number;
19
+ upper: number;
20
+ };
21
+ defaultEffort?: string;
22
+ capabilities: string[];
23
+ }
24
+ export declare const CLAUDE_MODEL_CATALOG: Record<string, ClaudeModelCatalogEntry>;
25
+ /** Strip the "[1m]" wire marker, reporting whether it was present. */
26
+ export declare function stripOneMillionContextMarker(modelId: string | undefined): {
27
+ modelId: string;
28
+ requestedOneMillion: boolean;
29
+ };
30
+ /**
31
+ * Normalize a routed model id (CCR "provider,model" selector, an optional
32
+ * "[1m]" marker, and Anthropic's dated model-id suffix) down to a catalog
33
+ * key, e.g. "anthropic,claude-opus-5-20260101[1m]" -> "claude-opus-5".
34
+ */
35
+ export declare function normalizeModelIdForCatalog(modelId: string | undefined): string;
36
+ /** Look up a model's catalog entry. Unknown models resolve to undefined (minimal capability set). */
37
+ export declare function lookupClaudeModelCatalogEntry(modelId: string | undefined): ClaudeModelCatalogEntry | undefined;
38
+ export declare function catalogEntryHasCapability(entry: ClaudeModelCatalogEntry | undefined, capability: string): boolean;
39
+ /**
40
+ * Whether extended-thinking betas apply to this model. The catalog has no
41
+ * dedicated "supports_thinking" column, but every entry that predates the
42
+ * Claude 4 thinking generation (claude-3-5-haiku, claude-3-5-sonnet,
43
+ * claude-3-7-sonnet) — plus the not-yet-classified claude-mythos-5 — carries
44
+ * an empty capability set; every thinking-capable model carries at least
45
+ * "context_management". A non-empty capability set is therefore a reliable,
46
+ * self-documenting proxy that also degrades unknown models to "no thinking"
47
+ * rather than guessing.
48
+ */
49
+ export declare function catalogEntrySupportsThinking(entry: ClaudeModelCatalogEntry | undefined): boolean;
@@ -0,0 +1,10 @@
1
+ export type HeaderRecord = Record<string, string | undefined>;
2
+ export declare function mergeHeadersCaseInsensitive(base: HeaderRecord | undefined, update: HeaderRecord | undefined): HeaderRecord;
3
+ /**
4
+ * Carry non-representational upstream response headers (rate-limit/usage
5
+ * metadata, request ids, etc.) onto a reshaped `Response`, so overage
6
+ * observability (`anthropic-ratelimit-unified-*`) survives SSE re-framing.
7
+ */
8
+ export declare function preserveUpstreamResponseHeaders(headers: Headers | undefined): Record<string, string>;
9
+ export declare function selectSafeDownstreamHeaders(headers: Headers | HeaderRecord | undefined): Record<string, string>;
10
+ export declare function canonicalizeOutboundHeaders(headers: HeaderRecord | undefined, fallbackBearer?: string): Record<string, string>;
@@ -2,7 +2,7 @@ import { UnifiedChatRequest } from "../types/llm";
2
2
  /**
3
3
  * Transform incoming request to Mistral-compatible format
4
4
  */
5
- export declare function buildRequestBody(request: UnifiedChatRequest, context?: any, provider?: any): Record<string, any>;
5
+ export declare function buildRequestBody(request: UnifiedChatRequest, context?: any, _provider?: any): Record<string, any>;
6
6
  /**
7
7
  * Transform a Mistral provider request back into a UnifiedChatRequest
8
8
  */