@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.
- package/LICENSE +215 -0
- package/README.md +46 -0
- package/dist/index.cjs +895 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +572 -0
- package/dist/index.d.ts +572 -0
- package/dist/index.js +855 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|