inference-relay 1.4.0

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.
@@ -0,0 +1,716 @@
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-B20BqToz.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-B20BqToz.cjs';
3
+ import { RelayStream } from './stream/index.cjs';
4
+ export { createRelayStream } from './stream/index.cjs';
5
+ import { EventEmitter } from 'node:events';
6
+
7
+ interface LicenseValidation {
8
+ valid: boolean;
9
+ tier?: string;
10
+ features?: string[];
11
+ expiresAt?: number | null;
12
+ /**
13
+ * Effective cap: base cap plus any in-cycle top-up grants. Kept as the
14
+ * `usageCap` field for backward compat — the library has always compared
15
+ * against this.
16
+ */
17
+ usageCap?: number | null;
18
+ /** Raw base cap (before top-ups). Optional for back-compat. */
19
+ baseCap?: number | null;
20
+ /** Sum of active top-up grants for the current cycle. */
21
+ topUpGrants?: number;
22
+ /**
23
+ * Bumped by the backend on every top-up purchase. Library uses this to
24
+ * short-circuit the normal validation cache: if the cached entry has an
25
+ * older `lastCapChange` than the server, force a re-validate immediately.
26
+ * Enables sub-30s recovery after a top-up.
27
+ */
28
+ lastCapChange?: number;
29
+ usageThisMonth?: number;
30
+ capExceeded?: boolean;
31
+ error?: string;
32
+ }
33
+ /**
34
+ * Validate a license key against the inference-relay backend.
35
+ *
36
+ * SECURITY: This is the SOLE authoritative implementation of license verification
37
+ * in the entire monorepo. The Pro package re-exports this function rather than
38
+ * reimplementing it. Do NOT copy this logic into other packages — single source
39
+ * of truth for the asymmetric (RS256) trust chain.
40
+ *
41
+ * - Verifies JWS signatures from the Protocol Authority
42
+ * - Rejects unsigned valid:true responses (MITM protection — fail-closed)
43
+ * - Caches positive results for 24h, negative for 60s
44
+ * - Falls back to cached result on network failure within 7-day staleness window
45
+ */
46
+ declare function validateLicense(key: string, endpoint?: string): Promise<LicenseValidation>;
47
+ declare class InferenceRelay {
48
+ private providers;
49
+ private config;
50
+ private fallbackEngine;
51
+ private telemetryReporter;
52
+ private routingFn;
53
+ private license;
54
+ private licensePromise;
55
+ private licenseValidatedAt;
56
+ private callsSinceValidation;
57
+ private manifestDecryptor;
58
+ private capProbeTimer;
59
+ private capProbeTicksRemaining;
60
+ messages: {
61
+ create(params: CreateParams & {
62
+ stream?: false;
63
+ }, options?: {
64
+ signal?: AbortSignal;
65
+ }): Promise<RelayResult>;
66
+ create(params: CreateParams & {
67
+ stream: true;
68
+ }, options?: {
69
+ signal?: AbortSignal;
70
+ }): Promise<RelayStream>;
71
+ create(params: CreateParams, options?: {
72
+ signal?: AbortSignal;
73
+ }): Promise<RelayResult | RelayStream>;
74
+ };
75
+ constructor(config: InferenceRelayConfig);
76
+ /**
77
+ * Start the High-Frequency Probe: background poll of /v1/validate every
78
+ * 15 seconds for up to 5 minutes. Exits early when `lastCapChange` differs
79
+ * from the cached value — meaning a top-up purchase has landed — at which
80
+ * point the normal validation cache takes over and the next inference call
81
+ * succeeds.
82
+ *
83
+ * Idempotent: a second call while a probe is already running is a no-op.
84
+ * Properly cleaned up in dispose() so there are no timer leaks.
85
+ */
86
+ private startCapProbe;
87
+ /**
88
+ * Single validation call used by the cap probe loop. Extracted as a
89
+ * method so tests can override it without going through the RS256 JWS
90
+ * verification path. Production behavior delegates to the module-level
91
+ * validateLicense() function, which is the sole authoritative source of
92
+ * truth for the asymmetric trust chain.
93
+ */
94
+ private probeValidate;
95
+ private stopCapProbe;
96
+ static autoDetect(config?: Partial<InferenceRelayConfig>): Promise<InferenceRelay>;
97
+ dispose(): void;
98
+ private resolveProviders;
99
+ private executeMessage;
100
+ private streamMessage;
101
+ private createProvider;
102
+ }
103
+
104
+ declare abstract class BaseProvider {
105
+ abstract readonly type: string;
106
+ abstract readonly supportsStreaming: boolean;
107
+ abstract readonly supportsMultimodal: boolean;
108
+ abstract readonly platformRequirement: 'desktop' | 'web' | 'any';
109
+ priority: number;
110
+ abstract probe(): Promise<ProbeResult>;
111
+ abstract execute(params: CreateParams, options?: {
112
+ signal?: AbortSignal;
113
+ }): Promise<RelayResult>;
114
+ /**
115
+ * Lower-level streaming entrypoint. Returns the raw provider event iterator
116
+ * with NO RelayStream wrapping. The FallbackEngine consumes streamRaw()
117
+ * directly so it can run mid-stream failover, accumulate state, and wrap
118
+ * the final output in createRelayStream exactly once.
119
+ *
120
+ * Returns a Promise so eager setup (auth, spawn, fetch) happens BEFORE the
121
+ * iterator is returned. This matches the previous stream() semantics — by
122
+ * the time streamRaw() resolves, any side effects (subprocess spawn, HTTP
123
+ * connection) have already started.
124
+ *
125
+ * Providers MUST override either streamRaw() OR stream() (preferably
126
+ * streamRaw — stream() will be removed once all callers migrate).
127
+ */
128
+ streamRaw(_params: CreateParams, _options?: {
129
+ signal?: AbortSignal;
130
+ }): Promise<AsyncIterable<any>>;
131
+ /**
132
+ * Default RelayStream wrapper. Most providers should NOT override this —
133
+ * implement streamRaw() instead. Kept as an instance method (not abstract)
134
+ * for backward compatibility with callers that hit providers directly.
135
+ */
136
+ stream(params: CreateParams, options?: {
137
+ signal?: AbortSignal;
138
+ }): Promise<RelayStream>;
139
+ abstract estimateCost(params: CreateParams): CostEstimate;
140
+ abstract getHealth(): ProviderHealth;
141
+ abstract reportRateLimit(event: RateLimitEvent): void;
142
+ }
143
+
144
+ interface AnthropicAPIOptions {
145
+ apiKey: string;
146
+ baseUrl?: string;
147
+ /** Set true when running in a browser context */
148
+ dangerousDirectBrowserAccess?: boolean;
149
+ }
150
+ declare class AnthropicAPIProvider extends BaseProvider {
151
+ readonly type = "anthropic-api";
152
+ readonly supportsStreaming = true;
153
+ readonly supportsMultimodal = true;
154
+ readonly platformRequirement: "any";
155
+ private apiKey;
156
+ private baseUrl;
157
+ private dangerousDirectBrowserAccess;
158
+ private health;
159
+ constructor(options: AnthropicAPIOptions);
160
+ probe(): Promise<ProbeResult>;
161
+ execute(params: CreateParams, options?: {
162
+ signal?: AbortSignal;
163
+ }): Promise<RelayResult>;
164
+ streamRaw(params: CreateParams, options?: {
165
+ signal?: AbortSignal;
166
+ }): Promise<AsyncIterable<any>>;
167
+ estimateCost(params: CreateParams): CostEstimate;
168
+ getHealth(): ProviderHealth;
169
+ reportRateLimit(event: RateLimitEvent): void;
170
+ private buildHeaders;
171
+ private buildRequestBody;
172
+ private handleErrorResponse;
173
+ private recordSuccess;
174
+ }
175
+
176
+ interface OpenAIAPIOptions {
177
+ apiKey: string;
178
+ baseUrl?: string;
179
+ }
180
+ declare class OpenAIAPIProvider extends BaseProvider {
181
+ readonly type = "openai-api";
182
+ readonly supportsStreaming = true;
183
+ readonly supportsMultimodal = true;
184
+ readonly platformRequirement: "any";
185
+ private apiKey;
186
+ private baseUrl;
187
+ private health;
188
+ constructor(options: OpenAIAPIOptions);
189
+ probe(): Promise<ProbeResult>;
190
+ execute(params: CreateParams, options?: {
191
+ signal?: AbortSignal;
192
+ }): Promise<RelayResult>;
193
+ streamRaw(params: CreateParams, options?: {
194
+ signal?: AbortSignal;
195
+ }): Promise<AsyncIterable<any>>;
196
+ estimateCost(params: CreateParams): CostEstimate;
197
+ getHealth(): ProviderHealth;
198
+ reportRateLimit(event: RateLimitEvent): void;
199
+ private buildHeaders;
200
+ private buildRequestBody;
201
+ private handleErrorResponse;
202
+ private recordSuccess;
203
+ }
204
+
205
+ interface OllamaOptions {
206
+ baseUrl?: string;
207
+ }
208
+ declare class OllamaProvider extends BaseProvider {
209
+ readonly type = "ollama";
210
+ readonly supportsStreaming = true;
211
+ readonly supportsMultimodal = false;
212
+ readonly platformRequirement: "desktop";
213
+ private baseUrl;
214
+ private health;
215
+ constructor(options?: OllamaOptions);
216
+ probe(): Promise<ProbeResult>;
217
+ execute(params: CreateParams, options?: {
218
+ signal?: AbortSignal;
219
+ }): Promise<RelayResult>;
220
+ streamRaw(params: CreateParams, options?: {
221
+ signal?: AbortSignal;
222
+ }): Promise<AsyncIterable<any>>;
223
+ estimateCost(params: CreateParams): CostEstimate;
224
+ getHealth(): ProviderHealth;
225
+ reportRateLimit(event: RateLimitEvent): void;
226
+ private buildRequestBody;
227
+ private handleErrorResponse;
228
+ private recordSuccess;
229
+ }
230
+
231
+ /**
232
+ * Typed façade over Node's synchronous EventEmitter. Used by FallbackEngine
233
+ * to emit lifecycle events that the TelemetryReporter buffers per-call and
234
+ * flushes into a coherent AuditEvent at terminal stream_complete.
235
+ *
236
+ * MUST be synchronous (Node EventEmitter is). Do NOT replace with a custom
237
+ * Promise/microtask-queued impl — audit-event ordering depends on synchronous
238
+ * listener invocation so that lineage is fully buffered before flushLineageForCall
239
+ * runs.
240
+ */
241
+ declare class EngineEventEmitter extends EventEmitter {
242
+ constructor();
243
+ emitTyped(event: EngineEvent): boolean;
244
+ onTyped<K extends EngineEvent['kind']>(kind: K, listener: (event: Extract<EngineEvent, {
245
+ kind: K;
246
+ }>) => void): this;
247
+ offTyped<K extends EngineEvent['kind']>(kind: K, listener: (event: Extract<EngineEvent, {
248
+ kind: K;
249
+ }>) => void): this;
250
+ }
251
+
252
+ interface FallbackOptions {
253
+ signal?: AbortSignal;
254
+ maxAttempts?: number;
255
+ /** Maximum mid-stream failovers per call. Default: 2. */
256
+ maxMidStreamFailovers?: number;
257
+ /** Security state of the licence chain. SEC_DEGRADED enables jitter. */
258
+ licenseState?: SecurityState$1;
259
+ }
260
+ interface HealthRecord {
261
+ status: ProviderHealth['status'];
262
+ lastError?: string;
263
+ updatedAt: number;
264
+ }
265
+ declare class RelayError extends Error {
266
+ readonly code = "ALL_PROVIDERS_FAILED";
267
+ readonly fallbackChain: string[];
268
+ constructor(chain: string[]);
269
+ }
270
+ declare class FallbackEngine {
271
+ /** Tracks provider health across calls. Keyed by provider type string. */
272
+ private healthMap;
273
+ /**
274
+ * Public lifecycle event emitter. The TelemetryReporter subscribes to this
275
+ * to buffer per-call lineage and flush a coherent AuditEvent at terminal
276
+ * stream_complete. Synchronous semantics — see telemetry/engine-events.ts.
277
+ */
278
+ readonly engineEvents: EngineEventEmitter;
279
+ /** Unhealthy providers recover after 5 minutes (allows retry after transient failures). */
280
+ private static readonly UNHEALTHY_TTL_MS;
281
+ getHealthStatus(providerType: string): HealthRecord['status'];
282
+ markUnhealthy(providerType: string, reason: string): void;
283
+ markDegraded(providerType: string, reason: string): void;
284
+ markHealthy(providerType: string): void;
285
+ resetHealth(): void;
286
+ execute(params: CreateParams, providers: BaseProvider[], options?: FallbackOptions): Promise<RelayResult>;
287
+ stream(params: CreateParams, providers: BaseProvider[], options?: FallbackOptions): Promise<RelayStream>;
288
+ /**
289
+ * Find the next provider in priority order after `current` that supports
290
+ * streaming AND has not already been tried-and-failed in this call.
291
+ *
292
+ * IMPORTANT: this method intentionally does NOT consult `getHealthStatus`.
293
+ * Type-health is keyed by provider type string, but the documented "two
294
+ * API keys for redundancy" pattern uses two instances of the SAME type
295
+ * (e.g. primary + backup AnthropicAPIProvider with different keys). If we
296
+ * skipped by type-health, the first instance failing would poison the
297
+ * second instance because they share the type string. Per-call exclusion
298
+ * by INSTANCE preserves global health-tracking semantics for the next
299
+ * call's connect-time loop while letting same-type siblings serve as
300
+ * legitimate failover targets.
301
+ */
302
+ private nextHealthyProvider;
303
+ /**
304
+ * Internal emit shim — keeps the public emit API tight.
305
+ */
306
+ private emitEngineEvent;
307
+ /**
308
+ * Emit a stream_complete event on the terminal exhaustion path so the
309
+ * reporter flushes its lineage buffer for this callId and we don't leak
310
+ * Map entries forever (bug C3).
311
+ */
312
+ private emitTerminalExhaustion;
313
+ }
314
+
315
+ /**
316
+ * Model Registry — Single Source of Truth
317
+ *
318
+ * v1.4.0: expanded from alias-only to a full registry with capabilities,
319
+ * pricing, and provider ownership. The `equivalents` field (cross-provider
320
+ * model mapping) is intentionally absent here — it lives in the gateway
321
+ * bundle as trade-secret IP.
322
+ *
323
+ * Public consumers use the helpers at the bottom (findModel, providerForModel,
324
+ * modelsForProvider) to enumerate and route. The backwards-compat
325
+ * MODEL_ALIASES + normalizeModel() exports are preserved.
326
+ */
327
+ type ProviderType = 'anthropic-api' | 'openai-api' | 'ollama' | 'claude-cli';
328
+ interface ModelEntry {
329
+ /** Canonical short name customers use */
330
+ canonical: string;
331
+ /** Provider-specific dated/full ID (passed to the API) */
332
+ apiId: string;
333
+ /** Which provider class handles this model */
334
+ provider: ProviderType;
335
+ /** Capabilities the model supports */
336
+ capabilities: {
337
+ streaming: boolean;
338
+ tools: boolean;
339
+ multimodal: boolean;
340
+ /** Max context window in tokens */
341
+ contextWindow: number;
342
+ };
343
+ /** Pricing per million tokens [input, output] — undefined for local models */
344
+ pricing?: [number, number];
345
+ }
346
+ declare const MODEL_REGISTRY: ModelEntry[];
347
+ declare const MODEL_ALIASES: Record<string, string>;
348
+ declare function normalizeModel(model: string): string;
349
+ declare function findModel(canonical: string): ModelEntry | undefined;
350
+ declare function providerForModel(canonical: string): ProviderType | undefined;
351
+ declare function modelsForProvider(provider: ProviderType): ModelEntry[];
352
+
353
+ /**
354
+ * Detect Claude CLI installation status.
355
+ *
356
+ * This is the public, lightweight binary-presence check. It runs entirely
357
+ * within the public package and only checks (a) is the binary installed
358
+ * (b) what version (c) version-drift warning. It does NOT check credential
359
+ * status — that requires reading OS keychain stores, which is gateway-only.
360
+ *
361
+ * The `authenticated` field returned here is `null` to signal "not yet
362
+ * checked." The LazyClaudeCLIProvider's probe() method calls this AND
363
+ * the gateway-loaded credential resolver to fill in the full picture.
364
+ *
365
+ * Customers who want the full probe (including authenticated status)
366
+ * should use `relay.providers[0].probe()` once the relay is constructed.
367
+ */
368
+ declare function detectCLI(): Promise<ProbeResult>;
369
+
370
+ /**
371
+ * Validates an Anthropic API key by making a minimal request.
372
+ * 200 -> valid, 401 -> invalid, 429/529 -> valid but rate limited/overloaded.
373
+ */
374
+ declare function validateAnthropicKey(apiKey: string): Promise<{
375
+ valid: boolean;
376
+ error?: string;
377
+ }>;
378
+
379
+ interface CLIManifest {
380
+ version: string;
381
+ cliVersion: string;
382
+ eventTypes: {
383
+ system: string;
384
+ streamEvent: string;
385
+ assistant: string;
386
+ result: string;
387
+ };
388
+ fieldMappings: {
389
+ sessionId: string;
390
+ model: string;
391
+ cliVersion: string;
392
+ content: string;
393
+ usage: string;
394
+ inputTokens: string;
395
+ outputTokens: string;
396
+ stopReason: string;
397
+ isError: string;
398
+ totalCostUsd: string;
399
+ durationMs: string;
400
+ error: string;
401
+ event: string;
402
+ };
403
+ syntheticModel: string;
404
+ }
405
+ type SecurityState = 'SEC_NOMINAL' | 'SEC_CACHE' | 'SEC_RECOVERY' | 'SEC_DEGRADED';
406
+ declare class ManifestDecryptor {
407
+ private verificationFailures;
408
+ private cachedManifest;
409
+ private currentSecurityState;
410
+ /**
411
+ * Verify a JWS-signed manifest from the backend.
412
+ * The server signs with RS256 private key; we verify with the embedded public key.
413
+ */
414
+ verifyManifest(jwsString: string): Promise<{
415
+ manifest: CLIManifest;
416
+ securityState: SecurityState;
417
+ }>;
418
+ getSecurityState(): SecurityState;
419
+ clearCache(): void;
420
+ }
421
+ declare function getLastKnownGoodManifest(): CLIManifest;
422
+ /**
423
+ * Path-based value accessor for manifest field mappings.
424
+ * Zero-dependency alternative to lodash.get().
425
+ * Prototype pollution guarded.
426
+ */
427
+ declare function getValue(obj: any, path: string): any;
428
+
429
+ declare class TelemetryReporter {
430
+ private endpoint;
431
+ private enabled;
432
+ private keyId;
433
+ /**
434
+ * Lineage buffer: per-call accumulation of EngineEvents from FallbackEngine.
435
+ * Flushed into the AuditEvent.lineage field at terminal stream_complete.
436
+ * Keyed by callId so concurrent calls don't cross-contaminate.
437
+ */
438
+ private lineageBuffer;
439
+ /** Stored unsubscribe handlers so dispose() can detach cleanly. */
440
+ private unsubscribers;
441
+ constructor(options: {
442
+ endpoint?: string;
443
+ enabled?: boolean;
444
+ keyId?: string;
445
+ });
446
+ /**
447
+ * Subscribe to the FallbackEngine's lifecycle event stream. The reporter
448
+ * buffers per-call lineage and flushes a coherent AuditEvent at terminal
449
+ * stream_complete. Synchronous emit semantics from EngineEventEmitter
450
+ * guarantee that all lineage events for a callId are buffered before
451
+ * the stream_complete handler runs.
452
+ */
453
+ subscribeToEngine(engine: FallbackEngine): void;
454
+ /** Detach all engine subscriptions. Called by InferenceRelay.dispose(). */
455
+ unsubscribe(): void;
456
+ private bufferLineageEvent;
457
+ /**
458
+ * Construct the final AuditEvent from the stream_complete payload + buffered
459
+ * lineage and call report(). Privacy invariant: lineage carries IDs only,
460
+ * never content.
461
+ */
462
+ private flushLineageForCall;
463
+ report(event: AuditEvent): void;
464
+ }
465
+
466
+ type RoutingDecision = 'prefer-user' | 'prefer-app' | string;
467
+ type RoutingFunction = (params: CreateParams, estimate: {
468
+ estimatedCost: number;
469
+ }) => RoutingDecision;
470
+ /**
471
+ * Apply routing decision to reorder providers.
472
+ * 'prefer-user' puts subscription-backed providers first (claude-cli, then user API keys).
473
+ * 'prefer-app' puts app-owned providers first (app API key).
474
+ * A specific provider name puts that provider first.
475
+ */
476
+ declare function applyRouting(providers: BaseProvider[], decision: RoutingDecision): BaseProvider[];
477
+ /**
478
+ * Create a routing function from a RoutingConfig.
479
+ */
480
+ declare function createRoutingFunction(config: RoutingConfig): RoutingFunction;
481
+
482
+ /**
483
+ * Estimate the cost of a request before sending it.
484
+ * Used by routing to decide which provider handles the call.
485
+ */
486
+ declare function estimateCost(params: CreateParams): CostEstimate;
487
+
488
+ /**
489
+ * Get pricing for a model. Falls back to Sonnet pricing for unknown models.
490
+ */
491
+ declare function getModelPricing(model: string): [number, number];
492
+
493
+ /**
494
+ * CredentialStore — secure local storage for user API keys.
495
+ * Keys never leave the client. Never sent to inference-relay servers.
496
+ */
497
+ interface CredentialStore {
498
+ get(key: string): Promise<string | null>;
499
+ set(key: string, value: string): Promise<void>;
500
+ delete(key: string): Promise<void>;
501
+ has(key: string): Promise<boolean>;
502
+ }
503
+
504
+ /**
505
+ * Electron safeStorage credential store.
506
+ *
507
+ * Uses Electron's safeStorage.encryptString() / decryptString() to protect
508
+ * API keys at rest. Encrypted blobs are persisted as a JSON file in the
509
+ * user's data directory (~/.inference-relay/credentials.enc by default).
510
+ *
511
+ * Falls back to an in-memory Map if safeStorage is unavailable (e.g. when
512
+ * running outside Electron or before the app is ready).
513
+ */
514
+ declare class ElectronCredentialStore implements CredentialStore {
515
+ private filePath;
516
+ private safeStorage;
517
+ private memoryFallback;
518
+ constructor(options?: {
519
+ storagePath?: string;
520
+ });
521
+ private readDisk;
522
+ private writeDisk;
523
+ get(key: string): Promise<string | null>;
524
+ set(key: string, value: string): Promise<void>;
525
+ delete(key: string): Promise<void>;
526
+ has(key: string): Promise<boolean>;
527
+ }
528
+
529
+ /**
530
+ * IndexedDB credential store with AES-GCM encryption via Web Crypto API.
531
+ *
532
+ * Encryption key is derived from a user-provided passphrase (PBKDF2) or,
533
+ * if no passphrase is given, a random key is generated and held in
534
+ * sessionStorage for the lifetime of the browser tab. This means
535
+ * credentials survive page refreshes within the same session but are
536
+ * wiped when the tab closes — a deliberate safety tradeoff.
537
+ */
538
+ declare class IndexedDBCredentialStore implements CredentialStore {
539
+ private dbName;
540
+ private storeName;
541
+ private cryptoKey;
542
+ private passphrase?;
543
+ private initPromise;
544
+ constructor(options?: {
545
+ passphrase?: string;
546
+ dbName?: string;
547
+ });
548
+ private ensureReady;
549
+ private init;
550
+ private openDB;
551
+ private idbGet;
552
+ private idbPut;
553
+ private idbDelete;
554
+ private idbHas;
555
+ private getOrCreateSessionSalt;
556
+ private deriveKey;
557
+ private encrypt;
558
+ private decrypt;
559
+ get(key: string): Promise<string | null>;
560
+ set(key: string, value: string): Promise<void>;
561
+ delete(key: string): Promise<void>;
562
+ has(key: string): Promise<boolean>;
563
+ }
564
+
565
+ /**
566
+ * In-memory credential store. Volatile — cleared on process exit.
567
+ * Suitable for testing, CLI apps, or as a fallback when no persistent
568
+ * storage backend is available.
569
+ */
570
+ declare class MemoryCredentialStore implements CredentialStore {
571
+ private store;
572
+ get(key: string): Promise<string | null>;
573
+ set(key: string, value: string): Promise<void>;
574
+ delete(key: string): Promise<void>;
575
+ has(key: string): Promise<boolean>;
576
+ }
577
+
578
+ /**
579
+ * GatewayLoader — fetches the signed Native Subscription Gateway bundle
580
+ * from the Protocol Authority on first use, verifies it against the
581
+ * embedded RSA public key, caches it locally, and loads it into the
582
+ * relay process via vm.Script.
583
+ *
584
+ * The bundle contains the implementation of the gateway providers
585
+ * (subscription-bridged providers, stream parsers, credential resolvers).
586
+ * It is NOT distributed via npm — it lives in Convex blob storage on the
587
+ * Protocol Authority and is downloaded on demand by license holders.
588
+ *
589
+ * This file is bundled INTO the public npm package (no IP here). It is
590
+ * the ONLY thing the public package needs to know about the gateway —
591
+ * after `await GatewayLoader.load(licenseKey)`, the loader returns the
592
+ * gateway module's exports as a plain object. Callers (specifically
593
+ * gateway/lazy-provider.ts) treat that object as opaque.
594
+ *
595
+ * Threat model:
596
+ * - Non-customer cannot fetch the bundle: the Protocol Authority's
597
+ * /v1/gateway/fetch endpoint requires a valid license key. Bundles are
598
+ * served via short-TTL pre-signed URLs.
599
+ * - MITM attacker cannot substitute a malicious bundle: the bundle ships
600
+ * with an RS256 signature over `{ version, sha256, purpose: 'gateway-bundle' }`,
601
+ * verified against the embedded RSA public key. Cross-payload confusion
602
+ * is prevented by the `purpose` field — a manifest signature cannot be
603
+ * replayed as a gateway-bundle signature and vice versa.
604
+ * - Customer who leaks bundle: contractual problem (covered by EULA).
605
+ * Each bundle has a build identifier embedded; leaked copies are
606
+ * identifiable.
607
+ * - Local cache tampering: bundle SHA-256 is verified against the
608
+ * server-asserted SHA-256 before loading. A tampered cache file fails
609
+ * verification and triggers a fresh fetch.
610
+ * - Gateway endpoint outage: fail closed. The relay cannot serve
611
+ * claude-cli requests until the gateway is reachable. The user can
612
+ * still configure direct API providers as a fallback.
613
+ */
614
+ /**
615
+ * The shape of the gateway module's exports after loading. The lazy
616
+ * proxies use these to instantiate concrete implementations without the
617
+ * public package needing to know any internal details.
618
+ */
619
+ interface GatewayModule {
620
+ ClaudeCLIProvider: new () => any;
621
+ ProcessPool: new (options?: {
622
+ maxSize?: number;
623
+ idleTimeoutMs?: number;
624
+ }) => any;
625
+ _lsp?: (model: string, baseUrl: string) => Promise<boolean>;
626
+ _lspClear?: () => void;
627
+ _lt?: (tools: any[]) => any[];
628
+ _lpc?: (toolCalls: any[]) => any[];
629
+ _lpsc?: (chunk: any, state: any) => any[];
630
+ _lcss?: () => any;
631
+ _rfp?: (model: string, targetProvider: string) => string | undefined;
632
+ _cfc?: (model: string) => string[];
633
+ _ge?: (model: string) => Record<string, string> | undefined;
634
+ _saf?: (input: any) => any | null;
635
+ }
636
+ /**
637
+ * Errors thrown by the loader. Designed to be matched against by error
638
+ * classification code in fallback/engine.ts and verify.ts so customer-
639
+ * facing diagnostics stay structured.
640
+ */
641
+ declare class GatewayLoaderError extends Error {
642
+ readonly code: 'GATEWAY_FETCH_FAILED' | 'LICENSE_REJECTED' | 'NETWORK_TIMEOUT' | 'GATEWAY_VERIFICATION_FAILED' | 'GATEWAY_CACHE_WRITE_FAILED' | 'GATEWAY_LOAD_FAILED';
643
+ readonly cause?: unknown | undefined;
644
+ constructor(message: string, code: 'GATEWAY_FETCH_FAILED' | 'LICENSE_REJECTED' | 'NETWORK_TIMEOUT' | 'GATEWAY_VERIFICATION_FAILED' | 'GATEWAY_CACHE_WRITE_FAILED' | 'GATEWAY_LOAD_FAILED', cause?: unknown | undefined);
645
+ }
646
+ declare class GatewayLoader {
647
+ /**
648
+ * Load (or resolve from cache) the gateway module for a given license
649
+ * key. On first call, fetches from the Protocol Authority, verifies the
650
+ * signature, writes to local cache, and instantiates the module.
651
+ *
652
+ * @param licenseKey - The customer's license key (`ir_live_…`)
653
+ * @param libraryVersion - The library version, used as the cache namespace
654
+ * @param baseUrl - Override the gateway endpoint (test fixture only)
655
+ */
656
+ static load(licenseKey: string, libraryVersion: string, baseUrl?: string): Promise<GatewayModule>;
657
+ /**
658
+ * Test helper: bypass the network and load from a local file path.
659
+ * Used by tests and by the airgapped pre-fetch flow.
660
+ */
661
+ static loadFromDisk(bundlePath: string): Promise<GatewayModule>;
662
+ private static tryLoadFromDisk;
663
+ private static fetchFromAuthority;
664
+ /**
665
+ * Verify the RS256 signature over
666
+ * { libraryVersion, bundleVersion, sha256, purpose: 'gateway-bundle' }.
667
+ *
668
+ * The `purpose` field is mandatory — a payload signed for `manifest`
669
+ * cannot be replayed here. The `libraryVersion` field is the
670
+ * version-rollback guard: a 1.3.0 bundle's signature cannot be replayed
671
+ * to satisfy a 1.4.0 client (the SHA-256 also pins the bytes, but
672
+ * comparing libraryVersion catches the case where an attacker somehow
673
+ * generates a SHA collision OR substitutes a different bundle whose
674
+ * libraryVersion field doesn't match what the client requested).
675
+ * The signature itself is a JWS compact form.
676
+ */
677
+ private static verifySignature;
678
+ /**
679
+ * Execute the gateway bundle as a CommonJS module.
680
+ *
681
+ * The bundle is a CJS module — it expects `module.exports = { ClaudeCLIProvider, ... }`
682
+ * after evaluation. We use Node's `vm.compileFunction` API, which is
683
+ * the same primitive Node's own CJS loader uses internally to evaluate
684
+ * every .js file in a Node process. The gateway bundle gets called as
685
+ * if it were a normal CJS module: with explicit `exports`, `require`,
686
+ * `module`, `__filename`, and `__dirname` parameters.
687
+ *
688
+ * Why not vm.Script + createContext: that approach captures `require`
689
+ * from the parent (loader.ts) module's scope, which under tsup ESM
690
+ * compilation becomes a dynamic-require shim that throws inside the
691
+ * sandbox. compileFunction passes `require` as an explicit named
692
+ * argument, so the gateway bundle's bare `require()` calls resolve to
693
+ * the function parameter, never to a captured outer-scope wrapper.
694
+ *
695
+ * The require we pass is built via createRequire() bound to a path
696
+ * inside the local cache directory. Node built-in modules resolve
697
+ * via this require regardless of the bound path; the path only
698
+ * matters for relative file resolves, which the gateway bundle does
699
+ * not perform (it's self-contained).
700
+ *
701
+ * Trust model: by accepting the EULA, the customer agrees that the
702
+ * gateway bundle runs with the privileges of their Node process.
703
+ * Removing the vm.Script sandbox does not change this — the prior
704
+ * sandbox provided full `require` access anyway, so it was security
705
+ * theater. compileFunction is more honest: the gateway runs with the
706
+ * same Node API surface as any other CJS file in the host process.
707
+ */
708
+ private static loadIntoVm;
709
+ /**
710
+ * Test helper: clear the in-process module cache. Tests use this between
711
+ * cases to force a fresh load.
712
+ */
713
+ static clearCache(): void;
714
+ }
715
+
716
+ export { AnthropicAPIProvider, AuditEvent, BaseProvider, type CLIManifest, CostEstimate, CreateParams, type CredentialStore, ElectronCredentialStore, EngineEvent, EngineEventEmitter, FallbackEngine, GatewayLoader, GatewayLoaderError, type GatewayModule, IndexedDBCredentialStore, InferenceRelay, InferenceRelayConfig, type LicenseValidation, MODEL_ALIASES, MODEL_REGISTRY, ManifestDecryptor, MemoryCredentialStore, type ModelEntry, OllamaProvider, OpenAIAPIProvider, ProbeResult, ProviderHealth, type ProviderType, RateLimitEvent, RelayError, RelayResult, RelayStream, RoutingConfig, type RoutingDecision, type RoutingFunction, SecurityState$1 as SecurityState, TelemetryReporter, applyRouting, createRoutingFunction, detectCLI, estimateCost, findModel, getLastKnownGoodManifest, getModelPricing, getValue, modelsForProvider, normalizeModel, providerForModel, validateAnthropicKey, validateLicense };