@caeliq/llms 1.0.60 → 1.0.61

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/server.d.ts CHANGED
@@ -60,3 +60,5 @@ export { sanitizeHeadersForLog, diffHeadersForLog, sanitizeBodyForLog, DEFAULT_L
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
61
  export { matchClientProtocol, isRoutedLlmPost, listClientRouteRegistrations, type ClientProtocol, type ClientProtocolContext, type ProtocolRouteMatch, } from "./routing/protocol-endpoints";
62
62
  export { protocolErrorBody } from "./routing/protocol-errors";
63
+ export { setHealthReporter } from "./utils/health-reporter";
64
+ export type { HealthReporter } from "./utils/health-reporter";
@@ -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 {};
@@ -29,6 +29,7 @@ import { OpencodeHeadersTransformer } from "./opencode-headers.transformer";
29
29
  import { ClaudeAuthTransformer } from "./claude-auth.transformer";
30
30
  import { CursorSdkTransformer } from "./cursor-sdk.transformer";
31
31
  import { AntigravityAuthTransformer } from "./antigravity-auth.transformer";
32
+ import { XaiAuthTransformer } from "./xai-auth.transformer";
32
33
  declare const _default: {
33
34
  AnthropicTransformer: typeof AnthropicTransformer;
34
35
  GeminiTransformer: typeof GeminiTransformer;
@@ -61,5 +62,6 @@ declare const _default: {
61
62
  ClaudeAuthTransformer: typeof ClaudeAuthTransformer;
62
63
  CursorSdkTransformer: typeof CursorSdkTransformer;
63
64
  AntigravityAuthTransformer: typeof AntigravityAuthTransformer;
65
+ XaiAuthTransformer: typeof XaiAuthTransformer;
64
66
  };
65
67
  export default _default;
@@ -23,6 +23,7 @@ export declare class OpenAIResponsesTransformer implements Transformer {
23
23
  * allocated per Responses item by `toolIndexFor`.
24
24
  */
25
25
  private convertStreamEvent;
26
+ private convertStreamEventCore;
26
27
  private normalizeRequestContent;
27
28
  private convertResponseToChat;
28
29
  private buildImageContent;
@@ -0,0 +1,24 @@
1
+ import { Transformer } from "../types/transformer";
2
+ export declare class XaiAuthTransformer implements Transformer {
3
+ name: string;
4
+ logger?: any;
5
+ private resolveAuth;
6
+ /**
7
+ * openai-responses only converts the body — it doesn't own the outbound
8
+ * URL (its endPoint is a client-facing inbound route registration, not an
9
+ * outbound path). The generic pipeline falls back to a bare
10
+ * `provider.baseUrl` when no transformer sets config.url (routes.ts:739),
11
+ * so this transformer must build the actual `/responses` URL itself,
12
+ * mirroring CodexTransformer's `${baseUrl}/responses`.
13
+ */
14
+ private buildConfig;
15
+ transformRequestIn(request: any, provider: any): Promise<Record<string, any>>;
16
+ auth(_request: any, provider: any): Promise<any>;
17
+ /**
18
+ * 401 recovery. PAT mode has nothing to recover — a bad literal key or
19
+ * env value can't be fixed by CCR. OAuth mode reloads the token file
20
+ * (another process may have already refreshed it), otherwise refreshes
21
+ * and persists, mirroring ClaudeAuthTransformer.recoverUnauthorizedAuth.
22
+ */
23
+ private recoverUnauthorizedAuth;
24
+ }
@@ -63,7 +63,12 @@ export interface UnifiedMessage {
63
63
  };
64
64
  thinking?: {
65
65
  content: string;
66
+ /** Anthropic thinking signature; never treat as Responses ciphertext. */
66
67
  signature?: string;
68
+ /** Provider-minted Responses/Codex reasoning ciphertext for replay. */
69
+ encrypted_content?: string;
70
+ /** Responses reasoning item id (`rs_…`); not interchangeable with ciphertext. */
71
+ id?: string;
67
72
  };
68
73
  }
69
74
  export interface UnifiedTool {
@@ -161,6 +166,8 @@ export interface StreamChunk {
161
166
  thinking?: {
162
167
  content?: string;
163
168
  signature?: string;
169
+ encrypted_content?: string;
170
+ id?: string;
164
171
  };
165
172
  tool_calls?: Array<{
166
173
  id?: string;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Pipeline position a snapshot was taken at. `client` is the Unified/exact-wire
3
+ * body handed to the provider chain; `wire` is what actually left for upstream.
4
+ * Comparing the two attributes a broken prefix to the client or to our own
5
+ * transformer chain without a bisect.
6
+ */
7
+ export type CachePrefixStage = "client" | "wire";
8
+ export type CacheAffinityHeaders = {
9
+ sessionId?: string;
10
+ threadId?: string;
11
+ clientRequestId?: string;
12
+ };
13
+ export type CachePrefixSegment = {
14
+ path: string;
15
+ role?: string;
16
+ type?: string;
17
+ hash: string;
18
+ /** Rough token weight (JSON chars / 4) — enough to rank misses by cost. */
19
+ approxTokens: number;
20
+ breakpoints: number;
21
+ reasoningId?: string;
22
+ };
23
+ export type CachePrefixSnapshot = {
24
+ createdAt: number;
25
+ model?: string;
26
+ prompt_cache_key?: string;
27
+ session_id?: string;
28
+ affinity?: CacheAffinityHeaders;
29
+ lastAssistantBlockOrder?: string[];
30
+ breakpointPaths: string[];
31
+ systemHash?: string;
32
+ toolsHash?: string;
33
+ approxTokens: number;
34
+ unstableIds: string[];
35
+ segments: CachePrefixSegment[];
36
+ };
37
+ export type CachePrefixChange = "none" | "appended" | "modified" | "removed";
38
+ export type CachePrefixIdSource = "session" | "cache_key" | "fingerprint";
39
+ export type CachePrefixDiff = {
40
+ conversationId: string;
41
+ conversationIdSource: CachePrefixIdSource;
42
+ stage: CachePrefixStage;
43
+ firstTurn: boolean;
44
+ prefixIntact: boolean;
45
+ change: CachePrefixChange;
46
+ unchangedPrefixCount: number;
47
+ previousSegmentCount: number;
48
+ currentSegmentCount: number;
49
+ /** Approximate tokens of prefix that stayed byte-identical. */
50
+ unchangedPrefixApproxTokens: number;
51
+ /** Approximate tokens the provider must re-read because the prefix moved. */
52
+ approxPrefixTokensLost: number;
53
+ /** Gap since the previous turn — separates a real break from TTL expiry. */
54
+ msSinceLastTurn?: number;
55
+ prompt_cache_keyChanged: boolean;
56
+ affinityChanged: boolean;
57
+ lastAssistantBlockOrderChanged: boolean;
58
+ modelChanged: boolean;
59
+ systemHashChanged: boolean;
60
+ toolsHashChanged: boolean;
61
+ breakpointsMoved: boolean;
62
+ /** Ids carrying an embedded timestamp — they rewrite the prefix every turn. */
63
+ unstableIds?: string[];
64
+ firstDivergencePath?: string;
65
+ firstDivergence?: {
66
+ previous?: DivergenceSide;
67
+ current?: DivergenceSide;
68
+ };
69
+ appendedPaths?: string[];
70
+ removedPaths?: string[];
71
+ prompt_cache_key?: {
72
+ previous?: string;
73
+ current?: string;
74
+ };
75
+ affinity?: {
76
+ previous?: CacheAffinityHeaders;
77
+ current?: CacheAffinityHeaders;
78
+ };
79
+ lastAssistantBlockOrder?: {
80
+ previous?: string[];
81
+ current?: string[];
82
+ };
83
+ };
84
+ export type CachePrefixDiffOptions = {
85
+ stage?: CachePrefixStage;
86
+ provider?: string;
87
+ model?: string;
88
+ /**
89
+ * Persist this snapshot as the baseline for the next turn. Pass false when
90
+ * upstream rejected the request — a body that was never cached must not
91
+ * become the baseline the following turn is judged against.
92
+ */
93
+ commit?: boolean;
94
+ };
95
+ export declare function __resetCachePrefixSnapshotsForTests(): void;
96
+ /**
97
+ * Compact, content-free snapshot of the outbound fields prompt caching uses.
98
+ */
99
+ export declare function snapshotOutboundCachePrefix(body: Record<string, any> | null | undefined, affinity?: CacheAffinityHeaders): CachePrefixSnapshot | null;
100
+ type DivergenceSide = Pick<CachePrefixSegment, "path" | "role" | "type" | "approxTokens" | "breakpoints" | "reasoningId">;
101
+ export declare function diffCachePrefixSnapshots(conversationId: string, previous: CachePrefixSnapshot | undefined, current: CachePrefixSnapshot, meta?: {
102
+ stage?: CachePrefixStage;
103
+ conversationIdSource?: CachePrefixIdSource;
104
+ }): CachePrefixDiff;
105
+ /**
106
+ * Compare this outbound body to the last one for the conversation, then
107
+ * remember the current snapshot. Returns null when there is nothing cacheable.
108
+ *
109
+ * Snapshots are keyed per stage and per provider/model: a routed fallback to a
110
+ * different destination is a legitimate cache miss, not prefix corruption, and
111
+ * must not be reported as one.
112
+ */
113
+ export declare function rememberAndDiffOutboundCachePrefix(conversationId: string | undefined, body: Record<string, any> | null | undefined, affinity?: CacheAffinityHeaders, options?: CachePrefixDiffOptions): CachePrefixDiff | null;
114
+ /**
115
+ * Attribute a broken wire prefix to the stage that broke it. The client leg is
116
+ * checked first: if Claude Code itself rewrote history there is nothing our
117
+ * transformer chain could have preserved.
118
+ */
119
+ export declare function attributeDivergenceStage(clientDiff: CachePrefixDiff | null | undefined, wireDiff: CachePrefixDiff | null | undefined): CachePrefixStage | "none" | undefined;
120
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional process vitals for the `/health` probe.
3
+ *
4
+ * The API routes are registered inside encapsulated Fastify plugins, which
5
+ * snapshot the instance they inherit from at registration time — a decorator
6
+ * added to the root afterwards is invisible to them. Whoever owns the process
7
+ * (the server package, which builds its health heartbeat after the routes are
8
+ * up) therefore publishes the reporter here instead.
9
+ */
10
+ export type HealthReporter = () => unknown;
11
+ export declare function setHealthReporter(fn: HealthReporter | undefined): void;
12
+ /** Vitals for the current instant, or `undefined` when none are published. */
13
+ export declare function readHealthVitals(): unknown;
@@ -12,22 +12,166 @@ export declare function mapCallId(map: ResponsesCallIdMap, id: unknown, directio
12
12
  * Supports the Responses MVP subset; rejects CCR-unsupported stateful fields.
13
13
  */
14
14
  export declare function responsesRequestToUnified(body: any, callIdMap?: ResponsesCallIdMap, customToolNames?: Set<string>): UnifiedChatRequest;
15
+ export declare function isResponsesReasoningItemId(value: unknown): value is string;
16
+ /**
17
+ * Ciphertext Codex/OpenAI will verify. Item ids and Anthropic/Gemini
18
+ * signatures are not encrypted_content — replaying them 400s with
19
+ * `invalid_encrypted_content`.
20
+ */
21
+ export declare function responsesEncryptedContentFrom(value: unknown): string | undefined;
22
+ export interface UnifiedAssistantThinking {
23
+ content: string;
24
+ signature?: string;
25
+ encrypted_content?: string;
26
+ id?: string;
27
+ }
28
+ /** Anthropic/Gemini thinking.signature — never a Responses item id. */
29
+ export declare function anthropicThinkingSignatureFrom(thinking: {
30
+ signature?: string;
31
+ } | undefined): string | undefined;
32
+ /** Responses `reasoning` item → Unified assistant.thinking. */
33
+ export declare function thinkingFromResponsesReasoningItem(item: any): UnifiedAssistantThinking | undefined;
34
+ /**
35
+ * Record streamed reasoning summary text for one item. Late handlers
36
+ * (`output_item.done`, `response.completed`) consult this map so they can
37
+ * emit ciphertext / id without re-sending content — Unified
38
+ * `delta.thinking.content` is additive in every downstream consumer.
39
+ */
40
+ export declare function recordReasoningSummaryDelta(deliveredContentByItemId: Map<string, string>, itemId: unknown, delta: unknown): void;
41
+ /**
42
+ * Late Responses reasoning handlers exist to rescue `encrypted_content` /
43
+ * item id after summary deltas have already streamed the text. If this
44
+ * item's content was already delivered on the current stream, return
45
+ * replay metadata only. A terminal-only reasoning item (no summary
46
+ * deltas) still delivers content exactly once.
47
+ */
48
+ export declare function thinkingForLateReasoningItem(item: any, deliveredContentByItemId: Map<string, string>): UnifiedAssistantThinking | undefined;
49
+ /**
50
+ * Inverse of the inbound `text.format` → Chat Completions `response_format`
51
+ * mapping in `responsesRequestToUnified`. Shared by Codex and generic
52
+ * Responses outbound so the two reconstruct sites cannot drift.
53
+ */
54
+ export declare function responsesTextFormatFromResponseFormat(responseFormat: any): {
55
+ type: string;
56
+ name?: string;
57
+ schema?: any;
58
+ strict?: boolean;
59
+ } | undefined;
60
+ /** Unified assistant.thinking → Responses `reasoning` input/output item. */
61
+ export declare function thinkingFromUnifiedAssistant(message: any): UnifiedAssistantThinking | undefined;
62
+ /**
63
+ * Fixed assistant-turn order for every protocol:
64
+ * thinking → text → images → tool calls.
65
+ * Reordering here keeps cache prefixes stable across Anthropic, Chat,
66
+ * Responses, Gemini, and Mistral.
67
+ */
68
+ export interface CanonicalAssistantTurn {
69
+ thinking?: UnifiedAssistantThinking;
70
+ texts: Array<{
71
+ text: string;
72
+ cache_control?: any;
73
+ }>;
74
+ images: any[];
75
+ toolCalls: any[];
76
+ }
77
+ export declare function canonicalAssistantTurn(message: any): CanonicalAssistantTurn;
78
+ export declare function assistantTurnHasText(turn: CanonicalAssistantTurn): boolean;
79
+ export declare function responsesReasoningItemFromThinking(thinking: {
80
+ content?: string;
81
+ signature?: string;
82
+ encrypted_content?: string;
83
+ id?: string;
84
+ } | undefined, id?: string): any | null;
15
85
  /** Synthetic argument key used to carry a `custom` tool's freeform text
16
86
  * through the Unified/Chat Completions function-call shape. */
17
87
  export declare const CUSTOM_TOOL_INPUT_KEY = "input";
88
+ /**
89
+ * Codex's apply_patch/apply_update grammar accepts only the exact markers
90
+ * `*** Begin Patch` and `*** End Patch` (no trailing asterisk triplet) as the
91
+ * first/last patch lines — anything else is rejected with "The first line of
92
+ * the patch must be '*** Begin Patch'". Models not trained on that grammar
93
+ * (e.g. Grok reaches for Claude's trailing-asterisk variant) emit
94
+ * `*** Begin Patch ***` / `*** End Patch ***`. Normalize those to the Codex
95
+ * form on the client-facing emission, scoped by the marker token itself so a
96
+ * rewrite can only ever hit an apply_patch-style payload regardless of the
97
+ * declared tool name.
98
+ */
99
+ export declare function normalizeCodexPatchMarkers(text: string): string;
100
+ /**
101
+ * Codex's `exec` tool runs raw JavaScript in a V8 isolate; a model that was
102
+ * never trained on that convention (Grok) sometimes fills the freeform input
103
+ * with a JSON shell-envelope instead — e.g. `{"cmd": "ls -la /tmp"}`. That is
104
+ * not valid JS (`{"cmd":…}` parses as a block with a string label →
105
+ * `SyntaxError: Unexpected token ':'`), so the shell call fails and the model
106
+ * wastes a turn retrying as `await tools.exec_command({cmd: …})`.
107
+ * Rewrite the envelope into exactly that retry shape on the client-facing
108
+ * emission. Scoped by the tool name and the whole-input-is-a-shell-object
109
+ * shape so a freeform tool that legitimately takes a JSON blob is never
110
+ * touched. Grok also alternates the key between `cmd` and `command`, and
111
+ * `exec_command` only accepts `cmd`, so normalize either to `cmd`.
112
+ */
113
+ export declare function normalizeExecCommandEnvelope(name: string | undefined, text: string): string;
114
+ /**
115
+ * A model that reaches for the patch tool through a shell hereditary habit
116
+ * wraps the patch in a heredoc instead of JS:
117
+ * apply_patch << 'PATCH'\n*** Begin Patch … *** End Patch\nPATCH
118
+ * That is a shell command, not the raw JS `exec` runs, so it fails with a
119
+ * syntax error and the model wastes a turn retrying as
120
+ * `await tools.apply_patch(…)`. Normalize the heredoc invocation into the JS
121
+ * call form. Whole-value heredoc match only; anything else passes through.
122
+ */
123
+ export declare function normalizeExecApplyPatchHeredoc(name: string | undefined, text: string): string;
124
+ /**
125
+ * `exec` may cross the wire as a plain function tool (arguments stay the JSON
126
+ * wrapper `{"input":"…"}`) rather than a custom tool (whose freeform input was
127
+ * unwrapped already). The envelope/heredoc normalizers above operate on the
128
+ * unwrapped inner text, so when only the wrapper is available, descend into it
129
+ * and normalize its inner `input` value with the same rules.
130
+ */
131
+ export declare function normalizeExecFunctionArguments(name: string | undefined, rawArguments: string, options?: CodexIsolateConventionsOptions): string;
132
+ /** Undo the CUSTOM_TOOL_INPUT_KEY wrapping applied in normalizeResponsesTools.
133
+ * Falls back to the raw text so a malformed/empty call still round-trips
134
+ * instead of vanishing. Also strips a whole-value shell heredoc wrapper
135
+ * (see stripHeredocWrapper). That strip is intentional on both paths this
136
+ * helper serves: client emission (so the Responses client sees clean
137
+ * freeform text) and CodexTransformer history replay (so the backend is
138
+ * not re-fed a wrapper the client never kept). Replay is therefore
139
+ * normalized, not byte-faithful to the model's original heredoc. */
140
+ export declare function unwrapCustomToolInput(rawArguments: string): string;
141
+ /**
142
+ * Codex V8 isolate calling conventions (`await tools.exec_command` /
143
+ * `await tools.apply_patch`). Default on: CCR's `/v1/responses` inbound
144
+ * is the Codex CLI path, and Grok-via-Chat recovery depends on it.
145
+ * Pass `false` for a generic Responses destination whose `exec` tool
146
+ * legitimately accepts `{"cmd": …}` JSON.
147
+ */
148
+ export interface CodexIsolateConventionsOptions {
149
+ codexIsolateConventions?: boolean;
150
+ }
151
+ /** Client-facing custom-tool input: unwrap the Unified JSON wrapper, then
152
+ * apply the exec/patch normalizers in one place so stream finalize, the
153
+ * completed skeleton, and the non-stream JSON path cannot drift. */
154
+ export declare function normalizeClientCustomToolInput(name: string | undefined, rawArguments: string, options?: CodexIsolateConventionsOptions): string;
18
155
  /** Unified Chat JSON → Responses API non-stream response. */
19
156
  export declare function unifiedResponseToResponses(chat: any, options?: {
20
157
  originalModel?: string;
21
158
  callIdMap?: ResponsesCallIdMap;
22
159
  customToolNames?: Set<string>;
160
+ codexIsolateConventions?: boolean;
23
161
  }): any;
24
162
  export interface ResponsesStreamState {
25
163
  responseId: string;
26
164
  model?: string;
27
165
  textItemId: string;
28
166
  textStarted: boolean;
167
+ textClosed: boolean;
29
168
  textOutputIndex?: number;
30
169
  textContent: string;
170
+ closedTextItems: Array<{
171
+ id: string;
172
+ outputIndex: number;
173
+ content: string;
174
+ }>;
31
175
  toolCalls: Map<number, {
32
176
  id: string;
33
177
  name: string;
@@ -45,11 +189,16 @@ export interface ResponsesStreamState {
45
189
  usage?: any;
46
190
  callIdMap: ResponsesCallIdMap;
47
191
  customToolNames: Set<string>;
192
+ thinkingContent: string;
193
+ thinkingEncryptedContent?: string;
194
+ thinkingId?: string;
195
+ codexIsolateConventions: boolean;
48
196
  }
49
197
  export declare function createResponsesStreamState(options?: {
50
198
  model?: string;
51
199
  callIdMap?: ResponsesCallIdMap;
52
200
  customToolNames?: Set<string>;
201
+ codexIsolateConventions?: boolean;
53
202
  }): ResponsesStreamState;
54
203
  /**
55
204
  * Convert one Unified Chat Completions chunk into zero or more Responses
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Client-facing SSE keepalive.
3
+ *
4
+ * Claude Code shows "Waiting for API response · will retry in … · check your
5
+ * network" when no bytes arrive on the response stream for 20s (advisor: 90s),
6
+ * even before any retry has started — see Claude Code error docs. Anthropic's
7
+ * own `ping` events often arrive only every ~25–30s during long thinking /
8
+ * tool-argument streams, so a transparent proxy that only forwards upstream
9
+ * bytes trips that spinner on every slow Opus turn.
10
+ *
11
+ * Inject SSE comment frames (`: …\n\n`) after `idleMs` of silence. Comments are
12
+ * transport keepalives: EventSource / Anthropic SSE parsers ignore them, but
13
+ * they reset Claude Code's byte-idle timer. Default 10s is half the 20s warning.
14
+ */
15
+ export type SSEClientKeepaliveOptions = {
16
+ /** Silence before emitting a comment frame. Default 10_000. */
17
+ idleMs?: number;
18
+ };
19
+ export declare function withSSEClientKeepalive(body: ReadableStream<Uint8Array>, options?: SSEClientKeepaliveOptions): ReadableStream<Uint8Array>;
@@ -1,3 +1,4 @@
1
1
  export { SSEParserTransform } from './SSEParser.transform';
2
2
  export { SSESerializerTransform } from './SSESerializer.transform';
3
3
  export { rewriteStream } from './rewriteStream';
4
+ export { withSSEClientKeepalive, type SSEClientKeepaliveOptions, } from './client-keepalive';
@@ -0,0 +1,62 @@
1
+ import { type CacheAffinityHeaders, type CachePrefixDiff, type CachePrefixStage } from "./cache-prefix-debug";
2
+ export type UpstreamSSEDebugOptions = {
3
+ logger?: any;
4
+ reqId?: string | number;
5
+ provider?: string;
6
+ /** Routed model — snapshots are keyed per destination. */
7
+ model?: string;
8
+ /** Conversation / Claude session id used to pair consecutive cache snapshots. */
9
+ conversationId?: string;
10
+ /** Pipeline position this body was captured at. Defaults to `wire`. */
11
+ stage?: CachePrefixStage;
12
+ /** Codex (and similar) routing headers that pin prompt-cache affinity. */
13
+ cacheAffinity?: CacheAffinityHeaders;
14
+ /** Upstream status. Non-2xx bodies are diffed but never become the baseline. */
15
+ responseStatus?: number;
16
+ /** Client-leg diff, used to attribute a broken wire prefix to a stage. */
17
+ clientStageDiff?: CachePrefixDiff | null;
18
+ /** Outbound diff for this request, joined with the observed cache usage. */
19
+ cacheDiff?: CachePrefixDiff | null;
20
+ /** Cap for a single logged payload string (raw `data` field). */
21
+ maxBytes?: number;
22
+ };
23
+ /**
24
+ * Byte-preserving upstream response debug tap.
25
+ *
26
+ * For SSE: mirrors bytes to a background consumer that emits Codex-parity
27
+ * `recieved data` / `Original Response` logs (including Anthropic usage /
28
+ * cache fields on message_start / message_delta).
29
+ *
30
+ * Important: do **not** use `ReadableStream.tee()` here. Tee couples
31
+ * backpressure across both branches — a slow debug logger (pino file I/O on
32
+ * every thinking delta) stalls the client branch. Claude Code then idles long
33
+ * enough to surface "Waiting for API response · check your network" and retry
34
+ * while CCR eventually still finishes the upstream as HTTP 200.
35
+ *
36
+ * Instead, a TransformStream forwards each chunk to the client immediately and
37
+ * copies into a debug tunnel whose writable side has an infinite high-water
38
+ * mark, so debug I/O can never delay the client. The debug consumer drains the
39
+ * buffered copy at its own pace (memory-bound to the stream size).
40
+ *
41
+ * Exact-wire passthrough never enters transformer stream loggers; this tap is
42
+ * the single shared place that covers every outbound provider.
43
+ */
44
+ export declare function tapUpstreamSSEDebug(response: Response, opts: UpstreamSSEDebugOptions): Promise<Response>;
45
+ export type CacheStructureSummary = {
46
+ systemBreakpoints: number;
47
+ messageBreakpoints: number;
48
+ toolBreakpoints: number;
49
+ prompt_cache_key?: string;
50
+ lastAssistantBlockOrder?: string[];
51
+ };
52
+ /**
53
+ * Summarize outbound Anthropic/OpenAI cache-oriented request structure for
54
+ * debug verification (breakpoints / prompt_cache_key / assistant block order).
55
+ */
56
+ export declare function summarizeOutboundCacheStructure(body: Record<string, any> | null | undefined): CacheStructureSummary | null;
57
+ /**
58
+ * Snapshot the outbound body, diff it against the previous turn, and log both.
59
+ * Returns the diff so the caller can join it with the observed cache usage
60
+ * once upstream responds.
61
+ */
62
+ export declare function logOutboundCacheStructure(body: Record<string, any> | null | undefined, opts: UpstreamSSEDebugOptions): CachePrefixDiff | null;
@@ -0,0 +1,37 @@
1
+ declare const OAUTH_CONFIG: {
2
+ client_id: string;
3
+ device_authorization_endpoint: string;
4
+ token_endpoint: string;
5
+ scope: string;
6
+ };
7
+ export interface XaiTokens {
8
+ access_token: string;
9
+ refresh_token?: string;
10
+ id_token?: string;
11
+ token_type: string;
12
+ scope?: string;
13
+ expires_at?: number;
14
+ last_refresh?: number;
15
+ }
16
+ export interface DeviceCodeResponse {
17
+ device_code: string;
18
+ user_code: string;
19
+ verification_uri: string;
20
+ verification_uri_complete?: string;
21
+ expires_in?: number;
22
+ interval?: number;
23
+ }
24
+ declare function getAuthFilePath(): string;
25
+ export declare function loadTokens(): XaiTokens | null;
26
+ export declare function saveTokens(tokens: XaiTokens): void;
27
+ export declare function isTokenExpiring(tokens: XaiTokens, skewSeconds?: number): boolean;
28
+ export declare function requestDeviceCode(): Promise<DeviceCodeResponse>;
29
+ export declare function pollDeviceCodeToken(device: DeviceCodeResponse, options?: {
30
+ sleep?: (ms: number) => Promise<void>;
31
+ now?: () => number;
32
+ }): Promise<XaiTokens>;
33
+ export declare function refreshTokens(_refreshToken?: string): Promise<XaiTokens>;
34
+ export declare function getValidAccessToken(options?: {
35
+ force?: boolean;
36
+ }): Promise<XaiTokens>;
37
+ export { getAuthFilePath, OAUTH_CONFIG };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caeliq/llms",
3
- "version": "1.0.60",
3
+ "version": "1.0.61",
4
4
  "description": "A universal LLM API transformation server",
5
5
  "main": "dist/cjs/server.cjs",
6
6
  "module": "dist/esm/server.mjs",
@@ -30,29 +30,29 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@anthropic-ai/sdk": "^0.115.0",
33
- "@caeliq/ccr-shared": "^2.1.2",
34
- "@cursor/sdk": "^1.0.26",
33
+ "@caeliq/ccr-shared": "^2.1.3",
34
+ "@cursor/sdk": "^1.0.28",
35
35
  "@fastify/cors": "^11.3.0",
36
- "@fastify/rate-limit": "^10.3.0",
37
- "@google/genai": "^2.15.0",
36
+ "@fastify/rate-limit": "^11.2.0",
37
+ "@google/genai": "^2.17.1",
38
38
  "@huggingface/tokenizers": "^0.1.3",
39
39
  "dotenv": "^17.4.2",
40
- "fastify": "^5.11.2",
40
+ "fastify": "^5.12.0",
41
41
  "fastify-plugin": "^6.0.0",
42
- "google-auth-library": "^11.0.0",
42
+ "google-auth-library": "^11.0.2",
43
43
  "json5": "^2.2.3",
44
44
  "jsonrepair": "^3.15.0",
45
45
  "latex-to-unicode": "^0.1.0",
46
46
  "lru-cache": "^11.5.2",
47
- "openai": "^7.2.0",
47
+ "openai": "^7.4.0",
48
48
  "tiktoken": "^1.0.22",
49
- "undici": "^8.9.0",
49
+ "undici": "^8.10.0",
50
50
  "uuid": "^14.0.1"
51
51
  },
52
52
  "devDependencies": {
53
- "@types/node": "^26.1.2",
54
- "esbuild": "^0.28.1",
55
- "tsx": "^4.23.1",
53
+ "@types/node": "^26.2.0",
54
+ "esbuild": "^0.28.2",
55
+ "tsx": "^4.23.12",
56
56
  "typescript": "^6.0.3"
57
57
  },
58
58
  "engines": {