@prampta/sdk 0.9.1 → 0.10.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/dist/index.js CHANGED
@@ -42,21 +42,27 @@ __export(index_exports, {
42
42
  PramptaSchemaError: () => PramptaSchemaError,
43
43
  PramptaSignatureError: () => PramptaSignatureError,
44
44
  PramptaTimeoutError: () => PramptaTimeoutError,
45
+ PregenError: () => PregenError,
45
46
  REFUSAL_DESCRIPTIONS: () => REFUSAL_DESCRIPTIONS,
46
47
  RegistryDirectoryError: () => RegistryDirectoryError,
47
48
  SOFT_REFUSAL_CODES: () => SOFT_REFUSAL_CODES,
48
49
  TrustedRegistryDirectory: () => TrustedRegistryDirectory,
49
50
  canonicalJson: () => canonicalJson,
51
+ createPregen: () => createPregen,
50
52
  hashPrompt: () => hashPrompt,
51
53
  loadPregenDirectory: () => import_verify2.loadDirectory,
52
54
  matchSubjects: () => matchSubjects,
55
+ memoryStore: () => memoryStore,
56
+ messageId: () => messageId,
53
57
  normalizeForMatch: () => normalizeForMatch,
58
+ postgresStore: () => postgresStore,
59
+ signingInput: () => signingInput,
54
60
  singleProcessJournal: () => singleProcessJournal,
55
61
  verifyLicenseSignatures: () => verifyLicenseSignatures,
56
62
  verifyMerkleProof: () => verifyMerkleProof
57
63
  });
58
64
  module.exports = __toCommonJS(index_exports);
59
- var ed3 = __toESM(require("@noble/ed25519"));
65
+ var ed4 = __toESM(require("@noble/ed25519"));
60
66
 
61
67
  // src/canonical.ts
62
68
  function canonicalJson(obj) {
@@ -268,9 +274,9 @@ var TrustedRegistryDirectory = class _TrustedRegistryDirectory {
268
274
  // src/license.ts
269
275
  var ed2 = __toESM(require("@noble/ed25519"));
270
276
  async function verifyLicenseSignatures(input) {
271
- const bytes = (hex, size) => {
272
- if (typeof hex !== "string" || hex.length !== size * 2 || !/^[0-9a-fA-F]+$/.test(hex)) throw new TypeError("Invalid hex");
273
- return Uint8Array.from(hex.match(/../g), (s) => parseInt(s, 16));
277
+ const bytes = (hex2, size) => {
278
+ if (typeof hex2 !== "string" || hex2.length !== size * 2 || !/^[0-9a-fA-F]+$/.test(hex2)) throw new TypeError("Invalid hex");
279
+ return Uint8Array.from(hex2.match(/../g), (s) => parseInt(s, 16));
274
280
  };
275
281
  let body;
276
282
  let subjectSig;
@@ -410,6 +416,376 @@ var PramptaReporter = class {
410
416
  }
411
417
  };
412
418
 
419
+ // src/pregen.ts
420
+ var ed3 = __toESM(require("@noble/ed25519"));
421
+ var PregenError = class extends Error {
422
+ constructor(code, message) {
423
+ super(`${code}: ${message}`);
424
+ this.code = code;
425
+ this.name = "PregenError";
426
+ }
427
+ code;
428
+ };
429
+ var PROFILE = "likeness/1";
430
+ var MAX_LIFETIME = 900;
431
+ var CLOCK_TOLERANCE = 60;
432
+ var JOB_ID = /^[A-Za-z0-9_.:-]{1,100}$/;
433
+ var HEX64 = /^[0-9a-f]{64}$/;
434
+ var STATUSES = /* @__PURE__ */ new Set(["allow", "deny", "review", "not_blocked"]);
435
+ var enc = new TextEncoder();
436
+ var now = () => Math.floor(Date.now() / 1e3);
437
+ var hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
438
+ var unhex = (s) => {
439
+ if (!/^(?:[0-9a-fA-F]{2})+$/.test(s)) throw new PregenError("PG_MALFORMED", "not hex");
440
+ return Uint8Array.from(s.match(/../g), (h) => parseInt(h, 16));
441
+ };
442
+ async function sha2562(data) {
443
+ return hex(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", data)));
444
+ }
445
+ function signingInput(type, body) {
446
+ return enc.encode(`PREGEN-V5\0${type}\0${canonicalJson(body)}`);
447
+ }
448
+ function messageId(type, body) {
449
+ return sha2562(enc.encode(`PREGEN-V5-ID\0${type}\0${canonicalJson(body)}`));
450
+ }
451
+ var keyIdOf = async (pub) => "pg-ed25519:" + (await sha2562(pub)).slice(0, 32);
452
+ async function verifyEnvelope(env, type, role, keys) {
453
+ const e = env;
454
+ if (!e || typeof e !== "object" || Object.keys(e).sort().join() !== "body,signatures,type" || e.type !== type || !e.body || typeof e.body !== "object" || !Array.isArray(e.signatures)) {
455
+ throw new PregenError("PG_MALFORMED", `not a ${type}`);
456
+ }
457
+ const s = e.body.signers;
458
+ const pub = Array.isArray(s) && s.length === 1 && s[0]?.role === role && e.signatures.length === 1 ? keys.get(s[0].key_id) : void 0;
459
+ if (!pub) throw new PregenError("PG_SIGNATURE", `not signed by a pinned ${role} key`);
460
+ let ok = false;
461
+ try {
462
+ ok = await ed3.verifyAsync(unhex(e.signatures[0]), signingInput(type, e.body), pub);
463
+ } catch {
464
+ }
465
+ if (!ok) throw new PregenError("PG_SIGNATURE", `${type} signature does not verify`);
466
+ return e.body;
467
+ }
468
+ async function checkAnswer(body, request, at) {
469
+ if (Array.isArray(body.critical) && body.critical.length) throw new PregenError("PG_CRITICAL", body.critical.join());
470
+ if (!(body.expires_at - body.issued_at > 0 && body.expires_at - body.issued_at <= MAX_LIFETIME)) throw new PregenError("PG_TIME", "lifetime");
471
+ if (body.issued_at > at + CLOCK_TOLERANCE) throw new PregenError("PG_TIME", "issued in the future");
472
+ if (body.request_id !== await messageId("pg.request.v1", request)) throw new PregenError("PG_BINDING", "answer is for another request");
473
+ if (body.profile !== request.profile) throw new PregenError("PG_BINDING", "profile");
474
+ if (!STATUSES.has(body.status)) throw new PregenError("PG_MALFORMED", "status");
475
+ const shaped = body.status === "allow" ? "basis" in body && !("reason" in body) : "reason" in body && !("basis" in body);
476
+ if (!shaped) throw new PregenError("PG_MALFORMED", "an allow has a basis and no reason; a refusal the reverse");
477
+ }
478
+ function createPregen(config) {
479
+ const url = new URL(config.baseUrl);
480
+ const local = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
481
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && local)) throw new PregenError("PG_CONFIG", "use an HTTPS registry URL");
482
+ if (!config.providerId || !config.token) throw new PregenError("PG_CONFIG", "providerId and token are required");
483
+ if (!config.store) throw new PregenError("PG_CONFIG", "a store is required (postgresStore(pool), or memoryStore() for one process)");
484
+ const base = url.origin, mode = config.mode ?? "enforce", timeoutMs = config.timeoutMs ?? 5e3;
485
+ const providerId = config.providerId, store = config.store;
486
+ const secret = unhex(config.providerSigningKeyHex);
487
+ if (secret.length !== 32) throw new PregenError("PG_CONFIG", "providerSigningKeyHex must be a 32-byte Ed25519 private key");
488
+ const pins = (Array.isArray(config.operatorPublicKeyHex) ? config.operatorPublicKeyHex : config.operatorPublicKeyHex.split(/[\s,]+/)).filter(Boolean);
489
+ if (!pins.length) throw new PregenError("PG_CONFIG", "operatorPublicKeyHex is required");
490
+ const ready = (async () => {
491
+ const registry = /* @__PURE__ */ new Map();
492
+ for (const p of pins) registry.set(await keyIdOf(unhex(p)), unhex(p));
493
+ const pub = await ed3.getPublicKeyAsync(secret);
494
+ return { registry, pub, keyId: await keyIdOf(pub) };
495
+ })();
496
+ async function post(path, payload) {
497
+ const resp = await fetch(base + path, {
498
+ method: "POST",
499
+ redirect: "error",
500
+ signal: AbortSignal.timeout(timeoutMs),
501
+ body: JSON.stringify(payload),
502
+ headers: { "Content-Type": "application/json", "X-Provider-ID": providerId, Authorization: `Bearer ${config.token}` }
503
+ });
504
+ let data = null;
505
+ try {
506
+ data = await resp.json();
507
+ } catch {
508
+ }
509
+ return { status: resp.status, data };
510
+ }
511
+ const errorCode = (r) => r.data?.detail?.error ?? `HTTP_${r.status}`;
512
+ async function seal(type, body) {
513
+ const { keyId } = await ready;
514
+ body = { ...body, signers: [{ role: "provider", key_id: keyId }] };
515
+ return { type, body, signatures: [hex(await ed3.signAsync(signingInput(type, body), secret))] };
516
+ }
517
+ async function ask(request) {
518
+ const { registry } = await ready;
519
+ let r = null;
520
+ for (let attempt = 0; attempt < 3; attempt++) {
521
+ if (attempt) await new Promise((ok) => setTimeout(ok, 200 * 2 ** attempt));
522
+ try {
523
+ r = await post("/v2/requests", { type: "pg.request.v1", body: request, signatures: [] });
524
+ } catch {
525
+ r = null;
526
+ continue;
527
+ }
528
+ if (r.status !== 429 && r.status < 500) break;
529
+ }
530
+ if (!r) return { request, answer: null, error: "PG_UNREACHABLE" };
531
+ if (r.status !== 200) return { request, answer: null, error: errorCode(r) };
532
+ const answer = r.data?.answer;
533
+ try {
534
+ await verifyEnvelope(answer, "pg.answer.v1", "registry", registry);
535
+ } catch (e) {
536
+ return { request, answer: null, error: e instanceof PregenError ? e.code : "PG_MALFORMED" };
537
+ }
538
+ try {
539
+ await checkAnswer(answer.body, request, now());
540
+ return { request, answer };
541
+ } catch (e) {
542
+ const code = e.code;
543
+ return code === "PG_BINDING" ? { request, answer: null, error: code } : { request, answer, error: code };
544
+ }
545
+ }
546
+ const usableAllow = (a, at) => !a.error && a.answer?.body.status === "allow" && a.answer.body.expires_at > at;
547
+ async function report(a, outcome, outputHash, applied) {
548
+ const answer = a.answer ? await messageId("pg.answer.v1", a.answer.body) : null;
549
+ const required = new Set((a.answer?.body.obligations ?? []).map((o) => o.id));
550
+ const body = {
551
+ request_id: await messageId("pg.request.v1", a.request),
552
+ answer,
553
+ mode: a.request.mode,
554
+ job_id: a.request.job_id,
555
+ outcome,
556
+ output_hash: outcome === "generated" ? outputHash : "",
557
+ obligations_applied: outcome === "generated" ? [...new Set(applied)].filter((id) => required.has(id)).sort() : [],
558
+ occurred_at: now()
559
+ };
560
+ if (answer === null) body.request = a.request;
561
+ return { report: await seal("pg.report.v1", body) };
562
+ }
563
+ async function send(job) {
564
+ const { registry } = await ready;
565
+ for (const r of job.reports) {
566
+ if (r.ack || r.rejected) continue;
567
+ let res;
568
+ try {
569
+ res = await post("/v2/reports", r.report);
570
+ } catch {
571
+ r.error = "PG_UNREACHABLE";
572
+ continue;
573
+ }
574
+ if (res.status === 409 || res.status === 422) {
575
+ r.rejected = true;
576
+ r.error = errorCode(res);
577
+ continue;
578
+ }
579
+ if (res.status !== 200) {
580
+ r.error = errorCode(res);
581
+ continue;
582
+ }
583
+ try {
584
+ const ack = await verifyEnvelope(res.data?.ack, "pg.report_ack.v1", "registry", registry);
585
+ if (ack.report !== await messageId("pg.report.v1", r.report.body)) throw new PregenError("PG_BINDING", "ack for another report");
586
+ r.ack = res.data.ack;
587
+ delete r.error;
588
+ } catch (e) {
589
+ r.error = e.code ?? "PG_MALFORMED";
590
+ }
591
+ }
592
+ if (job.reports.every((r) => r.ack || r.rejected)) job.stage = "done";
593
+ await store.put(job);
594
+ return job;
595
+ }
596
+ function result(job, output, detail) {
597
+ const unknown = job.stage === "claimed" || job.stage === "asked";
598
+ const conflict = job.reports.some((r) => r.rejected);
599
+ return {
600
+ jobId: job.jobId,
601
+ status: unknown || conflict ? "needs_check" : job.stage === "done" ? "confirmed" : "report_pending",
602
+ generated: unknown ? null : !!job.generated,
603
+ ...output !== void 0 ? { output } : {},
604
+ ...job.outputHash ? { outputHash: job.outputHash } : {},
605
+ answers: job.asks.map((a) => ({
606
+ subject: a.request.subject_ref,
607
+ status: a.answer?.body.status ?? null,
608
+ ...a.answer?.body.reason ? { reason: a.answer.body.reason } : {},
609
+ ...a.error ? { error: a.error } : {}
610
+ })),
611
+ unmetObligations: job.unmetObligations ?? [],
612
+ detail: detail ?? (unknown ? "running in another worker, or interrupted: check your generator, then resolve()" : conflict ? "the registry refused a report: see its error" : void 0)
613
+ };
614
+ }
615
+ async function finish(job, generated, outputHash = "", applied = []) {
616
+ const asks = generated ? job.asks : job.asks.filter((a) => a.answer?.body.status === "allow");
617
+ job.reports = await Promise.all(asks.map((a) => report(a, generated ? "generated" : "not_generated", outputHash, applied)));
618
+ job.unmetObligations = generated ? obligationsOf(job).filter((o) => !applied.includes(o.id)) : [];
619
+ Object.assign(job, { stage: "finished", generated, ...generated ? { outputHash } : {} });
620
+ await store.put(job);
621
+ return send(job);
622
+ }
623
+ const obligationsOf = (job) => job.asks.flatMap((a) => a.answer && (mode === "observe" || a.answer.body.status === "allow") ? (a.answer.body.obligations ?? []).map((o) => ({ subject: a.request.subject_ref, id: o.id, params: o.params ?? {} })) : []);
624
+ async function outputHashOf(r) {
625
+ if (r.outputHash !== void 0) {
626
+ if (!HEX64.test(r.outputHash)) throw new PregenError("PG_MALFORMED", "outputHash must be 64 lowercase hex characters");
627
+ return r.outputHash;
628
+ }
629
+ if (r.outputBytes === void 0) throw new PregenError("PG_MALFORMED", "return outputHash or outputBytes");
630
+ return sha2562(typeof r.outputBytes === "string" ? enc.encode(r.outputBytes) : r.outputBytes);
631
+ }
632
+ async function run(input, fn) {
633
+ const subjects = [...new Set(input.subjects)].sort();
634
+ const promptHash = input.promptHash ?? (input.prompt !== void 0 ? await sha2562(enc.encode(input.prompt)) : "");
635
+ if (!HEX64.test(promptHash)) throw new PregenError("PG_MALFORMED", "give prompt or a 64-hex promptHash");
636
+ const use = JSON.parse(JSON.stringify(input.use));
637
+ const manifest = {
638
+ job_id: input.jobId,
639
+ provider_id: providerId,
640
+ licensee_id: input.licenseeId ?? "",
641
+ prompt_hash: promptHash,
642
+ model: input.model,
643
+ modality: input.modality,
644
+ mode,
645
+ asks: subjects.map((s) => ({ subject_ref: s, registry: "", profile: PROFILE, use }))
646
+ };
647
+ const jobHash = await sha2562(enc.encode(canonicalJson(manifest)));
648
+ const fresh = { providerId, jobId: input.jobId, jobHash, stage: "claimed", asks: [], reports: [] };
649
+ const existing = await store.claim(fresh);
650
+ if (existing) {
651
+ if (existing.jobHash !== jobHash) throw new PregenError("PG_JOB_CONFLICT", `job ${input.jobId} was used for another generation`);
652
+ return result(existing.stage === "finished" ? await send(existing) : existing);
653
+ }
654
+ const job = fresh;
655
+ job.asks = await Promise.all(subjects.map((s) => ask({
656
+ job_id: input.jobId,
657
+ job_hash: jobHash,
658
+ subject_ref: s,
659
+ provider_id: providerId,
660
+ licensee_id: manifest.licensee_id,
661
+ prompt_hash: promptHash,
662
+ model: input.model,
663
+ modality: input.modality,
664
+ profile: PROFILE,
665
+ use,
666
+ mode
667
+ })));
668
+ job.stage = "asked";
669
+ await store.put(job);
670
+ const at = now();
671
+ if (mode === "enforce" && !job.asks.every((a) => usableAllow(a, at))) return result(await finish(job, false));
672
+ let out;
673
+ try {
674
+ out = await fn({ obligations: obligationsOf(job) });
675
+ } catch (e) {
676
+ await finish(job, false);
677
+ throw e;
678
+ }
679
+ const outputHash = await outputHashOf(out);
680
+ return result(await finish(job, true, outputHash, out.obligationsApplied ?? []), out.output);
681
+ }
682
+ return {
683
+ /** Ask, verify, generate (only when permitted), report. See the module comment. */
684
+ async generate(input, fn) {
685
+ if (!JOB_ID.test(input?.jobId ?? "")) throw new PregenError("PG_MALFORMED", "jobId: 1-100 of A-Z a-z 0-9 _ . : -");
686
+ if (!Array.isArray(input.subjects) || !input.subjects.length) {
687
+ throw new PregenError("PG_MALFORMED", "subjects: name at least one; with no registered person there is nothing to ask");
688
+ }
689
+ return run(input, fn);
690
+ },
691
+ /** Resend every report still waiting (after a restart, an outage). Run at startup and every minute or so. */
692
+ async flush() {
693
+ const out = [];
694
+ for (const job of await store.pending(providerId)) out.push(result(await send(job)));
695
+ return out;
696
+ },
697
+ /** Where a job stands, or null if this store never saw it. */
698
+ async status(jobId) {
699
+ const job = await store.get(providerId, jobId);
700
+ return job && result(job);
701
+ },
702
+ /** After `needs_check`: record what your generator actually did with this job. */
703
+ async resolve(jobId, outcome) {
704
+ const job = await store.get(providerId, jobId);
705
+ if (!job) throw new PregenError("PG_UNKNOWN_JOB", jobId);
706
+ if (job.stage === "finished" || job.stage === "done") return result(job.stage === "finished" ? await send(job) : job);
707
+ if (!outcome.generated) {
708
+ if (job.stage === "claimed") {
709
+ Object.assign(job, { stage: "done", generated: false });
710
+ await store.put(job);
711
+ return result(job);
712
+ }
713
+ return result(await finish(job, false));
714
+ }
715
+ if (job.stage === "claimed") throw new PregenError("PG_JOB", "generation cannot have started before the answers were stored");
716
+ return result(await finish(job, true, await outputHashOf(outcome), outcome.obligationsApplied ?? []));
717
+ },
718
+ /** Register this provider's signing key with the registry, once (a `first` keyset, §4.9). */
719
+ async registerKey() {
720
+ const { pub, keyId } = await ready;
721
+ const keyset = await seal("pg.keyset.v1", {
722
+ kind: "first",
723
+ provider_id: providerId,
724
+ previous: null,
725
+ recovery_registry: "",
726
+ keys: [{ key_id: keyId, public_key_hex: hex(pub), status: "current" }]
727
+ });
728
+ const r = await post("/v2/keys", keyset);
729
+ if (r.status !== 200) throw new PregenError(errorCode(r), r.data?.detail?.error_description ?? "key not registered");
730
+ return { keyId };
731
+ }
732
+ };
733
+ }
734
+ function memoryStore() {
735
+ const jobs = /* @__PURE__ */ new Map();
736
+ const k = (p, j) => `${p}\0${j}`;
737
+ return {
738
+ async claim(job) {
739
+ const found = jobs.get(k(job.providerId, job.jobId));
740
+ if (found) return structuredClone(found);
741
+ jobs.set(k(job.providerId, job.jobId), structuredClone(job));
742
+ return null;
743
+ },
744
+ async put(job) {
745
+ jobs.set(k(job.providerId, job.jobId), structuredClone(job));
746
+ },
747
+ async get(p, j) {
748
+ const f = jobs.get(k(p, j));
749
+ return f ? structuredClone(f) : null;
750
+ },
751
+ async pending(p) {
752
+ return [...jobs.values()].filter((j) => j.providerId === p && j.stage === "finished").map((j) => structuredClone(j));
753
+ }
754
+ };
755
+ }
756
+ function postgresStore(pool) {
757
+ let ready = null;
758
+ const q = async (text, values) => {
759
+ ready ??= pool.query(`CREATE TABLE IF NOT EXISTS pregen_jobs (
760
+ provider_id text NOT NULL, job_id text NOT NULL, stage text NOT NULL, job jsonb NOT NULL,
761
+ updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (provider_id, job_id))`).catch((e) => {
762
+ ready = null;
763
+ throw e;
764
+ });
765
+ await ready;
766
+ return (await pool.query(text, values)).rows;
767
+ };
768
+ const one = async (p, j) => (await q("SELECT job FROM pregen_jobs WHERE provider_id = $1 AND job_id = $2", [p, j]))[0]?.job ?? null;
769
+ return {
770
+ async claim(job) {
771
+ const won = await q(`INSERT INTO pregen_jobs (provider_id, job_id, stage, job) VALUES ($1, $2, $3, $4)
772
+ ON CONFLICT DO NOTHING RETURNING job_id`, [job.providerId, job.jobId, job.stage, JSON.stringify(job)]);
773
+ return won.length ? null : one(job.providerId, job.jobId);
774
+ },
775
+ async put(job) {
776
+ await q(
777
+ "UPDATE pregen_jobs SET stage = $3, job = $4, updated_at = now() WHERE provider_id = $1 AND job_id = $2",
778
+ [job.providerId, job.jobId, job.stage, JSON.stringify(job)]
779
+ );
780
+ },
781
+ get: one,
782
+ async pending(p) {
783
+ return (await q(`SELECT job FROM pregen_jobs WHERE provider_id = $1 AND stage = 'finished'
784
+ ORDER BY updated_at LIMIT 100`, [p])).map((r) => r.job);
785
+ }
786
+ };
787
+ }
788
+
413
789
  // src/index.ts
414
790
  function singleProcessJournal() {
415
791
  const entries = /* @__PURE__ */ new Map();
@@ -554,16 +930,16 @@ function envVar(key) {
554
930
  return "";
555
931
  }
556
932
  async function hashPrompt(prompt) {
557
- return sha2562(prompt);
933
+ return sha2563(prompt);
558
934
  }
559
- async function sha2562(input) {
935
+ async function sha2563(input) {
560
936
  const data = new TextEncoder().encode(input);
561
937
  return sha256Bytes(data);
562
938
  }
563
939
  async function verifyMerkleProof(leafHash, proof, rootHash) {
564
940
  let current = leafHash;
565
941
  for (const step of proof) {
566
- current = step.position === "right" ? await sha2562(current + step.hash) : await sha2562(step.hash + current);
942
+ current = step.position === "right" ? await sha2563(current + step.hash) : await sha2563(step.hash + current);
567
943
  }
568
944
  return current === rootHash;
569
945
  }
@@ -583,13 +959,13 @@ async function sha256Bytes(data) {
583
959
  const { createHash } = await import("crypto");
584
960
  return createHash("sha256").update(data).digest("hex");
585
961
  }
586
- function hexToBytes(hex) {
587
- if (typeof hex !== "string" || !/^(?:[0-9a-fA-F]{2})+$/.test(hex)) {
962
+ function hexToBytes(hex2) {
963
+ if (typeof hex2 !== "string" || !/^(?:[0-9a-fA-F]{2})+$/.test(hex2)) {
588
964
  throw new PramptaSignatureError("Invalid hexadecimal key or signature");
589
965
  }
590
- const bytes = new Uint8Array(hex.length / 2);
591
- for (let i = 0; i < hex.length; i += 2) {
592
- bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
966
+ const bytes = new Uint8Array(hex2.length / 2);
967
+ for (let i = 0; i < hex2.length; i += 2) {
968
+ bytes[i / 2] = parseInt(hex2.substring(i, i + 2), 16);
593
969
  }
594
970
  return bytes;
595
971
  }
@@ -741,7 +1117,7 @@ var Prampta = class _Prampta {
741
1117
  if (!currentHex || !sig) throw new PramptaSignatureError("Operator key set from /keys is malformed");
742
1118
  const body = { ...ks };
743
1119
  delete body.signature;
744
- const ok = await ed3.verifyAsync(hexToBytes(sig), new TextEncoder().encode(canonicalJson(body)), hexToBytes(currentHex));
1120
+ const ok = await ed4.verifyAsync(hexToBytes(sig), new TextEncoder().encode(canonicalJson(body)), hexToBytes(currentHex));
745
1121
  if (!ok) throw new PramptaSignatureError("Operator key set signature is invalid");
746
1122
  this.keySetCache = ks;
747
1123
  return ks;
@@ -756,8 +1132,8 @@ var Prampta = class _Prampta {
756
1132
  * from the self-consistent /keys set with a warning.
757
1133
  */
758
1134
  async resolveOperatorKey(keyId) {
759
- for (const hex of this.pinnedKeys) {
760
- if (await keyFingerprint(hex) === keyId) return hex;
1135
+ for (const hex2 of this.pinnedKeys) {
1136
+ if (await keyFingerprint(hex2) === keyId) return hex2;
761
1137
  }
762
1138
  if (this.pinnedKeys.length > 0) {
763
1139
  throw new PramptaSignatureError(
@@ -790,11 +1166,11 @@ var Prampta = class _Prampta {
790
1166
  * copy of the subject index. Call verify()/assertAllowed() per hit.
791
1167
  */
792
1168
  async matchSubjects(text, opts) {
793
- const now = Date.now();
794
- if (opts?.forceRefresh || !this.subjectIndexEntries || now - this.subjectIndexFetchedAt > _Prampta.SUBJECT_INDEX_TTL_MS) {
1169
+ const now2 = Date.now();
1170
+ if (opts?.forceRefresh || !this.subjectIndexEntries || now2 - this.subjectIndexFetchedAt > _Prampta.SUBJECT_INDEX_TTL_MS) {
795
1171
  const payload = await this.fetchSubjectIndex();
796
1172
  this.subjectIndexEntries = payload.subjects || [];
797
- this.subjectIndexFetchedAt = now;
1173
+ this.subjectIndexFetchedAt = now2;
798
1174
  }
799
1175
  return matchSubjects(text, this.subjectIndexEntries);
800
1176
  }
@@ -882,7 +1258,7 @@ var Prampta = class _Prampta {
882
1258
  const sigBytes = hexToBytes(sig);
883
1259
  let valid;
884
1260
  try {
885
- valid = await ed3.verifyAsync(sigBytes, messageBytes, pubKeyBytes);
1261
+ valid = await ed4.verifyAsync(sigBytes, messageBytes, pubKeyBytes);
886
1262
  } catch (e) {
887
1263
  throw new PramptaSignatureError(`Ed25519 verification error: ${e}`);
888
1264
  }
@@ -891,13 +1267,13 @@ var Prampta = class _Prampta {
891
1267
  "Operator signature is invalid \u2014 decision cannot be trusted. This may indicate a compromised backend, proxy tampering, or key mismatch."
892
1268
  );
893
1269
  }
894
- const now = Math.floor(Date.now() / 1e3);
895
- if (!Number.isSafeInteger(raw.expires_at) || !Number.isSafeInteger(raw.issued_at) || raw.issued_at <= 0 || raw.expires_at <= raw.issued_at || raw.expires_at - raw.issued_at > 900 || raw.issued_at > now + 60) {
1270
+ const now2 = Math.floor(Date.now() / 1e3);
1271
+ if (!Number.isSafeInteger(raw.expires_at) || !Number.isSafeInteger(raw.issued_at) || raw.issued_at <= 0 || raw.expires_at <= raw.issued_at || raw.expires_at - raw.issued_at > 900 || raw.issued_at > now2 + 60) {
896
1272
  throw new PramptaSchemaError("Decision has invalid validity timestamps");
897
1273
  }
898
- if (decision.expiresAt <= now) {
1274
+ if (decision.expiresAt <= now2) {
899
1275
  throw new PramptaSignatureError(
900
- `Decision expired at ${decision.expiresAt}, current time ${now}`
1276
+ `Decision expired at ${decision.expiresAt}, current time ${now2}`
901
1277
  );
902
1278
  }
903
1279
  }
@@ -983,7 +1359,7 @@ var Prampta = class _Prampta {
983
1359
  } : void 0 };
984
1360
  this.registryDirectory?.assertEndpoint(request.subjectId, this.baseUrl);
985
1361
  this.assertPregenEndpoint(request.subjectId);
986
- const promptHash = request.promptHash || (request.prompt ? await sha2562(request.prompt) : null);
1362
+ const promptHash = request.promptHash || (request.prompt ? await sha2563(request.prompt) : null);
987
1363
  if (!promptHash) {
988
1364
  throw new PramptaSchemaError(
989
1365
  "Either prompt or promptHash is required. SDK does not allow empty prompt hashes to prevent unbound decisions."
@@ -1160,7 +1536,7 @@ var Prampta = class _Prampta {
1160
1536
  /** SHA-256 of everything that defines the job. Same id + different hash = a different job. */
1161
1537
  async requestHash(request) {
1162
1538
  const { generationId, subjectIds, prompt, promptHash, idempotencyKey, returnUrl, detectionId, ...rest } = request;
1163
- return sha2562(canonicalJson(JSON.parse(JSON.stringify({
1539
+ return sha2563(canonicalJson(JSON.parse(JSON.stringify({
1164
1540
  ...rest,
1165
1541
  generationId,
1166
1542
  subjectIds: [...new Set(subjectIds)].sort(),
@@ -1318,7 +1694,7 @@ var Prampta = class _Prampta {
1318
1694
  provider_id: this.providerId,
1319
1695
  event_type: eventType
1320
1696
  };
1321
- const signature = await ed3.signAsync(
1697
+ const signature = await ed4.signAsync(
1322
1698
  new TextEncoder().encode(canonicalJson(body)),
1323
1699
  hexToBytes(keyHex)
1324
1700
  );
@@ -1374,7 +1750,7 @@ var Prampta = class _Prampta {
1374
1750
  return this.get("/version");
1375
1751
  }
1376
1752
  static async hashPrompt(prompt) {
1377
- return sha2562(prompt);
1753
+ return sha2563(prompt);
1378
1754
  }
1379
1755
  // ── Internal ─────────────────────────────────────────────────────────
1380
1756
  parseDecision(data) {
@@ -1514,15 +1890,21 @@ function sameEndpoint(a, b) {
1514
1890
  PramptaSchemaError,
1515
1891
  PramptaSignatureError,
1516
1892
  PramptaTimeoutError,
1893
+ PregenError,
1517
1894
  REFUSAL_DESCRIPTIONS,
1518
1895
  RegistryDirectoryError,
1519
1896
  SOFT_REFUSAL_CODES,
1520
1897
  TrustedRegistryDirectory,
1521
1898
  canonicalJson,
1899
+ createPregen,
1522
1900
  hashPrompt,
1523
1901
  loadPregenDirectory,
1524
1902
  matchSubjects,
1903
+ memoryStore,
1904
+ messageId,
1525
1905
  normalizeForMatch,
1906
+ postgresStore,
1907
+ signingInput,
1526
1908
  singleProcessJournal,
1527
1909
  verifyLicenseSignatures,
1528
1910
  verifyMerkleProof