@vectoral-labs/sdk 0.1.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,572 @@
1
+ /** Categorizes why a call failed, so callers can branch without string-matching. */
2
+ type VectoralErrorCode = "http_error" | "network_error" | "timeout" | "invalid_response";
3
+ /** Thrown for any non-2xx response, network failure, or timeout. */
4
+ declare class VectoralError extends Error {
5
+ /** HTTP status code, or 0 for network/timeout errors. */
6
+ readonly status: number;
7
+ readonly code: VectoralErrorCode;
8
+ /** Raw response body, when there was one. */
9
+ readonly responseBody: string | undefined;
10
+ constructor(message: string, opts: {
11
+ code: VectoralErrorCode;
12
+ status: number;
13
+ responseBody?: string;
14
+ cause?: unknown;
15
+ });
16
+ /**
17
+ * True for failures where the request provably did not reach a decision:
18
+ * network errors, timeouts, and 5xx. A retry is safe only if the call also
19
+ * carried an `event_id` — see `docs/concepts/reliability.md`.
20
+ */
21
+ get transient(): boolean;
22
+ }
23
+ /** Thrown at construction time for a misconfigured client. Never at call time. */
24
+ declare class VectoralConfigError extends Error {
25
+ constructor(message: string);
26
+ }
27
+
28
+ /** Minimal fetch signature so this runs on Node 18+, Deno, Bun, and edge runtimes. */
29
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
30
+ interface TransportOptions {
31
+ baseUrl: string;
32
+ authHeader: Record<string, string>;
33
+ headers: Record<string, string>;
34
+ timeoutMs: number;
35
+ fetch: FetchLike;
36
+ /**
37
+ * Attempts after the first, for transient failures. Only applied to requests
38
+ * the caller marked idempotent — see `post()`.
39
+ */
40
+ retries: number;
41
+ }
42
+ interface PostOptions {
43
+ /**
44
+ * Whether a retry is safe. Every Vectoral write endpoint mints a new row per
45
+ * call unless the body carries an `event_id`, so the transport refuses to
46
+ * retry anything without one: a blind retry after a response lost in transit
47
+ * would double-write.
48
+ */
49
+ idempotent: boolean;
50
+ /**
51
+ * A per-call timeout FLOOR, used by `deadline_ms`. It can only raise the
52
+ * effective timeout, never lower it: a caller who configured `timeoutMs`
53
+ * explicitly must not have it silently reduced by asking the server for a
54
+ * tight budget, or a slow link turns every good verdict into a fail-open
55
+ * zero — the failure mode `httpTimeoutFor` exists to prevent.
56
+ */
57
+ timeoutMs?: number;
58
+ }
59
+ declare class Transport {
60
+ private readonly opts;
61
+ constructor(opts: TransportOptions);
62
+ post<T>(path: string, body: unknown, po: PostOptions): Promise<T>;
63
+ private doPost;
64
+ private send;
65
+ }
66
+
67
+ /** Minimum salt length we will accept. 32 hex chars = 128 bits. */
68
+ declare const MIN_SALT_LENGTH = 32;
69
+ interface FingerprintOptions$1 {
70
+ /**
71
+ * Master switch. When false or absent, prompt text is never read and never
72
+ * hashed — the SDK does not touch it.
73
+ */
74
+ enabled: boolean;
75
+ /**
76
+ * Secret tenant salt. Defaults to `process.env.VECTORAL_FINGERPRINT_SALT`.
77
+ * Treat it like a signing key: 128+ bits, out of source control, rotated
78
+ * deliberately.
79
+ */
80
+ salt?: string;
81
+ /**
82
+ * Generation id for `salt`, e.g. `s_2026_09`. Sent alongside every
83
+ * fingerprint so the server knows which generation a value belongs to and
84
+ * never compares across a rotation. Defaults to
85
+ * `process.env.VECTORAL_FINGERPRINT_SALT_ID`.
86
+ */
87
+ saltId?: string;
88
+ /**
89
+ * Additionally compute an UNSALTED fingerprint, comparable across all
90
+ * Vectoral customers who opted in. Requires `enabled`. Opting in
91
+ * acknowledges the reduced confidentiality of that tier.
92
+ */
93
+ shareGlobal?: boolean;
94
+ }
95
+ /** Resolved config. `null` means fingerprinting is inert for this client. */
96
+ interface ResolvedFingerprintConfig {
97
+ salt: string;
98
+ saltId: string;
99
+ shareGlobal: boolean;
100
+ }
101
+ /**
102
+ * Generate a fresh tenant salt: 32 random bytes, hex-encoded.
103
+ *
104
+ * For bootstrapping and rotation. Store the output in your secret manager and
105
+ * pair it with a new `saltId`; do not call this at process start, which would
106
+ * mint a new salt per deploy and make every stored fingerprint incomparable.
107
+ */
108
+ declare function generateSalt(): string;
109
+ /**
110
+ * A conventional salt id for the current month, e.g. `s_2026_09`. Any stable
111
+ * string works; the convention just makes rotations self-documenting in logs.
112
+ */
113
+ declare function suggestSaltId(now?: Date): string;
114
+
115
+ /** Browser-environment tells, as collected by `@vectoral-labs/browser`. */
116
+ interface RegistrationClientBlock {
117
+ /** `navigator.webdriver`. Cheap, and a strong tell when true. */
118
+ webdriver?: boolean;
119
+ /** Your own [0,1] canvas/WebGL/capability-consistency score. */
120
+ fingerprint_anomaly?: number;
121
+ /** Milliseconds from page load to submit. */
122
+ load_to_submit_ms?: number;
123
+ /** IANA zone, compared against the observed origin. */
124
+ timezone?: string;
125
+ }
126
+ /** Per-field fill behaviour. `pasted` is the highest-value bit. */
127
+ interface RegistrationFormField {
128
+ pasted?: boolean;
129
+ keystrokes?: number;
130
+ corrections?: number;
131
+ focus_ms?: number;
132
+ }
133
+ interface RegistrationFormBlock {
134
+ load_to_submit_ms?: number;
135
+ fields?: Record<string, RegistrationFormField>;
136
+ }
137
+ /**
138
+ * Body of POST /v1/registrations/score.
139
+ *
140
+ * Every optional field you omit produces an **absent** signal, never a
141
+ * favourable one: no `device_fingerprint` does not mean "no device reuse", and
142
+ * no `form` does not mean "a human typed it".
143
+ */
144
+ interface RegistrationRequest {
145
+ /** The submitted address. The only required field. */
146
+ email: string;
147
+ /**
148
+ * Idempotency key. A repeat of the same `(customer, event_id)` returns the
149
+ * original verdict with `duplicate: true` and writes nothing.
150
+ *
151
+ * Strongly recommended, and the SDK only retries transient failures when it
152
+ * is present. Derive it from your own pending-signup record, not from a
153
+ * transport message id.
154
+ */
155
+ event_id?: string;
156
+ /** The user's browser IP at submit — the one your server observed. */
157
+ ip?: string;
158
+ /** Your own keyed token instead of `ip`. Disables per-subnet velocity. */
159
+ ip_hash?: string;
160
+ asn?: number;
161
+ ip_country?: string;
162
+ /** The country the user claimed, if your form collects one. */
163
+ declared_country?: string;
164
+ user_agent?: string;
165
+ /**
166
+ * Client-side device identifier. **The single highest-value optional field** —
167
+ * one device across many registrations is the strongest farm signal there is.
168
+ * `@vectoral-labs/browser`'s `deviceFingerprint()` produces one.
169
+ */
170
+ device_fingerprint?: string;
171
+ /** A token from the browser sensor, if deployed. */
172
+ sensor_token?: string;
173
+ client?: RegistrationClientBlock;
174
+ form?: RegistrationFormBlock;
175
+ phone?: string;
176
+ username?: string;
177
+ referrer?: string;
178
+ /**
179
+ * Whole-call budget in ms for the server's external lookups. Defaults to 400
180
+ * server-side and is clamped to [250, 2000] rather than rejected. The SDK
181
+ * raises its own HTTP timeout to sit above whatever you set here.
182
+ */
183
+ deadline_ms?: number;
184
+ }
185
+ /**
186
+ * What to do with the signup. An **open, ordered scale** — higher means more
187
+ * friction. Compare (`tier >= Tier.StepUp`); never switch exhaustively, because
188
+ * new tiers can be added without a new API version.
189
+ */
190
+ declare const RegistrationTier: {
191
+ readonly Allow: 0;
192
+ readonly Challenge: 1;
193
+ readonly StepUp: 2;
194
+ };
195
+ interface RegistrationVerdict {
196
+ /**
197
+ * Opaque handle to pass back on `identity.record()` when the account is
198
+ * created. `null` only when the call failed open — there is nothing to link.
199
+ */
200
+ registration_id: string | null;
201
+ /** See `RegistrationTier`. Compare, do not switch. */
202
+ tier: number;
203
+ /** The underlying risk score in [0,1], for your own tuning. */
204
+ score: number;
205
+ /** Up to three contributing facts, most significant first. Do not branch on these. */
206
+ reasons: string[];
207
+ /** True on an idempotent replay of a previous `event_id`. */
208
+ duplicate?: boolean;
209
+ /** True during the warm-up window, when `tier` is pinned to 0. */
210
+ shadow_mode?: boolean;
211
+ /**
212
+ * True when this verdict is the SDK's fail-open default rather than a real
213
+ * answer — the call errored and `failOpen` was on. Log it: a flow silently
214
+ * running at `tier: 0` because Vectoral is unreachable looks identical to one
215
+ * where every signup is clean.
216
+ */
217
+ degraded: boolean;
218
+ /** The underlying failure, when `degraded`. */
219
+ error?: VectoralError;
220
+ }
221
+ interface RegistrationsOptions {
222
+ failOpen: boolean;
223
+ onError: ((err: VectoralError, context: string) => void) | undefined;
224
+ }
225
+ declare class Registrations {
226
+ private readonly transport;
227
+ private readonly opts;
228
+ constructor(transport: Transport, opts: RegistrationsOptions);
229
+ /**
230
+ * Score a registration at form submit.
231
+ *
232
+ * With `failOpen` (the default) this never throws: any network failure,
233
+ * timeout, or HTTP error yields `tier: 0, degraded: true`. A screening check
234
+ * that can take your signup page down is worse than no screening check.
235
+ */
236
+ score(req: RegistrationRequest): Promise<RegistrationVerdict>;
237
+ }
238
+
239
+ /** Risk banding returned alongside the numeric score. */
240
+ type Tier = "low" | "medium" | "high";
241
+ /**
242
+ * Reason codes that can appear in a score response, as a hint for autocomplete.
243
+ *
244
+ * **This list is not exhaustive and cannot be made exhaustive.** Reasons are
245
+ * produced by the scoring algorithm running server-side, which ships
246
+ * independently of this package — a new one can appear in a response without an
247
+ * SDK release. The `(string & {})` member is what makes that safe: an unlisted
248
+ * code type-checks. It is also why you must not write an exhaustive `switch`
249
+ * over this type, and why `reasons` is for your logs and support conversations
250
+ * rather than for branching. `tier` is the field to act on.
251
+ *
252
+ * The server returns at most three, ordered by contribution.
253
+ *
254
+ * Listed below by where the code comes from, because the three groups behave
255
+ * differently — an operational code means the verdict was overridden and the
256
+ * algorithm's opinion is not what you are looking at. See
257
+ * `docs/concepts/inference-scoring.md` for what each one means.
258
+ */
259
+ type ReasonCode = "machine_paced" | "hidden_telemetry" | "birth_cohort" | "probing" | "resource_shape" | "value_extraction" | "resource_extraction" | "datacenter_origin" | "account_risk" | "synthetic_noop" | "account_blocked" | "spend_cap_exceeded:account" | "spend_cap_exceeded:org" | "scoring_unavailable" | "reputation_discount" | "webdriver_present" | "automation_signature" | "headless_browser" | "no_accept_languages" | "missing_chrome_object" | "no_human_interaction" | "no_pointer_activity" | "cursor_teleport" | "thin_fingerprint" | (string & {});
260
+ /**
261
+ * Account-context block. All fields optional but recommended.
262
+ *
263
+ * Both fields are sent verbatim in the request body — this block is context you
264
+ * supply, not something derived from the prompt. Worth knowing if anything on
265
+ * your egress path inspects outgoing bodies: a label you chose will appear in
266
+ * them as plain text. See `docs/concepts/inference-scoring.md`.
267
+ */
268
+ interface AccountBlock {
269
+ /** RFC 3339 timestamp of when the account first existed in your system. */
270
+ first_seen?: string;
271
+ /**
272
+ * Free-form tier label, e.g. "free" | "pro" | "enterprise". Sent as given;
273
+ * use a stable internal label rather than anything user-supplied.
274
+ */
275
+ subscription_tier?: string;
276
+ }
277
+ /**
278
+ * Per-session behavioral signals.
279
+ *
280
+ * Semantics matter: OMITTING this block entirely differs from sending it with
281
+ * zero values. If absent, session-derived signals are skipped. If present (even
282
+ * as `{}`), they participate — and `interaction_events_count: 0` is a
283
+ * meaningful "scripted-shaped" signal.
284
+ */
285
+ interface SessionSignals {
286
+ ms_since_last_request?: number;
287
+ ms_since_page_load?: number;
288
+ interaction_events_count?: number;
289
+ is_first_request_in_session?: boolean;
290
+ ms_since_signup?: number;
291
+ }
292
+ /**
293
+ * Wire form of the prompt fingerprint. 64-bit values are strings (hex for
294
+ * hashes, decimal for band keys) because JSON cannot carry 64-bit integers
295
+ * safely through every runtime.
296
+ */
297
+ interface PromptFingerprintBlock {
298
+ v: 1;
299
+ salt_id: string;
300
+ simhash: string;
301
+ exact_hash: string;
302
+ conversation_key?: string;
303
+ minhash?: string;
304
+ band_keys?: string[];
305
+ simhash_global?: string;
306
+ }
307
+ /** Per-request context. */
308
+ interface RequestBlock {
309
+ /**
310
+ * End-user IP. Drives datacenter / IP-rotation / sybil signals. Use the IP
311
+ * your SERVER observed, not one reported by the browser.
312
+ */
313
+ ip?: string;
314
+ ip_hash?: string;
315
+ user_agent?: string;
316
+ /** The model you're about to invoke, e.g. "claude-opus-5". */
317
+ model_requested?: string;
318
+ estimated_prompt_tokens?: number;
319
+ /** Pass `null` or omit to skip session signals entirely. */
320
+ session_signals?: SessionSignals | null;
321
+ /** Only used by Deep Mode. Never leaves the customer VPC. */
322
+ prompt_text?: string;
323
+ /** Customer-precomputed embedding (alternative to `prompt_text`). */
324
+ prompt_embedding?: number[];
325
+ /**
326
+ * LOCAL-ONLY input for prompt fingerprinting. When fingerprinting is
327
+ * configured, the SDK hashes this in-process and attaches
328
+ * `prompt_fingerprint`. **This field is always stripped before the request
329
+ * leaves the process** — enabled or not — and is independent of
330
+ * `prompt_text`/Deep Mode.
331
+ */
332
+ prompt_text_to_fingerprint?: string;
333
+ /** Normally set by the SDK; pass it yourself only if you precompute. */
334
+ prompt_fingerprint?: PromptFingerprintBlock;
335
+ }
336
+ /** Body of POST /v1/score. */
337
+ interface ScoreRequest {
338
+ /** Stable end-user identifier; the primary join key for scoring history. */
339
+ account_id: string;
340
+ /** Your own session token; stored for forensic correlation. */
341
+ session_id?: string;
342
+ account?: AccountBlock;
343
+ request: RequestBlock;
344
+ }
345
+ interface ScoreResponse {
346
+ /** Calibrated [0,1] score. 0 = clean, 1 = certain fraud. */
347
+ score: number;
348
+ /** low (<0.4) | medium (<0.7) | high (>=0.7). */
349
+ tier: Tier;
350
+ /** Up to 3 reason codes. */
351
+ reasons: ReasonCode[];
352
+ deep_mode_active: boolean;
353
+ /**
354
+ * False while the per-account baseline is still forming: the score is
355
+ * provisional — advisory, not enforcement-grade.
356
+ */
357
+ baseline_ready: boolean;
358
+ /** True during the customer's warm-up window. The score is still real. */
359
+ shadow_mode: boolean;
360
+ /** Diagnostic only; treat as an opaque string. */
361
+ algorithm?: string;
362
+ algorithm_version?: string;
363
+ /** The Vectoral release that answered, e.g. `0.1.7`. */
364
+ service_version?: string;
365
+ /**
366
+ * True when this is the SDK's fail-open default rather than a real verdict.
367
+ * See `RegistrationVerdict.degraded`.
368
+ */
369
+ degraded?: boolean;
370
+ /** The underlying failure, when `degraded`. */
371
+ error?: VectoralError;
372
+ }
373
+ /** Body of POST /v1/events/post-call. */
374
+ interface PostCallEvent {
375
+ /** Must match the `account_id` from the preceding score call. */
376
+ account_id: string;
377
+ session_id?: string;
378
+ /** Recommended; omitting it loses model-mix features. */
379
+ model?: string;
380
+ /** Required if `inference_cost_usd` is not supplied. */
381
+ prompt_tokens?: number;
382
+ /** Required if `inference_cost_usd` is not supplied. */
383
+ completion_tokens?: number;
384
+ latency_ms?: number;
385
+ /** If omitted, the server computes cost from its bundled rate table. */
386
+ inference_cost_usd?: number;
387
+ /** Idempotency key. Also what makes an SDK-level retry safe. */
388
+ event_id?: string;
389
+ }
390
+ interface OkResponse {
391
+ ok: boolean;
392
+ duplicate?: boolean;
393
+ }
394
+ interface InferenceOptions {
395
+ failOpen: boolean;
396
+ fingerprint: ResolvedFingerprintConfig | null;
397
+ onError: ((err: VectoralError, context: string) => void) | undefined;
398
+ onWarning: ((message: string) => void) | undefined;
399
+ }
400
+ declare class Inference {
401
+ private readonly transport;
402
+ private readonly opts;
403
+ private warnedMissingFingerprintInput;
404
+ constructor(transport: Transport, opts: InferenceOptions);
405
+ /** Pre-call risk score. Call before invoking the LLM. */
406
+ score(req: ScoreRequest): Promise<ScoreResponse>;
407
+ /**
408
+ * Post-call token/cost telemetry. Call after the LLM responds.
409
+ *
410
+ * This is the feedback that makes every later score meaningful — it is where
411
+ * token velocity and cost signals come from. It never fails open: telemetry
412
+ * you silently drop is telemetry you never notice missing.
413
+ */
414
+ postCall(event: PostCallEvent): Promise<OkResponse>;
415
+ /**
416
+ * Consume `prompt_text_to_fingerprint` (always stripped from the wire,
417
+ * enabled or not) and attach the computed block when fingerprinting is
418
+ * active. The caller's object is never mutated.
419
+ */
420
+ private prepare;
421
+ }
422
+
423
+ type IdentityEventType = "signup" | "login" | "dashboard";
424
+ interface IdentityEvent {
425
+ /** Created if not already present, so identity-only accounts still surface. */
426
+ account_id: string;
427
+ /** Defaults to `login` server-side. */
428
+ event_type?: IdentityEventType;
429
+ /** The user's real browser IP at that moment. */
430
+ ip?: string;
431
+ /** Your own keyed token instead of `ip`. */
432
+ ip_hash?: string;
433
+ asn?: number;
434
+ ip_country?: string;
435
+ user_agent?: string;
436
+ /** Idempotency key. Also what makes an SDK-level retry safe. */
437
+ event_id?: string;
438
+ /**
439
+ * The handle from `registrations.score()`. Send it once, on the `signup`
440
+ * event, and the fraud labels you later report reach back to the registration
441
+ * that produced the account. That is the feedback loop.
442
+ *
443
+ * It can arrive late — if you gate account creation on email verification,
444
+ * store the id with your pending-signup record and send it whenever the
445
+ * account is finally created. An unknown or already-linked id is ignored,
446
+ * never rejected.
447
+ */
448
+ registration_id?: string;
449
+ }
450
+ declare class Identity {
451
+ private readonly transport;
452
+ constructor(transport: Transport);
453
+ /**
454
+ * Record an authentication event.
455
+ *
456
+ * Use the same IP shape here as on `inference.score()`: the accounts-per-IP
457
+ * query unions both sources, so mixing raw addresses on one and tokens on the
458
+ * other splits your own sybil graph.
459
+ */
460
+ record(event: IdentityEvent): Promise<OkResponse>;
461
+ /**
462
+ * Convenience for the common signup case: record the event and link the
463
+ * registration in one call.
464
+ */
465
+ linkRegistration(accountId: string, registrationId: string, extra?: Omit<IdentityEvent, "account_id" | "registration_id" | "event_type">): Promise<OkResponse>;
466
+ }
467
+
468
+ type Label = "fraud" | "legitimate";
469
+ interface LabelRequest {
470
+ account_id: string;
471
+ label: Label;
472
+ notes?: string;
473
+ }
474
+ declare class Labels {
475
+ private readonly transport;
476
+ constructor(transport: Transport);
477
+ /**
478
+ * Report a verdict you reached yourself — a chargeback, a ban, a support
479
+ * resolution. Label legitimate accounts too: a corpus of only-fraud labels
480
+ * teaches a model nothing about the boundary.
481
+ */
482
+ submit(req: LabelRequest): Promise<OkResponse>;
483
+ fraud(accountId: string, notes?: string): Promise<OkResponse>;
484
+ legitimate(accountId: string, notes?: string): Promise<OkResponse>;
485
+ }
486
+
487
+ interface VectoralOptions {
488
+ /**
489
+ * API key (`vg_live_…`), sent as `Authorization: Bearer`. Defaults to
490
+ * `process.env.VECTORAL_API_KEY`. Provide exactly one of `apiKey` or
491
+ * `customerId`.
492
+ */
493
+ apiKey?: string;
494
+ /**
495
+ * Customer identifier, sent as `X-Customer-ID`. Only for a self-hosted or
496
+ * in-VPC deployment running in header auth mode.
497
+ */
498
+ customerId?: string;
499
+ /** API base URL. Defaults to `VECTORAL_BASE_URL`, then the hosted endpoint. */
500
+ baseUrl?: string;
501
+ /** Per-request timeout in ms. Default 5000. */
502
+ timeoutMs?: number;
503
+ /** Custom fetch implementation. Defaults to global fetch. */
504
+ fetch?: FetchLike;
505
+ /** Extra headers merged into every request. */
506
+ headers?: Record<string, string>;
507
+ /**
508
+ * Retry attempts for transient failures. Default 2.
509
+ *
510
+ * **Only applied to calls that carry an `event_id`.** Every write endpoint
511
+ * mints a new row per call, so retrying a request without an idempotency key
512
+ * would double-write after a response lost in transit. Supplying `event_id`
513
+ * is what buys you retries.
514
+ */
515
+ retries?: number;
516
+ /**
517
+ * Return a safe default instead of throwing when a scoring call fails.
518
+ * Default true. Applies to `registrations.score()` and `inference.score()`;
519
+ * results carry `degraded: true`. Telemetry and label calls always throw —
520
+ * you want to know when those are being dropped.
521
+ */
522
+ failOpen?: boolean;
523
+ /** Privacy-preserving prompt fingerprinting. Off unless `enabled`. */
524
+ fingerprint?: FingerprintOptions$1;
525
+ /** Called for every fail-open failure. Default: none (silent). Wire this up. */
526
+ onError?: (err: VectoralError, context: string) => void;
527
+ /** Called for non-fatal configuration problems. Defaults to `console.warn`. */
528
+ onWarning?: (message: string) => void;
529
+ }
530
+ /**
531
+ * Server-side Vectoral client.
532
+ *
533
+ * Use this on your backend only. It holds a secret API key, and the signals it
534
+ * sends — above all the client IP — are only trustworthy when your own server
535
+ * observed them.
536
+ */
537
+ declare class Vectoral {
538
+ readonly registrations: Registrations;
539
+ readonly inference: Inference;
540
+ readonly identity: Identity;
541
+ readonly labels: Labels;
542
+ /** True when prompt fingerprinting resolved to a usable salt. */
543
+ readonly fingerprintingActive: boolean;
544
+ constructor(opts?: VectoralOptions);
545
+ }
546
+ /** Convenience factory equivalent to `new Vectoral(opts)`. */
547
+ declare const createClient: (opts?: VectoralOptions) => Vectoral;
548
+
549
+ interface FingerprintOptions {
550
+ /** Also compute the MinHash signature + band keys. */
551
+ minHash: boolean;
552
+ /** Also compute the unsalted global SimHash (cross-customer opt-in). */
553
+ global: boolean;
554
+ }
555
+ interface ComputedFingerprint {
556
+ /** True when the prompt is under the 8-token floor; no other field is set. */
557
+ tooShort: boolean;
558
+ simhash?: string;
559
+ exactHash?: string;
560
+ minhashB64?: string;
561
+ bandKeys?: string[];
562
+ simhashGlobal?: string;
563
+ }
564
+ /** Run the full normative pipeline over text with the tenant salt. */
565
+ declare function computeFingerprint(text: string, salt: string, opts: FingerprintOptions): ComputedFingerprint;
566
+ /**
567
+ * Fallback conversation key derived from the first user message:
568
+ * "c_" + hex64(XXH64(normalized text, tenant seed)).
569
+ */
570
+ declare function conversationKey(firstUserMessage: string, salt: string): string;
571
+
572
+ export { type AccountBlock, type ComputedFingerprint, type FetchLike, type FingerprintOptions$1 as FingerprintOptions, Identity, type IdentityEvent, type IdentityEventType, Inference, type Label, type LabelRequest, Labels, MIN_SALT_LENGTH, type OkResponse, type PostCallEvent, type PromptFingerprintBlock, type ReasonCode, type RegistrationClientBlock, type RegistrationFormBlock, type RegistrationFormField, type RegistrationRequest, RegistrationTier, type RegistrationVerdict, Registrations, type RequestBlock, type ScoreRequest, type ScoreResponse, type SessionSignals, type Tier, Vectoral, VectoralConfigError, VectoralError, type VectoralErrorCode, type VectoralOptions, computeFingerprint, conversationKey, createClient, generateSalt, suggestSaltId };