@prampta/sdk 0.3.2 → 0.7.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,615 @@
1
+ import { Directory } from '@pregen/verify';
2
+ export { Directory as PregenDirectory, loadDirectory as loadPregenDirectory } from '@pregen/verify';
3
+
4
+ /** PRE-GEN v5 serialization. Do not silently replace it with JCS: existing
5
+ * signatures use these bytes. A new canonicalization needs a new version. */
6
+ declare function canonicalJson(obj: unknown): string;
7
+
8
+ declare class RegistryDirectoryError extends Error {
9
+ constructor(message: string);
10
+ }
11
+ interface RegistryDirectoryOptions {
12
+ /** Trust anchor obtained independently, NOT from the directory itself. */
13
+ stewardPublicKeyHex: string;
14
+ /** Highest accepted sequence, persisted by the application across restarts. */
15
+ minSequence?: number;
16
+ /** Last accepted state from durable storage; detects same-sequence forks. */
17
+ previousSnapshot?: {
18
+ sequence: number;
19
+ hash: string;
20
+ };
21
+ }
22
+ /** Immutable, signature-verified directory snapshot. No network discovery,
23
+ * automatic root replacement, registry accreditation or rights attestation. */
24
+ declare class TrustedRegistryDirectory {
25
+ #private;
26
+ readonly sequence: number;
27
+ readonly snapshotHash: string;
28
+ private constructor();
29
+ static verify(input: unknown, options: RegistryDirectoryOptions): Promise<TrustedRegistryDirectory>;
30
+ private registryFor;
31
+ /** V-11: checked before forwarding a provider's credential to an endpoint. */
32
+ assertEndpoint(subject: string, baseUrl: string): void;
33
+ /** V-10: use only AFTER independently verifying the object's signature. */
34
+ assertSigner(code: string, keyId: string): void;
35
+ assertDecision(subject: string, license: string | null, keyId: string, baseUrl: string): void;
36
+ }
37
+
38
+ interface LicenseSignatureInput {
39
+ body: Record<string, unknown>;
40
+ licenseId: string;
41
+ subjectPublicKeyHex: string;
42
+ subjectSignatureHex: string;
43
+ operatorPublicKeyHex: string;
44
+ operatorSignatureHex: string;
45
+ }
46
+ /** V-9 crypto check only. Obtain keys independently and check namespace,
47
+ * authority, status and scope separately; true is NOT a proof of consent. */
48
+ declare function verifyLicenseSignatures(input: LicenseSignatureInput): Promise<{
49
+ subjectValid: boolean;
50
+ operatorValid: boolean;
51
+ }>;
52
+
53
+ /** Connect Step 1. Server-side only; reporting NEVER authorizes generation. */
54
+ interface Observation {
55
+ schema_version: "pg.observation.v1";
56
+ event_id: string;
57
+ output_id: string;
58
+ subject_id: string;
59
+ event_type: "output.created" | "output.modified" | "output.published" | "publication.removed";
60
+ output_hash: string;
61
+ occurred_at: string;
62
+ declared_purpose: "personal" | "commercial" | "educational" | "research" | "editorial" | "unknown";
63
+ parent_output_hash?: string;
64
+ publication_url?: string;
65
+ }
66
+ interface ReportInput {
67
+ /** Stable provider job/event key. Different publications/edits need different keys. */
68
+ eventKey: string;
69
+ outputId: string;
70
+ /** Provider-confirmed association, NOT automatically a text matcher hit. */
71
+ subjectId: string;
72
+ /** Original event time, retained across retries. */
73
+ occurredAt: string;
74
+ eventType?: Observation["event_type"];
75
+ purpose?: Observation["declared_purpose"];
76
+ /** Actual output bytes or text, never a URL to be downloaded. Stays local. */
77
+ output?: Uint8Array | string;
78
+ /** For large files, compute incrementally in your storage pipeline. */
79
+ outputHash?: string;
80
+ parentOutputHash?: string;
81
+ publicationUrl?: string;
82
+ }
83
+ interface ObservationOutbox {
84
+ /** Persist atomically under event_id; same payload is a no-op, changed payload
85
+ * must reject. Namespace storage by provider AND environment. Resolve only
86
+ * after durable commit. Implement using the provider's existing DB/job queue. */
87
+ put(event: Observation): Promise<void>;
88
+ }
89
+ type DeliveryResult = {
90
+ status: "accepted";
91
+ observationId: string;
92
+ } | {
93
+ status: "retry";
94
+ httpStatus?: number;
95
+ retryAfterMs: number;
96
+ } | {
97
+ status: "rejected";
98
+ httpStatus: number;
99
+ };
100
+ declare class PramptaReporter {
101
+ private readonly config;
102
+ private readonly baseUrl;
103
+ private readonly timeoutMs;
104
+ constructor(config: {
105
+ baseUrl: string;
106
+ providerId: string;
107
+ token: string;
108
+ outbox: ObservationOutbox;
109
+ timeoutMs?: number;
110
+ });
111
+ /** No PRAMPTA network call: hash locally and commit to the durable outbox.
112
+ * A storage failure is surfaced, never reported as success. Run from the
113
+ * provider's generation-completed job, not as an authorization gate. */
114
+ report(input: ReportInput): Promise<{
115
+ status: "queued";
116
+ eventId: string;
117
+ }>;
118
+ /** One bounded attempt by your queue worker. Keep/retry on `retry`, quarantine
119
+ * on `rejected`, acknowledge only `accepted`. Never changes /verify behavior. */
120
+ deliver(event: Observation): Promise<DeliveryResult>;
121
+ }
122
+
123
+ /**
124
+ * PRAMPTA SDK for TypeScript / Node.js
125
+ *
126
+ * Pre-generation authorization for AI content.
127
+ * Verifies operator Ed25519 signatures on decisions — not a blind HTTP wrapper.
128
+ *
129
+ * @example Basic verification
130
+ * ```ts
131
+ * import { Prampta } from "@prampta/sdk";
132
+ *
133
+ * const pg = new Prampta({
134
+ * baseUrl: "https://api2.prampta.com",
135
+ * providerId: "my-ai-service",
136
+ * licenseeId: "lic-sandbox", // synthetic sandbox fixture only
137
+ * // Your runtime credential's `exchange_secret` — from your provider
138
+ * // application's verify-email/verify-domain response (see the Provider
139
+ * // Connect Guide). NOT the legacy shared pair token; see `token` below.
140
+ * token: process.env.PRAMPTA_TOKEN,
141
+ * });
142
+ *
143
+ * // Throws if denied — fail-closed by default
144
+ * await pg.assertAllowed("sbx-allowed", {
145
+ * prompt: "A sandbox portrait",
146
+ * modality: "image",
147
+ * model: "your-model",
148
+ * intendedUse: { useCase: "research" },
149
+ * });
150
+ * ```
151
+ */
152
+
153
+ interface PramptaConfig {
154
+ /** The PRAMPTA registry URL. Falls back to PRAMPTA_BASE_URL env var. */
155
+ baseUrl?: string;
156
+ /** Your provider ID. Falls back to PRAMPTA_PROVIDER_ID env var. */
157
+ providerId?: string;
158
+ /** The licensee ID (`prampta_licensee_id` from Connect AI). Falls back to
159
+ * PRAMPTA_LICENSEE_ID. Optional: without it, verify is a provider-level
160
+ * check that can only answer `not_blocked` for `useCase: "personal"`. */
161
+ licenseeId?: string;
162
+ /**
163
+ * The runtime bearer credential. This is your `ProviderCredential`'s
164
+ * `exchange_secret` — issued inline by `verify-email` (sandbox) or
165
+ * `verify-domain/confirm` (production) during provider onboarding.
166
+ * The `prampta_connection_token` returned by Connect AI is NOT accepted
167
+ * for production runtime calls.
168
+ * Falls back to PRAMPTA_TOKEN env var.
169
+ *
170
+ * The legacy shared PAIR TOKEN also works here ONLY while the server is
171
+ * running in `PILOT_MODE` (pre-migration deployments) — production
172
+ * deployments with the trust flags on (the v1.0.0 default) reject it with
173
+ * `PG_NO_PAIR`. Do not build new integrations against the pair token; it
174
+ * exists solely so an already-connected provider keeps working during
175
+ * migration. See docs/PRAMPTA-Provider-Connect-Guide-2026-08.md.
176
+ */
177
+ token?: string;
178
+ /** Request timeout in milliseconds (default 3000). */
179
+ timeoutMs?: number;
180
+ /**
181
+ * Max retry attempts for transient failures (network errors, timeouts,
182
+ * HTTP 429, and 5xx). Default 2 (so up to 3 total attempts). Set 0 to disable.
183
+ * Retries use exponential backoff. Non-transient errors (4xx other than 429,
184
+ * refusals, signature/schema errors) are never retried.
185
+ */
186
+ maxRetries?: number;
187
+ /** Base backoff in ms between retries; doubles each attempt. Default 200. */
188
+ retryBackoffMs?: number;
189
+ /** If true (default), deny on backend errors/timeouts. */
190
+ failClosed?: boolean;
191
+ /**
192
+ * Pinned operator public key(s), hex — the trust anchor for signature
193
+ * verification. Obtain out of band (PRAMPTA docs), not from the API that
194
+ * serves decisions. Pass one, or several (comma/space separated) to pin the
195
+ * current + next key and rotate with zero downtime. Falls back to
196
+ * PRAMPTA_OPERATOR_PUBLIC_KEY. If omitted, verification runs in
197
+ * trust-on-first-use mode (a warning is emitted) — do not do this in prod.
198
+ */
199
+ operatorPublicKeyHex?: string;
200
+ /**
201
+ * Monitor mode: when false, refusals do NOT block generation — the SDK still
202
+ * verifies and records a signed decision, but assertAllowed / withAuthorization
203
+ * proceed instead of throwing. Use for a pilot rollout. Default true.
204
+ */
205
+ enforce?: boolean;
206
+ /**
207
+ * If true (default), verify operator signature on every decision.
208
+ * Set to false ONLY for local development — never in production.
209
+ */
210
+ verifyDecisionSignature?: boolean;
211
+ /**
212
+ * If true, verify() throws PramptaSchemaError LOCALLY — before any network
213
+ * call — when neither providerUserId nor providerIdentityLinkId was
214
+ * supplied. Off by default (most integrations have legitimately unbound
215
+ * calls); turn it on once your integration always acts for a specific end
216
+ * user, so a caller that forgot to pass one fails fast and loud in your own
217
+ * code rather than silently reaching the server as an "unbound" request.
218
+ * Independent of, and a cheaper backstop for, the server-side
219
+ * REQUIRE_PROVIDER_USER_BINDING enforcement — this catches the mistake
220
+ * before spending a round-trip. Default false.
221
+ */
222
+ requireUserBinding?: boolean;
223
+ /** Out-of-band verified namespace directory. Opt-in while the public
224
+ * steward root is not yet published. Requires pinned operator keys. */
225
+ registryDirectory?: TrustedRegistryDirectory;
226
+ /** The PRE-GEN directory from `loadPregenDirectory()`, with the steward key built in.
227
+ * Before a request the SDK checks that `baseUrl` owns the subject's namespace; on an
228
+ * `allow` it also runs @pregen/verify `checkDecision`: the signing key must be one the
229
+ * directory lists for the license's namespace (origin keys pinned), on top of the checks
230
+ * below. Recommended over `registryDirectory`. */
231
+ pregenDirectory?: Directory;
232
+ }
233
+ interface VerifyRequest {
234
+ /** Subject to verify authorization for. */
235
+ subjectId: string;
236
+ /** Raw prompt text — will be hashed, never sent to PRAMPTA. */
237
+ prompt?: string;
238
+ /** Pre-computed SHA-256 hash. Use instead of prompt if you've already hashed. */
239
+ promptHash?: string;
240
+ /** Generation modality: "image", "video", "audio", "text", "3d". */
241
+ modality?: string;
242
+ /** The AI model being used. */
243
+ model?: string;
244
+ /** Intended use metadata. */
245
+ intendedUse?: {
246
+ channel?: string;
247
+ productName?: string;
248
+ projectName?: string;
249
+ categories?: string[];
250
+ territory?: string;
251
+ /** Commercial-model campaign binding — required by premium billing terms. */
252
+ campaignId?: string;
253
+ /** The use-case tier: "personal" | "educational" | "research" |
254
+ * "editorial" | "commercial". Personal use with no licence answers
255
+ * disposition "not_blocked" only when this is declared (NMP Q13). */
256
+ useCase?: string;
257
+ /** Rights requested in addition to the generation modality. */
258
+ rights?: string[];
259
+ };
260
+ /** ── Provider-user binding (prampta.identity.v2) ──
261
+ * WHO you are acting for. Optional, so existing integrations are
262
+ * unaffected — but once supplied it is held to: it must resolve to an
263
+ * identity link PRAMPTA verified, and a licence belonging to a different
264
+ * end user is refused. Supply either identifier (or both — they must then
265
+ * describe the same person). */
266
+ providerUserId?: string;
267
+ providerIdentityLinkId?: string;
268
+ /** Your own id for this generation attempt. Echoed into the decision so a
269
+ * later receipt can be tied to it without a provider-side lookup table. */
270
+ generationId?: string;
271
+ /** Lets you safely retry a preflight you are unsure landed. */
272
+ idempotencyKey?: string;
273
+ /** Links this call to a prior reportDetection()/confirmDetection() advisory
274
+ * record, purely for audit — never used to decide the outcome. */
275
+ detectionId?: string;
276
+ /** Where to send the end user back to after they buy/request a licence via
277
+ * the returned decision's `remediation.url` (set only on PG_NO_LICENSE) —
278
+ * same idea as Connect AI's connect_return_url. Omit to leave them on
279
+ * prampta.com after they act. */
280
+ returnUrl?: string;
281
+ /** Protocol version negotiation: declares the decision schema THIS CALL
282
+ * expects (e.g. "pg.decision.v1"). A mismatch refuses with 409 before any
283
+ * licensing work happens, instead of silently misreading a future schema.
284
+ * Optional — omit to keep today's behavior. Discover the server's current
285
+ * version via GET /version -> protocol_versions.decision_schema. See
286
+ * docs/PRAMPTA-Protocol-Versions.md. */
287
+ expectedSchemaVersion?: string;
288
+ }
289
+ /** Options for the flat `pg.verify(subjectId, options)` convenience call. */
290
+ interface VerifyOptions extends Omit<VerifyRequest, "subjectId"> {
291
+ categories?: string[];
292
+ channel?: string;
293
+ productName?: string;
294
+ projectName?: string;
295
+ territory?: string;
296
+ campaignId?: string;
297
+ useCase?: string;
298
+ rights?: string[];
299
+ }
300
+ interface SignedDecision {
301
+ schemaVersion: string;
302
+ decisionId: string;
303
+ allowed: boolean;
304
+ reason: string | null;
305
+ subjectId: string;
306
+ licenseeId: string;
307
+ providerId: string;
308
+ licenseId: string | null;
309
+ promptHash: string;
310
+ model: string;
311
+ modality: string;
312
+ intendedUse: Record<string, unknown>;
313
+ obligations: Record<string, unknown>;
314
+ rulesText: string;
315
+ rulesTextHash: string;
316
+ watermarkPayload: string | null;
317
+ isHardRefusal: boolean;
318
+ issuedAt: number;
319
+ expiresAt: number;
320
+ operatorKeyId: string;
321
+ operatorSignature: string;
322
+ /** Strongest authority backing the subject: self | agency_asserted |
323
+ * consented | verified. "self" is only the registrant's own claim. */
324
+ subjectAuthority: string;
325
+ denied: boolean;
326
+ /** Outcome (§8): "allow" | "deny" | "review" | "not_blocked". `allowed` stays
327
+ * the boolean; a REVIEW is allowed=false + disposition="review" — hold for a
328
+ * human, not a flat refusal. "not_blocked" (NMP Q13) is personal use with no
329
+ * licence: PRAMPTA does not object but grants nothing (reason
330
+ * PG_STD_TRACKING_ONLY). Older code reading only `allowed` fails closed. */
331
+ disposition: string;
332
+ /** Policy version that produced this decision. */
333
+ policyVersion: string;
334
+ /** §7 freshness controls. `revocationEpoch` is the subject's epoch at
335
+ * issuance — compare it against GET /subjects/{id}/epoch to learn cheaply
336
+ * whether anything happened since (revoke, dispute, lifecycle change)
337
+ * without re-running verify(). `maxCacheAgeSeconds` and `cacheScope` say
338
+ * whether and how long this exact decision may be reused without a live
339
+ * re-check; only a plain ALLOW is ever cacheable. */
340
+ revocationEpoch: number;
341
+ maxCacheAgeSeconds: number;
342
+ cacheScope: string;
343
+ /** Provider-user binding: "verified" — bound to an end-user identity
344
+ * PRAMPTA verified; "unbound" — no end user was asserted; "invalid" — an
345
+ * assertion was made and failed (always accompanies a deny). */
346
+ providerUserBinding: string;
347
+ /** Which identity link was used, when one was. */
348
+ providerIdentityLinkId: string;
349
+ /** Echoed back from the request, for receipt matching. */
350
+ generationId: string;
351
+ /** Echoed back from the request — links to a detection advisory record. */
352
+ detectionId: string;
353
+ /** Set only when reason === "PG_NO_LICENSE": a direct link to buy/request a
354
+ * licence for this exact subject, plus the one canonical explanation text
355
+ * (show it before redirecting — every integration should read the same
356
+ * wording). null on every other outcome, including allow and refusals a
357
+ * licence purchase would not fix. */
358
+ remediation: {
359
+ url: string;
360
+ message: string;
361
+ } | null;
362
+ }
363
+ interface SubjectInfo {
364
+ subjectId: string;
365
+ status: string;
366
+ visibility: string;
367
+ rulesText: string;
368
+ aliases: string[];
369
+ publicKeyHex?: string;
370
+ publicKeyFingerprint?: string;
371
+ registeredAt: string;
372
+ }
373
+ interface LicenseRequestInput {
374
+ subjectId: string;
375
+ useCase: "commercial" | "editorial" | "personal" | "educational" | "research";
376
+ purpose?: string;
377
+ durationDays?: number;
378
+ message?: string;
379
+ }
380
+ interface LicenseRequestResult {
381
+ requestId: string;
382
+ status: string;
383
+ }
384
+ interface OutputMetadata {
385
+ prampta_decision_id: string;
386
+ prampta_license_id: string | null;
387
+ prampta_watermark: string | null;
388
+ prampta_obligations: Record<string, unknown>;
389
+ prampta_issued_at: number;
390
+ }
391
+ declare class PramptaError extends Error {
392
+ constructor(message: string);
393
+ }
394
+ declare class PramptaNetworkError extends PramptaError {
395
+ readonly cause_: unknown;
396
+ constructor(cause_: unknown);
397
+ }
398
+ declare class PramptaTimeoutError extends PramptaError {
399
+ readonly timeoutMs: number;
400
+ constructor(timeoutMs: number);
401
+ }
402
+ declare class PramptaApiError extends PramptaError {
403
+ readonly status: number;
404
+ readonly detail: string;
405
+ constructor(status: number, detail: string);
406
+ }
407
+ declare class PramptaSchemaError extends PramptaError {
408
+ constructor(message: string);
409
+ }
410
+ /** Operator signature verification failed — decision cannot be trusted. */
411
+ declare class PramptaSignatureError extends PramptaError {
412
+ constructor(message: string);
413
+ }
414
+ declare class PramptaRefusalError extends PramptaError {
415
+ readonly decision: SignedDecision;
416
+ constructor(decision: SignedDecision);
417
+ get reason(): string;
418
+ get isHardRefusal(): boolean;
419
+ get decisionId(): string;
420
+ get licenseId(): string | null;
421
+ }
422
+ declare class PramptaFailClosedError extends PramptaError {
423
+ readonly cause_: unknown;
424
+ constructor(cause_: unknown);
425
+ }
426
+ declare const REFUSAL_DESCRIPTIONS: Record<string, string>;
427
+ declare const HARD_REFUSAL_CODES: Set<string>;
428
+ declare const SOFT_REFUSAL_CODES: Set<string>;
429
+ /** SHA-256 of a prompt — what a decision gets bound to. Standalone twin of
430
+ * `Prampta.hashPrompt` so it can be imported directly. */
431
+ declare function hashPrompt(prompt: string): Promise<string>;
432
+ /**
433
+ * Client-side Merkle inclusion-proof verification (PRE-GEN v1.1 addendum
434
+ * §10, Anchored Audit Profile). Mirror of the registry's own
435
+ * `app/core/merkle.py` — same algorithm, same odd-node handling — so a
436
+ * proof from `Prampta.getInclusionProof()` can be checked entirely
437
+ * offline against a root hash you already trust, without the registry
438
+ * being a party to its own audit.
439
+ *
440
+ * `proof` is the array PRAMPTA returns as `inclusion_proof.proof` — each
441
+ * step `{hash: <sibling hash>, position: "left"|"right"}`. Returns true
442
+ * only if `leafHash` is genuinely one of the leaves committed to by
443
+ * `rootHash`.
444
+ */
445
+ declare function verifyMerkleProof(leafHash: string, proof: Array<{
446
+ hash: string;
447
+ position: "left" | "right";
448
+ }>, rootHash: string): Promise<boolean>;
449
+ /** One entry of the /v1/subjects/index detection index. */
450
+ interface SubjectIndexEntry {
451
+ subject_id: string;
452
+ aliases: string[];
453
+ status: string;
454
+ visibility: string;
455
+ }
456
+ /**
457
+ * Canonical normalization for subject matching: lowercase, strip Latin
458
+ * diacritics, de-leet, -/_ → space, strip punctuation, collapse whitespace.
459
+ * Must stay behaviorally identical to the Python SDK's normalize_for_match.
460
+ */
461
+ declare function normalizeForMatch(text: string): string;
462
+ /**
463
+ * Detect which index entries are mentioned in `text`. Whole-word phrase
464
+ * containment, plus a squeezed-substring fallback (min length) that defeats
465
+ * letter-spacing ("a d a") and concatenation ("AdaLovelace"). Baseline layer —
466
+ * text only; image/voice detection is provider-side perceptual work.
467
+ */
468
+ declare function matchSubjects(text: string, entries: SubjectIndexEntry[]): SubjectIndexEntry[];
469
+ declare class Prampta {
470
+ private readonly baseUrl;
471
+ private readonly providerId;
472
+ private readonly licenseeId;
473
+ private readonly token;
474
+ private readonly timeoutMs;
475
+ private readonly maxRetries;
476
+ private readonly retryBackoffMs;
477
+ private readonly failClosed;
478
+ private readonly verifySignature;
479
+ private readonly enforce;
480
+ private readonly requireUserBinding;
481
+ private readonly registryDirectory?;
482
+ private readonly pregenDirectory?;
483
+ private pinnedKeys;
484
+ private keySetCache;
485
+ private keySetPromise;
486
+ private tofuWarned;
487
+ constructor(config?: PramptaConfig);
488
+ /** V-11: a PG code is answered only by the registry that owns its namespace. */
489
+ private assertPregenEndpoint;
490
+ /** Fetch /keys and check it is self-signed by its current key. Cached. */
491
+ private fetchVerifiedKeySet;
492
+ /**
493
+ * Public key hex to verify a decision signed by `keyId`, enforcing the
494
+ * pinning trust model. A pinned match returns immediately (no network); an
495
+ * unpinned key id fails closed in pinned mode; unpinned/TOFU mode resolves
496
+ * from the self-consistent /keys set with a warning.
497
+ */
498
+ private resolveOperatorKey;
499
+ private subjectIndexEntries;
500
+ private subjectIndexFetchedAt;
501
+ private static readonly SUBJECT_INDEX_TTL_MS;
502
+ /** Fetch the raw subject detection index (/v1/subjects/index). */
503
+ fetchSubjectIndex(): Promise<{
504
+ version: number;
505
+ count: number;
506
+ subjects: SubjectIndexEntry[];
507
+ }>;
508
+ /**
509
+ * Detect registered subjects mentioned in `text`, using a TTL-cached
510
+ * copy of the subject index. Call verify()/assertAllowed() per hit.
511
+ */
512
+ matchSubjects(text: string, opts?: {
513
+ forceRefresh?: boolean;
514
+ }): Promise<SubjectIndexEntry[]>;
515
+ /** Record what a local text match found for a prompt, BEFORE the user
516
+ * answers. Purely advisory — creates no entitlement or licence. Pass the
517
+ * returned detectionId to confirmDetection() and optionally to verify(). */
518
+ reportDetection(promptHash: string, candidateSubjectIds: string[]): Promise<{
519
+ detectionId: string;
520
+ status: string;
521
+ candidateSubjectIds: string[];
522
+ confirmedSubjectId: string | null;
523
+ createdAt: string;
524
+ confirmedAt: string | null;
525
+ }>;
526
+ /** Record what the user actually said. `subjectId` undefined/null means
527
+ * they rejected every candidate. Still advisory-only. */
528
+ confirmDetection(detectionId: string, subjectId?: string | null): Promise<{
529
+ detectionId: string;
530
+ status: string;
531
+ candidateSubjectIds: string[];
532
+ confirmedSubjectId: string | null;
533
+ createdAt: string;
534
+ confirmedAt: string | null;
535
+ }>;
536
+ /**
537
+ * Verify operator Ed25519 signature over decision body.
538
+ * Fails closed on any error.
539
+ */
540
+ private verifyDecision;
541
+ /**
542
+ * Verify that the signed decision is bound to the correct request context.
543
+ * Prevents replay attacks where a valid decision for one context is used in another.
544
+ */
545
+ private verifyContextBinding;
546
+ verifyGeneration(request: VerifyRequest): Promise<SignedDecision>;
547
+ /** Flat convenience API mirroring the Python SDK: intended-use fields
548
+ * (categories, channel, productName, …) are accepted at the top level.
549
+ * Either `prompt` or `promptHash` is REQUIRED — decisions are bound to
550
+ * the prompt hash and the SDK refuses to request unbound ones. */
551
+ verify(subjectId: string, options?: VerifyOptions): Promise<SignedDecision>;
552
+ assertAllowed(subjectId: string, options?: Omit<VerifyRequest, "subjectId">): Promise<SignedDecision>;
553
+ /** Strict license-backed path. Unlike assertAllowed, this never accepts
554
+ * reporting-only not_blocked, an internal no-license allow, or monitor mode.
555
+ * It validates an authorization; it does not prove output compliance. */
556
+ assertLicensed(subjectId: string, options?: Omit<VerifyRequest, "subjectId">): Promise<SignedDecision>;
557
+ withAuthorization<T>(request: VerifyRequest, generateFn: (decision: SignedDecision) => Promise<T>): Promise<{
558
+ output: T;
559
+ decision: SignedDecision;
560
+ metadata: OutputMetadata;
561
+ }>;
562
+ getSubject(subjectId: string): Promise<SubjectInfo | null>;
563
+ requestLicense(input: LicenseRequestInput): Promise<LicenseRequestResult>;
564
+ createOutputMetadata(decision: SignedDecision): OutputMetadata;
565
+ /**
566
+ * Report an output after generation. With a registered provider signing key,
567
+ * opt into v3 so eventType is bound to the provider's signature. This is a
568
+ * provider attestation, not proof of complete reporting or actual execution.
569
+ */
570
+ submitReceipt(decision: SignedDecision, result?: {
571
+ outputHash?: string;
572
+ model?: string;
573
+ watermarkEmbedded?: boolean;
574
+ obligationsApplied?: Record<string, unknown>;
575
+ generatedAt?: number;
576
+ eventType?: "preview" | "output_accepted" | "output_delivered" | "output_published";
577
+ /** 32-byte Ed25519 private key, hex; keep server-side. Never sent to PRAMPTA. */
578
+ providerSigningKeyHex?: string;
579
+ }): Promise<Record<string, unknown>>;
580
+ /**
581
+ * Get (issuing if needed) the signed `pg.assertion.v1` for a decision this
582
+ * provider/licensee pair already submitted a receipt for — embed it in
583
+ * your OWN C2PA manifest (PRAMPTA does not build the manifest itself).
584
+ * Idempotent: safe to call again for the same decisionId, always returns
585
+ * the same assertion. Requires `submitReceipt()` to have run first.
586
+ */
587
+ createAssertion(decisionId: string): Promise<Record<string, unknown>>;
588
+ /** Fetch a previously issued assertion. Public — no auth required, since a
589
+ * third party checking a C2PA manifest's embedded assertion needs to
590
+ * verify it without going through PRAMPTA's own auth. */
591
+ getAssertion(decisionId: string): Promise<Record<string, unknown>>;
592
+ /** The most recently published Merkle root over the audit log. Public,
593
+ * unauthenticated. */
594
+ getMerkleRoot(): Promise<Record<string, unknown>>;
595
+ /** Proof that `eventId` is one of the leaves committed to by a Merkle
596
+ * root, without downloading the rest of the log. Check it offline with
597
+ * the standalone `verifyMerkleProof(eventHash, proof, rootHash)` — don't
598
+ * just trust the 200 response; the proof exists so you don't have to.
599
+ * Public, unauthenticated. */
600
+ getInclusionProof(eventId: string): Promise<Record<string, unknown>>;
601
+ health(): Promise<boolean>;
602
+ version(): Promise<Record<string, unknown>>;
603
+ static hashPrompt(prompt: string): Promise<string>;
604
+ private parseDecision;
605
+ private headers;
606
+ /** Transient errors worth retrying: connectivity, timeout, 429, and 5xx. */
607
+ private isRetryable;
608
+ private withRetry;
609
+ private post;
610
+ private postOnce;
611
+ private get;
612
+ private getOnce;
613
+ }
614
+
615
+ export { type DeliveryResult, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, type Observation, type ObservationOutbox, type OutputMetadata, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, REFUSAL_DESCRIPTIONS, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, hashPrompt, matchSubjects, normalizeForMatch, verifyLicenseSignatures, verifyMerkleProof };