inference-relay 1.6.13 → 1.6.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts DELETED
@@ -1,953 +0,0 @@
1
- import { C as CreateParams, R as RelayResult, I as InferenceRelayConfig, P as ProbeResult, a as CostEstimate, b as ProviderHealth, c as RateLimitEvent, E as EngineEvent, S as SecurityState$1, A as AuditEvent, d as RoutingConfig } from './index-yGhLy31i.cjs';
2
- export { e as ConcurrencyConfig, f as ContentBlock, M as Message, g as ProviderConfig, h as RelayCapExceededError, i as SecureEnclaveConfig, j as SecureEnclaveCredentials, k as SegmentUsage } from './index-yGhLy31i.cjs';
3
- import { RelayStream } from './stream/index.cjs';
4
- export { createRelayStream } from './stream/index.cjs';
5
- import { EventEmitter } from 'node:events';
6
-
7
- /**
8
- * The relay's version and what its roads keep, for its caller (1.6.10, 9 Nov 2026).
9
- *
10
- * A caller that streams (a desktop app, for one) has to know, BEFORE it chooses the streamed road, whether the relay it
11
- * runs keeps the whole request there. Published 1.6.8's streamed road sent the model only the system prompt and the last message, and
12
- * returned tool names prefixed mcp__r__; 1.6.10's streamed road runs the queue route's own round (claude-cli streamRaw over executeImpl),
13
- * so every message, every tool_use and tool_result block, and the bare tool names survive.
14
- *
15
- * Read it with no call and no network:
16
- * import { RELAY_VERSION, RELAY_FEATURES, relayHello } from 'inference-relay';
17
- * RELAY_FEATURES.streamKeepsHistory === true // the one test a caller needs; undefined on 1.6.8 and earlier (no such export)
18
- * relayHello() // { name: 'inference-relay', version: '1.6.10', features: { streamKeepsHistory: true, ... } }
19
- * or on an instance: relay.version, relay.features, relay.hello() (and InferenceRelay.version).
20
- *
21
- * A caller that may run an OLDER relay (1.6.8 and earlier have none of these exports) must not import them by name: a static
22
- * `import { RELAY_FEATURES }` of a missing export is a hard error in ESM and in strict bundlers. Read them off the module instead, with
23
- * optional access, so an old relay reads as "does not keep history":
24
- * const ir = await import('inference-relay'); // or: import * as ir from 'inference-relay'
25
- * const keepsHistory = ir.RELAY_FEATURES?.streamKeepsHistory === true;
26
- *
27
- * The flags describe the claude-cli streamed road of the gateway bundle published with this library version: the library fetches the
28
- * bundle by its own version (gateway/lazy-provider.ts, LIBRARY_VERSION), so a 1.6.10 library always runs the 1.6.10 bundle.
29
- */
30
- declare const RELAY_VERSION: string;
31
- interface RelayFeatures {
32
- /** The streamed road sends the model every message of the request (earlier turns, a tool round's call and result), as the queue road does. */
33
- readonly streamKeepsHistory: boolean;
34
- /** tool_use and tool_result content blocks in the request survive on the streamed road. */
35
- readonly streamToolBlocks: boolean;
36
- /** Tool names on the streamed road come back as the caller declared them (no mcp__r__ prefix, no CLI caller field). */
37
- readonly streamBareToolNames: boolean;
38
- /** 1.6.12, the claude-cli provider only (kimi-cli unchanged): a caller that goes (its signal aborts, or a stream's reader stops) stops the CLI run within seconds on both roads, a Stop given
39
- * before the call starts no run, and an aborted call fails as an AbortError, never a cut answer handed back as finished. The signal is read
40
- * from create(params, { signal }) and also from create({ ...params, signal }). Undefined on 1.6.11 and earlier. */
41
- readonly stopsWhenCallerGoes: boolean;
42
- /** 1.6.13, the claude-cli provider only (kimi-cli unchanged): a call with max_tokens ends at the FIRST output cap, with the text written so
43
- * far and stop_reason "max_tokens", on both roads. The CLI is stopped there, before its own "Resume directly" continuations (which ran
44
- * a capped call to about 4 x max_tokens, ending end_turn when the model finished inside one). Undefined on 1.6.12 and earlier. */
45
- readonly stopsAtFirstCap: boolean;
46
- }
47
- declare const RELAY_FEATURES: RelayFeatures;
48
- interface RelayHello {
49
- readonly name: 'inference-relay';
50
- readonly version: string;
51
- readonly features: RelayFeatures;
52
- }
53
- /** One object a caller can log, send to its server, or put on a response header (JSON). */
54
- declare function relayHello(): RelayHello;
55
-
56
- interface LicenseValidation {
57
- valid: boolean;
58
- tier?: string;
59
- features?: string[];
60
- expiresAt?: number | null;
61
- /**
62
- * Effective cap: base cap plus any in-cycle top-up grants. Kept as the
63
- * `usageCap` field for backward compat — the library has always compared
64
- * against this.
65
- */
66
- usageCap?: number | null;
67
- /** Raw base cap (before top-ups). Optional for back-compat. */
68
- baseCap?: number | null;
69
- /** Sum of active top-up grants for the current cycle. */
70
- topUpGrants?: number;
71
- /**
72
- * Bumped by the backend on every top-up purchase. Library uses this to
73
- * short-circuit the normal validation cache: if the cached entry has an
74
- * older `lastCapChange` than the server, force a re-validate immediately.
75
- * Enables sub-30s recovery after a top-up.
76
- */
77
- lastCapChange?: number;
78
- usageThisMonth?: number;
79
- capExceeded?: boolean;
80
- error?: string;
81
- }
82
- /**
83
- * Validate a license key against the inference-relay backend.
84
- *
85
- * SECURITY: This is the SOLE authoritative implementation of license verification
86
- * in the entire monorepo. The Pro package re-exports this function rather than
87
- * reimplementing it. Do NOT copy this logic into other packages — single source
88
- * of truth for the asymmetric (RS256) trust chain.
89
- *
90
- * - Verifies JWS signatures from the Protocol Authority
91
- * - Rejects unsigned valid:true responses (MITM protection — fail-closed)
92
- * - Caches positive results for 24h, negative for 60s
93
- * - Falls back to cached result on network failure within 7-day staleness window
94
- */
95
- declare function validateLicense(key: string, endpoint?: string): Promise<LicenseValidation>;
96
- declare class InferenceRelay {
97
- /** This library's version (package.json at build time), e.g. '1.6.10'. Also exported as RELAY_VERSION. */
98
- static readonly version: string;
99
- /** This library's version, for a caller holding an instance. */
100
- get version(): string;
101
- /** What this relay's roads keep (src/version.ts). A caller lifts a one-message streaming guard only when streamKeepsHistory is true. */
102
- get features(): RelayFeatures;
103
- /** { name, version, features }: one object a caller can log or send on (no call, no network). */
104
- hello(): RelayHello;
105
- private providers;
106
- private config;
107
- private fallbackEngine;
108
- private telemetryReporter;
109
- private routingFn;
110
- private license;
111
- private licensePromise;
112
- private licenseValidatedAt;
113
- private callsSinceValidation;
114
- private manifestDecryptor;
115
- private capProbeTimer;
116
- private capProbeTicksRemaining;
117
- messages: {
118
- create(params: CreateParams & {
119
- stream?: false;
120
- }, options?: {
121
- signal?: AbortSignal;
122
- }): Promise<RelayResult>;
123
- create(params: CreateParams & {
124
- stream: true;
125
- }, options?: {
126
- signal?: AbortSignal;
127
- }): Promise<RelayStream>;
128
- create(params: CreateParams, options?: {
129
- signal?: AbortSignal;
130
- }): Promise<RelayResult | RelayStream>;
131
- };
132
- constructor(config: InferenceRelayConfig);
133
- /**
134
- * Start the High-Frequency Probe: background poll of /v1/validate every
135
- * 15 seconds for up to 5 minutes. Exits early when `lastCapChange` differs
136
- * from the cached value — meaning a top-up purchase has landed — at which
137
- * point the normal validation cache takes over and the next inference call
138
- * succeeds.
139
- *
140
- * Idempotent: a second call while a probe is already running is a no-op.
141
- * Properly cleaned up in dispose() so there are no timer leaks.
142
- */
143
- private startCapProbe;
144
- /**
145
- * Single validation call used by the cap probe loop. Extracted as a
146
- * method so tests can override it without going through the RS256 JWS
147
- * verification path. Production behavior delegates to the module-level
148
- * validateLicense() function, which is the sole authoritative source of
149
- * truth for the asymmetric trust chain.
150
- */
151
- private probeValidate;
152
- private stopCapProbe;
153
- static autoDetect(config?: Partial<InferenceRelayConfig>): Promise<InferenceRelay>;
154
- /**
155
- * Pre-flight: trigger the gateway bundle fetch + load now, so subsequent
156
- * `messages.create` / `stream` calls don't pay the cold-start latency
157
- * (and don't surface a gateway outage as a confusing chained provider
158
- * error). Throws `GatewayUnavailableError` on sustained outage so the
159
- * caller can early-skip a test cluster cleanly.
160
- *
161
- * No-op when the cascade has no claude-cli provider (API-only deployment).
162
- * On failure, the lazy provider's `loadingPromise` is reset so the next
163
- * call retries from scratch — `warmup()` doesn't poison subsequent calls.
164
- *
165
- * Added in 1.6.2.
166
- */
167
- warmup(): Promise<void>;
168
- /**
169
- * Clear all provider-health state on this client's fallback engine.
170
- *
171
- * Use between bench tasks, between user sessions, or after handling a
172
- * known-transient upstream incident (e.g., a subscription auth flap
173
- * that's been resolved). Equivalent to calling `markHealthy` for every
174
- * provider type — every provider's next call gets a fresh attempt.
175
- *
176
- * Added in 1.6.4. Replaces the deep-import workaround
177
- * `client.fallbackEngine.resetHealth()` (which relied on TypeScript
178
- * `private` being compile-time-only).
179
- */
180
- resetHealth(): void;
181
- /**
182
- * Clear the unhealthy mark for a single provider type. Useful when you
183
- * want to reset just (e.g.) `claude-cli` after a transient auth flap
184
- * without disturbing other providers' degraded/rate-limited state.
185
- *
186
- * Provider type strings: `claude-cli`, `anthropic-api`, `openai-api`,
187
- * `ollama`, plus any custom-provider type names you've registered.
188
- *
189
- * Added in 1.6.4.
190
- */
191
- markHealthy(providerType: string): void;
192
- dispose(): void;
193
- private resolveProviders;
194
- private executeMessage;
195
- private streamMessage;
196
- private createProvider;
197
- }
198
-
199
- declare abstract class BaseProvider {
200
- abstract readonly type: string;
201
- abstract readonly supportsStreaming: boolean;
202
- abstract readonly supportsMultimodal: boolean;
203
- abstract readonly platformRequirement: 'desktop' | 'web' | 'any';
204
- priority: number;
205
- abstract probe(): Promise<ProbeResult>;
206
- abstract execute(params: CreateParams, options?: {
207
- signal?: AbortSignal;
208
- }): Promise<RelayResult>;
209
- /**
210
- * Lower-level streaming entrypoint. Returns the raw provider event iterator
211
- * with NO RelayStream wrapping. The FallbackEngine consumes streamRaw()
212
- * directly so it can run mid-stream failover, accumulate state, and wrap
213
- * the final output in createRelayStream exactly once.
214
- *
215
- * Returns a Promise so eager setup (auth, spawn, fetch) happens BEFORE the
216
- * iterator is returned. This matches the previous stream() semantics — by
217
- * the time streamRaw() resolves, any side effects (subprocess spawn, HTTP
218
- * connection) have already started.
219
- *
220
- * Providers MUST override either streamRaw() OR stream() (preferably
221
- * streamRaw — stream() will be removed once all callers migrate).
222
- */
223
- streamRaw(_params: CreateParams, _options?: {
224
- signal?: AbortSignal;
225
- }): Promise<AsyncIterable<any>>;
226
- /**
227
- * Default RelayStream wrapper. Most providers should NOT override this —
228
- * implement streamRaw() instead. Kept as an instance method (not abstract)
229
- * for backward compatibility with callers that hit providers directly.
230
- */
231
- stream(params: CreateParams, options?: {
232
- signal?: AbortSignal;
233
- }): Promise<RelayStream>;
234
- abstract estimateCost(params: CreateParams): CostEstimate;
235
- abstract getHealth(): ProviderHealth;
236
- abstract reportRateLimit(event: RateLimitEvent): void;
237
- }
238
-
239
- interface AnthropicAPIOptions {
240
- apiKey: string;
241
- baseUrl?: string;
242
- /** Set true when running in a browser context */
243
- dangerousDirectBrowserAccess?: boolean;
244
- }
245
- declare class AnthropicAPIProvider extends BaseProvider {
246
- readonly type = "anthropic-api";
247
- readonly supportsStreaming = true;
248
- readonly supportsMultimodal = true;
249
- readonly platformRequirement: "any";
250
- private apiKey;
251
- private baseUrl;
252
- private dangerousDirectBrowserAccess;
253
- private health;
254
- constructor(options: AnthropicAPIOptions);
255
- probe(): Promise<ProbeResult>;
256
- execute(params: CreateParams, options?: {
257
- signal?: AbortSignal;
258
- }): Promise<RelayResult>;
259
- streamRaw(params: CreateParams, options?: {
260
- signal?: AbortSignal;
261
- }): Promise<AsyncIterable<any>>;
262
- estimateCost(params: CreateParams): CostEstimate;
263
- getHealth(): ProviderHealth;
264
- reportRateLimit(event: RateLimitEvent): void;
265
- private buildHeaders;
266
- private buildRequestBody;
267
- private handleErrorResponse;
268
- private recordSuccess;
269
- }
270
-
271
- interface OpenAIAPIOptions {
272
- apiKey: string;
273
- baseUrl?: string;
274
- }
275
- declare class OpenAIAPIProvider extends BaseProvider {
276
- readonly type = "openai-api";
277
- readonly supportsStreaming = true;
278
- readonly supportsMultimodal = true;
279
- readonly platformRequirement: "any";
280
- private apiKey;
281
- private baseUrl;
282
- private health;
283
- constructor(options: OpenAIAPIOptions);
284
- probe(): Promise<ProbeResult>;
285
- execute(params: CreateParams, options?: {
286
- signal?: AbortSignal;
287
- }): Promise<RelayResult>;
288
- streamRaw(params: CreateParams, options?: {
289
- signal?: AbortSignal;
290
- }): Promise<AsyncIterable<any>>;
291
- estimateCost(params: CreateParams): CostEstimate;
292
- getHealth(): ProviderHealth;
293
- reportRateLimit(event: RateLimitEvent): void;
294
- private buildHeaders;
295
- private buildRequestBody;
296
- private handleErrorResponse;
297
- private recordSuccess;
298
- }
299
-
300
- interface OllamaOptions {
301
- baseUrl?: string;
302
- }
303
- declare class OllamaProvider extends BaseProvider {
304
- readonly type = "ollama";
305
- readonly supportsStreaming = true;
306
- readonly supportsMultimodal = false;
307
- readonly platformRequirement: "desktop";
308
- private baseUrl;
309
- private health;
310
- constructor(options?: OllamaOptions);
311
- probe(): Promise<ProbeResult>;
312
- execute(params: CreateParams, options?: {
313
- signal?: AbortSignal;
314
- }): Promise<RelayResult>;
315
- streamRaw(params: CreateParams, options?: {
316
- signal?: AbortSignal;
317
- }): Promise<AsyncIterable<any>>;
318
- estimateCost(params: CreateParams): CostEstimate;
319
- getHealth(): ProviderHealth;
320
- reportRateLimit(event: RateLimitEvent): void;
321
- private buildRequestBody;
322
- private handleErrorResponse;
323
- private recordSuccess;
324
- }
325
-
326
- /**
327
- * Typed façade over Node's synchronous EventEmitter. Used by FallbackEngine
328
- * to emit lifecycle events that the TelemetryReporter buffers per-call and
329
- * flushes into a coherent AuditEvent at terminal stream_complete.
330
- *
331
- * MUST be synchronous (Node EventEmitter is). Do NOT replace with a custom
332
- * Promise/microtask-queued impl — audit-event ordering depends on synchronous
333
- * listener invocation so that lineage is fully buffered before flushLineageForCall
334
- * runs.
335
- */
336
- declare class EngineEventEmitter extends EventEmitter {
337
- constructor();
338
- emitTyped(event: EngineEvent): boolean;
339
- onTyped<K extends EngineEvent['kind']>(kind: K, listener: (event: Extract<EngineEvent, {
340
- kind: K;
341
- }>) => void): this;
342
- offTyped<K extends EngineEvent['kind']>(kind: K, listener: (event: Extract<EngineEvent, {
343
- kind: K;
344
- }>) => void): this;
345
- }
346
-
347
- interface FallbackOptions {
348
- signal?: AbortSignal;
349
- maxAttempts?: number;
350
- /** Maximum mid-stream failovers per call. Default: 2. */
351
- maxMidStreamFailovers?: number;
352
- /** Security state of the licence chain. SEC_DEGRADED enables jitter. */
353
- licenseState?: SecurityState$1;
354
- /**
355
- * Maximum same-provider retries on `transient` failures (e.g., Bun
356
- * crash on Windows). Default: 2. Set to 0 to disable retries (failure
357
- * immediately cascades to the next provider). Added 1.6.6.
358
- */
359
- maxSameProviderRetries?: number;
360
- /**
361
- * When true, refuses to cascade from `claude-cli` to any paid provider
362
- * (`anthropic-api`, `openai-api`). The cascade halts with
363
- * `subscription_only:cascade_halted` instead of falling through to a
364
- * paid leg. Billing safety net for users with stale ANTHROPIC_API_KEY
365
- * in env. Added 1.6.6.
366
- */
367
- subscriptionOnly?: boolean;
368
- }
369
- interface HealthRecord {
370
- status: ProviderHealth['status'];
371
- lastError?: string;
372
- updatedAt: number;
373
- /**
374
- * TTL override in milliseconds. Optional — when omitted,
375
- * `UNHEALTHY_TTL_DEFAULT_MS` is used. Set by `markUnhealthy` for
376
- * classifications that auto-recover faster than the default (1.6.4
377
- * adds 30s for `auth_failed`).
378
- */
379
- ttlMs?: number;
380
- }
381
- declare class RelayError extends Error {
382
- readonly code = "ALL_PROVIDERS_FAILED";
383
- readonly fallbackChain: string[];
384
- constructor(chain: string[]);
385
- }
386
- /**
387
- * Thrown when the cascade exhausts and every provider failure classified as
388
- * `request_too_large` — i.e., the prompt exceeded the upstream API's input
389
- * token ceiling. Subclass of {@link RelayError} so existing
390
- * `instanceof RelayError` matchers and chain-inspecting code keep working.
391
- *
392
- * Callers should branch on `instanceof RequestTooLargeError` and apply
393
- * domain-specific distillation (drop oldest messages, summarize step
394
- * trajectories, filter screenshots) before retrying. IR cannot distill the
395
- * payload itself — it has no semantic understanding of what content matters.
396
- *
397
- * Detection: {@link classifyError} matches stderr patterns like
398
- * `prompt is too long`, `input length`, `request too large`, and tokens-
399
- * exceed strings. When EVERY provider in the cascade failed with this
400
- * classification, the cascade throws {@link RequestTooLargeError} instead
401
- * of vanilla {@link RelayError}. If even one provider failed for a
402
- * different reason (e.g., auth, gateway), the cascade throws plain
403
- * `RelayError` and the size-related entries appear in `fallbackChain`.
404
- *
405
- * Added in 1.6.3.
406
- */
407
- declare class RequestTooLargeError extends RelayError {
408
- constructor(chain: string[]);
409
- }
410
- declare class FallbackEngine {
411
- /** Tracks provider health across calls. Keyed by provider type string. */
412
- private healthMap;
413
- /**
414
- * Public lifecycle event emitter. The TelemetryReporter subscribes to this
415
- * to buffer per-call lineage and flush a coherent AuditEvent at terminal
416
- * stream_complete. Synchronous semantics — see telemetry/engine-events.ts.
417
- */
418
- readonly engineEvents: EngineEventEmitter;
419
- /**
420
- * Default unhealthy TTL — appropriate for infrastructure outages
421
- * (`gateway_unavailable`, `provider_incompatible`) that typically take
422
- * minutes to resolve. Transient failure classes can override via the
423
- * `ttlMs` arg to {@link markUnhealthy}.
424
- */
425
- static readonly UNHEALTHY_TTL_DEFAULT_MS: number;
426
- /**
427
- * Shorter TTL for `auth_failed` (1.6.4). Token-rotation flaps and
428
- * brief auth-backend hiccups are typically sub-second; 5 minutes was
429
- * too aggressive a blast radius for what's usually transient. After
430
- * 1.6.4's auth-retry in claude-cli provider, anything that survives
431
- * the in-call retry is more likely to be a real auth problem — but
432
- * still recoverable in seconds-to-tens-of-seconds, not minutes.
433
- */
434
- static readonly UNHEALTHY_TTL_AUTH_MS: number;
435
- getHealthStatus(providerType: string): HealthRecord['status'];
436
- markUnhealthy(providerType: string, reason: string, ttlMs?: number): void;
437
- markDegraded(providerType: string, reason: string): void;
438
- markHealthy(providerType: string): void;
439
- resetHealth(): void;
440
- execute(params: CreateParams, providers: BaseProvider[], options?: FallbackOptions): Promise<RelayResult>;
441
- stream(params: CreateParams, providers: BaseProvider[], options?: FallbackOptions): Promise<RelayStream>;
442
- /**
443
- * Find the next provider in priority order after `current` that supports
444
- * streaming AND has not already been tried-and-failed in this call.
445
- *
446
- * IMPORTANT: this method intentionally does NOT consult `getHealthStatus`.
447
- * Type-health is keyed by provider type string, but the documented "two
448
- * API keys for redundancy" pattern uses two instances of the SAME type
449
- * (e.g. primary + backup AnthropicAPIProvider with different keys). If we
450
- * skipped by type-health, the first instance failing would poison the
451
- * second instance because they share the type string. Per-call exclusion
452
- * by INSTANCE preserves global health-tracking semantics for the next
453
- * call's connect-time loop while letting same-type siblings serve as
454
- * legitimate failover targets.
455
- */
456
- private nextHealthyProvider;
457
- /**
458
- * Internal emit shim — keeps the public emit API tight.
459
- */
460
- private emitEngineEvent;
461
- /**
462
- * Emit a stream_complete event on the terminal exhaustion path so the
463
- * reporter flushes its lineage buffer for this callId and we don't leak
464
- * Map entries forever (bug C3).
465
- */
466
- private emitTerminalExhaustion;
467
- }
468
-
469
- /**
470
- * Pre-flight input-token estimation for {@link CreateParams}.
471
- *
472
- * Lets callers gate distillation (drop screenshots, summarize trajectories,
473
- * filter DOM bits) BEFORE incurring the round-trip + reject cost of an
474
- * oversized prompt. The catch-it-after path is `instanceof
475
- * RequestTooLargeError`; this is the catch-it-before path.
476
- *
477
- * **Approximate, not exact.** Anthropic's BPE tokenizer is closed-source;
478
- * we use the standard 4-chars-per-token heuristic plus per-message
479
- * overhead. Within ±15% on typical English text. For budgeting against
480
- * a known ceiling, leave a 20% safety margin (e.g., target 100K when
481
- * the provider ceiling is 120K).
482
- *
483
- * Image content blocks count for `IMAGE_TOKENS_FLAT` each — Anthropic
484
- * bills images at a flat per-image rate that depends on dimensions, but
485
- * the size-vs-tokens function is non-linear. The flat estimate is
486
- * intentionally conservative (overcounts small images, slightly
487
- * undercounts very large ones) so callers planning around it lean safe.
488
- *
489
- * Added in 1.6.3.
490
- */
491
-
492
- /**
493
- * Estimate the input token count for a `messages.create` request.
494
- *
495
- * Counts: system prompt + every message content block (text + JSON-
496
- * serialized structured blocks) + per-message overhead + flat per-image
497
- * cost + request overhead. Excludes the `max_tokens` output reservation —
498
- * callers should add that themselves when budgeting against a context
499
- * window.
500
- *
501
- * @example
502
- * const inputTokens = estimateInputTokens(params);
503
- * if (inputTokens > 100_000) {
504
- * params = distill(params);
505
- * }
506
- * const result = await client.messages.create(params);
507
- *
508
- * @example Budget against a 200K context window:
509
- * const total = estimateInputTokens(params) + params.max_tokens;
510
- * if (total > 180_000) {
511
- * params = distill(params);
512
- * }
513
- */
514
- declare function estimateInputTokens(params: CreateParams): number;
515
-
516
- /**
517
- * Model Registry — Single Source of Truth
518
- *
519
- * v1.4.0: expanded from alias-only to a full registry with capabilities,
520
- * pricing, and provider ownership. The `equivalents` field (cross-provider
521
- * model mapping) is intentionally absent here — it lives in the gateway
522
- * bundle as trade-secret IP.
523
- *
524
- * Public consumers use the helpers at the bottom (findModel, providerForModel,
525
- * modelsForProvider) to enumerate and route. The backwards-compat
526
- * MODEL_ALIASES + normalizeModel() exports are preserved.
527
- */
528
- type ProviderType = 'anthropic-api' | 'openai-api' | 'ollama' | 'claude-cli';
529
- interface ModelEntry {
530
- /** Canonical short name customers use */
531
- canonical: string;
532
- /** Provider-specific dated/full ID (passed to the API) */
533
- apiId: string;
534
- /** Which provider class handles this model */
535
- provider: ProviderType;
536
- /** Capabilities the model supports */
537
- capabilities: {
538
- streaming: boolean;
539
- tools: boolean;
540
- multimodal: boolean;
541
- /** Max context window in tokens */
542
- contextWindow: number;
543
- };
544
- /** Pricing per million tokens [input, output] — undefined for local models */
545
- pricing?: [number, number];
546
- }
547
- declare const MODEL_REGISTRY: ModelEntry[];
548
- declare const MODEL_ALIASES: Record<string, string>;
549
- declare function normalizeModel(model: string): string;
550
- declare function findModel(canonical: string): ModelEntry | undefined;
551
- declare function providerForModel(canonical: string): ProviderType | undefined;
552
- declare function modelsForProvider(provider: ProviderType): ModelEntry[];
553
-
554
- /**
555
- * Detect Claude CLI installation status.
556
- *
557
- * This is the public, lightweight binary-presence check. It runs entirely
558
- * within the public package and only checks (a) is the binary installed
559
- * (b) what version (c) version-drift warning. It does NOT check credential
560
- * status — that requires reading OS keychain stores, which is gateway-only.
561
- *
562
- * The `authenticated` field returned here is `null` to signal "not yet
563
- * checked." The LazyClaudeCLIProvider's probe() method calls this AND
564
- * the gateway-loaded credential resolver to fill in the full picture.
565
- *
566
- * Customers who want the full probe (including authenticated status)
567
- * should use `relay.providers[0].probe()` once the relay is constructed.
568
- */
569
- declare function detectCLI(): Promise<ProbeResult>;
570
-
571
- /**
572
- * Validates an Anthropic API key by making a minimal request.
573
- * 200 -> valid, 401 -> invalid, 429/529 -> valid but rate limited/overloaded.
574
- */
575
- declare function validateAnthropicKey(apiKey: string): Promise<{
576
- valid: boolean;
577
- error?: string;
578
- }>;
579
-
580
- interface CLIManifest {
581
- version: string;
582
- cliVersion: string;
583
- eventTypes: {
584
- system: string;
585
- streamEvent: string;
586
- assistant: string;
587
- result: string;
588
- };
589
- fieldMappings: {
590
- sessionId: string;
591
- model: string;
592
- cliVersion: string;
593
- content: string;
594
- usage: string;
595
- inputTokens: string;
596
- outputTokens: string;
597
- stopReason: string;
598
- isError: string;
599
- totalCostUsd: string;
600
- durationMs: string;
601
- error: string;
602
- event: string;
603
- };
604
- syntheticModel: string;
605
- }
606
- type SecurityState = 'SEC_NOMINAL' | 'SEC_CACHE' | 'SEC_RECOVERY' | 'SEC_DEGRADED';
607
- declare class ManifestDecryptor {
608
- private verificationFailures;
609
- private cachedManifest;
610
- private currentSecurityState;
611
- /**
612
- * Verify a JWS-signed manifest from the backend.
613
- * The server signs with RS256 private key; we verify with the embedded public key.
614
- */
615
- verifyManifest(jwsString: string): Promise<{
616
- manifest: CLIManifest;
617
- securityState: SecurityState;
618
- }>;
619
- getSecurityState(): SecurityState;
620
- clearCache(): void;
621
- }
622
- declare function getLastKnownGoodManifest(): CLIManifest;
623
- /**
624
- * Path-based value accessor for manifest field mappings.
625
- * Zero-dependency alternative to lodash.get().
626
- * Prototype pollution guarded.
627
- */
628
- declare function getValue(obj: any, path: string): any;
629
-
630
- declare class TelemetryReporter {
631
- private endpoint;
632
- private enabled;
633
- private keyId;
634
- /**
635
- * Lineage buffer: per-call accumulation of EngineEvents from FallbackEngine.
636
- * Flushed into the AuditEvent.lineage field at terminal stream_complete.
637
- * Keyed by callId so concurrent calls don't cross-contaminate.
638
- */
639
- private lineageBuffer;
640
- /** Stored unsubscribe handlers so dispose() can detach cleanly. */
641
- private unsubscribers;
642
- constructor(options: {
643
- endpoint?: string;
644
- enabled?: boolean;
645
- keyId?: string;
646
- });
647
- /**
648
- * Subscribe to the FallbackEngine's lifecycle event stream. The reporter
649
- * buffers per-call lineage and flushes a coherent AuditEvent at terminal
650
- * stream_complete. Synchronous emit semantics from EngineEventEmitter
651
- * guarantee that all lineage events for a callId are buffered before
652
- * the stream_complete handler runs.
653
- */
654
- subscribeToEngine(engine: FallbackEngine): void;
655
- /** Detach all engine subscriptions. Called by InferenceRelay.dispose(). */
656
- unsubscribe(): void;
657
- private bufferLineageEvent;
658
- /**
659
- * Construct the final AuditEvent from the stream_complete payload + buffered
660
- * lineage and call report(). Privacy invariant: lineage carries IDs only,
661
- * never content.
662
- */
663
- private flushLineageForCall;
664
- report(event: AuditEvent): void;
665
- }
666
-
667
- type RoutingDecision = 'prefer-user' | 'prefer-app' | string;
668
- type RoutingFunction = (params: CreateParams, estimate: {
669
- estimatedCost: number;
670
- }) => RoutingDecision;
671
- /**
672
- * Apply routing decision to reorder providers.
673
- * 'prefer-user' puts subscription-backed providers first (claude-cli, then user API keys).
674
- * 'prefer-app' puts app-owned providers first (app API key).
675
- * A specific provider name puts that provider first.
676
- */
677
- declare function applyRouting(providers: BaseProvider[], decision: RoutingDecision): BaseProvider[];
678
- /**
679
- * Create a routing function from a RoutingConfig.
680
- */
681
- declare function createRoutingFunction(config: RoutingConfig): RoutingFunction;
682
-
683
- /**
684
- * Estimate the cost of a request before sending it.
685
- * Used by routing to decide which provider handles the call.
686
- */
687
- declare function estimateCost(params: CreateParams): CostEstimate;
688
-
689
- /**
690
- * Get pricing for a model. Falls back to Sonnet pricing for unknown models.
691
- */
692
- declare function getModelPricing(model: string): [number, number];
693
-
694
- /**
695
- * CredentialStore — secure local storage for user API keys.
696
- * Keys never leave the client. Never sent to inference-relay servers.
697
- */
698
- interface CredentialStore {
699
- get(key: string): Promise<string | null>;
700
- set(key: string, value: string): Promise<void>;
701
- delete(key: string): Promise<void>;
702
- has(key: string): Promise<boolean>;
703
- }
704
-
705
- /**
706
- * Electron safeStorage credential store.
707
- *
708
- * Uses Electron's safeStorage.encryptString() / decryptString() to protect
709
- * API keys at rest. Encrypted blobs are persisted as a JSON file in the
710
- * user's data directory (~/.inference-relay/credentials.enc by default).
711
- *
712
- * Falls back to an in-memory Map if safeStorage is unavailable (e.g. when
713
- * running outside Electron or before the app is ready).
714
- */
715
- declare class ElectronCredentialStore implements CredentialStore {
716
- private filePath;
717
- private safeStorage;
718
- private memoryFallback;
719
- constructor(options?: {
720
- storagePath?: string;
721
- });
722
- private readDisk;
723
- private writeDisk;
724
- get(key: string): Promise<string | null>;
725
- set(key: string, value: string): Promise<void>;
726
- delete(key: string): Promise<void>;
727
- has(key: string): Promise<boolean>;
728
- }
729
-
730
- /**
731
- * IndexedDB credential store with AES-GCM encryption via Web Crypto API.
732
- *
733
- * Encryption key is derived from a user-provided passphrase (PBKDF2) or,
734
- * if no passphrase is given, a random key is generated and held in
735
- * sessionStorage for the lifetime of the browser tab. This means
736
- * credentials survive page refreshes within the same session but are
737
- * wiped when the tab closes — a deliberate safety tradeoff.
738
- */
739
- declare class IndexedDBCredentialStore implements CredentialStore {
740
- private dbName;
741
- private storeName;
742
- private cryptoKey;
743
- private passphrase?;
744
- private initPromise;
745
- constructor(options?: {
746
- passphrase?: string;
747
- dbName?: string;
748
- });
749
- private ensureReady;
750
- private init;
751
- private openDB;
752
- private idbGet;
753
- private idbPut;
754
- private idbDelete;
755
- private idbHas;
756
- private getOrCreateSessionSalt;
757
- private deriveKey;
758
- private encrypt;
759
- private decrypt;
760
- get(key: string): Promise<string | null>;
761
- set(key: string, value: string): Promise<void>;
762
- delete(key: string): Promise<void>;
763
- has(key: string): Promise<boolean>;
764
- }
765
-
766
- /**
767
- * In-memory credential store. Volatile — cleared on process exit.
768
- * Suitable for testing, CLI apps, or as a fallback when no persistent
769
- * storage backend is available.
770
- */
771
- declare class MemoryCredentialStore implements CredentialStore {
772
- private store;
773
- get(key: string): Promise<string | null>;
774
- set(key: string, value: string): Promise<void>;
775
- delete(key: string): Promise<void>;
776
- has(key: string): Promise<boolean>;
777
- }
778
-
779
- /**
780
- * GatewayLoader — fetches the signed Native Subscription Gateway bundle
781
- * from the Protocol Authority on first use, verifies it against the
782
- * embedded RSA public key, caches it locally, and loads it into the
783
- * relay process via vm.Script.
784
- *
785
- * The bundle contains the implementation of the gateway providers
786
- * (subscription-bridged providers, stream parsers, credential resolvers).
787
- * It is NOT distributed via npm — it lives in Convex blob storage on the
788
- * Protocol Authority and is downloaded on demand by license holders.
789
- *
790
- * This file is bundled INTO the public npm package (no IP here). It is
791
- * the ONLY thing the public package needs to know about the gateway —
792
- * after `await GatewayLoader.load(licenseKey)`, the loader returns the
793
- * gateway module's exports as a plain object. Callers (specifically
794
- * gateway/lazy-provider.ts) treat that object as opaque.
795
- *
796
- * Threat model:
797
- * - Non-customer cannot fetch the bundle: the Protocol Authority's
798
- * /v1/gateway/fetch endpoint requires a valid license key. Bundles are
799
- * served via short-TTL pre-signed URLs.
800
- * - MITM attacker cannot substitute a malicious bundle: the bundle ships
801
- * with an RS256 signature over `{ version, sha256, purpose: 'gateway-bundle' }`,
802
- * verified against the embedded RSA public key. Cross-payload confusion
803
- * is prevented by the `purpose` field — a manifest signature cannot be
804
- * replayed as a gateway-bundle signature and vice versa.
805
- * - Customer who leaks bundle: contractual problem (covered by EULA).
806
- * Each bundle has a build identifier embedded; leaked copies are
807
- * identifiable.
808
- * - Local cache tampering: bundle SHA-256 is verified against the
809
- * server-asserted SHA-256 before loading. A tampered cache file fails
810
- * verification and triggers a fresh fetch.
811
- * - Gateway endpoint outage: fail closed. The relay cannot serve
812
- * claude-cli requests until the gateway is reachable. The user can
813
- * still configure direct API providers as a fallback.
814
- */
815
- /**
816
- * The shape of the gateway module's exports after loading. The lazy
817
- * proxies use these to instantiate concrete implementations without the
818
- * public package needing to know any internal details.
819
- */
820
- interface GatewayModule {
821
- ClaudeCLIProvider: new () => any;
822
- KimiCLIProvider?: new () => any;
823
- ProcessPool: new (options?: {
824
- maxSize?: number;
825
- idleTimeoutMs?: number;
826
- }) => any;
827
- _lsp?: (model: string, baseUrl: string) => Promise<boolean>;
828
- _lspClear?: () => void;
829
- _lt?: (tools: any[]) => any[];
830
- _lpc?: (toolCalls: any[]) => any[];
831
- _lpsc?: (chunk: any, state: any) => any[];
832
- _lcss?: () => any;
833
- _rfp?: (model: string, targetProvider: string) => string | undefined;
834
- _cfc?: (model: string) => string[];
835
- _ge?: (model: string) => Record<string, string> | undefined;
836
- _saf?: (input: any) => any | null;
837
- }
838
- /**
839
- * Errors thrown by the loader. Designed to be matched against by error
840
- * classification code in fallback/engine.ts and verify.ts so customer-
841
- * facing diagnostics stay structured.
842
- */
843
- declare class GatewayLoaderError extends Error {
844
- readonly code: 'GATEWAY_FETCH_FAILED' | 'LICENSE_REJECTED' | 'NETWORK_TIMEOUT' | 'GATEWAY_VERIFICATION_FAILED' | 'GATEWAY_CACHE_WRITE_FAILED' | 'GATEWAY_LOAD_FAILED';
845
- readonly cause?: unknown | undefined;
846
- constructor(message: string, code: 'GATEWAY_FETCH_FAILED' | 'LICENSE_REJECTED' | 'NETWORK_TIMEOUT' | 'GATEWAY_VERIFICATION_FAILED' | 'GATEWAY_CACHE_WRITE_FAILED' | 'GATEWAY_LOAD_FAILED', cause?: unknown | undefined);
847
- }
848
- /**
849
- * Thrown specifically when /v1/gateway/fetch exhausts the retry budget —
850
- * Protocol Authority unreachable, returning 5xx, or DNS/network errored.
851
- * Branch on `instanceof GatewayUnavailableError` to handle gateway outages
852
- * separately from license/verification errors (which stay as
853
- * `GatewayLoaderError`).
854
- *
855
- * Inherits `code: 'GATEWAY_FETCH_FAILED'` and `name: 'GatewayLoaderError'`
856
- * (parent's name is intentionally preserved) so every existing string-match,
857
- * code-match, and name-match consumer keeps working. The new typed-error
858
- * path is purely additive — switch to `instanceof GatewayUnavailableError`
859
- * at your leisure.
860
- */
861
- declare class GatewayUnavailableError extends GatewayLoaderError {
862
- constructor(message: string, cause?: unknown);
863
- }
864
- /**
865
- * Options for {@link GatewayLoader.load}. Added in 1.6.2 so callers can
866
- * extend the retry budget for sustained Cloudflare/Convex 502s (the default
867
- * `[0, 1000, 4000]` exhausts in ~5 seconds, which is too short for some
868
- * upstream incidents).
869
- */
870
- interface GatewayLoadOptions {
871
- /**
872
- * Per-attempt delay budget in milliseconds. Defaults to
873
- * `[0, 1000, 4000]` (3 attempts over ~5s). Env override:
874
- * `IR_GATEWAY_RETRY_DELAYS_MS=0,5000,15000,60000`.
875
- *
876
- * Explicit option wins over env; env wins over default. Malformed values
877
- * (non-array, non-numeric, negative) log a `debugLog` warning and fall
878
- * back to default — don't throw, so a typo doesn't brick startup.
879
- */
880
- retryDelaysMs?: number[];
881
- }
882
- declare class GatewayLoader {
883
- /**
884
- * Load (or resolve from cache) the gateway module for a given license
885
- * key. On first call, fetches from the Protocol Authority, verifies the
886
- * signature, writes to local cache, and instantiates the module.
887
- *
888
- * @param licenseKey - The customer's license key (`ir_live_…`)
889
- * @param libraryVersion - The library version, used as the cache namespace
890
- * @param baseUrl - Override the gateway endpoint (test fixture only)
891
- * @param options - Optional load-time tuning (e.g., retry budget). Added 1.6.2.
892
- */
893
- static load(licenseKey: string, libraryVersion: string, baseUrl?: string, options?: GatewayLoadOptions): Promise<GatewayModule>;
894
- /**
895
- * Test helper: bypass the network and load from a local file path.
896
- * Used by tests and by the airgapped pre-fetch flow.
897
- */
898
- static loadFromDisk(bundlePath: string): Promise<GatewayModule>;
899
- private static tryLoadFromDisk;
900
- private static fetchFromAuthority;
901
- /**
902
- * Verify the RS256 signature over
903
- * { libraryVersion, bundleVersion, sha256, purpose: 'gateway-bundle' }.
904
- *
905
- * The `purpose` field is mandatory — a payload signed for `manifest`
906
- * cannot be replayed here. The `libraryVersion` field is the
907
- * version-rollback guard: a 1.3.0 bundle's signature cannot be replayed
908
- * to satisfy a 1.4.0 client (the SHA-256 also pins the bytes, but
909
- * comparing libraryVersion catches the case where an attacker somehow
910
- * generates a SHA collision OR substitutes a different bundle whose
911
- * libraryVersion field doesn't match what the client requested).
912
- * The signature itself is a JWS compact form.
913
- */
914
- private static verifySignature;
915
- /**
916
- * Execute the gateway bundle as a CommonJS module.
917
- *
918
- * The bundle is a CJS module — it expects `module.exports = { ClaudeCLIProvider, ... }`
919
- * after evaluation. We use Node's `vm.compileFunction` API, which is
920
- * the same primitive Node's own CJS loader uses internally to evaluate
921
- * every .js file in a Node process. The gateway bundle gets called as
922
- * if it were a normal CJS module: with explicit `exports`, `require`,
923
- * `module`, `__filename`, and `__dirname` parameters.
924
- *
925
- * Why not vm.Script + createContext: that approach captures `require`
926
- * from the parent (loader.ts) module's scope, which under tsup ESM
927
- * compilation becomes a dynamic-require shim that throws inside the
928
- * sandbox. compileFunction passes `require` as an explicit named
929
- * argument, so the gateway bundle's bare `require()` calls resolve to
930
- * the function parameter, never to a captured outer-scope wrapper.
931
- *
932
- * The require we pass is built via createRequire() bound to a path
933
- * inside the local cache directory. Node built-in modules resolve
934
- * via this require regardless of the bound path; the path only
935
- * matters for relative file resolves, which the gateway bundle does
936
- * not perform (it's self-contained).
937
- *
938
- * Trust model: by accepting the EULA, the customer agrees that the
939
- * gateway bundle runs with the privileges of their Node process.
940
- * Removing the vm.Script sandbox does not change this — the prior
941
- * sandbox provided full `require` access anyway, so it was security
942
- * theater. compileFunction is more honest: the gateway runs with the
943
- * same Node API surface as any other CJS file in the host process.
944
- */
945
- private static loadIntoVm;
946
- /**
947
- * Test helper: clear the in-process module cache. Tests use this between
948
- * cases to force a fresh load.
949
- */
950
- static clearCache(): void;
951
- }
952
-
953
- export { AnthropicAPIProvider, AuditEvent, BaseProvider, type CLIManifest, CostEstimate, CreateParams, type CredentialStore, ElectronCredentialStore, EngineEvent, EngineEventEmitter, FallbackEngine, GatewayLoader, GatewayLoaderError, type GatewayModule, GatewayUnavailableError, IndexedDBCredentialStore, InferenceRelay, InferenceRelayConfig, type LicenseValidation, MODEL_ALIASES, MODEL_REGISTRY, ManifestDecryptor, MemoryCredentialStore, type ModelEntry, OllamaProvider, OpenAIAPIProvider, ProbeResult, ProviderHealth, type ProviderType, RELAY_FEATURES, RELAY_VERSION, RateLimitEvent, RelayError, type RelayFeatures, type RelayHello, RelayResult, RelayStream, RequestTooLargeError, RoutingConfig, type RoutingDecision, type RoutingFunction, SecurityState$1 as SecurityState, TelemetryReporter, applyRouting, createRoutingFunction, detectCLI, estimateCost, estimateInputTokens, findModel, getLastKnownGoodManifest, getModelPricing, getValue, modelsForProvider, normalizeModel, providerForModel, relayHello, validateAnthropicKey, validateLicense };