@prampta/sdk 0.11.1 → 0.11.3

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/README.md CHANGED
@@ -40,8 +40,8 @@ exists solely so an already-connected provider keeps working mid-migration.
40
40
 
41
41
  `createPregen` speaks the simplified PRE-GEN v5 formats at `/v2`: signed
42
42
  answers bound to your request, signed reports, signed acknowledgements. Set it
43
- up once; wrap each generation in one call. Use 0.11.1 or later: 0.10.0 and
44
- 0.11.0 had defects found by external reviews (see CHANGELOG). A complete
43
+ up once; wrap each generation in one call. Use 0.11.3 or later: earlier versions
44
+ had defects found by external reviews (see CHANGELOG). A complete
45
45
  program you can run as it stands: https://prampta.com/get-started-v2.md.
46
46
 
47
47
  ```typescript
@@ -180,8 +180,9 @@ const result = await pg.generateAuthorized(
180
180
  { subjectIds: [subjectCode], generationId: jobId, promptHash, model, modality: "image",
181
181
  intendedUse: { useCase: "commercial" } },
182
182
  async (decisions) => {
183
- const image = await render(prompt); // apply every decision's obligations
184
- return { output: image, outputBytes: image.bytes };
183
+ const image = await render(prompt, decisions.map((d) => d.obligations)); // apply the obligations yourself
184
+ return { output: image, outputBytes: image.bytes,
185
+ obligationsApplied: { attribution: true } }; // only what you actually did (0.11.3: nothing is assumed)
185
186
  },
186
187
  { journal },
187
188
  );
@@ -267,10 +268,17 @@ const decision = await pg.assertLicensed(subjectCode, {
267
268
  - stores the output hash and the **exact receipts** (signed, if you sign)
268
269
  before sending them, and after a failure resends those receipts byte for
269
270
  byte — receipt options passed on a later call do not replace them;
270
- - a receipt counts as filed only when the registry accepted exactly it:
271
- `unreported` lists receipts to resend (call again with the same id and
272
- request), `conflicts` lists decisions for which the registry holds a
271
+ - a receipt counts as filed only when the registry's answer is about exactly
272
+ it (0.11.3): `recorded` or `already_recorded`, this decision, and the
273
+ `receipt_hash` of these bytes (`checkReceiptAck`; `submitReceipt` checks
274
+ it too); `unreported` lists receipts to resend (call again with the same
275
+ id and request), `conflicts` lists decisions for which the registry holds a
273
276
  different receipt;
277
+ - claims only the obligations your `generate` returns in
278
+ `obligationsApplied` (and `watermarkEmbedded`); with none, the receipt
279
+ says none were applied and PRAMPTA records it as not compliant. Before
280
+ 0.11.3 the SDK copied the decision's obligations into the receipt, a claim
281
+ of work nobody had confirmed;
274
282
  - gives the allows back (`releaseDecision`) when a subject refuses or your
275
283
  `generate` throws; that job is then closed, use a new `generationId`;
276
284
  - never reruns a job that may have started generating.
package/dist/index.d.mts CHANGED
@@ -619,6 +619,17 @@ interface AuthorizedGeneration<T> {
619
619
  * Resending will not help; find out why. */
620
620
  conflicts: SignedDecision[];
621
621
  }
622
+ /** What your `generate` returns to `generateAuthorized`. Report only obligations you carried out: the receipt is a
623
+ * signed statement, and nothing is claimed on your behalf. */
624
+ interface GeneratedOutput<T> {
625
+ output: T;
626
+ outputHash?: string;
627
+ outputBytes?: Uint8Array | string;
628
+ /** The obligations you applied, by name (e.g. `{ attribution: true }`). Default: none. */
629
+ obligationsApplied?: Record<string, unknown>;
630
+ /** Whether you embedded the watermark an obligation asked for. Default: false. */
631
+ watermarkEmbedded?: boolean;
632
+ }
622
633
  interface OutputMetadata {
623
634
  prampta_decision_id: string;
624
635
  prampta_license_id: string | null;
@@ -819,11 +830,7 @@ declare class Prampta {
819
830
  * If a subject refuses or `generate` throws, the allows are given back.
820
831
  * `generate` must throw only when nothing was produced.
821
832
  */
822
- generateAuthorized<T>(request: AuthorizedRequest, generate: (decisions: SignedDecision[]) => Promise<{
823
- output: T;
824
- outputHash?: string;
825
- outputBytes?: Uint8Array | string;
826
- }>, options: {
833
+ generateAuthorized<T>(request: AuthorizedRequest, generate: (decisions: SignedDecision[]) => Promise<GeneratedOutput<T>>, options: {
827
834
  journal: GenerationJournal;
828
835
  receipt?: ReceiptOptions;
829
836
  }): Promise<AuthorizedGeneration<T>>;
@@ -855,6 +862,10 @@ declare class Prampta {
855
862
  /** 32-byte Ed25519 private key, hex; keep server-side. Never sent to PRAMPTA. */
856
863
  providerSigningKeyHex?: string;
857
864
  }): Promise<Record<string, unknown>>;
865
+ /** A receipt counts as filed only when PRAMPTA's answer is about exactly it: `recorded` or `already_recorded`
866
+ * (the identical receipt again), this decision, and the receipt_hash of the body the registry built from these
867
+ * bytes (app/core/receipt_body.py). A success status alone proves nothing about which receipt was accepted. */
868
+ checkReceiptAck(decision: SignedDecision, payload: Record<string, unknown>, ack: unknown): Promise<Record<string, unknown>>;
858
869
  /** The exact receipt body submitReceipt() would send, signed if a key is given. */
859
870
  buildReceipt(decision: SignedDecision, result?: NonNullable<Parameters<Prampta["submitReceipt"]>[1]>): Promise<Record<string, unknown>>;
860
871
  /**
@@ -892,4 +903,4 @@ declare class Prampta {
892
903
  private getOnce;
893
904
  }
894
905
 
895
- export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, NotGeneratedError, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresSchema, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
906
+ export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GeneratedOutput, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, NotGeneratedError, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresSchema, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
package/dist/index.d.ts CHANGED
@@ -619,6 +619,17 @@ interface AuthorizedGeneration<T> {
619
619
  * Resending will not help; find out why. */
620
620
  conflicts: SignedDecision[];
621
621
  }
622
+ /** What your `generate` returns to `generateAuthorized`. Report only obligations you carried out: the receipt is a
623
+ * signed statement, and nothing is claimed on your behalf. */
624
+ interface GeneratedOutput<T> {
625
+ output: T;
626
+ outputHash?: string;
627
+ outputBytes?: Uint8Array | string;
628
+ /** The obligations you applied, by name (e.g. `{ attribution: true }`). Default: none. */
629
+ obligationsApplied?: Record<string, unknown>;
630
+ /** Whether you embedded the watermark an obligation asked for. Default: false. */
631
+ watermarkEmbedded?: boolean;
632
+ }
622
633
  interface OutputMetadata {
623
634
  prampta_decision_id: string;
624
635
  prampta_license_id: string | null;
@@ -819,11 +830,7 @@ declare class Prampta {
819
830
  * If a subject refuses or `generate` throws, the allows are given back.
820
831
  * `generate` must throw only when nothing was produced.
821
832
  */
822
- generateAuthorized<T>(request: AuthorizedRequest, generate: (decisions: SignedDecision[]) => Promise<{
823
- output: T;
824
- outputHash?: string;
825
- outputBytes?: Uint8Array | string;
826
- }>, options: {
833
+ generateAuthorized<T>(request: AuthorizedRequest, generate: (decisions: SignedDecision[]) => Promise<GeneratedOutput<T>>, options: {
827
834
  journal: GenerationJournal;
828
835
  receipt?: ReceiptOptions;
829
836
  }): Promise<AuthorizedGeneration<T>>;
@@ -855,6 +862,10 @@ declare class Prampta {
855
862
  /** 32-byte Ed25519 private key, hex; keep server-side. Never sent to PRAMPTA. */
856
863
  providerSigningKeyHex?: string;
857
864
  }): Promise<Record<string, unknown>>;
865
+ /** A receipt counts as filed only when PRAMPTA's answer is about exactly it: `recorded` or `already_recorded`
866
+ * (the identical receipt again), this decision, and the receipt_hash of the body the registry built from these
867
+ * bytes (app/core/receipt_body.py). A success status alone proves nothing about which receipt was accepted. */
868
+ checkReceiptAck(decision: SignedDecision, payload: Record<string, unknown>, ack: unknown): Promise<Record<string, unknown>>;
858
869
  /** The exact receipt body submitReceipt() would send, signed if a key is given. */
859
870
  buildReceipt(decision: SignedDecision, result?: NonNullable<Parameters<Prampta["submitReceipt"]>[1]>): Promise<Record<string, unknown>>;
860
871
  /**
@@ -892,4 +903,4 @@ declare class Prampta {
892
903
  private getOnce;
893
904
  }
894
905
 
895
- export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, NotGeneratedError, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresSchema, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
906
+ export { type AskResult, type AuthorizedGeneration, type AuthorizedRequest, type DeliveryResult, type Envelope, type GenerateFn, type GeneratedOutput, type GenerationJournal, type GenerationJournalEntry, HARD_REFUSAL_CODES, type LicenseRequestInput, type LicenseRequestResult, type LicenseSignatureInput, NotGeneratedError, type Obligation, type Observation, type ObservationOutbox, type OutputMetadata, type PgQueryable, Prampta, PramptaApiError, type PramptaConfig, PramptaError, PramptaFailClosedError, PramptaJobConflictError, PramptaNetworkError, PramptaRefusalError, PramptaReporter, PramptaSchemaError, PramptaSignatureError, PramptaTimeoutError, type Pregen, type PregenConfig, PregenError, type PregenJob, type PregenJobInput, type PregenResult, type PregenStore, type Purpose, REFUSAL_DESCRIPTIONS, type ReceiptOptions, RegistryDirectoryError, type RegistryDirectoryOptions, type ReportInput, SOFT_REFUSAL_CODES, type SignedDecision, type SubjectIndexEntry, type SubjectInfo, TrustedRegistryDirectory, type VerifyOptions, type VerifyRequest, canonicalJson, createPregen, hashPrompt, matchSubjects, memoryStore, messageId, normalizeForMatch, postgresSchema, postgresStore, signingInput, singleProcessJournal, verifyLicenseSignatures, verifyMerkleProof };
package/dist/index.js CHANGED
@@ -502,7 +502,7 @@ function checkBasis(b) {
502
502
  if (!isObj(b)) return false;
503
503
  if (b.kind === "grant") {
504
504
  const g = b.grant;
505
- return keysWithin(b, ["kind", "grant"]) && isObj(g) && typeof g.format === "string" && !!GRANT_FORMATS[g.format]?.(g);
505
+ return keysWithin(b, ["kind", "grant"]) && isObj(g) && typeof g.format === "string" && Object.hasOwn(GRANT_FORMATS, g.format) && GRANT_FORMATS[g.format](g) === true;
506
506
  }
507
507
  return b.kind === "policy" && keysWithin(b, ["kind", "rule"]) && str(b.rule);
508
508
  }
@@ -1787,16 +1787,20 @@ var Prampta = class _Prampta {
1787
1787
  const outputHash = result.outputHash ?? (bytes ? await sha256Bytes(bytes) : "");
1788
1788
  const generatedAt = Math.floor(Date.now() / 1e3);
1789
1789
  const receipts = {};
1790
- for (const d of decisions) receipts[d.decisionId] = await this.buildReceipt(d, { ...receipt, outputHash, generatedAt });
1790
+ const done = {
1791
+ ...result.obligationsApplied !== void 0 ? { obligationsApplied: result.obligationsApplied } : {},
1792
+ ...result.watermarkEmbedded !== void 0 ? { watermarkEmbedded: result.watermarkEmbedded } : {}
1793
+ };
1794
+ for (const d of decisions) receipts[d.decisionId] = await this.buildReceipt(d, { ...receipt, ...done, outputHash, generatedAt });
1791
1795
  const generated = { ...entry, stage: "generated", outputHash, generatedAt, receipts };
1792
1796
  await journal.put(generated);
1793
1797
  return this.report(journal, generated, "generated", result.output, receipt);
1794
1798
  }
1795
1799
  async report(journal, entry, status, output, receipt) {
1796
- const results = await Promise.allSettled(entry.decisions.map(async (d) => this.post(
1797
- "/v1/receipts/",
1798
- entry.receipts?.[d.decisionId] ?? await this.buildReceipt(d, { ...receipt, outputHash: entry.outputHash ?? "", generatedAt: entry.generatedAt })
1799
- )));
1800
+ const results = await Promise.allSettled(entry.decisions.map(async (d) => {
1801
+ const payload = entry.receipts?.[d.decisionId] ?? await this.buildReceipt(d, { ...receipt, outputHash: entry.outputHash ?? "", generatedAt: entry.generatedAt });
1802
+ return this.checkReceiptAck(d, payload, await this.post("/v1/receipts/", payload));
1803
+ }));
1800
1804
  const unreported = [], conflicts = [];
1801
1805
  results.forEach((r, i) => {
1802
1806
  if (r.status === "fulfilled") return;
@@ -1863,7 +1867,33 @@ var Prampta = class _Prampta {
1863
1867
  * provider attestation, not proof of complete reporting or actual execution.
1864
1868
  */
1865
1869
  async submitReceipt(decision, result = {}) {
1866
- return this.post("/v1/receipts/", await this.buildReceipt(decision, result));
1870
+ const payload = await this.buildReceipt(decision, result);
1871
+ return this.checkReceiptAck(decision, payload, await this.post("/v1/receipts/", payload));
1872
+ }
1873
+ /** A receipt counts as filed only when PRAMPTA's answer is about exactly it: `recorded` or `already_recorded`
1874
+ * (the identical receipt again), this decision, and the receipt_hash of the body the registry built from these
1875
+ * bytes (app/core/receipt_body.py). A success status alone proves nothing about which receipt was accepted. */
1876
+ async checkReceiptAck(decision, payload, ack) {
1877
+ const a = ack;
1878
+ const v = String(payload.schema_version ?? "pg.receipt.v2");
1879
+ const body = {
1880
+ v,
1881
+ decision_id: payload.decision_id,
1882
+ subject_id: decision.subjectId,
1883
+ licensee_id: this.licenseeId,
1884
+ provider_id: this.providerId,
1885
+ prompt_hash: payload.prompt_hash,
1886
+ output_hash: payload.output_hash,
1887
+ model: payload.model,
1888
+ obligations_applied: payload.obligations_applied,
1889
+ watermark_embedded: payload.watermark_embedded,
1890
+ generated_at: payload.generated_at,
1891
+ ...v === "pg.receipt.v3" ? { event_type: payload.event_type } : {}
1892
+ };
1893
+ if (!a || typeof a !== "object" || a.status !== "recorded" && a.status !== "already_recorded" || a.decision_id !== decision.decisionId || a.receipt_hash !== await sha2563(canonicalJson(body))) {
1894
+ throw new PramptaSchemaError("The registry's receipt answer is not about this receipt");
1895
+ }
1896
+ return a;
1867
1897
  }
1868
1898
  /** The exact receipt body submitReceipt() would send, signed if a key is given. */
1869
1899
  async buildReceipt(decision, result = {}) {
@@ -1873,7 +1903,8 @@ var Prampta = class _Prampta {
1873
1903
  output_hash: result.outputHash ?? "",
1874
1904
  model: result.model ?? decision.model,
1875
1905
  watermark_embedded: result.watermarkEmbedded ?? false,
1876
- obligations_applied: result.obligationsApplied ?? decision.obligations ?? {},
1906
+ // Only what the caller says it did: copying the decision's obligations here claimed work never done.
1907
+ obligations_applied: result.obligationsApplied ?? {},
1877
1908
  generated_at: result.generatedAt ?? Math.floor(Date.now() / 1e3)
1878
1909
  };
1879
1910
  if (result.providerSigningKeyHex !== void 0) {
package/dist/index.mjs CHANGED
@@ -437,7 +437,7 @@ function checkBasis(b) {
437
437
  if (!isObj(b)) return false;
438
438
  if (b.kind === "grant") {
439
439
  const g = b.grant;
440
- return keysWithin(b, ["kind", "grant"]) && isObj(g) && typeof g.format === "string" && !!GRANT_FORMATS[g.format]?.(g);
440
+ return keysWithin(b, ["kind", "grant"]) && isObj(g) && typeof g.format === "string" && Object.hasOwn(GRANT_FORMATS, g.format) && GRANT_FORMATS[g.format](g) === true;
441
441
  }
442
442
  return b.kind === "policy" && keysWithin(b, ["kind", "rule"]) && str(b.rule);
443
443
  }
@@ -1722,16 +1722,20 @@ var Prampta = class _Prampta {
1722
1722
  const outputHash = result.outputHash ?? (bytes ? await sha256Bytes(bytes) : "");
1723
1723
  const generatedAt = Math.floor(Date.now() / 1e3);
1724
1724
  const receipts = {};
1725
- for (const d of decisions) receipts[d.decisionId] = await this.buildReceipt(d, { ...receipt, outputHash, generatedAt });
1725
+ const done = {
1726
+ ...result.obligationsApplied !== void 0 ? { obligationsApplied: result.obligationsApplied } : {},
1727
+ ...result.watermarkEmbedded !== void 0 ? { watermarkEmbedded: result.watermarkEmbedded } : {}
1728
+ };
1729
+ for (const d of decisions) receipts[d.decisionId] = await this.buildReceipt(d, { ...receipt, ...done, outputHash, generatedAt });
1726
1730
  const generated = { ...entry, stage: "generated", outputHash, generatedAt, receipts };
1727
1731
  await journal.put(generated);
1728
1732
  return this.report(journal, generated, "generated", result.output, receipt);
1729
1733
  }
1730
1734
  async report(journal, entry, status, output, receipt) {
1731
- const results = await Promise.allSettled(entry.decisions.map(async (d) => this.post(
1732
- "/v1/receipts/",
1733
- entry.receipts?.[d.decisionId] ?? await this.buildReceipt(d, { ...receipt, outputHash: entry.outputHash ?? "", generatedAt: entry.generatedAt })
1734
- )));
1735
+ const results = await Promise.allSettled(entry.decisions.map(async (d) => {
1736
+ const payload = entry.receipts?.[d.decisionId] ?? await this.buildReceipt(d, { ...receipt, outputHash: entry.outputHash ?? "", generatedAt: entry.generatedAt });
1737
+ return this.checkReceiptAck(d, payload, await this.post("/v1/receipts/", payload));
1738
+ }));
1735
1739
  const unreported = [], conflicts = [];
1736
1740
  results.forEach((r, i) => {
1737
1741
  if (r.status === "fulfilled") return;
@@ -1798,7 +1802,33 @@ var Prampta = class _Prampta {
1798
1802
  * provider attestation, not proof of complete reporting or actual execution.
1799
1803
  */
1800
1804
  async submitReceipt(decision, result = {}) {
1801
- return this.post("/v1/receipts/", await this.buildReceipt(decision, result));
1805
+ const payload = await this.buildReceipt(decision, result);
1806
+ return this.checkReceiptAck(decision, payload, await this.post("/v1/receipts/", payload));
1807
+ }
1808
+ /** A receipt counts as filed only when PRAMPTA's answer is about exactly it: `recorded` or `already_recorded`
1809
+ * (the identical receipt again), this decision, and the receipt_hash of the body the registry built from these
1810
+ * bytes (app/core/receipt_body.py). A success status alone proves nothing about which receipt was accepted. */
1811
+ async checkReceiptAck(decision, payload, ack) {
1812
+ const a = ack;
1813
+ const v = String(payload.schema_version ?? "pg.receipt.v2");
1814
+ const body = {
1815
+ v,
1816
+ decision_id: payload.decision_id,
1817
+ subject_id: decision.subjectId,
1818
+ licensee_id: this.licenseeId,
1819
+ provider_id: this.providerId,
1820
+ prompt_hash: payload.prompt_hash,
1821
+ output_hash: payload.output_hash,
1822
+ model: payload.model,
1823
+ obligations_applied: payload.obligations_applied,
1824
+ watermark_embedded: payload.watermark_embedded,
1825
+ generated_at: payload.generated_at,
1826
+ ...v === "pg.receipt.v3" ? { event_type: payload.event_type } : {}
1827
+ };
1828
+ if (!a || typeof a !== "object" || a.status !== "recorded" && a.status !== "already_recorded" || a.decision_id !== decision.decisionId || a.receipt_hash !== await sha2563(canonicalJson(body))) {
1829
+ throw new PramptaSchemaError("The registry's receipt answer is not about this receipt");
1830
+ }
1831
+ return a;
1802
1832
  }
1803
1833
  /** The exact receipt body submitReceipt() would send, signed if a key is given. */
1804
1834
  async buildReceipt(decision, result = {}) {
@@ -1808,7 +1838,8 @@ var Prampta = class _Prampta {
1808
1838
  output_hash: result.outputHash ?? "",
1809
1839
  model: result.model ?? decision.model,
1810
1840
  watermark_embedded: result.watermarkEmbedded ?? false,
1811
- obligations_applied: result.obligationsApplied ?? decision.obligations ?? {},
1841
+ // Only what the caller says it did: copying the decision's obligations here claimed work never done.
1842
+ obligations_applied: result.obligationsApplied ?? {},
1812
1843
  generated_at: result.generatedAt ?? Math.floor(Date.now() / 1e3)
1813
1844
  };
1814
1845
  if (result.providerSigningKeyHex !== void 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prampta/sdk",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "PRAMPTA SDK — authorization before generation",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@noble/ed25519": "^3.1.0",
52
- "@pregen/verify": "^0.7.0"
52
+ "@pregen/verify": "^0.8.0"
53
53
  },
54
54
  "repository": {
55
55
  "type": "git",