@adcp/sdk 15.4.0 → 15.6.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 (164) hide show
  1. package/README.md +15 -0
  2. package/dist/lib/advanced.d.mts +2 -2
  3. package/dist/lib/advanced.d.ts +2 -2
  4. package/dist/lib/advanced.js +8 -2
  5. package/dist/lib/advanced.mjs +8 -2
  6. package/dist/lib/auth/oauth/index.d.mts +1 -0
  7. package/dist/lib/auth/oauth/index.d.ts +1 -0
  8. package/dist/lib/auth/oauth/index.js +5 -0
  9. package/dist/lib/auth/oauth/index.mjs +3 -0
  10. package/dist/lib/auth/oauth/pending-web-flow.d.mts +16 -0
  11. package/dist/lib/auth/oauth/pending-web-flow.d.ts +16 -0
  12. package/dist/lib/auth/oauth/pending-web-flow.js +66 -0
  13. package/dist/lib/auth/oauth/pending-web-flow.mjs +41 -0
  14. package/dist/lib/auth/oauth/web-flow.js +3 -3
  15. package/dist/lib/auth/oauth/web-flow.mjs +3 -3
  16. package/dist/lib/core/AgentClient.d.mts +4 -0
  17. package/dist/lib/core/AgentClient.d.ts +4 -0
  18. package/dist/lib/core/AgentClient.js +8 -0
  19. package/dist/lib/core/AgentClient.mjs +8 -0
  20. package/dist/lib/core/SingleAgentClient.d.mts +154 -0
  21. package/dist/lib/core/SingleAgentClient.d.ts +154 -0
  22. package/dist/lib/core/SingleAgentClient.js +661 -23
  23. package/dist/lib/core/SingleAgentClient.mjs +666 -23
  24. package/dist/lib/core/agent-discovery-cache.d.mts +103 -0
  25. package/dist/lib/core/agent-discovery-cache.d.ts +103 -0
  26. package/dist/lib/core/agent-discovery-cache.js +121 -0
  27. package/dist/lib/core/agent-discovery-cache.mjs +92 -0
  28. package/dist/lib/index.d.mts +5 -3
  29. package/dist/lib/index.d.ts +5 -3
  30. package/dist/lib/index.js +7 -0
  31. package/dist/lib/index.mjs +6 -0
  32. package/dist/lib/protocols/index.d.mts +2 -2
  33. package/dist/lib/protocols/index.d.ts +2 -2
  34. package/dist/lib/protocols/index.js +1 -1
  35. package/dist/lib/protocols/index.mjs +1 -1
  36. package/dist/lib/protocols/mcp-call-context.d.mts +109 -0
  37. package/dist/lib/protocols/mcp-call-context.d.ts +109 -0
  38. package/dist/lib/protocols/mcp-call-context.js +221 -0
  39. package/dist/lib/protocols/mcp-call-context.mjs +186 -0
  40. package/dist/lib/protocols/mcp-modern.d.mts +9 -1
  41. package/dist/lib/protocols/mcp-modern.d.ts +9 -1
  42. package/dist/lib/protocols/mcp-modern.js +180 -104
  43. package/dist/lib/protocols/mcp-modern.mjs +193 -104
  44. package/dist/lib/protocols/mcp-scope.d.mts +15 -0
  45. package/dist/lib/protocols/mcp-scope.d.ts +15 -0
  46. package/dist/lib/protocols/mcp.d.mts +4 -2
  47. package/dist/lib/protocols/mcp.d.ts +4 -2
  48. package/dist/lib/protocols/mcp.js +178 -154
  49. package/dist/lib/protocols/mcp.mjs +189 -153
  50. package/dist/lib/protocols/transportDiagnostics.d.mts +33 -1
  51. package/dist/lib/protocols/transportDiagnostics.d.ts +33 -1
  52. package/dist/lib/protocols/transportDiagnostics.js +128 -12
  53. package/dist/lib/protocols/transportDiagnostics.mjs +128 -12
  54. package/dist/lib/registry/types.generated.d.mts +63 -1
  55. package/dist/lib/registry/types.generated.d.ts +63 -1
  56. package/dist/lib/reporting/azure/index.d.mts +26 -0
  57. package/dist/lib/reporting/azure/index.d.ts +26 -0
  58. package/dist/lib/reporting/azure/index.js +247 -0
  59. package/dist/lib/reporting/azure/index.mjs +230 -0
  60. package/dist/lib/reporting/bigquery/index.d.mts +127 -0
  61. package/dist/lib/reporting/bigquery/index.d.ts +127 -0
  62. package/dist/lib/reporting/bigquery/index.js +379 -0
  63. package/dist/lib/reporting/bigquery/index.mjs +341 -0
  64. package/dist/lib/reporting/gcs/index.d.mts +2 -0
  65. package/dist/lib/reporting/gcs/index.d.ts +2 -0
  66. package/dist/lib/reporting/gcs/index.js +3 -0
  67. package/dist/lib/reporting/gcs/index.mjs +2 -0
  68. package/dist/lib/reporting/gcs/row-provider.d.mts +32 -0
  69. package/dist/lib/reporting/gcs/row-provider.d.ts +32 -0
  70. package/dist/lib/reporting/gcs/row-provider.js +205 -0
  71. package/dist/lib/reporting/gcs/row-provider.mjs +188 -0
  72. package/dist/lib/reporting/ledger/change-feed.d.mts +41 -0
  73. package/dist/lib/reporting/ledger/change-feed.d.ts +41 -0
  74. package/dist/lib/reporting/ledger/change-feed.js +101 -0
  75. package/dist/lib/reporting/ledger/change-feed.mjs +74 -0
  76. package/dist/lib/reporting/ledger/handler.js +22 -8
  77. package/dist/lib/reporting/ledger/handler.mjs +22 -8
  78. package/dist/lib/reporting/ledger/index.d.mts +4 -0
  79. package/dist/lib/reporting/ledger/index.d.ts +4 -0
  80. package/dist/lib/reporting/ledger/index.js +9 -1
  81. package/dist/lib/reporting/ledger/index.mjs +4 -0
  82. package/dist/lib/reporting/ledger/managed-postgres.d.mts +2 -0
  83. package/dist/lib/reporting/ledger/managed-postgres.d.ts +2 -0
  84. package/dist/lib/reporting/ledger/managed-postgres.js +21 -1
  85. package/dist/lib/reporting/ledger/managed-postgres.mjs +21 -1
  86. package/dist/lib/reporting/ledger/managed.d.mts +9 -0
  87. package/dist/lib/reporting/ledger/managed.d.ts +9 -0
  88. package/dist/lib/reporting/ledger/managed.js +18 -0
  89. package/dist/lib/reporting/ledger/managed.mjs +18 -0
  90. package/dist/lib/reporting/ledger/postgres.d.mts +185 -0
  91. package/dist/lib/reporting/ledger/postgres.d.ts +185 -0
  92. package/dist/lib/reporting/ledger/postgres.js +872 -26
  93. package/dist/lib/reporting/ledger/postgres.mjs +880 -26
  94. package/dist/lib/reporting/ledger/producer.js +10 -1
  95. package/dist/lib/reporting/ledger/producer.mjs +10 -1
  96. package/dist/lib/reporting/ledger/row-storage-errors.d.mts +7 -0
  97. package/dist/lib/reporting/ledger/row-storage-errors.d.ts +7 -0
  98. package/dist/lib/reporting/ledger/row-storage-errors.js +41 -0
  99. package/dist/lib/reporting/ledger/row-storage-errors.mjs +16 -0
  100. package/dist/lib/reporting/ledger/row-storage-migration.d.mts +41 -0
  101. package/dist/lib/reporting/ledger/row-storage-migration.d.ts +41 -0
  102. package/dist/lib/reporting/ledger/row-storage-migration.js +234 -0
  103. package/dist/lib/reporting/ledger/row-storage-migration.mjs +208 -0
  104. package/dist/lib/reporting/ledger/row-storage-object.d.mts +112 -0
  105. package/dist/lib/reporting/ledger/row-storage-object.d.ts +112 -0
  106. package/dist/lib/reporting/ledger/row-storage-object.js +304 -0
  107. package/dist/lib/reporting/ledger/row-storage-object.mjs +262 -0
  108. package/dist/lib/reporting/ledger/row-storage-provider-io.d.mts +44 -0
  109. package/dist/lib/reporting/ledger/row-storage-provider-io.d.ts +44 -0
  110. package/dist/lib/reporting/ledger/row-storage-provider-io.js +142 -0
  111. package/dist/lib/reporting/ledger/row-storage-provider-io.mjs +111 -0
  112. package/dist/lib/reporting/ledger/row-storage.d.mts +224 -0
  113. package/dist/lib/reporting/ledger/row-storage.d.ts +224 -0
  114. package/dist/lib/reporting/ledger/row-storage.js +692 -0
  115. package/dist/lib/reporting/ledger/row-storage.mjs +680 -0
  116. package/dist/lib/reporting/ledger/types.d.mts +8 -0
  117. package/dist/lib/reporting/ledger/types.d.ts +8 -0
  118. package/dist/lib/reporting/s3/index.d.mts +29 -0
  119. package/dist/lib/reporting/s3/index.d.ts +29 -0
  120. package/dist/lib/reporting/s3/index.js +324 -0
  121. package/dist/lib/reporting/s3/index.mjs +317 -0
  122. package/dist/lib/reporting/service/index.d.mts +43 -1
  123. package/dist/lib/reporting/service/index.d.ts +43 -1
  124. package/dist/lib/reporting/service/index.js +69 -1
  125. package/dist/lib/reporting/service/index.mjs +71 -1
  126. package/dist/lib/schemas/browser.d.mts +14 -0
  127. package/dist/lib/schemas/browser.d.ts +14 -0
  128. package/dist/lib/schemas/browser.js +48 -0
  129. package/dist/lib/schemas/browser.mjs +20 -0
  130. package/dist/lib/schemas/index.d.mts +2 -120
  131. package/dist/lib/schemas/index.d.ts +2 -120
  132. package/dist/lib/schemas/index.js +3 -11
  133. package/dist/lib/schemas/index.mjs +1 -9
  134. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  135. package/dist/lib/testing/index.d.mts +1 -0
  136. package/dist/lib/testing/index.d.ts +1 -0
  137. package/dist/lib/testing/index.js +3 -0
  138. package/dist/lib/testing/index.mjs +2 -0
  139. package/dist/lib/testing/pending-web-flow-store.d.mts +20 -0
  140. package/dist/lib/testing/pending-web-flow-store.d.ts +20 -0
  141. package/dist/lib/testing/pending-web-flow-store.js +132 -0
  142. package/dist/lib/testing/pending-web-flow-store.mjs +98 -0
  143. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  144. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  145. package/dist/lib/validation/get-products-request.d.mts +122 -0
  146. package/dist/lib/validation/get-products-request.d.ts +122 -0
  147. package/dist/lib/validation/get-products-request.js +39 -0
  148. package/dist/lib/validation/get-products-request.mjs +14 -0
  149. package/dist/lib/version.d.mts +3 -3
  150. package/dist/lib/version.d.ts +3 -3
  151. package/dist/lib/version.js +3 -3
  152. package/dist/lib/version.mjs +3 -3
  153. package/docs/README.md +1 -1
  154. package/docs/TYPE-SUMMARY.md +35 -2
  155. package/docs/ZOD-SCHEMAS.md +13 -1
  156. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +142 -0
  157. package/docs/guides/EXISTING-PLATFORM.md +43 -12
  158. package/docs/guides/OAUTH-ISSUER-BINDING.md +6 -0
  159. package/docs/guides/REPORTING-LEDGER.md +374 -0
  160. package/docs/guides/REPORTING-OPERATIONS.md +76 -1
  161. package/docs/guides/WEB-OAUTH.md +48 -1
  162. package/docs/llms.txt +4 -2
  163. package/docs/migration-14.x-rc-worksheet.md +6 -4
  164. package/package.json +64 -1
@@ -272,6 +272,380 @@ Official configurations also pin a `finalityPolicy` (`policyId` plus `source_fin
272
272
 
273
273
  Every revision stores its rows together with an RFC 8785 JCS SHA-256 binding and exact decimal control totals for requested numeric metrics. A revision number and obligation are immutable. Official revisions are terminal; later source corrections are immutable adjustments bound to the official revision, never superseding revisions. Status snapshots omit row payloads, are capped at 8 MiB, expire after 15 minutes, and keep cursor pages stable over the flat obligation/revision/adjustment union. A periods response returns an opaque `changes_checkpoint`; echo that value verbatim as `changes_after` rather than supplying a timestamp. Account-scoped write/snapshot locks make those checkpoints gap-free for SDK store writes. The default table set is deployment-wide; use a dedicated database/schema and acknowledge that boundary explicitly. `sourceScope` must contain opaque routing identities only—never credentials or bearer tokens—because it is retained with the obligation. Retained resource locations are held to the same rule, and at the seam that persists them rather than only in the worker's pre-flight, because a caller driving `settleMaterialization` directly would otherwise store a presigned location that `get_reporting_status` then publishes. Query and fragment are refused on the raw string rather than on a successful parse, because a relative path carrying a presigning query never parsed as a URL at all; userinfo is refused as a colon-separated pair before the `@`, which is the credential shape, plus any `http(s)` userinfo at all. A blanket `@` rule would refuse `abfss://container@account.dfs.core.windows.net/...` and a Snowflake stage reference, neither of which carries a secret.
274
274
 
275
+ ## Row storage
276
+
277
+ By default every revision document carries its rows inline. At pulse cadence
278
+ that keeps bulk report bytes in the transactional database. Opt in to row
279
+ storage to keep only the revision header in the ledger document and store
280
+ rows as verified canonical JSONL chunks (the shared SDK persistence layout,
281
+ adcontextprotocol/adcp#7996):
282
+
283
+ ```ts
284
+ import { REPORTING_ROW_STORAGE_MIGRATION } from '@adcp/sdk/reporting/ledger';
285
+
286
+ await pool.query(REPORTING_ROW_STORAGE_MIGRATION); // after REPORTING_LEDGER_MIGRATION; PostgreSQL 13+
287
+ const store = new PostgresReportingLedgerStore(pool, {
288
+ acknowledgeIsolatedDatabase: true,
289
+ rowStorage: true,
290
+ });
291
+ await store.readyRowStorage();
292
+ ```
293
+
294
+ `createPostgresReliableReportingProductionService` accepts the same
295
+ `rowStorage` option and adds the migration to `setup.migrations`.
296
+
297
+ - Rows are split into 500-row segments and chunks of at most 10,000 rows or
298
+ 8 MiB. Chunk, segment and manifest digests are committed with the revision,
299
+ and every read is verified before rows are released. Delivery pages read only
300
+ the segments they need.
301
+ - A revision whose rows are byte-identical to a live row set in the same
302
+ obligation is stored as a header-only revision. It gets a new identity,
303
+ freshness and binding, and references the existing chunks
304
+ (`rows_shared_from_row_set_id`), so an unchanged pulse uploads nothing.
305
+ - Revisions written before you enable row storage keep their inline rows.
306
+ Reads handle both forms whether or not the option is set.
307
+ - Missing or corrupt rows inside the retention window make
308
+ `get_media_buy_delivery` fail with `SERVICE_UNAVAILABLE` rather than reporting
309
+ the revision as absent; expired rows read like an unknown revision.
310
+ - Managed Delivery claims hydrate PostgreSQL-stored rows automatically.
311
+
312
+ To move existing inline rows into row storage, run the resumable migration in
313
+ bounded batches. A revision whose rows do not reproduce its committed binding is
314
+ reported in `quarantined` and left untouched:
315
+
316
+ ```ts
317
+ let cursor: string | undefined;
318
+ do {
319
+ const batch = await store.migrateInlineRows({ limit: 100, cursor });
320
+ if (batch.quarantined.length) console.warn('unverifiable inline rows', batch.quarantined);
321
+ cursor = batch.cursor;
322
+ } while (cursor);
323
+ ```
324
+
325
+ Removing rows from ledger documents frees space only after `VACUUM FULL` or an
326
+ online repack of `adcp_reporting_revisions` and `adcp_reporting_adjustments`.
327
+
328
+ ### Object storage bindings
329
+
330
+ Rows can live in object storage instead of PostgreSQL. A binding is defined in
331
+ host code, which makes the configuration the allowlist: the database records
332
+ which binding each revision used and its identity digest, but it can never
333
+ point reads or writes at a destination the host did not configure.
334
+
335
+ ```ts
336
+ import {
337
+ PostgresReportingLedgerStore,
338
+ createFilesystemReportingRowObjectProviderV1,
339
+ } from '@adcp/sdk/reporting/ledger';
340
+
341
+ const store = new PostgresReportingLedgerStore(pool, {
342
+ acknowledgeIsolatedDatabase: true,
343
+ rowStorage: {
344
+ deploymentNamespace: 'prod-us',
345
+ bindings: {
346
+ inline: { kind: 'postgres' },
347
+ reports: {
348
+ kind: 'object',
349
+ provider: 'filesystem', // or a cloud provider registered below
350
+ location: { root: 'reports' },
351
+ prefix: 'adcp-rows',
352
+ compression: 'gzip',
353
+ },
354
+ },
355
+ providers: {
356
+ filesystem: createFilesystemReportingRowObjectProviderV1({ roots: { reports: '/var/lib/adcp/rows' } }),
357
+ },
358
+ selectRowBinding: ({ canonical_byte_count }) => (canonical_byte_count < 256 * 1024 ? 'inline' : 'reports'),
359
+ },
360
+ });
361
+ await store.readyRowStorage();
362
+ ```
363
+
364
+ - Each chunk is one object, written create-only. If an object already exists at
365
+ a planned key, it is adopted only when its bytes decode to the exact chunk;
366
+ otherwise the commit fails with `CONTENT_CONFLICT`.
367
+ - Keys always start with `{prefix}/{namespace_key}/` and use a hashed account
368
+ segment, never a raw account ID. Override `keyTemplate` with the placeholders
369
+ in `REPORTING_ROW_KEY_PLACEHOLDERS`; templates must include
370
+ `{content_sha256}` and `{chunk_index}`.
371
+ - An upload runs only while the obligation lease still covers
372
+ `writeDeadlineMilliseconds` plus five seconds. A fenced write intent records
373
+ every object the upload created, and the ledger commit closes the intent in
374
+ the same transaction. Objects of an upload that never committed are left for
375
+ the intent sweep.
376
+ - Every read verifies the stored bytes against the recorded physical digest
377
+ before decompressing, then verifies segments, chunks and the revision binding.
378
+ - Keep a retired binding configured while revisions still reference it.
379
+ - The filesystem provider identifies each write by inode, birth time, mtime
380
+ and size, stamping a random sub-millisecond mtime on every write. It needs a
381
+ filesystem with sub-second mtime and, ideally, birth time (ext4, XFS, APFS,
382
+ NTFS). On a coarse filesystem (FAT, some NFS mounts, ext3), a reused inode
383
+ re-created with identical bytes in the same second gets the same version,
384
+ so a delayed delete could remove the replacement. The conformance check
385
+ `write-unique-version` fails on such filesystems. That failure means the
386
+ filesystem is wrong, not the code.
387
+ - Custom providers implement `ReportingRowObjectProviderV1`. Run
388
+ `runReportingRowObjectProviderConformanceV1(provider, { location, prefix })`
389
+ against a dedicated test prefix before using one.
390
+
391
+ #### Cloud providers
392
+
393
+ Cloud providers wrap official clients that the host constructs with its own
394
+ credentials. A binding's `location` holds only non-secret coordinates, and its
395
+ optional `credentialRef` names one of the clients you injected; an unknown
396
+ reference fails `UNSAFE_BINDING`. Provider errors surface only as stable
397
+ `ReportingRowStoreError` codes. Row buckets are separate from Managed Delivery
398
+ buckets.
399
+
400
+ **Google Cloud Storage** (`@adcp/sdk/reporting/gcs`, optional peer
401
+ `@google-cloud/storage`):
402
+
403
+ ```ts
404
+ import { Storage } from '@google-cloud/storage';
405
+ import { createGcsReportingRowObjectProviderV1 } from '@adcp/sdk/reporting/gcs';
406
+
407
+ const gcs = createGcsReportingRowObjectProviderV1({
408
+ storage: new Storage(), // keyless credentials (workload identity / ADC)
409
+ clients: { 'tenant-eu': euStorage }, // optional, selected by credentialRef
410
+ });
411
+ // binding: { kind: 'object', provider: 'gcs', location: { bucket: 'acme-adcp-rows' }, prefix: 'adcp-rows' }
412
+ // providers: { gcs }
413
+ ```
414
+
415
+ - Writes are single-request uploads with `ifGenerationMatch: 0` and a declared
416
+ MD5, without `Content-Encoding`. The recorded native version is the object
417
+ generation; reads and deletes pin it, and reads never transcode.
418
+ - The probe requires uniform bucket-level access and enforced public access
419
+ prevention. Versioning, soft delete and retention policies are allowed. A
420
+ lifecycle `Delete` rule that can match live objects under the binding prefix
421
+ (no `matchesPrefix`, or a prefix that overlaps it) is refused; rules limited
422
+ to noncurrent versions (`isLive: false`, `numNewerVersions`,
423
+ `daysSinceNoncurrentTime`, `noncurrentTimeBefore`) are allowed. Keep
424
+ retention-driven deletion in the ledger's pruning, not in bucket lifecycle.
425
+ - The client needs `storage.buckets.get` for the probe plus object create, get
426
+ and delete on the bucket.
427
+
428
+ **Amazon S3 and S3-compatible stores** (`@adcp/sdk/reporting/s3`, optional peer
429
+ `@aws-sdk/client-s3`):
430
+
431
+ ```ts
432
+ import { S3Client } from '@aws-sdk/client-s3';
433
+ import { createS3ReportingRowObjectProviderV1 } from '@adcp/sdk/reporting/s3';
434
+
435
+ const s3 = createS3ReportingRowObjectProviderV1({
436
+ client: new S3Client({ region: 'us-east-1' }), // default credential chain
437
+ });
438
+ // binding: { kind: 'object', provider: 's3', location: { bucket: 'acme-adcp-rows', region: 'us-east-1' }, prefix: 'adcp-rows' }
439
+ // providers: { s3 }
440
+ ```
441
+
442
+ - Endpoint, region, credentials and path style come only from the client you
443
+ inject; `location` accepts just `bucket` and an optional `region`, which the
444
+ probe checks against the client.
445
+ - **Bucket versioning is required.** Writes use `PutObject` with
446
+ `If-None-Match: *` (a 409 is retried once). The recorded native version is
447
+ the write-unique `VersionId`; reads pin it with `VersionId`, and deletes
448
+ confirm the exact version with `HeadObject`, then delete by `VersionId`.
449
+ Deletes must be fenced to one write, and an ETag identifies content, not a
450
+ write: a delayed delete (for example an intent sweep removing an abandoned
451
+ upload) pinned to an ETag would also remove a replacement object later
452
+ re-created with identical bytes at the same key. The probe therefore refuses
453
+ any bucket whose `GetBucketVersioning` status is not `Enabled` (suspended or
454
+ unversioned), and `putIfAbsent` refuses a result with no `VersionId`.
455
+ Deleting a specific `VersionId` permanently removes that version without
456
+ leaving a delete marker, so retention still frees storage; noncurrent-version
457
+ lifecycle rules are not needed for rows this provider deletes.
458
+ - S3-compatible stores may ignore `If-None-Match`, so the probe writes a probe
459
+ key under the binding prefix twice and requires the second write to fail with
460
+ 412, then deletes it. It also refuses missing or inaccessible buckets, a
461
+ bucket policy reported as public, and enabled lifecycle `Expiration` rules
462
+ that can match the prefix. The probe cannot verify Block Public Access on
463
+ every store, so keep row buckets private.
464
+ - The client needs `s3:ListBucket` (HeadBucket),
465
+ `s3:GetLifecycleConfiguration`, `s3:GetBucketVersioning`, object put/get,
466
+ `s3:GetObjectVersion` and `s3:DeleteObjectVersion`.
467
+ `s3:GetBucketPolicyStatus` is optional. For stores without flexible
468
+ checksums, construct the client with `requestChecksumCalculation: 'WHEN_REQUIRED'`.
469
+
470
+ **Azure Blob Storage** (`@adcp/sdk/reporting/azure`, optional peer
471
+ `@azure/storage-blob`):
472
+
473
+ ```ts
474
+ import { DefaultAzureCredential } from '@azure/identity';
475
+ import { BlobServiceClient } from '@azure/storage-blob';
476
+ import { createAzureBlobReportingRowObjectProviderV1 } from '@adcp/sdk/reporting/azure';
477
+
478
+ const azure = createAzureBlobReportingRowObjectProviderV1({
479
+ client: new BlobServiceClient('https://acmerows.blob.core.windows.net', new DefaultAzureCredential()),
480
+ });
481
+ // binding: { kind: 'object', provider: 'azure_blob', location: { account: 'acmerows', container: 'adcp-rows' }, prefix: 'adcp-rows' }
482
+ // providers: { azure_blob: azure }
483
+ ```
484
+
485
+ - `location` names the storage `account` and `container`; the selected client
486
+ must belong to that account. The endpoint and credentials come only from
487
+ the client.
488
+ - **Blob versioning is required** on the storage account. Writes are
489
+ block-blob uploads with `If-None-Match: *`. A 409 `BlobAlreadyExists` or 412
490
+ adopts the existing blob. The recorded native version is the write-unique
491
+ `versionId`; reads and deletes pin it, and only count a response that echoes
492
+ that exact version. ETags are content-derived, so they cannot fence a delayed
493
+ delete away from an identical re-upload at the same key. The probe's
494
+ create-only write must return a `versionId` (otherwise `UNSAFE_BINDING`), and
495
+ `putIfAbsent` refuses a result without one. Deleting a specific version
496
+ permanently removes it, so retention still frees storage. Azurite does not
497
+ implement blob versioning and cannot back this provider. Metadata names are
498
+ stored as C# identifiers (`adcp_intent`).
499
+ - The probe refuses missing or inaccessible containers and containers with
500
+ anonymous access. Like S3, it proves create-only writes empirically with a
501
+ probe blob under the prefix. Lifecycle management policies are account-level
502
+ and are not visible to the data-plane client, so make sure none can delete
503
+ blobs under the binding prefix.
504
+ - The client needs read, write and delete on blobs in the container (for
505
+ example `Storage Blob Data Contributor`), plus permission to delete
506
+ versions.
507
+
508
+ ### Retention
509
+
510
+ Retention is period-aligned and opt-in. A period becomes eligible once both its
511
+ end and its latest publication are older than `max(statusRetentionDays,
512
+ recordRetentionDays)`, no worker holds its lease, and no Managed Delivery
513
+ materialization still references it. Superseded snapshots stay readable for the
514
+ whole window, as the protocol requires.
515
+
516
+ When the change feed is installed, retention also waits for registered feed
517
+ consumers. A period is held while any live consumer (one whose cursor was saved
518
+ within `maxFeedHoldDays`, default 7) has not yet passed that period's change
519
+ rows, in the consumer's own feed order. This is the same rule `pruneChanges`
520
+ uses, so a warehouse sink that is briefly down does not lose revisions it has
521
+ not loaded. A held period is simply not retired on this pass. A consumer silent
522
+ for longer than `maxFeedHoldDays` stops holding, so a dead consumer cannot block
523
+ retention forever; it has then fallen behind retention (see the warehouse sink
524
+ below). Pass `maxFeedHoldDays` to `retireExpiredPeriods`; the production service
525
+ passes `changeFeed.maxFeedHoldDays` for you so retention and feed pruning agree.
526
+
527
+ ```ts
528
+ await store.retireExpiredPeriods({ statusRetentionDays: 30, recordRetentionDays: 45, limit: 100 });
529
+ await store.sweepRowWriteIntents({ limit: 100 }); // abandoned object uploads
530
+ ```
531
+
532
+ Retirement runs in three resumable phases. First it records a `retiring`
533
+ tombstone and marks the period's rows as expired. Then it deletes external row
534
+ objects at their recorded versions. Finally it deletes the period's rows and
535
+ ledger records together. The tombstone keeps the final revision ID and digest,
536
+ and planners never re-create a retired period. `ledger_retained_from` advances
537
+ because the retired obligations no longer exist.
538
+
539
+ `createPostgresReliableReportingProductionService` accepts
540
+ `retention: { enabled: true, recordRetentionDays?, limit? }`. Its scheduler
541
+ always sweeps abandoned row uploads and expired cursor snapshots and
542
+ checkpoints, and retires periods when retention is enabled.
543
+
544
+ ### Host reads: current revisions and the change feed
545
+
546
+ In-process consumers such as pacing, alerts and warehouse loaders read from the
547
+ ledger, never from a warehouse copy.
548
+
549
+ ```ts
550
+ import { REPORTING_LEDGER_CHANGES_MIGRATION } from '@adcp/sdk/reporting/ledger';
551
+
552
+ await pool.query(REPORTING_LEDGER_CHANGES_MIGRATION); // PostgreSQL 13+
553
+ const store = new PostgresReportingLedgerStore(pool, { acknowledgeIsolatedDatabase: true, changeFeed: true });
554
+
555
+ const current = await store.getCurrentRevision({ account_id, reporting_obligation_id });
556
+ const { revisions, cursor: next } = await store.listCurrentRevisions({
557
+ account_id,
558
+ period_start_from: '2026-10-01T00:00:00Z',
559
+ period_start_to: '2026-10-08T00:00:00Z',
560
+ });
561
+
562
+ let cursor = await loadCursor();
563
+ const page = await store.changesAfter({ account_id, cursor, kinds: ['revision'] });
564
+ // ...apply page.records...
565
+ await store.saveFeedConsumerCursor('pacing', page.cursor); // holds back pruning
566
+ ```
567
+
568
+ - The current revision is the official revision if one exists, otherwise the
569
+ highest-numbered snapshot. An official revision is terminal, so this is always
570
+ one index lookup.
571
+ - `changesAfter` lists committed obligation, revision, adjustment and retirement
572
+ changes. Account feeds order by sequence. The deployment-wide feed (omit
573
+ `account_id`) orders by `(xid, seq)` and returns only changes whose
574
+ transaction precedes every transaction still in flight, so a slow commit is
575
+ never skipped.
576
+ - `pruneChanges` deletes changes older than `changeRetentionDays` (default 30)
577
+ that every consumer updated within `maxFeedHoldDays` (default 7) has passed.
578
+ An older cursor throws `ReportingChangeCursorExpiredError`; resynchronize from
579
+ `listCurrentRevisions`.
580
+ - Period retention honours the same hold: `retireExpiredPeriods` does not retire
581
+ a period while a live consumer has not read its changes. A consumer that
582
+ stays silent past `maxFeedHoldDays` stops holding, and its unread changes can
583
+ then point at revisions whose period was retired.
584
+ - The production service accepts `changeFeed: true | { changeRetentionDays,
585
+ maxFeedHoldDays }` and prunes the feed on its scheduler.
586
+
587
+ ### BigQuery warehouse sink
588
+
589
+ `@adcp/sdk/reporting/bigquery` loads committed revisions into your own BigQuery
590
+ tables for analysts. The warehouse copy is derived and non-authoritative. It
591
+ is never read for serving or verification, so its table names, columns,
592
+ partitioning and retention are yours.
593
+
594
+ ```ts
595
+ import { BigQuery } from '@google-cloud/bigquery';
596
+ import {
597
+ REPORTING_WAREHOUSE_SINK_MIGRATION,
598
+ createBigQueryReportingWarehouseSinkV1,
599
+ } from '@adcp/sdk/reporting/bigquery';
600
+
601
+ await pool.query(REPORTING_WAREHOUSE_SINK_MIGRATION); // after REPORTING_LEDGER_CHANGES_MIGRATION
602
+ const sink = createBigQueryReportingWarehouseSinkV1({
603
+ store, // PostgresReportingLedgerStore with changeFeed: true
604
+ db: pool,
605
+ bigquery: new BigQuery({ projectId: 'analytics-prod' }), // ADC / workload identity
606
+ name: 'warehouse',
607
+ projectId: 'analytics-prod',
608
+ datasetId: 'reporting',
609
+ rowsTableId: 'media_buy_delivery_rows',
610
+ revisionsTableId: 'media_buy_delivery_revisions',
611
+ location: 'US',
612
+ mapRow: row => ({ media_buy_id: row.media_buy_id, impressions: row.impressions, spend: row.spend }),
613
+ labels: { component: 'adcp-reporting' },
614
+ });
615
+ await bigquery.query({ query: sink.defaultTablesSql }); // or create your own tables
616
+ await bigquery.query({ query: sink.currentRowsViewSql });
617
+ setInterval(() => void sink.runOnce(), 15 * 60_000);
618
+ ```
619
+
620
+ - Each run follows the change feed and reads every revision's rows through the
621
+ store's verified reader. It loads rows first and revision metadata second,
622
+ in one load job per table per tick. The sink always adds
623
+ `reporting_revision_id`, `reporting_obligation_id`, `account_id`,
624
+ `revision_number`, `finality`, `period_date` (the period's date in its source
625
+ timezone) and `ordinal` to your mapped columns.
626
+ - Batches are planned durably before loading, and job IDs derive from the
627
+ change-feed range. A crash between a load and the cursor commit replays the
628
+ same batch under the same job ID, which BigQuery refuses to run twice. A job
629
+ that finished with an error is retried under a new attempt for that table
630
+ only.
631
+ - Query `current_rows`, not the raw rows table. Pacing snapshots are
632
+ cumulative, so summing across revisions double-counts. The view keeps each
633
+ obligation's current revision.
634
+ - The sink registers as a change-feed consumer, so `pruneChanges` keeps changes
635
+ it has not loaded yet, and `retireExpiredPeriods` keeps the periods it has not
636
+ loaded yet, in both cases only while its cursor was saved within
637
+ `maxFeedHoldDays`. Superseded snapshots are the intraday pacing curve, and the
638
+ warehouse may keep them as long as you like.
639
+ - `runOnce()` resolves with `{ idle, revisions, rows, missing }`. `missing`
640
+ lists revision IDs the feed announced that were retired before the batch ran.
641
+ A non-empty `missing` means the sink fell behind retention (it was down longer
642
+ than `maxFeedHoldDays` plus the retention window): the warehouse lacks those
643
+ revisions. Backfill them from another source or accept the gap. The cursor
644
+ still advances so the sink is not stuck. After a crash and replay of a planned
645
+ batch, a listed revision may already have been loaded by the first attempt.
646
+ - The sink uses only `dataset().table().load()`, `job().getMetadata()` and
647
+ `query()` from the official client you construct. It adds no dependency.
648
+
275
649
  ## Managed Delivery and Reconciled Billing
276
650
 
277
651
  Core remains the default and has no destination, external-resource, or receipt dependency. To opt into the higher tiers, apply `REPORTING_MANAGED_DELIVERY_MIGRATION` **after** `REPORTING_LEDGER_MIGRATION`, explicitly construct the Core store with `managedDelivery: true`, create a `PostgresReportingManagedDeliveryStore`, and pass both stores with a destination adapter to `createReportingManagedDeliveryRuntime`. The async factory proves the stores share one authority and validates the RC3 tier wiring before returning it. It advertises `managed_delivery` only when an immutable binding, delivery, bounded resource reading, generation-fenced revocation, and at least one verification profile are installed. The advertised automated recovery window must be at least the widest installed managed Core configuration recovery window. It advertises `reconciled_billing` and `receipt_task` only when an authenticated consumer resolver and canonical-digest verification are also installed.
@@ -72,6 +72,65 @@ different operators own tenant partitions. For Reconciled Billing offerings,
72
72
  provide `obligatedConsumers` from a trusted authorization roster. A missing or
73
73
  incomplete roster cannot safely mark a billing obligation reconciled.
74
74
 
75
+ ## Adopting row storage, retention and the change feed
76
+
77
+ Each of these features is opt-in and additive. Enable them one at a time on a
78
+ running deployment. See the [ledger guide](REPORTING-LEDGER.md#row-storage)
79
+ for configuration details.
80
+
81
+ 1. **Row storage.** Apply `REPORTING_ROW_STORAGE_MIGRATION` (PostgreSQL 13+),
82
+ or pass `rowStorage` to the production service, which adds it to
83
+ `setup.migrations`. Roll out with `rowStorage` set. New revisions store
84
+ verified chunks. Existing revisions keep their inline rows, and every
85
+ binary built with row storage reads both forms. Do not run a writer older
86
+ than this release once chunked revisions exist: it cannot read them.
87
+ 2. **Existing inline rows (optional).** Run `migrateInlineRows` in bounded
88
+ batches from a maintenance job until it returns no cursor. Alert on any
89
+ `quarantined` IDs: those revisions no longer reproduce their committed
90
+ binding and are left untouched for investigation. Then reclaim space with
91
+ `VACUUM FULL` or an online repack in a maintenance window.
92
+ 3. **Object storage.** Create a dedicated private bucket, container or prefix
93
+ for rows, separate from Managed Delivery buckets. Configure the binding in
94
+ host code and run `runReportingRowObjectProviderConformanceV1` against a
95
+ test prefix first. S3 bucket versioning and Azure blob versioning are
96
+ **required**, and the providers' probes refuse unversioned buckets and
97
+ accounts: deletes must be fenced to one write, and ETags are content-derived,
98
+ so an ETag-pinned delayed delete (for example an intent sweep) could remove a
99
+ replacement object re-created with identical bytes at the same key. Deleting
100
+ a specific `VersionId` permanently removes that version (no delete marker),
101
+ so retention still frees storage. Do not
102
+ attach lifecycle rules that can delete live rows sooner than
103
+ `max(statusRetentionDays, recordRetentionDays)` plus the restatement window.
104
+ The GCS probe refuses them; for S3 and Azure, policies outside the client's
105
+ view are the operator's responsibility.
106
+ 4. **Change feed.** Apply `REPORTING_LEDGER_CHANGES_MIGRATION` (PostgreSQL 13+)
107
+ or pass `changeFeed`. Register each durable consumer with
108
+ `saveFeedConsumerCursor`, and size `maxFeedHoldDays` so one stalled consumer
109
+ cannot hold the table indefinitely.
110
+ 5. **Warehouse sink.** Apply `REPORTING_WAREHOUSE_SINK_MIGRATION`, create the
111
+ tables, and the `current_rows` view, then schedule `runOnce`. Point analysts
112
+ at `current_rows`.
113
+ 6. **Retention.** Enable `retention` last, after confirming the advertised
114
+ `status_retention_days` is the window you intend to keep. Retirement deletes
115
+ rows and ledger records for whole periods and cannot be undone. Keep backups
116
+ for any longer audit requirement.
117
+
118
+ Alert on these row-storage signals:
119
+
120
+ - `ROWS_INTEGRITY_FAILED` or `ROWS_UNAVAILABLE` from reads. These are never
121
+ normal, and buyers see `SERVICE_UNAVAILABLE` for the affected revision.
122
+ - A growing count of `adcp_reporting_row_write_intents` in `sweeping` state,
123
+ or `sweepRowWriteIntents` reporting failures. Provider deletes are failing.
124
+ - `retireExpiredPeriods` returning `failed` IDs. Usually a binding was removed
125
+ from configuration while revisions still reference it; keep retired bindings
126
+ configured.
127
+ - `ReportingChangeCursorExpiredError` in a consumer. That consumer fell behind
128
+ retention and must resynchronize from `listCurrentRevisions`.
129
+ - A non-empty `missing` from the warehouse sink's `runOnce()`. The sink was down
130
+ longer than `maxFeedHoldDays`, retention stopped waiting for it, and the
131
+ warehouse lacks those revisions. Backfill them or accept the gap, and keep
132
+ `maxFeedHoldDays` above your longest tolerated sink outage.
133
+
75
134
  ## Service objectives and alerts
76
135
 
77
136
  Choose targets stricter than contractual delivery SLAs. A reasonable starting
@@ -146,7 +205,17 @@ Quarterly, restore into an isolated environment and verify:
146
205
  - notification recovery resumes without duplicate logical events;
147
206
  - buyer cursors/checkpoints resume and duplicate notification keys remain deduped;
148
207
  - a new lease fences an expired owner;
149
- - sampled manifest and canonical-content digests still verify.
208
+ - sampled manifest and canonical-content digests still verify;
209
+ - sampled chunked revisions read back through `getRevision`, including rows in
210
+ object-storage bindings.
211
+
212
+ Row objects live outside the database. A database restore and the matching
213
+ row buckets must come from the same point in time, or revisions committed
214
+ after the bucket snapshot read as `ROWS_UNAVAILABLE`. A restored replica of
215
+ the same authority keeps its installation identity. An independently writable
216
+ clone, such as staging restored from production, must mint a new
217
+ `adcp_persistence_installation` row and use new bindings before it writes or
218
+ sweeps, so it can never delete production objects.
150
219
 
151
220
  After regional failover, fence the old primary before enabling writers. Ensure
152
221
  database time is healthy, then start canary workers and watch lease generation,
@@ -177,6 +246,12 @@ Disable notification-triggered acceleration if necessary, retain dedupe rows,
177
246
  and repair the authenticated seller/principal registry before resuming. Never
178
247
  choose an account based only on an untrusted webhook body.
179
248
 
249
+ Row-storage integrity failure: stop serving the affected scope and do not
250
+ re-upload over the recorded object. Compare the stored object's bytes and
251
+ version with the chunk manifest, restore the exact recorded version from bucket
252
+ versioning or backups, and verify by reading the revision through `getRevision`.
253
+ Never edit a chunk manifest or digest to match the stored bytes.
254
+
180
255
  Clock skew: remove the host from service. Lease and event times use PostgreSQL
181
256
  where correctness needs a shared clock, while provider deadlines and process
182
257
  timeouts still depend on healthy host clocks.
@@ -69,10 +69,57 @@ Auth0 `audience`. With storage, overrides persist/reuse on omission; successful
69
69
  `PendingWebFlowStore`: `put(flow)` and atomic `consume(state)`, e.g. PostgreSQL
70
70
  `DELETE ... WHERE state = $1 AND expires_at > now() RETURNING payload`, or Redis
71
71
  `SET pending:flow:<state> <payload> EX 600 NX` then `GETDEL`. SELECT/DELETE races
72
- permit replay. Store contract tests: `test/lib/oauth-web-flow.test.js`.
72
+ permit replay. Check your store with `assertPendingWebFlowStoreRoundTrip` (below).
73
73
  `DEFAULT_WEB_FLOW_TTL_MS` is 10 minutes; shorten as needed. `InMemoryPendingFlowStore` is
74
74
  test/dev-only; restarts lose flows. Encrypt PKCE verifiers at rest across trust boundaries.
75
75
 
76
+ Use `serializePendingWebFlow` and `parsePendingWebFlow` from `@adcp/sdk/auth`
77
+ for the stored payload. The parser validates SDK-owned fields, revives `createdAt`
78
+ and `expiresAt` as `Date` values, and returns `null` for malformed input. It accepts
79
+ JSON strings and decoded JSON values such as `pg`'s `jsonb` payloads. It preserves
80
+ unknown fields and all client metadata, including `issuer` and `client_secret`.
81
+ Do not narrow the persisted shape to today's fields: a hand-written schema that
82
+ strips new optional fields can type-check while breaking sign-in after an SDK upgrade.
83
+
84
+ ```ts
85
+ import { parsePendingWebFlow, serializePendingWebFlow } from '@adcp/sdk/auth';
86
+ import { assertPendingWebFlowStoreRoundTrip } from '@adcp/sdk/testing';
87
+
88
+ // In put(flow), serialize before encrypting and inserting with duplicate rejection:
89
+ const payload = serializePendingWebFlow(flow);
90
+
91
+ // In consume(state), decrypt the payload returned by atomic get-and-delete:
92
+ function restoreConsumedFlow(state: string, decryptedPayload: unknown) {
93
+ const restored = parsePendingWebFlow(decryptedPayload);
94
+ return restored && restored.state === state && restored.expiresAt.getTime() > Date.now()
95
+ ? restored
96
+ : null;
97
+ }
98
+
99
+ // In CI, use a disposable instance of your production store on each SDK upgrade:
100
+ await assertPendingWebFlowStoreRoundTrip(pendingFlowStore);
101
+ // Stores with application-owned constraints can supply valid fixture values:
102
+ await assertPendingWebFlowStoreRoundTrip(pendingFlowStore, {
103
+ agentId: testAgentId,
104
+ carry: { user_id: testUserId, nested: { values: ['one', 2, null] } },
105
+ });
106
+ ```
107
+
108
+ The helper checks every current SDK field (including nested `carry` and issuer-bound
109
+ client secrets), Date revival, missing states, single use, expired rows, duplicate
110
+ rejection, and concurrent consume. It also checks null/absent snapshots, clear actions,
111
+ public clients, registration metadata, and unknown extensions. Stores may reject
112
+ already-expired inserts; the helper still checks that consumption returns `null`.
113
+ Its fixture is exhaustive at SDK build time, including optional flow and client
114
+ information fields. The concurrency check is a smoke test; the backend must
115
+ provide atomic deletion across processes. Use JSON-serializable `carry` and validate
116
+ its application-owned contents separately. Keep absent optional fields absent when
117
+ hydrating rows; an undefined-valued own `resourceOverrideSnapshot` can trigger the
118
+ callback's concurrent-edit guard. The store still owns encryption of PKCE
119
+ and client secrets, TTL, duplicate rejection, atomic consume, and binding the payload's
120
+ `state` to its storage key. The parser does not filter expired flows or establish
121
+ issuer trust; `completeWebOAuthFlow` also enforces expiry and issuer binding.
122
+
76
123
  Optional `agentStorage` (`OAuthConfigStorage`): callback loads the pending row's
77
124
  agent ID. Without it, completion
78
125
  returns `persisted: false` for application persistence. Preserve the issuer guide's
package/docs/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ad Context Protocol (AdCP)
2
2
 
3
- > Generated at: 2026-10-08
4
- > Library: @adcp/sdk v15.4.0
3
+ > Generated at: 2026-10-09
4
+ > Library: @adcp/sdk v15.6.0
5
5
  > AdCP major version: 3
6
6
  > Canonical URL: https://adcontextprotocol.github.io/adcp-client/llms.txt
7
7
  > Note: the `Library` stamp reflects the package.json version at doc-generation time. The narrative below describes the surface that lands on the next-published minor — including any 6.7 helpers documented here ahead of the release tag.
@@ -136,6 +136,8 @@ A submitted mutation must settle before it can be controlled; retain the task ha
136
136
 
137
137
  **Existing applications.** The [thin existing-platform recipe](./guides/EXISTING-PLATFORM.md) shows one SDK task inside application-owned auth and transactions, durable submitted-task recording, the error/cancellation matrix, non-blocking bounded diagnostics, and same-instance capability evidence reuse with `AgentClient.createWithCapabilityPreflight()` (or the lower-level `getCapabilityEvidenceScope()` + `primeCapabilities()` pair). The SDK 14 release represented by the checkout, its registry integrity check, peer/runtime ranges, wire pin, and a historical rc.33/rc.35 → rc.36 example are generated in [the release worksheet](./migration-14.x-rc-worksheet.md).
138
138
 
139
+ **Request-local clients.** Share endpoint and capability discovery with an application-owned `discoveryCache` and explicit authorization identity. The public `withMCPConnectionScope()` groups a workflow's MCP calls onto reusable sessions; correlation headers, cancellation and deadlines remain per call. See [Discovery cache and session reuse](./guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md).
140
+
139
141
  ## Canonical Reference Resolver
140
142
 
141
143
  `format_schema` and `platform_extensions` references use immutable `{ uri, digest }` pointers. Use `createCanonicalReferenceResolver` from `@adcp/sdk/canonical-references` instead of raw fetches; it applies SSRF-safe DNS-pinned fetches, redirect blocking, timeout/body caps, SHA-256 verification, structured non-throwing statuses, and bounded policy-scoped LRU caching. The zero-argument cache holds at most 64 entries / 32 MiB estimated retained data; use `createCanonicalReferenceCache({ maxEntries, maxBytes })` to tune the per-resolver budget or inject a fully caller-owned cache.
@@ -11,8 +11,8 @@ below as the publication/deployment gate.
11
11
 
12
12
  | Fact | Value |
13
13
  | --- | --- |
14
- | Exact npm package | `@adcp/sdk@15.4.0` |
15
- | npm integrity | registry-derived after publication; run `npm view @adcp/sdk@15.4.0 dist.integrity` |
14
+ | Exact npm package | `@adcp/sdk@15.6.0` |
15
+ | npm integrity | registry-derived after publication; run `npm view @adcp/sdk@15.6.0 dist.integrity` |
16
16
  | Node.js runtime | `^20.19.0 || >=22.12.0` |
17
17
  | Default AdCP wire release | `3.2.3` |
18
18
  | Maintained wire releases | `v2.5`, `v2.6`, `v3`, `3.0.0`, `3.0`, `3.0.1`, `3.0.2`, `3.0.3`, `3.0.4`, `3.0.5`, `3.0.6`, `3.0.7`, `3.0.8`, `3.0.9`, `3.0.10`, `3.0.11`, `3.0.12`, `3.0.13`, `3.0.14`, `3.0.15`, `3.0.16`, `3.0.17`, `3.0.18`, `3.0.19`, `3.0.20`, `3.0.21`, `3.0.22`, `3.0.23`, `3.0.24`, `3.0.25`, `3.1.0`, `3.1`, `3.1.1`, `3.1.2`, `3.1.3`, `3.1.4`, `3.1.5`, `3.1.6`, `3.1.7`, `3.1.8`, `3.1.9`, `3.1.10`, `3.1.11`, `3.1.12`, `3.1.13`, `3.1.14`, `3.1.15`, `3.1.16`, `3.1.17`, `3.1.18`, `3.1.19`, `3.1.20`, `3.1.21`, `3.1.22`, `3.1.23`, `3.1.24`, `3.2.1`, `3.2`, `3.2.2`, `3.2.3` |
@@ -21,8 +21,8 @@ below as the publication/deployment gate.
21
21
  Install exact production inputs rather than a moving range or dist-tag:
22
22
 
23
23
  ```bash
24
- npm install --save-exact '@adcp/sdk@15.4.0'
25
- npm view '@adcp/sdk@15.4.0' dist.integrity
24
+ npm install --save-exact '@adcp/sdk@15.6.0'
25
+ npm view '@adcp/sdk@15.6.0' dist.integrity
26
26
  ```
27
27
 
28
28
  ### Required and optional peers
@@ -32,6 +32,8 @@ Install the peer packages used by your application at versions satisfying these
32
32
  | Peer | Supported range | Installation |
33
33
  | --- | --- | --- |
34
34
  | `@a2a-js/sdk` | `^1.0.1` | Required |
35
+ | `@aws-sdk/client-s3` | `^3.700.0` | Optional |
36
+ | `@azure/storage-blob` | `^12.23.0` | Optional |
35
37
  | `@google-cloud/storage` | `^7.22.0 || ^8.2.0` | Optional |
36
38
  | `@modelcontextprotocol/sdk` | `^1.31.0` | Required |
37
39
  | `@opentelemetry/api` | `^1.0.0` | Optional |