@adcp/sdk 15.7.1 → 15.8.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.
Files changed (63) hide show
  1. package/dist/lib/core/SingleAgentClient.d.mts +14 -0
  2. package/dist/lib/core/SingleAgentClient.d.ts +14 -0
  3. package/dist/lib/core/SingleAgentClient.js +138 -54
  4. package/dist/lib/core/SingleAgentClient.mjs +141 -54
  5. package/dist/lib/core/agent-discovery-cache.d.mts +2 -0
  6. package/dist/lib/core/agent-discovery-cache.d.ts +2 -0
  7. package/dist/lib/core/creative-schema-support.d.mts +3 -0
  8. package/dist/lib/core/creative-schema-support.d.ts +3 -0
  9. package/dist/lib/core/creative-schema-support.js +113 -0
  10. package/dist/lib/core/creative-schema-support.mjs +89 -0
  11. package/dist/lib/core/product-cache.js +4 -3
  12. package/dist/lib/core/product-cache.mjs +4 -3
  13. package/dist/lib/reporting/ledger/handler.d.mts +10 -3
  14. package/dist/lib/reporting/ledger/handler.d.ts +10 -3
  15. package/dist/lib/reporting/ledger/handler.js +17 -4
  16. package/dist/lib/reporting/ledger/handler.mjs +17 -4
  17. package/dist/lib/reporting/ledger/managed.d.mts +3 -1
  18. package/dist/lib/reporting/ledger/managed.d.ts +3 -1
  19. package/dist/lib/reporting/ledger/managed.js +4 -1
  20. package/dist/lib/reporting/ledger/managed.mjs +4 -1
  21. package/dist/lib/reporting/ledger/postgres.d.mts +87 -0
  22. package/dist/lib/reporting/ledger/postgres.d.ts +87 -0
  23. package/dist/lib/reporting/ledger/postgres.js +314 -13
  24. package/dist/lib/reporting/ledger/postgres.mjs +314 -13
  25. package/dist/lib/reporting/ledger/row-storage.d.mts +23 -0
  26. package/dist/lib/reporting/ledger/row-storage.d.ts +23 -0
  27. package/dist/lib/reporting/ledger/row-storage.js +56 -0
  28. package/dist/lib/reporting/ledger/row-storage.mjs +56 -0
  29. package/dist/lib/reporting/ledger/types.d.mts +26 -0
  30. package/dist/lib/reporting/ledger/types.d.ts +26 -0
  31. package/dist/lib/reporting/service/index.d.mts +33 -3
  32. package/dist/lib/reporting/service/index.d.ts +33 -3
  33. package/dist/lib/reporting/service/index.js +20 -2
  34. package/dist/lib/reporting/service/index.mjs +20 -2
  35. package/dist/lib/reporting/service/maintenance.d.mts +20 -1
  36. package/dist/lib/reporting/service/maintenance.d.ts +20 -1
  37. package/dist/lib/reporting/service/maintenance.js +25 -2
  38. package/dist/lib/reporting/service/maintenance.mjs +25 -2
  39. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  40. package/dist/lib/testing/storyboard/request-builder.js +14 -16
  41. package/dist/lib/testing/storyboard/request-builder.mjs +14 -16
  42. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  43. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  44. package/dist/lib/v2/projection/creative-delivery.d.mts +22 -0
  45. package/dist/lib/v2/projection/creative-delivery.d.ts +22 -0
  46. package/dist/lib/v2/projection/creative-delivery.js +92 -0
  47. package/dist/lib/v2/projection/creative-delivery.mjs +91 -0
  48. package/dist/lib/v2/projection/legacy-metadata.d.mts +6 -3
  49. package/dist/lib/v2/projection/legacy-metadata.d.ts +6 -3
  50. package/dist/lib/v2/projection/legacy-metadata.js +17 -0
  51. package/dist/lib/v2/projection/legacy-metadata.mjs +15 -0
  52. package/dist/lib/version.d.mts +3 -3
  53. package/dist/lib/version.d.ts +3 -3
  54. package/dist/lib/version.js +3 -3
  55. package/dist/lib/version.mjs +3 -3
  56. package/docs/TYPE-SUMMARY.md +2 -2
  57. package/docs/guides/CREATIVE-DELIVERY.md +16 -0
  58. package/docs/guides/REPORTING-LEDGER-CUTOVER.md +405 -0
  59. package/docs/guides/REPORTING-LEDGER.md +130 -0
  60. package/docs/guides/REPORTING-OPERATIONS.md +12 -0
  61. package/docs/llms.txt +3 -2
  62. package/docs/migration-14.x-rc-worksheet.md +4 -4
  63. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import type { ReportingAdjustmentReceipt, ReportingMaterialization, ReportingReceipt } from '../../types/index.mjs';
2
2
  import { projectReportingObligationHealthV1 } from './health.mjs';
3
- import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types.mjs';
3
+ import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, CreateReportingDeliveryHandlerOptionsV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types.mjs';
4
4
  export interface ReportingStatusConsumerScopeOptionsV1<TContext = unknown> {
5
5
  resolveConsumerId(context: TContext): string | Promise<string>;
6
6
  /**
@@ -21,8 +21,15 @@ export declare function createReportingStatusHandler<TContext = unknown>(store:
21
21
  * them through the exact `get_media_buy_delivery` read.
22
22
  */
23
23
  export declare const REPORTING_STATUS_REVISION_VIEW_MAX_ROWS = 10000;
24
- /** Exact revision reader for createAdcpServer's getMediaBuyDelivery slot. */
25
- export declare function createReportingDeliveryHandler(store: ReportingLedgerStore): ReportingDeliveryHandlerV1;
24
+ /**
25
+ * Exact revision reader for createAdcpServer's getMediaBuyDelivery slot.
26
+ *
27
+ * With `legacyRevisionReader`, a revision the SDK ledger does not hold for the
28
+ * resolved account is offered to that reader (see
29
+ * {@link ReportingLegacyRevisionReaderV1}). The SDK always answers first, and
30
+ * only that one not-found case falls through.
31
+ */
32
+ export declare function createReportingDeliveryHandler(store: ReportingLedgerStore, options?: CreateReportingDeliveryHandlerOptionsV1): ReportingDeliveryHandlerV1;
26
33
  export interface ProjectManagedDeliveryInputV1 {
27
34
  obligation: ReportingLedgerObligationV1;
28
35
  binding?: ReportingManagedDeliveryBindingV1;
@@ -1,6 +1,6 @@
1
1
  import type { ReportingAdjustmentReceipt, ReportingMaterialization, ReportingReceipt } from '../../types';
2
2
  import { projectReportingObligationHealthV1 } from './health';
3
- import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types';
3
+ import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, CreateReportingDeliveryHandlerOptionsV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types';
4
4
  export interface ReportingStatusConsumerScopeOptionsV1<TContext = unknown> {
5
5
  resolveConsumerId(context: TContext): string | Promise<string>;
6
6
  /**
@@ -21,8 +21,15 @@ export declare function createReportingStatusHandler<TContext = unknown>(store:
21
21
  * them through the exact `get_media_buy_delivery` read.
22
22
  */
23
23
  export declare const REPORTING_STATUS_REVISION_VIEW_MAX_ROWS = 10000;
24
- /** Exact revision reader for createAdcpServer's getMediaBuyDelivery slot. */
25
- export declare function createReportingDeliveryHandler(store: ReportingLedgerStore): ReportingDeliveryHandlerV1;
24
+ /**
25
+ * Exact revision reader for createAdcpServer's getMediaBuyDelivery slot.
26
+ *
27
+ * With `legacyRevisionReader`, a revision the SDK ledger does not hold for the
28
+ * resolved account is offered to that reader (see
29
+ * {@link ReportingLegacyRevisionReaderV1}). The SDK always answers first, and
30
+ * only that one not-found case falls through.
31
+ */
32
+ export declare function createReportingDeliveryHandler(store: ReportingLedgerStore, options?: CreateReportingDeliveryHandlerOptionsV1): ReportingDeliveryHandlerV1;
26
33
  export interface ProjectManagedDeliveryInputV1 {
27
34
  obligation: ReportingLedgerObligationV1;
28
35
  binding?: ReportingManagedDeliveryBindingV1;
@@ -416,9 +416,14 @@ function reportingRevisionNotFound() {
416
416
  field: "reporting_revision_id"
417
417
  });
418
418
  }
419
- function createReportingDeliveryHandler(store) {
419
+ const SDK_REVISION_NOT_OWNED = /* @__PURE__ */ Symbol("sdk-revision-not-owned");
420
+ function createReportingDeliveryHandler(store, options = {}) {
421
+ const legacyRevisionReader = options.legacyRevisionReader;
422
+ if (legacyRevisionReader !== void 0 && typeof legacyRevisionReader !== "function") {
423
+ throw new TypeError("legacyRevisionReader must be a function");
424
+ }
420
425
  const activeReadsByAccount = /* @__PURE__ */ new Map();
421
- return async (request, context) => {
426
+ const readFromSdkLedger = async (request, context) => {
422
427
  const accountId = resolvedAccountId(context.account);
423
428
  let releaseReadSlot;
424
429
  try {
@@ -440,9 +445,9 @@ function createReportingDeliveryHandler(store) {
440
445
  });
441
446
  }
442
447
  const revision = await (0, import_revision_metadata.getRevisionMetadataFromStore)(store, raw.reporting_revision_id, accountId);
443
- if (!revision) throw reportingRevisionNotFound();
448
+ if (!revision) return SDK_REVISION_NOT_OWNED;
444
449
  const obligation = await store.getObligation(revision.reporting_obligation_id);
445
- if (!obligation || obligation.account.account_id !== accountId) throw reportingRevisionNotFound();
450
+ if (!obligation || obligation.account.account_id !== accountId) return SDK_REVISION_NOT_OWNED;
446
451
  const pagination = isRecord(raw.pagination) ? raw.pagination : {};
447
452
  const maxResults = typeof pagination.max_results === "number" && Number.isFinite(pagination.max_results) ? Math.max(1, Math.min(500, Math.trunc(pagination.max_results))) : 100;
448
453
  const totalCount = revision.binding.rowCount;
@@ -520,6 +525,14 @@ function createReportingDeliveryHandler(store) {
520
525
  releaseReadSlot();
521
526
  }
522
527
  };
528
+ return async (request, context) => {
529
+ const result = await readFromSdkLedger(request, context);
530
+ if (result !== SDK_REVISION_NOT_OWNED) return result;
531
+ if (!legacyRevisionReader) throw reportingRevisionNotFound();
532
+ const legacy = await legacyRevisionReader(request, context);
533
+ if (legacy === null || legacy === void 0) throw reportingRevisionNotFound();
534
+ return legacy;
535
+ };
523
536
  }
524
537
  function resolvedAccountId(account) {
525
538
  const resolved = isRecord(account) ? account : {};
@@ -399,9 +399,14 @@ function reportingRevisionNotFound() {
399
399
  field: "reporting_revision_id"
400
400
  });
401
401
  }
402
- function createReportingDeliveryHandler(store) {
402
+ const SDK_REVISION_NOT_OWNED = /* @__PURE__ */ Symbol("sdk-revision-not-owned");
403
+ function createReportingDeliveryHandler(store, options = {}) {
404
+ const legacyRevisionReader = options.legacyRevisionReader;
405
+ if (legacyRevisionReader !== void 0 && typeof legacyRevisionReader !== "function") {
406
+ throw new TypeError("legacyRevisionReader must be a function");
407
+ }
403
408
  const activeReadsByAccount = /* @__PURE__ */ new Map();
404
- return async (request, context) => {
409
+ const readFromSdkLedger = async (request, context) => {
405
410
  const accountId = resolvedAccountId(context.account);
406
411
  let releaseReadSlot;
407
412
  try {
@@ -423,9 +428,9 @@ function createReportingDeliveryHandler(store) {
423
428
  });
424
429
  }
425
430
  const revision = await getRevisionMetadataFromStore(store, raw.reporting_revision_id, accountId);
426
- if (!revision) throw reportingRevisionNotFound();
431
+ if (!revision) return SDK_REVISION_NOT_OWNED;
427
432
  const obligation = await store.getObligation(revision.reporting_obligation_id);
428
- if (!obligation || obligation.account.account_id !== accountId) throw reportingRevisionNotFound();
433
+ if (!obligation || obligation.account.account_id !== accountId) return SDK_REVISION_NOT_OWNED;
429
434
  const pagination = isRecord(raw.pagination) ? raw.pagination : {};
430
435
  const maxResults = typeof pagination.max_results === "number" && Number.isFinite(pagination.max_results) ? Math.max(1, Math.min(500, Math.trunc(pagination.max_results))) : 100;
431
436
  const totalCount = revision.binding.rowCount;
@@ -503,6 +508,14 @@ function createReportingDeliveryHandler(store) {
503
508
  releaseReadSlot();
504
509
  }
505
510
  };
511
+ return async (request, context) => {
512
+ const result = await readFromSdkLedger(request, context);
513
+ if (result !== SDK_REVISION_NOT_OWNED) return result;
514
+ if (!legacyRevisionReader) throw reportingRevisionNotFound();
515
+ const legacy = await legacyRevisionReader(request, context);
516
+ if (legacy === null || legacy === void 0) throw reportingRevisionNotFound();
517
+ return legacy;
518
+ };
506
519
  }
507
520
  function resolvedAccountId(account) {
508
521
  const resolved = isRecord(account) ? account : {};
@@ -2,7 +2,7 @@ import type { ReportingAdjustmentReceipt, ReportingDeliveryCapabilities, Reporti
2
2
  import type { AdcpToolMap } from '../../server/create-adcp-server.mjs';
3
3
  import { createReportingDeliveryHandler, createReportingStatusHandler } from './handler.mjs';
4
4
  import type { ReportingStatusConsumerScopeOptionsV1 } from './handler.mjs';
5
- import type { ReportingLedgerAdjustmentV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types.mjs';
5
+ import type { ReportingLedgerAdjustmentV1, ReportingLegacyRevisionReaderV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types.mjs';
6
6
  export interface ReportingDestinationAuthorizationV1 {
7
7
  account_id: string;
8
8
  destination_ref: string;
@@ -274,6 +274,8 @@ export interface CreateReportingManagedDeliveryRuntimeOptionsV1<TContext extends
274
274
  account?: unknown;
275
275
  }> {
276
276
  coreStore: ReportingLedgerStore;
277
+ /** Exact-revision fallback for revisions the SDK ledger does not hold. */
278
+ legacyRevisionReader?: ReportingLegacyRevisionReaderV1;
277
279
  store: ReportingManagedDeliveryStore & Required<Pick<ReportingManagedDeliveryStore, 'adoptAdvertisedPolicies'>>;
278
280
  adapter: ReportingManagedDeliveryAdapterV1;
279
281
  offerings: ReportingDeliveryCapabilities['offerings'];
@@ -2,7 +2,7 @@ import type { ReportingAdjustmentReceipt, ReportingDeliveryCapabilities, Reporti
2
2
  import type { AdcpToolMap } from '../../server/create-adcp-server';
3
3
  import { createReportingDeliveryHandler, createReportingStatusHandler } from './handler';
4
4
  import type { ReportingStatusConsumerScopeOptionsV1 } from './handler';
5
- import type { ReportingLedgerAdjustmentV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types';
5
+ import type { ReportingLedgerAdjustmentV1, ReportingLegacyRevisionReaderV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types';
6
6
  export interface ReportingDestinationAuthorizationV1 {
7
7
  account_id: string;
8
8
  destination_ref: string;
@@ -274,6 +274,8 @@ export interface CreateReportingManagedDeliveryRuntimeOptionsV1<TContext extends
274
274
  account?: unknown;
275
275
  }> {
276
276
  coreStore: ReportingLedgerStore;
277
+ /** Exact-revision fallback for revisions the SDK ledger does not hold. */
278
+ legacyRevisionReader?: ReportingLegacyRevisionReaderV1;
277
279
  store: ReportingManagedDeliveryStore & Required<Pick<ReportingManagedDeliveryStore, 'adoptAdvertisedPolicies'>>;
278
280
  adapter: ReportingManagedDeliveryAdapterV1;
279
281
  offerings: ReportingDeliveryCapabilities['offerings'];
@@ -89,7 +89,10 @@ async function createReportingManagedDeliveryRuntime(options) {
89
89
  ...options.consumerMismatchEscalation ? { consumerMismatchEscalation: options.consumerMismatchEscalation } : {}
90
90
  } : void 0;
91
91
  const getReportingStatus = (0, import_handler.createReportingStatusHandler)(options.coreStore, statusOptions);
92
- const getMediaBuyDelivery = (0, import_handler.createReportingDeliveryHandler)(options.coreStore);
92
+ const getMediaBuyDelivery = (0, import_handler.createReportingDeliveryHandler)(
93
+ options.coreStore,
94
+ options.legacyRevisionReader ? { legacyRevisionReader: options.legacyRevisionReader } : {}
95
+ );
93
96
  const syncReportingReceipts = reconciledBilling ? createSyncReportingReceiptsHandler(options.store, options.resolveConsumerId) : void 0;
94
97
  const reportingDeliveryCapabilities = {
95
98
  supported: true,
@@ -62,7 +62,10 @@ async function createReportingManagedDeliveryRuntime(options) {
62
62
  ...options.consumerMismatchEscalation ? { consumerMismatchEscalation: options.consumerMismatchEscalation } : {}
63
63
  } : void 0;
64
64
  const getReportingStatus = createReportingStatusHandler(options.coreStore, statusOptions);
65
- const getMediaBuyDelivery = createReportingDeliveryHandler(options.coreStore);
65
+ const getMediaBuyDelivery = createReportingDeliveryHandler(
66
+ options.coreStore,
67
+ options.legacyRevisionReader ? { legacyRevisionReader: options.legacyRevisionReader } : {}
68
+ );
66
69
  const syncReportingReceipts = reconciledBilling ? createSyncReportingReceiptsHandler(options.store, options.resolveConsumerId) : void 0;
67
70
  const reportingDeliveryCapabilities = {
68
71
  supported: true,
@@ -150,6 +150,12 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
150
150
  }>;
151
151
  /** Append one host change-feed row inside the caller's account-locked transaction. */
152
152
  private appendChange;
153
+ /**
154
+ * Throws `INVALID_INPUT` when any configured object row binding's key template omits
155
+ * `{revision_key}`, which makes snapshot compaction unsafe (see `compactSupersededSnapshots`).
156
+ * Called by `compactSupersededSnapshots` and by service construction when compaction is enabled.
157
+ */
158
+ assertCompactionSafeRowBindings(): void;
153
159
  /** Probe and register row storage when enabled. Safe to call repeatedly. */
154
160
  readyRowStorage(): Promise<void>;
155
161
  /**
@@ -318,6 +324,65 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
318
324
  }[];
319
325
  }>;
320
326
  private retirePeriod;
327
+ /**
328
+ * Compact superseded snapshot revisions to metadata only, as adcontextprotocol/adcp#8100
329
+ * permits (`reporting-revision.json` `content_retention`). Opt-in: nothing calls this
330
+ * unless the host enables `compaction` in maintenance, and it must not be enabled in
331
+ * production before the AdCP release that takes compaction into effect (the next
332
+ * eligible minor after November 20, 2026).
333
+ *
334
+ * A snapshot revision is compacted only when every one of these holds:
335
+ *
336
+ * - it is superseded: a later revision names it in `supersedes_reporting_revision_id`
337
+ * (so a current revision, and every official revision, is never compacted);
338
+ * - it is not the revision the current, unsuperseded revision names;
339
+ * - its recovery grace has passed: `max(automatedRecoveryWindowSeconds, 3600)` seconds
340
+ * since its successor's `created_at`;
341
+ * - while its window is open (`statusRetentionDays` since its successor's `created_at`),
342
+ * no caller's reporting receipt (any status) names it and no caller's current consumer
343
+ * status statement of `received`, `unreadable` or `content_mismatch` names it. The
344
+ * check spans every caller, so content any caller is entitled to is kept;
345
+ * - it has rows (nothing to reclaim otherwise, and an empty exact read is still correct);
346
+ * - no live obligation lease, no pending materialization of it, and no live change-feed
347
+ * consumer that has not yet passed its change row (the holds retirement applies,
348
+ * narrowed to this revision).
349
+ *
350
+ * Only row content is dropped: the revision header, its manifest and every metadata
351
+ * listing stay. The row set is marked `pruned`, so an exact read answers
352
+ * `ROWS_EXPIRED`, which the handlers surface as the same nondisclosing
353
+ * `REFERENCE_NOT_FOUND` as an unknown ID. No change-feed row is recorded: the revision's
354
+ * metadata is unchanged, and a sink still catching up holds compaction back.
355
+ *
356
+ * Compaction is resumable and runs in the same three phases as retirement, under the
357
+ * account lock where eligibility is decided:
358
+ *
359
+ * 1. re-check eligibility and mark the row set `pruning`, so reads answer as expired and
360
+ * no new revision can share its chunks;
361
+ * 2. delete external objects at their recorded versions, skipping any object another
362
+ * row set that is not being pruned still references (chunk sharing);
363
+ * 3. delete the chunk manifest rows and any PostgreSQL chunk body nothing else
364
+ * references, and mark the row set `pruned`.
365
+ *
366
+ * A crash after phase 1 leaves a `pruning` row set that the next pass resumes.
367
+ */
368
+ compactSupersededSnapshots(input: {
369
+ statusRetentionDays: number;
370
+ automatedRecoveryWindowSeconds: number;
371
+ maxFeedHoldDays?: number;
372
+ limit?: number;
373
+ account_id?: string;
374
+ signal?: AbortSignal;
375
+ }): Promise<{
376
+ compacted: number;
377
+ failed: string[];
378
+ /** Secret-free cause per failed revision: a row-store code or the error class name. */
379
+ failures: {
380
+ reporting_obligation_id: string;
381
+ reporting_revision_id: string;
382
+ cause: string;
383
+ }[];
384
+ }>;
385
+ private compactSnapshot;
321
386
  /**
322
387
  * Resolve abandoned object uploads (spec §4.3). An expired `open` write
323
388
  * intent whose ledger commit never happened is moved to `sweeping` under
@@ -335,6 +400,28 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
335
400
  objectsDeleted: number;
336
401
  failed: number;
337
402
  }>;
403
+ /**
404
+ * Delete external row objects, skipping any object another row set still references.
405
+ *
406
+ * A custom `keyTemplate` need only include `{content_sha256}` and `{chunk_index}`, so row sets
407
+ * from different obligations or accounts with identical chunk content can share one object (a
408
+ * create-only write adopts an existing object). An object is therefore deleted only when no
409
+ * chunk of a live (or unavailable) row set outside this operation references the same binding,
410
+ * key and native version, and no other open write intent created it. Row sets already `pruning`
411
+ * or `pruned` do not count: their own operation deletes the object, and counting them would let
412
+ * two concurrent retirements each skip the object and leak it. `scope` names what this
413
+ * operation is deleting: an obligation (retirement) or a row set (compaction).
414
+ *
415
+ * The check and the provider delete run in one transaction under a per-object advisory lock.
416
+ * A commit that adopts an existing object takes the same lock before it inserts its chunk rows
417
+ * and re-verifies the object under it (`writePreparedRowSet`), and holds it until commit. So
418
+ * either the delete wins the lock and the adopting commit then fails cleanly, or the commit wins
419
+ * and the delete sees its chunk rows and skips. A skipped object stays for the row set that owns
420
+ * it: that row set's own retirement, compaction or sweep deletes it, because by then nothing
421
+ * else references it. Locks are taken one object at a time, never while waiting on the account
422
+ * lock, so commits (account lock, then object locks) cannot deadlock with these deletes.
423
+ */
424
+ private deleteUnreferencedObjects;
338
425
  private optionalTables;
339
426
  /**
340
427
  * Upload an object-kind row set before its ledger commit (spec §3.5):
@@ -150,6 +150,12 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
150
150
  }>;
151
151
  /** Append one host change-feed row inside the caller's account-locked transaction. */
152
152
  private appendChange;
153
+ /**
154
+ * Throws `INVALID_INPUT` when any configured object row binding's key template omits
155
+ * `{revision_key}`, which makes snapshot compaction unsafe (see `compactSupersededSnapshots`).
156
+ * Called by `compactSupersededSnapshots` and by service construction when compaction is enabled.
157
+ */
158
+ assertCompactionSafeRowBindings(): void;
153
159
  /** Probe and register row storage when enabled. Safe to call repeatedly. */
154
160
  readyRowStorage(): Promise<void>;
155
161
  /**
@@ -318,6 +324,65 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
318
324
  }[];
319
325
  }>;
320
326
  private retirePeriod;
327
+ /**
328
+ * Compact superseded snapshot revisions to metadata only, as adcontextprotocol/adcp#8100
329
+ * permits (`reporting-revision.json` `content_retention`). Opt-in: nothing calls this
330
+ * unless the host enables `compaction` in maintenance, and it must not be enabled in
331
+ * production before the AdCP release that takes compaction into effect (the next
332
+ * eligible minor after November 20, 2026).
333
+ *
334
+ * A snapshot revision is compacted only when every one of these holds:
335
+ *
336
+ * - it is superseded: a later revision names it in `supersedes_reporting_revision_id`
337
+ * (so a current revision, and every official revision, is never compacted);
338
+ * - it is not the revision the current, unsuperseded revision names;
339
+ * - its recovery grace has passed: `max(automatedRecoveryWindowSeconds, 3600)` seconds
340
+ * since its successor's `created_at`;
341
+ * - while its window is open (`statusRetentionDays` since its successor's `created_at`),
342
+ * no caller's reporting receipt (any status) names it and no caller's current consumer
343
+ * status statement of `received`, `unreadable` or `content_mismatch` names it. The
344
+ * check spans every caller, so content any caller is entitled to is kept;
345
+ * - it has rows (nothing to reclaim otherwise, and an empty exact read is still correct);
346
+ * - no live obligation lease, no pending materialization of it, and no live change-feed
347
+ * consumer that has not yet passed its change row (the holds retirement applies,
348
+ * narrowed to this revision).
349
+ *
350
+ * Only row content is dropped: the revision header, its manifest and every metadata
351
+ * listing stay. The row set is marked `pruned`, so an exact read answers
352
+ * `ROWS_EXPIRED`, which the handlers surface as the same nondisclosing
353
+ * `REFERENCE_NOT_FOUND` as an unknown ID. No change-feed row is recorded: the revision's
354
+ * metadata is unchanged, and a sink still catching up holds compaction back.
355
+ *
356
+ * Compaction is resumable and runs in the same three phases as retirement, under the
357
+ * account lock where eligibility is decided:
358
+ *
359
+ * 1. re-check eligibility and mark the row set `pruning`, so reads answer as expired and
360
+ * no new revision can share its chunks;
361
+ * 2. delete external objects at their recorded versions, skipping any object another
362
+ * row set that is not being pruned still references (chunk sharing);
363
+ * 3. delete the chunk manifest rows and any PostgreSQL chunk body nothing else
364
+ * references, and mark the row set `pruned`.
365
+ *
366
+ * A crash after phase 1 leaves a `pruning` row set that the next pass resumes.
367
+ */
368
+ compactSupersededSnapshots(input: {
369
+ statusRetentionDays: number;
370
+ automatedRecoveryWindowSeconds: number;
371
+ maxFeedHoldDays?: number;
372
+ limit?: number;
373
+ account_id?: string;
374
+ signal?: AbortSignal;
375
+ }): Promise<{
376
+ compacted: number;
377
+ failed: string[];
378
+ /** Secret-free cause per failed revision: a row-store code or the error class name. */
379
+ failures: {
380
+ reporting_obligation_id: string;
381
+ reporting_revision_id: string;
382
+ cause: string;
383
+ }[];
384
+ }>;
385
+ private compactSnapshot;
321
386
  /**
322
387
  * Resolve abandoned object uploads (spec §4.3). An expired `open` write
323
388
  * intent whose ledger commit never happened is moved to `sweeping` under
@@ -335,6 +400,28 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
335
400
  objectsDeleted: number;
336
401
  failed: number;
337
402
  }>;
403
+ /**
404
+ * Delete external row objects, skipping any object another row set still references.
405
+ *
406
+ * A custom `keyTemplate` need only include `{content_sha256}` and `{chunk_index}`, so row sets
407
+ * from different obligations or accounts with identical chunk content can share one object (a
408
+ * create-only write adopts an existing object). An object is therefore deleted only when no
409
+ * chunk of a live (or unavailable) row set outside this operation references the same binding,
410
+ * key and native version, and no other open write intent created it. Row sets already `pruning`
411
+ * or `pruned` do not count: their own operation deletes the object, and counting them would let
412
+ * two concurrent retirements each skip the object and leak it. `scope` names what this
413
+ * operation is deleting: an obligation (retirement) or a row set (compaction).
414
+ *
415
+ * The check and the provider delete run in one transaction under a per-object advisory lock.
416
+ * A commit that adopts an existing object takes the same lock before it inserts its chunk rows
417
+ * and re-verifies the object under it (`writePreparedRowSet`), and holds it until commit. So
418
+ * either the delete wins the lock and the adopting commit then fails cleanly, or the commit wins
419
+ * and the delete sees its chunk rows and skips. A skipped object stays for the row set that owns
420
+ * it: that row set's own retirement, compaction or sweep deletes it, because by then nothing
421
+ * else references it. Locks are taken one object at a time, never while waiting on the account
422
+ * lock, so commits (account lock, then object locks) cannot deadlock with these deletes.
423
+ */
424
+ private deleteUnreferencedObjects;
338
425
  private optionalTables;
339
426
  /**
340
427
  * Upload an object-kind row set before its ledger commit (spec §3.5):